嵌入式Linux之我行——S3C2440上LCD驱动(FrameBuffer)实例开发讲解

发布者:等放假的zr0最新更新时间:2016-04-27 来源: eefocus关键字:Linux  S3C2440  LCD驱动 手机看文章 扫描二维码
随时随地手机看文章
   开发环境
  • 主  机:VMWare--Fedora 9
  • 开发板:Mini2440--64MB Nand, Kernel:2.6.30.4
  • 编译器:arm-linux-gcc-4.3.2

上接:S3C2440上LCD驱动(FrameBuffer)实例开发详解(一)

四、帧缓冲(FrameBuffer)设备驱动实例代码:

①、建立驱动文件:my2440_lcd.c,依就是驱动程序的最基本结构:FrameBuffer驱动的初始化和卸载部分及其他,如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 



static char driver_name[] = "my2440_lcd";


struct my2440fb_var
{
    int lcd_irq_no;           
    struct clk *lcd_clock;    
    struct resource *lcd_mem; 
    void __iomem *lcd_base;   
    struct device *dev;

    struct s3c2410fb_hw regs; 

    
    u32    palette_buffer[256]; 

    u32 pseudo_pal[16];   
    unsigned int palette_ready; 
};


#define PALETTE_BUFF_CLEAR (0x80000000)    


static struct platform_driver lcd_fb_driver 
{
    .probe     lcd_fb_probe,               
    .remove    __devexit_p(lcd_fb_remove), 
    .suspend   lcd_fb_suspend,             
    .resume    lcd_fb_resume,              
    .driver    
    {
        
        .name "s3c2410-lcd",
        .owner THIS_MODULE,
    },
};

static int __init lcd_init(void)
{
    
    return platform_driver_register(&lcd_fb_driver);
}

static void __exit lcd_exit(void)
{
    
    platform_driver_unregister(&lcd_fb_driver);
}

module_init(lcd_init);
module_exit(lcd_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Huang Gang");
MODULE_DESCRIPTION("My2440 LCD FrameBuffer Driver");


②、LCD平台设备各接口函数的实现:


static int __devinit lcd_fb_probe(struct platform_device *pdev)
{
    int i;
    int ret;
    struct resource *res;  
    struct fb_info  *fbinfo; 
    struct s3c2410fb_mach_info *mach_info; 
    struct my2440fb_var *fbvar; 
    struct s3c2410fb_display *display; 

    
    mach_info pdev->dev.platform_data;
    if(mach_info == NULL)
    {
        
        dev_err(&pdev->dev, "no platform data for lcd\n");
        return -EINVAL;
    }

    
    display mach_info->displays mach_info->default_display;

   
    fbinfo framebuffer_alloc(sizeof(struct my2440fb_var), &pdev->dev);
    if(!fbinfo)
    {
        dev_err(&pdev->dev, "framebuffer alloc of registers failed\n");
        ret -ENOMEM;
        goto err_noirq;
    }
    platform_set_drvdata(pdev, fbinfo);

    
    fbvar fbinfo->par;
    fbvar->dev &pdev->dev;

    
    fbvar->lcd_irq_no platform_get_irq(pdev, 0);
    if(fbvar->lcd_irq_no 0)
    {
        
        dev_err(&pdev->dev, "no lcd irq for platform\n");
        return -ENOENT;
    }

    
    res platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if(res == NULL)
    {
        
        dev_err(&pdev->dev, "failed to get memory region resource\n");
        return -ENOENT;
    }

    
    fbvar->lcd_mem request_mem_region(res->start, res->end res->start 1,pdev->name);
    if(fbvar->lcd_mem == NULL)
    {
        
        dev_err(&pdev->dev, "failed to reserve memory region\n");
        return -ENOENT;
    }

    
    fbvar->lcd_base ioremap(res->start, res->end res->start 1);
    if(fbvar->lcd_base == NULL)
    {
        
        dev_err(&pdev->dev, "ioremap() of registers failed\n");
        ret -EINVAL;
        goto err_nomem;
    }

    
    fbvar->lcd_clock clk_get(NULL, "lcd");
    if(!fbvar->lcd_clock)
    {
        
        dev_err(&pdev->dev, "failed to find lcd clock source\n");
        ret -ENOENT;
        goto err_nomap;
    }
    
    clk_enable(fbvar->lcd_clock);

    
    ret request_irq(fbvar->lcd_irq_no, lcd_fb_irq, IRQF_DISABLED, pdev->name, fbvar);
    if(ret)
    {
        
        dev_err(&pdev->dev, "IRQ%d error %d\n", fbvar->lcd_irq_no, ret);
        ret -EBUSY;
        goto err_noclk;
    }
    

    
    
    strcpy(fbinfo->fix.id, driver_name);
    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.accel_flags     0;
    fbinfo->var.vmode           FB_VMODE_NONINTERLACED;
    fbinfo->var.xres            display->xres;
    fbinfo->var.yres            display->yres;
    fbinfo->var.bits_per_pixel  display->bpp;

   
    fbinfo->fbops               &my2440fb_ops;

    fbinfo->flags               FBINFO_FLAG_DEFAULT;

    fbinfo->pseudo_palette      = &fbvar->pseudo_pal;

 

   
    for(i 0; 256; i++)
    {
        fbvar->palette_buffer[i] PALETTE_BUFF_CLEAR;
    }


    for (i 0; mach_info->num_displays; i++) 
    {
        
        unsigned long smem_len (mach_info->displays[i].xres *mach_info->displays[i].yres mach_info->displays[i].bpp) >> 3;

        if(fbinfo->fix.smem_len smem_len)
        {
            fbinfo->fix.smem_len smem_len;
        }
    }

    
    msleep(1);

    
    my2440fb_init_registers(fbinfo);

    
    my2440fb_check_var(fbinfo);
    
    
    ret my2440fb_map_video_memory(fbinfo);
    if (ret) 
    {
        dev_err(&pdev->dev, "failed to allocate video RAM: %d\n", ret);
        ret -ENOMEM;
        goto err_nofb;
    }

    
    ret register_framebuffer(fbinfo);
    if (ret 0) 
    {
        dev_err(&pdev->dev, "failed to register framebuffer device: %d\n", ret);
        goto err_video_nomem;
    }

    
    ret device_create_file(&pdev->dev, &dev_attr_debug);
    if (ret) 
    {
        dev_err(&pdev->dev, "failed to add debug attribute\n");
    }

    return 0;


err_nomem:
    release_resource(fbvar->lcd_mem);
    kfree(fbvar->lcd_mem);

err_nomap:
    iounmap(fbvar->lcd_base);

err_noclk:
    clk_disable(fbvar->lcd_clock);
    clk_put(fbvar->lcd_clock);

err_noirq:
    free_irq(fbvar->lcd_irq_no, fbvar);

err_nofb:
    platform_set_drvdata(pdev, NULL);
    framebuffer_release(fbinfo);

err_video_nomem:
    my2440fb_unmap_video_memory(fbinfo);

    return ret;
}


static irqreturn_t lcd_fb_irq(int irq, void *dev_id)
{
    struct my2440fb_var    *fbvar dev_id;
    void __iomem *lcd_irq_base;
    unsigned long lcdirq;

    
    lcd_irq_base fbvar->lcd_base S3C2410_LCDINTBASE;

    
    lcdirq readl(lcd_irq_base S3C24XX_LCDINTPND);

    
    if(lcdirq S3C2410_LCDINT_FRSYNC)
    {
        
        if (fbvar->palette_ready)
        {
            my2440fb_write_palette(fbvar);
        }

        
        writel(S3C2410_LCDINT_FRSYNC, lcd_irq_base S3C24XX_LCDINTPND);
        writel(S3C2410_LCDINT_FRSYNC, lcd_irq_base S3C24XX_LCDSRCPND);
    }

    return IRQ_HANDLED;
}


static void my2440fb_write_palette(struct my2440fb_var *fbvar)
{
    unsigned int i;
    void __iomem *regs fbvar->lcd_base;

    fbvar->palette_ready 0;

    for (i 0; 256; i++) 
    {
        unsigned long ent fbvar->palette_buffer[i];

        if (ent == PALETTE_BUFF_CLEAR)
        {
            continue;
        }

        writel(ent, regs S3C2410_TFTPAL(i));

        if (readw(regs S3C2410_TFTPAL(i)) == ent)
        {
            fbvar->palette_buffer[i] PALETTE_BUFF_CLEAR;
        }
        else
        {
            fbvar->palette_ready 1;
        }
    }
}


static int my2440fb_init_registers(struct fb_info *fbinfo)
{
    unsigned long flags;
    void __iomem *tpal;
    void __iomem *lpcsel;

    
    struct my2440fb_var    *fbvar fbinfo->par;
    struct s3c2410fb_mach_info *mach_info fbvar->dev->platform_data;

    
    tpal fbvar->lcd_base S3C2410_TPAL;
    lpcsel fbvar->lcd_base S3C2410_LPCSEL;

    
    local_irq_save(flags);

    
    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);

    writel(0x00, tpal);
    writel(mach_info->lpcsel, lpcsel);

    return 0;
}


static inline void modify_gpio(void __iomem *reg, unsigned long set, unsigned long mask)
{
    unsigned long tmp;

    tmp readl(reg) ~mask;
    writel(tmp set, reg);
}


static int my2440fb_check_var(struct fb_info *fbinfo)
{
    unsigned i;

    
    struct fb_var_screeninfo *var &fbinfo->var;
    struct my2440fb_var    *fbvar fbinfo->par;
    struct s3c2410fb_mach_info *mach_info fbvar->dev->platform_data;

    struct s3c2410fb_display *display NULL;
    struct s3c2410fb_display *default_display mach_info->displays +mach_info->default_display;
    int type default_display->type;

    
    if (var->yres == default_display->yres && 
        var->xres == default_display->xres && 
        var->bits_per_pixel == default_display->bpp)
    {
        display default_display;
    }
    else
    {
        for (i 0; mach_info->num_displays; i++)
        {
            if (type == mach_info->displays[i].type &&
             var->yres == mach_info->displays[i].yres &&
             var->xres == mach_info->displays[i].xres &&
             var->bits_per_pixel == mach_info->displays[i].bpp) 
            {
                display mach_info->displays i;
                break;
            }
        }
    }

    if (!display) 
    {
        return -EINVAL;
    }

    
    fbvar->regs.lcdcon1 display->type;
    fbvar->regs.lcdcon5 display->lcdcon5;

    
    var->xres_virtual display->xres;
    var->yres_virtual display->yres;
    var->height display->height;
    var->width display->width;

    
    var->pixclock display->pixclock;
    var->left_margin display->left_margin;
    var->right_margin display->right_margin;
    var->upper_margin display->upper_margin;
    var->lower_margin display->lower_margin;
    var->vsync_len display->vsync_len;
    var->hsync_len display->hsync_len;

    
    var->transp.offset 0;
    var->transp.length 0;

    
    switch (var->bits_per_pixel) 
    {
        case 1:
        case 2:
        case 4:
            var->red.offset  0;
            var->red.length  var->bits_per_pixel;
            var->green       var->red;
            var->blue        var->red;
            break;
        case 8:
            if (display->type != S3C2410_LCDCON1_TFT) 
            {
                var->red.length     3;
                var->red.offset     5;
                var->green.length   3;
                var->green.offset   2;
                var->blue.length    2;
                var->blue.offset    0;
            }else{
                var->red.offset     0;
                var->red.length     8;
                var->green          var->red;
                var->blue           var->red;
            }
            break;
        case 12:
            var->red.length         4;
            var->red.offset         8;
            var->green.length       4;
            var->green.offset       4;
            var->blue.length        4;
            var->blue.offset        0;
            break;
        case 16:
            if (display->lcdcon5 S3C2410_LCDCON5_FRM565) 
            {
                
                var->red.offset      11;
                var->green.offset    5;
                var->blue.offset     0;
                var->red.length      5;
                var->green.length    6;
                var->blue.length     5;
            else {
                
                var->red.offset      11;
                var->green.offset    6;
                var->blue.offset     1;
                var->red.length      5;
                var->green.length    5;
                var->blue.length     5;
            }
            break;
        case 32:
            var->red.length        8;
            var->red.offset        16;
            var->green.length      8;
            var->green.offset      8;
            var->blue.length       8;
            var->blue.offset       0;
            break;
    }

    return 0;
}


static int __init my2440fb_map_video_memory(struct fb_info *fbinfo)
{
    dma_addr_t map_dma;
    struct my2440fb_var    *fbvar fbinfo->par;
    unsigned map_size PAGE_ALIGN(fbinfo->fix.smem_len);

    
    fbinfo->screen_base dma_alloc_writecombine(fbvar->dev, map_size, &map_dma,GFP_KERNEL);

    if (fbinfo->screen_base) 
    {
        
        memset(fbinfo->screen_base, 0x00, map_size);

        
        fbinfo->fix.smem_start map_dma;
    }

    return fbinfo->screen_base -ENOMEM;
}


static inline void my2440fb_unmap_video_memory(struct fb_info *fbinfo)
{
    struct my2440fb_var    *fbvar fbinfo->par;
    unsigned map_size PAGE_ALIGN(fbinfo->fix.smem_len);

    
    dma_free_writecombine(fbvar->dev, map_size, fbinfo->screen_base,fbinfo->fix.smem_start);
}



static int __devexit lcd_fb_remove(struct platform_device *pdev)
{
    struct fb_info *fbinfo platform_get_drvdata(pdev);
    struct my2440fb_var    *fbvar fbinfo->par;

    
    unregister_framebuffer(fbinfo);

    
    my2440fb_lcd_enable(fbvar, 0);

    
    msleep(1);

    
    my2440fb_unmap_video_memory(fbinfo);

    
    platform_set_drvdata(pdev, NULL);
    framebuffer_release(fbinfo);

    
    free_irq(fbvar->lcd_irq_no, fbvar);

    
    if (fbvar->lcd_clock) 
    {
        clk_disable(fbvar->lcd_clock);
        clk_put(fbvar->lcd_clock);
        fbvar->lcd_clock NULL;
    }

    
    iounmap(fbvar->lcd_base);

    
    release_resource(fbvar->lcd_mem);
    kfree(fbvar->lcd_mem);

    return 0;
}


static void my2440fb_lcd_enable(struct my2440fb_var *fbvar, int enable)
{
    unsigned long flags;

    
    local_irq_save(flags);

    if (enable)
    {
        fbvar->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID;
    }
    else
    {
        fbvar->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;
    }

    writel(fbvar->regs.lcdcon1, fbvar->lcd_base S3C2410_LCDCON1);

    
    local_irq_restore(flags);
}


#ifdef CONFIG_PM

static int lcd_fb_suspend(struct platform_device *pdev, pm_message_t state)
{
    
    struct fb_info *fbinfo platform_get_drvdata(pdev);
    struct my2440fb_var    *fbvar fbinfo->par;

    
    my2440fb_lcd_enable(fbvar, 0);

    msleep(1);

    
    clk_disable(fbvar->lcd_clock);

    return 0;
}

static int lcd_fb_resume(struct platform_device *pdev)
{
    
    struct fb_info *fbinfo platform_get_drvdata(pdev);
    struct my2440fb_var    *fbvar fbinfo->par;

    
    clk_enable(fbvar->lcd_clock);

    
    msleep(1);

    
    my2440fb_init_registers(fbinfo);

    
    my2440fb_activate_var(fbinfo);

    
    my2440fb_blank(FB_BLANK_UNBLANK, fbinfo);

    return 0;
}
#else

#define lcd_fb_suspend    NULL
#define lcd_fb_resume    NULL
#endif

 

关键字:Linux  S3C2440  LCD驱动 引用地址:嵌入式Linux之我行——S3C2440上LCD驱动(FrameBuffer)实例开发讲解

上一篇:嵌入式Linux之我行——S3C2440上LCD驱动(FrameBuffer)实例开发讲解
下一篇:嵌入式Linux之我行——S3C2440上LCD驱动(FrameBuffer)实例开发讲解

推荐阅读最新更新时间:2024-03-16 14:51

基于LabVIEW8.6和S3C2440的手持数字波形表的界面设计
    将LabVIEW设计的虚拟仪器程序移植到运行WindowsCE的便携式手持设备上。可以极大地提高嵌入式系统软件开发效率。具体提出一种有效解决数据波形交替显示的界面设计,阐述了如何使用LabVIEW的触摸屏toueh panel模块开发的数字波形表用于Windows CE 5.0设备的测试项目的案例。 1 开发平台简介 1.1 基础平台     本文设计的数字波形表采用高性能、低功耗、高集成度的S3C2440A微处理器。触摸屏配合采用S3C2440A的高速处理器驱动,具有更好的视频显示效果。 1.2 软件平台    LabVIEW是美国国家仪器公司推出的一种虚拟仪器开发平台。LabVIEW包含有很多的
[嵌入式]
反汇编解析S3C2440汇编点灯
代码 首先回顾代码 /* *点亮LED:GPF4 */ .text //表明它是代码段 .global _start _start: /*配置GPF4为输出引脚 *把0x100写到地址0x56000050上,熄灭led */ ldr r1, =0x56000050 /*将这个地址存放到r1中*/ ldr r0, =0x100 /*或者使用 mov r0, #0x100 将0x100放入r0 */ str r0, /*将r0的值写入到r1的地址中*/ /*设置GPF4输出高电平 *把0x00写到地址0x56000054上,点亮led */ ldr r1, =0x56000054
[单片机]
反汇编解析<font color='red'>S3C2440</font>汇编点灯
基于 ARM 和 Linux 通用工控平台设计与实现
随着工控技术的进步和市场竞争的加剧,开发人员通常需要在尽可能短的时间内设计出满足用户要求的测控系统。本文针对嵌入式系统的特点,以高性价比的32位ARM嵌入式处理器AT91RM9200为硬件核心,搭建了通用工控硬件平台,在此平台上移植嵌入式Linux操作系统和图形界面开发环境MiniGUI。以此通用工控平台为基础,可以方便地构建工程应用所需的绝大部分自动测控系统。其应用无论是在性能还是在成本方面都极具竞争力,这预示着本平台具有较好的应用前景。 本工控平台在硬件上,选择ATMEL公司的AT91RM9200微处理器,并对其最小系统及外围部件进行设计,以适应当前工控现场更加丰富的技术要求,并结合工业测控Modbus协议,扩展多种通信接口,
[单片机]
基于 ARM 和 <font color='red'>Linux</font> 通用工控平台设计与实现
基于S3C2410的磁场测量系统的设计
1 引言 随着科技的发展,嵌入式操作系统在越来越多的领域发挥着重要的作用,目前已成为产品技术水平的标志之一。其中Linux因为其拥有开放性、多用户、多任务、良好的用户界面、丰富的网络功能、可靠的系统安全和良好的可移植等特性被广泛的应用到仪器测量设备中。    传统的磁场测量设备(持斯拉计、高斯计)普遍存在精度低(典型测量精度为1.5%)、操作不便等缺点。本文提出一种基于嵌入式Linux的中频磁场测量系统,它不但可以满足当前磁场测量数据采集的需要,还因为其嵌入了操作系统Linux,使具有可靠性好、升级方便的特点,既提高了磁场测量的准确性,又为仪器的功能升级带来便利。可应用于实验室仪器,医疗仪器,姿态控制,安全检测等需磁场检测的领域。
[单片机]
基于S3C2410的磁场测量系统的设计
GNU ARM汇编--(十)s3c2440的RTC
RTC 概述 在系统电源关掉时RTC可以在备份电池的支持下来工作.RTC可以使用STRB/LDRB指令传输8bit的BCD值到CPU.数据包括秒,分,时,日期,天,月和年.RTC工作在外部32.768KHz的晶振下,而且有报警功能. 属性 BCD:秒,分,时,日期,天,月和年 闰年产生器 报警功能:报警中断 从power-off模式唤醒 独立的电源管脚(RTCVDD) 为RTOS kernel time tick支持毫秒级的tick. 闰年产生器 闰年产生器通过BCDDATA,BCDMON和BCDYEAR来决定每个月最后一天的日期.一个8bit的计数器只能表示两个BCD码,所以无法决定'
[单片机]
新版U-boot2012.04.01移植(二)(JZ2440-S3C2440)
1、分析u-boot启动过程 根据u-boot编译过程分析。 然后打开u-boot.lds文件:
[单片机]
新版U-boot2012.04.01移植(二)(JZ2440-S3C2440)
基于嵌入式Linux与S3C2410平台的视频采集
随着多媒体技术、网络技术的迅猛发展和后PC机时代的到来,利用嵌入式系统实现远程视频监控、可视电话和视频会议等应用已成为可能。为了实现这些应用,实时获得视频数据是一个重要环节。针对这一点,本文在基于嵌入式Linux系统平台上,利用Video4Linux内核应用编程接口函数,实现了单帧图像和视频连续帧的采集,并保存成文件的形式供进一步视频处理和网络传输用。 1 系统平台上的硬件系统 本文使用的系统平台硬件功能框图如图1所示。该平台采用Samsung公司的处理器S3C2410。该处理器内部集成了ARM公司ARM920T处理器核的32位微控制器,资源丰富,带独立的16KB的指令Cache和16KB数据Cache、LCD控制器、RAM控
[单片机]
基于嵌入式<font color='red'>Linux</font>与S3C2410平台的视频采集
基于嵌入式ARM Linux步进电机驱动程序的设计
0 引言 随着激光雕刻机的不断发展和改进,嵌入式Linux的激光雕刻机比CNC(Computer numerical control)激光雕刻的优势不断显现,它大幅度提高了处理能力,方便了设计开发,节约了成本,是未来经济型激光雕刻机发展的趋势。而嵌入式ARM(Advanced RISC Machines)Linux步进电机驱动是实现激光雕刻的核心。 嵌入式开发过程中,经常需要为特定设备开发驱动程序。这些驱动程序的编写和编译与PC上的Linux驱动开发相比存在明显的差异,需要考虑的因素较多,实现过程较为复杂。本文以Samsung公司的友善之譬S3C2440开发板为例,探讨如何使用嵌入式Linux开发字符设备驱动程序来驱动步进
[单片机]
基于嵌入式ARM <font color='red'>Linux</font>步进电机驱动程序的设计
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved