本文主要是介绍在x210上编写lcd驱动,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一、前提准备:
1、将video/samsung/s3cfb.c文件s3cfb_probe函数里的内容注释掉,目的是去掉原来的lcd驱动。
2、将/drivers/gpu/pvr的Makefile里的obj-$(CONFIG_ARCH_S5PV210) += s3c_bc.o 注释掉,在s3c_bc.c文件S3C_BC_Register函数里使用到registered_fb,但我们上一步将原来的lcd驱动去掉,所以不应该链接这个文件,否则会出现段错误。如下图。
3、重新编译内核
。
二、驱动编写流程
1、首先先把总线平台框架编写好、在probe函数里按下面顺序编写
1)、lcd电源设置.
2)、分配fb_info
3)、设置fb_info
4)、lcd io口映射、设置
5)、lcd 时钟设置
6)、lcd 控制器映射、设置
7)、打开lcd控制器&&背光
8)、注册
9)、测试
2、分析
2.1、lcd电源设置。
要使用lcd控制器,首先要设置lcd的电源,linux内核是通过Regulator管理电源。若不设置的话,无法操作lcd控制器的寄存器。这里涉及到三个函数,regulator_get()、regulator_is_enabled()、regulator_enable()。
struct regulator *regulator_get(struct device *dev, const char *id):获取该驱动对应的regulator
struct device *dev:驱动对应的设备指针
const char *id:电源id
如:regulator_get(&pdev->dev, "pd") 。 &pdev->dev:总线平台的驱动,驱动的init_name,要和power_domain.c的s5pv210_pd_lcd_supply的dev_name设置一样。 "pd":要和power_domain.c的s5pv210_pd_lcd_supply的supply匹配。如下图。
int regulator_is_enabled(struct regulator *regulator):通过此接口判断电源输出是否已被使能。返回值大于0时已使能。
int regulator_enable(struct regulator *regulator):使能电源
代码如下:
/******************************************1、lcd电源设置********************************************/lcd_regulator = regulator_get(&pdev->dev, "pd");//获取该驱动对应的regulatorprintk("lcd_regulator = %p \n",lcd_regulator);if (!lcd_regulator) {printk("failed to get lcd_regulator\n");ret = -EINVAL;}ret = regulator_is_enabled(lcd_regulator);//通过此接口判断电源输出是否已被使能。返回值大于0时已使能。printk("ret = %d \n",ret);if(ret == 0){ret = regulator_enable(lcd_regulator);//使能lcd电源输出。if (ret < 0) {printk("failed to enable regulator\n");ret = -EINVAL;}}
2.2、分配fb_info
lcd_info = framebuffer_alloc(0,NULL);
2.3、设置fb_info
strcpy(lcd_info->fix.id,"s5p-lcd");
// lcd_info->fix.smem_start = //显存物理地址lcd_info->fix.smem_len = 1024*600*4;//显存大小lcd_info->fix.type = FB_TYPE_PACKED_PIXELS;lcd_info->fix.visual = FB_VISUAL_TRUECOLOR;//真彩色lcd_info->fix.line_length = 1024*4;//每行的字节数lcd_info->var.xres = 1024;//lcd宽度lcd_info->var.yres = 600;//lcd高度lcd_info->var.xres_virtual = 1024;//虚拟屏幕的宽度lcd_info->var.yres_virtual = 600;//虚拟屏幕的高度lcd_info->var.bits_per_pixel = 32;//bpp, ALPHA0-R-G-Blcd_info->var.red.length = 8;//位宽lcd_info->var.red.offset = 16;//偏移lcd_info->var.green.length = 8;lcd_info->var.green.offset = 8;lcd_info->var.blue.length = 8;lcd_info->var.blue.offset = 0;lcd_info->var.transp.offset = 24;lcd_info->var.transp.length = 8;lcd_info->var.activate = FB_ACTIVATE_NOW;/*设置操作函数*/lcd_info->fbops = &s3cfb_ops;lcd_info->pseudo_palette = pseudo_palette;/* Fake palette of 16 colors */ //调试板//lcd_info->screen_base = //显存虚拟大小lcd_info->screen_size = 1024*600*4;//显存虚拟大小
2.4、lcd io口映射、设置。
背光引脚GPD0_0
设置显示模式RGB=FIMD
代码如下:
/******************************************4、lcd io口映射、设置******************************************/// 配置引脚用于LCD功能GPF0_0-GPF3_5// 背光引脚 GPD0_0(PWMTOUT0)gpf0_con = ioremap(0xE0200120, 4);//GPF0_CONif (!gpf0_con) {printk(KERN_ERR "failed to request io GPF0_CON\n");ret = -EINVAL;return ret;}gpf1_con = ioremap(0xE0200140 , 4);//GPF1_CONif (!gpf1_con) {printk(KERN_ERR "failed to request io GPF1_CON\n");ret = -EINVAL;return ret;}gpf2_con = ioremap(0xE0200160 , 4);//GPF2_CONif (!gpf2_con) {printk(KERN_ERR "failed to request io GPF2_CON\n");ret = -EINVAL;return ret;}gpf3_con = ioremap(0xE0200180 , 4);//GPF3_CONif (!gpf3_con) {printk(KERN_ERR "failed to request io GPF3_CON\n");ret = -EINVAL;return ret;}gpd0_con = ioremap(0xE02000A0 , 8);//GPD0_CONif (!gpd0_con) {printk(KERN_ERR "failed to request io GPD0_CON\n");ret = -EINVAL;return ret;}gpd0_dat = gpd0_con + 1;//GPD0_DAT//配置lcd引脚*gpf0_con = 0x22222222;*gpf1_con = 0x22222222;*gpf2_con = 0x22222222;*gpf3_con = 0x22222222;//配置背光引脚*gpd0_con &= ~(0xf<<0);*gpd0_con |= (1<<0); *gpd0_dat |= (1<<0);//关闭背光display_con = ioremap(0xE0107008, 4);if (!display_con) {printk(KERN_ERR "failed to remap display_con\n");ret = -EINVAL;}/*DISPLAY_CONTROL 0xE010_7008* DISPLAY_PATH_SEL [1:0] --> 10: RGB=FIMD I80=FIMD ITU=FIMD //Display path selection*/*display_con = 2<<0;//显示模式RGB=FIMD
2.5、lcd 时钟设置。
lcd的时钟源可以选择HCLK或者SCLK_FIMD。这里选择SCLK_FIMD。
struct clk *clk_get(struct device *dev, const char *id):获取XXX时钟结构体
unsigned long clk_get_rate(struct clk *clk):获取时钟的频率
int clk_set_parent(struct clk *clk, struct clk *parent):设置XXX时钟的时钟源,也就是从哪里分频。
int clk_set_rate(struct clk *clk, unsigned long rate):设置时钟频率
int clk_enable(struct clk *clk):使能时钟
/******************************************5、lcd 时钟设置******************************************/ lcd_clk = clk_get(NULL, "sclk_fimd"); //获取sclk_fimd域的时钟clk结构体if (!lcd_clk || IS_ERR(lcd_clk)) { printk(KERN_INFO "failed to get lcd clock source\n"); } new_rate = clk_get_rate(lcd_clk);//获取sclk_fimd域当前的时钟频率printk(KERN_INFO "old_rate = %d \n",new_rate); clk_set_parent(lcd_clk, clk_get(NULL, "mout_mpll"));//设置sclk_fimd域的时钟源mout_mpllclk_set_rate(lcd_clk, 166750000);//设置sclk_fimd域的时钟频率为166MHZclk_enable(lcd_clk);//使能时钟
2.6、lcd 控制器映射、设置。
s5pv210有0-4屏幕选择,我选择的是屏幕2。
主要设置的寄存器有:VIDCON0、VIDCON1、VIDTCON0、VIDTCON1、VIDTCON2、WINCON2、VIDOSD2A、VIDOSD2B、VIDOSD2D、VIDW02ADD0、VIDW02ADD1、VIDW02ADD2。
VIDCON0:设置lcd时钟源为SCLK_FIMD,CLKVAL_F = 1选择对lcd时钟进行分频,CLKVAL = 3,则VCLK = 166M/(1+3) = 41.5MHZ。
VIDCON1:IHSYNC和IVSYNC设为1,即 HSYNC 和 VSYNC极性翻转,看lcd屏幕的手册。
VIDTCON0:帧同步信号。VBPD、VFPD、VSPW值设置
VIDTCON1:行同步信号。HBPD、HFPD、HSPW值设置
VIDTCON2:LINEVAL、HOZVAL设置。具体的值如下面的图。
WINCON2:ALPHA_SEL_F = 1,24位到32位用来存放透明度值;BPPMODE_F =1101,Unpacked 25 bpp ( non-palletized A:1-R:8-G:8-B:8 );BLD_PIX_F = 1,透明度混合。WSWP_F = 1,Swap Enable,显存的低位放低字节。看下图。
VIDOSD2A:显存左上角的XY坐标。
VIDOSD2B:显存右下角的XY坐标。
VIDOSD2D:屏幕大小。
VIDW02ADD0:显存地址,一定是要映射后的,不能直接操作物理地址。用这个函数dma_alloc_writecombine映射。
VIDW02ADD1:显存长度, 字节单位
VIDW02ADD2:PAGEWIDTH_F:屏幕宽度(字节);OFFSIZE_F: 屏幕显示的位置在虚拟显存的位置。
这里我的lcd屏幕的时序参数表
当WINCON2的WSWP_F置1后,低位放在显存的低字节里。PXXX表示RGB、ALPHA value表示透明度值。
//lcd控制器res = request_mem_region(S5P_PA_LCD,S5P_SZ_LCD, "s5p-lcd");if (!res) {printk(KERN_ERR "failed to request io memory region\n");ret = -EINVAL;}lcd_regs = ioremap(S5P_PA_LCD, S5P_SZ_LCD);if (!lcd_regs) {printk(KERN_ERR "failed to remap io region\n");ret = -EINVAL;}printk(KERN_INFO "lcd_regs = %p \n",lcd_regs);lcd_debug();//当前寄存器值查看/*VIDCON0 * DSI_EN [30] --> 0 = disable MIPI DSI. * VIDOUT [28:26] --> 000 = RGB interface * RGSPSEL [18] --> 0 = RGB parallel format * CLKVAL_F [13:6] --> 3 = VCLK = HCLK / (CLKVAL+1),即VCLK = 166M/(1+3) = 41.5MHZ* CLKDIR [4] --> 1 = Divided by CLKVAL_F ,选择对lcd时钟进行分频,若为0则不分频* CLKSEL_F [2] --> 1 = SCLK_FIMD Selects the video clock source. 0 = HCLK * ENVID [1] --> 1 = Enables the video output and display control signal. //使能lcd控制器* ENVID_F [0] -->1 = Enables the video output and display control signal. //当前帧结束后使能lcd控制器 * NOTE: Display On: ENVID and ENVID_F are set to “1”. Direct Off: ENVID and ENVID_F are set to “0” simultaneously. Per Frame Off: ENVID_F is set to “0” and ENVID is set to “1”. */cfg = (1<<2) | (1<< 4)| (3<<6);writel(cfg, lcd_regs + S3C_VIDCON0);/*VIDCON1 *IVCLK [7] -->0 = Video data is fetched at VCLK falling edge *IHSYNC [6] -->1 = Inverted Specifies the HSYNC pulse polarity HSYNC极性翻转,看lcd屏幕的手册*IVSYNC [5] -->1 = Inverted Specifies the VSYNC pulse polarity. VSYNC极性翻转,看lcd屏幕的手册*IVDEN [4] -->0 = Normal Specifies the VDEN signal polarity. */cfg = (1<<5) | (1<< 6);writel(cfg, lcd_regs + S3C_VIDCON1);/*VIDCON2 * RGB_ORDER_E [21:19] --> 000 = RGB Even line* RGB_ORDER_O [18:16] --> 000 = RGB Odd Line*//*VIDTCON0 看lcd屏幕的手册,关于时序部分帧同步信号* VBPD [23:16] --> 17-1 = 16* VFPD [15:8] --> 12-1=11* VSPW [7:0] --> 3-1=2*/cfg = ( (16<<16) | (11<<8) | (2<<0) );writel(cfg, lcd_regs + S3C_VIDTCON0);/*VIDTCON1 看lcd屏幕的手册,关于时序部分行同步信号* HBPD [23:16] --> 120-1=119* HFPD [15:8] --> 160-1=159* HSPW [7:0] --> 20-1=19*/cfg = ( (119<<16) | (159<<8) | (19<<0));writel(cfg, lcd_regs + S3C_VIDTCON1);/*VIDTCON2 * LINEVAL [21:11] --> 600-1=599 * HOZVAL [10:0] --> 1024-1*1023*/cfg = ( (599<<11) | (1023<<0) );writel(cfg, lcd_regs + S3C_VIDTCON2);/*WINCON2 * InRGB [13] --> 0 = RGB * WSWP_F [15] -->1 = Swap Enable //Specifies the Word swap control bit. 显存的低位放低字节* BLD_PIX_F [6] --> 1 = Per pixel blending //混合 * BPPMODE_F [5:2] --> *1101 = Unpacked 25 bpp ( non-palletized A:1-R:8-G:8-B:8 )* ALPHA_SEL_F [1] -->1 = Using DATA[31:24] data in word boundary (only when BPPMODE_F = 4’b1101) 24位到32位用来存放透明度* ENWIN_F [0] -->1 = Enables the video output and video control signal. //Enables/ disables video output and logic immediately*/cfg = (1<<1) | (13<<2) | (1<<6) | (1<<15);writel(cfg, lcd_regs + S3C_WINCON2);/*WINCHMAP2 * CH0FISEL [18:16] --> 001 = Window 0 //Selects Channel 0’s channel. * W0FISEL [2:0] --> 001 = Channel 0 //Selects Window 0’s channel. 001 *//*VIDOSD2A 显存左上角的XY坐标* OSD_LeftTopX_F [21:11] --> 0* OSD_LeftTopY_F [10:0] --> 0*/cfg = (0<<11) | (0 << 0);writel(cfg, lcd_regs + S3C_VIDOSD2A);/*VIDOSD2B 显存右下角的XY坐标* OSD_RightBotX_F [21:11] --> 1024 - 1 = 1023* OSD_RightBotY_F [10:0] --> 600 - 1 = 599*/cfg = ( (1023<<11) | (599<<0) );writel(cfg, lcd_regs + S3C_VIDOSD2B);/*VIDOSD2D 屏幕大小 * OSDSIZE [23:0] --> (1024) * (600) //Specifies the Window Size */cfg = 1024*600;writel(cfg, lcd_regs + S3C_VIDOSD2D); /*分配显存(framebuffer), 并把地址告诉LCD控制器 */lcd_info->screen_base = dma_alloc_writecombine(NULL, lcd_info->fix.smem_len,&lcd_info->fix.smem_start, GFP_KERNEL);printk(KERN_INFO "lcd_info->fix.smem_start = %x \n",lcd_info->fix.smem_start);printk(KERN_INFO "lcd_info->screen_base = %p \n",lcd_info->screen_base);/*VIDW02ADD0 显存的映射后的地址* VBASEL_F [31:0] -->显存地址 Specifies A [31:0] of the start address for Video frame buffer. */cfg = lcd_info->fix.smem_start;writel(cfg, lcd_regs + S3C_VIDW02ADD0);/*VIDW02ADD1 显存长度 1024*600*4 字节单位* VBASEU_F [31:0] -->((((HOZVAL + 1)*4 + 0) * (LINEVAL + 1)) & (0xffffff)); //Specifies A [31:0] of the start address for Video frame buffer.*/ cfg = lcd_info->fix.smem_len;writel(cfg, lcd_regs + S3C_VIDW02ADD1);/*VIDW02ADD2, *OFFSIZE_F [25:13] --> 0 屏幕显示的位置在虚拟显存的位置*PAGEWIDTH_F [12:0] --> 1024 * 4 屏幕宽度(字节)*/cfg = (1024*4 << 0);writel(cfg, lcd_regs + S3C_VIDW02ADD2);
2.7、打开lcd控制器&&背光。
/******************************************7、打开lcd控制器&&背光***********************************/ *gpd0_dat &= ~(1<<0);//打开背光/*SHODOWCON * W0_SHADOW _PROTECT [10] --> 0 = Updates shadow register per frame * C0_EN_F [2] --> 1 = Enables //Enables Channel 2. 使能通道2传输*/cfg |= (1<<2);writel(cfg, lcd_regs + S3C_WINSHMAP);cfg = readl(lcd_regs + S3C_WINCON2);cfg |= (1<<0);writel(cfg, lcd_regs + S3C_WINCON2);cfg = readl(lcd_regs + S3C_VIDCON0);cfg |= ((1<<0) | (1<< 1));writel(cfg, lcd_regs + S3C_VIDCON0);
2.8、注册。
/******************************************7、注册***********************************/ ret = register_framebuffer(lcd_info);printk(KERN_INFO "register_framebuffer\n");
2.9、测试。
测试效果为,在屏幕上显示分三块,分别显示红绿蓝。
/******************************************8、测试***********************************/ LCD_Fill(0,0,1024,200,0x00ff0000);LCD_Fill(0,200,1024,400,0x0000ff00);LCD_Fill(0,400,1024,600,0x000000ff);
三、全部代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/clk.h>
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/fs.h>
#include <linux/irq.h>
#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/ctype.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/memory.h>
#include <linux/cpufreq.h>
#include <plat/clock.h>
#include <plat/cpu-freq.h>
#include <plat/media.h>#ifdef CONFIG_HAS_WAKELOCK
#include <linux/wakelock.h>
#include <linux/earlysuspend.h>
#include <linux/suspend.h>
#endif
#include <mach/map.h>
#include <plat/regs-fb.h>struct regulator* lcd_regulator;
struct device lcd_dev;
struct clk *lcd_clk; void __iomem *lcd_regs;
struct fb_info* lcd_info;
static u32 pseudo_palette[16];static volatile unsigned int* gpf0_con;
static volatile unsigned int* gpf1_con;
static volatile unsigned int* gpf2_con;
static volatile unsigned int* gpf3_con;
static volatile unsigned int* gpd0_con;
static volatile unsigned int* gpd0_dat;
static volatile unsigned int* display_con;static int s5pfb_setcolreg(unsigned int regno, unsigned int red,unsigned int green, unsigned int blue,unsigned int transp, struct fb_info *fb);struct fb_ops s3cfb_ops = {.owner = THIS_MODULE,.fb_fillrect = cfb_fillrect,.fb_copyarea = cfb_copyarea,.fb_imageblit = cfb_imageblit,.fb_setcolreg = s5pfb_setcolreg,
};static unsigned int __chan_to_field(unsigned int chan,struct fb_bitfield bf)
{chan &= 0xffff;chan >>= 16 - bf.length;return chan << bf.offset;
}static int s5pfb_setcolreg(unsigned int regno, unsigned int red,unsigned int green, unsigned int blue,unsigned int transp, struct fb_info *fb)
{unsigned int val = 0;if (regno < 16) {val |= __chan_to_field(red, fb->var.red);val |= __chan_to_field(green, fb->var.green);val |= __chan_to_field(blue, fb->var.blue);val |= __chan_to_field(transp, fb->var.transp);pseudo_palette[regno] = val;}return 0;
}void LCD_Set_Point(unsigned int X_Pos,unsigned int Y_Pos,unsigned int color)
{*((unsigned int*)lcd_info->screen_base + Y_Pos*1024+X_Pos) = color;
}//在指定区域内填充单个颜色
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
//color:要填充的颜色
void LCD_Fill(unsigned int sx,unsigned int sy,unsigned int ex,unsigned int ey,unsigned int color)
{ unsigned int i,j;unsigned int xlen=0;xlen=ex-sx; for(i=sy;i<ey;i++){for(j=0;j<xlen;j++)LCD_Set_Point(j,i,color); //显示颜色 }
} //lcd寄存器调试
void lcd_debug(void)
{printk("S3C_VIDCON0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDCON0)));printk("S3C_VIDCON1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDCON1)));printk("S3C_VIDCON2 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDCON2)));printk("S3C_PRTCON = %x \n ",*((unsigned int*)(lcd_regs + S3C_PRTCON)));printk("S3C_VIDTCON0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDTCON0)));printk("S3C_VIDTCON1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDTCON1)));printk("S3C_VIDTCON2 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDTCON2)));printk("S3C_WINCON0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_WINCON0)));printk("S3C_WINCON1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_WINCON1)));printk("S3C_WINCON2 = %x \n ",*((unsigned int*)(lcd_regs + S3C_WINCON2)));printk("S3C_WINCON3 = %x \n ",*((unsigned int*)(lcd_regs + S3C_WINCON3)));printk("S3C_WINCON4 = %x \n ",*((unsigned int*)(lcd_regs + S3C_WINCON4)));printk("S3C_WINSHMAP = %x \n ",*((unsigned int*)(lcd_regs + S3C_WINSHMAP)));printk("S3C_VIDOSD0A = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD0A)));printk("S3C_VIDOSD0B = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD0B)));printk("S3C_VIDOSD0C = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD0C)));printk("S3C_VIDOSD1A = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD1A)));printk("S3C_VIDOSD1B = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD1B)));printk("S3C_VIDOSD1C = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD1C)));printk("S3C_VIDOSD1D = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD1D)));printk("S3C_VIDOSD2A = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD2A)));printk("S3C_VIDOSD2B = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD2B)));printk("S3C_VIDOSD2C = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD2C)));printk("S3C_VIDOSD2D = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD2D)));printk("S3C_VIDOSD3A = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD3A)));printk("S3C_VIDOSD3B = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD3B)));printk("S3C_VIDOSD3C = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD3C)));printk("S3C_VIDOSD4A = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD4A)));printk("S3C_VIDOSD4B = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD4B)));printk("S3C_VIDOSD4C = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDOSD4C)));printk("S3C_VIDW00ADD0B0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW00ADD0B0)));printk("S3C_VIDW00ADD0B1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW00ADD0B1)));printk("S3C_VIDW01ADD0B0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW01ADD0B0)));printk("S3C_VIDW01ADD0B1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW01ADD0B1)));printk("S3C_VIDW02ADD0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW02ADD0)));printk("S3C_VIDW03ADD0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW03ADD0)));printk("S3C_VIDW04ADD0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW04ADD0)));printk("S3C_VIDW00ADD1B0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW00ADD1B0)));printk("S3C_VIDW00ADD1B1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW00ADD1B1)));printk("S3C_VIDW01ADD1B0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW01ADD1B0)));printk("S3C_VIDW01ADD1B1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW01ADD1B1)));printk("S3C_VIDW02ADD1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW02ADD1)));printk("S3C_VIDW03ADD1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW03ADD1)));printk("S3C_VIDW04ADD1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW04ADD1)));printk("S3C_VIDW00ADD2 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW00ADD2)));printk("S3C_VIDW01ADD2 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW01ADD2)));printk("S3C_VIDW02ADD2 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW02ADD2)));printk("S3C_VIDW03ADD2 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW03ADD2)));printk("S3C_VIDW04ADD2 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW04ADD2)));printk("S3C_VP1TCON0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VP1TCON0)));printk("S3C_VP1TCON1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VP1TCON1)));printk("S3C_VIDINTCON0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDINTCON0)));printk("S3C_VIDINTCON1 = %x \n ",*(((unsigned int*)lcd_regs + S3C_VIDINTCON1)));printk("S3C_W1KEYCON0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_W1KEYCON0)));printk("S3C_W1KEYCON1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_W1KEYCON1)));printk("S3C_W2KEYCON0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_W2KEYCON0)));printk("S3C_W2KEYCON1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_W2KEYCON1)));printk("S3C_W3KEYCON0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_W3KEYCON0)));printk("S3C_W3KEYCON1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_W3KEYCON1)));printk("S3C_W4KEYCON0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_W4KEYCON0)));printk("S3C_W4KEYCON1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_W4KEYCON1)));printk("S3C_W1KEYALPHA = %x \n ",*((unsigned int*)(lcd_regs + S3C_W1KEYALPHA)));printk("S3C_W2KEYALPHA = %x \n ",*((unsigned int*)(lcd_regs + S3C_W2KEYALPHA)));printk("S3C_W3KEYALPHA = %x \n ",*((unsigned int*)(lcd_regs + S3C_W3KEYALPHA)));printk("S3C_W4KEYALPHA = %x \n ",*((unsigned int*)(lcd_regs + S3C_W4KEYALPHA)));printk("S3C_DITHMODE = %x \n ",*((unsigned int*)(lcd_regs + S3C_DITHMODE)));printk("S3C_WIN0MAP = %x \n ",*((unsigned int*)(lcd_regs + S3C_WIN0MAP)));printk("S3C_WIN1MAP = %x \n ",*((unsigned int*)(lcd_regs + S3C_WIN1MAP)));printk("S3C_WIN2MAP = %x \n ",*((unsigned int*)(lcd_regs + S3C_WIN2MAP)));printk("S3C_WIN3MAP = %x \n ",*((unsigned int*)(lcd_regs + S3C_WIN3MAP)));printk("S3C_WIN4MAP = %x \n ",*((unsigned int*)(lcd_regs + S3C_WIN4MAP)));printk("S3C_WPALCON_H = %x \n ",*((unsigned int*)(lcd_regs + S3C_WPALCON_H)));printk("S3C_WPALCON_L = %x \n ",*((unsigned int*)(lcd_regs + S3C_WPALCON_L)));printk("S3C_VIDW0ALPHA0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW0ALPHA0)));printk("S3C_VIDW0ALPHA1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW0ALPHA1)));printk("S3C_VIDW1ALPHA0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW1ALPHA0)));printk("S3C_VIDW1ALPHA1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW1ALPHA1)));printk("S3C_VIDW2ALPHA0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW2ALPHA0)));printk("S3C_VIDW2ALPHA1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW2ALPHA1)));printk("S3C_VIDW3ALPHA0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW3ALPHA0)));printk("S3C_VIDW3ALPHA1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW3ALPHA1)));printk("S3C_VIDW4ALPHA0 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW4ALPHA0)));printk("S3C_VIDW4ALPHA1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_VIDW4ALPHA1)));printk("S3C_BLENDEQ1 = %x \n ",*((unsigned int*)(lcd_regs + S3C_BLENDEQ1)));printk("S3C_BLENDEQ2 = %x \n ",*((unsigned int*)(lcd_regs + S3C_BLENDEQ2)));printk("S3C_BLENDEQ3 = %x \n ",*((unsigned int*)(lcd_regs + S3C_BLENDEQ3)));printk("S3C_BLENDEQ4 = %x \n ",*((unsigned int*)(lcd_regs + S3C_BLENDEQ4)));printk("S3C_BLENDCON = %x \n ",*((unsigned int*)(lcd_regs + S3C_BLENDCON)));
}static int s5pfb_probe(struct platform_device *pdev)
{int ret;struct resource *res;u32 cfg;long new_rate = 0;/******************************************1、lcd电源设置********************************************/lcd_regulator = regulator_get(&pdev->dev, "pd");//获取该驱动对应的regulatorprintk("lcd_regulator = %p \n",lcd_regulator);if (!lcd_regulator) {printk("failed to get lcd_regulator\n");ret = -EINVAL;}ret = regulator_is_enabled(lcd_regulator);//通过此接口判断电源输出是否已被使能。返回值大于0时已使能。printk("ret = %d \n",ret);if(ret == 0){ret = regulator_enable(lcd_regulator);//使能lcd电源输出。if (ret < 0) {printk("failed to enable regulator\n");ret = -EINVAL;}}/******************************************2、分配fb_info********************************************/lcd_info = framebuffer_alloc(0,NULL);/******************************************3、设置***************************************************/strcpy(lcd_info->fix.id,"s5p-lcd");
// lcd_info->fix.smem_start = //显存物理地址lcd_info->fix.smem_len = 1024*600*4;//显存大小lcd_info->fix.type = FB_TYPE_PACKED_PIXELS;lcd_info->fix.visual = FB_VISUAL_TRUECOLOR;//真彩色lcd_info->fix.line_length = 1024*4;//每行的字节数lcd_info->var.xres = 1024;//lcd宽度lcd_info->var.yres = 600;//lcd高度lcd_info->var.xres_virtual = 1024;//虚拟屏幕的宽度lcd_info->var.yres_virtual = 600;//虚拟屏幕的高度lcd_info->var.bits_per_pixel = 32;//bpp, ALPHA0-R-G-Blcd_info->var.red.length = 8;//位宽lcd_info->var.red.offset = 16;//偏移lcd_info->var.green.length = 8;lcd_info->var.green.offset = 8;lcd_info->var.blue.length = 8;lcd_info->var.blue.offset = 0;lcd_info->var.transp.offset = 24;lcd_info->var.transp.length = 8;lcd_info->var.activate = FB_ACTIVATE_NOW;/*设置操作函数*/lcd_info->fbops = &s3cfb_ops;lcd_info->pseudo_palette = pseudo_palette;/* Fake palette of 16 colors */ //调试板//lcd_info->screen_base = //显存虚拟大小lcd_info->screen_size = 1024*600*4;//显存虚拟大小/******************************************4、lcd io口映射、设置******************************************/// 配置引脚用于LCD功能GPF0_0-GPF3_5// 背光引脚 GPD0_0(PWMTOUT0)gpf0_con = ioremap(0xE0200120, 4);//GPF0_CONif (!gpf0_con) {printk(KERN_ERR "failed to request io GPF0_CON\n");ret = -EINVAL;return ret;}gpf1_con = ioremap(0xE0200140 , 4);//GPF1_CONif (!gpf1_con) {printk(KERN_ERR "failed to request io GPF1_CON\n");ret = -EINVAL;return ret;}gpf2_con = ioremap(0xE0200160 , 4);//GPF2_CONif (!gpf2_con) {printk(KERN_ERR "failed to request io GPF2_CON\n");ret = -EINVAL;return ret;}gpf3_con = ioremap(0xE0200180 , 4);//GPF3_CONif (!gpf3_con) {printk(KERN_ERR "failed to request io GPF3_CON\n");ret = -EINVAL;return ret;}gpd0_con = ioremap(0xE02000A0 , 8);//GPD0_CONif (!gpd0_con) {printk(KERN_ERR "failed to request io GPD0_CON\n");ret = -EINVAL;return ret;}gpd0_dat = gpd0_con + 1;//GPD0_DAT//配置lcd引脚*gpf0_con = 0x22222222;*gpf1_con = 0x22222222;*gpf2_con = 0x22222222;*gpf3_con = 0x22222222;//配置背光引脚*gpd0_con &= ~(0xf<<0);*gpd0_con |= (1<<0); *gpd0_dat |= (1<<0);//关闭背光display_con = ioremap(0xE0107008, 4);if (!display_con) {printk(KERN_ERR "failed to remap display_con\n");ret = -EINVAL;}/*DISPLAY_CONTROL 0xE010_7008* DISPLAY_PATH_SEL [1:0] --> 10: RGB=FIMD I80=FIMD ITU=FIMD //Display path selection*/*display_con = 2<<0;//显示模式RGB=FIMD/******************************************5、lcd 时钟设置******************************************/ lcd_clk = clk_get(NULL, "sclk_fimd"); //获取sclk_fimd域的时钟clk结构体if (!lcd_clk || IS_ERR(lcd_clk)) { printk(KERN_INFO "failed to get lcd clock source\n"); } new_rate = clk_get_rate(lcd_clk);//获取sclk_fimd域当前的时钟频率printk(KERN_INFO "old_rate = %d \n",new_rate); clk_set_parent(lcd_clk, clk_get(NULL, "mout_mpll"));//设置sclk_fimd域的时钟源mout_mpllclk_set_rate(lcd_clk, 166750000);//设置sclk_fimd域的时钟频率为166MHZclk_enable(lcd_clk);//使能时钟new_rate = clk_get_rate(lcd_clk);printk(KERN_INFO "new_rate = %d \n",new_rate); /******************************************6、lcd 控制器映射、设置***********************************/ //lcd控制器res = request_mem_region(S5P_PA_LCD,S5P_SZ_LCD, "s5p-lcd");if (!res) {printk(KERN_ERR "failed to request io memory region\n");ret = -EINVAL;}lcd_regs = ioremap(S5P_PA_LCD, S5P_SZ_LCD);if (!lcd_regs) {printk(KERN_ERR "failed to remap io region\n");ret = -EINVAL;}printk(KERN_INFO "lcd_regs = %p \n",lcd_regs);//lcd_debug();//当前寄存器值查看/*VIDCON0 * DSI_EN [30] --> 0 = disable MIPI DSI. * VIDOUT [28:26] --> 000 = RGB interface * RGSPSEL [18] --> 0 = RGB parallel format * CLKVAL_F [13:6] --> 3 = VCLK = HCLK / (CLKVAL+1),即VCLK = 166M/(1+3) = 41.5MHZ* CLKDIR [4] --> 1 = Divided by CLKVAL_F ,选择对lcd时钟进行分频,若为0则不分频* CLKSEL_F [2] --> 1 = SCLK_FIMD Selects the video clock source. 0 = HCLK * ENVID [1] --> 1 = Enables the video output and display control signal. //使能lcd控制器* ENVID_F [0] --> 1 = Enables the video output and display control signal. //当前帧结束后使能lcd控制器 * NOTE: Display On: ENVID and ENVID_F are set to “1”. Direct Off: ENVID and ENVID_F are set to “0” simultaneously. Per Frame Off: ENVID_F is set to “0” and ENVID is set to “1”. */cfg = (1<<2) | (1<< 4)| (3<<6);writel(cfg, lcd_regs + S3C_VIDCON0);/*VIDCON1 *IVCLK [7] -->0 = Video data is fetched at VCLK falling edge *IHSYNC [6] -->1 = Inverted Specifies the HSYNC pulse polarity HSYNC极性翻转,看lcd屏幕的手册*IVSYNC [5] -->1 = Inverted Specifies the VSYNC pulse polarity. VSYNC极性翻转,看lcd屏幕的手册*IVDEN [4] -->0 = Normal Specifies the VDEN signal polarity. */cfg = (1<<5) | (1<< 6);writel(cfg, lcd_regs + S3C_VIDCON1);/*VIDCON2 * RGB_ORDER_E [21:19] --> 000 = RGB Even line* RGB_ORDER_O [18:16] --> 000 = RGB Odd Line*//*VIDTCON0 看lcd屏幕的手册,关于时序部分帧同步信号* VBPD [23:16] --> 17-1 = 16* VFPD [15:8] --> 12-1=11* VSPW [7:0] --> 3-1=2*/cfg = ( (16<<16) | (11<<8) | (2<<0) );writel(cfg, lcd_regs + S3C_VIDTCON0);/*VIDTCON1 看lcd屏幕的手册,关于时序部分行同步信号* HBPD [23:16] --> 120-1=119* HFPD [15:8] --> 160-1=159* HSPW [7:0] --> 20-1=19*/cfg = ( (119<<16) | (159<<8) | (19<<0));writel(cfg, lcd_regs + S3C_VIDTCON1);/*VIDTCON2 * LINEVAL [21:11] --> 600-1=599 * HOZVAL [10:0] --> 1024-1*1023*/cfg = ( (599<<11) | (1023<<0) );writel(cfg, lcd_regs + S3C_VIDTCON2);/*WINCON2 * InRGB [13] --> 0 = RGB * WSWP_F [15] -->1 = Swap Enable //Specifies the Word swap control bit. 显存的低位放低字节* BLD_PIX_F [6] --> 1 = Per pixel blending //混合 * BPPMODE_F [5:2] --> *1101 = Unpacked 25 bpp ( non-palletized A:1-R:8-G:8-B:8 )* ALPHA_SEL_F [1] -->1 = Using DATA[31:24] data in word boundary (only when BPPMODE_F = 4’b1101) 24位到32位用来存放透明度* ENWIN_F [0] -->1 = Enables the video output and video control signal. //Enables/ disables video output and logic immediately*/cfg = (1<<1) | (13<<2) | (1<<6) | (1<<15);writel(cfg, lcd_regs + S3C_WINCON2);/*WINCHMAP2 * CH0FISEL [18:16] --> 001 = Window 0 //Selects Channel 0’s channel. * W0FISEL [2:0] --> 001 = Channel 0 //Selects Window 0’s channel. 001 *//*VIDOSD2A 显存左上角的XY坐标* OSD_LeftTopX_F [21:11] --> 0* OSD_LeftTopY_F [10:0] --> 0*/cfg = (0<<11) | (0 << 0);writel(cfg, lcd_regs + S3C_VIDOSD2A);/*VIDOSD2B 显存右下角的XY坐标* OSD_RightBotX_F [21:11] --> 1024 - 1 = 1023* OSD_RightBotY_F [10:0] --> 600 - 1 = 599*/cfg = ( (1023<<11) | (599<<0) );writel(cfg, lcd_regs + S3C_VIDOSD2B);/*VIDOSD2D 屏幕大小 * OSDSIZE [23:0] --> (1024) * (600) //Specifies the Window Size */cfg = 1024*600;writel(cfg, lcd_regs + S3C_VIDOSD2D); /*分配显存(framebuffer), 并把地址告诉LCD控制器 */lcd_info->screen_base = dma_alloc_writecombine(NULL, lcd_info->fix.smem_len,&lcd_info->fix.smem_start, GFP_KERNEL);printk(KERN_INFO "lcd_info->fix.smem_start = %x \n",lcd_info->fix.smem_start);printk(KERN_INFO "lcd_info->screen_base = %p \n",lcd_info->screen_base);/*VIDW02ADD0 显存的映射后的地址* VBASEL_F [31:0] -->显存地址 Specifies A [31:0] of the start address for Video frame buffer. */cfg = lcd_info->fix.smem_start;writel(cfg, lcd_regs + S3C_VIDW02ADD0);/*VIDW02ADD1 显存长度 1024*600*4 字节单位* VBASEU_F [31:0] -->((((HOZVAL + 1)*4 + 0) * (LINEVAL + 1)) & (0xffffff)); //Specifies A [31:0] of the start address for Video frame buffer.*/ cfg = lcd_info->fix.smem_len;writel(cfg, lcd_regs + S3C_VIDW02ADD1);/*VIDW02ADD2, *OFFSIZE_F [25:13] --> 0 屏幕显示的位置在虚拟显存的位置*PAGEWIDTH_F [12:0] --> 1024 * 4 屏幕宽度(字节)*/cfg = (1024*4 << 0);writel(cfg, lcd_regs + S3C_VIDW02ADD2);/******************************************7、打开lcd控制器&&背光***********************************/ *gpd0_dat &= ~(1<<0);//打开背光/*SHODOWCON * W0_SHADOW _PROTECT [10] --> 0 = Updates shadow register per frame * C0_EN_F [2] --> 1 = Enables //Enables Channel 2. 使能通道2传输*/cfg = (1<<2);writel(cfg, lcd_regs + S3C_WINSHMAP);cfg = readl(lcd_regs + S3C_WINCON2);cfg |= (1<<0);writel(cfg, lcd_regs + S3C_WINCON2);cfg = readl(lcd_regs + S3C_VIDCON0);cfg |= ((1<<0) | (1<< 1));writel(cfg, lcd_regs + S3C_VIDCON0);/******************************************7、注册***********************************/ ret = register_framebuffer(lcd_info);printk(KERN_INFO "register_framebuffer\n");/******************************************8、测试***********************************/ LCD_Fill(0,0,1024,200,0x00ff0000);LCD_Fill(0,200,1024,400,0x0000ff00);LCD_Fill(0,400,1024,600,0x000000ff);//lcd_debug();return 0;
}static int s5pfb_remove(struct platform_device *pdev)
{unregister_framebuffer(lcd_info);dma_free_writecombine(NULL,lcd_info->fix.smem_len, lcd_info->screen_base, lcd_info->fix.smem_start);iounmap(display_con);iounmap(lcd_regs);release_mem_region(S5P_PA_LCD,S5P_SZ_LCD); iounmap(gpf0_con);//GPF0_CONiounmap(gpf1_con);//GPF1_CONiounmap(gpf2_con);//GPF2_CONiounmap(gpf3_con);//GPF3_CONiounmap(gpd0_con);//GPD0_CONframebuffer_release(lcd_info);regulator_disable(lcd_regulator);regulator_put(lcd_regulator);
}static void s5p_lcd_release(struct device * dev)
{
}struct platform_device s5p_device_fb = {.name = "s5p-lcd",//总线平台名称.id = -1,.dev = {.init_name = "s5p-lcd",//设备init_name,用来获取电源管理pd的regulator。与power-domain.c文件的s5pv210_pd_lcd_supply里dev_name对应.release = s5p_lcd_release,}
};static struct platform_driver s5pfb_driver = {.probe = s5pfb_probe,.remove = s5pfb_remove,.driver = {.name = "s5p-lcd", //总线平台名称.owner = THIS_MODULE,},
};static int __init s5pfb_register(void)
{platform_driver_register(&s5pfb_driver);platform_device_register(&s5p_device_fb);return 0;
}
static void __exit s5pfb_unregister(void)
{platform_driver_unregister(&s5pfb_driver); platform_device_unregister(&s5p_device_fb);
}module_init(s5pfb_register);
module_exit(s5pfb_unregister);
MODULE_LICENSE("GPL");
这篇关于在x210上编写lcd驱动的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!