omapfb_main.c

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

C
1,875
字号
/* * File: drivers/video/omap/omapfb_main.c * * Framebuffer driver for TI OMAP boards * * Copyright (C) 2004 Nokia Corporation * Author: Imre Deak <imre.deak@nokia.com> * * Acknowledgements: *   Alex McMains <aam@ridgerun.com>       - Original driver *   Juha Yrjola <juha.yrjola@nokia.com>   - Original driver and improvements *   Dirk Behme <dirk.behme@de.bosch.com>  - changes for 2.6 kernel API *   Texas Instruments                     - H3 support * * 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/config.h>#include <linux/module.h>#include <linux/mm.h>#include <linux/init.h>#include <linux/fb.h>#include <linux/delay.h>#include <linux/device.h>#include <linux/dma-mapping.h>#include <asm/uaccess.h>#include <asm/atomic.h>#include <asm/mach-types.h>#include <asm/arch/dma.h>#include <asm/arch/irqs.h>#include <asm/arch/mux.h>#include <asm/arch/board.h>#include "omapfb.h"#include "lcdc.h"// #define OMAPFB_DBG_FIFO 1// #define OMAPFB_DBG 1#include "debug.h"#define COPY_MODE_REV_DIR	0x01#define COPY_MODE_TRANSPARENT	0x02#define COPY_MODE_IMAGE		0x04#ifdef OMAPFB_DBG_FIFOstruct gfx_stat {	unsigned long f_run[GFX_FIFO_SIZE];} stat;#endifstatic unsigned int	def_accel;static unsigned long	def_vram;static long		def_vxres;static long		def_vyres;static unsigned int	def_rotate;static unsigned int	def_mirror;#ifdef CONFIG_FB_OMAP_DELAY_ACTIVATIONstatic int		late_activate = 1;#elsestatic int		late_activate;#endif/* * --------------------------------------------------------------------------- * LCD panel * --------------------------------------------------------------------------- */extern struct lcd_panel h3_panel;extern struct lcd_panel h2_panel;extern struct lcd_panel osk_panel;extern struct lcd_panel innovator1610_panel;extern struct lcd_panel innovator1510_panel;static struct lcd_panel *panels[] = {#ifdef CONFIG_MACH_OMAP_H2	&h2_panel,#endif#ifdef CONFIG_MACH_OMAP_H3	&h3_panel,#endif#ifdef CONFIG_MACH_OMAP_OSK	&osk_panel,#endif#ifdef CONFIG_MACH_OMAP_INNOVATOR#ifdef CONFIG_ARCH_OMAP1510	&innovator1510_panel,#endif#ifdef CONFIG_ARCH_OMAP16XX	&innovator1610_panel,#endif#endif};extern struct lcd_ctrl omapfb_lcdc_ctrl;static struct lcd_ctrl *ctrls[] = {#ifdef CONFIG_FB_OMAP_INTERNAL_LCDC	&omapfb_lcdc_ctrl,#endif};/* * --------------------------------------------------------------------------- * gfx DMA * --------------------------------------------------------------------------- *//* Get a new logical channel from the gfx fifo. */static int inline gfxdma_get_lch(struct gfx_dma *gfx, int *lch){	int r = 0;	DBGENTER(3);	if (down_interruptible(&gfx->f_free)) {		r = -ERESTARTSYS;		goto exit;	}	spin_lock_bh(&gfx->spinlock);	*lch = gfx->f_tail->lch_num;	gfx->f_tail = gfx->f_tail->next;	spin_unlock_bh(&gfx->spinlock);exit:	DBGLEAVE(3);	return r;}/* Set basic transfer params for the logical channel */static inline void gfxdma_set_lch_params(int lch, int data_type,					 int enumber, int fnumber,				       	 unsigned long src_start, int src_amode,				         unsigned long dst_start, int dst_amode){	omap_set_dma_transfer_params(lch, data_type, enumber, fnumber, 0);	omap_set_dma_src_params(lch, OMAP_DMA_PORT_EMIFF,				src_amode, src_start);	omap_set_dma_dest_params(lch, OMAP_DMA_PORT_EMIFF,				 dst_amode, dst_start);}/* Set element and frame indexes for the logical channel, to support * image transformations */static inline void gfxdma_set_lch_index(int lch, int src_eidx, int src_fidx,					int dst_eidx, int dst_fidx){	omap_set_dma_src_index(lch, src_eidx, src_fidx);	omap_set_dma_dest_index(lch, dst_eidx, dst_fidx);}/* Set color parameter for the logical channel, to support constant fill and * transparent copy operations */static inline void gfxdma_set_lch_color(int lch, u32 color,					enum omap_dma_color_mode mode){	omap_set_dma_color_mode(lch, mode, color);}/* Start a new transfer consisting of a single DMA logical channel, * or a chain (f_run > 1). Can be called in interrupt context. * gfx->spinlock must be held. */static void inline gfxdma_start_chain(struct gfx_dma *gfx){	DBGENTER(3);	gfx->f_run = gfx->f_wait;#ifdef OMAPFB_DBG_FIFO	stat.f_run[gfx->f_run - 1]++;#endif	gfx->f_wait = 0;	omap_enable_dma_irq(gfx->f_chain_end->lch_num, OMAP_DMA_BLOCK_IRQ);	/* Let it go */	DBGPRINT(1, "start %d\n", gfx->f_head->lch_num);	omap_start_dma(gfx->f_head->lch_num);	gfx->f_chain_end = gfx->f_chain_end->next;	DBGLEAVE(3);}/* Enqueue a logical channel, that has been set up. If no other transfers * are pending start this new one right away. */static void inline gfxdma_enqueue(struct gfx_dma *gfx, int lch){	DBGENTER(3);	spin_lock_bh(&gfx->spinlock);	DBGPRINT(3, "run:%d wait:%d\n", gfx->f_run, gfx->f_wait);	if (gfx->f_wait) {		DBGPRINT(1, "link %d, %d\n", gfx->f_chain_end->lch_num, lch);		omap_dma_link_lch(gfx->f_chain_end->lch_num, lch);		gfx->f_chain_end = gfx->f_chain_end->next;	}	omap_disable_dma_irq(lch, OMAP_DMA_BLOCK_IRQ);	gfx->f_wait++;	if (!gfx->f_run)		gfxdma_start_chain(gfx);	spin_unlock_bh(&gfx->spinlock);	DBGLEAVE(3);}/* Called by DMA core when the last transfer ended, or there is an error * condition. We dispatch handling of the end of transfer case to a tasklet. * Called in interrupt context. */static void gfxdma_handler(int lch, u16 ch_status, void *data){	struct gfx_dma *gfx = (struct gfx_dma *)data;	int done = 0;	DBGENTER(3);	DBGPRINT(4, "lch=%d status=%#010x\n", lch, ch_status);	if (unlikely(ch_status & (OMAP_DMA_TOUT_IRQ | OMAP_DMA_DROP_IRQ))) {		PRNERR("gfx DMA error. status=%#010x\n", ch_status);		done = 1;	} else if (likely(ch_status & OMAP_DMA_BLOCK_IRQ))		done = 1;	if (likely(done)) {		gfx->done = 1;		tasklet_schedule(&gfx->dequeue_tasklet);	}	DBGLEAVE(3);}/* Let the DMA core know that the last transfer has ended. If there are * pending transfers in the fifo start them now. * Called in interrupt context. */static void gfxdma_dequeue_tasklet(unsigned long data){	struct gfx_dma *gfx = (struct gfx_dma *)data;	struct gfx_lchannel *f_chain;	DBGENTER(3);	/* start an already programmed transfer	 */	while (likely(gfx->done)) {		gfx->done = 0;		spin_lock(&gfx->spinlock);		f_chain = gfx->f_head;		omap_stop_dma(f_chain->lch_num);		/* Would be better w/o a loop.. */		while (gfx->f_run--) {			if (gfx->f_run)				omap_dma_unlink_lch(f_chain->lch_num,						    f_chain->next->lch_num);			f_chain = f_chain->next;			up(&gfx->f_free);		}		gfx->f_run = 0;		gfx->f_head = f_chain;		if (likely(gfx->f_wait))			gfxdma_start_chain(gfx);		else			complete(&gfx->sync_complete);		spin_unlock(&gfx->spinlock);	}	DBGLEAVE(3);}/* Wait till any pending transfers end. */static void gfxdma_sync(struct gfx_dma *gfx){	int wait = 0;	DBGENTER(1);	for (;;) {		spin_lock_bh(&gfx->spinlock);		if (gfx->f_run + gfx->f_wait) {			wait = 1;			init_completion(&gfx->sync_complete);		}		spin_unlock_bh(&gfx->spinlock);		if (wait) {			wait_for_completion(&gfx->sync_complete);			wait = 0;		} else			break;	}	DBGLEAVE(1);}/* Initialize the gfx DMA object. * Allocate DMA logical channels according to the fifo size. * Set the channel parameters that will be the same for all transfers. */static int gfxdma_init(struct gfx_dma *gfx){	int			r = 0;	int			i;	DBGENTER(1);	for (i = 0; i < GFX_FIFO_SIZE; i++) {		int next_idx;		int lch_num;		r = omap_request_dma(0, OMAPFB_DRIVER,				     gfxdma_handler, gfx, &lch_num);		if (r) {			int j;			PRNERR("unable to get GFX DMA %d\n", i);			for (j = 0; j < i; j++)				omap_free_dma(lch_num);			r = -1;			goto exit;		}		omap_set_dma_src_data_pack(lch_num, 1);		omap_set_dma_src_burst_mode(lch_num, OMAP_DMA_DATA_BURST_4);		omap_set_dma_dest_data_pack(lch_num, 1);		omap_set_dma_dest_burst_mode(lch_num, OMAP_DMA_DATA_BURST_4);		gfx->fifo[i].lch_num = lch_num;		next_idx = i < GFX_FIFO_SIZE - 1 ? i + 1 : 0;		gfx->fifo[next_idx].prev = &gfx->fifo[i];		gfx->fifo[i].next = &gfx->fifo[next_idx];	}	gfx->f_head = gfx->f_tail = gfx->f_chain_end = &gfx->fifo[0];	sema_init(&gfx->f_free, GFX_FIFO_SIZE);	spin_lock_init(&gfx->spinlock);	tasklet_init(&gfx->dequeue_tasklet, gfxdma_dequeue_tasklet,		     (unsigned long)gfx);	init_completion(&gfx->sync_complete);exit:	DBGLEAVE(1);	return r;}/* Clean up the gfx DMA object */static void gfxdma_cleanup(struct gfx_dma *gfx){	int i;	DBGENTER(1);	for (i = 0; i < GFX_FIFO_SIZE; i++)		omap_free_dma(gfx->fifo[i].lch_num);	DBGLEAVE(1);}/* * --------------------------------------------------------------------------- * LCD controller and LCD DMA * --------------------------------------------------------------------------- *//* Lookup table to map elem size to elem type. */static const int dma_elem_type[] = {	0,	OMAP_DMA_DATA_TYPE_S8,	OMAP_DMA_DATA_TYPE_S16,	0,	OMAP_DMA_DATA_TYPE_S32,};/* Allocate resources needed for LCD controller and LCD DMA operations. Video * memory is allocated from system memory according to the virtual display * size, except if a bigger memory size is specified explicitly as a kernel * parameter. */static int ctrl_init(struct omapfb_device *fbdev){	int size;	int r;	DBGENTER(1);	r = fbdev->ctrl->init(fbdev);	if (r < 0)		goto exit;	size = fbdev->lcddma_mem_size;		/* set by ctrl->init */	if (def_vram) {		if (fbdev->lcddma_mem_size > def_vram) {			PRNERR("specified frame buffer memory too small\n");			r = -ENOMEM;			goto cleanup_ctrl;		}		size = def_vram;	}	fbdev->lcddma_mem_size = PAGE_SIZE << get_order(size);	fbdev->lcddma_base = dma_alloc_writecombine(fbdev->dev, fbdev->lcddma_mem_size,						    &fbdev->lcddma_handle,						    PTE_BUFFERABLE);	if (fbdev->lcddma_base == NULL) {		PRNERR("unable to allocate fb DMA memory\n");		r = -ENOMEM;		goto cleanup_ctrl;	}	memset(fbdev->lcddma_base, 0, size);	DBGPRINT(1, "lcddma_base=%#10x lcddma_handle=%#10x lcddma_mem_size=%d"		 "palette_size=%d frame0_org=%d palette_org=%d\n",		 fbdev->lcddma_base, fbdev->lcddma_handle,		 fbdev->lcddma_mem_size,		 fbdev->palette_size,		 fbdev->frame0_org, fbdev->palette_org);	DBGLEAVE(1);	return 0;cleanup_ctrl:        fbdev->ctrl->cleanup(fbdev);exit:	DBGLEAVE(1);	return r;}static void ctrl_cleanup(struct omapfb_device *fbdev){	fbdev->ctrl->cleanup(fbdev);	dma_free_writecombine(fbdev->dev, fbdev->lcddma_mem_size,			      fbdev->lcddma_base, fbdev->lcddma_handle);}static void ctrl_change_mode(struct omapfb_device *fbdev){	if (fbdev->state != OMAPFB_DEACTIVATED)		fbdev->ctrl->change_mode(fbdev);}/* * --------------------------------------------------------------------------- * fbdev framework callbacks and the ioctl interface * --------------------------------------------------------------------------- *//* Called each time the omapfb device is opened */static int omapfb_open(struct fb_info *info, int user){	DBGENTER(1);#ifdef OMAPFB_DBG_FIFO	memset(&stat, 0, sizeof(stat));#endif

⌨️ 快捷键说明

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