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

📄 atmel_lcdfb.c

📁 Linux环境下视频显示卡设备的驱动程序源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  Driver for AT91/AT32 LCD Controller * *  Copyright (C) 2007 Atmel Corporation * * This file is subject to the terms and conditions of the GNU General Public * License.  See the file COPYING in the main directory of this archive for * more details. */#include <linux/kernel.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <linux/interrupt.h>#include <linux/clk.h>#include <linux/fb.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/backlight.h>#include <mach/board.h>#include <mach/cpu.h>#include <mach/gpio.h>#include <video/atmel_lcdc.h>#define lcdc_readl(sinfo, reg)		__raw_readl((sinfo)->mmio+(reg))#define lcdc_writel(sinfo, reg, val)	__raw_writel((val), (sinfo)->mmio+(reg))/* configurable parameters */#define ATMEL_LCDC_CVAL_DEFAULT		0xc8#define ATMEL_LCDC_DMA_BURST_LEN	8#if defined(CONFIG_ARCH_AT91SAM9263) || defined(CONFIG_ARCH_AT91CAP9) || \	defined(CONFIG_ARCH_AT91SAM9RL)#define ATMEL_LCDC_FIFO_SIZE		2048#else#define ATMEL_LCDC_FIFO_SIZE		512#endif#if defined(CONFIG_ARCH_AT91)#define	ATMEL_LCDFB_FBINFO_DEFAULT	(FBINFO_DEFAULT \					 | FBINFO_PARTIAL_PAN_OK \					 | FBINFO_HWACCEL_YPAN)static inline void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo,					struct fb_var_screeninfo *var){}#elif defined(CONFIG_AVR32)#define	ATMEL_LCDFB_FBINFO_DEFAULT	(FBINFO_DEFAULT \					| FBINFO_PARTIAL_PAN_OK \					| FBINFO_HWACCEL_XPAN \					| FBINFO_HWACCEL_YPAN)static void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo,				     struct fb_var_screeninfo *var){	u32 dma2dcfg;	u32 pixeloff;	pixeloff = (var->xoffset * var->bits_per_pixel) & 0x1f;	dma2dcfg = ((var->xres_virtual - var->xres) * var->bits_per_pixel) / 8;	dma2dcfg |= pixeloff << ATMEL_LCDC_PIXELOFF_OFFSET;	lcdc_writel(sinfo, ATMEL_LCDC_DMA2DCFG, dma2dcfg);	/* Update configuration */	lcdc_writel(sinfo, ATMEL_LCDC_DMACON,		    lcdc_readl(sinfo, ATMEL_LCDC_DMACON)		    | ATMEL_LCDC_DMAUPDT);}#endifstatic const u32 contrast_ctr = ATMEL_LCDC_PS_DIV8		| ATMEL_LCDC_POL_POSITIVE		| ATMEL_LCDC_ENA_PWMENABLE;#ifdef CONFIG_BACKLIGHT_ATMEL_LCDC/* some bl->props field just changed */static int atmel_bl_update_status(struct backlight_device *bl){	struct atmel_lcdfb_info *sinfo = bl_get_data(bl);	int			power = sinfo->bl_power;	int			brightness = bl->props.brightness;	/* REVISIT there may be a meaningful difference between	 * fb_blank and power ... there seem to be some cases	 * this doesn't handle correctly.	 */	if (bl->props.fb_blank != sinfo->bl_power)		power = bl->props.fb_blank;	else if (bl->props.power != sinfo->bl_power)		power = bl->props.power;	if (brightness < 0 && power == FB_BLANK_UNBLANK)		brightness = lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_VAL);	else if (power != FB_BLANK_UNBLANK)		brightness = 0;	lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_VAL, brightness);	lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR,			brightness ? contrast_ctr : 0);	bl->props.fb_blank = bl->props.power = sinfo->bl_power = power;	return 0;}static int atmel_bl_get_brightness(struct backlight_device *bl){	struct atmel_lcdfb_info *sinfo = bl_get_data(bl);	return lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_VAL);}static struct backlight_ops atmel_lcdc_bl_ops = {	.update_status = atmel_bl_update_status,	.get_brightness = atmel_bl_get_brightness,};static void init_backlight(struct atmel_lcdfb_info *sinfo){	struct backlight_device	*bl;	sinfo->bl_power = FB_BLANK_UNBLANK;	if (sinfo->backlight)		return;	bl = backlight_device_register("backlight", &sinfo->pdev->dev,			sinfo, &atmel_lcdc_bl_ops);	if (IS_ERR(bl)) {		dev_err(&sinfo->pdev->dev, "error %ld on backlight register\n",				PTR_ERR(bl));		return;	}	sinfo->backlight = bl;	bl->props.power = FB_BLANK_UNBLANK;	bl->props.fb_blank = FB_BLANK_UNBLANK;	bl->props.max_brightness = 0xff;	bl->props.brightness = atmel_bl_get_brightness(bl);}static void exit_backlight(struct atmel_lcdfb_info *sinfo){	if (sinfo->backlight)		backlight_device_unregister(sinfo->backlight);}#elsestatic void init_backlight(struct atmel_lcdfb_info *sinfo){	dev_warn(&sinfo->pdev->dev, "backlight control is not available\n");}static void exit_backlight(struct atmel_lcdfb_info *sinfo){}#endifstatic void init_contrast(struct atmel_lcdfb_info *sinfo){	/* have some default contrast/backlight settings */	lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, contrast_ctr);	lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT);	if (sinfo->lcdcon_is_backlight)		init_backlight(sinfo);}static struct fb_fix_screeninfo atmel_lcdfb_fix __initdata = {	.type		= FB_TYPE_PACKED_PIXELS,	.visual		= FB_VISUAL_TRUECOLOR,	.xpanstep	= 0,	.ypanstep	= 1,	.ywrapstep	= 0,	.accel		= FB_ACCEL_NONE,};static unsigned long compute_hozval(unsigned long xres, unsigned long lcdcon2){	unsigned long value;	if (!(cpu_is_at91sam9261() || cpu_is_at32ap7000()))		return xres;	value = xres;	if ((lcdcon2 & ATMEL_LCDC_DISTYPE) != ATMEL_LCDC_DISTYPE_TFT) {		/* STN display */		if ((lcdcon2 & ATMEL_LCDC_DISTYPE) == ATMEL_LCDC_DISTYPE_STNCOLOR) {			value *= 3;		}		if ( (lcdcon2 & ATMEL_LCDC_IFWIDTH) == ATMEL_LCDC_IFWIDTH_4		   || ( (lcdcon2 & ATMEL_LCDC_IFWIDTH) == ATMEL_LCDC_IFWIDTH_8		      && (lcdcon2 & ATMEL_LCDC_SCANMOD) == ATMEL_LCDC_SCANMOD_DUAL ))			value = DIV_ROUND_UP(value, 4);		else			value = DIV_ROUND_UP(value, 8);	}	return value;}static void atmel_lcdfb_stop_nowait(struct atmel_lcdfb_info *sinfo){	/* Turn off the LCD controller and the DMA controller */	lcdc_writel(sinfo, ATMEL_LCDC_PWRCON,			sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET);	/* Wait for the LCDC core to become idle */	while (lcdc_readl(sinfo, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY)		msleep(10);	lcdc_writel(sinfo, ATMEL_LCDC_DMACON, 0);}static void atmel_lcdfb_stop(struct atmel_lcdfb_info *sinfo){	atmel_lcdfb_stop_nowait(sinfo);	/* Wait for DMA engine to become idle... */	while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY)		msleep(10);}static void atmel_lcdfb_start(struct atmel_lcdfb_info *sinfo){	lcdc_writel(sinfo, ATMEL_LCDC_DMACON, sinfo->default_dmacon);	lcdc_writel(sinfo, ATMEL_LCDC_PWRCON,		(sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET)		| ATMEL_LCDC_PWR);}static void atmel_lcdfb_update_dma(struct fb_info *info,			       struct fb_var_screeninfo *var){	struct atmel_lcdfb_info *sinfo = info->par;	struct fb_fix_screeninfo *fix = &info->fix;	unsigned long dma_addr;	dma_addr = (fix->smem_start + var->yoffset * fix->line_length		    + var->xoffset * var->bits_per_pixel / 8);	dma_addr &= ~3UL;	/* Set framebuffer DMA base address and pixel offset */	lcdc_writel(sinfo, ATMEL_LCDC_DMABADDR1, dma_addr);	atmel_lcdfb_update_dma2d(sinfo, var);}static inline void atmel_lcdfb_free_video_memory(struct atmel_lcdfb_info *sinfo){	struct fb_info *info = sinfo->info;	dma_free_writecombine(info->device, info->fix.smem_len,				info->screen_base, info->fix.smem_start);}/** *	atmel_lcdfb_alloc_video_memory - Allocate framebuffer memory *	@sinfo: the frame buffer to allocate memory for */static int atmel_lcdfb_alloc_video_memory(struct atmel_lcdfb_info *sinfo){	struct fb_info *info = sinfo->info;	struct fb_var_screeninfo *var = &info->var;	unsigned int smem_len;	smem_len = (var->xres_virtual * var->yres_virtual		    * ((var->bits_per_pixel + 7) / 8));	info->fix.smem_len = max(smem_len, sinfo->smem_len);	info->screen_base = dma_alloc_writecombine(info->device, info->fix.smem_len,					(dma_addr_t *)&info->fix.smem_start, GFP_KERNEL);	if (!info->screen_base) {		return -ENOMEM;	}	memset(info->screen_base, 0, info->fix.smem_len);	return 0;}static const struct fb_videomode *atmel_lcdfb_choose_mode(struct fb_var_screeninfo *var,						     struct fb_info *info){	struct fb_videomode varfbmode;	const struct fb_videomode *fbmode = NULL;	fb_var_to_videomode(&varfbmode, var);	fbmode = fb_find_nearest_mode(&varfbmode, &info->modelist);	if (fbmode)		fb_videomode_to_var(var, fbmode);	return fbmode;}/** *      atmel_lcdfb_check_var - Validates a var passed in. *      @var: frame buffer variable screen structure *      @info: frame buffer structure that represents a single frame buffer * *	Checks to see if the hardware supports the state requested by *	var passed in. This function does not alter the hardware *	state!!!  This means the data stored in struct fb_info and *	struct atmel_lcdfb_info do not change. This includes the var *	inside of struct fb_info.  Do NOT change these. This function *	can be called on its own if we intent to only test a mode and *	not actually set it. The stuff in modedb.c is a example of *	this. If the var passed in is slightly off by what the *	hardware can support then we alter the var PASSED in to what *	we can do. If the hardware doesn't support mode change a *	-EINVAL will be returned by the upper layers. You don't need *	to implement this function then. If you hardware doesn't *	support changing the resolution then this function is not *	needed. In this case the driver would just provide a var that *	represents the static state the screen is in. * *	Returns negative errno on error, or zero on success. */static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var,			     struct fb_info *info){	struct device *dev = info->device;	struct atmel_lcdfb_info *sinfo = info->par;	unsigned long clk_value_khz;	clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000;	dev_dbg(dev, "%s:\n", __func__);	if (!(var->pixclock && var->bits_per_pixel)) {		/* choose a suitable mode if possible */		if (!atmel_lcdfb_choose_mode(var, info)) {			dev_err(dev, "needed value not specified\n");			return -EINVAL;		}	}	dev_dbg(dev, "  resolution: %ux%u\n", var->xres, var->yres);	dev_dbg(dev, "  pixclk:     %lu KHz\n", PICOS2KHZ(var->pixclock));	dev_dbg(dev, "  bpp:        %u\n", var->bits_per_pixel);	dev_dbg(dev, "  clk:        %lu KHz\n", clk_value_khz);	if ((PICOS2KHZ(var->pixclock) * var->bits_per_pixel / 8) > clk_value_khz) {		dev_err(dev, "%lu KHz pixel clock is too fast\n", PICOS2KHZ(var->pixclock));		return -EINVAL;	}	/* Do not allow to have real resoulution larger than virtual */	if (var->xres > var->xres_virtual)		var->xres_virtual = var->xres;	if (var->yres > var->yres_virtual)		var->yres_virtual = var->yres;	/* Force same alignment for each line */	var->xres = (var->xres + 3) & ~3UL;	var->xres_virtual = (var->xres_virtual + 3) & ~3UL;	var->red.msb_right = var->green.msb_right = var->blue.msb_right = 0;	var->transp.msb_right = 0;

⌨️ 快捷键说明

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