LCD設備驅動分析

1. LCD設備驅動相關的數據結構

  1. struct fb_info {  
  2.     atomic_t count;  
  3.     int node;  
  4.     int flags;  
  5.     struct mutex lock;      /* Lock for open/release/ioctl funcs */  
  6.     struct mutex mm_lock;       /* Lock for fb_mmap and smem_* fields */  
  7.     struct fb_var_screeninfo var;   /* Current var */       //可變參數  
  8.     struct fb_fix_screeninfo fix;   /* Current fix */       //固定參數  
  9.     struct fb_monspecs monspecs;    /* Current Monitor specs */     //顯示器標誌  
  10.     struct work_struct queue;   /* Framebuffer event queue */  
  11.     struct fb_pixmap pixmap;    /* Image hardware mapper */     //圖像硬件映射  
  12.     struct fb_pixmap sprite;    /* Cursor hardware mapper */    //光標硬件映射  
  13.     struct fb_cmap cmap;        /* Current cmap */      //調色板  
  14.     struct list_head modelist;      /* mode list */  
  15.     struct fb_videomode *mode;  /* current mode */      //視頻模式  
  16.   
  17. #ifdef CONFIG_FB_BACKLIGHT  
  18.     /* assigned backlight device */  
  19.     /* set before framebuffer registration,  
  20.        remove after unregister */  
  21.     struct backlight_device *bl_dev;  
  22.   
  23.     /* Backlight level curve */  
  24.     struct mutex bl_curve_mutex;      
  25.     u8 bl_curve[FB_BACKLIGHT_LEVELS];  
  26. #endif  
  27. #ifdef CONFIG_FB_DEFERRED_IO  
  28.     struct delayed_work deferred_work;  
  29.     struct fb_deferred_io *fbdefio;  
  30. #endif  
  31.   
  32.     struct fb_ops *fbops;           //幀緩衝操作函數集  
  33.     struct device *device;      /* This is the parent */  
  34.     struct device *dev;     /* This is this fb device */  
  35.     int class_flag;                    /* private sysfs flags */  
  36. #ifdef CONFIG_FB_TILEBLITTING  
  37.     struct fb_tile_ops *tileops;    /* Tile Blitting */  
  38. #endif  
  39.     char __iomem *screen_base;  /* Virtual address */   //虛擬基地址  
  40.     unsigned long screen_size;  /* Amount of ioremapped VRAM or 0 */    //ioremap的虛擬內存大小  
  41.     void *pseudo_palette;       /* Fake palette of 16 colors */   
  42. #define FBINFO_STATE_RUNNING    0  
  43. #define FBINFO_STATE_SUSPENDED  1  
  44.     u32 state;          /* Hardware state i.e suspend */  
  45.     void *fbcon_par;                /* fbcon use-only private area */  
  46.     /* From here on everything is device dependent */  
  47.     void *par;  
  48.     /* we need the PCI or similar aperture base/size not 
  49.        smem_start/size as smem_start may just be an object 
  50.        allocated inside the aperture so may not actually overlap */  
  51.     struct apertures_struct {  
  52.         unsigned int count;  
  53.         struct aperture {  
  54.             resource_size_t base;  
  55.             resource_size_t size;  
  56.         } ranges[0];  
  57.     } *apertures;  
  58. };  
  59. struct fb_var_screeninfo {  
  60.     __u32 xres;         /* visible resolution       */  //x方向可見解析度  
  61.     __u32 yres;  
  62.     __u32 xres_virtual;     /* virtual resolution       */  //x方向虛擬解析度  
  63.     __u32 yres_virtual;  
  64.     __u32 xoffset;          /* offset from virtual to visible */    //可見和不可見部分的偏移  
  65.     __u32 yoffset;          /* resolution           */  
  66.   
  67.     __u32 bits_per_pixel;       /* guess what           */  //每一個像素的位數  
  68.     __u32 grayscale;        /* != 0 Graylevels instead of colors */  
  69.   
  70.     struct fb_bitfield red;     /* bitfield in fb mem if true color, */  
  71.     struct fb_bitfield green;   /* else only length is significant */  
  72.     struct fb_bitfield blue;  
  73.     struct fb_bitfield transp;  /* transparency         */    
  74.   
  75.     __u32 nonstd;           /* != 0 Non standard pixel format */  
  76.   
  77.     __u32 activate;         /* see FB_ACTIVATE_*        */  
  78.   
  79.     __u32 height;           /* height of picture in mm    */    //屏幕的高度  
  80.     __u32 width;            /* width of picture in mm     */    //屏幕的寬度  
  81.   
  82.     __u32 accel_flags;      /* (OBSOLETE) see fb_info.flags */  
  83.   
  84.     /* Timing: All values in pixclocks, except pixclock (of course) */  
  85.     __u32 pixclock;         /* pixel clock in ps (pico seconds) */  //像素時鐘  
  86.     __u32 left_margin;      /* time from sync to picture    */  //行切換時間,行同步與開始繪圖之間的延遲  
  87.     __u32 right_margin;     /* time from picture to sync    */  //行切換時間,繪圖結束與行同步之間的延遲  
  88.     __u32 upper_margin;     /* time from sync to picture    */  //幀切換,從同步到開始繪圖之間的延遲  
  89.     __u32 lower_margin;                         //幀切換,從繪圖結束到同步之間的延遲  
  90.     __u32 hsync_len;        /* length of horizontal sync    */  //水平同步的長度  
  91.     __u32 vsync_len;        /* length of vertical sync  */  //垂直同步的長度  
  92.     __u32 sync;         /* see FB_SYNC_*        */  
  93.     __u32 vmode;            /* see FB_VMODE_*       */  
  94.     __u32 rotate;           /* angle we rotate counter clockwise */  
  95.     __u32 reserved[5];      /* Reserved for future compatibility */  
  96. };  
  97. struct fb_fix_screeninfo {  
  98.     char id[16];            /* identification string eg "TT Builtin" */  
  99.     unsigned long smem_start;   /* Start of frame buffer mem */     //fb緩衝區的開始位置  
  100.                     /* (physical address) */<span style="white-space:pre">    </span>  
  101.     __u32 smem_len;         /* Length of frame buffer mem */    //fb緩衝區的長度  
  102.     __u32 type;         /* see FB_TYPE_*        */  
  103.     __u32 type_aux;         /* Interleave for interleaved Planes */  
  104.     __u32 visual;           /* see FB_VISUAL_*      */   
  105.     __u16 xpanstep;         /* zero if no hardware panning  */  
  106.     __u16 ypanstep;         /* zero if no hardware panning  */  
  107.     __u16 ywrapstep;        /* zero if no hardware ywrap    */  
  108.     __u32 line_length;      /* length of a line in bytes    */  //1行的字節數  
  109.     unsigned long mmio_start;   /* Start of Memory Mapped I/O   */  //內存映射IO的開始位置  
  110.                     /* (physical address) */  
  111.     __u32 mmio_len;         /* Length of Memory Mapped I/O  */  //內存映射IO的長度  
  112.     __u32 accel;            /* Indicate to driver which */  
  113.                     /*  specific chip/card we have  */  
  114.     __u16 reserved[3];      /* Reserved for future compatibility */  
  115. };  
struct fb_info {
	atomic_t count;
	int node;
	int flags;
	struct mutex lock;		/* Lock for open/release/ioctl funcs */
	struct mutex mm_lock;		/* Lock for fb_mmap and smem_* fields */
	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];
#endif
#ifdef CONFIG_FB_DEFERRED_IO
	struct delayed_work deferred_work;
	struct fb_deferred_io *fbdefio;
#endif

	struct fb_ops *fbops;			//幀緩衝操作函數集
	struct device *device;		/* This is the parent */
	struct device *dev;		/* This is this fb device */
	int class_flag;                    /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
	struct fb_tile_ops *tileops;    /* Tile Blitting */
#endif
	char __iomem *screen_base;	/* Virtual address */	//虛擬基地址
	unsigned long screen_size;	/* Amount of ioremapped VRAM or 0 */ 	//ioremap的虛擬內存大小
	void *pseudo_palette;		/* Fake palette of 16 colors */ 
#define FBINFO_STATE_RUNNING	0
#define FBINFO_STATE_SUSPENDED	1
	u32 state;			/* Hardware state i.e suspend */
	void *fbcon_par;                /* fbcon use-only private area */
	/* From here on everything is device dependent */
	void *par;
	/* we need the PCI or similar aperture base/size not
	   smem_start/size as smem_start may just be an object
	   allocated inside the aperture so may not actually overlap */
	struct apertures_struct {
		unsigned int count;
		struct aperture {
			resource_size_t base;
			resource_size_t size;
		} ranges[0];
	} *apertures;
};
struct fb_var_screeninfo {
	__u32 xres;			/* visible resolution		*/	//x方向可見解析度
	__u32 yres;
	__u32 xres_virtual;		/* virtual resolution		*/	//x方向虛擬解析度
	__u32 yres_virtual;
	__u32 xoffset;			/* offset from virtual to visible */	//可見和不可見部分的偏移
	__u32 yoffset;			/* resolution			*/

	__u32 bits_per_pixel;		/* guess what			*/	//每一個像素的位數
	__u32 grayscale;		/* != 0 Graylevels instead of colors */

	struct fb_bitfield red;		/* bitfield in fb mem if true color, */
	struct fb_bitfield green;	/* else only length is significant */
	struct fb_bitfield blue;
	struct fb_bitfield transp;	/* transparency			*/	

	__u32 nonstd;			/* != 0 Non standard pixel format */

	__u32 activate;			/* see FB_ACTIVATE_*		*/

	__u32 height;			/* height of picture in mm    */	//屏幕的高度
	__u32 width;			/* width of picture in mm     */	//屏幕的寬度

	__u32 accel_flags;		/* (OBSOLETE) see fb_info.flags */

	/* Timing: All values in pixclocks, except pixclock (of course) */
	__u32 pixclock;			/* pixel clock in ps (pico seconds) */	//像素時鐘
	__u32 left_margin;		/* time from sync to picture	*/	//行切換時間,行同步與開始繪圖之間的延遲
	__u32 right_margin;		/* time from picture to sync	*/	//行切換時間,繪圖結束與行同步之間的延遲
	__u32 upper_margin;		/* time from sync to picture	*/	//幀切換,從同步到開始繪圖之間的延遲
	__u32 lower_margin;							//幀切換,從繪圖結束到同步之間的延遲
	__u32 hsync_len;		/* length of horizontal sync	*/	//水平同步的長度
	__u32 vsync_len;		/* length of vertical sync	*/	//垂直同步的長度
	__u32 sync;			/* see FB_SYNC_*		*/
	__u32 vmode;			/* see FB_VMODE_*		*/
	__u32 rotate;			/* angle we rotate counter clockwise */
	__u32 reserved[5];		/* Reserved for future compatibility */
};
struct fb_fix_screeninfo {
	char id[16];			/* identification string eg "TT Builtin" */
	unsigned long smem_start;	/* Start of frame buffer mem */		//fb緩衝區的開始位置
					/* (physical address) */<span style="white-space:pre">	</span>
	__u32 smem_len;			/* Length of frame buffer mem */	//fb緩衝區的長度
	__u32 type;			/* see FB_TYPE_*		*/
	__u32 type_aux;			/* Interleave for interleaved Planes */
	__u32 visual;			/* see FB_VISUAL_*		*/ 
	__u16 xpanstep;			/* zero if no hardware panning  */
	__u16 ypanstep;			/* zero if no hardware panning  */
	__u16 ywrapstep;		/* zero if no hardware ywrap    */
	__u32 line_length;		/* length of a line in bytes    */	//1行的字節數
	unsigned long mmio_start;	/* Start of Memory Mapped I/O   */	//內存映射IO的開始位置
					/* (physical address) */
	__u32 mmio_len;			/* Length of Memory Mapped I/O  */	//內存映射IO的長度
	__u32 accel;			/* Indicate to driver which	*/
					/*  specific chip/card we have	*/
	__u16 reserved[3];		/* Reserved for future compatibility */
};

2. s3c2440 lcd控制器的結構


我們根據數據手冊來描述一下這個集成在S3C2440內部的LCD控制器
LCD控制器由REGBANK、LCDCDMA、TIMEGEN、VIDPRCS寄存器組成;
REGBANK由17個可編程的寄存器組和一塊256*16的調色板內存組成,它們用來配置LCD控制器的;
LCDCDMA是一個專用的DMA,它能自動地把在偵內存中的視頻數據傳送到LCD驅動器,通過使用這個DMA通道,視頻數據在不需要CPU的干預的情況下顯示在LCD屏上;
VIDPRCS接收來自LCDCDMA的數據,將數據轉換爲合適的數據格式,比如說4/8位單掃,4位雙掃顯示模式,然後通過數據端口VD[23:0]傳送視頻數據到LCD驅動器;
TIMEGEN由可編程的邏輯組成,他生成LCD驅動器需要的控制信號,比如VSYNC、HSYNC、VCLK和LEND等等,而這些控制信號又與REGBANK寄存器組中的LCDCON1/2/3/4/5的配置密切相關,通過不同的配置,TIMEGEN就能產生這些信號的不同形態,從而支持不同的LCD驅動器(即不同的STN/TFT屏)。


常見的TFT屏工作時序分析:


VSYNC:垂直同步信號
HSYNC:水平同步信號 
VCLK:像素時鐘信號
VD:像素數據輸出端口
VDEN:數據使能信號
LEND:行結束信號

VBPD(vertical back porch):表示在一幀圖像開始時,垂直同步信號以後的無效的行數,對應驅動中的upper_margin;   
VFBD(vertical front porch):表示在一幀圖像結束後,垂直同步信號以前的無效的行數,對應驅動中的lower_margin;   
VSPW(vertical sync pulse width):表示垂直同步脈衝的寬度,用行數計算,對應驅動中的vsync_len;   
HBPD(horizontal back porch):表示從水平同步信號開始到一行的有效數據開始之間的VCLK的個數,對應驅動中的left_margin;   
HFPD(horizontal front porth):表示一行的有效數據結束到下一個水平同步信號開始之間的VCLK的個數,對應驅動中的right_margin;   
HSPW(horizontal sync pulse width):表示水平同步信號的寬度,用VCLK計算,對應驅動中的hsync_len;  


3. 幀緩衝設備的註冊

 首先在arch/arm/mach-xx/目錄中定義一些信息
(1)LCD控制器的IO端口開始地址,結束地址,LCD中斷

(2)LCD硬件參數,如LCD的屏幕尺寸,屏幕類型,像素時鐘,水平可見的像素,垂直可見的像素,色位模式,以及上面介紹的6個參數。有的板子可以支持很多個尺寸的屏幕,所以要建立一個數組,default賦值相應的index。

(3)LCD引腳相關的配置,哪些配置成LEND、VCLK、HSYNC、VSYNC、VDEN。

第(1)行的信息保存在platform_device結構的resource成員。

第(2)行和第(3)行的信息保存在platform_device結構的device成員的void *platform_data指針中,在平臺初始化時賦值。

接下來看下probe的過程:

  1. static int __devinit s3c24xxfb_probe(struct platform_device *pdev,  
  2.                   enum s3c_drv_type drv_type)  
  3. {  
  4.     struct s3c2410fb_info *info;    //封裝了fb_info信息的結構  
  5.     struct s3c2410fb_display *display;  //指向定義的lcd信息  
  6.     struct fb_info *fbinfo;  
  7.     struct s3c2410fb_mach_info *mach_info;  //指向平臺數據  
  8.     struct resource *res;  
  9.     int ret;  
  10.     int irq;  
  11.     int i;  
  12.     int size;  
  13.     u32 lcdcon1;  
  14.   
  15.     mach_info = pdev->dev.platform_data;  
  16.     if (mach_info == NULL) {  
  17.         dev_err(&pdev->dev,  
  18.             "no platform data for lcd, cannot attach\n");  
  19.         return -EINVAL;  
  20.     }  
  21.   
  22.     if (mach_info->default_display >= mach_info->num_displays) {  
  23.         dev_err(&pdev->dev, "default is %d but only %d displays\n",  
  24.             mach_info->default_display, mach_info->num_displays);  
  25.         return -EINVAL;  
  26.     }  
  27.   
  28.     display = mach_info->displays + mach_info->default_display;   //指向你定義的lcd信息  
  29.   
  30.     irq = platform_get_irq(pdev, 0);  
  31.     if (irq < 0) {  
  32.         dev_err(&pdev->dev, "no irq for device\n");  
  33.         return -ENOENT;  
  34.     }  
  35.   
  36.     fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);   //分配s3c2410fb_info  
  37.     if (!fbinfo)  
  38.         return -ENOMEM;  
  39.   
  40.     platform_set_drvdata(pdev, fbinfo);  
  41.   
  42.     info = fbinfo->par;  
  43.     info->dev = &pdev->dev;  
  44.     info->drv_type = drv_type;  
  45.   
  46.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  47.     if (res == NULL) {  
  48.         dev_err(&pdev->dev, "failed to get memory registers\n");  
  49.         ret = -ENXIO;  
  50.         goto dealloc_fb;  
  51.     }  
  52.   
  53.     size = resource_size(res);  
  54.     info->mem = request_mem_region(res->start, size, pdev->name);  
  55.     if (info->mem == NULL) {  
  56.         dev_err(&pdev->dev, "failed to get memory region\n");  
  57.         ret = -ENOENT;  
  58.         goto dealloc_fb;  
  59.     }  
  60.   
  61.     info->io = ioremap(res->start, size);  
  62.     if (info->io == NULL) {  
  63.         dev_err(&pdev->dev, "ioremap() of registers failed\n");  
  64.         ret = -ENXIO;  
  65.         goto release_mem;  
  66.     }  
  67.   
  68.     info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);  
  69.   
  70.     dprintk("devinit\n");  
  71.   
  72.     strcpy(fbinfo->fix.id, driver_name);  
  73.   
  74.     /* Stop the video */    //關閉顯示器  
  75.     lcdcon1 = readl(info->io + S3C2410_LCDCON1);  
  76.     writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);  
  77.   
  78.     fbinfo->fix.type     = FB_TYPE_PACKED_PIXELS;  
  79.     fbinfo->fix.type_aux     = 0;  
  80.     fbinfo->fix.xpanstep     = 0;  
  81.     fbinfo->fix.ypanstep     = 0;  
  82.     fbinfo->fix.ywrapstep        = 0;  
  83.     fbinfo->fix.accel        = FB_ACCEL_NONE;  
  84.   
  85.     fbinfo->var.nonstd       = 0;  
  86.     fbinfo->var.activate     = FB_ACTIVATE_NOW;  
  87.     fbinfo->var.accel_flags     = 0;  
  88.     fbinfo->var.vmode        = FB_VMODE_NONINTERLACED;  
  89.   
  90.     fbinfo->fbops            = &s3c2410fb_ops;   //賦值fb操作函數集  
  91.     fbinfo->flags            = FBINFO_FLAG_DEFAULT;  
  92.     fbinfo->pseudo_palette      = &info->pseudo_pal;  
  93.   
  94.     for (i = 0; i < 256; i++)  
  95.         info->palette_buffer[i] = PALETTE_BUFF_CLEAR;  
  96.   
  97.     ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);  
  98.     if (ret) {  
  99.         dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);  
  100.         ret = -EBUSY;  
  101.         goto release_regs;  
  102.     }  
  103.   
  104.     info->clk = clk_get(NULL, "lcd");  
  105.     if (IS_ERR(info->clk)) {  
  106.         printk(KERN_ERR "failed to get lcd clock source\n");  
  107.         ret = PTR_ERR(info->clk);  
  108.         goto release_irq;  
  109.     }  
  110.   
  111.     clk_enable(info->clk);   //打開LCD時鐘  
  112.     dprintk("got and enabled clock\n");  
  113.   
  114.     msleep(1);  
  115.   
  116.     info->clk_rate = clk_get_rate(info->clk);  
  117.   
  118.     /* find maximum required memory size for display */  
  119.     for (i = 0; i < mach_info->num_displays; i++) {  
  120.         unsigned long smem_len = mach_info->displays[i].xres;  
  121.   
  122.         smem_len *= mach_info->displays[i].yres;  
  123.         smem_len *= mach_info->displays[i].bpp;  
  124.         smem_len >>= 3;  
  125.         if (fbinfo->fix.smem_len < smem_len)  
  126.             fbinfo->fix.smem_len = smem_len;  
  127.     }  
  128.     //計算顯存大小,並映射DMA資源  
  129.     /* Initialize video memory */  
  130.     ret = s3c2410fb_map_video_memory(fbinfo);  
  131.     if (ret) {  
  132.         printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);  
  133.         ret = -ENOMEM;  
  134.         goto release_clock;  
  135.     }  
  136.   
  137.     dprintk("got video memory\n");  
  138.   
  139.     fbinfo->var.xres = display->xres;  
  140.     fbinfo->var.yres = display->yres;  
  141.     fbinfo->var.bits_per_pixel = display->bpp;  
  142.     //初始化LCD控制器的各個寄存器  
  143.     s3c2410fb_init_registers(fbinfo);  
  144.     //調整可變參數  
  145.     s3c2410fb_check_var(&fbinfo->var, fbinfo);  
  146.   
  147.     ret = s3c2410fb_cpufreq_register(info);  
  148.     if (ret < 0) {  
  149.         dev_err(&pdev->dev, "Failed to register cpufreq\n");  
  150.         goto free_video_memory;  
  151.     }  
  152.     //註冊幀緩衝結構,也就是把指針賦給一個全局index指針數組  
  153.     ret = register_framebuffer(fbinfo);  
  154.     if (ret < 0) {  
  155.         printk(KERN_ERR "Failed to register framebuffer device: %d\n",  
  156.             ret);  
  157.         goto free_cpufreq;  
  158.     }  
  159.   
  160.     /* create device files */  
  161.     ret = device_create_file(&pdev->dev, &dev_attr_debug);  
  162.     if (ret) {  
  163.         printk(KERN_ERR "failed to add debug attribute\n");  
  164.     }  
  165.   
  166.     printk(KERN_INFO "fb%d: %s frame buffer device\n",  
  167.         fbinfo->node, fbinfo->fix.id);  
  168.   
  169.     return 0;  
  170.   
  171.  free_cpufreq:  
  172.     s3c2410fb_cpufreq_deregister(info);  
  173. free_video_memory:  
  174.     s3c2410fb_unmap_video_memory(fbinfo);  
  175. release_clock:  
  176.     clk_disable(info->clk);  
  177.     clk_put(info->clk);  
  178. release_irq:  
  179.     free_irq(irq, info);  
  180. release_regs:  
  181.     iounmap(info->io);  
  182. release_mem:  
  183.     release_mem_region(res->start, size);  
  184. dealloc_fb:  
  185.     platform_set_drvdata(pdev, NULL);  
  186.     framebuffer_release(fbinfo);  
  187.     return ret;  
  188. }  
static int __devinit s3c24xxfb_probe(struct platform_device *pdev,
				  enum s3c_drv_type drv_type)
{
	struct s3c2410fb_info *info;	//封裝了fb_info信息的結構
	struct s3c2410fb_display *display;	//指向定義的lcd信息
	struct fb_info *fbinfo;
	struct s3c2410fb_mach_info *mach_info;	//指向平臺數據
	struct resource *res;
	int ret;
	int irq;
	int i;
	int size;
	u32 lcdcon1;

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

	if (mach_info->default_display >= mach_info->num_displays) {
		dev_err(&pdev->dev, "default is %d but only %d displays\n",
			mach_info->default_display, mach_info->num_displays);
		return -EINVAL;
	}

	display = mach_info->displays + mach_info->default_display;	//指向你定義的lcd信息

	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		dev_err(&pdev->dev, "no irq for device\n");
		return -ENOENT;
	}

	fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);	//分配s3c2410fb_info
	if (!fbinfo)
		return -ENOMEM;

	platform_set_drvdata(pdev, fbinfo);

	info = fbinfo->par;
	info->dev = &pdev->dev;
	info->drv_type = drv_type;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res == NULL) {
		dev_err(&pdev->dev, "failed to get memory registers\n");
		ret = -ENXIO;
		goto dealloc_fb;
	}

	size = resource_size(res);
	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;
	}

	info->io = ioremap(res->start, size);
	if (info->io == NULL) {
		dev_err(&pdev->dev, "ioremap() of registers failed\n");
		ret = -ENXIO;
		goto release_mem;
	}

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

	dprintk("devinit\n");

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

	/* Stop the video */	//關閉顯示器
	lcdcon1 = readl(info->io + S3C2410_LCDCON1);
	writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);

	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->fbops		    = &s3c2410fb_ops;	//賦值fb操作函數集
	fbinfo->flags		    = FBINFO_FLAG_DEFAULT;
	fbinfo->pseudo_palette      = &info->pseudo_pal;

	for (i = 0; i < 256; i++)
		info->palette_buffer[i] = PALETTE_BUFF_CLEAR;

	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_regs;
	}

	info->clk = clk_get(NULL, "lcd");
	if (IS_ERR(info->clk)) {
		printk(KERN_ERR "failed to get lcd clock source\n");
		ret = PTR_ERR(info->clk);
		goto release_irq;
	}

	clk_enable(info->clk);	//打開LCD時鐘
	dprintk("got and enabled clock\n");

	msleep(1);

	info->clk_rate = clk_get_rate(info->clk);

	/* 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資源
	/* Initialize video memory */
	ret = s3c2410fb_map_video_memory(fbinfo);
	if (ret) {
		printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
		ret = -ENOMEM;
		goto release_clock;
	}

	dprintk("got video memory\n");

	fbinfo->var.xres = display->xres;
	fbinfo->var.yres = display->yres;
	fbinfo->var.bits_per_pixel = display->bpp;
	//初始化LCD控制器的各個寄存器
	s3c2410fb_init_registers(fbinfo);
	//調整可變參數
	s3c2410fb_check_var(&fbinfo->var, fbinfo);

	ret = s3c2410fb_cpufreq_register(info);
	if (ret < 0) {
		dev_err(&pdev->dev, "Failed to register cpufreq\n");
		goto free_video_memory;
	}
	//註冊幀緩衝結構,也就是把指針賦給一個全局index指針數組
	ret = register_framebuffer(fbinfo);
	if (ret < 0) {
		printk(KERN_ERR "Failed to register framebuffer device: %d\n",
			ret);
		goto free_cpufreq;
	}

	/* create device files */
	ret = device_create_file(&pdev->dev, &dev_attr_debug);
	if (ret) {
		printk(KERN_ERR "failed to add debug attribute\n");
	}

	printk(KERN_INFO "fb%d: %s frame buffer device\n",
		fbinfo->node, fbinfo->fix.id);

	return 0;

 free_cpufreq:
	s3c2410fb_cpufreq_deregister(info);
free_video_memory:
	s3c2410fb_unmap_video_memory(fbinfo);
release_clock:
	clk_disable(info->clk);
	clk_put(info->clk);
release_irq:
	free_irq(irq, info);
release_regs:
	iounmap(info->io);
release_mem:
	release_mem_region(res->start, size);
dealloc_fb:
	platform_set_drvdata(pdev, NULL);
	framebuffer_release(fbinfo);
	return ret;
}

  1. static int __devinit s3c2410fb_map_video_memory(struct fb_info *info)  
  2. {  
  3.     struct s3c2410fb_info *fbi = info->par;  
  4.     dma_addr_t map_dma;  
  5.     unsigned map_size = PAGE_ALIGN(info->fix.smem_len);  
  6.   
  7.     dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);  
  8.     //將分配的一個寫合併DMA緩衝區設置爲LCD屏幕的虛擬地址  
  9.     info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,  
  10.                            &map_dma, GFP_KERNEL);  
  11.   
  12.     if (info->screen_base) {  
  13.         /* prevent initial garbage on screen */  
  14.         dprintk("map_video_memory: clear %p:%08x\n",  
  15.             info->screen_base, map_size);  
  16.         memset(info->screen_base, 0x00, map_size);  
  17.   
  18.         info->fix.smem_start = map_dma;  
  19.   
  20.         dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",  
  21.             info->fix.smem_start, info->screen_base, map_size);  
  22.     }  
  23.   
  24.     return info->screen_base ? 0 : -ENOMEM;  
  25. }  
  26. static int s3c2410fb_init_registers(struct fb_info *info)  
  27. {  
  28.     struct s3c2410fb_info *fbi = info->par;  
  29.     struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;  
  30.     unsigned long flags;  
  31.     void __iomem *regs = fbi->io;  
  32.     void __iomem *tpal;  
  33.     void __iomem *lpcsel;  
  34.   
  35.     if (is_s3c2412(fbi)) {  
  36.         tpal = regs + S3C2412_TPAL;  
  37.         lpcsel = regs + S3C2412_TCONSEL;  
  38.     } else {  
  39.         tpal = regs + S3C2410_TPAL;  
  40.         lpcsel = regs + S3C2410_LPCSEL;  
  41.     }  
  42.   
  43.     /* Initialise LCD with values from haret */  
  44.   
  45.     local_irq_save(flags);  
  46.   
  47.     /* modify the gpio(s) with interrupts set (bjd) */  
  48.     //把GPIOC和GPIOD設置爲LCD模式  
  49.     modify_gpio(S3C2410_GPCUP,  mach_info->gpcup,  mach_info->gpcup_mask);  
  50.     modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);  
  51.     modify_gpio(S3C2410_GPDUP,  mach_info->gpdup,  mach_info->gpdup_mask);  
  52.     modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);  
  53.   
  54.     local_irq_restore(flags);  
  55.   
  56.     dprintk("LPCSEL    = 0x%08lx\n", mach_info->lpcsel);  
  57.     writel(mach_info->lpcsel, lpcsel);  
  58.   
  59.     dprintk("replacing TPAL %08x\n", readl(tpal));  
  60.   
  61.     /* ensure temporary palette disabled */  
  62.     writel(0x00, tpal);  
  63.   
  64.     return 0;  
  65. }  
  66. static int s3c2410fb_check_var(struct fb_var_screeninfo *var,  
  67.                    struct fb_info *info)  
  68. {  
  69.     struct s3c2410fb_info *fbi = info->par;  
  70.     struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;  
  71.     struct s3c2410fb_display *display = NULL;  
  72.     struct s3c2410fb_display *default_display = mach_info->displays +  
  73.                             mach_info->default_display;  
  74.     int type = default_display->type;  
  75.     unsigned i;  
  76.   
  77.     dprintk("check_var(var=%p, info=%p)\n", var, info);  
  78.   
  79.     /* validate x/y resolution */  
  80.     /* choose default mode if possible */  
  81.     if (var->yres == default_display->yres &&  
  82.         var->xres == default_display->xres &&  
  83.         var->bits_per_pixel == default_display->bpp)  
  84.         display = default_display;  
  85.     else  
  86.         for (i = 0; i < mach_info->num_displays; i++)  
  87.             if (type == mach_info->displays[i].type &&  
  88.                 var->yres == mach_info->displays[i].yres &&  
  89.                 var->xres == mach_info->displays[i].xres &&  
  90.                 var->bits_per_pixel == mach_info->displays[i].bpp) {  
  91.                 display = mach_info->displays + i;  
  92.                 break;  
  93.             }  
  94.   
  95.     if (!display) {  
  96.         dprintk("wrong resolution or depth %dx%d at %d bpp\n",  
  97.             var->xres, var->yres, var->bits_per_pixel);  
  98.         return -EINVAL;  
  99.     }  
  100.     //直接賦值給新分配的var  
  101.     /* it is always the size as the display */  
  102.     var->xres_virtual = display->xres;  
  103.     var->yres_virtual = display->yres;  
  104.     var->height = display->height;  
  105.     var->width = display->width;  
  106.   
  107.     /* copy lcd settings */  
  108.     var->pixclock = display->pixclock;  
  109.     var->left_margin = display->left_margin;  
  110.     var->right_margin = display->right_margin;  
  111.     var->upper_margin = display->upper_margin;  
  112.     var->lower_margin = display->lower_margin;  
  113.     var->vsync_len = display->vsync_len;  
  114.     var->hsync_len = display->hsync_len;  
  115.   
  116.     fbi->regs.lcdcon5 = display->lcdcon5;  
  117.     /* set display type */  
  118.     fbi->regs.lcdcon1 = display->type;  
  119.   
  120.     var->transp.offset = 0;  
  121.     var->transp.length = 0;  
  122.     /* set r/g/b positions */  
  123.     //根據設置的色位模式,設置相應的參數  
  124.     switch (var->bits_per_pixel) {  
  125.     case 1:  
  126.     case 2:  
  127.     case 4:  
  128.         var->red.offset  = 0;  
  129.         var->red.length  = var->bits_per_pixel;  
  130.         var->green   = var->red;  
  131.         var->blue    = var->red;  
  132.         break;  
  133.     case 8:  
  134.         if (display->type != S3C2410_LCDCON1_TFT) {  
  135.             /* 8 bpp 332 */  
  136.             var->red.length      = 3;  
  137.             var->red.offset      = 5;  
  138.             var->green.length    = 3;  
  139.             var->green.offset    = 2;  
  140.             var->blue.length = 2;  
  141.             var->blue.offset = 0;  
  142.         } else {  
  143.             var->red.offset      = 0;  
  144.             var->red.length      = 8;  
  145.             var->green       = var->red;  
  146.             var->blue        = var->red;  
  147.         }  
  148.         break;  
  149.     case 12:  
  150.         /* 12 bpp 444 */  
  151.         var->red.length      = 4;  
  152.         var->red.offset      = 8;  
  153.         var->green.length    = 4;  
  154.         var->green.offset    = 4;  
  155.         var->blue.length = 4;  
  156.         var->blue.offset = 0;  
  157.         break;  
  158.   
  159.     default:  
  160.     case 16:  
  161.         if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) {  
  162.             /* 16 bpp, 565 format */  
  163.             var->red.offset      = 11;  
  164.             var->green.offset    = 5;  
  165.             var->blue.offset = 0;  
  166.             var->red.length      = 5;  
  167.             var->green.length    = 6;  
  168.             var->blue.length = 5;  
  169.         } else {  
  170.             /* 16 bpp, 5551 format */  
  171.             var->red.offset      = 11;  
  172.             var->green.offset    = 6;  
  173.             var->blue.offset = 1;  
  174.             var->red.length      = 5;  
  175.             var->green.length    = 5;  
  176.             var->blue.length = 5;  
  177.         }  
  178.         break;  
  179.     case 32:  
  180.         /* 24 bpp 888 and 8 dummy */  
  181.         var->red.length      = 8;  
  182.         var->red.offset      = 16;  
  183.         var->green.length    = 8;  
  184.         var->green.offset    = 8;  
  185.         var->blue.length = 8;  
  186.         var->blue.offset = 0;  
  187.         break;  
  188.     }  
  189.     return 0;  
  190. }  
static int __devinit s3c2410fb_map_video_memory(struct fb_info *info)
{
	struct s3c2410fb_info *fbi = info->par;
	dma_addr_t map_dma;
	unsigned map_size = PAGE_ALIGN(info->fix.smem_len);

	dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);
	//將分配的一個寫合併DMA緩衝區設置爲LCD屏幕的虛擬地址
	info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
						   &map_dma, GFP_KERNEL);

	if (info->screen_base) {
		/* prevent initial garbage on screen */
		dprintk("map_video_memory: clear %p:%08x\n",
			info->screen_base, map_size);
		memset(info->screen_base, 0x00, map_size);

		info->fix.smem_start = map_dma;

		dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",
			info->fix.smem_start, info->screen_base, map_size);
	}

	return info->screen_base ? 0 : -ENOMEM;
}
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;
	void __iomem *tpal;
	void __iomem *lpcsel;

	if (is_s3c2412(fbi)) {
		tpal = regs + S3C2412_TPAL;
		lpcsel = regs + S3C2412_TCONSEL;
	} else {
		tpal = regs + S3C2410_TPAL;
		lpcsel = regs + S3C2410_LPCSEL;
	}

	/* Initialise LCD with values from haret */

	local_irq_save(flags);

	/* modify the gpio(s) with interrupts set (bjd) */
	//把GPIOC和GPIOD設置爲LCD模式
	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);
	writel(mach_info->lpcsel, lpcsel);

	dprintk("replacing TPAL %08x\n", readl(tpal));

	/* ensure temporary palette disabled */
	writel(0x00, tpal);

	return 0;
}
static int s3c2410fb_check_var(struct fb_var_screeninfo *var,
			       struct fb_info *info)
{
	struct s3c2410fb_info *fbi = info->par;
	struct s3c2410fb_mach_info *mach_info = fbi->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;
	unsigned i;

	dprintk("check_var(var=%p, info=%p)\n", var, info);

	/* validate x/y resolution */
	/* choose default mode if possible */
	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; i < 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) {
		dprintk("wrong resolution or depth %dx%d at %d bpp\n",
			var->xres, var->yres, var->bits_per_pixel);
		return -EINVAL;
	}
	//直接賦值給新分配的var
	/* it is always the size as the display */
	var->xres_virtual = display->xres;
	var->yres_virtual = display->yres;
	var->height = display->height;
	var->width = display->width;

	/* copy lcd settings */
	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;

	fbi->regs.lcdcon5 = display->lcdcon5;
	/* set display type */
	fbi->regs.lcdcon1 = display->type;

	var->transp.offset = 0;
	var->transp.length = 0;
	/* set r/g/b positions */
	//根據設置的色位模式,設置相應的參數
	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) {
			/* 8 bpp 332 */
			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:
		/* 12 bpp 444 */
		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;

	default:
	case 16:
		if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) {
			/* 16 bpp, 565 format */
			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 {
			/* 16 bpp, 5551 format */
			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:
		/* 24 bpp 888 and 8 dummy */
		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;
}


發佈了3 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章