9.LCD驅動架構
阿新 • • 發佈:2020-08-09
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; //長度
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