分类:
2008-09-07 16:10:55
液晶驱动
s3c2410fb.c中static int __init s3c2410fb_probe(struct platform_device *pdev)
{
}
platform_device在mach-smdk2410中
static struct platform_device *smdk2410_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
};
smdk2410_devices 和&s3c_device_lcd, 初始化lcd 其中lcd 的初始化是在以下的函数中
static void __init smdk2410_init(void)
{
s3c24xx_fb_set_platdata(&qt2410_fb_info);
platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));
smdk_machine_init();
}
s3c24xx_fb_set_platdata(&qt2410_fb_info);
void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
{
struct s3c2410fb_mach_info *npd;
npd = kmalloc(sizeof(*npd), GFP_KERNEL);
if (npd) {
memcpy(npd, pd, sizeof(*npd));
s3c_device_lcd.dev.platform_data = npd;
} else {
printk(KERN_ERR "no memory for LCD platform data\n");
}
}
其中s3c_device_lcd.dev.platform_data = npd; 语句对lcd 的结构即
static struct s3c2410fb_mach_info qt2410_fb_info __initdata = {
.displays = qt2410_lcd_cfg,
.num_displays = ARRAY_SIZE(qt2410_lcd_cfg),
.default_display = 0,
.gpcup = 0xffffffff,
.gpcup_mask = 0xffffffff,
.gpccon = 0xaaaaaaaa,
.gpccon_mask = 0xffffffff,
.gpdup = 0xffffffff,
.gpdup_mask = 0xffffffff,
.gpdcon = 0xaaaaaaaa,
.gpdcon_mask = 0xffffffff,
.lpcsel = ((0xCE6) & ~7) | 1<<4,
//.lpcsel = 0x0,
}; display 是在 如下定义的 看上面
static struct s3c2410fb_display qt2410_lcd_cfg[] __initdata = {
{
.type = S3C2410_LCDCON1_TFT,
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
S3C2410_LCDCON5_HWSWP|
S3C2410_LCDCON5_PWREN,
.width = 240,
.height = 320,
.pixclock = 100000, /* HCLK/4 */
.xres = 240,
.yres = 320,
.bpp = 16,
.left_margin = 13,
.right_margin = 8,
.hsync_len = 4,
.upper_margin = 2,
},
}; 整个过程是对smdk2410_devices的初始化
回到 s3c2410fb_probe函数 他是lcd 的probe 函数 即搜索函数
mach_info = pdev->dev.platform_data;
if (mach_info == NULL) {
dev_err(&pdev->dev,
"no platform data for lcd, cannot attach\n");
return -EINVAL;
}
一 检查系统般的类型 是否设置
display = mach_info->displays + mach_info->default_display;
display赋值
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq for device\n");
return -ENOENT;
}
获得irq 为fbinfo 结构分配空间
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
其中fbinfo 的定义
truct fb_info {
int node;
int flags;
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];
truct fb_info {
int node;
int flags;
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];
/* From here on everything is device dependent */
void *par;
};
二 为fb_info 赋值
platform_set_drvdata(pdev, fbinfo);
info = fbinfo->par;
info->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
size = (res->end - res->start) + 1;
info->mem = request_mem_region(res->start, size, pdev->name);
if (info->mem == NULL) {
dev_err(&pdev->dev, "failed to get memory region\n");
ret = -ENOENT;
goto dealloc_fb;
}
三 分配寄存器地址
/* Stop the video */
lcdcon1 = readl(info->io + S3C2410_LCDCON1);
writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
四 停止显示
fbinfo->fbops = &s3c2410fb_ops;
在相同文件下的定义如下
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,
};
s3c2410fb_setcolreg()设置颜色寄存器调用
schedule_palette_update(fbi, regno, val);
五 find maximum required memory size for display
for (i = 0; i < mach_info->num_displays; i++) {
unsigned long smem_len = mach_info->displays[i].xres;
smem_len *= mach_info->displays[i].yres;
smem_len *= mach_info->displays[i].bpp;
smem_len >>= 3;
if (fbinfo->fix.smem_len < smem_len)
fbinfo->fix.smem_len = smem_len;
}
六 为 DMA分配内才能
ret = s3c2410fb_map_video_memory(fbinfo);
if (ret) {
printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto release_clock;
}
七 初始化寄存器
s3c2410fb_init_registers(fbinfo);
定义如下
static int s3c2410fb_init_registers(struct fb_info *info)
{
struct s3c2410fb_info *fbi = info->par;
struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
unsigned long flags;
void __iomem *regs = fbi->io;
/* Initialise LCD with values from haret */
local_irq_save(flags);
/* modify the gpio(s) with interrupts set (bjd) */
modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask);
modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask);
modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);
local_irq_restore(flags);
dprintk("LPCSEL = 0x%08lx\n", mach_info->lpcsel);
}也就是在mach-smdk2410中GPCUP 和GPCCON的设置
八 检查fbinfo 中内存中fbinfo->var 的设置
s3c2410fb_check_var(&fbinfo->var, fbinfo);
九 注册内存
ret = register_framebuffer(fbinfo);
十 注册文件
device_create_file(&pdev->dev, &dev_attr_debug);
十一 返回
return 0;
总结
static struct platform_driver s3c2410fb_driver = {
.probe = s3c2410fb_probe,
.remove = s3c2410fb_remove,
.suspend = s3c2410fb_suspend,
.resume = s3c2410fb_resume,
.driver = { schedule_palette_update(fbi, regno, val);
.name = "s3c2410-lcd",
.owner = THIS_MODULE,
},
};
s3c2410fb_probe,
s3c2410fb_remove,
3c2410fb_suspend,
s3c2410fb_resume,
这些函数的都是 io_ctl的必备函数
lcd 的初始化 就是在 内核初始化的时候
int __init s3c2410fb_init(void)
{
return platform_driver_register(&s3c2410fb_driver);
}
其中最重要的就是fb_info 即frame buffer 的设置 这是以后 从上层对该层的调用
整个驱动的注册的过程就是设置各个寄存器的设置设置调色板
注册irq 然后 注册文件操作函数
注册函数初始化
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume;
return driver_register(&drv->driver);
}
其中 platform_driver 的配置如下
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
device_driver 为
struct device_driver {
const char * name;
struct bus_type * bus;
struct kobject kobj;
struct klist klist_devices;
struct klist_node knode_bus;
struct module * owner;
const char * mod_name; /* used for built-in modules */
struct module_kobject * mkobj;
int (*probe) (struct device * dev);
int (*remove) (struct device * dev);
void (*shutdown) (struct device * dev);
int (*suspend) (struct device * dev, pm_message_t state);
int (*resume) (struct device * dev);
};
二 注册总线
int driver_register(struct device_driver * drv)
{
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown)) {
printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
}
三 klist_init(&drv->klist_devices, NULL, NULL);
四 return bus_add_driver(drv);
}
三 klist_init()函数 是用来初始化 如下的函数
四 最重要的函数 就是bus_add_driver 函数
int bus_add_driver(struct device_driver *drv)
{
error = kobject_set_name(&drv->kobj, "%s", drv->name);
error = kobject_register(&drv->kobj);
klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
}