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

📄 atmel_lcdfb.c

📁 Linux环境下视频显示卡设备的驱动程序源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	var->transp.offset = var->transp.length = 0;	var->xoffset = var->yoffset = 0;	if (info->fix.smem_len) {		unsigned int smem_len = (var->xres_virtual * var->yres_virtual					 * ((var->bits_per_pixel + 7) / 8));		if (smem_len > info->fix.smem_len)			return -EINVAL;	}	/* Saturate vertical and horizontal timings at maximum values */	var->vsync_len = min_t(u32, var->vsync_len,			(ATMEL_LCDC_VPW >> ATMEL_LCDC_VPW_OFFSET) + 1);	var->upper_margin = min_t(u32, var->upper_margin,			ATMEL_LCDC_VBP >> ATMEL_LCDC_VBP_OFFSET);	var->lower_margin = min_t(u32, var->lower_margin,			ATMEL_LCDC_VFP);	var->right_margin = min_t(u32, var->right_margin,			(ATMEL_LCDC_HFP >> ATMEL_LCDC_HFP_OFFSET) + 1);	var->hsync_len = min_t(u32, var->hsync_len,			(ATMEL_LCDC_HPW >> ATMEL_LCDC_HPW_OFFSET) + 1);	var->left_margin = min_t(u32, var->left_margin,			ATMEL_LCDC_HBP + 1);	/* Some parameters can't be zero */	var->vsync_len = max_t(u32, var->vsync_len, 1);	var->right_margin = max_t(u32, var->right_margin, 1);	var->hsync_len = max_t(u32, var->hsync_len, 1);	var->left_margin = max_t(u32, var->left_margin, 1);	switch (var->bits_per_pixel) {	case 1:	case 2:	case 4:	case 8:		var->red.offset = var->green.offset = var->blue.offset = 0;		var->red.length = var->green.length = var->blue.length			= var->bits_per_pixel;		break;	case 15:	case 16:		if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) {			/* RGB:565 mode */			var->red.offset = 11;			var->blue.offset = 0;			var->green.length = 6;		} else if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB555) {			var->red.offset = 10;			var->blue.offset = 0;			var->green.length = 5;		} else {			/* BGR:555 mode */			var->red.offset = 0;			var->blue.offset = 10;			var->green.length = 5;		}		var->green.offset = 5;		var->red.length = var->blue.length = 5;		break;	case 32:		var->transp.offset = 24;		var->transp.length = 8;		/* fall through */	case 24:		if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) {			/* RGB:888 mode */			var->red.offset = 16;			var->blue.offset = 0;		} else {			/* BGR:888 mode */			var->red.offset = 0;			var->blue.offset = 16;		}		var->green.offset = 8;		var->red.length = var->green.length = var->blue.length = 8;		break;	default:		dev_err(dev, "color depth %d not supported\n",					var->bits_per_pixel);		return -EINVAL;	}	return 0;}/* * LCD reset sequence */static void atmel_lcdfb_reset(struct atmel_lcdfb_info *sinfo){	might_sleep();	atmel_lcdfb_stop(sinfo);	atmel_lcdfb_start(sinfo);}/** *      atmel_lcdfb_set_par - Alters the hardware state. *      @info: frame buffer structure that represents a single frame buffer * *	Using the fb_var_screeninfo in fb_info we set the resolution *	of the this particular framebuffer. This function alters the *	par AND the fb_fix_screeninfo stored in fb_info. It doesn't *	not alter var in fb_info since we are using that data. This *	means we depend on the data in var inside fb_info to be *	supported by the hardware.  atmel_lcdfb_check_var is always called *	before atmel_lcdfb_set_par to ensure this.  Again if you can't *	change the resolution you don't need this function. * */static int atmel_lcdfb_set_par(struct fb_info *info){	struct atmel_lcdfb_info *sinfo = info->par;	unsigned long hozval_linesz;	unsigned long value;	unsigned long clk_value_khz;	unsigned long bits_per_line;	might_sleep();	dev_dbg(info->device, "%s:\n", __func__);	dev_dbg(info->device, "  * resolution: %ux%u (%ux%u virtual)\n",		 info->var.xres, info->var.yres,		 info->var.xres_virtual, info->var.yres_virtual);	atmel_lcdfb_stop_nowait(sinfo);	if (info->var.bits_per_pixel == 1)		info->fix.visual = FB_VISUAL_MONO01;	else if (info->var.bits_per_pixel <= 8)		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;	else		info->fix.visual = FB_VISUAL_TRUECOLOR;	bits_per_line = info->var.xres_virtual * info->var.bits_per_pixel;	info->fix.line_length = DIV_ROUND_UP(bits_per_line, 8);	/* Re-initialize the DMA engine... */	dev_dbg(info->device, "  * update DMA engine\n");	atmel_lcdfb_update_dma(info, &info->var);	/* ...set frame size and burst length = 8 words (?) */	value = (info->var.yres * info->var.xres * info->var.bits_per_pixel) / 32;	value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET);	lcdc_writel(sinfo, ATMEL_LCDC_DMAFRMCFG, value);	/* Now, the LCDC core... */	/* Set pixel clock */	clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000;	value = DIV_ROUND_UP(clk_value_khz, PICOS2KHZ(info->var.pixclock));	if (value < 2) {		dev_notice(info->device, "Bypassing pixel clock divider\n");		lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS);	} else {		value = (value / 2) - 1;		dev_dbg(info->device, "  * programming CLKVAL = 0x%08lx\n",				value);		lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1,				value << ATMEL_LCDC_CLKVAL_OFFSET);		info->var.pixclock = KHZ2PICOS(clk_value_khz / (2 * (value + 1)));		dev_dbg(info->device, "  updated pixclk:     %lu KHz\n",					PICOS2KHZ(info->var.pixclock));	}	/* Initialize control register 2 */	value = sinfo->default_lcdcon2;	if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))		value |= ATMEL_LCDC_INVLINE_INVERTED;	if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))		value |= ATMEL_LCDC_INVFRAME_INVERTED;	switch (info->var.bits_per_pixel) {		case 1:	value |= ATMEL_LCDC_PIXELSIZE_1; break;		case 2: value |= ATMEL_LCDC_PIXELSIZE_2; break;		case 4: value |= ATMEL_LCDC_PIXELSIZE_4; break;		case 8: value |= ATMEL_LCDC_PIXELSIZE_8; break;		case 15: /* fall through */		case 16: value |= ATMEL_LCDC_PIXELSIZE_16; break;		case 24: value |= ATMEL_LCDC_PIXELSIZE_24; break;		case 32: value |= ATMEL_LCDC_PIXELSIZE_32; break;		default: BUG(); break;	}	dev_dbg(info->device, "  * LCDCON2 = %08lx\n", value);	lcdc_writel(sinfo, ATMEL_LCDC_LCDCON2, value);	/* Vertical timing */	value = (info->var.vsync_len - 1) << ATMEL_LCDC_VPW_OFFSET;	value |= info->var.upper_margin << ATMEL_LCDC_VBP_OFFSET;	value |= info->var.lower_margin;	dev_dbg(info->device, "  * LCDTIM1 = %08lx\n", value);	lcdc_writel(sinfo, ATMEL_LCDC_TIM1, value);	/* Horizontal timing */	value = (info->var.right_margin - 1) << ATMEL_LCDC_HFP_OFFSET;	value |= (info->var.hsync_len - 1) << ATMEL_LCDC_HPW_OFFSET;	value |= (info->var.left_margin - 1);	dev_dbg(info->device, "  * LCDTIM2 = %08lx\n", value);	lcdc_writel(sinfo, ATMEL_LCDC_TIM2, value);	/* Horizontal value (aka line size) */	hozval_linesz = compute_hozval(info->var.xres,					lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2));	/* Display size */	value = (hozval_linesz - 1) << ATMEL_LCDC_HOZVAL_OFFSET;	value |= info->var.yres - 1;	dev_dbg(info->device, "  * LCDFRMCFG = %08lx\n", value);	lcdc_writel(sinfo, ATMEL_LCDC_LCDFRMCFG, value);	/* FIFO Threshold: Use formula from data sheet */	value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3);	lcdc_writel(sinfo, ATMEL_LCDC_FIFO, value);	/* Toggle LCD_MODE every frame */	lcdc_writel(sinfo, ATMEL_LCDC_MVAL, 0);	/* Disable all interrupts */	lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL);	/* Enable FIFO & DMA errors */	lcdc_writel(sinfo, ATMEL_LCDC_IER, ATMEL_LCDC_UFLWI | ATMEL_LCDC_OWRI | ATMEL_LCDC_MERI);	/* ...wait for DMA engine to become idle... */	while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY)		msleep(10);	atmel_lcdfb_start(sinfo);	dev_dbg(info->device, "  * DONE\n");	return 0;}static inline unsigned int chan_to_field(unsigned int chan, const struct fb_bitfield *bf){	chan &= 0xffff;	chan >>= 16 - bf->length;	return chan << bf->offset;}/** *  	atmel_lcdfb_setcolreg - Optional function. Sets a color register. *      @regno: Which register in the CLUT we are programming *      @red: The red value which can be up to 16 bits wide *	@green: The green value which can be up to 16 bits wide *	@blue:  The blue value which can be up to 16 bits wide. *	@transp: If supported the alpha value which can be up to 16 bits wide. *      @info: frame buffer info structure * *  	Set a single color register. The values supplied have a 16 bit *  	magnitude which needs to be scaled in this function for the hardware. *	Things to take into consideration are how many color registers, if *	any, are supported with the current color visual. With truecolor mode *	no color palettes are supported. Here a psuedo palette is created *	which we store the value in pseudo_palette in struct fb_info. For *	pseudocolor mode we have a limited color palette. To deal with this *	we can program what color is displayed for a particular pixel value. *	DirectColor is similar in that we can program each color field. If *	we have a static colormap we don't need to implement this function. * *	Returns negative errno on error, or zero on success. In an *	ideal world, this would have been the case, but as it turns *	out, the other drivers return 1 on failure, so that's what *	we're going to do. */static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red,			     unsigned int green, unsigned int blue,			     unsigned int transp, struct fb_info *info){	struct atmel_lcdfb_info *sinfo = info->par;	unsigned int val;	u32 *pal;	int ret = 1;	if (info->var.grayscale)		red = green = blue = (19595 * red + 38470 * green				      + 7471 * blue) >> 16;	switch (info->fix.visual) {	case FB_VISUAL_TRUECOLOR:		if (regno < 16) {			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;			ret = 0;		}		break;	case FB_VISUAL_PSEUDOCOLOR:		if (regno < 256) {			val  = ((red   >> 11) & 0x001f);			val |= ((green >>  6) & 0x03e0);			val |= ((blue  >>  1) & 0x7c00);			/*			 * TODO: intensity bit. Maybe something like			 *   ~(red[10] ^ green[10] ^ blue[10]) & 1			 */			lcdc_writel(sinfo, ATMEL_LCDC_LUT(regno), val);			ret = 0;		}		break;	case FB_VISUAL_MONO01:		if (regno < 2) {			val = (regno == 0) ? 0x00 : 0x1F;			lcdc_writel(sinfo, ATMEL_LCDC_LUT(regno), val);			ret = 0;		}		break;	}	return ret;}static int atmel_lcdfb_pan_display(struct fb_var_screeninfo *var,			       struct fb_info *info){	dev_dbg(info->device, "%s\n", __func__);	atmel_lcdfb_update_dma(info, var);	return 0;}static struct fb_ops atmel_lcdfb_ops = {	.owner		= THIS_MODULE,	.fb_check_var	= atmel_lcdfb_check_var,	.fb_set_par	= atmel_lcdfb_set_par,	.fb_setcolreg	= atmel_lcdfb_setcolreg,	.fb_pan_display	= atmel_lcdfb_pan_display,	.fb_fillrect	= cfb_fillrect,	.fb_copyarea	= cfb_copyarea,	.fb_imageblit	= cfb_imageblit,};static irqreturn_t atmel_lcdfb_interrupt(int irq, void *dev_id){	struct fb_info *info = dev_id;	struct atmel_lcdfb_info *sinfo = info->par;	u32 status;	status = lcdc_readl(sinfo, ATMEL_LCDC_ISR);	if (status & ATMEL_LCDC_UFLWI) {		dev_warn(info->device, "FIFO underflow %#x\n", status);		/* reset DMA and FIFO to avoid screen shifting */		schedule_work(&sinfo->task);	}	lcdc_writel(sinfo, ATMEL_LCDC_ICR, status);	return IRQ_HANDLED;}/* * LCD controller task (to reset the LCD) */static void atmel_lcdfb_task(struct work_struct *work){	struct atmel_lcdfb_info *sinfo =		container_of(work, struct atmel_lcdfb_info, task);	atmel_lcdfb_reset(sinfo);

⌨️ 快捷键说明

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