linux驱动之LCD

开发板:TQ2440开发板 (TQ4.3寸屏)

内核:linux-2.6.30.4

lcd驱动程序:

###########################################################################
假设APP:open("/dev/fbo"...)主设备号29,次设备号0
-------------------------------------------------------
就会找到fb_open函数
kernel:
    fb_open
        int fbidx = iminor(inode);
        struct fb_info *info = registered_fb[fbidx];

假设AP:read
-------------------------------------------------------
kernel:
    fb_read
        int fbidx = iminor(inode);
        struct fb_info * info = registered_fb[fbidx];
        if (info->fbops->fb_read)
            return info->fbops->fb_read(info, buf, count, ppos);
        
        src = (u32 __iomem *) (info->screen_base + p);
        dst = buffer;
        *dst++ = fb_readl(src++);
        copy_to_user(buf, buffer, c)

问:registered_fb 在哪里被设置?

register_framebuffer

###########################################################################

怎么写lcd驱动程序




1、分配一个fb_info结构体

fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);

2、设置

3、注册

register_framebuffer

4、硬件相关的操作

配置引脚用于LCD
根据LCD手册,设置LCD控制器

分配显存并把地址告诉LCD控制器

--------------------------------------------------------------------------------------------------------------------------------------

开始写lcd驱动程序:

1、分配一个fb_info结构体:

static struct fb_info  *s3c_lcd;
1、初始化这个结构体:

s3c_lcd = framebuffer_alloc(0,NULL);

这个函数原型: struct fb_info *framebuffer_alloc(size_t size, struct device *dev) 
这个函数的核心的作用是申请一块空间,并转换为struct fb_info 指针返回,在这个申请过程私有数据空间size并非是必须的,需要的话可以有但并非必须。


2、设置这个fb_info结构体:

2.1固定参数的设置:

    strcpy(s3c_lcd->fix.id, "mylcd");
    s3c_lcd->fix.smem_len = 480*272*32/8;
    s3c_lcd->fix.type       = FB_TYPE_PACKED_PIXELS;
    s3c_lcd->fix.visual   = FB_VISUAL_TRUECOLOR;/*TFT*/
    s3c_lcd->fix.line_length =480*4;

说明:

fix.smem_len显存的长度。

fix.line_length 是一行的长度。  一个像素占4个字节

2.2可变参数的设置:

	s3c_lcd->var.xres		= 480;//分辨率
	s3c_lcd->var.yres	  	= 272;
	s3c_lcd->var.xres_virtual 	= 480;//虚拟分辨率
	s3c_lcd->var.yres_virtual 	= 272;
	s3c_lcd->var.bits_per_pixel = 32;

	s3c_lcd->var.red.offset		= 16;
	s3c_lcd->var.red.length		= 8;

	s3c_lcd->var.green.offset	= 8;
	s3c_lcd->var.green.length	= 8;

	s3c_lcd->var.blue.offset	= 0;
	s3c_lcd->var.blue.length	= 8;

	s3c_lcd->var.activate 		=FB_ACTIVATE_NOW;

说明:

bits_per_pixel 是每个像素点占的字节

红绿蓝三原色分配如下:

redgreenblue
888
一般都是RGB 565的,不知这个屏幕为何这么奇葩??

所以红绿蓝的设置就如上所示了。

2.3.设置操作函数:

s3c_lcd->fbops 	= &s3cfb_ops;

操作函数的结构体定义如下:

static struct fb_ops s3cfb_ops = {
	.owner		= THIS_MODULE,
	.fb_setcolreg	= s3c2410fb_setcolreg,
	.fb_fillrect	= cfb_fillrect,
	.fb_copyarea	= cfb_copyarea,
	.fb_imageblit	= cfb_imageblit,
};
说明:

s3c2410fb_setcolreg为调色板函数。调色板是显存和LCD中间的介质,调色板相当于用很多中调好的颜色,使用索引值可以将其访问到。

调色板函数定义如下;

static int s3c2410fb_setcolreg(unsigned regno,
			       unsigned red, unsigned green, unsigned blue,
			       unsigned transp, struct fb_info *info)
{
	unsigned int val;
	if(regno>16)
		return 1;
	else
	{
		//用红绿蓝三原色
		val  = chan_to_field(red,   &info->var.red);
		val |= chan_to_field(green, &info->var.green);
		val |= chan_to_field(blue,  &info->var.blue);
	}
	pseudo_palette[regno] = val;
	return 0;

}

cfb_fillrect
cfb_copyarea
cfb_imageblit
这三个函数可以比较复杂,可以使用2410驱动编译出来的模块,这里就不去实现他。

3、硬件相关的操作:

3.1、进行映射:

映射io口

映射LCD寄存器

3.2根据LCD手册配置LCD寄存器:

lcd_regsp = ioremap(0x4D000000,sizeof(struct lcd_regs));

	/*lcdcon1
	 * bit[17:8]   VCLK = VCLK = HCLK / [(CLKVAL+1) x 2]
	 *			  VCLK = 100MHz/[(CLKVAL+1) x 2]
	 * 				10M= 100MHz/[(CLKVAL+1) x 2]
	 * 				CLKVAL=4
	 * bit[6:5]:	0b11 : TFT
	 * bit[4:1]     BBPMODE  0b1101 = 24bbp
	 * bit[0]       0 = Disable the video output and the LCD control signal.
	 */
	lcd_regsp->lcdcon1 	= (4<<8)|(3<<5)|(0x0d<<1);

	/*lcdcon2  垂直方向的时间参数
	 * bit[31:24] VBPD ,VSYNC之后再过多久才能发出第一行数据
	 * 			LCD手册  tvb =2
	 * 			VBPD=1;
	 * bit[23:14] 272行   LINEVAL=272-1=271
	 * bit[13:6] VFPD发出最后一行数据之后再过多长时间发出 VSYNC
	 * 			LCD手册  tvf = 2
	 * 			VFPD =2-1=1
	 * bit[5:0]:VSPW VSYNC信号的脉冲宽度,LCD手册 tvp=10;所以 VSPW =10-1 =9
	 */
	lcd_regsp->lcdcon2 = (1<<24)|(271<<14)|(1<<6)|(9<<0);

	/*lcdcon3  水平方向的时间参数
	 * bit [25:19]HBPD,VSYNc之后过多长时间才能发出第一行数据
	 * 				LCD手册 thb =2
	 * 				HBPD =1
	 * bit[18:8] 480列, HPZVAL=480-1 =479
	 * bit[7:0]  HFPD 发出最后一行,最后一个数据之后,再过多长时间俺发出 HSYNC
	 * 				LCD手册 thf=2 所以 HFPD=2-1=1
	 */
	lcd_regsp->lcdcon3 =(1<<19)|(479<<8)|(1<<0);

	/*水平方向的同步信号
	 * bit[7:0] HSPW,HSYNC信号的脉冲宽度 LCD手册 THp=41,所以HSPW =41-1=40
	 */
	lcd_regsp->lcdcon4 = 40;


	/*信号的极性
	 * bit[11] 1=RGB565 format  24bpp不用设
	 * bit[10] 0 = The video data is fetched at VCLK falling edge
	 * bit[9]  1 = HSYNC 信号要反转
	 * bit[8]  1 = VSYNC 信号要反转
	 * bit[6]  0 = VDEN  不需要反转
	 * bit[3]  0 = PWREN 输出零
	 * bit[1]  0 = BSWP
	 * bit[0]  0 = HWSWP
	 */
	lcd_regsp->lcdcon5= (1<<9)|(1<<8);
	/*3.3分配framebuffer,并把地址告诉LCD控制器*/
	s3c_lcd->screen_base = dma_alloc_writecombine(NULL,s3c_lcd->fix.smem_len,&s3c_lcd->fix.smem_start,GFP_KERNEL);
	lcd_regsp->lcdsaddr1 = (s3c_lcd->fix.smem_start>>1) & ~(3<<30);
	lcd_regsp->lcdsaddr2 = ((s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len)>>1) & 0x1fffff;
	lcd_regsp->lcdsaddr3 = (480 *32/16); //一行的长度(单位 :2字节)

	/*启动lcd*/
	lcd_regsp->lcdcon1 |=(1<<0);//使能LCD控制器
	lcd_regsp->lcdcon5 |=(1<<3);//使能LCD本身

4、注册frame_buffer

register_framebuffer(s3c_lcd);

完整的驱动代码:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/clk.h>

#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/div64.h>

#include <asm/mach/map.h>
#include <mach/regs-lcd.h>
#include <mach/regs-gpio.h>
#include <mach/fb.h>
#include <linux/gfp.h>


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

struct lcd_regs {
	unsigned long	lcdcon1;
	unsigned long	lcdcon2;
	unsigned long	lcdcon3;
	unsigned long	lcdcon4;
	unsigned long	lcdcon5;
    unsigned long	lcdsaddr1;
    unsigned long	lcdsaddr2;
    unsigned long	lcdsaddr3;
    unsigned long	redlut;
    unsigned long	greenlut;
    unsigned long	bluelut;
    unsigned long	reserved[9];
    unsigned long	dithmode;
    unsigned long	tpal;
    unsigned long	lcdintpnd;
    unsigned long	lcdsrcpnd;
    unsigned long	lcdintmsk;
    unsigned long	lpcsel;
};

static volatile struct lcd_regs *lcd_regsp;

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

static u32 pseudo_palette[16];
static volatile unsigned long *gpbcon;
static volatile unsigned long *gpbdat;
static volatile unsigned long *gpccon;
static volatile unsigned long *gpdcon;
static volatile unsigned long *gpgcon;

static struct fb_info  *s3c_lcd;
/* 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 s3c2410fb_setcolreg(unsigned regno,
			       unsigned red, unsigned green, unsigned blue,
			       unsigned transp, struct fb_info *info)
{
	unsigned int val;
	if(regno>16)
		return 1;
	else
	{
		//用红绿蓝三原色
		val  = chan_to_field(red,   &info->var.red);
		val |= chan_to_field(green, &info->var.green);
		val |= chan_to_field(blue,  &info->var.blue);
	}
	pseudo_palette[regno] = val;
	return 0;

}
static int lcd_init(void)
{
	int ret;
	/*1、分配一个fb_info结构体*/
	s3c_lcd = framebuffer_alloc(0,NULL);
	if (!s3c_lcd)
			return -ENOMEM;
	/*2、设置*/
	/*2.1设置固定的参数:*/
	strcpy(s3c_lcd->fix.id, "mylcd");
	s3c_lcd->fix.smem_len = 480*272*32/8;
	s3c_lcd->fix.type 	  = FB_TYPE_PACKED_PIXELS;
	s3c_lcd->fix.visual   = FB_VISUAL_TRUECOLOR;/*TFT*/
	s3c_lcd->fix.line_length =480*4;

	/*2.2设置可变的参数*/
	s3c_lcd->var.xres			= 480;//分辨率
	s3c_lcd->var.yres	  		= 272;
	s3c_lcd->var.xres_virtual 	= 480;//虚拟分辨率
	s3c_lcd->var.yres_virtual 	= 272;
	s3c_lcd->var.bits_per_pixel = 32;

	s3c_lcd->var.red.offset		= 16;
	s3c_lcd->var.red.length		= 8;

	s3c_lcd->var.green.offset		= 8;
	s3c_lcd->var.green.length		= 8;

	s3c_lcd->var.blue.offset		= 0;
	s3c_lcd->var.blue.length		= 8;

	//s3c_lcd->var.activate 		=FB_ACTIVATE_NOW;//
	/*2.3设置操作函数*/
	s3c_lcd->fbops 				= &s3cfb_ops;

	/*2.4其他的设置*/
	s3c_lcd->pseudo_palette 	= pseudo_palette;
	s3c_lcd->screen_size		= 480*272*32/8;
	/*3、硬件相关的操作*/
	/*3.1配置GPIO用于LCD*/
	gpbcon = ioremap(0x56000010,8);
	gpbdat = gpbcon+1;
	gpccon = ioremap(0x56000020,4);
	gpdcon = ioremap(0x56000030,4);
	gpgcon = ioremap(0x56000060,4);

	*gpccon = 0xaaaaaaaa;//
	*gpdcon = 0xaaaaaaaa;

	*gpgcon	|=(3<<8);//lcd power
	/*3.2根据LCD手册设置LCD控制器,频率等*/
	lcd_regsp = ioremap(0x4D000000,sizeof(struct lcd_regs));

	/*lcdcon1
	 * bit[17:8]   VCLK = VCLK = HCLK / [(CLKVAL+1) x 2]
	 *			  VCLK = 100MHz/[(CLKVAL+1) x 2]
	 * 				10M= 100MHz/[(CLKVAL+1) x 2]
	 * 				CLKVAL=4
	 * bit[6:5]:	0b11 : TFT
	 * bit[4:1]     BBPMODE  0b1101 = 24bbp
	 * bit[0]       0 = Disable the video output and the LCD control signal.
	 */
	lcd_regsp->lcdcon1 	= (4<<8)|(3<<5)|(0x0d<<1);

	/*lcdcon2  垂直方向的时间参数
	 * bit[31:24] VBPD ,VSYNC之后再过多久才能发出第一行数据
	 * 			LCD手册  tvb =2
	 * 			VBPD=1;
	 * bit[23:14] 272行   LINEVAL=272-1=271
	 * bit[13:6] VFPD发出最后一行数据之后再过多长时间发出 VSYNC
	 * 			LCD手册  tvf = 2
	 * 			VFPD =2-1=1
	 * bit[5:0]:VSPW VSYNC信号的脉冲宽度,LCD手册 tvp=10;所以 VSPW =10-1 =9
	 */
	lcd_regsp->lcdcon2 = (1<<24)|(271<<14)|(1<<6)|(9<<0);

	/*lcdcon3  水平方向的时间参数
	 * bit [25:19]HBPD,VSYNc之后过多长时间才能发出第一行数据
	 * 				LCD手册 thb =2
	 * 				HBPD =1
	 * bit[18:8] 480列, HPZVAL=480-1 =479
	 * bit[7:0]  HFPD 发出最后一行,最后一个数据之后,再过多长时间俺发出 HSYNC
	 * 				LCD手册 thf=2 所以 HFPD=2-1=1
	 */
	lcd_regsp->lcdcon3 =(1<<19)|(479<<8)|(1<<0);

	/*水平方向的同步信号
	 * bit[7:0] HSPW,HSYNC信号的脉冲宽度 LCD手册 THp=41,所以HSPW =41-1=40
	 */
	lcd_regsp->lcdcon4 = 40;


	/*信号的极性
	 * bit[11] 1=RGB565 format  24bpp不用设
	 * bit[10] 0 = The video data is fetched at VCLK falling edge
	 * bit[9]  1 = HSYNC 信号要反转
	 * bit[8]  1 = VSYNC 信号要反转
	 * bit[6]  0 = VDEN  不需要反转
	 * bit[3]  0 = PWREN 输出零
	 * bit[1]  0 = BSWP
	 * bit[0]  0 = HWSWP
	 */
	lcd_regsp->lcdcon5= (1<<9)|(1<<8);
	/*3.3分配framebuffer,并把地址告诉LCD控制器*/
	s3c_lcd->screen_base = dma_alloc_writecombine(NULL,s3c_lcd->fix.smem_len,&s3c_lcd->fix.smem_start,GFP_KERNEL);
	lcd_regsp->lcdsaddr1 = (s3c_lcd->fix.smem_start>>1) & ~(3<<30);
	lcd_regsp->lcdsaddr2 = ((s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len)>>1) & 0x1fffff;
	lcd_regsp->lcdsaddr3 = (480 *32/16); //一行的长度(单位 :2字节)

	/*启动lcd*/
	lcd_regsp->lcdcon1 |=(1<<0);//使能LCD控制器
	lcd_regsp->lcdcon5 |=(1<<3);//使能LCD本身


	/*4、注册*/
	ret = register_framebuffer(s3c_lcd);
		if (ret < 0) {
			printk(KERN_ERR "Failed to register framebuffer device: %d\n",ret);
		}
	return 0;
}

static void lcd_exit(void)
{
	unregister_framebuffer(s3c_lcd);
	lcd_regsp->lcdcon1 &=~(1<<0);//关闭LCD控制器
	lcd_regsp->lcdcon5 &=~(1<<3);//关闭LCD本身
	dma_free_writecombine(NULL,s3c_lcd->fix.smem_len,s3c_lcd->screen_base,s3c_lcd->fix.smem_start);

	iounmap(lcd_regsp);
	iounmap(gpbcon);
	iounmap(gpccon);
	iounmap(gpdcon);
	iounmap(gpgcon);

	framebuffer_release(s3c_lcd);
}


module_init(lcd_init);
module_exit(lcd_exit);

MODULE_LICENSE("GPL");

测试:

重新编译内核:

make menuconfig

 Device Drivers  ---> 
               Graphics support  --->  
                        <*> Support for frame buffer devices  ---> 
                                    <M>   S3C2410 LCD framebuffer support         
S3C2410 LCD framebuffer support    这一项改成M,将其生成模块。以使用:

cfb_fillrect
cfb_copyarea
cfb_imageblit

开发板重新烧写这个内核。加载 cfb*这三个驱动。然后加载:lcd的驱动。测试效果


使用以下命令:

cat a.txt >/dev/fb0    察看是否花屏

echo helloworld > /dev/tty1   察看能否在屏幕上看到helloworld
















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值