📄 mb862xxfb.c
字号:
/* * drivers/mb862xx/mb862xxfb.c * * Fujitsu Carmine/Coral-P(A)/Lime framebuffer driver * * (C) 2008 Anatolij Gustschin <agust@denx.de> * DENX Software Engineering * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */#undef DEBUG#include <linux/fb.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/pci.h>#if defined(CONFIG_PPC_OF)#include <linux/of_platform.h>#endif#include "mb862xxfb.h"#include "mb862xx_reg.h"#define NR_PALETTE 256#define MB862XX_MEM_SIZE 0x1000000#define CORALP_MEM_SIZE 0x4000000#define CARMINE_MEM_SIZE 0x8000000#define DRV_NAME "mb862xxfb"#if defined(CONFIG_LWMON5)static struct mb862xx_gc_mode lwmon5_gc_mode = { /* Mode for Sharp LQ104V1DG61 TFT LCD Panel */ { "640x480", 60, 640, 480, 40000, 48, 16, 32, 11, 96, 2, 0, 0, 0 }, /* 16 bits/pixel, 32MB, 100MHz, SDRAM memory mode value */ 16, 0x2000000, GC_CCF_COT_100, 0x414fb7f2};#endif#if defined(CONFIG_SOCRATES)static struct mb862xx_gc_mode socrates_gc_mode = { /* Mode for Prime View PM070WL4 TFT LCD Panel */ { "800x480", 45, 800, 480, 40000, 86, 42, 33, 10, 128, 2, 0, 0, 0 }, /* 16 bits/pixel, 16MB, 133MHz, SDRAM memory mode value */ 16, 0x1000000, GC_CCF_COT_133, 0x4157ba63};#endif/* Helpers */static inline int h_total(struct fb_var_screeninfo *var){ return var->xres + var->left_margin + var->right_margin + var->hsync_len;}static inline int v_total(struct fb_var_screeninfo *var){ return var->yres + var->upper_margin + var->lower_margin + var->vsync_len;}static inline int hsp(struct fb_var_screeninfo *var){ return var->xres + var->right_margin - 1;}static inline int vsp(struct fb_var_screeninfo *var){ return var->yres + var->lower_margin - 1;}static inline int d_pitch(struct fb_var_screeninfo *var){ return var->xres * var->bits_per_pixel / 8;}static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf){ chan &= 0xffff; chan >>= 16 - bf->length; return chan << bf->offset;}static int mb862xxfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info){ struct mb862xxfb_par *par = info->par; unsigned int val; switch (info->fix.visual) { case FB_VISUAL_TRUECOLOR: if (regno < 16) { val = chan_to_field(red, &info->var.red); val |= chan_to_field(green, &info->var.green); val |= chan_to_field(blue, &info->var.blue); par->pseudo_palette[regno] = val; } break; case FB_VISUAL_PSEUDOCOLOR: if (regno < 256) { val = (red >> 8) << 16; val |= (green >> 8) << 8; val |= blue >> 8; outreg(disp, GC_L0PAL0 + (regno * 4), val); } break; default: return 1; /* unsupported type */ } return 0;}static int mb862xxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi){ unsigned long tmp; if (fbi->dev) dev_dbg(fbi->dev, "%s\n", __func__); /* check if these values fit into the registers */ if (var->hsync_len > 255 || var->vsync_len > 255) return -EINVAL; if ((var->xres + var->right_margin) >= 4096) return -EINVAL; if ((var->yres + var->lower_margin) > 4096) return -EINVAL; if (h_total(var) > 4096 || v_total(var) > 4096) return -EINVAL; if (var->xres_virtual > 4096 || var->yres_virtual > 4096) return -EINVAL; if (var->bits_per_pixel <= 8) var->bits_per_pixel = 8; else if (var->bits_per_pixel <= 16) var->bits_per_pixel = 16; else if (var->bits_per_pixel <= 32) var->bits_per_pixel = 32; /* * can cope with 8,16 or 24/32bpp if resulting * pitch is divisible by 64 without remainder */ if (d_pitch(&fbi->var) % GC_L0M_L0W_UNIT) { int r; var->bits_per_pixel = 0; do { var->bits_per_pixel += 8; r = d_pitch(&fbi->var) % GC_L0M_L0W_UNIT; } while (r && var->bits_per_pixel <= 32); if (d_pitch(&fbi->var) % GC_L0M_L0W_UNIT) return -EINVAL; } /* line length is going to be 128 bit aligned */ tmp = (var->xres * var->bits_per_pixel) / 8; if ((tmp & 15) != 0) return -EINVAL; /* set r/g/b positions and validate bpp */ switch (var->bits_per_pixel) { case 8: var->red.length = var->bits_per_pixel; var->green.length = var->bits_per_pixel; var->blue.length = var->bits_per_pixel; var->red.offset = 0; var->green.offset = 0; var->blue.offset = 0; var->transp.length = 0; break; case 16: var->red.length = 5; var->green.length = 5; var->blue.length = 5; var->red.offset = 10; var->green.offset = 5; var->blue.offset = 0; var->transp.length = 0; break; case 24: case 32: var->transp.length = 8; var->red.length = 8; var->green.length = 8; var->blue.length = 8; var->transp.offset = 24; var->red.offset = 16; var->green.offset = 8; var->blue.offset = 0; break; default: return -EINVAL; } return 0;}/* * set display parameters */static int mb862xxfb_set_par(struct fb_info *fbi){ struct mb862xxfb_par *par = fbi->par; unsigned long reg, sc; dev_dbg(par->dev, "%s\n", __func__); if (par->pre_init) return 0; /* disp off */ reg = inreg(disp, GC_DCM1); reg &= ~GC_DCM01_DEN; outreg(disp, GC_DCM1, reg); /* set display reference clock div. */ sc = par->refclk / (1000000 / fbi->var.pixclock) - 1; reg = inreg(disp, GC_DCM1); reg &= ~(GC_DCM01_CKS | GC_DCM01_RESV | GC_DCM01_SC); reg |= sc << 8; outreg(disp, GC_DCM1, reg); dev_dbg(par->dev, "SC 0x%lx\n", sc); /* disp dimension, format */ reg = pack(d_pitch(&fbi->var) / GC_L0M_L0W_UNIT, (fbi->var.yres - 1)); if (fbi->var.bits_per_pixel == 16) reg |= GC_L0M_L0C_16; outreg(disp, GC_L0M, reg); if (fbi->var.bits_per_pixel == 32) { reg = inreg(disp, GC_L0EM); outreg(disp, GC_L0EM, reg | GC_L0EM_L0EC_24); } outreg(disp, GC_WY_WX, 0); reg = pack(fbi->var.yres - 1, fbi->var.xres); outreg(disp, GC_WH_WW, reg); outreg(disp, GC_L0OA0, 0); outreg(disp, GC_L0DA0, 0); outreg(disp, GC_L0DY_L0DX, 0); outreg(disp, GC_L0WY_L0WX, 0); outreg(disp, GC_L0WH_L0WW, reg); /* both HW-cursors off */ reg = inreg(disp, GC_CPM_CUTC); reg &= ~(GC_CPM_CEN0 | GC_CPM_CEN1); outreg(disp, GC_CPM_CUTC, reg); /* timings */ reg = pack(fbi->var.xres - 1, fbi->var.xres - 1); outreg(disp, GC_HDB_HDP, reg); reg = pack((fbi->var.yres - 1), vsp(&fbi->var)); outreg(disp, GC_VDP_VSP, reg); reg = ((fbi->var.vsync_len - 1) << 24) | pack((fbi->var.hsync_len - 1), hsp(&fbi->var)); outreg(disp, GC_VSW_HSW_HSP, reg); outreg(disp, GC_HTP, pack(h_total(&fbi->var) - 1, 0)); outreg(disp, GC_VTR, pack(v_total(&fbi->var) - 1, 0)); /* display on */ reg = inreg(disp, GC_DCM1); reg |= GC_DCM01_DEN | GC_DCM01_L0E; reg &= ~GC_DCM01_ESY; outreg(disp, GC_DCM1, reg); return 0;}static int mb862xxfb_pan(struct fb_var_screeninfo *var, struct fb_info *info){ struct mb862xxfb_par *par = info->par; unsigned long reg; reg = pack(var->yoffset, var->xoffset); outreg(disp, GC_L0WY_L0WX, reg); reg = pack(var->yres_virtual, var->xres_virtual); outreg(disp, GC_L0WH_L0WW, reg); return 0;}static int mb862xxfb_blank(int mode, struct fb_info *fbi){ struct mb862xxfb_par *par = fbi->par; unsigned long reg; dev_dbg(fbi->dev, "blank mode=%d\n", mode); switch (mode) { case FB_BLANK_POWERDOWN: reg = inreg(disp, GC_DCM1); reg &= ~GC_DCM01_DEN; outreg(disp, GC_DCM1, reg); break; case FB_BLANK_UNBLANK: reg = inreg(disp, GC_DCM1); reg |= GC_DCM01_DEN; outreg(disp, GC_DCM1, reg); break; case FB_BLANK_NORMAL: case FB_BLANK_VSYNC_SUSPEND: case FB_BLANK_HSYNC_SUSPEND: default: return 1; } return 0;}/* framebuffer ops */static struct fb_ops mb862xxfb_ops = { .owner = THIS_MODULE, .fb_check_var = mb862xxfb_check_var, .fb_set_par = mb862xxfb_set_par, .fb_setcolreg = mb862xxfb_setcolreg, .fb_blank = mb862xxfb_blank, .fb_pan_display = mb862xxfb_pan, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit,};/* initialize fb_info data */static int mb862xxfb_init_fbinfo(struct fb_info *fbi){ struct mb862xxfb_par *par = fbi->par; struct mb862xx_gc_mode *mode = par->gc_mode; unsigned long reg; fbi->fbops = &mb862xxfb_ops; fbi->pseudo_palette = par->pseudo_palette; fbi->screen_base = par->fb_base; fbi->screen_size = par->mapped_vram; strcpy(fbi->fix.id, DRV_NAME); fbi->fix.smem_start = (unsigned long)par->fb_base_phys; fbi->fix.smem_len = par->mapped_vram; fbi->fix.mmio_start = (unsigned long)par->mmio_base_phys; fbi->fix.mmio_len = par->mmio_len; fbi->fix.accel = FB_ACCEL_NONE; fbi->fix.type = FB_TYPE_PACKED_PIXELS; fbi->fix.type_aux = 0; fbi->fix.xpanstep = 1; fbi->fix.ypanstep = 1; fbi->fix.ywrapstep = 0; reg = inreg(disp, GC_DCM1); if (reg & GC_DCM01_DEN && reg & GC_DCM01_L0E) { /* get the disp mode from active display cfg */ unsigned long sc = ((reg & GC_DCM01_SC) >> 8) + 1; unsigned long hsp, vsp, ht, vt; dev_dbg(par->dev, "using bootloader's disp. mode\n"); fbi->var.pixclock = (sc * 1000000) / par->refclk; fbi->var.xres = (inreg(disp, GC_HDB_HDP) & 0x0fff) + 1; reg = inreg(disp, GC_VDP_VSP); fbi->var.yres = ((reg >> 16) & 0x0fff) + 1; vsp = (reg & 0x0fff) + 1; fbi->var.xres_virtual = fbi->var.xres; fbi->var.yres_virtual = fbi->var.yres; reg = inreg(disp, GC_L0EM); if (reg & GC_L0EM_L0EC_24) { fbi->var.bits_per_pixel = 32; } else { reg = inreg(disp, GC_L0M); if (reg & GC_L0M_L0C_16) fbi->var.bits_per_pixel = 16; else fbi->var.bits_per_pixel = 8; } reg = inreg(disp, GC_VSW_HSW_HSP); fbi->var.hsync_len = ((reg & 0xff0000) >> 16) + 1; fbi->var.vsync_len = ((reg & 0x3f000000) >> 24) + 1; hsp = (reg & 0xffff) + 1; ht = ((inreg(disp, GC_HTP) & 0xfff0000) >> 16) + 1; fbi->var.right_margin = hsp - fbi->var.xres; fbi->var.left_margin = ht - hsp - fbi->var.hsync_len; vt = ((inreg(disp, GC_VTR) & 0xfff0000) >> 16) + 1; fbi->var.lower_margin = vsp - fbi->var.yres; fbi->var.upper_margin = vt - vsp - fbi->var.vsync_len; } else if (mode) { dev_dbg(par->dev, "using supplied mode\n"); fb_videomode_to_var(&fbi->var, (struct fb_videomode *)mode); fbi->var.bits_per_pixel = mode->def_bpp ? mode->def_bpp : 8; } else { int ret; ret = fb_find_mode(&fbi->var, fbi, "640x480-16@60", NULL, 0, NULL, 16); if (ret == 0 || ret == 4) { dev_err(par->dev, "failed to get initial mode\n"); return -EINVAL; } } fbi->var.xoffset = 0; fbi->var.yoffset = 0; fbi->var.grayscale = 0; fbi->var.nonstd = 0; fbi->var.height = -1; fbi->var.width = -1; fbi->var.accel_flags = 0; fbi->var.vmode = FB_VMODE_NONINTERLACED; fbi->var.activate = FB_ACTIVATE_NOW; fbi->flags = FBINFO_DEFAULT |#ifdef __BIG_ENDIAN FBINFO_FOREIGN_ENDIAN |#endif FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; /* check and possibly fix bpp */ if ((fbi->fbops->fb_check_var)(&fbi->var, fbi)) dev_err(par->dev, "check_var() failed on initial setup?\n"); fbi->fix.visual = fbi->var.bits_per_pixel == 8 ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; fbi->fix.line_length = (fbi->var.xres_virtual * fbi->var.bits_per_pixel) / 8; return 0;}/* * show some display controller and cursor registers */static ssize_t mb862xxfb_show_dispregs(struct device *dev, struct device_attribute *attr, char *buf){ struct fb_info *fbi = dev_get_drvdata(dev); struct mb862xxfb_par *par = fbi->par; char *ptr = buf; unsigned int reg; for (reg = GC_DCM0; reg <= GC_L0DY_L0DX; reg += 4) ptr += sprintf(ptr, "%08x = %08x\n", reg, inreg(disp, reg)); for (reg = GC_CPM_CUTC; reg <= GC_CUY1_CUX1; reg += 4) ptr += sprintf(ptr, "%08x = %08x\n", reg, inreg(disp, reg)); for (reg = GC_DCM1; reg <= GC_L0WH_L0WW; reg += 4) ptr += sprintf(ptr, "%08x = %08x\n", reg, inreg(disp, reg)); return ptr - buf;}static DEVICE_ATTR(dispregs, 0444, mb862xxfb_show_dispregs, NULL);irqreturn_t mb862xx_intr(int irq, void *dev_id){ struct mb862xxfb_par *par = (struct mb862xxfb_par *) dev_id; unsigned long reg_ist, mask; if (!par) return IRQ_NONE; if (par->type == BT_CARMINE) { /* Get Interrupt Status */ reg_ist = inreg(ctrl, GC_CTRL_STATUS); mask = inreg(ctrl, GC_CTRL_INT_MASK); if (reg_ist == 0) return IRQ_HANDLED; reg_ist &= mask; if (reg_ist == 0) return IRQ_HANDLED; /* Clear interrupt status */ outreg(ctrl, 0x0, reg_ist); } else { /* Get status */ reg_ist = inreg(host, GC_IST); mask = inreg(host, GC_IMASK); reg_ist &= mask; if (reg_ist == 0) return IRQ_HANDLED; /* Clear status */ outreg(host, GC_IST, ~reg_ist); } return IRQ_HANDLED;}#if defined(CONFIG_FB_MB862XX_LIME)/* * GDC (Lime, Coral(B/Q), Mint, ...) on host bus */static int mb862xx_gdc_init(struct mb862xxfb_par *par){ unsigned long ccf, mmr; unsigned long ver, rev; if (!par) return -ENODEV;#if defined(CONFIG_FB_PRE_INIT_FB) par->pre_init = 1;#endif par->host = par->mmio_base; par->i2c = par->mmio_base + MB862XX_I2C_BASE; par->disp = par->mmio_base + MB862XX_DISP_BASE; par->cap = par->mmio_base + MB862XX_CAP_BASE; par->draw = par->mmio_base + MB862XX_DRAW_BASE; par->geo = par->mmio_base + MB862XX_GEO_BASE; par->pio = par->mmio_base + MB862XX_PIO_BASE; par->refclk = GC_DISP_REFCLK_400; ver = inreg(host, GC_CID); rev = inreg(pio, GC_REVISION); if ((ver == 0x303) && (rev & 0xffffff00) == 0x20050100) { dev_info(par->dev, "Fujitsu Lime v1.%d found\n", (int)rev & 0xff); par->type = BT_LIME; ccf = par->gc_mode ? par->gc_mode->ccf : GC_CCF_COT_100; mmr = par->gc_mode ? par->gc_mode->mmr : 0x414fb7f2; } else { dev_info(par->dev, "? GDC, CID/Rev.: 0x%lx/0x%lx \n", ver, rev);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -