linux LCD 驱动编写(四)

Table of Contents

一、应用程序访问驱动原理

二、LCD驱动编写步骤

2.1、.分配fb_info

2.2、初始化fb_info(var   fix)

2.3、硬件寄存器操作

2.4、显存设置

2.5、注册

三、LCD驱动代码


一、应用程序访问驱动原理

LCD驱动程序

假设
app:  open("/dev/fb0", ...)   主设备号: 29, 次设备号: 0
--------------------------------------------------------------
kernel:
         fb_open
             int fbidx = iminor(inode);
             struct fb_info *info = = registered_fb[0];

app:  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)   

问1. registered_fb在哪里被设置?
答1. register_framebuffer

二、LCD驱动编写步骤

怎么写LCD驱动程序? linux 自带LCD驱动框架(三)
1. 分配一个fb_info结构体: framebuffer_alloc

2. 初始化填充fb_info 成员    重要结构体解析

3. 硬件相关的操作   LCD数据手册解析

4. 注册: register_framebuffer
 

2.1、.分配fb_info

s5pv210_lcd = framebuffer_alloc(0, NULL);

2.2、初始化fb_info(var   fix)

// 3.初始化fb_info
    // 3.1初始化屏幕的固定参数信息
    strcpy(s5pv210_lcd->fix.id, "s5pv210_lcd");
    s5pv210_lcd->fix.smem_len = 800*480*4; //480*272*2
    s5pv210_lcd->fix.type = FB_TYPE_PACKED_PIXELS;
    s5pv210_lcd->fix.visual = FB_VISUAL_TRUECOLOR;
    s5pv210_lcd->fix.line_length = 800*4; //480*2

    // 3.2初始化屏幕的可变参数信息
    s5pv210_lcd->var.xres             = 800; //480     开发板的屏幕是800*480    注释部分为480*272的屏幕
    s5pv210_lcd->var.yres             = 480; //272
    s5pv210_lcd->var.xres_virtual  = 800;  //480
    s5pv210_lcd->var.yres_virtual  = 480;  //272
    s5pv210_lcd->var.bits_per_pixel = 32; //16
    s5pv210_lcd->var.red.offset          = 16; //11
    s5pv210_lcd->var.red.length         = 8;//5
    s5pv210_lcd->var.green.offset       = 8; //5
    s5pv210_lcd->var.green.length      = 8; //6
    s5pv210_lcd->var.blue.offset         = 0; //0
    s5pv210_lcd->var.blue.length        = 8; //5
    s5pv210_lcd->var.activate             = FB_ACTIVATE_NOW;

2.3、硬件寄存器操作

    // 4.硬件初始化
    // 4.1GPIO的复用处理
    gpf0con = ioremap(0xE0200120,4);
    gpf1con = ioremap(0xE0200140,4);
    gpf2con = ioremap(0xE0200160,4);
    gpf3con = ioremap(0xE0200180,4);
    *gpf0con = 0x22222222;        // GPF0[7:0]
    *gpf1con = 0x22222222;        // GPF1[7:0]
    *gpf2con = 0x22222222;        // GPF2[7:0]
    *gpf3con = 0x22222222;        // GPF3[7:0]

    // 4.2初始化LCD控制器相关的寄存器
    vidcon0 = ioremap(0xF8000000,4);
    vidcon1 = ioremap(0xF8000004,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);
    vidw00add2  = ioremap(0xF8000100,4);
    vidtcon0  = ioremap(0xF8000010,4);
    vidtcon1  = ioremap(0xF8000014,4);
    vidtcon2  = ioremap(0xF8000018,4);
    wpalcon  = ioremap(0xF80001A0,4);
    shadowcon  = ioremap(0xF8000034,4);
    
    *vidcon0 &= ~((3<<26) | (1<<18) | (0xff<<6)  | (1<<2));     /* RGB I/F, RGB Parallel format,  */
    *vidcon0 |= ((4<<6) | (1<<4) );      /* Divided by CLKVAL_F,vclk== HCLK / (CLKVAL+1) = 166.75/5 = 33.35MHz */
    *vidcon1 &= ~(1<<7);   
    *vidcon1 |= ((1<<6) | (1<<5));  

    // 这个硬件时序初始化一定要会换算!
    // 一旦屏幕发生变化,这些参数也要重新换算
    *vidtcon0 = (VBPD << 16) | (VFPD << 8) | (VSPW << 0);
    *vidtcon1 = (HBPD << 16) | (HFPD << 8) | (HSPW << 0);

    *vidtcon2 = (LINEVAL << 11) | (HOZVAL << 0);
    *wincon0 &= ~(0xf << 2);
    *wincon0 |= (0xB<<2)/*|(1<<15)*/;
    *vidosd0a = (LeftTopX<<11) | (LeftTopY << 0);
    *vidosd0b = (RightBotX<<11) | (RightBotY << 0);
    *vidosd0c = (LINEVAL + 1) * (HOZVAL + 1);

2.4、显存设置

// 3.5 让内核帮你分配显存的起始物理地址和对应的内核虚拟地址
    //内核分配好以后,把显存的物理地址保存在fix.smem_start
    //对应的显存的内核虚拟地址保存在screen_base
    s5pv210_lcd->screen_base = dma_alloc_writecombine(NULL, 
                                    s5pv210_lcd->fix.smem_len, 
                                    (dma_addr_t *)&s5pv210_lcd->fix.smem_start, 
                                    GFP_KERNEL);

    //告诉CPU显存的起始物理地址和结束物理地址
    *vidw00add0b0 = s5pv210_lcd->fix.smem_start
    *vidw00add1b0 = s5pv210_lcd->fix.smem_start 
                        + s5pv210_lcd->fix.smem_len

2.5、注册

//向核心层注册分配初始化好的fb_info
    register_framebuffer(s5pv210_lcd);

三、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 <linux/cpufreq.h>
#include <asm/io.h>
#include <asm/div64.h>

#include <asm/mach/map.h>
#include <mach/regs-gpio.h>

#define MHZ (1000*1000)
#define PRINT_MHZ(m) 			((m) / MHZ), ((m / 1000) % 1000)
#define VSPW       9  
#define VBPD       13 
#define LINEVAL    479
#define VFPD       21 

#define HSPW       19 
#define HBPD       25 
#define HOZVAL     799
#define HFPD       209

#define LeftTopX     0
#define LeftTopY     0
#define RightBotX   799
#define RightBotY   479

static unsigned long *vidcon0;	/* video control 0 */                          
static unsigned long *vidcon1;	/* video control 1 */                          
//static unsigned long *vidcon2;	/* video control 2 */                          
static unsigned long *vidtcon0; /* video time control 0 */                   
static unsigned long *vidtcon1; /* video time control 1 */                   
static unsigned long *vidtcon2; /* video time control 2 */
static unsigned long *wincon0;	/* window control 0 */                         
static unsigned long *vidosd0a;	/* video window 0 position control */        
static unsigned long *vidosd0b;	/* video window 0 position control1 */       
static unsigned long *vidosd0c;	/* video window 0 position control */
static unsigned long *vidw00add0b0; 	/* window 0 buffer start address, buffer 0 */
static unsigned long *vidw00add1b0; 	/* window 0 buffer end address, buffer 0 */  
static unsigned long *vidw00add2; 	/* window 0 buffer size */   
static unsigned long *wpalcon;
static unsigned long *shadowcon;

static unsigned long *gpf0con;
static unsigned long *gpf1con;
static unsigned long *gpf2con;
static unsigned long *gpf3con;
//static unsigned long *gpd0con;
//static unsigned long *gpd0dat;
//static unsigned long *clk_gate_block;
//static unsigned long *display_control;

static struct fb_info *s5pv210_lcd;

static struct fb_ops s5pv210_lcdfb_ops = {
	.owner		= THIS_MODULE,
	.fb_fillrect	= cfb_fillrect, //填充矩形
	.fb_copyarea	= cfb_copyarea, //显存区域的拷贝
	.fb_imageblit	= cfb_imageblit, //处理图像
};

static int s5pv210_lcd_init(void)
{
	// 1.获取LCD控制器的时钟,并且启动LCD控制器时钟
	struct clk		*s5pv210_clk;
	s5pv210_clk = clk_get(NULL, "lcd");
	clk_enable(s5pv210_clk);

	// 2.分配fb_info
	s5pv210_lcd = framebuffer_alloc(0, NULL);

	// 3.初始化fb_info
	// 3.1初始化屏幕的固定参数信息
	strcpy(s5pv210_lcd->fix.id, "s5pv210_lcd");
	s5pv210_lcd->fix.smem_len = 800*480*4; //480*272*2
	s5pv210_lcd->fix.type = FB_TYPE_PACKED_PIXELS;
	s5pv210_lcd->fix.visual = FB_VISUAL_TRUECOLOR;
	s5pv210_lcd->fix.line_length = 800*4; //480*2

	// 3.2初始化屏幕的可变参数信息
	s5pv210_lcd->var.xres             = 800; //480
	s5pv210_lcd->var.yres             = 480; //272
	s5pv210_lcd->var.xres_virtual  = 800;  //480
	s5pv210_lcd->var.yres_virtual  = 480;  //272
	s5pv210_lcd->var.bits_per_pixel = 32; //16
	s5pv210_lcd->var.red.offset          = 16; //11
	s5pv210_lcd->var.red.length         = 8;//5
	s5pv210_lcd->var.green.offset       = 8; //5
	s5pv210_lcd->var.green.length      = 8; //6
	s5pv210_lcd->var.blue.offset         = 0; //0
	s5pv210_lcd->var.blue.length        = 8; //5
	s5pv210_lcd->var.activate             = FB_ACTIVATE_NOW;

	// 3.3提供操作显存的接口
	s5pv210_lcd->fbops = &s5pv210_lcdfb_ops;

	// 3.4 初始化屏幕的大小
	s5pv210_lcd->screen_size = 800*480*4;

	// 4.硬件初始化
	// 4.1GPIO的复用处理
	gpf0con = ioremap(0xE0200120,4);
	gpf1con = ioremap(0xE0200140,4);
	gpf2con = ioremap(0xE0200160,4);
	gpf3con = ioremap(0xE0200180,4);
	*gpf0con = 0x22222222;		// GPF0[7:0]
	*gpf1con = 0x22222222;		// GPF1[7:0]
	*gpf2con = 0x22222222;		// GPF2[7:0]
	*gpf3con = 0x22222222;		// GPF3[7:0]

	// 4.2初始化LCD控制器相关的寄存器
	vidcon0 = ioremap(0xF8000000,4);
	vidcon1 = ioremap(0xF8000004,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);
	vidw00add2  = ioremap(0xF8000100,4);
	vidtcon0  = ioremap(0xF8000010,4);
	vidtcon1  = ioremap(0xF8000014,4);
	vidtcon2  = ioremap(0xF8000018,4);
	wpalcon  = ioremap(0xF80001A0,4);
	shadowcon  = ioremap(0xF8000034,4);
	
	*vidcon0 &= ~((3<<26) | (1<<18) | (0xff<<6)  | (1<<2));     /* RGB I/F, RGB Parallel format,  */
	*vidcon0 |= ((4<<6) | (1<<4) );      /* Divided by CLKVAL_F,vclk== HCLK / (CLKVAL+1) = 166.75/5 = 33.35MHz */
	*vidcon1 &= ~(1<<7);   
	*vidcon1 |= ((1<<6) | (1<<5));  

	// 这个硬件时序初始化一定要会换算!
	// 一旦屏幕发生变化,这些参数也要重新换算
	*vidtcon0 = (VBPD << 16) | (VFPD << 8) | (VSPW << 0);
	*vidtcon1 = (HBPD << 16) | (HFPD << 8) | (HSPW << 0);

	*vidtcon2 = (LINEVAL << 11) | (HOZVAL << 0);
	*wincon0 &= ~(0xf << 2);
	*wincon0 |= (0xB<<2)/*|(1<<15)*/;
	*vidosd0a = (LeftTopX<<11) | (LeftTopY << 0);
	*vidosd0b = (RightBotX<<11) | (RightBotY << 0);
	*vidosd0c = (LINEVAL + 1) * (HOZVAL + 1);

	// 3.5 让内核帮你分配显存的起始物理地址和对应的内核虚拟地址
	//内核分配好以后,把显存的物理地址保存在fix.smem_start
	//对应的显存的内核虚拟地址保存在screen_base
	s5pv210_lcd->screen_base = dma_alloc_writecombine(NULL, 
									s5pv210_lcd->fix.smem_len, 
									(dma_addr_t *)&s5pv210_lcd->fix.smem_start, 
									GFP_KERNEL);

	//告诉CPU显存的起始物理地址和结束物理地址
	*vidw00add0b0 = s5pv210_lcd->fix.smem_start; 
	*vidw00add1b0 = s5pv210_lcd->fix.smem_start 
						+ s5pv210_lcd->fix.smem_len; 

	*shadowcon = 0x1;
	*vidcon0 |= 0x3; 
	*wincon0 |= 1;   

	//向核心层注册分配初始化好的fb_info
	register_framebuffer(s5pv210_lcd);
	return 0;
}

static void s5pv210_lcd_exit(void)
{
	unregister_framebuffer(s5pv210_lcd);
	dma_free_writecombine(NULL, 
						s5pv210_lcd->fix.smem_len, 
						s5pv210_lcd->screen_base, 
						s5pv210_lcd->fix.smem_start);
	iounmap(gpf0con);
	iounmap(gpf1con);
	iounmap(gpf2con);
	iounmap(gpf3con);
	//iounmap(gpd0con);
	//iounmap(gpd0dat);
	//iounmap(display_control);
	iounmap(vidcon0);
	iounmap(vidcon1);
	iounmap(vidtcon2);
	iounmap(wincon0);
	iounmap(vidosd0a);
	iounmap(vidosd0b);
	iounmap(vidosd0c);
	iounmap(vidw00add0b0);
	iounmap(vidw00add1b0);
	iounmap(vidw00add2);
	iounmap(vidtcon0);
	iounmap(vidtcon1);
	iounmap(shadowcon);
	framebuffer_release(s5pv210_lcd);
}

module_init(s5pv210_lcd_init);
module_exit(s5pv210_lcd_exit);

MODULE_LICENSE("GPL");
发布了135 篇原创文章 · 获赞 112 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/shenlong1356/article/details/104429467