📄 omap_fb.c
字号:
/* * drivers/video/omap24xxfb.c * * Framebuffer driver for OMAP24xx display controller. * * Copyright (C) 2004-2005-2006 Texas Instruments, Inc. * * Author: Andy Lowe (source@mvista.com) * Copyright (C) 2004 MontaVista Software, Inc. * * 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 <linux/console.h>#include <linux/platform_device.h>#include <asm/irq.h>#include <asm/arch/clock.h>#include <asm/uaccess.h>#define PM_DEBUG 1#include <linux/notifier.h>#include <linux/pm.h>#include <asm/arch/display.h>#include "omap_fb.h"#undef DEBUG#define DBGENTER#define DBGLEAVE#define DBGENTER_c#define OMAPFB_DEVICE "omap24xxfb"#define OMAPFB_DRIVER "omap24xxfb"#define FB_NAME "omap24xxfb" /* 16 chars max */#define SCHEDULE_WAIT 0#define BUSY_WAIT 1/* To use the rotation feature, include this in your boot params: video=omap24xxfb:rotation=[0|90|180|270]*/int omap24xxfb_rotation = -1; // -1 = no rotation supportint omap24xxfb_mirroring = 0; // the status of mirroring#define omap_rotation_index(rotation_deg) \ (rotation_deg == 90)?(270/90): \ (rotation_deg == 270)?(90/90): \ (rotation_deg == 180)?(180/90): \ (0/90)#define omap_rot_mirror_index(rotation_deg) \ (rotation_deg == 90)?(90/90): \ (rotation_deg == 270)?(270/90): \ (rotation_deg == 180)?(0/90): \ (180/90)struct omap24xxfb_info { /* The fb_info structure must be first! */ struct fb_info info; 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; int blanked; int rotation_support; int rotation_deg; dma_addr_t sms_rot_phy[4]; unsigned long sms_rot_virt[4]; unsigned long vrfb_size;};static struct omap24xxfb_info *saved_oinfo;static struct fb_var_screeninfo default_var;int fb_out_layer = OMAP2_GRAPHICS;extern void get_panel_default_var(struct fb_var_screeninfo *var, int output_dev);extern u32 get_panel_pixclock_max(int output_dev);extern u32 get_panel_pixclock_min(int output_dev);extern void enable_backlight(void);extern void disable_backlight(void);int omap24xx_get_dss1_clock(void);extern int omap24xxfb_set_output_layer(int layer);/******************************************************************************//* Platform-specific customization for the framebuffer driver. *//* 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() (1000000000UL/(omap24xx_get_dss1_clock()/1000))/* 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. * * In non-rotation mode, we're allocating a framebuffer 2 times the size of * the physical display. This is to support double buffering. The panning ioctl * can be used to switch between the two different framebuffer regions, so * you effectively have an onscreen framebuffer and two offscreen framebuffers. * * In rotation mode vrfb line length is fixed that is 2048 pixels. So * allocating buffer size of 2048 * 640 * 4. 640 is max y resolution and * 4 is for 32bpps mode. Panning is not supported with rotation so * allocating only 1 buffer. * prototype: * unsigned long omap24xxfb_fb_size(void); */#define omap24xxfb_fb_size(rotation) \ rotation ? (2048 * 640 * (32/8)) : (720 * 576 * (32/8) * 2) /* 720 x 576 is the max framebuffer size we allow for TV (PAL) *//* omap24xxfb_vrfb_size() must return the size of the virtual rotated * framebuffer. * * prototype: * unsigned long omap24xxfb_fb_size(void); */#define omap24xxfb_vrfb_size() (MAX_PIXELS_PER_LINE * 640 * (32/8))/* omap24xx_display_pixclock_max() must return the maximum pixclock period * supported by the display. * * prototype: * unsigned int omap24xx_display_pixclock_max(void); */#define omap24xx_display_pixclock_max(ouput_dev) \ (get_panel_pixclock_max(output_dev))/* omap24xx_display_pixclock_min() must return the minimum pixclock period * supported by the display. * * prototype: * unsigned int omap24xx_display_pixclock_min(void); */#define omap24xx_display_pixclock_min(output_dev) \ (get_panel_pixclock_min(output_dev))/* 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. * * prototype: * struct fb_var_screeninfo *omap24xxfb_default_var(void); */static struct fb_var_screeninfo *omap24xxfb_default_var(void){ struct fb_var_screeninfo *v = &default_var; int output_dev = omap2_disp_get_output_dev(OMAP2_GRAPHICS); u32 tmp; get_panel_default_var(v, output_dev); if (omap24xxfb_rotation >= 0) { v->xres_virtual = v->yres_virtual = max(v->xres, v->yres); switch(omap24xxfb_rotation) { case 0: default: v->xoffset = 0; v->yoffset = 0; v->rotate = 0; break; case 90: tmp = v->xres, v->xres = v->yres, v->yres = tmp; v->xoffset = 0; v->yoffset = 0; v->rotate = 90; break; case 180: v->xoffset = 0; v->yoffset = 0; v->rotate = 180; break; case 270: tmp = v->xres, v->xres = v->yres, v->yres = tmp; v->xoffset = 0; v->yoffset = 0; v->rotate = 270; break; } } return v;}/* 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(const struct omap24xxfb_info *oinfo, * struct fb_var_screeninfo *var); */static intomap24xxfb_check_mode(const struct omap24xxfb_info *oinfo, struct fb_var_screeninfo *var){ u32 pixclock, clkdiv; u32 display_xres, display_yres; int output_dev; omap2_disp_get_dss(); output_dev = omap2_disp_get_output_dev(OMAP2_GRAPHICS); omap2_disp_get_panel_size(output_dev, &display_xres, &display_yres); if (oinfo->rotation_support) { if (var->rotate % 90 != 0) { omap2_disp_put_dss(); return -EINVAL; } if (!((var->bits_per_pixel == 8) || (var->bits_per_pixel == 16)|| (var->bits_per_pixel == 32))) { omap2_disp_put_dss(); return -EINVAL; } switch (var->rotate) { case 0: case 180: default: if ((var->xres > display_xres) || (var->yres > display_yres)) { omap2_disp_put_dss(); return -EINVAL; } break; case 90: case 270: if ((var->xres > display_yres) || (var->yres > display_xres)) { omap2_disp_put_dss(); return -EINVAL; } break; } } else { if ((var->xres > display_xres) || (var->yres > display_yres)) { omap2_disp_put_dss(); return -EINVAL; } } pixclock = (var->pixclock > 0) ? var->pixclock : omap24xx_display_pixclock_max(output_dev); if (pixclock < omap24xx_display_pixclock_min(output_dev)){ omap2_disp_put_dss(); return -EINVAL; } clkdiv = pixclock / oinfo->gfx_clk_period; pixclock = oinfo->gfx_clk_period * clkdiv; if (pixclock > omap24xx_display_pixclock_max(output_dev)) { omap2_disp_put_dss(); return -EINVAL; } /* due to round-down error in division, the pixclock may fall below the lower threshold of the panel. Fix that by adding 1 to clkdiv. */ if (pixclock < omap24xx_display_pixclock_min(output_dev)) clkdiv = clkdiv + 1; if (clkdiv < 2) /* minimum divisor is 2 */ clkdiv = 2; else if (clkdiv > 255) clkdiv = 255; /* maximum divisor is 255 */ /* recalculate pixclock and change the var structure */ pixclock = oinfo->gfx_clk_period * clkdiv; omap2_disp_put_dss(); return 0;}struct omap24xxfb_suspend_data { /* Power management suspend lockout stuff */ int suspended; wait_queue_head_t suspend_wq;};static struct omap24xxfb_suspend_data fb_suspend_data;#define omap24xxfb_suspend_lockout_fp(s,f) \ if ((s)->suspended) {\ if ((f)->f_flags & O_NONBLOCK)\ return -EBUSY;\ wait_event_interruptible((s)->suspend_wq,\ (s)->suspended == 0);\ }#define omap24xxfb_suspend_lockout(s) \ if ((s)->suspended) {\ wait_event_interruptible((s)->suspend_wq,\ (s)->suspended == 0);\ }/******************************************************************************//* 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 intvar_to_depth(const struct fb_var_screeninfo *var){ DBGENTER; 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: if (var->transp.length > 0) return 32; else return 24; } DBGLEAVE;}/* Calculate the horizontal sync frequency in Hertz * with a resolution of 250Hz. */static unsigned inthorizontal_sync_freq(const struct fb_var_screeninfo *var){ unsigned int hsf, hs, nom, den; unsigned int xres, yres; int output_dev = omap2_disp_get_output_dev(OMAP2_GRAPHICS); DBGENTER; /* Calculate the number of pixels per clock. */ omap2_disp_pixels_per_clock(&nom, &den); /* get the horizontal display resolution */ omap2_disp_get_panel_size(output_dev, &xres, &yres); 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; /* pixclock is in picoseconds * 10^12 / (pixclock*hs) = 4 * 10^9 * 250 / (pixclock*hs) */ else hsf = 0; DBGLEAVE; return hsf;}/* Calculate the vertical sync frequency in Hertz. */static unsigned intvertical_sync_freq(const struct fb_var_screeninfo *var){ unsigned int vsf, vs; unsigned int xres, yres; int output_dev = omap2_disp_get_output_dev(OMAP2_GRAPHICS); DBGENTER; /* get the vertical display resolution */ omap2_disp_get_panel_size(output_dev, &xres, &yres); vs = yres + var->upper_margin + var->lower_margin + var->vsync_len; if (vs > 0) vsf = horizontal_sync_freq(var)/vs; else vsf = 0; DBGLEAVE; return vsf;}/* Interrupt service routine. */static voidomap24xxfb_isr(void *arg, struct pt_regs *regs, u32 irqstatus){ struct omap24xxfb_info *oinfo = (struct omap24xxfb_info *) arg; ++oinfo->vsync_cnt; wake_up_interruptible(&oinfo->vsync_wait);}/* Wait for a vsync interrupt. This routine sleeps so it can only be called * from process context. */static intomap24xxfb_wait_for_vsync(struct omap24xxfb_info *oinfo){ wait_queue_t wqt; unsigned long cnt; int ret; unsigned int mask = 0; DBGENTER; mask = (DISPC_IRQSTATUS_EVSYNC_ODD | DISPC_IRQSTATUS_EVSYNC_EVEN | DISPC_IRQSTATUS_VSYNC); omap2_disp_irqenable(omap24xxfb_isr,mask); init_waitqueue_entry(&wqt, current); cnt = oinfo->vsync_cnt; ret = wait_event_interruptible_timeout(oinfo->vsync_wait, cnt != oinfo->vsync_cnt, oinfo->timeout); /* * If the GFX is on TV, then wait for another VSYNC * to compensate for Interlaced scan */ if(omap2_disp_get_output_dev(OMAP2_GRAPHICS) == OMAP2_OUTPUT_TV){ if(ret<0){ cnt = oinfo->vsync_cnt; ret = wait_event_interruptible_timeout( oinfo->vsync_wait, cnt != oinfo->vsync_cnt, oinfo->timeout); } } omap2_disp_irqdisable(omap24xxfb_isr,~(mask)); DBGLEAVE; if (ret < 0) return ret; if (ret == 0) return -ETIMEDOUT; return 0;}/* The GO bit needs to be set for the shadowed registers to take effect in * hardware. Once the hardware is ready, the GO bit will be reset. Per the * hardware specifications, we should not change any display controller * registers until the GO bit is reset. * This function polls the GO bit and waits until it is reset. * If the function may be called when the interrupts are disabled (jiffies * not running). In such cases, the 'count' variable helps to exit the loop * incase the bit gets stuck. */static voidwait_for_reg_sync(int busy_wait, unsigned long timeout){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -