linux中LCD设备驱动(2)——基于s3c6410平台

时间:2021-12-26 02:40:43

s3c6410硬件DISPLAY CONTROLLER(显示控制器)的链接地址

linux中LCD设备驱动(1)——framebuffer(帧缓冲)的链接地址


上一篇说了framebuffer帧缓冲的有关知识,这一篇具体的说下LCD驱动的实现。

1、LCD设备驱动在linux内核中是作为平台设备存在,所以又要说那些已经说过很多遍的东西。

int __devinit s3cfb_init(void)
{
return platform_driver_register(&s3cfb_driver);
}
static void __exit s3cfb_cleanup(void)
{
platform_driver_unregister(&s3cfb_driver);
}
module_init(s3cfb_init);
module_exit(s3cfb_cleanup);

对应的platform_driver结构体为:
static struct platform_driver s3cfb_driver = {
.probe = s3cfb_probe,
.remove = s3cfb_remove,
.suspend = s3cfb_suspend,
.resume = s3cfb_resume,
        .driver = {
.name = "s3c-lcd",
.owner = THIS_MODULE,
},
};

那么平台设备的资源呢?

/* LCD Controller */

static u64 s3c_device_lcd_dmamask = 0xffffffffUL;


struct platform_device s3c_device_lcd = {
.name  = "s3c-lcd",
.id  = -1,设备编号,-1表示只有这样一个设备
.num_resources = ARRAY_SIZE(s3c_lcd_resource),
.resource  = s3c_lcd_resource,
.dev              = {
.dma_mask = &s3c_device_lcd_dmamask,
.coherent_dma_mask= 0xffffffffUL
}
};


static struct resource s3c_lcd_resource[] = {
[0] = {
.start = S3C64XX_PA_LCD,  //LCD的I/O内存的开始位置
.end   = S3C64XX_PA_LCD + SZ_1M - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_LCD_VSYNC,    //LCD的开始中断号
.end   = IRQ_LCD_SYSTEM,  //LCD的结束中断号
.flags = IORESOURCE_IRQ,
}
};

2、分析其probe函数,源码如下:

/*
 *  Probe
 */
static int __init s3cfb_probe(struct platform_device *pdev)
{
struct resource *res;
struct fb_info *fbinfo;
s3cfb_info_t *info;



char driver_name[] = "s3cfb";
int index = 0, ret, size;


fbinfo = framebuffer_alloc(sizeof(s3cfb_info_t), &pdev->dev);申请一个s3cfb_info_t结构体的空间

if (!fbinfo)
return -ENOMEM;


platform_set_drvdata(pdev, fbinfo);
info = fbinfo->par;
info->dev = &pdev->dev;  填充info结构体变量的相应信息


res = platform_get_resource(pdev, IORESOURCE_MEM, 0);获取LCD平台设备所使用的I/O端口资源。
if (res == NULL) {
dev_err(&pdev->dev, "failed to get memory registers\n");
ret = -ENXIO;
goto dealloc_fb;
}

size = (res->end - res->start) + 1;
info->mem = request_mem_region(res->start, size, pdev->name);申请LCD设备所占的I/O空间
if (info->mem == NULL) {
dev_err(&pdev->dev, "failed to get memory region\n");
ret = -ENOENT;
goto dealloc_fb;
}

info->io = ioremap(res->start, size);将LCD的I/O端口所占用的这段I/O空间映射到内存的虚拟地址。
if (info->io == NULL) {
dev_err(&pdev->dev, "ioremap() of registers failed\n");
ret = -ENXIO;
goto release_mem;
}


s3cfb_pre_init();这个函数的源码在S3cfb_fimd4x.c (linux2.6.28\drivers\video\samsung)文件中,如下所示:

void s3cfb_pre_init(void)
{
/* initialize the fimd specific */
s3cfb_fimd.vidintcon0 &= ~S3C_VIDINTCON0_FRAMESEL0_MASK;
s3cfb_fimd.vidintcon0 |= S3C_VIDINTCON0_FRAMESEL0_VSYNC;
s3cfb_fimd.vidintcon0 |= S3C_VIDINTCON0_INTFRMEN_ENABLE;


writel(s3cfb_fimd.vidintcon0, S3C_VIDINTCON0);
}

这个函数中涉及到一个结构体s3cfb_fimd_info_t s3cfb_fimd,和这个函数在同一个文件中定义,列出部分源码:

s3cfb_fimd_info_t s3cfb_fimd = {
.vidcon0 = S3C_VIDCON0_INTERLACE_F_PROGRESSIVE | S3C_VIDCON0_VIDOUT_RGB_IF | S3C_VIDCON0_L1_DATA16_SUB_16_MODE | \
S3C_VIDCON0_L0_DATA16_MAIN_16_MODE | S3C_VIDCON0_PNRMODE_RGB_P | \
S3C_VIDCON0_CLKVALUP_ALWAYS | S3C_VIDCON0_CLKDIR_DIVIDED | S3C_VIDCON0_CLKSEL_F_HCLK | \
S3C_VIDCON0_ENVID_DISABLE | S3C_VIDCON0_ENVID_F_DISABLE,


.dithmode = (S3C_DITHMODE_RDITHPOS_5BIT | S3C_DITHMODE_GDITHPOS_6BIT | S3C_DITHMODE_BDITHPOS_5BIT ) & S3C_DITHMODE_DITHERING_DISABLE,


#if defined (CONFIG_FB_S3C_BPP_8)
.wincon0 =  S3C_WINCONx_BYTSWP_ENABLE | S3C_WINCONx_BURSTLEN_4WORD | S3C_WINCONx_BPPMODE_F_8BPP_PAL,
.wincon1 =  S3C_WINCONx_HAWSWP_ENABLE | S3C_WINCONx_BURSTLEN_4WORD | S3C_WINCONx_BPPMODE_F_16BPP_565 | S3C_WINCONx_BLD_PIX_PLANE | S3C_WINCONx_ALPHA_SEL_1,
.bpp = S3CFB_PIXEL_BPP_8,
.bytes_per_pixel = 1,
.wpalcon = S3C_WPALCON_W0PAL_16BIT,

..........

..........

.vidosd1c = S3C_VIDOSDxC_ALPHA1_B(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_G(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_R(S3CFB_MAX_ALPHA_LEVEL),
.vidosd2c = S3C_VIDOSDxC_ALPHA1_B(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_G(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_R(S3CFB_MAX_ALPHA_LEVEL),
.vidosd3c = S3C_VIDOSDxC_ALPHA1_B(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_G(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_R(S3CFB_MAX_ALPHA_LEVEL),
.vidosd4c = S3C_VIDOSDxC_ALPHA1_B(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_G(S3CFB_MAX_ALPHA_LEVEL) | S3C_VIDOSDxC_ALPHA1_R(S3CFB_MAX_ALPHA_LEVEL),


.vidintcon0 = S3C_VIDINTCON0_FRAMESEL0_VSYNC | S3C_VIDINTCON0_FRAMESEL1_NONE | S3C_VIDINTCON0_INTFRMEN_DISABLE | \
S3C_VIDINTCON0_FIFOSEL_WIN0 | S3C_VIDINTCON0_FIFOLEVEL_25 | S3C_VIDINTCON0_INTFIFOEN_DISABLE | S3C_VIDINTCON0_INTEN_ENABLE,
.vidintcon1 = 0,


.xoffset = 0,
.yoffset = 0,


.w1keycon0 = S3C_WxKEYCON0_KEYBLEN_DISABLE | S3C_WxKEYCON0_KEYEN_F_DISABLE | S3C_WxKEYCON0_DIRCON_MATCH_FG_IMAGE | S3C_WxKEYCON0_COMPKEY(0x0),
.............
.w4keycon1 = S3C_WxKEYCON1_COLVAL(0xffffff),


.sync = 0,
.cmap_static = 1,


.vs_offset = S3CFB_DEFAULT_DISPLAY_OFFSET,
.brightness = S3CFB_DEFAULT_BRIGHTNESS,
.backlight_level = S3CFB_DEFAULT_BACKLIGHT_LEVEL,
.backlight_power = 1,
.lcd_power = 1,
};

那么对应的结构体原型在哪呢?

在S3cfb.h (linux2.6.28\drivers\video\samsung)文件中,如下所示:

看那些对应的注释,也应该大致明白这个结构体的作用,存储与显示控制器有关的信息,还有显示屏幕的信息。不知道大家对s3cfb_fimd_info_t这个结构体的命名有何看法?名字往往代表了这个结构体的作用。其实这个结构体的名字给我们的信息是:

fimd—   ——    ——

FIMD: 
                Fully Interactive Mobile Display (完全交互式移动显示设备)

这是第一个概念,另一个概念是 OSD,我查了下资料,有这样的含义:

OSD是on-screen display的简称,即屏幕菜单式调节方式。一般是按Menu键后屏幕弹出的显示器各项调节项目信息的矩形菜单,可通过该菜单对显示器各项工作指标包括色彩、模式、几何形状等进行调整,从而达到最佳的使用状态。

现在对这个结构体的认识是不是深刻多了?

typedef struct {


/* Screen size */
int width;
int height;


/* Screen info */
int xres;
int yres;


/* Virtual Screen info */
int xres_virtual;
int yres_virtual;
int xoffset;
int yoffset;


/* OSD Screen size */
int osd_width;
int osd_height;


/* OSD Screen info */
int osd_xres;
int osd_yres;


/* OSD Screen info */
int osd_xres_virtual;
int osd_yres_virtual;


int bpp;
int bytes_per_pixel;
unsigned long pixclock;


int hsync_len;
int left_margin;
int right_margin;
int vsync_len;
int upper_margin;
int lower_margin;
int sync;


int cmap_grayscale:1;
int cmap_inverse:1;
int cmap_static:1;
int unused:29;


/* backlight info */
int backlight_min;
int backlight_max;
int backlight_default;


int vs_offset;
int brightness;
int palette_win;
int backlight_level;
int backlight_power;
int lcd_power;


s3cfb_vsync_info_t vsync_info;
s3cfb_vs_info_t vs_info;


/* lcd configuration registers */
unsigned long lcdcon1;
unsigned long lcdcon2;


        unsigned long lcdcon3;
unsigned long lcdcon4;
unsigned long lcdcon5;


/* GPIOs */
unsigned long gpcup;
unsigned long gpcup_mask;
unsigned long gpccon;
unsigned long gpccon_mask;
unsigned long gpdup;
unsigned long gpdup_mask;
unsigned long gpdcon;
unsigned long gpdcon_mask;


/* lpc3600 control register */
unsigned long lpcsel;
unsigned long lcdtcon1;
unsigned long lcdtcon2;
unsigned long lcdtcon3;
unsigned long lcdosd1;
unsigned long lcdosd2;
unsigned long lcdosd3;
unsigned long lcdsaddrb1;
unsigned long lcdsaddrb2;
unsigned long lcdsaddrf1;
unsigned long lcdsaddrf2;
unsigned long lcdeaddrb1;
unsigned long lcdeaddrb2;
unsigned long lcdeaddrf1;
unsigned long lcdeaddrf2;
unsigned long lcdvscrb1;
unsigned long lcdvscrb2;
unsigned long lcdvscrf1;
unsigned long lcdvscrf2;
unsigned long lcdintcon;
unsigned long lcdkeycon;
unsigned long lcdkeyval;
unsigned long lcdbgcon;
unsigned long lcdfgcon;
unsigned long lcddithcon;


unsigned long vidcon0;
unsigned long vidcon1;
unsigned long vidtcon0;
unsigned long vidtcon1;
unsigned long vidtcon2;
unsigned long vidtcon3;
unsigned long wincon0;
unsigned long wincon2;
unsigned long wincon1;
unsigned long wincon3;
unsigned long wincon4;


unsigned long vidosd0a;
unsigned long vidosd0b;
unsigned long vidosd0c;
unsigned long vidosd1a;
unsigned long vidosd1b;
unsigned long vidosd1c;
unsigned long vidosd1d;
unsigned long vidosd2a;
unsigned long vidosd2b;
unsigned long vidosd2c;
unsigned long vidosd2d;
unsigned long vidosd3a;
unsigned long vidosd3b;
unsigned long vidosd3c;
unsigned long vidosd4a;
unsigned long vidosd4b;
unsigned long vidosd4c;


unsigned long vidw00add0b0;
unsigned long vidw00add0b1;
unsigned long vidw01add0;
unsigned long vidw01add0b0;
unsigned long vidw01add0b1;


unsigned long vidw00add1b0;
unsigned long vidw00add1b1;
unsigned long vidw01add1;
unsigned long vidw01add1b0;
unsigned long vidw01add1b1;


unsigned long vidw00add2b0;
unsigned long vidw00add2b1;


unsigned long vidw02add0;
unsigned long vidw03add0;
unsigned long vidw04add0;


unsigned long vidw02add1;
unsigned long vidw03add1;
unsigned long vidw04add1;
unsigned long vidw00add2;
unsigned long vidw01add2;
unsigned long vidw02add2;
unsigned long vidw03add2;
unsigned long vidw04add2;


unsigned long vidintcon;
unsigned long vidintcon0;
unsigned long vidintcon1;
unsigned long w1keycon0;
unsigned long w1keycon1;
unsigned long w2keycon0;
unsigned long w2keycon1;
unsigned long w3keycon0;
unsigned long w3keycon1;
unsigned long w4keycon0;
unsigned long w4keycon1;


unsigned long win0map;
unsigned long win1map;
unsigned long win2map;
unsigned long win3map;
unsigned long win4map;


unsigned long wpalcon;
unsigned long dithmode;
unsigned long intclr0;
unsigned long intclr1;
unsigned long intclr2;


unsigned long win0pal;
unsigned long win1pal;


/* utility functions */
void (*set_backlight_power)(int);
void (*set_lcd_power)(int);
void (*set_brightness)(int);
int (*map_video_memory)(s3cfb_info_t *);
int (*unmap_video_memory)(s3cfb_info_t *);
}s3cfb_fimd_info_t;


下一篇接着从这里分析:
s3cfb_set_backlight_power(1);
s3cfb_set_lcd_power(1);
s3cfb_set_backlight_level(S3CFB_DEFAULT_BACKLIGHT_LEVEL);


info->clk = clk_get(NULL, "lcd");


if (!info->clk || IS_ERR(info->clk)) {
printk(KERN_INFO "failed to get lcd clock source\n");
ret =  -ENOENT;
goto release_io;
}


clk_enable(info->clk);
printk("S3C_LCD clock got enabled :: %ld.%03ld Mhz\n", PRINT_MHZ(clk_get_rate(info->clk)));


s3cfb_fimd.vsync_info.count = 0;
init_waitqueue_head(&s3cfb_fimd.vsync_info.wait_queue);


res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);


if (res == NULL) {
dev_err(&pdev->dev, "failed to get irq\n");
ret = -ENXIO;
goto release_clock;
}


ret = request_irq(res->start, s3cfb_irq, 0, "s3c-lcd", pdev);


if (ret != 0) {
printk("Failed to install irq (%d)\n", ret);
goto release_clock;
}


msleep(5);


for (index = 0; index < S3CFB_NUM; index++) {
s3cfb_info[index].mem = info->mem;
s3cfb_info[index].io = info->io;
s3cfb_info[index].clk = info->clk;


s3cfb_init_fbinfo(&s3cfb_info[index], driver_name, index);


/* Initialize video memory */
ret = s3cfb_map_video_memory(&s3cfb_info[index]);


if (ret) {
printk("Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto release_irq;
}


ret = s3cfb_init_registers(&s3cfb_info[index]);
ret = s3cfb_check_var(&s3cfb_info[index].fb.var, &s3cfb_info[index].fb);


if (index < 2){
if (fb_alloc_cmap(&s3cfb_info[index].fb.cmap, 256, 0) < 0)
goto dealloc_fb;
} else {
if (fb_alloc_cmap(&s3cfb_info[index].fb.cmap, 16, 0) < 0)
goto dealloc_fb;
}


ret = register_framebuffer(&s3cfb_info[index].fb);


if (ret < 0) {
printk(KERN_ERR "Failed to register framebuffer device: %d\n", ret);
goto free_video_memory;
}


printk(KERN_INFO "fb%d: %s frame buffer device\n", s3cfb_info[index].fb.node, s3cfb_info[index].fb.fix.id);
}


/* create device files */
ret = device_create_file(&(pdev->dev), &dev_attr_backlight_power);


if (ret < 0)
printk(KERN_WARNING "s3cfb: failed to add entries\n");


ret = device_create_file(&(pdev->dev), &dev_attr_backlight_level);


if (ret < 0)
printk(KERN_WARNING "s3cfb: failed to add entries\n");


ret = device_create_file(&(pdev->dev), &dev_attr_lcd_power);


if (ret < 0)
printk(KERN_WARNING "s3cfb: failed to add entries\n");


return 0;


free_video_memory:
s3cfb_unmap_video_memory(&s3cfb_info[index]);


release_irq:
free_irq(res->start, &info);


release_clock:
clk_disable(info->clk);
clk_put(info->clk);


release_io:
iounmap(info->io);


release_mem:
release_resource(info->mem);
kfree(info->mem);


dealloc_fb:
framebuffer_release(fbinfo);
return ret;
}

linux中LCD设备驱动(3)——基于s3c6410平台的链接地址

linux中LCD设备驱动(4)——基于s3c6410平台的链接地址