⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pxafb.c

📁 Linux环境下视频显示卡设备的驱动程序源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
		    fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR)			for (i = 0; i < fbi->palette_size; i++)				pxafb_setpalettereg(i, 0, 0, 0, 0, info);		pxafb_schedule_work(fbi, C_DISABLE);		/* TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); */		break;	case FB_BLANK_UNBLANK:		/* TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); */		if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR ||		    fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR)			fb_set_cmap(&fbi->fb.cmap, info);		pxafb_schedule_work(fbi, C_ENABLE);	}	return 0;}static struct fb_ops pxafb_ops = {	.owner		= THIS_MODULE,	.fb_check_var	= pxafb_check_var,	.fb_set_par	= pxafb_set_par,	.fb_pan_display	= pxafb_pan_display,	.fb_setcolreg	= pxafb_setcolreg,	.fb_fillrect	= cfb_fillrect,	.fb_copyarea	= cfb_copyarea,	.fb_imageblit	= cfb_imageblit,	.fb_blank	= pxafb_blank,};#ifdef CONFIG_FB_PXA_OVERLAYstatic void overlay1fb_setup(struct pxafb_layer *ofb){	int size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual;	unsigned long start = ofb->video_mem_phys;	setup_frame_dma(ofb->fbi, DMA_OV1, PAL_NONE, start, size);}/* Depending on the enable status of overlay1/2, the DMA should be * updated from FDADRx (when disabled) or FBRx (when enabled). */static void overlay1fb_enable(struct pxafb_layer *ofb){	int enabled = lcd_readl(ofb->fbi, OVL1C1) & OVLxC1_OEN;	uint32_t fdadr1 = ofb->fbi->fdadr[DMA_OV1] | (enabled ? 0x1 : 0);	lcd_writel(ofb->fbi, enabled ? FBR1 : FDADR1, fdadr1);	lcd_writel(ofb->fbi, OVL1C2, ofb->control[1]);	lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] | OVLxC1_OEN);}static void overlay1fb_disable(struct pxafb_layer *ofb){	uint32_t lccr5 = lcd_readl(ofb->fbi, LCCR5);	lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] & ~OVLxC1_OEN);	lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(1));	lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(1));	lcd_writel(ofb->fbi, FBR1, ofb->fbi->fdadr[DMA_OV1] | 0x3);	if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0)		pr_warning("%s: timeout disabling overlay1\n", __func__);	lcd_writel(ofb->fbi, LCCR5, lccr5);}static void overlay2fb_setup(struct pxafb_layer *ofb){	int size, div = 1, pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd);	unsigned long start[3] = { ofb->video_mem_phys, 0, 0 };	if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED) {		size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual;		setup_frame_dma(ofb->fbi, DMA_OV2_Y, -1, start[0], size);	} else {		size = ofb->fb.var.xres_virtual * ofb->fb.var.yres_virtual;		switch (pfor) {		case OVERLAY_FORMAT_YUV444_PLANAR: div = 1; break;		case OVERLAY_FORMAT_YUV422_PLANAR: div = 2; break;		case OVERLAY_FORMAT_YUV420_PLANAR: div = 4; break;		}		start[1] = start[0] + size;		start[2] = start[1] + size / div;		setup_frame_dma(ofb->fbi, DMA_OV2_Y,  -1, start[0], size);		setup_frame_dma(ofb->fbi, DMA_OV2_Cb, -1, start[1], size / div);		setup_frame_dma(ofb->fbi, DMA_OV2_Cr, -1, start[2], size / div);	}}static void overlay2fb_enable(struct pxafb_layer *ofb){	int pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd);	int enabled = lcd_readl(ofb->fbi, OVL2C1) & OVLxC1_OEN;	uint32_t fdadr2 = ofb->fbi->fdadr[DMA_OV2_Y]  | (enabled ? 0x1 : 0);	uint32_t fdadr3 = ofb->fbi->fdadr[DMA_OV2_Cb] | (enabled ? 0x1 : 0);	uint32_t fdadr4 = ofb->fbi->fdadr[DMA_OV2_Cr] | (enabled ? 0x1 : 0);	if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED)		lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2);	else {		lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2);		lcd_writel(ofb->fbi, enabled ? FBR3 : FDADR3, fdadr3);		lcd_writel(ofb->fbi, enabled ? FBR4 : FDADR4, fdadr4);	}	lcd_writel(ofb->fbi, OVL2C2, ofb->control[1]);	lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] | OVLxC1_OEN);}static void overlay2fb_disable(struct pxafb_layer *ofb){	uint32_t lccr5 = lcd_readl(ofb->fbi, LCCR5);	lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] & ~OVLxC1_OEN);	lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(2));	lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(2));	lcd_writel(ofb->fbi, FBR2, ofb->fbi->fdadr[DMA_OV2_Y]  | 0x3);	lcd_writel(ofb->fbi, FBR3, ofb->fbi->fdadr[DMA_OV2_Cb] | 0x3);	lcd_writel(ofb->fbi, FBR4, ofb->fbi->fdadr[DMA_OV2_Cr] | 0x3);	if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0)		pr_warning("%s: timeout disabling overlay2\n", __func__);}static struct pxafb_layer_ops ofb_ops[] = {	[0] = {		.enable		= overlay1fb_enable,		.disable	= overlay1fb_disable,		.setup		= overlay1fb_setup,	},	[1] = {		.enable		= overlay2fb_enable,		.disable	= overlay2fb_disable,		.setup		= overlay2fb_setup,	},};static int overlayfb_open(struct fb_info *info, int user){	struct pxafb_layer *ofb = (struct pxafb_layer *)info;	/* no support for framebuffer console on overlay */	if (user == 0)		return -ENODEV;	/* allow only one user at a time */	if (atomic_inc_and_test(&ofb->usage))		return -EBUSY;	/* unblank the base framebuffer */	fb_blank(&ofb->fbi->fb, FB_BLANK_UNBLANK);	return 0;}static int overlayfb_release(struct fb_info *info, int user){	struct pxafb_layer *ofb = (struct pxafb_layer*) info;	atomic_dec(&ofb->usage);	ofb->ops->disable(ofb);	free_pages_exact(ofb->video_mem, ofb->video_mem_size);	ofb->video_mem = NULL;	ofb->video_mem_size = 0;	return 0;}static int overlayfb_check_var(struct fb_var_screeninfo *var,			       struct fb_info *info){	struct pxafb_layer *ofb = (struct pxafb_layer *)info;	struct fb_var_screeninfo *base_var = &ofb->fbi->fb.var;	int xpos, ypos, pfor, bpp;	xpos = NONSTD_TO_XPOS(var->nonstd);	ypos = NONSTD_TO_XPOS(var->nonstd);	pfor = NONSTD_TO_PFOR(var->nonstd);	bpp = pxafb_var_to_bpp(var);	if (bpp < 0)		return -EINVAL;	/* no support for YUV format on overlay1 */	if (ofb->id == OVERLAY1 && pfor != 0)		return -EINVAL;	/* for YUV packed formats, bpp = 'minimum bpp of YUV components' */	switch (pfor) {	case OVERLAY_FORMAT_RGB:		bpp = pxafb_var_to_bpp(var);		if (bpp < 0)			return -EINVAL;		pxafb_set_pixfmt(var, var_to_depth(var));		break;	case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break;	case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 8; break;	case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 4; break;	case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 2; break;	default:		return -EINVAL;	}	/* each line must start at a 32-bit word boundary */	if ((xpos * bpp) % 32)		return -EINVAL;	/* xres must align on 32-bit word boundary */	var->xres = roundup(var->xres * bpp, 32) / bpp;	if ((xpos + var->xres > base_var->xres) ||	    (ypos + var->yres > base_var->yres))		return -EINVAL;	var->xres_virtual = var->xres;	var->yres_virtual = max(var->yres, var->yres_virtual);	return 0;}static int overlayfb_map_video_memory(struct pxafb_layer *ofb){	struct fb_var_screeninfo *var = &ofb->fb.var;	int pfor = NONSTD_TO_PFOR(var->nonstd);	int size, bpp = 0;	switch (pfor) {	case OVERLAY_FORMAT_RGB: bpp = var->bits_per_pixel; break;	case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break;	case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 24; break;	case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 16; break;	case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 12; break;	}	ofb->fb.fix.line_length = var->xres_virtual * bpp / 8;	size = PAGE_ALIGN(ofb->fb.fix.line_length * var->yres_virtual);	/* don't re-allocate if the original video memory is enough */	if (ofb->video_mem) {		if (ofb->video_mem_size >= size)			return 0;		free_pages_exact(ofb->video_mem, ofb->video_mem_size);	}	ofb->video_mem = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);	if (ofb->video_mem == NULL)		return -ENOMEM;	ofb->video_mem_phys = virt_to_phys(ofb->video_mem);	ofb->video_mem_size = size;	ofb->fb.fix.smem_start	= ofb->video_mem_phys;	ofb->fb.fix.smem_len	= ofb->fb.fix.line_length * var->yres_virtual;	ofb->fb.screen_base	= ofb->video_mem;	return 0;}static int overlayfb_set_par(struct fb_info *info){	struct pxafb_layer *ofb = (struct pxafb_layer *)info;	struct fb_var_screeninfo *var = &info->var;	int xpos, ypos, pfor, bpp, ret;	ret = overlayfb_map_video_memory(ofb);	if (ret)		return ret;	bpp  = pxafb_var_to_bpp(var);	xpos = NONSTD_TO_XPOS(var->nonstd);	ypos = NONSTD_TO_XPOS(var->nonstd);	pfor = NONSTD_TO_PFOR(var->nonstd);	ofb->control[0] = OVLxC1_PPL(var->xres) | OVLxC1_LPO(var->yres) |			  OVLxC1_BPP(bpp);	ofb->control[1] = OVLxC2_XPOS(xpos) | OVLxC2_YPOS(ypos);	if (ofb->id == OVERLAY2)		ofb->control[1] |= OVL2C2_PFOR(pfor);	ofb->ops->setup(ofb);	ofb->ops->enable(ofb);	return 0;}static struct fb_ops overlay_fb_ops = {	.owner			= THIS_MODULE,	.fb_open		= overlayfb_open,	.fb_release		= overlayfb_release,	.fb_check_var 		= overlayfb_check_var,	.fb_set_par		= overlayfb_set_par,};static void __devinit init_pxafb_overlay(struct pxafb_info *fbi,					 struct pxafb_layer *ofb, int id){	sprintf(ofb->fb.fix.id, "overlay%d", id + 1);	ofb->fb.fix.type		= FB_TYPE_PACKED_PIXELS;	ofb->fb.fix.xpanstep		= 0;	ofb->fb.fix.ypanstep		= 1;	ofb->fb.var.activate		= FB_ACTIVATE_NOW;	ofb->fb.var.height		= -1;	ofb->fb.var.width		= -1;	ofb->fb.var.vmode		= FB_VMODE_NONINTERLACED;	ofb->fb.fbops			= &overlay_fb_ops;	ofb->fb.flags			= FBINFO_FLAG_DEFAULT;	ofb->fb.node			= -1;	ofb->fb.pseudo_palette		= NULL;	ofb->id = id;	ofb->ops = &ofb_ops[id];	atomic_set(&ofb->usage, 0);	ofb->fbi = fbi;	init_completion(&ofb->branch_done);}static int __devinit pxafb_overlay_init(struct pxafb_info *fbi){	int i, ret;	for (i = 0; i < 2; i++) {		init_pxafb_overlay(fbi, &fbi->overlay[i], i);		ret = register_framebuffer(&fbi->overlay[i].fb);		if (ret) {			dev_err(fbi->dev, "failed to register overlay %d\n", i);			return ret;		}	}	/* mask all IU/BS/EOF/SOF interrupts */	lcd_writel(fbi, LCCR5, ~0);	/* place overlay(s) on top of base */	fbi->lccr0 |= LCCR0_OUC;	pr_info("PXA Overlay driver loaded successfully!\n");	return 0;}static void __devexit pxafb_overlay_exit(struct pxafb_info *fbi){	int i;	for (i = 0; i < 2; i++)		unregister_framebuffer(&fbi->overlay[i].fb);}#elsestatic inline void pxafb_overlay_init(struct pxafb_info *fbi) {}static inline void pxafb_overlay_exit(struct pxafb_info *fbi) {}#endif /* CONFIG_FB_PXA_OVERLAY *//* * Calculate the PCD value from the clock rate (in picoseconds). * We take account of the PPCR clock setting. * From PXA Developer's Manual: * *   PixelClock =      LCLK *                ------------- *                2 ( PCD + 1 ) * *   PCD =      LCLK *         ------------- - 1 *         2(PixelClock) * * Where: *   LCLK = LCD/Memory Clock *   PCD = LCCR3[7:0] * * PixelClock here is in Hz while the pixclock argument given is the * period in picoseconds. Hence PixelClock = 1 / ( pixclock * 10^-12 ) * * The function get_lclk_frequency_10khz returns LCLK in units of * 10khz. Calling the result of this function lclk gives us the * following * *    PCD = (lclk * 10^4 ) * ( pixclock * 10^-12 ) *          -------------------------------------- - 1 *                          2 * * Factoring the 10^4 and 10^-12 out gives 10^-8 == 1 / 100000000 as used below. */static inline unsigned int get_pcd(struct pxafb_info *fbi,				   unsigned int pixclock){	unsigned long long pcd;	/* FIXME: Need to take into account Double Pixel Clock mode	 * (DPC) bit? or perhaps set it based on the various clock	 * speeds */	pcd = (unsigned long long)(clk_get_rate(fbi->clk) / 10000);	pcd *= pixclock;	do_div(pcd, 100000000 * 2);	/* no need for this, since we should subtract 1 anyway. they cancel */	/* pcd += 1; */ /* make up for integer math truncations */	return (unsigned int)pcd;}/* * Some touchscreens need hsync information from the video driver to * function correctly. We export it here.  Note that 'hsync_time' and * the value returned from pxafb_get_hsync_time() is the *reciprocal* * of the hsync period in seconds. */static inline void set_hsync_time(struct pxafb_info *fbi, unsigned int pcd){	unsigned long htime;	if ((pcd == 0) || (fbi->fb.var.hsync_len == 0)) {		fbi->hsync_time = 0;		return;	}	htime = clk_get_rate(fbi->clk) / (pcd * fbi->fb.var.hsync_len);	fbi->hsync_time = htime;}unsigned long pxafb_get_hsync_time(struct device *dev){	struct pxafb_info *fbi = dev_get_drvdata(dev);	/* If display is blanked/suspended, hsync isn't active */	if (!fbi || (fbi->state != C_ENABLE))		return 0;	return fbi->hsync_time;}EXPORT_SYMBOL(pxafb_get_hsync_time);static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal,			   unsigned long start, size_t size){	struct pxafb_dma_descriptor *dma_desc, *pal_desc;	unsigned int dma_desc_off, pal_desc_off;	if (dma < 0 || dma >= DMA_MAX * 2)		return -EINVAL;	dma_desc = &fbi->dma_buff->dma_desc[dma];	dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]);	dma_desc->fsadr = start;	dma_desc->fidr  = 0;	dma_desc->ldcmd = size;	if (pal < 0 || pal >= PAL_MAX * 2) {		dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off;		fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off;	} else {		pal_desc = &fbi->dma_buff->pal_desc[pal];		pal_desc_off = offsetof(struct pxafb_dma_buff, pal_desc[pal]);		pal_desc->fsadr = fbi->dma_buff_phys + pal * PALETTE_SIZE;		pal_desc->fidr  = 0;		if ((fbi->lccr4 & LCCR4_PAL_FOR_MASK) == LCCR4_PAL_FOR_0)			pal_desc->ldcmd = fbi->palette_size * sizeof(u16);		else			pal_desc->ldcmd = fbi->palette_size * sizeof(u32);		pal_desc->ldcmd |= LDCMD_PAL;		/* flip back and forth between palette and frame buffer */		pal_desc->fdadr = fbi->dma_buff_phys + dma_desc_off;		dma_desc->fdadr = fbi->dma_buff_phys + pal_desc_off;		fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off;	}	return 0;}static void setup_base_frame(struct pxafb_info *fbi, int branch){	struct fb_var_screeninfo *var = &fbi->fb.var;	struct fb_fix_screeninfo *fix = &fbi->fb.fix;	int nbytes, dma, pal, bpp = var->bits_per_pixel;	unsigned long offset;	dma = DMA_BASE + (branch ? DMA_MAX : 0);	pal = (bpp >= 16) ? PAL_NONE : PAL_BASE + (branch ? PAL_MAX : 0);	nbytes = fix->line_length * var->yres;	offset = fix->line_length * var->yoffset + fbi->video_mem_phys;	if (fbi->lccr0 & LCCR0_SDS) {		nbytes = nbytes / 2;		setup_frame_dma(fbi, dma + 1, PAL_NONE, offset + nbytes, nbytes);	}	setup_frame_dma(fbi, dma, pal, offset, nbytes);}#ifdef CONFIG_FB_PXA_SMARTPANELstatic int setup_smart_dma(struct pxafb_info *fbi){	struct pxafb_dma_descriptor *dma_desc;	unsigned long dma_desc_off, cmd_buff_off;	dma_desc = &fbi->dma_buff->dma_desc[DMA_CMD];	dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[DMA_CMD]);	cmd_buff_off = offsetof(struct pxafb_dma_buff, cmd_buff);	dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off;	dma_desc->fsadr = fbi->dma_buff_phys + cmd_buff_off;	dma_desc->fidr  = 0;	dma_desc->ldcmd = fbi->n_smart_cmds * sizeof(uint16_t);	fbi->fdadr[DMA_CMD] = dma_desc->fdadr;	return 0;}int pxafb_smart_flush(struct fb_info *info){	struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb);	uint32_t prsr;	int ret = 0;	/* disable controller until all registers are set up */	lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB);	/* 1. make it an even number of commands to align on 32-bit boundary	 * 2. add the interrupt command to the end of the chain so we can	 *    keep track of the end of the transfer	 */	while (fbi->n_smart_cmds & 1)		fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_NOOP;	fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_INTERRUPT;	fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_WAIT_FOR_VSYNC;	setup_smart_dma(fbi);	/* continue to execute next command */	prsr = lcd_readl(fbi, PRSR) | PRSR_ST_OK | PRSR_CON_NT;	lcd_writel(fbi, PRSR, prsr);	/* stop the processor in case it executed "wait for sync" cmd */	lcd_writel(fbi, CMDCR, 0x0001);	/* don't send interrupts for fifo underruns on channel 6 */	lcd_writel(fbi, LCCR5, LCCR5_IUM(6));	lcd_writel(fbi, LCCR1, fbi->reg_lccr1);	lcd_writel(fbi, LCCR2, fbi->reg_lccr2);	lcd_writel(fbi, LCCR3, fbi->reg_lccr3);	lcd_writel(fbi, LCCR4, fbi->reg_lccr4);	lcd_writel(fbi, FDADR0, fbi->fdadr[0]);	lcd_writel(fbi, FDADR6, fbi->fdadr[6]);	/* begin sending */	lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB);	if (wait_for_completion_timeout(&fbi->command_done, HZ/2) == 0) {		pr_warning("%s: timeout waiting for command done\n",				__func__);		ret = -ETIMEDOUT;	}	/* quick disable */	prsr = lcd_readl(fbi, PRSR) & ~(PRSR_ST_OK | PRSR_CON_NT);	lcd_writel(fbi, PRSR, prsr);	lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -