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

你可能感兴趣的:(c,struct,Module,video,buffer,平台)