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

📄 pxafb.c

📁 Linux环境下视频显示卡设备的驱动程序源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  linux/drivers/video/pxafb.c * *  Copyright (C) 1999 Eric A. Thomas. *  Copyright (C) 2004 Jean-Frederic Clere. *  Copyright (C) 2004 Ian Campbell. *  Copyright (C) 2004 Jeff Lackey. *   Based on sa1100fb.c Copyright (C) 1999 Eric A. Thomas *  which in turn is *   Based on acornfb.c Copyright (C) Russell King. * * 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. * *	        Intel PXA250/210 LCD Controller Frame Buffer Driver * * Please direct your questions and comments on this driver to the following * email address: * *	linux-arm-kernel@lists.arm.linux.org.uk * * Add support for overlay1 and overlay2 based on pxafb_overlay.c: * *   Copyright (C) 2004, Intel Corporation * *     2003/08/27: <yu.tang@intel.com> *     2004/03/10: <stanley.cai@intel.com> *     2004/10/28: <yan.yin@intel.com> * *   Copyright (C) 2006-2008 Marvell International Ltd. *   All Rights Reserved */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/fb.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/cpufreq.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <linux/clk.h>#include <linux/err.h>#include <linux/completion.h>#include <linux/mutex.h>#include <linux/kthread.h>#include <linux/freezer.h>#include <mach/hardware.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/div64.h>#include <mach/pxa-regs.h>#include <mach/bitfield.h>#include <mach/pxafb.h>/* * Complain if VAR is out of range. */#define DEBUG_VAR 1#include "pxafb.h"/* Bits which should not be set in machine configuration structures */#define LCCR0_INVALID_CONFIG_MASK	(LCCR0_OUM | LCCR0_BM | LCCR0_QDM |\					 LCCR0_DIS | LCCR0_EFM | LCCR0_IUM |\					 LCCR0_SFM | LCCR0_LDM | LCCR0_ENB)#define LCCR3_INVALID_CONFIG_MASK	(LCCR3_HSP | LCCR3_VSP |\					 LCCR3_PCD | LCCR3_BPP(0xf))static int pxafb_activate_var(struct fb_var_screeninfo *var,				struct pxafb_info *);static void set_ctrlr_state(struct pxafb_info *fbi, u_int state);static void setup_base_frame(struct pxafb_info *fbi, int branch);static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal,			   unsigned long offset, size_t size);static unsigned long video_mem_size = 0;static inline unsigned longlcd_readl(struct pxafb_info *fbi, unsigned int off){	return __raw_readl(fbi->mmio_base + off);}static inline voidlcd_writel(struct pxafb_info *fbi, unsigned int off, unsigned long val){	__raw_writel(val, fbi->mmio_base + off);}static inline void pxafb_schedule_work(struct pxafb_info *fbi, u_int state){	unsigned long flags;	local_irq_save(flags);	/*	 * We need to handle two requests being made at the same time.	 * There are two important cases:	 *  1. When we are changing VT (C_REENABLE) while unblanking	 *     (C_ENABLE) We must perform the unblanking, which will	 *     do our REENABLE for us.	 *  2. When we are blanking, but immediately unblank before	 *     we have blanked.  We do the "REENABLE" thing here as	 *     well, just to be sure.	 */	if (fbi->task_state == C_ENABLE && state == C_REENABLE)		state = (u_int) -1;	if (fbi->task_state == C_DISABLE && state == C_ENABLE)		state = C_REENABLE;	if (state != (u_int)-1) {		fbi->task_state = state;		schedule_work(&fbi->task);	}	local_irq_restore(flags);}static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf){	chan &= 0xffff;	chan >>= 16 - bf->length;	return chan << bf->offset;}static intpxafb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,		       u_int trans, struct fb_info *info){	struct pxafb_info *fbi = (struct pxafb_info *)info;	u_int val;	if (regno >= fbi->palette_size)		return 1;	if (fbi->fb.var.grayscale) {		fbi->palette_cpu[regno] = ((blue >> 8) & 0x00ff);		return 0;	}	switch (fbi->lccr4 & LCCR4_PAL_FOR_MASK) {	case LCCR4_PAL_FOR_0:		val  = ((red   >>  0) & 0xf800);		val |= ((green >>  5) & 0x07e0);		val |= ((blue  >> 11) & 0x001f);		fbi->palette_cpu[regno] = val;		break;	case LCCR4_PAL_FOR_1:		val  = ((red   << 8) & 0x00f80000);		val |= ((green >> 0) & 0x0000fc00);		val |= ((blue  >> 8) & 0x000000f8);		((u32 *)(fbi->palette_cpu))[regno] = val;		break;	case LCCR4_PAL_FOR_2:		val  = ((red   << 8) & 0x00fc0000);		val |= ((green >> 0) & 0x0000fc00);		val |= ((blue  >> 8) & 0x000000fc);		((u32 *)(fbi->palette_cpu))[regno] = val;		break;	case LCCR4_PAL_FOR_3:		val  = ((red   << 8) & 0x00ff0000);		val |= ((green >> 0) & 0x0000ff00);		val |= ((blue  >> 8) & 0x000000ff);		((u32 *)(fbi->palette_cpu))[regno] = val;		break;	}	return 0;}static intpxafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,		   u_int trans, struct fb_info *info){	struct pxafb_info *fbi = (struct pxafb_info *)info;	unsigned int val;	int ret = 1;	/*	 * If inverse mode was selected, invert all the colours	 * rather than the register number.  The register number	 * is what you poke into the framebuffer to produce the	 * colour you requested.	 */	if (fbi->cmap_inverse) {		red   = 0xffff - red;		green = 0xffff - green;		blue  = 0xffff - blue;	}	/*	 * If greyscale is true, then we convert the RGB value	 * to greyscale no matter what visual we are using.	 */	if (fbi->fb.var.grayscale)		red = green = blue = (19595 * red + 38470 * green +					7471 * blue) >> 16;	switch (fbi->fb.fix.visual) {	case FB_VISUAL_TRUECOLOR:		/*		 * 16-bit True Colour.  We encode the RGB value		 * according to the RGB bitfield information.		 */		if (regno < 16) {			u32 *pal = fbi->fb.pseudo_palette;			val  = chan_to_field(red, &fbi->fb.var.red);			val |= chan_to_field(green, &fbi->fb.var.green);			val |= chan_to_field(blue, &fbi->fb.var.blue);			pal[regno] = val;			ret = 0;		}		break;	case FB_VISUAL_STATIC_PSEUDOCOLOR:	case FB_VISUAL_PSEUDOCOLOR:		ret = pxafb_setpalettereg(regno, red, green, blue, trans, info);		break;	}	return ret;}/* calculate pixel depth, transparency bit included, >=16bpp formats _only_ */static inline int var_to_depth(struct fb_var_screeninfo *var){	return var->red.length + var->green.length +		var->blue.length + var->transp.length;}/* calculate 4-bit BPP value for LCCR3 and OVLxC1 */static int pxafb_var_to_bpp(struct fb_var_screeninfo *var){	int bpp = -EINVAL;	switch (var->bits_per_pixel) {	case 1:  bpp = 0; break;	case 2:  bpp = 1; break;	case 4:  bpp = 2; break;	case 8:  bpp = 3; break;	case 16: bpp = 4; break;	case 24:		switch (var_to_depth(var)) {		case 18: bpp = 6; break; /* 18-bits/pixel packed */		case 19: bpp = 8; break; /* 19-bits/pixel packed */		case 24: bpp = 9; break;		}		break;	case 32:		switch (var_to_depth(var)) {		case 18: bpp = 5; break; /* 18-bits/pixel unpacked */		case 19: bpp = 7; break; /* 19-bits/pixel unpacked */		case 25: bpp = 10; break;		}		break;	}	return bpp;}/* *  pxafb_var_to_lccr3(): *    Convert a bits per pixel value to the correct bit pattern for LCCR3 * *  NOTE: for PXA27x with overlays support, the LCCR3_PDFOR_x bits have an *  implication of the acutal use of transparency bit,  which we handle it *  here separatedly. See PXA27x Developer's Manual, Section <<7.4.6 Pixel *  Formats>> for the valid combination of PDFOR, PAL_FOR for various BPP. * *  Transparency for palette pixel formats is not supported at the moment. */static uint32_t pxafb_var_to_lccr3(struct fb_var_screeninfo *var){	int bpp = pxafb_var_to_bpp(var);	uint32_t lccr3;	if (bpp < 0)		return 0;	lccr3 = LCCR3_BPP(bpp);	switch (var_to_depth(var)) {	case 16: lccr3 |= var->transp.length ? LCCR3_PDFOR_3 : 0; break;	case 18: lccr3 |= LCCR3_PDFOR_3; break;	case 24: lccr3 |= var->transp.length ? LCCR3_PDFOR_2 : LCCR3_PDFOR_3;		 break;	case 19:	case 25: lccr3 |= LCCR3_PDFOR_0; break;	}	return lccr3;}#define SET_PIXFMT(v, r, g, b, t)				\({								\	(v)->transp.offset = (t) ? (r) + (g) + (b) : 0;		\	(v)->transp.length = (t) ? (t) : 0;			\	(v)->blue.length   = (b); (v)->blue.offset = 0;		\	(v)->green.length  = (g); (v)->green.offset = (b);	\	(v)->red.length    = (r); (v)->red.offset = (b) + (g);	\})/* set the RGBT bitfields of fb_var_screeninf according to * var->bits_per_pixel and given depth */static void pxafb_set_pixfmt(struct fb_var_screeninfo *var, int depth){	if (depth == 0)		depth = var->bits_per_pixel;	if (var->bits_per_pixel < 16) {		/* indexed pixel formats */		var->red.offset    = 0; var->red.length    = 8;		var->green.offset  = 0; var->green.length  = 8;		var->blue.offset   = 0; var->blue.length   = 8;		var->transp.offset = 0; var->transp.length = 8;	}	switch (depth) {	case 16: var->transp.length ?		 SET_PIXFMT(var, 5, 5, 5, 1) :		/* RGBT555 */		 SET_PIXFMT(var, 5, 6, 5, 0); break;	/* RGB565 */	case 18: SET_PIXFMT(var, 6, 6, 6, 0); break;	/* RGB666 */	case 19: SET_PIXFMT(var, 6, 6, 6, 1); break;	/* RGBT666 */	case 24: var->transp.length ?		 SET_PIXFMT(var, 8, 8, 7, 1) :		/* RGBT887 */		 SET_PIXFMT(var, 8, 8, 8, 0); break;	/* RGB888 */	case 25: SET_PIXFMT(var, 8, 8, 8, 1); break;	/* RGBT888 */	}}#ifdef CONFIG_CPU_FREQ/* *  pxafb_display_dma_period() *    Calculate the minimum period (in picoseconds) between two DMA *    requests for the LCD controller.  If we hit this, it means we're *    doing nothing but LCD DMA. */static unsigned int pxafb_display_dma_period(struct fb_var_screeninfo *var){	/*	 * Period = pixclock * bits_per_byte * bytes_per_transfer	 *              / memory_bits_per_pixel;	 */	return var->pixclock * 8 * 16 / var->bits_per_pixel;}#endif/* * Select the smallest mode that allows the desired resolution to be * displayed. If desired parameters can be rounded up. */static struct pxafb_mode_info *pxafb_getmode(struct pxafb_mach_info *mach,					     struct fb_var_screeninfo *var){	struct pxafb_mode_info *mode = NULL;	struct pxafb_mode_info *modelist = mach->modes;	unsigned int best_x = 0xffffffff, best_y = 0xffffffff;	unsigned int i;	for (i = 0; i < mach->num_modes; i++) {		if (modelist[i].xres >= var->xres &&		    modelist[i].yres >= var->yres &&		    modelist[i].xres < best_x &&		    modelist[i].yres < best_y &&		    modelist[i].bpp >= var->bits_per_pixel) {			best_x = modelist[i].xres;			best_y = modelist[i].yres;			mode = &modelist[i];		}	}	return mode;}static void pxafb_setmode(struct fb_var_screeninfo *var,			  struct pxafb_mode_info *mode){	var->xres		= mode->xres;	var->yres		= mode->yres;	var->bits_per_pixel	= mode->bpp;	var->pixclock		= mode->pixclock;	var->hsync_len		= mode->hsync_len;	var->left_margin	= mode->left_margin;	var->right_margin	= mode->right_margin;	var->vsync_len		= mode->vsync_len;	var->upper_margin	= mode->upper_margin;	var->lower_margin	= mode->lower_margin;	var->sync		= mode->sync;	var->grayscale		= mode->cmap_greyscale;	/* set the initial RGBA bitfields */	pxafb_set_pixfmt(var, mode->depth);}static int pxafb_adjust_timing(struct pxafb_info *fbi,			       struct fb_var_screeninfo *var){	int line_length;	var->xres = max_t(int, var->xres, MIN_XRES);	var->yres = max_t(int, var->yres, MIN_YRES);	if (!(fbi->lccr0 & LCCR0_LCDT)) {		clamp_val(var->hsync_len, 1, 64);		clamp_val(var->vsync_len, 1, 64);		clamp_val(var->left_margin,  1, 255);		clamp_val(var->right_margin, 1, 255);		clamp_val(var->upper_margin, 1, 255);		clamp_val(var->lower_margin, 1, 255);	}	/* make sure each line is aligned on word boundary */	line_length = var->xres * var->bits_per_pixel / 8;	line_length = ALIGN(line_length, 4);	var->xres = line_length * 8 / var->bits_per_pixel;	/* we don't support xpan, force xres_virtual to be equal to xres */	var->xres_virtual = var->xres;	if (var->accel_flags & FB_ACCELF_TEXT)		var->yres_virtual = fbi->fb.fix.smem_len / line_length;	else		var->yres_virtual = max(var->yres_virtual, var->yres);	/* check for limits */	if (var->xres > MAX_XRES || var->yres > MAX_YRES)		return -EINVAL;	if (var->yres > var->yres_virtual)		return -EINVAL;	return 0;}/* *  pxafb_check_var(): *    Get the video params out of 'var'. If a value doesn't fit, round it up, *    if it's too big, return -EINVAL. * *    Round up in the following order: bits_per_pixel, xres, *    yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, *    bitfields, horizontal timing, vertical timing. */static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info){	struct pxafb_info *fbi = (struct pxafb_info *)info;	struct pxafb_mach_info *inf = fbi->dev->platform_data;	int err;	if (inf->fixed_modes) {		struct pxafb_mode_info *mode;		mode = pxafb_getmode(inf, var);		if (!mode)			return -EINVAL;		pxafb_setmode(var, mode);	}	/* do a test conversion to BPP fields to check the color formats */	err = pxafb_var_to_bpp(var);	if (err < 0)		return err;	pxafb_set_pixfmt(var, var_to_depth(var));	err = pxafb_adjust_timing(fbi, var);	if (err)		return err;#ifdef CONFIG_CPU_FREQ	pr_debug("pxafb: dma period = %d ps\n",		 pxafb_display_dma_period(var));#endif	return 0;}/* * pxafb_set_par(): *	Set the user defined part of the display for the specified console */static int pxafb_set_par(struct fb_info *info){	struct pxafb_info *fbi = (struct pxafb_info *)info;	struct fb_var_screeninfo *var = &info->var;	if (var->bits_per_pixel >= 16)		fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR;	else if (!fbi->cmap_static)		fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;	else {		/*		 * Some people have weird ideas about wanting static		 * pseudocolor maps.  I suspect their user space		 * applications are broken.		 */		fbi->fb.fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;	}	fbi->fb.fix.line_length = var->xres_virtual *				  var->bits_per_pixel / 8;	if (var->bits_per_pixel >= 16)		fbi->palette_size = 0;	else		fbi->palette_size = var->bits_per_pixel == 1 ?					4 : 1 << var->bits_per_pixel;	fbi->palette_cpu = (u16 *)&fbi->dma_buff->palette[0];	if (fbi->fb.var.bits_per_pixel >= 16)		fb_dealloc_cmap(&fbi->fb.cmap);	else		fb_alloc_cmap(&fbi->fb.cmap, 1<<fbi->fb.var.bits_per_pixel, 0);	pxafb_activate_var(var, fbi);	return 0;}static int pxafb_pan_display(struct fb_var_screeninfo *var,			     struct fb_info *info){	struct pxafb_info *fbi = (struct pxafb_info *)info;	int dma = DMA_MAX + DMA_BASE;	if (fbi->state != C_ENABLE)		return 0;	setup_base_frame(fbi, 1);	if (fbi->lccr0 & LCCR0_SDS)		lcd_writel(fbi, FBR1, fbi->fdadr[dma + 1] | 0x1);	lcd_writel(fbi, FBR0, fbi->fdadr[dma] | 0x1);	return 0;}/* * pxafb_blank(): *	Blank the display by setting all palette values to zero.  Note, the * 	16 bpp mode does not really use the palette, so this will not *      blank the display in all modes. */static int pxafb_blank(int blank, struct fb_info *info){	struct pxafb_info *fbi = (struct pxafb_info *)info;	int i;	switch (blank) {	case FB_BLANK_POWERDOWN:	case FB_BLANK_VSYNC_SUSPEND:	case FB_BLANK_HSYNC_SUSPEND:	case FB_BLANK_NORMAL:		if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR ||

⌨️ 快捷键说明

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