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