x4412 linux4.9.123的lcd驱动移植

本文分享一下如何移植x4412的lcd驱动程序。x4412默认使用的液晶屏型号VS070CXN,分辨率: 1024X600。根据4412手册, LCD 接口支持三种接口, RGB-interface indirect-i80 interface and YUV interface for write-back。使用 RGB888 接口,包含 24 位并行数据总线。

 

首先需要修改设备树,添加如下节点:

lcd_vs070@11C00000 {
         compatible = "x4412, lcd_vs070";
         reg = <0x11C00000 0x20c0 0x10010210 0x08 0x10023c80 0x04 0x1003c000 0x1000>;
         pinctrl-names = "default";
         pinctrl-0 = <&lcd_vs070>;
         clocks = <&clock CLK_FIMD0 &clock CLK_ACLK160>;
         clock-names = "fimd0","aclk160";
};

然后添加lcd的pin管脚配置,如下:

&pinctrl_0 {

lcd_vs070:lcd {

samsung,pins = "gpf0-0", "gpf0-1", "gpf0-2", "gpf0-3", "gpf0-4",
                             "gpf0-5", "gpf0-6","gpf0-7", "gpf1-0", "gpf1-1",
                             "gpf1-2", "gpf1-3", "gpf1-4", "gpf1-5", "gpf1-6",
                             "gpf1-7", "gpf2-0", "gpf2-1", "gpf2-2", "gpf2-3",
                             "gpf2-4", "gpf2-5", "gpf2-6","gpf2-7", "gpf3-0",
                              "gpf3-1", "gpf3-2", "gpf3-3";
                              samsung,pin-function = <2>;
                              samsung,pin-pud = <0>;
                              samsung,pin-drv = <0>;
                      };

};

接着要执行menuconfig指令,添加相关驱动支持。如下:

Device Drivers --->
         Graphics support --->
                 Console display driver support --->
                         <*> Framebuffer Console support

完成后重新编译生成uImage内核文件,替换老的版本。

接下来最重要的是添加lcd驱动源码,具体配置可以参考x4412提供的源码,在kernel/driver/video/samsung下的fb文件里。

#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  

#include  
#include  
#include  

#include  
#include  
#include  

 #define         VIDCON0                 0x00 
 #define         VIDCON1                 0x04 
 #define         VIDTCON0                0x10 
 #define         VIDTCON1                0x14 
 #define         VIDTCON2                0x18 
 #define         WINCON0                 0x20 
 #define         VIDOSD0C                0x48 
 #define         SHADOWCON               0x34 
 #define         WINCHMAP2               0x3c 
 #define         VIDOSD0A                0x40 
 #define         VIDOSD0B                0x44 
 #define         VIDW00ADD0B0    0xA0 
 #define         VIDW00ADD1B0    0xD0 

 #define         CLK_SRC_LCD0            0x234 
 #define         CLK_SRC_MASK_LCD        0x334 
 #define         CLK_DIV_LCD                     0x534 
 #define         CLK_GATE_IP_LCD         0x934 

 #define         LCDBLK_CFG                      0x00 
 #define         LCDBLK_CFG2                     0x04 

 #define         LCD_LENTH                       1024 
 #define         LCD_WIDTH                       600 
 #define         BITS_PER_PIXEL          32 

 static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red, 
                                unsigned int green, unsigned int blue, 
                                unsigned int transp, struct fb_info *info); 


 static struct fb_ops s3c_lcdfb_ops = 
 {     .owner              = THIS_MODULE, 
     .fb_setcolreg       = s3c_lcdfb_setcolreg, 
     .fb_fillrect        = cfb_fillrect, 
     .fb_copyarea        = cfb_copyarea, 
     .fb_imageblit       = cfb_imageblit, 
 }; 


 static struct fb_info *s3c_lcd; 
 static volatile void __iomem *lcd_regs_base; 
 static volatile void __iomem *clk_regs_base; 
 static volatile void __iomem *lcdblk_regs_base; 
 static volatile void __iomem *lcd0_configuration; 
 static u32 pseudo_palette[16]; 
 static struct resource *res1, *res2, *res3, *res4; 

 /* 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; 
 } 


 static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red, 
                                unsigned int green, unsigned int blue, 
                                unsigned int transp, struct fb_info *info) 
 { 
     unsigned int color = 0; 
         uint32_t *p; 

         color  = chan_to_field(red,     &info->var.red); 
         color |= chan_to_field(green, &info->var.green); 
         color |= chan_to_field(blue, &info->var.blue); 


         p = info->pseudo_palette;   
     p[regno] = color; 
     return 0; 
 } 

 static int lcd_probe(struct platform_device *pdev) 
 { 
     int ret; 
     unsigned int temp; 

         /* 1. 分配一个fb_info */ 
     s3c_lcd = framebuffer_alloc(0, NULL); 

          /* 2. 设置 */ 
     /* 2.1 设置 fix 固定的参数 */ 
     strcpy(s3c_lcd->fix.id, "s702"); 
     s3c_lcd->fix.smem_len = LCD_LENTH * LCD_WIDTH * BITS_PER_PIXEL / 8;     //显存的长度 
     s3c_lcd->fix.type     = FB_TYPE_PACKED_PIXELS;                                                      //类型 
     s3c_lcd->fix.visual   = FB_VISUAL_TRUECOLOR;                                                        //TFT 真彩色 
     s3c_lcd->fix.line_length = LCD_LENTH * BITS_PER_PIXEL / 8;                          //一行的长度 
     /* 2.2 设置 var 可变的参数 */ 
     s3c_lcd->var.xres           = LCD_LENTH;                    //x方向分辨率 
     s3c_lcd->var.yres           = LCD_WIDTH;                    //y方向分辨率 
     s3c_lcd->var.xres_virtual   = LCD_LENTH;                    //x方向虚拟分辨率 
     s3c_lcd->var.yres_virtual   = LCD_WIDTH;                    //y方向虚拟分辨率 
     s3c_lcd->var.bits_per_pixel = BITS_PER_PIXEL;               //每个像素占的bit 
     /* RGB:888 */ 
     s3c_lcd->var.red.length     = 8; 
     s3c_lcd->var.red.offset     = 16;       //红 
     s3c_lcd->var.green.length   = 8; 
     s3c_lcd->var.green.offset   = 8;    //绿 
     s3c_lcd->var.blue.length    = 8; 
     s3c_lcd->var.blue.offset    = 0;    //蓝 
     s3c_lcd->var.activate       = FB_ACTIVATE_NOW; 
     /* 2.3 设置操作函数 */ 
     s3c_lcd->fbops              = &s3c_lcdfb_ops; 

     /* 2.4 其他的设置 */ 
     s3c_lcd->pseudo_palette     = pseudo_palette;               //调色板 
     s3c_lcd->screen_size        = LCD_LENTH * LCD_WIDTH * BITS_PER_PIXEL / 8;   //显存大小 

      /* 3. 硬件相关的操作 */ 
     /* 3.1 配置GPIO用于LCD */ 
     //设备树中使用"default" 
     /* 3.2 根据LCD手册设置LCD控制器, 比如VCLK的频率等 */ 
     //寄存器映射 
     res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); 
     if (res1 == NULL) 
     { 
         printk("platform_get_resource error\n"); 
         return -EINVAL; 
     } 
     lcd_regs_base = devm_ioremap_resource(&pdev->dev, res1); 
     if (lcd_regs_base == NULL) 
     { 
         printk("devm_ioremap_resource error\n"); 
         return -EINVAL; 
     } 

     res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); 
     if (res2 == NULL) 
     { 
         printk("platform_get_resource error\n"); 
         return -EINVAL; 
     } 
     lcdblk_regs_base = devm_ioremap_resource(&pdev->dev, res2); 
     if (lcdblk_regs_base == NULL) 
     { 
         printk("devm_ioremap_resource error\n"); 
         return -EINVAL; 
     } 


     res3 = platform_get_resource(pdev, IORESOURCE_MEM, 2); 
     if (res3 == NULL) 
     { 
         printk("platform_get_resource error\n"); 
         return -EINVAL; 
     } 
         lcd0_configuration = ioremap(res3->start, 0x04);     
         if (lcd0_configuration == NULL) 
     { 
         printk("devm_ioremap_resource error\n"); 
         return -EINVAL; 
     } 
         *(unsigned long *)lcd0_configuration = 7; 

     res4 = platform_get_resource(pdev, IORESOURCE_MEM, 3); 
     if (res3 == NULL) 
     { 
         printk("platform_get_resource error\n"); 
         return -EINVAL; 
     } 
     clk_regs_base = ioremap(res4->start, 0x1000); 
         if (clk_regs_base == NULL) 
     { 
         printk("devm_ioremap_resource error\n"); 
         return -EINVAL; 
     } 

     //使能时钟 
     //时钟源选择 0110  SCLKMPLL_USER_T 800M
     temp = readl(clk_regs_base + CLK_SRC_LCD0); 
     temp &= ~0x0f; 
     temp |= 0x06; 
     writel(temp, clk_regs_base + CLK_SRC_LCD0); 
     //FIMD0_MASK 
     temp = readl(clk_regs_base + CLK_SRC_MASK_LCD); 
     temp |= 0x01; 
     writel(temp, clk_regs_base + CLK_SRC_MASK_LCD); 
     //SCLK_FIMD0 = MOUTFIMD0/(FIMD0_RATIO + 1),分频 1/1 
     temp = readl(clk_regs_base + CLK_DIV_LCD); 
     temp &= ~0x0f; 
     writel(temp, clk_regs_base + CLK_DIV_LCD); 
     //CLK_FIMD0 Pass 
     temp = readl(clk_regs_base + CLK_GATE_IP_LCD); 
     temp |= 0x01; 
     writel(temp, clk_regs_base + CLK_GATE_IP_LCD); 
     //FIMDBYPASS_LBLK0 FIMD Bypass 
     temp = readl(lcdblk_regs_base + LCDBLK_CFG); 
     temp |= 1 << 1; 
     writel(temp, lcdblk_regs_base + LCDBLK_CFG); 
     temp = readl(lcdblk_regs_base + LCDBLK_CFG2); 
     temp |= 1 << 0; 
     writel(temp, lcdblk_regs_base + LCDBLK_CFG2); 
     mdelay(1000); 
     //分频      800/(23 +1 ) == 33.3M
     temp = readl(lcd_regs_base + VIDCON0); 
     temp |= (11 << 6); 
     writel(temp, lcd_regs_base + VIDCON0); 
     /* 
      * VIDTCON1: 
      * [5]:IVSYNC  ===> 1 : Inverted(反转) 
      * [6]:IHSYNC  ===> 1 : Inverted(反转) 
      * [7]:IVCLK   ===> 1 : Fetches video data at VCLK rising edge (下降沿触发) 
      * [10:9]:FIXVCLK  ====> 01 : VCLK running 
      */ 
     temp = readl(lcd_regs_base + VIDCON1); 
     temp |= (1 << 5) | (1 << 6); 
     writel(temp, lcd_regs_base + VIDCON1); 
     /* 
      * VIDTCON0: 
      * [23:16]:  VBPD + 1  <------> tvpw (1 - 20)  13 
      * [15:8] :  VFPD + 1  <------> tvfp 22 
      * [7:0]  :  VSPW  + 1 <------> tvb - tvpw = 23 - 13 = 10 
      */ 
     temp = 0;//readl(lcd_regs_base + VIDTCON0); 
     temp |= (17 << 16) | (21 << 8) | (6); 
     writel(temp, lcd_regs_base + VIDTCON0); 
     /* 
      * VIDTCON1: 
      * [23:16]:  HBPD + 1  <------> thpw (1 - 40)  36 
      * [15:8] :  HFPD + 1  <------> thfp 210 
      * [7:0]  :  HSPW  + 1 <------> thb - thpw = 46 - 36 = 10 
      */ 
     temp = readl(lcd_regs_base + VIDTCON1); 
     temp |= (37 << 16) | (209 << 8)  | (9); 
     writel(temp, lcd_regs_base + VIDTCON1); 
     /* 
      * HOZVAL = (Horizontal display size) - 1 and LINEVAL = (Vertical display size) - 1. 
      * Horizontal(水平) display size : 1024 
      * Vertical(垂直) display size : 600 
      */ 
     temp = ((LCD_WIDTH-1) << 11) | (LCD_LENTH-1); 
     writel(temp, lcd_regs_base + VIDTCON2); 
     /* 
      * WINCON0: 
      * [16]:Specifies Half-Word swap control bit.  1 = Enables swap P1779 低位像素存放在低字节 
      * [5:2]: Selects Bits Per Pixel (BPP) mode for Window image : 0101 ===> 16BPP RGB565 
      * [1]:Enables/disables video output   1 = Enables 
      */ 
     temp = readl(lcd_regs_base + WINCON0); 
         temp &= ~(0xf << 2); 
     temp |= (1 << 15) | (0xd << 2) | 1; 
     writel(temp, lcd_regs_base + WINCON0); 
     //Window Size For example, Height ? Width (number of word) 
     temp = (LCD_LENTH * LCD_WIDTH) >> 1; 
     writel(temp, lcd_regs_base + VIDOSD0C); 
     temp = readl(lcd_regs_base + SHADOWCON); 
     writel(temp | 0x01, lcd_regs_base + SHADOWCON); 
     //p1769 
     temp = readl(lcd_regs_base + WINCHMAP2); 
     temp &= ~(7 << 16);
     temp |= 1 << 16; 
     temp &= ~7; 
     temp |= 1; 
     writel(temp, lcd_regs_base + WINCHMAP2); 
     /* 
      * bit0-10 : 指定OSD图像左上像素的垂直屏幕坐标 
      * bit11-21: 指定OSD图像左上像素的水平屏幕坐标 
      */ 
     writel(0, lcd_regs_base + VIDOSD0A); 
     /* 
      * bit0-10 : 指定OSD图像右下像素的垂直屏幕坐标 
      * bit11-21: 指定OSD图像右下像素的水平屏幕坐标 
      */ 
     writel(((LCD_LENTH-1) << 11) | (LCD_WIDTH-1), lcd_regs_base + VIDOSD0B); 
     //Enables video output and logic immediately 
     temp = readl(lcd_regs_base + VIDCON0); 
     writel(temp | 0x03, lcd_regs_base + VIDCON0); 

         /* 3.3 分配显存(framebuffer), 并把地址告诉LCD控制器 */ 
         // s3c_lcd->screen_base         显存虚拟地址 
         // s3c_lcd->fix.smem_len        显存大小,前面计算的 
         // s3c_lcd->fix.smem_start      显存物理地址 
         s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len, (dma_addr_t *)&s3c_lcd->fix.smem_start, GFP_KERNEL);

         //显存起始地址 
         writel(s3c_lcd->fix.smem_start, lcd_regs_base + VIDW00ADD0B0); 
         //显存结束地址 
         writel(s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len, lcd_regs_base + VIDW00ADD1B0); 

     /* 4. 注册 */ 
     ret = register_framebuffer(s3c_lcd); 
     return ret; 
 } 

 static int lcd_remove(struct platform_device *pdev) 
 { 
     unregister_framebuffer(s3c_lcd); 
     dma_free_writecombine(NULL, s3c_lcd->fix.smem_len, s3c_lcd->screen_base, s3c_lcd->fix.smem_start); 
     framebuffer_release(s3c_lcd);
     return 0; 
 } 

 static const struct of_device_id lcd_dt_ids[] = 
 { 
     { .compatible = "x4412,lcd_s702", }, 
     {}, 
 }; 

MODULE_DEVICE_TABLE(of, lcd_dt_ids); 

 static struct platform_driver lcd_driver = 
 { 
     .driver        = { 
         .name      = "lcd_vs070", 
         .of_match_table    = of_match_ptr(lcd_dt_ids), 
     }, 
    .probe         = lcd_probe, 
     .remove        = lcd_remove, 
 }; 

 static int lcd_init(void) 
 { 
     int ret; 
     ret = platform_driver_register(&lcd_driver); 

    if (ret) 
     { 
        printk(KERN_ERR "lcd: probe fail: %d\n", ret); 
     } 

    return ret; 
 } 

 static void lcd_exit(void) 
 { 
     printk("enter %s\n", __func__); 
     platform_driver_unregister(&lcd_driver); 
 } 

module_init(lcd_init); 
module_exit(lcd_exit); 

MODULE_LICENSE("GPL");

编译生成lcd_vs070.ko驱动文件,将其放在最小文件系统的指定目录下,例如放在/driver/lcd/下。然后修改rcS文件,添加如下内容: insmod /lcd/app/lcd_vs070.ko 。这样开机启动后就会自动加载驱动文件,就可以使用lcd作为显示用。

然后进入 etc/inittab ,输入如下内容,确保可以用lcd输出控制信息:

::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
console::askfirst:-/bin/sh
tty0::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r

注意:tty0 设备表示 lcd 输出, askfirst 会询问用户激活命令行, /bin/sh 表示命令行脚本。 这样,重新上电后 lcd 默认加载命令行,命令行同时可以在lcd和uart里输出

你可能感兴趣的:(linux)