LCD控制器驱动流程

来源:互联网 发布:求生之路2mac版下载 编辑:程序博客网 时间:2024/05/01 07:05

                             LCD控制器驱动流程

LCD是一个片上设备,该设备对象及资源是静态初始化的并在系统启动时被添加到系统中去,

/* LCD Controller */

static struct resource s3c_lcd_resource[] = {  /*LCD资源*/

       [0] = {

              .start = S3C24XX_PA_LCD,   /*寄存器地址*/

              .end   = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,

              .flags = IORESOURCE_MEM,

       },

       [1] = {

              .start = IRQ_LCD,   /*中断号*/

              .end   = IRQ_LCD,

              .flags = IORESOURCE_IRQ,

       }

 

};

 

static u64 s3c_device_lcd_dmamask = 0xffffffffUL;

 

struct platform_device s3c_device_lcd = {   /*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

       }

}

 

/* LCD driver info */

/*LCD的初始化参数,包括寄存器的初始值,LCD的长宽等*/

static struct s3c2410fb_mach_info smdk2440_lcd_cfg __initdata = {

    .regs   = {   /*寄存器初始值*/

 

        .lcdcon1    = S3C2410_LCDCON1_TFT16BPP |

                  S3C2410_LCDCON1_TFT |

                  S3C2410_LCDCON1_CLKVAL(0x04),

 

        .lcdcon2    = S3C2410_LCDCON2_VBPD(7) |

                  S3C2410_LCDCON2_LINEVAL(319) |

                  S3C2410_LCDCON2_VFPD(6) |

                  S3C2410_LCDCON2_VSPW(3),

 

        .lcdcon3    = S3C2410_LCDCON3_HBPD(19) |

                  S3C2410_LCDCON3_HOZVAL(239) |

                  S3C2410_LCDCON3_HFPD(7),

 

        .lcdcon4    = S3C2410_LCDCON4_MVAL(0) |

                  S3C2410_LCDCON4_HSPW(3),

 

        .lcdcon5    = S3C2410_LCDCON5_FRM565 |

                  S3C2410_LCDCON5_INVVLINE |

                  S3C2410_LCDCON5_INVVFRAME |

                  S3C2410_LCDCON5_PWREN |

                  S3C2410_LCDCON5_HWSWP,

    },

 

#if 0

    /* currently setup by downloader */

    .gpccon     = 0xaa940659,

    .gpccon_mask    = 0xffffffff,

    .gpcup      = 0x0000ffff,

    .gpcup_mask = 0xffffffff,

    .gpdcon     = 0xaa84aaa0,

    .gpdcon_mask    = 0xffffffff,

    .gpdup      = 0x0000faff,

    .gpdup_mask = 0xffffffff,

#endif

 

    .lpcsel     = ((0xCE6) & ~7) | 1<<4,

 

    .width      = 240,  /**/

    .height     = 320,  /**/

 

    .xres       = {   /*坐标信息*/

        .min    = 240,

        .max    = 240,

        .defval = 240,

    },

 

    .yres       = {

        .min    = 320,

        .max    = 320,

        .defval = 320,

    },

 

    .bpp        = {   /*象素信息*/

        .min    = 16,

        .max    = 16,

        .defval = 16,

    },

};

这个对象可以在系统初始化时通过调用下面这个函数来保存在我们自己的内存区里,以便以后使用

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添加到系统设备树中去, 在随后把lcd驱动注册进系统后会匹配这个设备并调用驱动的probe函数。

接下来我们就详细看一下LCD的驱动, 实际上,几乎lcd设备驱动所要做的所有事情就是填充fb_info结构然后向系统注册或注销它。

首先看init函数:

int __devinit s3c2410fb_init(void)

{

    return platform_driver_register(&s3c2410fb_driver);  /*注册LCD驱动进系统*/

}

很简单就是把这个驱动注册进系统, 系统会找到LCD设备并调用这个驱动的probe函数。

static struct platform_driver s3c2410fb_driver = {

    .probe      = s3c2410fb_probe,  /*probe函数*/

    .remove     = s3c2410fb_remove,

    .suspend    = s3c2410fb_suspend,

    .resume     = s3c2410fb_resume,

    .driver     = {

        .name   = "s3c2410-lcd",  /*名字应与设备名匹配*/

        .owner  = THIS_MODULE,

    },

};

接下来重点看下probe函数

static char driver_name[]="s3c2410fb";

 

static int __init s3c2410fb_probe(struct platform_device *pdev)

{

    struct s3c2410fb_info *info;

    struct fb_info     *fbinfo;

    struct s3c2410fb_hw *mregs;

    int ret;

    int irq;

    int i;

    u32 lcdcon1;

 

    mach_info = pdev->dev.platform_data;  /*系统信息, 就是上面提到的smdk2440_lcd_cfg */

    if (mach_info == NULL) {

        dev_err(&pdev->dev,"no platform data for lcd, cannot attach/n");

        return -EINVAL;

    }

 

    mregs = &mach_info->regs;  /*通过上面的smdk2440_lcd_cfg */

 

    irq = platform_get_irq(pdev, 0);  /*获取中断号*/

    if (irq < 0) {

        dev_err(&pdev->dev, "no irq for device/n");

        return -ENOENT;

    }

 

    /*定义一个fb_info对象*/

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

    if (!fbinfo) {

        return -ENOMEM;

    }

   

   /*初始化fb_info对象*/

    info = fbinfo->par;

    info->fb = fbinfo;

    platform_set_drvdata(pdev, fbinfo);

 

    dprintk("devinit/n");

 

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

 

    memcpy(&info->regs, &mach_info->regs, sizeof(info->regs));

 

    /* Stop the video and unset ENVID if set */

    info->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;

    lcdcon1 = readl(S3C2410_LCDCON1);

    writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1); /*这里对着datasheet*/

 

    info->mach_info        = pdev->dev.platform_data;

 

    fbinfo->fix.type       = FB_TYPE_PACKED_PIXELS;

    fbinfo->fix.type_aux       = 0;

    fbinfo->fix.xpanstep       = 0;

    fbinfo->fix.ypanstep       = 0;

    fbinfo->fix.ywrapstep      = 0;

    fbinfo->fix.accel      = FB_ACCEL_NONE;

 

    fbinfo->var.nonstd     = 0;

    fbinfo->var.activate       = FB_ACTIVATE_NOW;

    fbinfo->var.height     = mach_info->height;

    fbinfo->var.width      = mach_info->width;

    fbinfo->var.accel_flags     = 0;

    fbinfo->var.vmode      = FB_VMODE_NONINTERLACED;

 

     /*LCD操作函数,实际上驱动主要就是实现这个函数集*/

    fbinfo->fbops          = &s3c2410fb_ops;

    fbinfo->flags          = FBINFO_FLAG_DEFAULT;

    fbinfo->pseudo_palette      = &info->pseudo_pal;

 

    fbinfo->var.xres       = mach_info->xres.defval;

    fbinfo->var.xres_virtual    = mach_info->xres.defval;

    fbinfo->var.yres       = mach_info->yres.defval;

    fbinfo->var.yres_virtual    = mach_info->yres.defval;

    fbinfo->var.bits_per_pixel  = mach_info->bpp.defval;

 

    /*这里对着datasheet*/

    fbinfo->var.upper_margin    = S3C2410_LCDCON2_GET_VBPD(mregs->lcdcon2) + 1;

    fbinfo->var.lower_margin    = S3C2410_LCDCON2_GET_VFPD(mregs->lcdcon2) + 1;

    fbinfo->var.vsync_len      = S3C2410_LCDCON2_GET_VSPW(mregs->lcdcon2) + 1;

 

    fbinfo->var.left_margin     = S3C2410_LCDCON3_GET_HFPD(mregs->lcdcon3) + 1;

    fbinfo->var.right_margin    = S3C2410_LCDCON3_GET_HBPD(mregs->lcdcon3) + 1;

    fbinfo->var.hsync_len      = S3C2410_LCDCON4_GET_HSPW(mregs->lcdcon4) + 1;

 

    fbinfo->var.red.offset      = 11;

    fbinfo->var.green.offset    = 5;

    fbinfo->var.blue.offset     = 0;

    fbinfo->var.transp.offset   = 0;

    fbinfo->var.red.length      = 5;

    fbinfo->var.green.length    = 6;

    fbinfo->var.blue.length     = 5;

    fbinfo->var.transp.length   = 0;

    fbinfo->fix.smem_len        =  mach_info->xres.max *

                   mach_info->yres.max *

                   mach_info->bpp.max / 8;

 

    for (i = 0; i < 256; i++)

        info->palette_buffer[i] = PALETTE_BUFF_CLEAR;

 

    if (!request_mem_region((unsigned long)S3C24XX_VA_LCD, SZ_1M, "s3c2410-lcd")) {

        ret = -EBUSY;

        goto dealloc_fb;

    }

 

    dprintk("got LCD region/n");

 

    ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info); /*注册中断*/

    if (ret) {

        dev_err(&pdev->dev, "cannot get irq %d - err %d/n", irq, ret);

        ret = -EBUSY;

        goto release_mem;

    }

   

    /*获取并打开LCD的时钟源*/

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

    if (!info->clk || IS_ERR(info->clk)) {

        printk(KERN_ERR "failed to get lcd clock source/n");

        ret = -ENOENT;

        goto release_irq;

    }

 

    clk_enable(info->clk);

    dprintk("got and enabled clock/n");

 

    msleep(1);

 

    /* Initialize video memory */

    ret = s3c2410fb_map_video_memory(info);  /*初始化LCD frame buffer*/

    if (ret) {

        printk( KERN_ERR "Failed to allocate video RAM: %d/n", ret);

        ret = -ENOMEM;

        goto release_clock;

    }

    dprintk("got video memory/n");

 

    ret = s3c2410fb_init_registers(info);  /*初始化LCD硬件*/

 

    ret = s3c2410fb_check_var(&fbinfo->var, fbinfo);

 

    /*把该frame buffer注册进系统, 以后对该设备文件的访问就会调到该驱动的操作函数集*/

    ret = register_framebuffer(fbinfo); 

    if (ret < 0) {

        printk(KERN_ERR "Failed to register framebuffer device: %d/n", ret);

        goto free_video_memory;

    }

 

    /* create device files */

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

 

    printk(KERN_INFO "fb%d: %s frame buffer device/n",

        fbinfo->node, fbinfo->fix.id);

 

    return 0;

 

free_video_memory:

    s3c2410fb_unmap_video_memory(info);

release_clock:

    clk_disable(info->clk);

    clk_put(info->clk);

release_irq:

    free_irq(irq,info);

release_mem:

    release_mem_region((unsigned long)S3C24XX_VA_LCD, S3C24XX_SZ_LCD);

dealloc_fb:

    framebuffer_release(fbinfo);

    return ret;

}

   这个函数主要就是分配并初始化一个fb_info对象,保存显卡的当前状态;初始化LCD硬件控制器, 初始化好LCDframe buffer等工作,

   s3c2410fb_map_video_memory这个函数就是用来分配frame buffer的函数。

/*

 * s3c2410fb_map_video_memory():

 *  Allocates the DRAM memory for the frame buffer.  This buffer is

 *  remapped into a non-cached, non-buffered, memory region to

 *  allow palette and pixel writes to occur without flushing the

 *  cache.  Once this area is remapped, all virtual memory

 *  access to the video memory should occur at the new region.

 */

static int __init s3c2410fb_map_video_memory(struct s3c2410fb_info *fbi)

{

    dprintk("map_video_memory(fbi=%p)/n", fbi);

 

    fbi->map_size = PAGE_ALIGN(fbi->fb->fix.smem_len + PAGE_SIZE); /*分配总数对齐*/

    fbi->map_cpu  = dma_alloc_writecombine(fbi->dev, fbi->map_size,

                          &fbi->map_dma, GFP_KERNEL);  /*分配DMA地址*/

 

    fbi->map_size = fbi->fb->fix.smem_len;

 

    if (fbi->map_cpu) {

        /* prevent initial garbage on screen */

        dprintk("map_video_memory: clear %p:%08x/n",

            fbi->map_cpu, fbi->map_size);

        memset(fbi->map_cpu, 0xf0, fbi->map_size);

 

        fbi->screen_dma    = fbi->map_dma;

        fbi->fb->screen_base   = fbi->map_cpu;

        fbi->fb->fix.smem_start  = fbi->screen_dma;

 

        dprintk("map_video_memory: dma=%08x cpu=%p size=%08x/n",

            fbi->map_dma, fbi->map_cpu, fbi->fb->fix.smem_len);

    }

 

    return fbi->map_cpu ? 0 : -ENOMEM;

}

这个函数分配完frame buffer地址后, 设置好相关参数就返回了.

接下来看看s3c2410fb_init_registers函数, 它实际上就是通过设备LCD寄存器来初始化LCD控制器。

/*

 * s3c2410fb_init_registers - Initialise all LCD-related registers

 */

static int s3c2410fb_init_registers(struct s3c2410fb_info *fbi)

{

    unsigned long flags;

 

    /* Initialise LCD with values from haret */

 

    local_irq_save(flags);  /*关中断*/

 

    /* modify the gpio(s) with interrupts set (bjd) */

    /*结合datasheet来看*/

    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);  /*开中断*/

    /*结合datasheet来看*/

    writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);

    writel(fbi->regs.lcdcon2, S3C2410_LCDCON2);

    writel(fbi->regs.lcdcon3, S3C2410_LCDCON3);

    writel(fbi->regs.lcdcon4, S3C2410_LCDCON4);

    writel(fbi->regs.lcdcon5, S3C2410_LCDCON5);

 

    s3c2410fb_set_lcdaddr(fbi);  /*设置好LCD的缓冲地址*/

 

    dprintk("LPCSEL    = 0x%08lx/n", mach_info->lpcsel);

    writel(mach_info->lpcsel, S3C2410_LPCSEL);

 

    dprintk("replacing TPAL %08x/n", readl(S3C2410_TPAL));

 

    /* ensure temporary palette disabled */

    writel(0x00, S3C2410_TPAL);

 

    /* Enable video by setting the ENVID bit to 1 */

    fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID;

    writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);

    return 0;

}

该函数主要就是设置LCD控制器, 可以结合datasheet来看代码。

接下来看一下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子系统的*/

    .fb_fillrect    = cfb_fillrect, 

    .fb_copyarea    = cfb_copyarea,

    .fb_imageblit   = cfb_imageblit,

};

我们驱动要做的就是实现上面这个ops集, 集体函数可以看源代码,这里不再讲述

原创粉丝点击