📄 jzlcd.c
字号:
/* * linux/drivers/video/jzlcd.c -- Ingenic On-Chip LCD frame buffer device * * Copyright (C) 2005-2007, Ingenic Semiconductor Inc. * * 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. * */#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/tty.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/fb.h>#include <linux/init.h>#include <linux/dma-mapping.h>#include <linux/platform_device.h>#include <linux/pm.h>#include <linux/pm_legacy.h>#include <linux/kthread.h>#include <asm/irq.h>#include <asm/pgtable.h>#include <asm/system.h>#include <asm/uaccess.h>#include <asm/processor.h>#include <asm/jzsoc.h>#include "console/fbcon.h"#include "jzlcd.h"#undef DEBUG//#define DEBUG#ifdef DEBUG#define dprintk(x...) printk(x)#else#define dprintk(x...)#endif#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg)#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg)#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg)#ifdef DEBUG#define print_dbg(f, arg...) printk("dbg::" __FILE__ ",LINE(%d): " f "\n", __LINE__, ## arg)#else#define print_dbg(f, arg...) do {} while (0)#endifstruct lcd_cfb_info { struct fb_info fb; struct display_switch *dispsw; signed int currcon; int func_use_count; struct { u16 red, green, blue; } palette[NR_PALETTE];#ifdef CONFIG_PM struct pm_dev *pm;#endif#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) struct task_struct *rotate_daemon_thread;#endif};static struct lcd_cfb_info *jzlcd_info;struct jzfb_info { unsigned int cfg; /* panel mode and pin usage etc. */ unsigned int w; unsigned int h; unsigned int bpp; /* bit per pixel */ unsigned int fclk; /* frame clk */ unsigned int hsw; /* hsync width, in pclk */ unsigned int vsw; /* vsync width, in line count */ unsigned int elw; /* end of line, in pclk */ unsigned int blw; /* begin of line, in pclk */ unsigned int efw; /* end of frame, in line count */ unsigned int bfw; /* begin of frame, in line count */};static struct jzfb_info jzfb = {#if defined(CONFIG_JZLCD_SHARP_LQ035Q7) MODE_TFT_SHARP | PCLK_N | VSYNC_N, 240, 320, 16, 60, 1, 2, 1, 2, 0, 6#endif#if defined(CONFIG_JZLCD_SAMSUNG_LTS350Q1) MODE_TFT_SAMSUNG | PCLK_N, 240, 320, 16, 60, 1, 2, (254-240), 0, 7, 0#endif#if defined(CONFIG_JZLCD_SAMSUNG_LTV350QVF04) MODE_TFT_GEN | HSYNC_N | VSYNC_N, 320, 240, 16, 70, 19, 4, 20, 14, 18, 6#endif#if defined(CONFIG_JZLCD_SAMSUNG_LTP400WQF01) MODE_TFT_GEN | HSYNC_N | VSYNC_N, 480, 272, 16, 60, 41, 10, 2, 2, 2, 2#endif#if defined(CONFIG_JZLCD_SAMSUNG_LTP400WQF02) /* MODE_TFT_18BIT: JZ4740@ version */ MODE_TFT_GEN | MODE_TFT_18BIT | HSYNC_N | VSYNC_N, 480, 272, 32, 60, 41, 10, 2, 2, 2, 2#endif#if defined(CONFIG_JZLCD_TRULY_TFTG320240DTSW) MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N, 320, 240, 16, 85, 30, 3, 38, 20, 11, 8#endif#if defined(CONFIG_JZLCD_TRULY_TFTG320240DTSW_SERIAL) MODE_8BIT_SERIAL_TFT | HSYNC_N | VSYNC_N | PCLK_N, /* serial mode 280 lines, parallel mode 240 lines */ 320, 280, 32, 60, (30*3), 3, (20*3), (38*3), 46, 23 #endif#if defined(CONFIG_JZLCD_AUO_A030FL01_V1) MODE_TFT_GEN | MODE_TFT_18BIT | HSYNC_N | VSYNC_N, 480, 272, 32, 60, 39, 10, 8, 4, 4, 2#endif#if defined(CONFIG_JZLCD_TRULY_TFTG240320UTSW_63W_E) MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N | DE_N, 320, 240, 16, 60, 3, 3, 3, 3, 3, 85 /* 320x240 */#endif#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) && defined(CONFIG_JZ4740_PAVO) MODE_TFT_GEN | HSYNC_N | VSYNC_N | MODE_TFT_18BIT | PCLK_N,// 320, 240, 18, 110, 1, 1, 10, 50, 10, 13 320, 240, 18, 80, 1, 1, 10, 50, 10, 13#endif#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) && !(defined(CONFIG_JZ4740_PAVO)) MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N, 320, 240, 16, 110, 1, 1, 10, 50, 10, 13#endif#if defined(CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL) MODE_8BIT_SERIAL_TFT | PCLK_N | HSYNC_N | VSYNC_N, 320, 240, 32, 60, 1, 1, 10, 50, 10, 13#endif#if defined(CONFIG_JZLCD_HYNIX_HT10X21) MODE_TFT_GEN | PCLK_N, 1024, 768, 16, 45, 1, 1, 75, 0, 3, 0#endif#if defined(CONFIG_JZLCD_TOSHIBA_LTM084P363) MODE_TFT_GEN | PCLK_N, 800, 600, 16, 50, 1, 2, 199, 0, 2, 0#endif#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42) MODE_TFT_SHARP | PCLK_N, 800, 600, 16, 40, 1, 1, 255, 0, 34, 0 #endif#if defined(CONFIG_JZLCD_CSTN_800x600) MODE_STN_COLOR_DUAL | STN_DAT_PIN8, 800, 600, 16, 30, 8, 1, 0, 0, 0, 0#endif#if defined(CONFIG_JZLCD_CSTN_320x240) MODE_STN_COLOR_SINGLE | STN_DAT_PIN8, 320, 240, 16, 120, 8, 1, 8, 0, 0, 0#endif#if defined(CONFIG_JZLCD_MSTN_640x480) MODE_STN_MONO_DUAL | STN_DAT_PIN4, 640, 480, 8, 110, 4, 1, 4, 0, 0, 0#endif#if defined(CONFIG_JZLCD_MSTN_320x240) MODE_STN_MONO_SINGLE | STN_DAT_PIN4, 320, 240, 8, 110, 4, 1, 4, 0, 0, 0#endif#if defined(CONFIG_JZLCD_MSTN_480x320) MODE_STN_MONO_SINGLE | STN_DAT_PIN8#if defined(CONFIG_JZLCD_MSTN_INVERSE) | DATA_INVERSE#endif , 480, 320, 8, 65, 8, 1, 8, 0, 0, 0#endif#if defined(CONFIG_JZLCD_MSTN_240x128) MODE_STN_MONO_SINGLE | STN_DAT_PIN1#if defined(CONFIG_JZLCD_MSTN_INVERSE) | DATA_INVERSE#endif , 240, 128, 8, 100, 1, 1, 1, 0, 0, 0 #endif};static struct lcd_desc *lcd_desc_base;static struct lcd_desc *lcd_palette_desc;static struct lcd_desc *lcd_frame_desc0;static struct lcd_desc *lcd_frame_desc1;static unsigned char *lcd_palette;static unsigned char *lcd_frame[CONFIG_JZLCD_FRAMEBUFFER_MAX];struct jz_lcd_buffer_addrs_t jz_lcd_buffer_addrs;//extern struct display fb_display[MAX_NR_CONSOLES];#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT)static unsigned char *lcd_frame_user_fb;/* default rotate angle */static volatile int rotate_angle = CONFIG_JZLCD_FRAMEBUFFER_DEFAULT_ROTATE_ANGLE;#endif#ifdef DEBUGstatic void print_regs(void) /* debug */{ printk("REG_LCD_CFG:\t0x%8.8x\n", REG_LCD_CFG); printk("REG_LCD_VSYNC:\t0x%8.8x\n", REG_LCD_VSYNC); printk("REG_LCD_HSYNC:\t0x%8.8x\n", REG_LCD_HSYNC); printk("REG_LCD_VAT:\t0x%8.8x\n", REG_LCD_VAT); printk("REG_LCD_DAH:\t0x%8.8x\n", REG_LCD_DAH); printk("REG_LCD_DAV:\t0x%8.8x\n", REG_LCD_DAV); printk("REG_LCD_PS:\t0x%8.8x\n", REG_LCD_PS); printk("REG_LCD_CLS:\t0x%8.8x\n", REG_LCD_CLS); printk("REG_LCD_SPL:\t0x%8.8x\n", REG_LCD_SPL); printk("REG_LCD_REV:\t0x%8.8x\n", REG_LCD_REV); printk("REG_LCD_CTRL:\t0x%8.8x\n", REG_LCD_CTRL); printk("REG_LCD_STATE:\t0x%8.8x\n", REG_LCD_STATE); printk("REG_LCD_IID:\t0x%8.8x\n", REG_LCD_IID); printk("REG_LCD_DA0:\t0x%8.8x\n", REG_LCD_DA0); printk("REG_LCD_SA0:\t0x%8.8x\n", REG_LCD_SA0); printk("REG_LCD_FID0:\t0x%8.8x\n", REG_LCD_FID0); printk("REG_LCD_CMD0:\t0x%8.8x\n", REG_LCD_CMD0); printk("==================================\n"); printk("REG_LCD_VSYNC:\t%d:%d\n", REG_LCD_VSYNC>>16, REG_LCD_VSYNC&0xfff); printk("REG_LCD_HSYNC:\t%d:%d\n", REG_LCD_HSYNC>>16, REG_LCD_HSYNC&0xfff); printk("REG_LCD_VAT:\t%d:%d\n", REG_LCD_VAT>>16, REG_LCD_VAT&0xfff); printk("REG_LCD_DAH:\t%d:%d\n", REG_LCD_DAH>>16, REG_LCD_DAH&0xfff); printk("REG_LCD_DAV:\t%d:%d\n", REG_LCD_DAV>>16, REG_LCD_DAV&0xfff); printk("==================================\n");}#else#define print_regs()#endif#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT)static int jzfb_rotate_daemon_thread(void *info){ int i,j; struct fb_info *fb = &jzlcd_info->fb; while (!kthread_should_stop()) {#if (CONFIG_JZLCD_FRAMEBUFFER_BPP == 8) unsigned char *plcd_frame = (unsigned char *)lcd_frame[0]; unsigned char *pfb = (unsigned char *) (fb->screen_base);#elif (CONFIG_JZLCD_FRAMEBUFFER_BPP == 16) unsigned short *plcd_frame = (unsigned short *)lcd_frame[0]; unsigned short *pfb = (unsigned short *) (fb->screen_base);#elif (CONFIG_JZLCD_FRAMEBUFFER_BPP == 32) unsigned int *plcd_frame = (unsigned int *)lcd_frame[0]; unsigned int *pfb = (unsigned int *) (fb->screen_base);#else#error "ERROR, rotate not support this bpp."#endif switch ( rotate_angle ) { case FB_ROTATE_UR: printk("%s, Warning, this shouldn't reache\n", __FUNCTION__); ssleep(1); break; case FB_ROTATE_UD: /* cost about 30ms, can be accelrated by dma in the future */ plcd_frame += jzfb.w*jzfb.h -1; for (i=0;i<jzfb.h*jzfb.w;i++) *plcd_frame-- = *pfb++; msleep(75); break; case FB_ROTATE_CW: /* cost about 80ms */ for (i=1;i<fb->var.height+1; i++) { for (j=1; j < fb->var.width+1; j++) plcd_frame[j*fb->var.height-i] = *pfb++; } msleep(100); /* sleep 100ms */ break; case FB_ROTATE_CCW: /* cost about 80ms */ for (i=0;i<fb->var.height;i++) { for ( j=fb->var.width-1;j>=0;j--) plcd_frame[j*fb->var.height+i] = *pfb++; } msleep(100); /* sleep 100ms */ break; default: /* FB_ROTATE_UR */ dprintk("Unknown rotate(%d) type\n", rotate_angle); ssleep(1); } dma_cache_wback_inv((unsigned int)(lcd_frame_user_fb), fb->fix.smem_len); } return 0;}/* * rotate param angle: * 0: FB_ROTATE_UR, 0'C * 1: FB_ROTATE_CW, 90'C * 2: FB_ROTATE_UD, 180'C * 3: FB_ROTATE_CCW, 270'C */static int jzfb_rotate_change( int angle ){ struct fb_info *fb = &jzlcd_info->fb; /* clear frame buffer */ memset((void*)lcd_frame_user_fb, 0x00, fb->fix.smem_len); switch ( angle ) { case FB_ROTATE_UR: fb->var.width = fb->var.xres = fb->var.xres_virtual = jzfb.w; fb->var.height = fb->var.yres = fb->var.yres_virtual = jzfb.h; /* change lcd controller's data buffer to lcd_frame_user_fb*/ lcd_frame_desc0->databuf = virt_to_phys((void *)lcd_frame_user_fb); if ( rotate_angle != FB_ROTATE_UR ) kthread_stop(jzlcd_info->rotate_daemon_thread); rotate_angle = angle; break; case FB_ROTATE_UD: case FB_ROTATE_CW: case FB_ROTATE_CCW: if ( angle == FB_ROTATE_UD ) { fb->var.width = fb->var.xres = fb->var.xres_virtual = jzfb.w; fb->var.height = fb->var.yres = fb->var.yres_virtual = jzfb.h; } else { /* CW, CCW */ fb->var.width = fb->var.xres = fb->var.xres_virtual = jzfb.h; fb->var.height = fb->var.yres = fb->var.yres_virtual = jzfb.w; } /* change lcd controller's data buffer to lcd_frame[0]*/ lcd_frame_desc0->databuf = virt_to_phys((void *)lcd_frame[0]); if ( rotate_angle == FB_ROTATE_UR || \ jzlcd_info->rotate_daemon_thread == NULL) jzlcd_info->rotate_daemon_thread = kthread_run( jzfb_rotate_daemon_thread, jzlcd_info, "%s", "jzlcd-rotate-daemon"); /* start rotate daemon */ rotate_angle = angle; break; default: printk("Invalid angle(%d)\n", (unsigned int)angle); } fb->fix.line_length = fb->var.xres * CONFIG_JZLCD_FRAMEBUFFER_BPP/8; dma_cache_wback_inv((unsigned int)(lcd_frame_desc0), sizeof(struct lcd_desc)); return 0;}void jzfb_fb_rotate(struct fb_info *fbi, int angle){ jzfb_rotate_change( angle/90 );}#endif /* #if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) */static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf){ chan &= 0xffff; chan >>= 16 - bf->length; return chan << bf->offset;}static int jzfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int transp, struct fb_info *info){ struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; unsigned short *ptr, ctmp;// print_dbg("regno:%d,RGBt:(%d,%d,%d,%d)\t", regno, red, green, blue, transp); if (regno >= NR_PALETTE) return 1; cfb->palette[regno].red = red ; cfb->palette[regno].green = green; cfb->palette[regno].blue = blue; if (cfb->fb.var.bits_per_pixel <= 16) { red >>= 8; green >>= 8; blue >>= 8; red &= 0xff; green &= 0xff; blue &= 0xff; } switch (cfb->fb.var.bits_per_pixel) { case 1: case 2: case 4: case 8: if (((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_SINGLE) || ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) { ctmp = (77L * red + 150L * green + 29L * blue) >> 8; ctmp = ((ctmp >> 3) << 11) | ((ctmp >> 2) << 5) | (ctmp >> 3); } else { /* RGB 565 */ if (((red >> 3) == 0) && ((red >> 2) != 0)) red = 1 << 3; if (((blue >> 3) == 0) && ((blue >> 2) != 0)) blue = 1 << 3; ctmp = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3); } ptr = (unsigned short *)lcd_palette; ptr = (unsigned short *)(((u32)ptr)|0xa0000000); ptr[regno] = ctmp; break; case 15: if (regno < 16) ((u32 *)cfb->fb.pseudo_palette)[regno] = ((red >> 3) << 10) | ((green >> 3) << 5) | (blue >> 3); break; case 16: if (regno < 16) { ((u32 *)cfb->fb.pseudo_palette)[regno] = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3); } break; case 18: case 24: case 32: if (regno < 16) ((u32 *)cfb->fb.pseudo_palette)[regno] = (red << 16) | (green << 8) | (blue << 0); /* if (regno < 16) { unsigned val; val = chan_to_field(red, &cfb->fb.var.red); val |= chan_to_field(green, &cfb->fb.var.green); val |= chan_to_field(blue, &cfb->fb.var.blue); ((u32 *)cfb->fb.pseudo_palette)[regno] = val; }*/ break; } return 0;}static int jzfb_ioctl (struct fb_info *fb, unsigned int cmd, unsigned long arg ){ int ret = 0; void __user *argp = (void __user *)arg; switch (cmd) { case FBIOSETBACKLIGHT: __lcd_set_backlight_level(arg); /* We support 8 levels here. */ break; case FBIODISPON: __lcd_display_on(); break; case FBIODISPOFF: __lcd_display_off(); break; case FBIOPRINT_REGS: print_regs(); break; case FBIOGETBUFADDRS: if ( copy_to_user(argp, &jz_lcd_buffer_addrs, sizeof(struct jz_lcd_buffer_addrs_t)) ) return -EFAULT; break;#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) case FBIOROTATE: ret = jzfb_rotate_change(arg); break;#endif /* defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) */ default: printk("Warn: Command(%x) not support\n", cmd); ret = -1; break; } return ret;}/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */static int jzfb_mmap(struct fb_info *info, struct vm_area_struct *vma){ struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; unsigned long start; unsigned long off; u32 len; off = vma->vm_pgoff << PAGE_SHIFT; //fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); /* frame buffer memory */ start = cfb->fb.fix.smem_start; len = PAGE_ALIGN((start & ~PAGE_MASK) + cfb->fb.fix.smem_len); start &= PAGE_MASK; if ((vma->vm_end - vma->vm_start + off) > len) return -EINVAL; off += start; vma->vm_pgoff = off >> PAGE_SHIFT; vma->vm_flags |= VM_IO; vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* Uncacheable */#if 1 pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; /* Uncacheable */// pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT; /* Write-Through */#endif if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot)) { return -EAGAIN; } return 0;}/* checks var and eventually tweaks it to something supported, * DO NOT MODIFY PAR */static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info){ print_dbg("jzfb_check_var"); return 0;}/* * set the video mode according to info->var */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -