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 + -
显示快捷键?