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

📄 lcdc.c

📁 Linux环境下视频显示卡设备的驱动程序源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * OMAP1 internal LCD controller * * Copyright (C) 2004 Nokia Corporation * Author: Imre Deak <imre.deak@nokia.com> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. */#include <linux/module.h>#include <linux/device.h>#include <linux/interrupt.h>#include <linux/spinlock.h>#include <linux/err.h>#include <linux/mm.h>#include <linux/fb.h>#include <linux/dma-mapping.h>#include <linux/vmalloc.h>#include <linux/clk.h>#include <mach/dma.h>#include <mach/omapfb.h>#include <asm/mach-types.h>#include "lcdc.h"#define MODULE_NAME			"lcdc"#define OMAP_LCDC_BASE			0xfffec000#define OMAP_LCDC_SIZE			256#define OMAP_LCDC_IRQ			INT_LCD_CTRL#define OMAP_LCDC_CONTROL		(OMAP_LCDC_BASE + 0x00)#define OMAP_LCDC_TIMING0		(OMAP_LCDC_BASE + 0x04)#define OMAP_LCDC_TIMING1		(OMAP_LCDC_BASE + 0x08)#define OMAP_LCDC_TIMING2		(OMAP_LCDC_BASE + 0x0c)#define OMAP_LCDC_STATUS		(OMAP_LCDC_BASE + 0x10)#define OMAP_LCDC_SUBPANEL		(OMAP_LCDC_BASE + 0x14)#define OMAP_LCDC_LINE_INT		(OMAP_LCDC_BASE + 0x18)#define OMAP_LCDC_DISPLAY_STATUS	(OMAP_LCDC_BASE + 0x1c)#define OMAP_LCDC_STAT_DONE		(1 << 0)#define OMAP_LCDC_STAT_VSYNC		(1 << 1)#define OMAP_LCDC_STAT_SYNC_LOST	(1 << 2)#define OMAP_LCDC_STAT_ABC		(1 << 3)#define OMAP_LCDC_STAT_LINE_INT		(1 << 4)#define OMAP_LCDC_STAT_FUF		(1 << 5)#define OMAP_LCDC_STAT_LOADED_PALETTE	(1 << 6)#define OMAP_LCDC_CTRL_LCD_EN		(1 << 0)#define OMAP_LCDC_CTRL_LCD_TFT		(1 << 7)#define OMAP_LCDC_CTRL_LINE_IRQ_CLR_SEL	(1 << 10)#define OMAP_LCDC_IRQ_VSYNC		(1 << 2)#define OMAP_LCDC_IRQ_DONE		(1 << 3)#define OMAP_LCDC_IRQ_LOADED_PALETTE	(1 << 4)#define OMAP_LCDC_IRQ_LINE_NIRQ		(1 << 5)#define OMAP_LCDC_IRQ_LINE		(1 << 6)#define OMAP_LCDC_IRQ_MASK		(((1 << 5) - 1) << 2)#define MAX_PALETTE_SIZE		PAGE_SIZEenum lcdc_load_mode {	OMAP_LCDC_LOAD_PALETTE,	OMAP_LCDC_LOAD_FRAME,	OMAP_LCDC_LOAD_PALETTE_AND_FRAME};static struct omap_lcd_controller {	enum omapfb_update_mode	update_mode;	int			ext_mode;	unsigned long		frame_offset;	int			screen_width;	int			xres;	int			yres;	enum omapfb_color_format	color_mode;	int			bpp;	void			*palette_virt;	dma_addr_t		palette_phys;	int			palette_code;	int			palette_size;	unsigned int		irq_mask;	struct completion	last_frame_complete;	struct completion	palette_load_complete;	struct clk		*lcd_ck;	struct omapfb_device	*fbdev;	void			(*dma_callback)(void *data);	void			*dma_callback_data;	int			fbmem_allocated;	dma_addr_t		vram_phys;	void			*vram_virt;	unsigned long		vram_size;} lcdc;static void inline enable_irqs(int mask){	lcdc.irq_mask |= mask;}static void inline disable_irqs(int mask){	lcdc.irq_mask &= ~mask;}static void set_load_mode(enum lcdc_load_mode mode){	u32 l;	l = omap_readl(OMAP_LCDC_CONTROL);	l &= ~(3 << 20);	switch (mode) {	case OMAP_LCDC_LOAD_PALETTE:		l |= 1 << 20;		break;	case OMAP_LCDC_LOAD_FRAME:		l |= 2 << 20;		break;	case OMAP_LCDC_LOAD_PALETTE_AND_FRAME:		break;	default:		BUG();	}	omap_writel(l, OMAP_LCDC_CONTROL);}static void enable_controller(void){	u32 l;	l = omap_readl(OMAP_LCDC_CONTROL);	l |= OMAP_LCDC_CTRL_LCD_EN;	l &= ~OMAP_LCDC_IRQ_MASK;	l |= lcdc.irq_mask | OMAP_LCDC_IRQ_DONE;	/* enabled IRQs */	omap_writel(l, OMAP_LCDC_CONTROL);}static void disable_controller_async(void){	u32 l;	u32 mask;	l = omap_readl(OMAP_LCDC_CONTROL);	mask = OMAP_LCDC_CTRL_LCD_EN | OMAP_LCDC_IRQ_MASK;	/*	 * Preserve the DONE mask, since we still want to get the	 * final DONE irq. It will be disabled in the IRQ handler.	 */	mask &= ~OMAP_LCDC_IRQ_DONE;	l &= ~mask;	omap_writel(l, OMAP_LCDC_CONTROL);}static void disable_controller(void){	init_completion(&lcdc.last_frame_complete);	disable_controller_async();	if (!wait_for_completion_timeout(&lcdc.last_frame_complete,				msecs_to_jiffies(500)))		dev_err(lcdc.fbdev->dev, "timeout waiting for FRAME DONE\n");}static void reset_controller(u32 status){	static unsigned long reset_count;	static unsigned long last_jiffies;	disable_controller_async();	reset_count++;	if (reset_count == 1 || time_after(jiffies, last_jiffies + HZ)) {		dev_err(lcdc.fbdev->dev,			  "resetting (status %#010x,reset count %lu)\n",			  status, reset_count);		last_jiffies = jiffies;	}	if (reset_count < 100) {		enable_controller();	} else {		reset_count = 0;		dev_err(lcdc.fbdev->dev,			"too many reset attempts, giving up.\n");	}}/* * Configure the LCD DMA according to the current mode specified by parameters * in lcdc.fbdev and fbdev->var. */static void setup_lcd_dma(void){	static const int dma_elem_type[] = {		0,		OMAP_DMA_DATA_TYPE_S8,		OMAP_DMA_DATA_TYPE_S16,		0,		OMAP_DMA_DATA_TYPE_S32,	};	struct omapfb_plane_struct *plane = lcdc.fbdev->fb_info[0]->par;	struct fb_var_screeninfo *var = &lcdc.fbdev->fb_info[0]->var;	unsigned long	src;	int		esize, xelem, yelem;	src = lcdc.vram_phys + lcdc.frame_offset;	switch (var->rotate) {	case 0:		if (plane->info.mirror || (src & 3) ||		    lcdc.color_mode == OMAPFB_COLOR_YUV420 ||		    (lcdc.xres & 1))			esize = 2;		else			esize = 4;		xelem = lcdc.xres * lcdc.bpp / 8 / esize;		yelem = lcdc.yres;		break;	case 90:	case 180:	case 270:		if (cpu_is_omap15xx()) {			BUG();		}		esize = 2;		xelem = lcdc.yres * lcdc.bpp / 16;		yelem = lcdc.xres;		break;	default:		BUG();		return;	}#ifdef VERBOSE	dev_dbg(lcdc.fbdev->dev,		 "setup_dma: src %#010lx esize %d xelem %d yelem %d\n",		 src, esize, xelem, yelem);#endif	omap_set_lcd_dma_b1(src, xelem, yelem, dma_elem_type[esize]);	if (!cpu_is_omap15xx()) {		int bpp = lcdc.bpp;		/*		 * YUV support is only for external mode when we have the		 * YUV window embedded in a 16bpp frame buffer.		 */		if (lcdc.color_mode == OMAPFB_COLOR_YUV420)			bpp = 16;		/* Set virtual xres elem size */		omap_set_lcd_dma_b1_vxres(			lcdc.screen_width * bpp / 8 / esize);		/* Setup transformations */		omap_set_lcd_dma_b1_rotation(var->rotate);		omap_set_lcd_dma_b1_mirror(plane->info.mirror);	}	omap_setup_lcd_dma();}static irqreturn_t lcdc_irq_handler(int irq, void *dev_id){	u32 status;	status = omap_readl(OMAP_LCDC_STATUS);	if (status & (OMAP_LCDC_STAT_FUF | OMAP_LCDC_STAT_SYNC_LOST))		reset_controller(status);	else {		if (status & OMAP_LCDC_STAT_DONE) {			u32 l;			/*			 * Disable IRQ_DONE. The status bit will be cleared			 * only when the controller is reenabled and we don't			 * want to get more interrupts.			 */			l = omap_readl(OMAP_LCDC_CONTROL);			l &= ~OMAP_LCDC_IRQ_DONE;			omap_writel(l, OMAP_LCDC_CONTROL);			complete(&lcdc.last_frame_complete);		}		if (status & OMAP_LCDC_STAT_LOADED_PALETTE) {			disable_controller_async();			complete(&lcdc.palette_load_complete);		}	}	/*	 * Clear these interrupt status bits.	 * Sync_lost, FUF bits were cleared by disabling the LCD controller	 * LOADED_PALETTE can be cleared this way only in palette only	 * load mode. In other load modes it's cleared by disabling the	 * controller.	 */	status &= ~(OMAP_LCDC_STAT_VSYNC |		    OMAP_LCDC_STAT_LOADED_PALETTE |		    OMAP_LCDC_STAT_ABC |		    OMAP_LCDC_STAT_LINE_INT);	omap_writel(status, OMAP_LCDC_STATUS);	return IRQ_HANDLED;}/* * Change to a new video mode. We defer this to a later time to avoid any * flicker and not to mess up the current LCD DMA context. For this we disable * the LCD controller, which will generate a DONE irq after the last frame has * been transferred. Then it'll be safe to reconfigure both the LCD controller * as well as the LCD DMA. */static int omap_lcdc_setup_plane(int plane, int channel_out,				 unsigned long offset, int screen_width,				 int pos_x, int pos_y, int width, int height,				 int color_mode){	struct fb_var_screeninfo *var = &lcdc.fbdev->fb_info[0]->var;	struct lcd_panel *panel = lcdc.fbdev->panel;	int rot_x, rot_y;	if (var->rotate == 0) {		rot_x = panel->x_res;		rot_y = panel->y_res;	} else {		rot_x = panel->y_res;		rot_y = panel->x_res;	}	if (plane != 0 || channel_out != 0 || pos_x != 0 || pos_y != 0 ||	    width > rot_x || height > rot_y) {#ifdef VERBOSE		dev_dbg(lcdc.fbdev->dev,			"invalid plane params plane %d pos_x %d pos_y %d "			"w %d h %d\n", plane, pos_x, pos_y, width, height);#endif		return -EINVAL;	}	lcdc.frame_offset = offset;	lcdc.xres = width;	lcdc.yres = height;	lcdc.screen_width = screen_width;	lcdc.color_mode = color_mode;	switch (color_mode) {	case OMAPFB_COLOR_CLUT_8BPP:		lcdc.bpp = 8;		lcdc.palette_code = 0x3000;		lcdc.palette_size = 512;		break;	case OMAPFB_COLOR_RGB565:		lcdc.bpp = 16;		lcdc.palette_code = 0x4000;		lcdc.palette_size = 32;		break;	case OMAPFB_COLOR_RGB444:		lcdc.bpp = 16;		lcdc.palette_code = 0x4000;		lcdc.palette_size = 32;		break;	case OMAPFB_COLOR_YUV420:		if (lcdc.ext_mode) {			lcdc.bpp = 12;			break;		}		/* fallthrough */	case OMAPFB_COLOR_YUV422:		if (lcdc.ext_mode) {			lcdc.bpp = 16;			break;		}		/* fallthrough */	default:		/* FIXME: other BPPs.		 * bpp1: code  0,     size 256		 * bpp2: code  0x1000 size 256		 * bpp4: code  0x2000 size 256		 * bpp12: code 0x4000 size 32		 */		dev_dbg(lcdc.fbdev->dev, "invalid color mode %d\n", color_mode);		BUG();		return -1;	}	if (lcdc.ext_mode) {		setup_lcd_dma();		return 0;	}	if (lcdc.update_mode == OMAPFB_AUTO_UPDATE) {		disable_controller();		omap_stop_lcd_dma();		setup_lcd_dma();		enable_controller();	}	return 0;}static int omap_lcdc_enable_plane(int plane, int enable){	dev_dbg(lcdc.fbdev->dev,		"plane %d enable %d update_mode %d ext_mode %d\n",		plane, enable, lcdc.update_mode, lcdc.ext_mode);	if (plane != OMAPFB_PLANE_GFX)		return -EINVAL;	return 0;}/* * Configure the LCD DMA for a palette load operation and do the palette * downloading synchronously. We don't use the frame+palette load mode of * the controller, since the palette can always be downloaded seperately. */static void load_palette(void){	u16	*palette;	palette = (u16 *)lcdc.palette_virt;	*(u16 *)palette &= 0x0fff;	*(u16 *)palette |= lcdc.palette_code;	omap_set_lcd_dma_b1(lcdc.palette_phys,		lcdc.palette_size / 4 + 1, 1, OMAP_DMA_DATA_TYPE_S32);	omap_set_lcd_dma_single_transfer(1);	omap_setup_lcd_dma();	init_completion(&lcdc.palette_load_complete);	enable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE);	set_load_mode(OMAP_LCDC_LOAD_PALETTE);	enable_controller();	if (!wait_for_completion_timeout(&lcdc.palette_load_complete,				msecs_to_jiffies(500)))		dev_err(lcdc.fbdev->dev, "timeout waiting for FRAME DONE\n");	/* The controller gets disabled in the irq handler */	disable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE);	omap_stop_lcd_dma();

⌨️ 快捷键说明

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