linux 2.6.29 lcd驱动:s3c2410fb.c分析

 

一些重要的结构体:

struct resource

{

resource_size_t start;

resource_size_t end;

const char *name;

unsigned long flags;

struct resource *parent, *sibling, *child;

};

 

struct s3c2410fb_hw

{

unsigned longlcdcon1;

unsigned longlcdcon2;

unsigned longlcdcon3;

unsigned longlcdcon4;

unsigned longlcdcon5;

};

 

struct s3c2410fb_display {

/* LCD type */

unsigned type;

 

/* Screen size */

unsigned short width;

unsigned short height;

 

/* Screen info */

unsigned short xres;

unsigned short yres;

unsigned short bpp;

 

unsigned pixclock;/* pixclock in picoseconds */

unsigned short left_margin; /* value in pixels (TFT) or HCLKs (STN) */

unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */

unsigned short hsync_len; /* value in pixels (TFT) or HCLKs (STN) */

unsigned short upper_margin;/* value in lines (TFT) or 0 (STN) */

unsigned short lower_margin;/* value in lines (TFT) or 0 (STN) */

unsigned short vsync_len;/* value in lines (TFT) or 0 (STN) */

 

/* lcd configuration registers */

unsigned longlcdcon5;

};

 

struct s3c2410fb_mach_info {

struct s3c2410fb_display *displays;/* attached diplays info */

unsigned num_displays;/* number of defined displays */

unsigned default_display;

 

/* GPIOs */

unsigned longgpcup;

unsigned longgpcup_mask;

unsigned longgpccon;

unsigned longgpccon_mask;

unsigned longgpdup;

unsigned longgpdup_mask;

unsigned longgpdcon;

unsigned longgpdcon_mask;

 

/* lpc3600 control register */

unsigned longlpcsel;

};

 

#define LCD_WIDTH 320

#define LCD_HEIGHT 240

#define LCD_PIXCLOCK 170000

 

#define LCD_RIGHT_MARGIN 0x44

#define LCD_LEFT_MARGIN 0x04

#define LCD_HSYNC_LEN 0x01

 

#define LCD_UPPER_MARGIN 10

#define LCD_LOWER_MARGIN 4

#define LCD_VSYNC_LEN 1

#define LCD_CON5 (S3C2410_LCDCON5_FRM565 | S3C2410_LCDCON5_INVVFRAME | \ S3C2410_LCDCON5_INVVLINE | S3C2410_LCDCON5_HWSWP )

 

static struct s3c2410fb_display mini2440_lcd_cfg = {

#if !defined (LCD_CON5)

.lcdcon5= S3C2410_LCDCON5_FRM565 |

S3C2410_LCDCON5_INVVLINE |

S3C2410_LCDCON5_INVVFRAME |

S3C2410_LCDCON5_PWREN |

S3C2410_LCDCON5_HWSWP,

#else

.lcdcon5= LCD_CON5,

#endif

 

.type= S3C2410_LCDCON1_TFT,

 

.width= LCD_WIDTH,

.height= LCD_HEIGHT,

 

.pixclock= LCD_PIXCLOCK,

.xres= LCD_WIDTH,

.yres= LCD_HEIGHT,

.bpp= 16,

.left_margin= LCD_LEFT_MARGIN + 1,

.right_margin= LCD_RIGHT_MARGIN + 1,

.hsync_len= LCD_HSYNC_LEN + 1,

.upper_margin= LCD_UPPER_MARGIN + 1,

.lower_margin= LCD_LOWER_MARGIN + 1,

.vsync_len= LCD_VSYNC_LEN + 1,

};

 

static struct s3c2410fb_mach_info mini2440_fb_info =

{

.displays= &mini2440_lcd_cfg,

.num_displays= 1,

.default_display = 0,

 

.gpccon = 0xaa955699,

.gpccon_mask = 0xffc003cc,

.gpcup = 0x0000ffff,

.gpcup_mask = 0xffffffff,

 

.gpdcon = 0xaa95aaa1,

.gpdcon_mask = 0xffc0fff0,

.gpdup = 0x0000faff,

.gpdup_mask = 0xffffffff,

 

.lpcsel= 0xf82,

};

 

 

/* 定义lcd 平台设备 */

struct platform_device s3c_device_lcd =

{

.name = "s3c2410-lcd",

.id = -1,

.num_resources = ARRAY_SIZE(s3c_lcd_resource),

.resource = s3c_lcd_resource,

.dev = {

.dma_mask= &s3c_device_lcd_dmamask,

.coherent_dma_mask= 0xffffffffUL

}

};

 

/* mini2440 所有platform设备数组*/

static struct platform_device *mini2440_devices[] __initdata =

{

&s3c_device_usb,

&s3c_device_rtc,

&s3c_device_lcd,

&s3c_device_wdt,

&s3c_device_i2c0,

&s3c_device_iis,

&s3c_device_dm9k,

&net_device_cs8900,

&s3c24xx_uda134x,

};

 

/* 内核启动的时候会调用该函数 */

static void __init mini2440_machine_init(void)

{

#if defined (LCD_WIDTH)

s3c24xx_fb_set_platdata(&mini2440_fb_info);

#endif

s3c_i2c0_set_platdata(NULL);

 

/* 以下两个函数会完成platform的注册等初始化 */

platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));

friendly_arm_machine_init();

}

 

void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)

{

struct s3c2410fb_mach_info *npd;

 

/* 分配一个3c2410fb_mach_info结构体*/

npd = kmalloc(sizeof(*npd), GFP_KERNEL);

if (npd) {

memcpy(npd, pd, sizeof(*npd));

/* s3c_device_lcd.dev.platform_data所指内容就为mini2440_fb_info

* 结构体内容的一个拷贝

*/

s3c_device_lcd.dev.platform_data = npd;

} else {

printk(KERN_ERR "no memory for LCD platform data\n");

}

}

 

/*s3c2410fb_init会注册该驱动,当driver和device匹配成功,调用s3c2412fb_probe*/

static struct platform_driver s3c2412fb_driver =

{

.probe= s3c2412fb_probe,

.remove= s3c2410fb_remove,

.suspend= s3c2410fb_suspend,

.resume= s3c2410fb_resume,

.driver= {

.name= "s3c2412-lcd",

.owner= THIS_MODULE,

},

};

 

int __init s3c2410fb_init(void)

{

int ret = platform_driver_register(&s3c2410fb_driver);

if (ret == 0)

ret = platform_driver_register(&s3c2412fb_driver);

return ret;

}

 

static struct fb_ops s3c2410fb_ops =

{

.owner= THIS_MODULE,

.fb_check_var= s3c2410fb_check_var,

.fb_set_par= s3c2410fb_set_par,

.fb_blank= s3c2410fb_blank,

.fb_setcolreg= s3c2410fb_setcolreg,

.fb_fillrect= cfb_fillrect,

.fb_copyarea= cfb_copyarea,

.fb_imageblit= cfb_imageblit,

};

 

/* 该函数很长,只列出关键部分 */

static int __init s3c24xxfb_probe(struct platform_device *pdev,enum s3c_drv_type drv_type)

{

 

struct s3c2410fb_info *info;

struct s3c2410fb_display *display;

struct fb_info *fbinfo;

struct s3c2410fb_mach_info *mach_info;

struct resource *res;

int irq;

u32 lcdcon1;

 

/* 现在mach_info指向有lcd引脚设置的结构体 */

mach_info = pdev->dev.platform_data;

 

/* dispaly指向mini2440_lcd_cfg,该结构体有LCD一些设置信息

*如:长度,宽度等。

*/

display = mach_info->displays + mach_info->default_display;

 

/* 获得中断号 */

irq = platform_get_irq(pdev, 0);

 

/* 该函数分配 sizeof(struct fb_info) + sizeof(struct s3c2410fb_info)大小的

*的内存,fb_info指向sizeof(struct fb_info)分配的内存,fb_info->par指向

*sizeof(struct s3c2410fb_info)分配的内存。fbinfo->device = &pdev->dev

*/

fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);

 

/*该函数执行一下赋值pdev->dev->private_data = fb_info;*/

platform_set_drvdata(pdev, fbinfo);

 

info = fbinfo->par; /*struct s3c2410fb_info *info;*/

info->dev = &pdev->dev;

info->drv_type = drv_type;

 

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

 

/ * 需映射内存大小 */

size = (res->end - res->start) + 1;

 

info->mem = request_mem_region(res->start, size, pdev->name);

 

/* 物理地址到虚拟地址的映射 */

info->io = ioremap(res->start, size);

 

info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);

/* Stop the video */

lcdcon1 = readl(info->io + S3C2410_LCDCON1);

writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);

 

/* 设置名称 static char driver_name[] = "s3c2410fb"; */

strcpy(fbinfo->fix.id, driver_name);

 

fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;

 

fbinfo->var.nonstd = 0;

 

fbinfo->fbops = &s3c2410fb_ops;

fbinfo->flags = FBINFO_FLAG_DEFAULT;

fbinfo->pseudo_palette = &info->pseudo_pal;

 

/* frame_buffer内存到物理地址的映射,该函数会调用

*info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,&map_dma, GFP_KERNEL);

*该函数将分配一块可以dma的内存区域,并且返回两个值,info->screen_base为虚拟地址

*map_dma的值为虚拟地址对应的物理地址,该值将被填至s3c2440的LCD控制器中。

*该函数还会执行一些赋值:info->fix.smem_start = map_dma;

*/

ret = s3c2410fb_map_video_memory(fbinfo);

 

/* 用 mini2440_fb_info结构体的成员值初始化LCD的引脚*/

s3c2410fb_init_registers(fbinfo);

 

/*做参数检查,并赋值*/

s3c2410fb_check_var(&fbinfo->var, fbinfo);

 

/* 注册fbinfo */

ret = register_framebuffer(fbinfo);

 

/* 创建属性文件 */

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

 

}

 

LCD控制器的初始化主要在s3c2410fb_activate_var(info);函数中,该函数被s3c2410fb_set_par函数调用,为fb_ops中的一个成员。

但是s3c24xxfb_probe函数并没有调用该函数,那是什么时候初始化的LCD控制器呢?

通过在s3c2410fb_set_pa设置dump_stack()函数,一下为返回的函数调用信息:

do_exit--->kernel_init--->__exception_text_end-->platform_driver_register--->driver_register--->bus_add_driver--->driver_attach--->bus_for_each_dev--->_driver_attach--->driver_probe_device--->platform_drv_probe--->s3c2410fb_probe--->s3c24xxfb_probe --->register_framebuffer--->fb_notifier_call_chain--->blocking_notifier_call_chain--->_blocking_notifier_call_chain--->notifier_call_chain--->fbcon_event_notify--->fbcon_takeover--->take_over_console--->visual_init--->fbcon_init--->s3c2410fb_set_par

我们看到在注册register_framebuffer时它会一路调用直到调用s3c2410fb_set_par函数执行LCD控制器的初始化。

 

 

你可能感兴趣的:(linux,LCD驱动)