9.LCD驱动架构

1.小结

1.分配一个fb_info
2.设置

  • 2.1固定的参数
  • 2.2设置可变的参数
  • 2.3设置具体的文件操作指针fb_info->fbops
  • 2.4其他设置

3.硬件相关操作

  • 3.1配置GPIO用于LCD
  • 3.2根据LCD手册设置LCD控制器,如VCLK的频率等
  • 3.3分配显存framebuffer并把地址告诉LCD控制器

4.注册

  • 4.1分配显存fb_info->screnn_base,使用dma_alloc_writecombine()
     注:函数返回值是虚拟地址,有参数*handler返回实际实际物理地址,这个物理地址需要设置到LCD控制器
  • 4.2注册fb_info结构体,register_framebuffer

5.出口

  • 5.1卸载内核中的fb_info
  • 5.2控制LCDCON1关闭PWREN信号,关背光,iounmap注销地址
  • 5.3释放DMA缓冲地址dma_free_write()
  • 5.4释放fb_info

2.关键函数和数据结构

  • dma_alloc_writecombine
    分配内存空间

    void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
    解析:
    返回值:申请到的DMA缓冲区虚拟地址,若为NULL则表示分配失败,此时需要使用dma_free_writecombine释放内存
    *dev: 这里填0,表示这个申请的缓冲区里没有内容
    size: 分配的地址大小(字节单位)
    *handle: 申请到的物理起始地址
    gfp: 分配出来的内存参数,标志定义在<linux/gfp.h>常用如下:
    GFP_ATOMIC:用来从中断处理和进程上下文之外的其他代码中分配内存,从不睡眠
    GFP_KERNEL:内核内存的正常分配,可能会睡眠
    GFP_USER: 用来为用户空间也分配,可能会睡眠

framebuffer_alloc
—创建一个新的帧缓冲区信息结构

struct fb_info *framebuffer_alloc(size_t size, struct device *dev)

fb_info

struct fb_info {
atomic_t count;
int node;
int flags;
struct mutex lock; /* Lock for open/release/ioctl funcs /
struct mutex mm_lock; /
Lock for fb_mmap and smem_* fields /
struct fb_var_screeninfo var; /
Current var /
struct fb_fix_screeninfo fix; /
Current fix /
struct fb_monspecs monspecs; /
Current Monitor specs /
struct work_struct queue; /
Framebuffer event queue /
struct fb_pixmap pixmap; /
Image hardware mapper /
struct fb_pixmap sprite; /
Cursor hardware mapper /
struct fb_cmap cmap; /
Current cmap /
struct list_head modelist; /
mode list */
struct fb_videomode mode; / current mode /
#ifdef CONFIG_FB_BACKLIGHT
/
assigned backlight device /
/
set before framebuffer registration,
remove after unregister */
struct backlight_device *bl_dev;

/* Backlight level curve */
struct mutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif
#ifdef CONFIG_FB_DEFERRED_IO
struct delayed_work deferred_work;
struct fb_deferred_io *fbdefio;
#endif

struct fb_ops *fbops;
struct device device; / This is the parent */
struct device dev; / This is this fb device /
int class_flag; /
private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops tileops; / Tile Blitting */
#endif
char __iomem screen_base; / Virtual address /
unsigned long screen_size; /
Amount of ioremapped VRAM or 0 */
void pseudo_palette; / Fake palette of 16 colors /
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state; /
Hardware state i.e suspend */
void fbcon_par; / fbcon use-only private area /
/
From here on everything is device dependent */
void par;
/
we need the PCI or similar aperture base/size not
smem_start/size as smem_start may just be an object
allocated inside the aperture so may not actually overlap */
struct apertures_struct {
unsigned int count;
struct aperture {
resource_size_t base;
resource_size_t size;
} ranges[0];
} *apertures;
};

  • register_framebuffer
    注册一个帧缓冲设备

    int register_framebuffer(struct fb_info *fb_info)

3.源码解析

lcd.c

define LCD_xres 480

define LCD_yres 270

static u32 pseudo_palette[16];

static struct fb_info *mylcd_info;

static volatile unsigned long *GPBcon;
static volatile unsigned long *GPCcon;
static volatile unsigned long *GPDcon;
static volatile unsigned long *GPGcon; //GPG4:控制LCD信号
static volatile unsigned long *GPBdat; //GPB0: 控制背光

/* lcd control */
static struct lcd_reg {
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 tconsel;

} *lcd_reg;

/* 把单色提出来,放到rgb中对应的位置 */
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 mylcd_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, unsigned int transp, struct fb_info *info)
{
unsigned int val;

if(regno >= 16)
return 1; //调色板数组不能大于15

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 struct fb_ops mylcd_fb_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = mylcd_fb_setcolreg,
.fb_fillrect = cfb_fillrect, //填充矩形
.fb_copyarea = cfb_copyarea, //复制数据
.fb_imageblit = cfb_imageblit, //绘画图像
};

static int lcd_init(void)
{
printk(KERN_DEBUG"%s %s %d\n", FILE, FUNCTION, LINE);
/* 1.申请fb_info结构体 */
mylcd_info = framebuffer_alloc(0, 0);

/* 2.设置 /
/
2.1设置固定参数 */
strcpy(mylcd_info->fix.id, "mylcd"); //名字
mylcd_info->fix.smem_len = LCD_xres * LCD_yres * 2; //长度(像素为16bit[2字节],所以乘以2)
mylcd_info->fix.type = FB_TYPE_PACKED_PIXELS; //支持的类型
mylcd_info->fix.visual = FB_VISUAL_TRUECOLOR; //真彩色
mylcd_info->fix.line_length = LCD_xres * 2; //同上

printk(KERN_DEBUG"%s %s %d\n", FILE, FUNCTION, LINE);
/* 2.2设置可变参数 */
mylcd_info->var.xres = 480; //可见屏x分辨率
mylcd_info->var.yres = 270; //可见屏y分辨率
mylcd_info->var.xres_virtual = 480; //虚拟屏x分辨率
mylcd_info->var.yres_virtual = 270; //虚拟屏y分辨率
mylcd_info->var.xoffset = 0; //虚拟屏到可见屏之间x偏移
mylcd_info->var.yoffset = 0; //y偏移

mylcd_info->var.bits_per_pixel = 16; //像素
mylcd_info->var.grayscale = 0; //灰度(透明度)

printk(KERN_DEBUG"%s %s %d\n", FILE, FUNCTION, LINE);
/* rgb:565 */
mylcd_info->var.red.offset = 11; //偏移位置
mylcd_info->var.red.length = 5; //长度
mylcd_info->var.green.offset = 5;
mylcd_info->var.green.length = 6;
mylcd_info->var.blue.offset = 0;
mylcd_info->var.blue.length = 5;

/* 2.3设置操作函数 */
mylcd_info->fbops = &mylcd_fb_ops;

printk(KERN_DEBUG"%s %s %d\n", FILE, FUNCTION, LINE);
/* 2.4设置其他 */
mylcd_info->pseudo_palette = pseudo_palette; //保存调色板数组
mylcd_info->screen_size = LCD_xres * LCD_yres * 2; //虚拟地址长

/* 3.硬件操作 /
/
3.1设置gpio为lcd */
GPBcon = ioremap(0x56000010, 8);
GPBdat = GPBcon + 1;
GPCcon = ioremap(0x56000020, 4);
GPDcon = ioremap(0x56000030, 4);
GPGcon = ioremap(0x56000060, 4);

*GPBcon &= ~(0X3 << 0);
*GPBcon |= (0X1 << 0); //输出
*GPBdat &= ~(0X1 << 0); //关背光

*GPCcon = 0Xaaaaaaaa;
*GPDcon = 0Xaaaaaaaa;
*GPGcon |= (0x3 << (4 * 2)); //lcd_power

/* 3.2设置lcd控制器 */
lcd_reg->lcdcon1 = (4<<8) | (3<<5) | (0xc<<1);
lcd_reg->lcdcon2 = (3<<24) | (271<<14) | (1<<6) |(0<<0);
lcd_reg->lcdcon3 = ((16)<<19) | (479<<8) | ((10));
lcd_reg->lcdcon4 = (4);
lcd_reg->lcdcon5 = (1<<11) | (1<<9) | (1<<8)|(1<<0);

lcd_reg->lcdcon1 &= ~(1<<0); //关闭PWREN信号输出
lcd_reg->lcdcon5 &= ~(1<<3); //禁止PWREN信号

/* 3.3分配显存 */
mylcd_info->screen_base = dma_alloc_writecombine(0, mylcd_info->fix.smem_len, mylcd_info->fix.smem_start, GFP_KERNEL); //显存虚拟地址

lcd_reg->lcdsaddr1 = (mylcd_info->fix.smem_start >> 1) & (0x3fffffff);
lcd_reg->lcdsaddr2 = ((mylcd_info->fix.smem_start + mylcd_info->screen_size) >> 1) & 0x1fffff;
lcd_reg->lcdsaddr3 = LCD_xres & 0x3ff;

/* 4.1开启lcd */
lcd_reg->lcdcon1 |= (1 << 0); //输出power信号
lcd_reg->lcdcon2 |= (1 << 3); //允许power信号
*GPBdat |= (1 << 0); //开背光

/* 4.2注册fb_info */
register_framebuffer(mylcd_info);

return 0;
}

static int lcd_exit(void)
{
printk(KERN_DEBUG"%s %s %d\n", FILE, FUNCTION, LINE);

/* 1.卸载fb_info */
unregister_framebuffer(mylcd_info);

/* 2.控制lcd关闭,释放io */
lcd_reg->lcdcon1 &= ~(1 << 0);
lcd_reg->lcdcon5 &= ~(1 << 3);
*GPBdat &= ~(1 << 0);

iounmap(GPBcon);
iounmap(GPCcon);
iounmap(GPDcon);
iounmap(GPGcon);

/* 3.释放显存 */
dma_free_writecombine(0, mylcd_info->screen_size, mylcd_info->screen_base, mylcd_info->fix.smem_start);

/* 4.释放fb_info */
framebuffer_release(mylcd_info);

return 0;
}

module_init(lcd_init);
module_exit(lcd_exit);
MODULE_LICENSE("GPL");
Makefile

KERN_DIR = /home/book/linux-2.6.22.6

all:
make -C $(KERN_DIR) M=pwd modules

clean:
make -C $(KERN_DIR) M=pwd modules clean
rm -rf modules.order

obj-m += lcd.o

posted @ 2020-08-09 15:32  人民广场的二道贩子  阅读(204)  评论(0)    收藏  举报