天天看點

samsung s3c-fb.c分析

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/fb.h>
#include <linux/io.h>

#include <mach/map.h>
#include <mach/regs-fb.h>
#include <plat/fb.h>

struct s3c_fb;
struct s3c_fb_win {
	struct s3c_fb_pd_win	*windata;
	struct s3c_fb		*parent;
	struct fb_info		*fbinfo;
	struct s3c_fb_palette	 palette;

	u32			*palette_buffer;
	u32			 pseudo_palette[16];
	unsigned int		 index;
};
struct s3c_fb {
	struct device		*dev;
	struct resource		*regs_res;
	struct clk		*bus_clk;
	void __iomem		*regs;

	unsigned char		 enabled;

	struct s3c_fb_platdata	*pdata;
	struct s3c_fb_win	*windows[S3C_FB_MAX_WIN];
};

static int s3c_fb_check_var(struct fb_var_screeninfo *var,
			    struct fb_info *info)
{

	/* always ensure these are zero, for drop through cases below */
	var->transp.offset = 0;
	var->transp.length = 0;
	/* 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;
	return 0;
}

static int s3c_fb_calc_pixclk(struct s3c_fb *sfb, unsigned int pixclk)
{
	unsigned long clk = clk_get_rate(sfb->bus_clk);
	unsigned long long tmp;
	unsigned int result;

	tmp = (unsigned long long)clk;
	tmp *= pixclk;

	do_div(tmp, 1000000000UL);
	result = (unsigned int)tmp / 1000;

	dev_dbg(sfb->dev, "pixclk=%u, clk=%lu, div=%d (%lu)\n",
		pixclk, clk, result, clk / result);

	return result;
}

static int s3c_fb_align_word(unsigned int bpp, unsigned int pix)
{
	int pix_per_word;

	if (bpp > 16)
		return pix;

	pix_per_word = (8 * 32) / bpp;
	return ALIGN(pix, pix_per_word);
}

static int s3c_fb_set_par(struct fb_info *info)
{
	struct fb_var_screeninfo *var = &info->var;
	struct s3c_fb_win *win = info->par;
	struct s3c_fb *sfb = win->parent;
	void __iomem *regs = sfb->regs;
	int win_no = win->index;
	u32 osdc_data = 0;
	u32 data;
	u32 pagewidth;
	int clkdiv;

	info->fix.visual = FB_VISUAL_TRUECOLOR;//true color
	info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
	/* disable the window whilst we update it */
	writel(0, regs + WINCON(win_no));
	if (win_no == 0) {
	
	clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock);
	data = sfb->pdata->vidcon0;
	data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);
	if (clkdiv > 1)
		data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR;
	else
		data &= ~VIDCON0_CLKDIR;	/* 1:1 clock */
	/* write the timing data to the panel */

	data |= VIDCON0_ENVID | VIDCON0_ENVID_F;
	writel(data, regs + VIDCON0);

	data = VIDTCON0_VBPD(var->upper_margin - 1) |
		      VIDTCON0_VFPD(var->lower_margin - 1) |
		      VIDTCON0_VSPW(var->vsync_len - 1);

	writel(data, regs + VIDTCON0);

	data = VIDTCON1_HBPD(var->left_margin - 1) |
		      VIDTCON1_HFPD(var->right_margin - 1) |
		      VIDTCON1_HSPW(var->hsync_len - 1);

	writel(data, regs + VIDTCON1);

	data = VIDTCON2_LINEVAL(var->yres - 1) |
		       VIDTCON2_HOZVAL(var->xres - 1);
	writel(data, regs + VIDTCON2);
	
	}
	/* write the buffer address */
	writel(info->fix.smem_start, regs + VIDW_BUF_START(win_no));

	data = info->fix.smem_start + info->fix.line_length * var->yres;
	writel(data, regs + VIDW_BUF_END(win_no));

	pagewidth = (var->xres * var->bits_per_pixel) >> 3;
	data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) |
	       VIDW_BUF_SIZE_PAGEWIDTH(pagewidth);
	writel(data, regs + VIDW_BUF_SIZE(win_no));
	/* write 'OSD' registers to control position of framebuffer */

	data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0);
	writel(data, regs + VIDOSD_A(win_no));

	data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel,
						     var->xres - 1)) |
	       VIDOSDxB_BOTRIGHT_Y(var->yres - 1);

	writel(data, regs + VIDOSD_B(win_no));

	data = var->xres * var->yres;

	osdc_data = VIDISD14C_ALPHA1_R(0xf) |
		VIDISD14C_ALPHA1_G(0xf) |
		VIDISD14C_ALPHA1_B(0xf);

	if (s3c_fb_has_osd_d(win_no)) {
		writel(data, regs + VIDOSD_D(win_no));
		writel(osdc_data, regs + VIDOSD_C(win_no));
	} else
		writel(data, regs + VIDOSD_C(win_no));

	data = WINCONx_ENWIN;
	data |= WINCON0_BPPMODE_16BPP_565;
	data |= WINCONx_HAWSWP;
	data |= WINCONx_BURSTLEN_16WORD;
	writel(data, regs + WINCON(win_no));
	writel(0x0, regs + WINxMAP(win_no));
	
	return 0;
}

static inline unsigned int chan_to_field(unsigned int chan,
					 struct fb_bitfield *bf)
{
	chan &= 0xffff;
	chan >>= 16 - bf->length;
	return chan << bf->offset;
}

static int s3c_fb_setcolreg(unsigned regno,
			    unsigned red, unsigned green, unsigned blue,
			    unsigned transp, struct fb_info *info)
{
	struct s3c_fb_win *win = info->par;
	struct s3c_fb *sfb = win->parent;
	unsigned int val;
	//regno 2-255
	switch (info->fix.visual) {
	case FB_VISUAL_TRUECOLOR:
	if (regno < 16) {
	u32 *pal = info->pseudo_palette;
	val  = chan_to_field(red,   &info->var.red);
	val |= chan_to_field(green, &info->var.green);
	val |= chan_to_field(blue,  &info->var.blue);

	pal[regno] = val;
	}
		break;
	default:
		return 1;
	}
	
	return 0;
}

static int s3c_fb_blank(int blank_mode, struct fb_info *info)
{
	return 0;
}
static struct fb_ops s3c_fb_ops = {
	.owner			= THIS_MODULE,
	.fb_check_var	= s3c_fb_check_var,//檢測可變參數,并調整到支援的值*/
	.fb_set_par		= s3c_fb_set_par,//根據info->var設定video模式*/
	.fb_blank		= s3c_fb_blank,//顯示空白*/
	.fb_setcolreg	= s3c_fb_setcolreg,//設定color寄存器*/
	.fb_fillrect	= cfb_fillrect,//矩形填充drivers/video/cfblillrect.c裡實作,把FB_CIRRUS打開*/
	.fb_copyarea	= cfb_copyarea,//資料複制drivers/video/cfbcopyarea.c*/
	.fb_imageblit	= cfb_imageblit,//圖形填充drivers/video/cfbimgblt.c*/
};

static int __devinit s3c_fb_alloc_memory(struct s3c_fb *sfb,
					 struct s3c_fb_win *win)
{
	struct s3c_fb_pd_win *windata = win->windata;
	dma_addr_t map_dma;//幀緩沖實體位址*/
	unsigned int real_size, virt_size, size;
	struct fb_info *fbi = win->fbinfo;
	

	real_size = windata->win_mode.xres * windata->win_mode.yres * windata->default_bpp;
	virt_size = windata->virtual_x * windata->virtual_y * windata->default_bpp;
	
	size = real_size / 4;
	fbi->fix.smem_len = size;
	printk("the size is %d, fbi->fix.smem_len = %d\n", size, windata->win_mode.xres * windata->win_mode.yres * windata->max_bpp);
	//the size is 522240, fbi->fix.smem_len = 4177920*/
	//PAGE_ALIGN()1k邊界對齊*/
	size = PAGE_ALIGN(size);
	/**通過dma_alloc_writecombine()配置設定的顯示緩沖區不會出現cache一緻性問題*/
	fbi->screen_base = dma_alloc_writecombine(sfb->dev, size,
						  &map_dma, GFP_KERNEL);
						  
	printk( "mapped %x to %p\n", (unsigned int)map_dma, fbi->screen_base);
	
	memset(fbi->screen_base, 0x0, size);
	//smem_start:幀緩沖裝置顯示緩沖區首位址*/
	fbi->fix.smem_start = map_dma;
	return 0;
}


/**
*s3c_fb_probe_win():注冊一個硬體視窗,幀緩沖顯示緩沖區的申請
*@sfb:硬體基礎資源
*@res:指向産生視窗的指針
*對硬體圖形視窗進行配置設定和執行基本的初始化
*/
static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
				      struct s3c_fb_win **res)
{
	struct fb_var_screeninfo *var;//記錄使用者可修改的顯示控制器參數*/
	struct fb_videomode *initmode;//include/linux/fb.h裡定義*/
	struct s3c_fb_pd_win *windata;//自定義的平台資料接收bsp參數*/
	struct s3c_fb_win *win;//驅動裡定義的結構體包括fb_info*/
	struct fb_info *fbinfo;//fb_info,幀緩沖關鍵結構*/
	int palette_size;
	int ret;

	palette_size = 256;//調色闆大小*/
	/**配置設定fb_info結構*/
	fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +
				   palette_size * sizeof(u32), sfb->dev);
				   
	//獲得bsp的平台資料.win[0]		= &smdk6410_fb_win0,			   
	windata = sfb->pdata->win[win_no];


	initmode = &windata->win_mode;
	win = fbinfo->par;
	var = &fbinfo->var;
	win->fbinfo = fbinfo;
	win->parent = sfb;
	win->windata = windata;
	win->index = win_no;
	win->palette_buffer = (u32 *)(win + 1);
	
	/**幀緩沖顯示緩沖區的申請,系統通過DMA方式搬移顯示資料*/
	ret = s3c_fb_alloc_memory(sfb, win);
	//初始化調色闆plat-samsung/include/plat/regs-fb-v4.h
	/* setup the r/b/g positions for the window's palette */
	s3c_fb_init_palette(win_no, &win->palette);
	
	/* setup the initial video mode from the window */
	
	fb_videomode_to_var(&fbinfo->var, initmode);
	/*給FBI設定參數*/
	fbinfo->fix.type	= FB_TYPE_PACKED_PIXELS;/* Packed Pixels	*/
	fbinfo->fix.accel	= FB_ACCEL_NONE;/* no hardware accelerator	*/
	fbinfo->var.activate	= FB_ACTIVATE_NOW;
	fbinfo->var.vmode	= FB_VMODE_NONINTERLACED;
	fbinfo->var.bits_per_pixel = windata->default_bpp;//每個點用多個位元組表示BPP
	fbinfo->fbops		= &s3c_fb_ops;//指向底層操作的函數指針
	fbinfo->flags		= FBINFO_FLAG_DEFAULT;//linux/fb.h定義0x0001	Low-level driver is a module */
	fbinfo->pseudo_palette  = &win->pseudo_palette;
	
	/* prepare to actually start the framebuffer */
	ret = s3c_fb_check_var(&fbinfo->var, fbinfo);//檢查framebuffer的可變參數
	
	/* create initial colour map 配置設定顔色空間*/
	ret = fb_alloc_cmap(&fbinfo->cmap, s3c_fb_win_pal_size(win_no), 1);
	fb_set_cmap(&fbinfo->cmap, fbinfo);//設定顔色空間drivers/video/fbcmap.c裡定
	s3c_fb_set_par(fbinfo);//framebuffer request to set new framebuffer state.
	//注冊幀緩沖設裝置
	ret = register_framebuffer(fbinfo);
	*res = win;
	return 0;
}

static int s3c_fb_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct s3c_fb_platdata *pd;//plat-samsung/include/plat/fb.h平台資料
	struct s3c_fb *sfb;/*s3c_fb 自定義的全局結構體一個最重要的資料結據, 
					   *它代表了一個顯示控制器,顯示控制器的所有東東都放在這裡了。
					   *但這裡把它做成一個局部變量了*/
	struct resource *res;
	int win;
	int ret = 0;
	pd = pdev->dev.platform_data;//get bsp's platform_data
	
	sfb = kzalloc(sizeof(struct s3c_fb), GFP_KERNEL);//給結構體配置設定記憶體fb_info
	
	sfb->dev = dev;
	sfb->pdata = pd;//向sfb填入顯示控制器的dev,pd
	//獲得lcd的時鐘并使能
	sfb->bus_clk = clk_get(dev, "lcd");
	clk_enable(sfb->bus_clk);
	/**獲得LCD平台裝置IO記憶體資源*/
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	/**申請IO記憶體*/
	sfb->regs_res = request_mem_region(res->start, resource_size(res), pdev->name);
	/**将IO記憶體空間映射到核心虛拟空間*/
	sfb->regs = ioremap(res->start, resource_size(res));
	
	//執行回調函數來設定gpio引腳,回調函數的實作是在
	//arch/arm/mach-s3c64xx/setup-fb-24bpp.c裡的
	pd->setup_gpio();
	//設定寄存器,把平台資料	.vidcon1	= VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
	//寫到寄存器VIDCON1的位址0x7710_0004,即HSYNC和VSYNC為1,脈沖極性反轉
	writel(pd->vidcon1, sfb->regs + VIDCON1);
	
	for (win = 0; win < S3C_FB_MAX_WIN; win++) {
		if (!pd->win[win])
			continue;//win=0
			//配置設定 及 注冊framebuffer的重要函數s3c_fb_probe_win
		ret = s3c_fb_probe_win(sfb, win, &sfb->windows[win]);
	}
	//可以将sfb儲存成平台總線裝置的私有資料,以後要使用它時隻需調用
	//platform_get_drvdata()就可以了
	platform_set_drvdata(pdev, sfb);
	return 0;
}

static int s3c_fb_remove(struct platform_device *pdev)
{
	

	return 0;
}

#define s3c_fb_suspend NULL
#define s3c_fb_resume  NULL
static struct platform_driver s3c_fb_driver = {
	.probe		= s3c_fb_probe,
	.remove		= s3c_fb_remove,
	.suspend	= s3c_fb_suspend,
	.resume		= s3c_fb_resume,
	.driver		= {
		.name	= "s3c-fb",
		.owner	= THIS_MODULE,
	},
};
static int __init s3c_fb_init(void)
{
	return platform_driver_register(&s3c_fb_driver);
}

static void __exit s3c_fb_cleanup(void)
{
	platform_driver_unregister(&s3c_fb_driver);
}

module_init(s3c_fb_init);
module_exit(s3c_fb_cleanup);

MODULE_AUTHOR("Ben Dooks <[email protected]>");
MODULE_DESCRIPTION("Samsung S3C SoC Framebuffer driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:s3c-fb");
           

繼續閱讀