《Linux驱动:s3c2440 lcd 驱动分析--终结篇》

news/2024/11/30 0:34:26/

文章目录

  • 一,前言
  • 二,LCD原理和硬件分析
    • 2.1 LCD原理解析
    • 2.2 硬件电路
      • 2.2.1 LCD背光电路
      • 2.2.2 LCD屏
      • 2.2.3 S3c2440主控
  • 三,LCD应用平台总线-设备-驱动模型
    • 3.1 lcd 设备的加载和注册
    • 3.2 lcd 驱动的加载和注册
      • 3.2.1 编译进内核,加载驱动
    • 3.3 lcd 设备和驱动的匹配
      • 3.3.1 lcd设备注册时的匹配
      • 3.3.2 lcd驱动注册时的匹配
      • 3.3.3 匹配成功后 driver_probe_device 调用驱动层的probe
  • 四,probe函数分析(s3c2410fb_probe)
    • 4.1 注册framebuffer
      • 4.1.1 申请struct fb_info
      • 4.1.2 参数设置
      • 4.1.3 注册
    • 4.2 LCD 参数设置
      • 4.2.1 固定参数设置
      • 4.2.2 可变参数设置
      • 4.2.3 其他参数设置
      • 4.2.4 硬件相关设置(相关寄存器和引脚)
  • 五,fbmem字符设备驱动
    • 5.1 驱动加载并初始化
    • 5.2 register_framebuffer
  • 六,一次LCD显示的过程
  • 七,其他处理函数和逻辑

一,前言

s3c2440 lcd 驱动分析,涉及到的内容有,LCD图像显示原理、s3c2440的LCD控制器的操作、LCD驱动使用平台总线-设备-驱动模型的实例、LCD相关参数的设置、fb字符设备驱动实例、framebuffer的注册和管理、以及一次LCD显示的完整过程分析。

二,LCD原理和硬件分析

2.1 LCD原理解析

SDRAM:在SDRAM中申请了一块连续的内存作为LCD显示数据的存储,叫做显存(framebuffer)。
LCD控制器:LCD控制器通过硬件电路和LCD屏连接。
LCD屏:作为一个外设通过硬件电路和MCU(引脚配置为LCD引脚)连接。
在这里插入图片描述

图像在LCD屏上显示,可以看成是LCD控制器先从显存中取出一帧图像数据,然后输入到LCD屏上。480*272的屏,所显示的一帧有480*272个像素点、272行、480列。对于每一行的像素点,LCD控制器有一个VCLK信号控制,每来一个VCLK,显示的像素点就向右移动一个,当移动到这一行中的最后一个像素点时,LCD控制器有一个HSYNC信号,控制像素点跳到下一行的第一个像素显示。对于一帧图像(也叫一场),即当像素点移动到最后一行的最后一个位置显示完后,LCD控制器有一个VSYNC信号,控制像素点重新移动到第一行的第一个像素显示下一帧图像。

2.2 硬件电路

2.2.1 LCD背光电路

开启LCD显示,需要使能KEYBOARD(一般EN表示高电平有效,EN上面画一横表示低电平有效)开启背光。
在这里插入图片描述
背光开关通过主控,GPB0引脚设置
在这里插入图片描述

2.2.2 LCD屏

VLINE:HSYNC信号输出引脚(由LCD控制器操作)
VFRAME:VSYNC信号输出引脚(由LCD控制器操作)
VCLK:VCLK信号输出引脚(由LCD控制器操作)
VD3~VD7:RGB(565)中B数据输出引脚
VD10~VD15:RGB(565)中G数据输出引脚
VD19~VD23:RGB(565)中R数据输出引脚
TS*:供ts触摸屏连接
在这里插入图片描述

2.2.3 S3c2440主控

涉及 GPG、GPD、GPC引脚。
VM:LCD控制器使能引脚(由LCD控制器的寄存器配置),开启LCD显示需要配置相关寄存器的相应位使能。
LCD_PWREN:LCD电源使能引脚(由LCD控制器的寄存器配置),开启LCD显示需要配置相关寄存器的相应位使能。
在这里插入图片描述

三,LCD应用平台总线-设备-驱动模型

3.1 lcd 设备的加载和注册

MACHINE_START(S3C2440, "SMDK2440")
/* Maintainer: Ben Dooks <ben@fluff.org> */
.phys_io	= S3C2410_PA_UART,
.io_pg_offst	= (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params	= S3C2410_SDRAM_PA + 0x100,.init_irq	= s3c24xx_init_irq,
.map_io		= smdk2440_map_io,
.init_machine	= smdk2440_machine_init,
.timer		= &s3c24xx_timer,
MACHINE_END

将上面的宏展开


static const struct machine_desc __mach_desc_SMDK2440__attribute_used____attribute__((__section__(".arch.info.init"))) = {.nr = MACH_TYPE_SMDK2410, /* architecture number */.name = "SMDK2440", /* architecture name *//* Maintainer: Jonas Dietsche */.phys_io = S3C2410_PA_UART, /* start of physical io */.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,.boot_params = S3C2410_SDRAM_PA + 0x100, /* tagged list */.map_io = smdk2440_map_io, /* IO mapping function */.init_irq = s3c24xx_init_irq,.init_machine = smdk2440_machine_init,.timer = &s3c24xx_timer,
}

MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(“.arch.info.init”),是初始化数据,Kernel 起来之后将被丢弃。
各个成员函数在不同时期被调用:

  1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用。
  2. init_irq在start_kernel() -> init_IRQ() -> init_arch_irq() 被调用
  3. map_io 在 setup_arch() -> paging_init() -> devicemaps_init()被调用
    其他主要都在 setup_arch() 中用到。

系统初始化时,会调用smdk2440_machine_init

static void __init smdk2440_machine_init(void)
{// 这里设置LCD的参数,和驱动分离。这样要修改LCD时,驱动层程序可以不需要改动,只需修改设备层参数就行了,方便移植。s3c24xx_fb_set_platdata(&smdk2440_lcd_cfg);// 将smdk2440_devices数组中的设备注册到平台总线platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));smdk_machine_init();
}// smdk2440_devices数组
static struct platform_device *smdk2440_devices[] __initdata = {&s3c_device_usb,&s3c_device_lcd,&s3c_device_wdt,&s3c_device_i2c,&s3c_device_iis,&s3c2440_device_sdi,
};// lcd设备
struct platform_device s3c_device_lcd = {.name		  = "s3c2410-lcd",.id		  = -1,.num_resources	  = ARRAY_SIZE(s3c_lcd_resource),.resource	  = s3c_lcd_resource,.dev              = {.dma_mask		= &s3c_device_lcd_dmamask,.coherent_dma_mask	= 0xffffffffUL}
};// 设置lcd设备参数 smdk2440_lcd_cfg。各参数含义后面在probe分析时解析
/* 480x272 */ 
static struct s3c2410fb_mach_info smdk2440_lcd_cfg __initdata = {.regs   = {.lcdcon1 =  S3C2410_LCDCON1_TFT16BPP | \S3C2410_LCDCON1_TFT | \S3C2410_LCDCON1_CLKVAL(0x04),.lcdcon2 =  S3C2410_LCDCON2_VBPD(1) | \S3C2410_LCDCON2_LINEVAL(271) | \S3C2410_LCDCON2_VFPD(1) | \S3C2410_LCDCON2_VSPW(9),.lcdcon3 =  S3C2410_LCDCON3_HBPD(1) | \S3C2410_LCDCON3_HOZVAL(479) | \S3C2410_LCDCON3_HFPD(1),.lcdcon4 =  S3C2410_LCDCON4_HSPW(40),.lcdcon5	= S3C2410_LCDCON5_FRM565 |S3C2410_LCDCON5_INVVLINE |S3C2410_LCDCON5_INVVFRAME |S3C2410_LCDCON5_PWREN |S3C2410_LCDCON5_HWSWP,},.gpccon      =  0xaaaaaaaa,.gpccon_mask	= 0xffffffff,.gpcup       =  0xffffffff,.gpcup_mask	= 0xffffffff,.gpdcon      =  0xaaaaaaaa,.gpdcon_mask	= 0xffffffff,.gpdup       =  0xffffffff,.gpdup_mask	= 0xffffffff,.fixed_syncs =  1,.type        =  S3C2410_LCDCON1_TFT, .width		= 480,.height		= 272,.xres		= {.min	= 480,.max	= 480,.defval	= 480,},.yres		= {.max    =   272,.min    =   272,.defval =   272,},.bpp    = {.min    =   16,.max    =   16,.defval =   16,},
};

调用platform_device_register注册平台设备

int platform_add_devices(struct platform_device **devs, int num)
{int i, ret = 0;for (i = 0; i < num; i++) {ret = platform_device_register(devs[i]);if (ret) {while (--i >= 0)platform_device_unregister(devs[i]);break;}}return ret;
}

3.2 lcd 驱动的加载和注册

3.2.1 编译进内核,加载驱动

编译内核设置,make menuconfig

-> Device Drivers-> Graphics support<*> Support for frame buffer devices

linux-2.6.22.6/.config
CONFIG_FB_S3C2410=y
linux-2.6.22.6/drivers/video/Makefile
obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o

然后make uImage,将s3c2410fb驱动编译进内核,系统启动便会加载,即调用驱动的s3c2410fb_init函数。

int __devinit s3c2410fb_init(void)
{// 注册到平台总线驱动return platform_driver_register(&s3c2410fb_driver);
}

3.3 lcd 设备和驱动的匹配

3.3.1 lcd设备注册时的匹配

platform_device_register ->platform_device_add ->device_add ->bus_attach_device ->device_attach -> bus_for_each_drv -> // 从平台总线的的驱动链表中,取出每一项驱动进行匹配__device_attach ->driver_probe_device ->if (drv->bus->match && !drv->bus->match(dev, drv)) 此总线类型为平台总线,其存在match函数,即调用platform_match进行匹配// 平台总线                            
struct bus_type platform_bus_type = {.name		= "platform",.dev_attrs	= platform_dev_attrs,.match		= platform_match,.uevent		= platform_uevent,.suspend	= platform_suspend,.suspend_late	= platform_suspend_late,.resume_early	= platform_resume_early,.resume		= platform_resume,
};              static int platform_match(struct device * dev, struct device_driver * drv)
{struct platform_device *pdev = container_of(dev, struct platform_device, dev);// 平台总线匹配设备和驱动的名称return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}// lcd 设备   name = "s3c2410-lcd"
struct platform_device s3c_device_lcd = {.name		  = "s3c2410-lcd",.id		  = -1,.num_resources	  = ARRAY_SIZE(s3c_lcd_resource),.resource	  = s3c_lcd_resource,.dev              = {.dma_mask		= &s3c_device_lcd_dmamask,.coherent_dma_mask	= 0xffffffffUL}
};// lcd 驱动 name	= "s3c2410-lcd"
static struct platform_driver s3c2410fb_driver = {.probe		= s3c2410fb_probe,.remove		= s3c2410fb_remove,.suspend	= s3c2410fb_suspend,.resume		= s3c2410fb_resume,.driver		= {.name	= "s3c2410-lcd",.owner	= THIS_MODULE,},
};

3.3.2 lcd驱动注册时的匹配

platform_driver_register->driver_register->bus_add_driver->driver_attach->bus_for_each_dev-> // 从平台总线的的设备链表中,取出每一项设备进行匹配__driver_attach->driver_probe_device->if (drv->bus->match && !drv->bus->match(dev, drv)) // 此总线类型为平台总线,其存在match函数,即调用platform_match进行匹配// 之后的执行和上一小节分析的一样

3.3.3 匹配成功后 driver_probe_device 调用驱动层的probe

driver_probe_device-> // 在此函数中匹配成功的话,就会去调用驱动的probe函数really_probe->drv->probe(dev)

四,probe函数分析(s3c2410fb_probe)

4.1 注册framebuffer

4.1.1 申请struct fb_info

struct fb_info	   *fbinfo;
...
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
if (!fbinfo) {return -ENOMEM;
}
...

4.1.2 参数设置

见4.2 LCD参数设置

4.1.3 注册

...
ret = register_framebuffer(fbinfo);
if (ret < 0) {printk(KERN_ERR "Failed to register framebuffer device: %d\n", ret);goto free_video_memory;
}
...

4.2 LCD 参数设置

获取平台设备数据,即smdk2440_lcd_cfg结构体内数据

// 获取平台数据即 linux-2.6.22.6/arch/arm/mach-s3c2440/mach-smdk2440.c中配置的smdk2440_lcd_cfg mach_info = pdev->dev.platform_data;if (mach_info == NULL) {dev_err(&pdev->dev,"no platform data for lcd, cannot attach\n");return -EINVAL;}

4.2.1 固定参数设置

struct fb_fix_screeninfo {char id[16];			/* identification string eg "TT Builtin" */unsigned long smem_start;	/* Start of frame buffer mem *//* (physical address) */__u32 smem_len;			/* Length of frame buffer mem */__u32 type;			/* see FB_TYPE_*		*/__u32 type_aux;			/* Interleave for interleaved Planes */__u32 visual;			/* see FB_VISUAL_*		*/ __u16 xpanstep;			/* zero if no hardware panning  */__u16 ypanstep;			/* zero if no hardware panning  */__u16 ywrapstep;		/* zero if no hardware ywrap    */__u32 line_length;		/* length of a line in bytes    */unsigned long mmio_start;	/* Start of Memory Mapped I/O   *//* (physical address) */__u32 mmio_len;			/* Length of Memory Mapped I/O  */__u32 accel;			/* Indicate to driver which	*//*  specific chip/card we have	*/__u16 reserved[3];		/* Reserved for future compatibility */
};
...
id:driver 标识strcpy(fbinfo->fix.id, driver_name);smem_start:frame buffer 的起始地址fbi->map_cpu  = dma_alloc_writecombine(fbi->dev, fbi->map_size,&fbi->map_dma, GFP_KERNEL);...fbi->screen_dma		= fbi->map_dma;fbi->fb->fix.smem_start  = fbi->screen_dma;...smem_len:frame buffer 的长度字节为单位 = 480*272*16/8   屏宽*屏高*一个像素的数据位数/一个字节的位数fbinfo->fix.smem_len        =	mach_info->xres.max *mach_info->yres.max *mach_info->bpp.max / 8;type、type_aux:fb数据类型为像素类型,还有平面模式等
fbinfo->fix.type	    = FB_TYPE_PACKED_PIXELS;
fbinfo->fix.type_aux	    = 0;visual:设置为真彩色,还有单色模式,黑/白
fbi->fb->fix.visual = FB_VISUAL_TRUECOLOR;默认设置为0,简单理解为边框宽度
fbinfo->fix.xpanstep	    = 0;
fbinfo->fix.ypanstep	    = 0;
fbinfo->fix.ywrapstep	    = 0;line_length:一行fb数据的字节数 480*16/8
fbi->fb->fix.line_length     = (var->width*var->bits_per_pixel)/8;

4.2.2 可变参数设置

struct fb_var_screeninfo {__u32 xres;			/* visible resolution		*/__u32 yres;__u32 xres_virtual;		/* virtual resolution		*/__u32 yres_virtual;__u32 xoffset;			/* offset from virtual to visible */__u32 yoffset;			/* resolution			*/__u32 bits_per_pixel;		/* guess what			*/__u32 grayscale;		/* != 0 Graylevels instead of colors */struct fb_bitfield red;		/* bitfield in fb mem if true color, */struct fb_bitfield green;	/* else only length is significant */struct fb_bitfield blue;struct fb_bitfield transp;	/* transparency			*/	__u32 nonstd;			/* != 0 Non standard pixel format */__u32 activate;			/* see FB_ACTIVATE_*		*/__u32 height;			/* height of picture in mm    */__u32 width;			/* width of picture in mm     */__u32 accel_flags;		/* (OBSOLETE) see fb_info.flags *//* Timing: All values in pixclocks, except pixclock (of course) */__u32 pixclock;			/* pixel clock in ps (pico seconds) */__u32 left_margin;		/* time from sync to picture	*/__u32 right_margin;		/* time from picture to sync	*/__u32 upper_margin;		/* time from sync to picture	*/__u32 lower_margin;__u32 hsync_len;		/* length of horizontal sync	*/__u32 vsync_len;		/* length of vertical sync	*/__u32 sync;			/* see FB_SYNC_*		*/__u32 vmode;			/* see FB_VMODE_*		*/__u32 rotate;			/* angle we rotate counter clockwise */__u32 reserved[5];		/* Reserved for future compatibility */
};
屏幕分辨率和单个像素点的数据位数
fbinfo->var.xres	    = mach_info->xres.defval; 		480
fbinfo->var.xres_virtual    = mach_info->xres.defval; 	480
fbinfo->var.yres	    = mach_info->yres.defval;   	272
fbinfo->var.yres_virtual    = mach_info->yres.defval;	272
fbinfo->var.bits_per_pixel  = mach_info->bpp.defval;	16设置一个像素点的位数分配,rgb+透明度 rrrrrggggggbbbbb 位数从左边为第0位算
fbinfo->var.red.offset      = 11;
fbinfo->var.green.offset    = 5;
fbinfo->var.blue.offset     = 0;
fbinfo->var.transp.offset   = 0;
fbinfo->var.red.length      = 5;
fbinfo->var.green.length    = 6;
fbinfo->var.blue.length     = 5;
fbinfo->var.transp.length   = 0;fbinfo->var.nonstd	    = 0;   // 标准像素格式
fbinfo->var.activate	    = FB_ACTIVATE_NOW;
// 真实分辨率,设置480*272就行了,影响不大
fbinfo->var.height	    = mach_info->height;
fbinfo->var.width	    = mach_info->width;
fbinfo->var.accel_flags     = 0;
fbinfo->var.vmode	    = FB_VMODE_NONINTERLACED;// VBPD:一个VSYNC信号到来之后,多才时间才开始输出数据(显示图像)。会造成上边黑框
// VFPD:一帧(场)数据结束后多长时间,才来一个VSYNC信号。会造成下边黑框
// VSPW: 一个VSYNC信号的时间宽度
fbinfo->var.upper_margin    = S3C2410_LCDCON2_GET_VBPD(mregs->lcdcon2) + 1;
fbinfo->var.lower_margin    = S3C2410_LCDCON2_GET_VFPD(mregs->lcdcon2) + 1;
fbinfo->var.vsync_len	    = S3C2410_LCDCON2_GET_VSPW(mregs->lcdcon2) + 1;// HFPD:一行数据结束多长时间才来一个HSYNC信号。会造成左边黑框
// HBPD:一个HSYNC信号到来之后,多长时间才开始输出数据(显示图像)。会造成右边黑框
// HSPW: 一个HSYNC信号的时间宽度
fbinfo->var.left_margin	    = S3C2410_LCDCON3_GET_HFPD(mregs->lcdcon3) + 1;
fbinfo->var.right_margin    = S3C2410_LCDCON3_GET_HBPD(mregs->lcdcon3) + 1;
fbinfo->var.hsync_len	    = S3C2410_LCDCON4_GET_HSPW(mregs->lcdcon4) + 1;

4.2.3 其他参数设置

fbinfo->fbops		    = &s3c2410fb_ops;
fbinfo->flags		    = FBINFO_FLAG_DEFAULT;// 设置调色板
fbinfo->pseudo_palette      = &info->pseudo_pal;s3c2410fb_ops:
static struct fb_ops s3c2410fb_ops = {.owner		= THIS_MODULE,.fb_check_var	= s3c2410fb_check_var,.fb_set_par	= s3c2410fb_set_par,.fb_blank	= s3c2410fb_blank,.fb_setcolreg	= s3c2410fb_setcolreg,.fb_fillrect	= cfb_fillrect,.fb_copyarea	= cfb_copyarea,.fb_imageblit	= cfb_imageblit,
};

4.2.4 硬件相关设置(相关寄存器和引脚)

/* Stop the video and unset ENVID if set */
// 设置LCD控制器的lcdcon1寄存器,第0位为0,先失能lcd等设置完其他参数后,需要开启LCD,再使能该位
info->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;
lcdcon1 = readl(S3C2410_LCDCON1);
writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);// 设置GPB0 低电平,即关闭LCD背光,后面需要开启背光
// add by thisway.diy@163.com, for eBlocks
s3c2410_gpio_setpin(S3C2410_GPB0, 0);	// back light control// 设置调色板数据
for (i = 0; i < 256; i++)info->palette_buffer[i] = PALETTE_BUFF_CLEAR;if (!request_mem_region((unsigned long)S3C24XX_VA_LCD, SZ_1M, "s3c2410-lcd")) {ret = -EBUSY;goto dealloc_fb;
}dprintk("got LCD region\n");// 设置LCD中断
ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
if (ret) {dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);ret = -EBUSY;goto release_mem;
}// 使能LCD clk 
info->clk = clk_get(NULL, "lcd");
if (!info->clk || IS_ERR(info->clk)) {printk(KERN_ERR "failed to get lcd clock source\n");ret = -ENOENT;goto release_irq;
}
clk_enable(info->clk);
dprintk("got and enabled clock\n");msleep(1);/* Initialize video memory */
// 申请frame buff内存并初始化
ret = s3c2410fb_map_video_memory(info);
if (ret) {printk( KERN_ERR "Failed to allocate video RAM: %d\n", ret);ret = -ENOMEM;goto release_clock;
}
dprintk("got video memory\n");// 根据s3c2440芯片手册和使用的屏的规格书设置相关寄存器和引脚
ret = s3c2410fb_init_registers(info);ret = s3c2410fb_check_var(&fbinfo->var, fbinfo);
static int s3c2410fb_init_registers(struct s3c2410fb_info *fbi)
{unsigned long flags;/* Initialise LCD with values from haret */local_irq_save(flags);/* modify the gpio(s) with interrupts set (bjd) */// 根据s3c2440芯片手册设置// 设置GPC引脚上拉使能 mach_info->gpcup 0xffffffffmodify_gpio(S3C2410_GPCUP,  mach_info->gpcup,  mach_info->gpcup_mask); // 设置GPC引脚为LCD功能 mach_info->gpccon 0xaaaaaaaa -> 1010 1010 1010 1010 1010 1010 1010 1010modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);// 设置GPD引脚上拉使能  mach_info->gpdup  0xffffffffmodify_gpio(S3C2410_GPDUP,  mach_info->gpdup,  mach_info->gpdup_mask); // 设置GPD引脚为LCD功能 mach_info->gpdcon 0xaaaaaaaamodify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);local_irq_restore(flags);// 根据s3c2440芯片手册设置// lcdcon1: S3C2410_LCDCON1_TFT16BPP | S3C2410_LCDCON1_TFT | S3C2410_LCDCON1_CLKVAL(0x04),writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);// lcdcon2: S3C2410_LCDCON2_VBPD(1) | S3C2410_LCDCON2_LINEVAL(271) | S3C2410_LCDCON2_VFPD(1) | S3C2410_LCDCON2_VSPW(9) writel(fbi->regs.lcdcon2, S3C2410_LCDCON2);// lcdcon3: S3C2410_LCDCON3_HBPD(1) |  S3C2410_LCDCON3_HOZVAL(479) |  S3C2410_LCDCON3_HFPD(1)writel(fbi->regs.lcdcon3, S3C2410_LCDCON3);// lcdcon4: S3C2410_LCDCON4_HSPW(40)writel(fbi->regs.lcdcon4, S3C2410_LCDCON4);// lcdcon5: S3C2410_LCDCON5_FRM565 | S3C2410_LCDCON5_INVVLINE | S3C2410_LCDCON5_INVVFRAME | S3C2410_LCDCON5_PWREN | S3C2410_LCDCON5_HWSWP,writel(fbi->regs.lcdcon5, S3C2410_LCDCON5);// 根据s3c2440芯片手册设置// 设置地址相关寄存器 LCDSADDR1、LCDSADDR2、LCDSADDR3s3c2410fb_set_lcdaddr(fbi);dprintk("LPCSEL    = 0x%08lx\n", mach_info->lpcsel);writel(mach_info->lpcsel, S3C2410_LPCSEL);dprintk("replacing TPAL %08x\n", readl(S3C2410_TPAL));// 不使用调色板/* ensure temporary palette disabled */writel(0x00, S3C2410_TPAL);#if 0	/* ghcstop modified */s3c2410_gpio_cfgpin(S3C2410_GPC5, S3C2410_GPC5_OUTP); // lcd display enable/disables3c2410_gpio_cfgpin(S3C2410_GPB1, S3C2410_GPB1_OUTP); // back light controls3c2410_gpio_cfgpin(S3C2410_GPH6, S3C2410_GPH6_OUTP); s3c2410_gpio_pullup(S3C2410_GPC5, 0); s3c2410_gpio_pullup(S3C2410_GPB1, 0);s3c2410_gpio_pullup(S3C2410_GPH6, 0);s3c2410_gpio_setpin(S3C2410_GPC5, 1);s3c2410_gpio_setpin(S3C2410_GPH6, 1); s3c2410_gpio_setpin(S3C2410_GPB1, 1);
#else/* thisway.diy@163.com modify again, for eBlocks */s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_OUTP); // back light controls3c2410_gpio_pullup(S3C2410_GPB0, 0); s3c2410_gpio_setpin(S3C2410_GPB0, 1);	// back light control, enable
#endif/* probably not required */msleep(10);		// 使能lcd/* Enable video by setting the ENVID bit to 1 */fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID;writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);// add by thisway.diy@163.com, for eBlocks// 开启背光s3c2410_gpio_setpin(S3C2410_GPB0, 1);	// back light controlreturn 0;
}

五,fbmem字符设备驱动

5.1 驱动加载并初始化

编译进内核,加载

linux-2.6.22.6/drivers/video/Makefile

fb-y                              := fbmem.o fbmon.o fbcmap.o fbsysfs.o \modedb.o fbcvt.o
fb-objs                           := $(fb-y)

linux-2.6.22.6/drivers/video/fbmem.c

subsys_initcall(fbmem_init);
static int __init
fbmem_init(void)
{create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL);// 注册字符设备if (register_chrdev(FB_MAJOR,"fb",&fb_fops))printk("unable to get major %d for fb devs\n", FB_MAJOR);// 创建设备类 /sys/class/graphicsfb_class = class_create(THIS_MODULE, "graphics");if (IS_ERR(fb_class)) {printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));fb_class = NULL;}return 0;
}

5.2 register_framebuffer

int
register_framebuffer(struct fb_info *fb_info)
{int i;struct fb_event event;struct fb_videomode mode;// 最多支持32个fb设备if (num_registered_fb == FB_MAX)return -ENXIO;num_registered_fb++;// 从registered_fb中找到一项空的位置,存放本次fb。下标作为次设备号,创建设备节点,以供应用程序使用for (i = 0 ; i < FB_MAX; i++)if (!registered_fb[i])break;fb_info->node = i;// 创建设备 节点。/dev/fb%dfb_info->dev = device_create(fb_class, fb_info->device,MKDEV(FB_MAJOR, i), "fb%d", i);if (IS_ERR(fb_info->dev)) {/* Not fatal */printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));fb_info->dev = NULL;} elsefb_init_device(fb_info);if (fb_info->pixmap.addr == NULL) {fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);if (fb_info->pixmap.addr) {fb_info->pixmap.size = FBPIXMAPSIZE;fb_info->pixmap.buf_align = 1;fb_info->pixmap.scan_align = 1;fb_info->pixmap.access_align = 32;fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;}}	fb_info->pixmap.offset = 0;if (!fb_info->pixmap.blit_x)fb_info->pixmap.blit_x = ~(u32)0;if (!fb_info->pixmap.blit_y)fb_info->pixmap.blit_y = ~(u32)0;if (!fb_info->modelist.prev || !fb_info->modelist.next)INIT_LIST_HEAD(&fb_info->modelist);fb_var_to_videomode(&mode, &fb_info->var);fb_add_videomode(&mode, &fb_info->modelist);registered_fb[i] = fb_info;event.info = fb_info;fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);return 0;
}

六,一次LCD显示的过程

应用程序app: file = open("/dev/fb0",0)
将要显示的图像数据,使用write接口写入,
write->fb_write->// 如果lcd驱动的fbops中存在write函数,则使用lcd驱动中的数据if (info->fbops->fb_write)return info->fbops->fb_write(info, buf, count, ppos);否则使用fb_write写到显存中产生一个LCD中断,进入中断处理函数static irqreturn_t s3c2410fb_irq(int irq, void *dev_id){struct s3c2410fb_info *fbi = dev_id;unsigned long lcdirq = readl(S3C2410_LCDINTPND);if (lcdirq & S3C2410_LCDINT_FRSYNC) {if (fbi->palette_ready)s3c2410fb_write_palette(fbi);// 控制LCD控制器开始从显存中取数据传输到LCD屏,显示图像writel(S3C2410_LCDINT_FRSYNC, S3C2410_LCDINTPND);writel(S3C2410_LCDINT_FRSYNC, S3C2410_LCDSRCPND);}return IRQ_HANDLED;}static struct fb_ops s3c2410fb_ops = {.owner		= THIS_MODULE,.fb_check_var	= s3c2410fb_check_var,.fb_set_par	= s3c2410fb_set_par,.fb_blank	= s3c2410fb_blank,.fb_setcolreg	= s3c2410fb_setcolreg,.fb_fillrect	= cfb_fillrect,.fb_copyarea	= cfb_copyarea,.fb_imageblit	= cfb_imageblit,
};

七,其他处理函数和逻辑

待定


http://www.ppmy.cn/news/141003.html

相关文章

A40I led driver

LDE DRIVER 亲试有效 #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/io.h> #include <linux/cdev…

ati hd 6470m驱动

ati hd 6470m驱动是官方提供的一款显卡驱动&#xff0c;本站收集提供高速下载&#xff0c;用于解决显卡不能正常运行&#xff0c;无法正常使用的问题&#xff0c;本动适用于&#xff1a;Windows XP / Windows 7 / Windows 8 / Windows 10 32/64位操作系统。有需要的朋友可以来本…

Linux-2440下的DMA驱动(详解)

DMA(Direct Memory Access) 即直接存储器访问&#xff0c; DMA 传输方式无需 CPU 直接控制传输&#xff0c;通过硬件为 RAM 、I/O 设备开辟一条直接传送数据的通路&#xff0c;能使 CPU 的效率大为提高。 学了这么多驱动&#xff0c;不难推出DMA的编写套路: 1)注册DMA中断,分…

DALSA Xtium系列MX4采集卡和AVAL GLOBAL APX-3324A采集卡参数对比

DALSA Xtium系列MX4采集卡和AVAL GLOBAL APX-3324A采集卡参数对比 DALSA Xtium系列采集卡Xtium-CL MX4 Teledyne DALSA公司已正式发布Xtium CL MX4采集卡&#xff0c;它采用了工业级PCI Express™ Gen 2.0标准&#xff0c;是Xtium采集卡系列中的新成员。Xtium-CLMX4采集卡基于…

PCF8574AT驱动LCD1602

STM32用IIC驱动LCD1602 介绍PCF8574AT选址芯片接线图 LCD1602显示问题乱码问题 实现代码 介绍 网上寻遍了资料发现驱动LCD1602液晶屏的程序几乎都是4线或8线数据线。基本思路都是直接操作I/O&#xff0c;但是缺点是接线太多&#xff0c;十分麻烦。PCF8574和PCF8574AT这两种芯片…

TI 2640 Quick start

&#xff08;1&#xff09;下载ccs 并且安装 &#xff08;2&#xff09;下载对应版本的SDK&#xff0c;并且安装 SDK对应版本下载可以通过搜索的方式&#xff1a;simplelink_cc2640r2_sdk_5_30_00_03 &#xff08;3&#xff09;导入对应的工程文件 此篇文档可以作为参考&#x…

[其他] ATI HD6630M 显卡在Win10下终于有救了(DEll 14R N4120)

重要提示&#xff01;&#xff01;&#xff01; 以前没有仔细的研究&#xff0c;今天重装了一遍Win10。然后重新安装了显卡驱动&#xff0c;现更新一些信息 0. 本教程不适用于索尼AMD双卡本&#xff0c;索尼双卡在AMD驱动下有Intel显卡信息&#xff0c;公版没有。&#xff08;…

Java程序猿搬砖笔记(十三)

文章目录 MySQL数据库生成自动增长序号MySQL修改密码SpringBoot定时任务解决Mybatis出现的各种Parameter not found. Available parameters are [ , ]Mybatis的foreach标签遍历mapSpringBoot项目打包SpringBoot Async使用注意事项Spring Cloud Config bootstrap文件&#xff…