LCD Hardware principle:
The monitor made of liquid crystal is called LCD, which can be divided into static drive, simple matrix drive and active matrix drive 3 kinds According to drive mode . Among them, the simple matrix can be subdivided into the torsional column type ( TN) and the ultra-torsional type ( STN) Two , while the active matrix is based on the thin-film transistor type (TFT).
TftScreen is the mainstream of the current embedded system application,The Typical timing of TFT screen is given. The VCLK, HSYNC, and VSYNC in the timing diagram are pixel clock signals (pixel clocks used to latch image data), line synchronization signals, and frame synchronization signals, which are Vden as valid signal signals for data. VD is the data signal of the image.
as frame synchronization Signal vsync HSYNC Each pulse indicates a new line of image data to start sending. In frame synchronization and line The synchronization must have a back sweep time. Such scheduling originated in crt The time required to monitor the electron gun deflection, but later became the de facto industry standard, so tft
How to write LCD driver?
1. Assigning a fb_info structure: framebuffer_alloc
2. Settings
/* Configure FB_INFO Member */* Fix fixed parameter */strcpy (lcd_info->fix.id, "S5PV210_LCD"); Lcd_info->fix.smem_len = 800*480*4; Lcd_info->fix.type = Fb_type_packed_pixels; Lcd_info->fix.visual = Fb_visual_truecolor; Lcd_info->fix.line_length = 800*4; /* var variable parameter */lcd_info->var.xres = 800; Lcd_info->var.yres = 480; lcd_info->var.xres_virtual = 800; lcd_info->var.yres_virtual = 480; Lcd_info->var.bits_per_pixel = 32; Lcd_info->var.red.offset = 16; Lcd_info->var.red.length = 8; Lcd_info->var.green.offset = 8; Lcd_info->var.green.length = 8; Lcd_info->var.blue.offset = 0; Lcd_info->var.blue.length = 8; Lcd_info->var.activate = Fb_activate_now; Lcd_info->screen_size = 800*480*4; Lcd_info->pseudo_palette = Pseudo_palette; Lcd_info->fbops = &lcd_fbops; <span style= "FONT-FAMILY:TIMESNEWROMANPSMT;" >fbops <span style= "fOnt-family:simsun; " > Pointer to a function that points to the underlying operation <br style= "ORPHANS:2; Text-align:-webkit-auto; Widows:2; "/></span></span>
3. Hardware-related operations
Map the corresponding registers according to the schematic diagram:
/* Configure Hardware Resources * /* Mapped memory * /Display_control = Ioremap (0xe0107008,4); Gpf0con = Ioremap (0xe0200120, 4); Gpf1con = Ioremap (0xe0200140, 4); Gpf2con = Ioremap (0xe0200160, 4); Gpf3con = Ioremap (0xe0200180, 4); Gpd0con = Ioremap (0xe02000a0, 4); Gpd0dat = Ioremap (0XE02000A4, 4); Vidcon0 = Ioremap (0xf8000000, 4); Vidcon1 = Ioremap (0xf8000004, 4); Vidtcon0 = Ioremap (0xf8000010, 4); Vidtcon1 = Ioremap (0xf8000014, 4); Vidtcon2 = Ioremap (0xf8000018, 4); Wincon0 = Ioremap (0xf8000020, 4); vidosd0a = Ioremap (0xf8000040, 4); vidosd0b = Ioremap (0xf8000044, 4); vidosd0c = Ioremap (0xf8000048, 4); vidw00add0b0 = Ioremap (0xf80000a0, 4); vidw00add1b0 = Ioremap (0xf80000d0, 4); Shodowcon = Ioremap (0xf8000034, 4);
View the chip manuals and configure the GPIO, for example:
/* Config gpio*/ *gpf0con = 0x22222222; *gpf1con = 0x22222222; *gpf2con = 0x22222222; *gpf3con = 0x22222222; *gpd0con &= ~0xf; *gpd0con |= 0x1; *gpd0dat |= 1<<0; *display_control = 2<<0;
/* Enable clock * /LCD_CLK = Clk_get (NULL, "LCD"); if (!LCD_CLK | | Is_err (LCD_CLK)) { PRINTK (kern_info "failed to get LCD clock source\n"); }
According to the chip manual and schematic diagram, configure the appropriate registers:
Timing diagram, as follows:
/* Configure LCD Controller *///VCLK = HCLK/(clkval+1), where Clkval >= 1 hclk=200mhz vclk=clock Frequency=40mhz clkval=4 *vidcon0 &= ~ (0xFF << 6 | 1 << 4); Data |= vidcon0_clkval_f (Clkdiv-1) | Vidcon0_clkdir; *vidcon0 |= (3 << 6) | (1 << 4); #define VIDCON0_CLKVAL_F (_x) ((_x) << 6) clkval=3; *vidcon0 |= (1 << 1) | (1 << 0); *vidcon1 = 0x04; Writel (Pd->vidcon1, Vidcon1);//writel (0x04, Vidcon1); /*data = VIDTCON0_VBPD (var->upper_margin-1) | VIDTCON0_VFPD (var->lower_margin-1) | VIDTCON0_VSPW (var->vsync_len-1); Writel (data, regs + Sfb->variant.vidtcon); *//*data = VIDTCON1_HBPD (var->left_margin-1) | VIDTCON1_HFPD (var->right_margin-1) | VIDTCON1_HSPW (var->hsync_len-1); Writel (data, regs + Sfb->variant.vidtcon + 4); *//*data = Vidtcon2_lineval (var->yres-1) | Vidtcon2_hozval (VAR->XRES-1); Writel (data, regs + Sfb->variant.vidtcon + 8); *//*VBPD [23:16]Vertical back Porch Specifies the number of inactive lines at TheStart for a frame after Vertical synchronization period. 0X00VFPD [15:8] Vertical front porch Specifies the number of inactive lines at the EndOf a frame before Vertical Synchroni Zation period. 0x00*///tvbp = 22; /*TVFP = TVP-TVW-TVBP-TW = 635-1-22-480 =125*/*vidtcon0 = (<< 16) | (<< 8); *vidtcon0 = (<< 16) | (<< 8); /*HBPD [23:16] Horizontal back porch Specifies the number of VCLK Periodsbetween the falling edge of HSYNC and start of AC tive data. 0X00HFPD [15:8] Horizontal front porch Specifies the number of VCLK Periodsbetween the end of active data and rising edge of HSYNC. *///THBP = 45;/*THFP = THP-THW-THBP-THV = 1056-1-45-800 = 210*/*vidtcon1 = (<< 16) | (<< 8); *vidtcon1 = (<< 16) | (<< 8); *vidtcon2 = (479 << 11) | (799 << 0); Hozval = (horizontal display size)-1; LINeval = (Vertical display size)-1 *wincon0 &= ~ (0xf<<2); *wincon0 |= (0XB<<2); Select BPP *vidosd0a = (0<<11) | (0<<0); Upper left corner coordinate *vidosd0b = (799<<11) | (479<<0); Lower right corner coordinates *vidosd0c = 480*800; specified window size//Physical Address Lcd_info->screen_base = Dma_alloc_writecombine (Null,lcd_info->fix.smem_len, (dma_addr_t *) & amp; (lcd_info->fix.smem_start), Gfp_kernel); *vidw00add0b0 = lcd_info->fix.smem_start; *vidw00add1b0 = Lcd_info->fix.smem_start + lcd_info->fix.smem_len; *shodowcon = 0x1;
4. Registration: Register_framebuffer
ret = Register_framebuffer (lcd_info);
The full LCD driver is as follows:
#include <linux/module.h> #include <linux/fb.h> #include <linux/dma-mapping.h> #include <linux/c lk.h> static struct Fb_info *lcd_info; unsigned long pseudo_palette[16]; unsigned long *display_control; volatile unsigned long* Gpf0con; volatile unsigned long* Gpf1con; volatile unsigned long* Gpf2con; volatile unsigned long* Gpf3con; volatile unsigned long* Gpd0con; volatile unsigned long* gpd0dat; volatile unsigned long* vidcon0; volatile unsigned long* vidcon1; volatile unsigned long* vidtcon0; volatile unsigned long* vidtcon1; volatile unsigned long* vidtcon2; volatile unsigned long* wincon0; volatile unsigned long* vidosd0a; volatile unsigned long* vidosd0b; volatile unsigned long* vidosd0c; volatile unsigned long* vidw00add0b0; volatile unsigned long* vidw00add1b0; volatile unsigned long* Shodowcon; struct CLK *lcd_clk; 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 Lcdfb_setcolreg (unsigned int regno, unsigned int red, unsigned int green, unsigned int b Lue, unsigned int transp, struct fb_info *info) {unsigned int val; if (Regno >) return 1; /* Use Red,green,blue to construct val */val = Chan_to_field (red, &info->var.red); Val |= Chan_to_field (Green, &info->var.green); Val |= Chan_to_field (Blue, &info->var.blue); ((U32 *) (Info->pseudo_palette)) [Regno] = val; Pseudo_palette[regno] = val; return 0; } static struct Fb_ops lcd_fbops = {. Owner = This_module,. Fb_setcolreg = Lcdfb_setcolreg,. Fb_f Illrect = Cfb_fillrect,. Fb_copyarea = Cfb_copyarea,. fb_imageblit = Cfb_imageblit,}; static int lcd_init (void) {int ret; /* Assign Fb_info */lcd_info = FramebuffeR_alloc (0, NULL); if (Lcd_info = = NULL) {PRINTK (kern_err "Alloc framebuffer failed!\n"); Return-enomem; }/* Configure FB_INFO Members */* fix */strcpy (lcd_info->fix.id, "S5PV210_LCD"); Lcd_info->fix.smem_len = 800*480*4; Lcd_info->fix.type = Fb_type_packed_pixels; Lcd_info->fix.visual = Fb_visual_truecolor; Lcd_info->fix.line_length = 800*4; /* var */lcd_info->var.xres = 800; Lcd_info->var.yres = 480; lcd_info->var.xres_virtual = 800; lcd_info->var.yres_virtual = 480; Lcd_info->var.bits_per_pixel = 32; Lcd_info->var.red.offset = 16; Lcd_info->var.red.length = 8; Lcd_info->var.green.offset = 8; Lcd_info->var.green.length = 8; Lcd_info->var.blue.offset = 0; Lcd_info->var.blue.length = 8; Lcd_info->var.activate = Fb_activate_now; Lcd_info->screen_size = 800*480*4; Lcd_info->pseudo_palette = Pseudo_Palette Lcd_info->fbops = &lcd_fbops; /* Configure Hardware Resources */* mapped memory */Display_control = Ioremap (0xe0107008,4); Gpf0con = Ioremap (0xe0200120, 4); Gpf1con = Ioremap (0xe0200140, 4); Gpf2con = Ioremap (0xe0200160, 4); Gpf3con = Ioremap (0xe0200180, 4); Gpd0con = Ioremap (0xe02000a0, 4); Gpd0dat = Ioremap (0XE02000A4, 4); Vidcon0 = Ioremap (0xf8000000, 4); Vidcon1 = Ioremap (0xf8000004, 4); Vidtcon0 = Ioremap (0xf8000010, 4); Vidtcon1 = Ioremap (0xf8000014, 4); Vidtcon2 = Ioremap (0xf8000018, 4); Wincon0 = Ioremap (0xf8000020, 4); vidosd0a = Ioremap (0xf8000040, 4); vidosd0b = Ioremap (0xf8000044, 4); vidosd0c = Ioremap (0xf8000048, 4); vidw00add0b0 = Ioremap (0xf80000a0, 4); vidw00add1b0 = Ioremap (0xf80000d0, 4); Shodowcon = Ioremap (0xf8000034, 4); /* Config gpio*/*gpf0con = 0x22222222; *gpf1con= 0x22222222; *gpf2con = 0x22222222; *gpf3con = 0x22222222; *gpd0con &= ~0xf; *gpd0con |= 0x1; *gpd0dat |= 1<<0; *display_control = 2<<0; /* Enable clock */LCD_CLK = Clk_get (NULL, "LCD"); if (!LCD_CLK | | Is_err (LCD_CLK)) {PRINTK (kern_info "failed to get LCD clock source\n"); } clk_enable (LCD_CLK); /* Configure LCD Controller *///VCLK = HCLK/(clkval+1), where Clkval >= 1 hclk=200mhz vclk=clock Frequency=40mhz clkval=4 *vidcon0 &= ~ (0xFF << 6 | 1 << 4); Data |= vidcon0_clkval_f (Clkdiv-1) | Vidcon0_clkdir; *vidcon0 |= (3 << 6) | (1 << 4); #define VIDCON0_CLKVAL_F (_x) ((_x) << 6) clkval=3; *vidcon0 |= (1 << 1) | (1 << 0); *vidcon1 = 0x04; Writel (Pd->vidcon1, Vidcon1);//writel (0x04, Vidcon1); /*data = VIDTCON0_VBPD (var->upper_margin-1) | VIDTCON0_VFPD (var->lower_margin-1) | VIDTCON0_VSPW (var->vsync_len-1); Writel (Data, regs + Sfb->variant.vidtcon); *//*data = VIDTCON1_HBPD (var->left_margin-1) | VIDTCON1_HFPD (var->right_margin-1) | VIDTCON1_HSPW (var->hsync_len-1); Writel (data, regs + Sfb->variant.vidtcon + 4); *//*data = Vidtcon2_lineval (var->yres-1) | Vidtcon2_hozval (VAR->XRES-1); Writel (data, regs + Sfb->variant.vidtcon + 8); *//*VBPD [23:16] Vertical back porch Specifies the number of inactive Lines at TheStart for a frame after vertical synchronization period. 0X00VFPD [15:8] Vertical front porch Specifies the number of inactive lines at the EndOf a frame before Vertical Synchroni Zation period. 0x00*///tvbp = 22; /*TVFP = TVP-TVW-TVBP-TW = 635-1-22-480 =125*/*vidtcon0 = (<< 16) | (<< 8); *vidtcon0 = (<< 16) | (<< 8); /*HBPD [23:16] Horizontal back porch Specifies the number of VCLK Periodsbetween the falling edge of HSYNC and start of AC tive data. 0X00HFPD [15:8] Horizontal front Porch Specifies the number of VCLK Periodsbetween the end of active data and rising edge of HSYNC. *///THBP = 45;/*THFP = THP-THW-THBP-THV = 1056-1-45-800 = 210*/*vidtcon1 = (<< 16) | (<< 8); *vidtcon1 = (<< 16) | (<< 8); *vidtcon2 = (479 << 11) | (799 << 0); Hozval = (horizontal display size)-1; Lineval = (Vertical display size)-1 *wincon0 &= ~ (0xf<<2); *wincon0 |= (0XB<<2); Select BPP *vidosd0a = (0<<11) | (0<<0); Upper left corner coordinate *vidosd0b = (799<<11) | (479<<0); Lower right corner coordinates *vidosd0c = 480*800; specified window size//Physical Address Lcd_info->screen_base = Dma_alloc_writecombine (Null,lcd_info->fix.smem_len, (dma_addr_t *) & amp; (lcd_info->fix.smem_start), Gfp_kernel); *vidw00add0b0 = lcd_info->fix.smem_start; *vidw00add1b0 = Lcd_info->fix.smem_start + lcd_info->fix.smem_len; *shodowcon = 0x1; Open state *wincon0 |= 1; *vidcon0 |= 3; /* Register Fb_info */ret = Register_framebuffer (lcd_info); return ret; } static void Lcd_exit (void) {Unregister_framebuffer (lcd_info); Dma_free_writecombine (NULL, Lcd_info->fix.smem_len, (void*) lcd_info->screen_base, (dma_addr_t) Lcd_info-> ; fix.smem_start); Iounmap (Shodowcon); Iounmap (VIDW00ADD1B0); Iounmap (vidw00add0b0); Iounmap (VIDOSD0C); Iounmap (VIDOSD0B); Iounmap (vidosd0a); Iounmap (Wincon0); Iounmap (Vidtcon2); Iounmap (Vidtcon1); Iounmap (Vidtcon0); Iounmap (Vidcon1); Iounmap (Vidcon0); Iounmap (Gpd0dat); Iounmap (Gpd0con); Iounmap (Gpf3con); Iounmap (Gpf2con); Iounmap (Gpf1con); Iounmap (Gpf0con); Framebuffer_release (Lcd_info); } module_init (Lcd_init); Module_exit (Lcd_exit); Module_license ("GPL");
Test:
Remove the original driver and load your own compiled driver.
echo Hello >/dev/tty1//Can see Hello on the LCD
Cat Lcd.ko >/dev/fb0//huaping
LCD driver based on s5pv-210 Development Board