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

좋은 웹페이지 즐겨찾기