omap24xxfb.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,697 行 · 第 1/4 页

C
1,697
字号
/* * drivers/video/omap24xxfb.c * * Framebuffer driver for OMAP24xx display controller. * * Author: Andy Lowe (source@mvista.com) * * Copyright (C) 2004 MontaVista Software, Inc. * Copyright (C) 2004 Texas Instruments. * * This file is licensed under the terms of the GNU General Public License  * version 2. This program is licensed "as is" without any warranty of any  * kind, whether express or implied. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/tty.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/fb.h>#include <linux/ioport.h>#include <linux/types.h>#include <linux/dma-mapping.h>#include <linux/init.h>#include <linux/smp_lock.h>#include <linux/interrupt.h>#include <asm/irq.h>#include "omap24xxfb.h"/******************************************************************************//* Board-specific customization for the framebuffer driver. * This stuff needs to be moved to board-specific files. */#define CONFIG_H4#ifdef CONFIG_H4//#define H4_CLK_PERIOD (20000)  /* f_pico(1/50MHz)=20000 for H4 at present */#define H4_CLK_PERIOD (26667)  /* f_pico(1/37.5MHz)=26667 for H4 at present */#define H4_XRES 240#define H4_YRES 320/* omap24xxfb_gfx_clk_period() must return the period (in picoseconds) of the  * graphics timebase clock.  This clock is the input to the pixel clock  * divider. * * prototype: * unsigned long omap24xxfb_gfx_clk_period(void); */#define omap24xxfb_gfx_clk_period() H4_CLK_PERIOD/* omap24xxfb_fb_base() must return the physical base address of the  * framebuffer.  If the address is non-zero, then the framebuffer memory is  * ioremap'd.  If the address is zero, then the framebuffer memory is  * dynamically allocated by the driver. * * prototype: * unsigned long omap24xxfb_fb_base(void); */#define omap24xxfb_fb_base() 0/* omap24xxfb_fb_size() must return the size of the framebuffer in bytes. * The framebuffer is only ioremap'd (or kmalloc'd) at driver initialization  * time.  It does not change when the video mode (resolution and color depth)  * changes, so it must be large enough for all video modes that are to be  * supported. * * We're allocating a framebuffer 3 times the size of the physical display.   * This is to support triple buffering.  The panning ioctl can be used to  * switch between the three different framebuffer regions, so you effectively  * have an onscreen framebuffer and two offscreen framebuffers. * * prototype: * unsigned long omap24xxfb_fb_size(void); */#define omap24xxfb_fb_size() (H4_XRES*H4_YRES*(16/8)*3) /* 450KB *//* omap24xxfb_display_xres() must return the horizontal resolution of the  * display. * * prototype: * unsigned int omap24xxfb_display_xres(void); */#define omap24xxfb_display_xres() (H4_XRES)/* omap24xxfb_display_yres() must return the vertical resolution of the  * display. * * prototype: * unsigned int omap24xxfb_display_yres(void); */#define omap24xxfb_display_yres() (H4_YRES)/* omap24xxfb_default_var() must return a pointer to a default  * fb_var_screeninfo struct that will be used to set the initial video mode.   * If this video mode is invalid (as determined by omap24xxfb_check_var) then  * the driver will fail to initialize. * * We're intializing the virtual framebuffer dimensions to  * (H4_XRES, H4_YRES*3) in order to support triple buffering.  The  * onscreen framebuffer can be flipped via the panning ioctl by specifying  * offsets of (0, 0), (0, H4_YRES), or (0, 2*H4_YRES). * * prototype: * struct fb_var_screeninfo *omap24xxfb_default_var(void); */static struct fb_var_screeninfo aptix_h4_var = {	.xres		= H4_XRES,	.yres		= H4_YRES,	.xres_virtual	= H4_XRES,	.yres_virtual	= H4_YRES*3,	.xoffset	= 0,	.yoffset	= 0,	.bits_per_pixel	= 16,	.grayscale	= 0,	.red		= {11, 5, 0},	.green		= { 5, 6, 0},	.blue		= { 0, 5, 0},	.transp		= { 0, 0, 0},	.nonstd		= 0,	.activate	= 0,	.height		= -1,	.width		= -1,	.accel_flags	= 0,	.pixclock	= H4_CLK_PERIOD*8, /* picoseconds */	.left_margin	= 40,		/* pixclocks */	.right_margin	= 4,		/* pixclocks */	.upper_margin	= 8,		/* line clocks */	.lower_margin	= 2,		/* line clocks */	.hsync_len	= 4,		/* pixclocks */	.vsync_len	= 2,		/* line clocks */	.sync		= 0,	.vmode		= FB_VMODE_NONINTERLACED,	.rotate		= 0,};#define omap24xxfb_default_var() (&aptix_h4_var)/* omap24xxfb_check_mode() must check the video mode specified in a  * fb_var_screeninfo struct and return 0 if the mode is supported and non-zero  * if the mode is not supported.  omap24xxfb_check_mode() is permitted to  * modify the var to make it conform to a supported mode. * * prototype: * int omap24xxfb_check_mode(struct fb_var_screeninfo *var); */int omap24xxfb_check_mode(struct fb_var_screeninfo *var){	if ((var->xres > H4_XRES) || (var->yres > H4_YRES))		return -EINVAL;	if (var->bits_per_pixel > 16)		return -EINVAL;	return 0;}/* omap24xxfb_change_mode() must make any changes to a shadow copy of the  * display controller registers that are required to change to the new video  * mode specified in var.  The values in var must not be modified. * * prototype: * void omap24xxfb_change_mode(const struct fb_var_screeninfo *var,  * 			       struct omap24xx_dispc_regs *dispc); */void omap24xxfb_change_mode(const struct fb_var_screeninfo *var, 			    struct omap24xx_dispc_regs *dispc){	dispc->sysconfig = DISPC_SYSCONFIG_MIDLEMODE_NSTANDBY			 | DISPC_SYSCONFIG_SIDLEMODE_NIDLE;	dispc->control = DISPC_CONTROL_GPOUT1 | DISPC_CONTROL_GPOUT0 			| DISPC_CONTROL_TFTDATALINES_OALSB16B 			| DISPC_CONTROL_STNTFT 			| DISPC_CONTROL_LCDENABLE;	dispc->config = 0;	dispc->gfx_attributes = DISPC_GFX_ATTRIBUTES_GFXBURSTSIZE_BURST8X32 			      | DISPC_GFX_ATTRIBUTES_ENABLE;	dispc->pol_freq = 0;	dispc->gfx_fifo_threshold = (252 << DISPC_GFX_FIFO_THRESHOLD_HIGH_SHIFT)				  | (192 << DISPC_GFX_FIFO_THRESHOLD_LOW_SHIFT);	dispc->gfx_position = 0;	dispc->gfx_window_skip = 0;	return;}#endif /* ifdef CONFIG_H4 *//******************************************************************************/#define FB_NAME "omap24xxfb"		/* 16 chars max */struct omap24xxfb_info {	/* The fb_info structure must be first! */	struct fb_info info;	/* horizontal and vertical resolution of display */	unsigned int display_xres;	unsigned int display_yres;	struct omap24xx_dispc_regs state;		dma_addr_t mmio_base_phys;	dma_addr_t fb_base_phys;	unsigned long fb_size;	unsigned long mmio_base;	unsigned long fb_base;	wait_queue_head_t vsync_wait;	unsigned long vsync_cnt;	u32 pseudo_palette[17];	u32 *palette;	dma_addr_t palette_phys;	/* Period of the graphics clock in picoseconds.	 * This is is the input to the pixel clock divider.	 */	unsigned long gfx_clk_period;		unsigned int hsync;	/* horizontal sync frequency in Hz */	unsigned int vsync;	/* vertical sync frequency in Hz */	unsigned long timeout;	/* register update timeout period in ticks */	int alloc_fb_mem;		int asleep;};static struct omap24xxfb_info *saved_oinfo;/* * display controller register I/O routines */static __inline__ u32 dispc_reg_in(const struct omap24xxfb_info *oinfo, u32 offset){	return readl(oinfo->mmio_base + DISPC_REG_OFFSET + offset);}static __inline__ u32 dispc_reg_out(const struct omap24xxfb_info *oinfo, u32 offset, u32 val){	writel(val, oinfo->mmio_base + DISPC_REG_OFFSET + offset);	return val;}static __inline__ u32 dispc_reg_merge(const struct omap24xxfb_info *oinfo, u32 offset, 		u32 val, u32 mask){	u32 addr = oinfo->mmio_base + DISPC_REG_OFFSET + offset;	u32 new_val = (readl(addr) & ~mask) | (val & mask);		writel(new_val, addr);	return new_val;}/* Bits-per-pixel and color depth aren't quite the same thing.  The OMAP24xx  * display controller supports color depths of 1, 2, 4, 8, 12, 16, and 24 bits. * Color depth and bits-per-pixel are the same for depths of 1, 2, 4, 8, and  * 16 bits, but for a color depth of 12 bits the pixel data is padded to  * 16 bits-per-pixel, and for a color depth of 24 bits the pixel data is padded  * to 32 bits-per-pixel. */static inline int var_to_depth(const struct fb_var_screeninfo *var){	switch (var->bits_per_pixel) {		case 1:		case 2:		case 4:		case 8:		default:			return var->bits_per_pixel;		case 16:			if ((var->red.length + var->green.length 				+ var->blue.length) == 12)			{				return 12;			}			else				return 16;		case 32:			return 24;	}}/* Calculate the number of pixels sent to the display per pixel clock as  * (nom/den) pixels per clock. */static void pixels_per_clock(const struct omap24xx_dispc_regs *dispc, 			unsigned int *nom, unsigned int *den){	if (dispc->control & DISPC_CONTROL_STNTFT) {		/* active display (TFT) */		if (dispc->control & DISPC_CONTROL_TDMENABLE) {			/* TFT with TDM */			switch (dispc->control & DISPC_CONTROL_TDMCYCLEFORMAT) {				case DISPC_CONTROL_TDMCYCLEFORMAT_1CYCPERPIX:					*nom = 1;					*den = 1;					break;				case DISPC_CONTROL_TDMCYCLEFORMAT_2CYCPERPIX:					*nom = 1;					*den = 2;					break;				case DISPC_CONTROL_TDMCYCLEFORMAT_3CYCPERPIX:					*nom = 1;					*den = 3;					break;				case DISPC_CONTROL_TDMCYCLEFORMAT_3CYCPER2PIX:					*nom = 2;					*den = 3;					break;			}		}		else {			/* TFT without TDM */			*nom = 1;			*den = 1;		}	}	else {		/* passive display (STN) */		if (dispc->control & DISPC_CONTROL_MONOCOLOR) {			/* STN mono */			if (dispc->control & DISPC_CONTROL_M8B) {				/* 8 pixels per pixclock */				*nom = 8;				*den = 1;			}			else {				/* 4 pixels per pixclock */				*nom = 4;				*den = 1;			}		}		else {			/* STN color--8 pixels per 3 pixclocks */			*nom = 8;			*den = 3;		}	}}/* Calculate the horizontal sync frequency in Hertz  * with a resolution of 250Hz. */static unsigned inthorizontal_sync_freq(const struct omap24xx_dispc_regs *dispc, 			const struct fb_var_screeninfo *var){	unsigned int hsf, hs, nom, den;	unsigned int xres;	/* Calculate the number of pixels per clock. */	pixels_per_clock(dispc, &nom, &den);	/* get the horizontal display resolution */	xres = omap24xxfb_display_xres();	hs = (xres*den)/nom	 	+ (var->left_margin + var->right_margin + var->hsync_len);	if ((var->pixclock > 0) && (hs > 0))		hsf = (4000000000UL/(var->pixclock*hs))*250;	else		hsf = 0;	return hsf;}/* Calculate the vertical sync frequency in Hertz. */static unsigned intvertical_sync_freq(const struct omap24xx_dispc_regs *dispc, 			const struct fb_var_screeninfo *var){	unsigned int vsf, vs;	unsigned int yres;	/* get the vertical display resolution */	yres = omap24xxfb_display_yres();	vs = yres + var->upper_margin + var->lower_margin + var->vsync_len;	if (vs > 0)		vsf = horizontal_sync_freq(dispc, var)/vs;	else		vsf = 0;	return vsf;}/* Wait with a timeout for both of the GO bits in the control register to  * be cleared.  Returns 0 if the GO bits are clear on exit, or non-zero if  * the timeout occurs before the GO bits are clear. */static u32wait_for_go(const struct omap24xxfb_info *oinfo, unsigned long timeout_ticks){	unsigned long timeout = jiffies + timeout_ticks;	u32 mask = 0;	u32 ctrl;		ctrl = dispc_reg_in(oinfo, DISPC_CONTROL);	if (ctrl & DISPC_CONTROL_DIGITALENABLE)		mask |= DISPC_CONTROL_GODIGITAL;	if (ctrl & DISPC_CONTROL_LCDENABLE)		mask |= DISPC_CONTROL_GOLCD;	while((ctrl & mask) && time_before(jiffies, timeout)) {		if (!in_atomic()) {			set_current_state(TASK_INTERRUPTIBLE);			schedule_timeout(1);		} else			udelay(100);		ctrl = dispc_reg_in(oinfo, DISPC_CONTROL);	}	if (ctrl & mask) {		printk(KERN_WARNING FB_NAME 			": timeout waiting for display controller update\n");	}

⌨️ 快捷键说明

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