zoukankan      html  css  js  c++  java
  • 基于ok6410的韦东山驱动视频简要分析lcd驱动

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/errno.h>
    #include <linux/string.h>
    #include <linux/mm.h>
    #include <linux/slab.h>
    #include <linux/vmalloc.h>
    #include <linux/delay.h>
    #include <linux/interrupt.h>
    #include <asm/uaccess.h>
    #include <linux/fb.h>
    #include <linux/init.h>
    #include <linux/clk.h>
    #include <linux/dma-mapping.h>
    
    static struct fb_info *s3c_lcd_info;
    static u32 s3c_pseudo_palette[16];
    
    static volatile unsigned long *gpbcon;
    static volatile unsigned long *gpbdat;
    static volatile unsigned long *gpfcon;
    static volatile unsigned long *gpfdat;
    static volatile unsigned long *gpicon;
    static volatile unsigned long *gpjcon;
    
    static volatile unsigned long *mifpcon;
    static volatile unsigned long *spcon;
    
    
    一个一个ioremap 太麻烦,统一放一结构里面,从6410的芯片手册上得到以下的寄存器P455
    struct s3c6410_lcd_regs {   
    	unsigned long vidcon0;    //视频时钟源
    	unsigned long vidcon1;
    	unsigned long vidcon2;
    	unsigned long reserver1;
    	unsigned long vidtcon0;
    	unsigned long vidtcon1;
    	unsigned long vidtcon2;
    	unsigned long reserver2;
    	unsigned long wincon0;
    	unsigned long wincon1;
    	unsigned long wincon2;
    	unsigned long wincon3;
    	unsigned long wincon4;
    	unsigned long reserver3[3];
    	unsigned long vidosd0a;
    	unsigned long vidosd0b;
    	unsigned long vidosd0c;
    	unsigned long reserver4;
    	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 reserver5;
    	unsigned long vidosd4a;
    	unsigned long vidosd4b;
    	unsigned long vidosd4c;
    	unsigned long reserver6[5];
    	unsigned long vidw00add0b0;
    	unsigned long vidw00add0b1;
    	unsigned long vidw01add0b0;
    	unsigned long vidw01add0b1;
    	unsigned long vidw02add0;
    	unsigned long reserver7;
    	unsigned long vidw03add0;
    	unsigned long reserver8;
    	unsigned long vidw04add0;
    	unsigned long reserver9[3];
    	unsigned long vidw00add1b0;
    	unsigned long vidw00add1b1;
    };
    
    static int s3c_fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
    			 u_int transp, struct fb_info *info)
    {
    	u32 color = 0;
    	u32 *p;
    
    	/* 0xRRGGBB */
    	/* red   = 0xAABB 
    	 * green = 0xCCDD
    	 * blue  = 0xEEFF
    	 * color = 0xAACCEE
    	 */
    
    	red   = (red >> (16 - info->var.red.length)) << info->var.red.offset; // 0xAA0000
    	green = (green >> (16 - info->var.green.length)) << info->var.green.offset;  // 0xCC00
    	blue  = (blue >> (16 - info->var.blue.length)) << info->var.blue.offset;     // 0xEE
    	
    	color = red | green | blue; // 0xAACCEE
    
    	p = info->pseudo_palette;
    	p[regno] = color; 
    	
    	return 0;
    }
    
    static struct fb_ops s3c_fb_ops = {
    	.fb_setcolreg	= s3c_fb_setcolreg,
    	.fb_fillrect	= cfb_fillrect,
    	.fb_copyarea	= cfb_copyarea,
    	.fb_imageblit	= cfb_imageblit,
    };
    
    static struct s3c6410_lcd_regs * lcd_regs;
    
    static int s3c_lcd_init(void)
    {
    	struct clk *clk;
    	int hclk;
    	int clkval;
    	
    	/* 1. 分配fb_info */
    	s3c_lcd_info = framebuffer_alloc(0, NULL);
    
    	/* 2. 设置 */
    	/* 2.1 设置固定的信息,在fb_info 结构体里的fix*/
    	strcpy(s3c_lcd_info->fix.id, "uplooking_lcd");
    	// s3c_lcd_info->fix.smem_start  /* 显存物理地址 */
    	s3c_lcd_info->fix.smem_len    = 480*272*32/8;//看lcd手册,4位
    	s3c_lcd_info->fix.type        = FB_TYPE_PACKED_PIXELS;//在fb.h里的FB_TYPE里第一个
    	s3c_lcd_info->fix.visual      = FB_VISUAL_TRUECOLOR;  //TFT屏是真彩色
    	s3c_lcd_info->fix.line_length = 480*32/8; //480乘以272,一个像素4位
    	
    	/* 2.2 设置可变的信息,fb_info结构体的var */
    	s3c_lcd_info->var.xres           = 480;
    	s3c_lcd_info->var.yres           = 272;
    	s3c_lcd_info->var.xres_virtual   = 480;
    	s3c_lcd_info->var.yres_virtual   = 272;
    	s3c_lcd_info->var.bits_per_pixel = 32;  //每个像素用多少位
    
    	// 0xRRGGBB
    	s3c_lcd_info->var.red.length     = 8;
    	s3c_lcd_info->var.red.offset     = 16;   //从16位开始,长度为8
    
    	s3c_lcd_info->var.green.length     = 8;
    	s3c_lcd_info->var.green.offset     = 8;
    
    	s3c_lcd_info->var.blue.length     = 8;
    	s3c_lcd_info->var.blue.offset     = 0;
    
    	s3c_lcd_info->var.activate        = FB_ACTIVATE_NOW;// 看不懂就用默认值为0 的
    
    	/* 2.3 设置操作函数 */
    	s3c_lcd_info->fbops = &s3c_fb_ops;
    
    	/* 2.4 设置其他信息 */
    	//s3c_lcd_info->screen_base = ;/* 显存的虚拟地址 */
    	s3c_lcd_info->screen_size = 480*272*4;
    	s3c_lcd_info->pseudo_palette = &s3c_pseudo_palette;//调色板,
    
    	/* 4. 硬件相关的操作 */
    	/* 4.1 配置GPIO用于LCD */
    	gpicon = ioremap(0x7F008100, 4);
    	*gpicon = 0xAAAAAAAA;
    
    	gpjcon = ioremap(0x7F008120, 4);
    	*gpjcon = 0xAAAAAAAA;
    
    	gpbcon = ioremap(0x7F008020, 4);
    	gpbdat = gpbcon + 1;
    	*gpbcon &= ~(0xf<<24);
    	*gpbcon |= (0x1<<24);   /* backlight enable pin */
    
    	gpfcon = ioremap(0x7F0080A0, 4);
    	gpfdat = gpfcon + 1;
    	*gpfcon &= ~(3<<28);
    	*gpfcon |= (1<<28); /* GPF14 use as PWREN */
    
    	/* 4.2 根据LCD的性能设置LCD控制器 */
    	/* MIFPCON:.调制解调器接口端口控制寄存器,6410手册要求就要这么设置 */
    	
    	*mifpcon &= ~(1<<3);
    	
    	/*spcon:特殊管脚功能寄存器,下面选择01=RGB*/
    	spcon = ioremap(0x7F0081A0, 4);
    	*spcon &= ~(3);
    	*spcon |= 1;
    
    	lcd_regs = ioremap(0x77100000, sizeof(struct s3c6410_lcd_regs));
    
    	clk = clk_get(NULL, "lcd");    /*struct clk *clk_get(struct device *dev, const char *id)*/
    	clk_enable(clk);  /* HCLK_GATE[3]设为1 */
    	/*
    	  HCLK_GATE = ioremap(0x7E00F030, 4);
    	* HCLK_GATE |= (1<<3);
    	*/
    
    	/* bit[13:6], CLKVAL_F,VCLK = Video Clock Source / (CLKVAL+1)=133/15=9MHZ,
    	                       VCLK = 9000000
    	              CLKVAL_F = hclk/9000000 - 1=14
    	 * bit[3:2], CLKSEL_F, 00 = HCLK
    	 * bit[1], ENVID-使能LCD控制器的输出, 先设为0, 最后全部设置好了再设为1
    	 */
    	clk = clk_get(NULL, "hclk");
    	hclk = clk_get_rate(clk);
    	clkval = hclk/9000000 - 1;
    	printk("hclk = %d, clkval = %d\n", hclk, clkval);
    	lcd_regs->vidcon0 = (0<<29)|(0<<26) | (0<<17) | (clkval << 6) | (1<<4);
    
    	/* bit[7], 0-在VLCK下降沿读数据,通过lcd手册的时序图看是上升还是下降
         * bit[6], HSYNC低脉冲有效	
         * bit[5], VSYNC低脉冲有效	
    	 * bit[4], VDEN高电平有效
    	 */	
    	lcd_regs->vidcon1 = (0<<7) | (1<<6) | (1<<5) | (0<<4);
    
    	/* LCD手册P11
    	 * tvp = 10, tvb = 2, tvf=2
    	 */
    	lcd_regs->vidtcon0 = (1<<16) | (1<<8) | (9<<0);
    
    	/* LCD手册P11
    	 * thp = 41, thb = 2, thf=2
    	 */
    	lcd_regs->vidtcon1 = (1<<16) | (1<<8) | (40<<0);
    
    	lcd_regs->vidtcon2 = (271<<11) | (479<<0);
    
    	/* 4.3 分配显存, 并告诉LCD控制器,专业的函数来分配显存!不用kmalloc! */
    	s3c_lcd_info->screen_base = dma_alloc_writecombine(NULL, s3c_lcd_info->screen_size, &s3c_lcd_info->fix.smem_start, GFP_KERNEL);
    	//void *dma_alloc_writecombine(struct device *dev, size_t size,
    			     dma_addr_t *handle, gfp_t gfp)
    
    	/* bit[17],BSWP = 0, bit[16],HWSWP = 0 
    	 * bit[5:2],BPPMODE_F, 0b1011, 24bpp
    	 * bit[0], ENWIN_F, 0-先不使能
    	 */
    	lcd_regs->wincon0 &= ~(0xf << 2);
    	lcd_regs->wincon0 |= (0xb<<2);    /* unpacked 24 BPP (non-palletized R:8-G:8-B:8 ) */
    
    #define LeftTopX     0
    #define LeftTopY     0
    
    #define RightBotX   479
    #define RightBotY   271
    
    	lcd_regs->vidosd0a = (LeftTopX<<11) | (LeftTopY << 0);           //6410手册 P473
    	lcd_regs->vidosd0b = (RightBotX<<11) | (RightBotY << 0);
    	lcd_regs->vidosd0c = 480*272*4 / 4;
    
    	lcd_regs->vidw00add0b0 = s3c_lcd_info->fix.smem_start;    //窗口 0 的缓冲区开始地址控制寄存器,缓冲区 0 
    	lcd_regs->vidw00add1b0 = (s3c_lcd_info->fix.smem_start + s3c_lcd_info->fix.smem_len) & 0xffffff;                       //窗口 0 的缓冲区结束地址控制寄存器,缓冲区 0		
    
    	/* 4.4 使能 */
    	*gpbdat |= (1<<6);  /* 使能背光 */
    	*gpfdat |= (1<<14); /* PWREN,提供电源 */
    	lcd_regs->vidcon0 |= (3); /* 使能LCD控制器输出信号 */
    	lcd_regs->wincon0 |= (1<<0); /* 使能窗口0 */
    		
    	/* 3. 注册 */
    	register_framebuffer(s3c_lcd_info);
    
    	
    	return 0;
    }
    
    static void s3c_lcd_exit(void)
    {
    	struct clk *clk;
    	
    	unregister_framebuffer(s3c_lcd_info);
    
    	*gpbdat &= ~(1<<6);  /* 禁止背光 */
    	*gpfdat &= ~(1<<14);  /* 禁止PWREN */
    	lcd_regs->vidcon0 &= ~(1<<1); /* 禁止LCD控制器输出信号 */
    	lcd_regs->wincon0 &= ~(1<<0); /* 禁止窗口0 */
    
    	dma_free_writecombine(NULL, s3c_lcd_info->screen_size, s3c_lcd_info->screen_base, s3c_lcd_info->fix.smem_start);
    
    	clk = clk_get(NULL, "lcd");
    	clk_disable(clk);  /* HCLK_GATE[3]设为0 */
    
    	iounmap(lcd_regs);
    	iounmap(gpbcon);
    	iounmap(gpfcon);
    	iounmap(gpicon);
    	iounmap(gpjcon);
    	iounmap(mifpcon);
    	iounmap(spcon);
    
    	framebuffer_release(s3c_lcd_info);
    	
    }
    
    module_init(s3c_lcd_init);
    module_exit(s3c_lcd_exit);
    
    MODULE_LICENSE("GPL");
    


     

  • 相关阅读:
    用dos批处理程序检测是否安装.netframework,并自动安装后运行指定程序(.net自启动光盘的制做)
    生成pdf文件的好东西,itextsharp
    阳春三月来了
    新年快乐!
    无法启动 MS DTC 事务管理器。LogInit 返回错误 0x2. 怎么办?
    如何获取文件在系统中的图标?
    自定义工作流程的实现方案(初稿)
    [正能量系列]女性程序员篇
    [正能量系列]失业的程序员(一)
    我们在囧途之裁员篇
  • 原文地址:https://www.cnblogs.com/start530/p/3834398.html
Copyright © 2011-2022 走看看