📄 sm501fb.c
字号:
return ret; } sm501fb_pan_crt(var, info); sm501fb_set_par_geometry(info, var); control |= SM501_FIFO_3; /* fill if >3 free slots */ switch(var->bits_per_pixel) { case 8: control |= SM501_DC_CRT_CONTROL_8BPP; break; case 16: control |= SM501_DC_CRT_CONTROL_16BPP; sm501fb_setup_gamma(fbi, SM501_DC_CRT_PALETTE); break; case 32: control |= SM501_DC_CRT_CONTROL_32BPP; sm501fb_setup_gamma(fbi, SM501_DC_CRT_PALETTE); break; default: BUG(); } control |= SM501_DC_CRT_CONTROL_SEL; /* CRT displays CRT data */ control |= SM501_DC_CRT_CONTROL_TE; /* enable CRT timing */ control |= SM501_DC_CRT_CONTROL_ENABLE; /* enable CRT plane */ out_update: dev_dbg(fbi->dev, "new control is %08lx\n", control); writel(control, fbi->regs + SM501_DC_CRT_CONTROL); sm501fb_sync_regs(fbi); return 0;}static void sm501fb_panel_power(struct sm501fb_info *fbi, int to){ unsigned long control; void __iomem *ctrl_reg = fbi->regs + SM501_DC_PANEL_CONTROL; struct sm501_platdata_fbsub *pd = fbi->pdata->fb_pnl; control = readl(ctrl_reg); if (to && (control & SM501_DC_PANEL_CONTROL_VDD) == 0) { /* enable panel power */ control |= SM501_DC_PANEL_CONTROL_VDD; /* FPVDDEN */ writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); control |= SM501_DC_PANEL_CONTROL_DATA; /* DATA */ writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); /* VBIASEN */ if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) { if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN) control &= ~SM501_DC_PANEL_CONTROL_BIAS; else control |= SM501_DC_PANEL_CONTROL_BIAS; writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); } if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) { if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN) control &= ~SM501_DC_PANEL_CONTROL_FPEN; else control |= SM501_DC_PANEL_CONTROL_FPEN; writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); } } else if (!to && (control & SM501_DC_PANEL_CONTROL_VDD) != 0) { /* disable panel power */ if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) { if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN) control |= SM501_DC_PANEL_CONTROL_FPEN; else control &= ~SM501_DC_PANEL_CONTROL_FPEN; writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); } if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) { if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN) control |= SM501_DC_PANEL_CONTROL_BIAS; else control &= ~SM501_DC_PANEL_CONTROL_BIAS; writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); } control &= ~SM501_DC_PANEL_CONTROL_DATA; writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); control &= ~SM501_DC_PANEL_CONTROL_VDD; writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); } sm501fb_sync_regs(fbi);}/* sm501fb_set_par_pnl * * Set the panel video mode from the fb_info structure*/static int sm501fb_set_par_pnl(struct fb_info *info){ struct sm501fb_par *par = info->par; struct sm501fb_info *fbi = par->info; struct fb_var_screeninfo *var = &info->var; unsigned long control; unsigned long reg; int ret; dev_dbg(fbi->dev, "%s(%p)\n", __func__, info); /* activate this new configuration */ ret = sm501fb_set_par_common(info, var); if (ret) return ret; sm501fb_pan_pnl(var, info); sm501fb_set_par_geometry(info, var); /* update control register */ control = readl(fbi->regs + SM501_DC_PANEL_CONTROL); control &= (SM501_DC_PANEL_CONTROL_GAMMA | SM501_DC_PANEL_CONTROL_VDD | SM501_DC_PANEL_CONTROL_DATA | SM501_DC_PANEL_CONTROL_BIAS | SM501_DC_PANEL_CONTROL_FPEN | SM501_DC_PANEL_CONTROL_CP | SM501_DC_PANEL_CONTROL_CK | SM501_DC_PANEL_CONTROL_HP | SM501_DC_PANEL_CONTROL_VP | SM501_DC_PANEL_CONTROL_HPD | SM501_DC_PANEL_CONTROL_VPD); control |= SM501_FIFO_3; /* fill if >3 free slots */ switch(var->bits_per_pixel) { case 8: control |= SM501_DC_PANEL_CONTROL_8BPP; break; case 16: control |= SM501_DC_PANEL_CONTROL_16BPP; sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE); break; case 32: control |= SM501_DC_PANEL_CONTROL_32BPP; sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE); break; default: BUG(); } writel(0x0, fbi->regs + SM501_DC_PANEL_PANNING_CONTROL); /* panel plane top left and bottom right location */ writel(0x00, fbi->regs + SM501_DC_PANEL_TL_LOC); reg = var->xres - 1; reg |= (var->yres - 1) << 16; writel(reg, fbi->regs + SM501_DC_PANEL_BR_LOC); /* program panel control register */ control |= SM501_DC_PANEL_CONTROL_TE; /* enable PANEL timing */ control |= SM501_DC_PANEL_CONTROL_EN; /* enable PANEL gfx plane */ if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0) control |= SM501_DC_PANEL_CONTROL_HSP; if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0) control |= SM501_DC_PANEL_CONTROL_VSP; writel(control, fbi->regs + SM501_DC_PANEL_CONTROL); sm501fb_sync_regs(fbi); /* ensure the panel interface is not tristated at this point */ sm501_modify_reg(fbi->dev->parent, SM501_SYSTEM_CONTROL, 0, SM501_SYSCTRL_PANEL_TRISTATE); /* power the panel up */ sm501fb_panel_power(fbi, 1); return 0;}/* chan_to_field * * convert a colour value into a field position * * from pxafb.c*/static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf){ chan &= 0xffff; chan >>= 16 - bf->length; return chan << bf->offset;}/* sm501fb_setcolreg * * set the colour mapping for modes that support palettised data*/static int sm501fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info){ struct sm501fb_par *par = info->par; struct sm501fb_info *fbi = par->info; void __iomem *base = fbi->regs; unsigned int val; if (par->head == HEAD_CRT) base += SM501_DC_CRT_PALETTE; else base += SM501_DC_PANEL_PALETTE; switch (info->fix.visual) { case FB_VISUAL_TRUECOLOR: /* true-colour, use pseuo-palette */ if (regno < 16) { u32 *pal = par->pseudo_palette; val = chan_to_field(red, &info->var.red); val |= chan_to_field(green, &info->var.green); val |= chan_to_field(blue, &info->var.blue); pal[regno] = val; } break; case FB_VISUAL_PSEUDOCOLOR: if (regno < 256) { val = (red >> 8) << 16; val |= (green >> 8) << 8; val |= blue >> 8; writel(val, base + (regno * 4)); } break; default: return 1; /* unknown type */ } return 0;}/* sm501fb_blank_pnl * * Blank or un-blank the panel interface*/static int sm501fb_blank_pnl(int blank_mode, struct fb_info *info){ struct sm501fb_par *par = info->par; struct sm501fb_info *fbi = par->info; dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info); switch (blank_mode) { case FB_BLANK_POWERDOWN: sm501fb_panel_power(fbi, 0); break; case FB_BLANK_UNBLANK: sm501fb_panel_power(fbi, 1); break; case FB_BLANK_NORMAL: case FB_BLANK_VSYNC_SUSPEND: case FB_BLANK_HSYNC_SUSPEND: default: return 1; } return 0;}/* sm501fb_blank_crt * * Blank or un-blank the crt interface*/static int sm501fb_blank_crt(int blank_mode, struct fb_info *info){ struct sm501fb_par *par = info->par; struct sm501fb_info *fbi = par->info; unsigned long ctrl; dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info); ctrl = readl(fbi->regs + SM501_DC_CRT_CONTROL); switch (blank_mode) { case FB_BLANK_POWERDOWN: ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE; sm501_misc_control(fbi->dev->parent, SM501_MISC_DAC_POWER, 0); case FB_BLANK_NORMAL: ctrl |= SM501_DC_CRT_CONTROL_BLANK; break; case FB_BLANK_UNBLANK: ctrl &= ~SM501_DC_CRT_CONTROL_BLANK; ctrl |= SM501_DC_CRT_CONTROL_ENABLE; sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER); break; case FB_BLANK_VSYNC_SUSPEND: case FB_BLANK_HSYNC_SUSPEND: default: return 1; } writel(ctrl, fbi->regs + SM501_DC_CRT_CONTROL); sm501fb_sync_regs(fbi); return 0;}/* sm501fb_cursor * * set or change the hardware cursor parameters*/static int sm501fb_cursor(struct fb_info *info, struct fb_cursor *cursor){ struct sm501fb_par *par = info->par; struct sm501fb_info *fbi = par->info; void __iomem *base = fbi->regs; unsigned long hwc_addr; unsigned long fg, bg; dev_dbg(fbi->dev, "%s(%p,%p)\n", __func__, info, cursor); if (par->head == HEAD_CRT) base += SM501_DC_CRT_HWC_BASE; else base += SM501_DC_PANEL_HWC_BASE; /* check not being asked to exceed capabilities */ if (cursor->image.width > 64) return -EINVAL; if (cursor->image.height > 64) return -EINVAL; if (cursor->image.depth > 1) return -EINVAL; hwc_addr = readl(base + SM501_OFF_HWC_ADDR); if (cursor->enable) writel(hwc_addr | SM501_HWC_EN, base + SM501_OFF_HWC_ADDR); else writel(hwc_addr & ~SM501_HWC_EN, base + SM501_OFF_HWC_ADDR); /* set data */ if (cursor->set & FB_CUR_SETPOS) { unsigned int x = cursor->image.dx; unsigned int y = cursor->image.dy; if (x >= 2048 || y >= 2048 ) return -EINVAL; dev_dbg(fbi->dev, "set position %d,%d\n", x, y); //y += cursor->image.height; writel(x | (y << 16), base + SM501_OFF_HWC_LOC); } if (cursor->set & FB_CUR_SETCMAP) { unsigned int bg_col = cursor->image.bg_color; unsigned int fg_col = cursor->image.fg_color; dev_dbg(fbi->dev, "%s: update cmap (%08x,%08x)\n", __func__, bg_col, fg_col); bg = ((info->cmap.red[bg_col] & 0xF8) << 8) | ((info->cmap.green[bg_col] & 0xFC) << 3) | ((info->cmap.blue[bg_col] & 0xF8) >> 3); fg = ((info->cmap.red[fg_col] & 0xF8) << 8) | ((info->cmap.green[fg_col] & 0xFC) << 3) | ((info->cmap.blue[fg_col] & 0xF8) >> 3); dev_dbg(fbi->dev, "fgcol %08lx, bgcol %08lx\n", fg, bg); writel(bg, base + SM501_OFF_HWC_COLOR_1_2); writel(fg, base + SM501_OFF_HWC_COLOR_3); } if (cursor->set & FB_CUR_SETSIZE || cursor->set & (FB_CUR_SETIMAGE | FB_CUR_SETSHAPE)) { /* SM501 cursor is a two bpp 64x64 bitmap this routine * clears it to transparent then combines the cursor * shape plane with the colour plane to set the * cursor */ int x, y; const unsigned char *pcol = cursor->image.data; const unsigned char *pmsk = cursor->mask; void __iomem *dst = par->cursor.k_addr; unsigned char dcol = 0; unsigned char dmsk = 0; unsigned int op; dev_dbg(fbi->dev, "%s: setting shape (%d,%d)\n", __func__, cursor->image.width, cursor->image.height); for (op = 0; op < (64*64*2)/8; op+=4) writel(0x0, dst + op); for (y = 0; y < cursor->image.height; y++) { for (x = 0; x < cursor->image.width; x++) { if ((x % 8) == 0) { dcol = *pcol++; dmsk = *pmsk++; } else { dcol >>= 1; dmsk >>= 1; } if (dmsk & 1) { op = (dcol & 1) ? 1 : 3; op <<= ((x % 4) * 2); op |= readb(dst + (x / 4)); writeb(op, dst + (x / 4)); } } dst += (64*2)/8; } } sm501fb_sync_regs(fbi); /* ensure cursor data flushed */ return 0;}/* sm501fb_crtsrc_show * * device attribute code to show where the crt output is sourced from*/static ssize_t sm501fb_crtsrc_show(struct device *dev, struct device_attribute *attr, char *buf){ struct sm501fb_info *info = dev_get_drvdata(dev); unsigned long ctrl; ctrl = readl(info->regs + SM501_DC_CRT_CONTROL); ctrl &= SM501_DC_CRT_CONTROL_SEL; return snprintf(buf, PAGE_SIZE, "%s\n", ctrl ? "crt" : "panel");}/* sm501fb_crtsrc_show * * device attribute code to set where the crt output is sourced from*/static ssize_t sm501fb_crtsrc_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){ struct sm501fb_info *info = dev_get_drvdata(dev); enum sm501_controller head; unsigned long ctrl; if (len < 1) return -EINVAL; if (strnicmp(buf, "crt", 3) == 0) head = HEAD_CRT; else if (strnicmp(buf, "panel", 5) == 0) head = HEAD_PANEL; else return -EINVAL; dev_info(dev, "setting crt source to head %d\n", head); ctrl = readl(info->regs + SM501_DC_CRT_CONTROL); if (head == HEAD_CRT) { ctrl |= SM501_DC_CRT_CONTROL_SEL; ctrl |= SM501_DC_CRT_CONTROL_ENABLE; ctrl |= SM501_DC_CRT_CONTROL_TE; } else { ctrl &= ~SM501_DC_CRT_CONTROL_SEL; ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE; ctrl &= ~SM501_DC_CRT_CONTROL_TE; } writel(ctrl, info->regs + SM501_DC_CRT_CONTROL); sm501fb_sync_regs(info); return len;}/* Prepare the device_attr for registration with sysfs later */static DEVICE_ATTR(crt_src, 0666, sm501fb_crtsrc_show, sm501fb_crtsrc_store);/* sm501fb_show_regs * * show the primary sm501 registers*/static int sm501fb_show_regs(struct sm501fb_info *info, char *ptr, unsigned int start, unsigned int len){ void __iomem *mem = info->regs; char *buf = ptr; unsigned int reg; for (reg = start; reg < (len + start); reg += 4) ptr += sprintf(ptr, "%08x = %08x\n", reg, readl(mem + reg)); return ptr - buf;}/* sm501fb_debug_show_crt * * show the crt control and cursor registers*/static ssize_t sm501fb_debug_show_crt(struct device *dev, struct device_attribute *attr, char *buf){ struct sm501fb_info *info = dev_get_drvdata(dev); char *ptr = buf; ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_CONTROL, 0x40); ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_HWC_BASE, 0x10); return ptr - buf;}static DEVICE_ATTR(fbregs_crt, 0444, sm501fb_debug_show_crt, NULL);/* sm501fb_debug_show_pnl * * show the panel control and cursor registers*/static ssize_t sm501fb_debug_show_pnl(struct device *dev, struct device_attribute *attr, char *buf){ struct sm501fb_info *info = dev_get_drvdata(dev); char *ptr = buf; ptr += sm501fb_show_regs(info, ptr, 0x0, 0x40); ptr += sm501fb_show_regs(info, ptr, SM501_DC_PANEL_HWC_BASE, 0x10); return ptr - buf;}static DEVICE_ATTR(fbregs_pnl, 0444, sm501fb_debug_show_pnl, NULL);/* framebuffer ops */static struct fb_ops sm501fb_ops_crt = { .owner = THIS_MODULE, .fb_check_var = sm501fb_check_var_crt, .fb_set_par = sm501fb_set_par_crt, .fb_blank = sm501fb_blank_crt, .fb_setcolreg = sm501fb_setcolreg, .fb_pan_display = sm501fb_pan_crt, .fb_cursor = sm501fb_cursor, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit,};static struct fb_ops sm501fb_ops_pnl = { .owner = THIS_MODULE, .fb_check_var = sm501fb_check_var_pnl, .fb_set_par = sm501fb_set_par_pnl, .fb_pan_display = sm501fb_pan_pnl, .fb_blank = sm501fb_blank_pnl, .fb_setcolreg = sm501fb_setcolreg, .fb_cursor = sm501fb_cursor, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit,};/* sm501_init_cursor * * initialise hw cursor parameters*/static int sm501_init_cursor(struct fb_info *fbi, unsigned int reg_base){ struct sm501fb_par *par; struct sm501fb_info *info; int ret; if (fbi == NULL) return 0; par = fbi->par; info = par->info; par->cursor_regs = info->regs + reg_base; ret = sm501_alloc_mem(info, &par->cursor, SM501_MEMF_CURSOR, 1024);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -