我是靠谱客的博主 喜悦野狼,最近开发中收集的这篇文章主要介绍linux LCD 驱动编写(四)一、应用程序访问驱动原理二、LCD驱动编写步骤三、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");

 

 

 

 

 

 

最后

以上就是喜悦野狼为你收集整理的linux LCD 驱动编写(四)一、应用程序访问驱动原理二、LCD驱动编写步骤三、LCD驱动代码的全部内容,希望文章能够帮你解决linux LCD 驱动编写(四)一、应用程序访问驱动原理二、LCD驱动编写步骤三、LCD驱动代码所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部