📄 davincifb.c
字号:
/* * drivers/video/davincifb.c * * Framebuffer driver for Texas Instruments DaVinci display controller. * * Copyright (C) 2006 Texas Instruments, Inc. * Rishi Bhattacharya <support@ti.com> * * Leveraged from the framebuffer driver for OMAP24xx * written by 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/init.h>#include <linux/dma-mapping.h>#include <linux/interrupt.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <video/davincifb.h>#include <asm/system.h>#define DAVINCIFB_DEVICE "davincifb"#define DAVINCIFB_DRIVER "davincifb"/* Output Format Selection */#ifdef DEBUG#define DBGENTER printk(DAVINCIFB_DEVICE ": Entered %s\n", __FUNCTION__)#define DBGEXIT printk(DAVINCIFB_DEVICE ": Exited %s\n", __FUNCTION__)#define RETURN(x) \do { \ int __ret = (x); \ printk("Exited %s : ret %d\n", __FUNCTION__, __ret); \ return __ret; \} while(0)#else#define DBGENTER#define DBGEXIT#define RETURN(x) return (x)#endif#define MULTIPLE_BUFFERING 1#ifdef MULTIPLE_BUFFERING#define DOUBLE_BUF 2#define TRIPLE_BUF 3#else#define DOUBLE_BUF 1#define TRIPLE_BUF 1#endif/* * display controller register I/O routines */static __inline__ u32 dispc_reg_in(u32 offset){ return (inl(offset));}static __inline__ u32 dispc_reg_out(u32 offset, u32 val){ outl(val, offset); return (val);}static __inline__ u32 dispc_reg_merge(u32 offset, u32 val, u32 mask){ u32 addr = offset; u32 new_val = (inl(addr) & ~mask) | (val & mask); outl(new_val, addr); return (new_val);}/* There are 4 framebuffers, each represented by an fb_info and * a dm_win_info structure */#define OSD0_FBNAME "dm_osd0_fb"#define OSD1_FBNAME "dm_osd1_fb"#define VID0_FBNAME "dm_vid0_fb"#define VID1_FBNAME "dm_vid1_fb"/* usage: if (is_win(info->fix.id, OSD0)) ... */#define is_win(name, x) ((strcmp(name, x ## _FBNAME) == 0) ? 1 : 0)struct dm_win_info { struct fb_info info; /* X and Y position */ unsigned int x, y; /* framebuffer area */ dma_addr_t fb_base_phys; unsigned long fb_base; unsigned long fb_size; u32 pseudo_palette[17]; /* flag to identify if framebuffer area is fixed already or not */ int alloc_fb_mem; unsigned long sdram_address; struct dm_info *dm;};static struct dm_info { struct dm_win_info *osd0; struct dm_win_info *osd1; struct dm_win_info *vid0; struct dm_win_info *vid1; /* to map the registers */ dma_addr_t mmio_base_phys; unsigned long mmio_base; unsigned long mmio_size; wait_queue_head_t vsync_wait; unsigned long vsync_cnt; int timeout; /* this is the function that configures the output device (NTSC/PAL/LCD) * for the required output format (composite/s-video/component/rgb) */ void (*output_device_config) (int on); struct device *dev;} dm_static;static struct dm_info *dm = &dm_static;static struct fb_ops davincifb_ops;#define BASEX 0x80#define BASEY 0x12#define DISP_XRES 720#define DISP_YRES 480#define DISP_MEMY 576/* Random value chosen for now. Should be within the panel's supported range */#define LCD_PANEL_CLOCK 180000/* All window widths have to be rounded up to a multiple of 32 bytes *//* The OSD0 window has to be always within VID0. Plus, since it is in RGB565 * mode, it _cannot_ overlap with VID1. * For defaults, we are setting the OSD0 window to be displayed in the top * left quadrant of the screen, and the VID1 in the bottom right quadrant. * So the default 'xres' and 'yres' are set to half of the screen width and * height respectively. Note however that the framebuffer size is allocated * for the full screen size so the user can change the 'xres' and 'yres' by * using the FBIOPUT_VSCREENINFO ioctl within the limits of the screen size. */#define round_32(width) ((((width) + 31) / 32) * 32 )#define OSD0_XRES round_32((DISP_XRES)*16/8) * 8/16 /* pixels */#define OSD0_YRES DISP_YRES#define OSD0_FB_PHY 0#define OSD0_FB_SIZE (round_32((DISP_XRES)*16/8) * DISP_MEMY * DOUBLE_BUF) /* 16 bpp, Double buffered */static struct fb_var_screeninfo osd0_default_var = { .xres = OSD0_XRES, .yres = OSD0_YRES, .xres_virtual = OSD0_XRES, .yres_virtual = OSD0_YRES * DOUBLE_BUF, .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 = FB_ACTIVATE_NOW, .height = -1, .width = -1, .accel_flags = 0, .pixclock = LCD_PANEL_CLOCK, /* 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_INTERLACED,};/* Using the full screen for OSD1 by default */#define OSD1_XRES round_32(DISP_XRES*4/8) * 8/4 /* pixels */#define OSD1_YRES DISP_YRES#define OSD1_FB_PHY 0#define OSD1_FB_SIZE (round_32(DISP_XRES*4/8) * DISP_MEMY * DOUBLE_BUF)static struct fb_var_screeninfo osd1_default_var = { .xres = DISP_XRES, .yres = OSD1_YRES, .xres_virtual = OSD1_XRES, .yres_virtual = OSD1_YRES * DOUBLE_BUF, .xoffset = 0, .yoffset = 0, .bits_per_pixel = 4, .activate = FB_ACTIVATE_NOW, .accel_flags = 0, .pixclock = LCD_PANEL_CLOCK, /* picoseconds */ .vmode = FB_VMODE_INTERLACED,};/* Using the full screen for OSD0 by default */#define VID0_XRES round_32(DISP_XRES*16/8) * 8/16 /* pixels */#define VID0_YRES DISP_YRES#define VID0_FB_PHY 0#define VID0_FB_SIZE (round_32(DISP_XRES*16/8) * DISP_MEMY * TRIPLE_BUF)static struct fb_var_screeninfo vid0_default_var = { .xres = VID0_XRES, .yres = VID0_YRES, .xres_virtual = VID0_XRES, .yres_virtual = VID0_YRES * TRIPLE_BUF, .xoffset = 0, .yoffset = 0, .bits_per_pixel = 16, .activate = FB_ACTIVATE_NOW, .accel_flags = 0, .pixclock = LCD_PANEL_CLOCK, /* picoseconds */ .vmode = FB_VMODE_INTERLACED,};/* Using the bottom right quadrant of the screen screen for VID1 by default, * but keeping the framebuffer allocated for the full screen, so the user can * change the 'xres' and 'yres' later using the FBIOPUT_VSCREENINFO ioctl. */#define VID1_BPP 16 /* Video1 can be in YUV or RGB888 format */#define VID1_XRES round_32(DISP_XRES*16/8) * 8/16 /* pixels */#define VID1_YRES DISP_YRES#define VID1_FB_PHY 0#define VID1_FB_SIZE (round_32(DISP_XRES*16/8) * DISP_MEMY * TRIPLE_BUF)static struct fb_var_screeninfo vid1_default_var = { .xres = VID1_XRES, .yres = VID1_YRES, .xres_virtual = VID1_XRES, .yres_virtual = VID1_YRES * TRIPLE_BUF, .xoffset = 0, .yoffset = 0, .bits_per_pixel = VID1_BPP, .activate = FB_ACTIVATE_NOW, .accel_flags = 0, .pixclock = LCD_PANEL_CLOCK, /* picoseconds */ .vmode = FB_VMODE_INTERLACED,};#define x_pos(w) ((w)->x)#define y_pos(w) ((w)->y)static struct dmparams_t { u8 output; u8 format; u8 windows; /* bitmap flag based on VID0, VID1, OSD0, OSD1 * definitions in header file */ u32 vid0_xres; u32 vid0_yres; u32 vid0_xpos; u32 vid0_ypos; u32 vid1_xres; u32 vid1_yres; u32 vid1_xpos; u32 vid1_ypos; u32 osd0_xres; u32 osd0_yres; u32 osd0_xpos; u32 osd0_ypos; u32 osd1_xres; u32 osd1_yres; u32 osd1_xpos; u32 osd1_ypos;} dmparams = { NTSC, /* output */ COMPOSITE, /* format */ (1 << VID0) | (1 << VID1) | (1 << OSD0) | (1 << OSD1), /* windows registered */ 720, 480, 0, 0, /* vid0 size and position */ 720, 480, 0, 0, /* vid1 size and position */ 720, 480, 0, 0, /* osd0 size and position */ 720, 480, 0, 0, /* osd1 size and position */};/* Must do checks against the limits of the output device */static int davincifb_venc_check_mode(const struct dm_win_info *w, const struct fb_var_screeninfo *var){ DBGENTER; RETURN(0);}static void set_sdram_params(char *id, u32 addr, u32 line_length);static irqreturn_t davincifb_isr(int irq, void *arg, struct pt_regs *regs){ struct dm_info *dm = (struct dm_info *)arg; unsigned long addr=0; if ((dispc_reg_in(VENC_VSTAT) & 0x00000010) == 0x10) { xchg(&addr, dm->osd0->sdram_address); if (addr) { set_sdram_params(dm->osd0->info.fix.id, dm->osd0->sdram_address, dm->osd0->info.fix.line_length); dm->osd0->sdram_address = 0; } addr = 0; xchg(&addr, dm->osd1->sdram_address); if (addr) { set_sdram_params(dm->osd1->info.fix.id, dm->osd1->sdram_address, dm->osd1->info.fix.line_length); dm->osd1->sdram_address = 0; } addr = 0; xchg(&addr, dm->vid0->sdram_address); if (addr) { set_sdram_params(dm->vid0->info.fix.id, dm->vid0->sdram_address, dm->vid0->info.fix.line_length); dm->vid0->sdram_address = 0; } addr = 0; xchg(&addr, dm->vid1->sdram_address); if (addr) { set_sdram_params(dm->vid1->info.fix.id, dm->vid1->sdram_address, dm->vid1->info.fix.line_length); dm->vid1->sdram_address = 0; } return IRQ_HANDLED; } else { ++dm->vsync_cnt; wake_up_interruptible(&dm->vsync_wait); return IRQ_HANDLED; } return IRQ_HANDLED;}/* Wait for a vsync interrupt. This routine sleeps so it can only be called * from process context. */static int davincifb_wait_for_vsync(struct dm_win_info *w){ struct dm_info *dm = w->dm; wait_queue_t wq; unsigned long cnt; int ret; DBGENTER; init_waitqueue_entry(&wq, current); cnt = dm->vsync_cnt; ret = wait_event_interruptible_timeout(dm->vsync_wait, cnt != dm->vsync_cnt, dm->timeout); if (ret < 0) RETURN(ret); if (ret == 0) RETURN(-ETIMEDOUT); RETURN(0);}/* Sets a uniform attribute value over a rectangular area on the attribute * window. The attribute value (0 to 7) is passed through the fb_fillrect's * color parameter. */static int davincifb_set_attr_blend(struct fb_fillrect *r){ struct fb_info *info = &dm->osd1->info; struct fb_var_screeninfo *var = &dm->osd1->info.var; unsigned long start = 0; u8 blend; u32 width_bytes; if (r->dx + r->width > var->xres_virtual) return -EINVAL; if (r->dy + r->height > var->yres_virtual) return -EINVAL; if (r->color < 0 || r->color > 7) return -EINVAL; /* since bits_per_pixel = 4, this will truncate the width if it is * not even. Similarly r->dx will be rounded down to an even pixel. * ... Do we want to return an error otherwise? */ width_bytes = r->width * var->bits_per_pixel / 8; start = dm->osd1->fb_base + r->dy * info->fix.line_length + r->dx * var->bits_per_pixel / 8; blend = (((u8) r->color & 0xf) << 4) | ((u8) r->color); while (r->height--) { start += info->fix.line_length; memset((void *)start, blend, width_bytes); } return 0;}/* These position parameters are given through fb_var_screeninfo. * xp = var.reserved[0], yp = var.reserved[1], * xl = var.xres, yl = var.yres */static void set_win_position(char *id, u32 xp, u32 yp, u32 xl, u32 yl){ int i = 0; DBGENTER; if (is_win(id, VID0)) { i = 0; } else if (is_win(id, VID1)) { i = 1; } else if (is_win(id, OSD0)) { i = 2; } else if (is_win(id, OSD1)) { i = 3; } dispc_reg_out(OSD_WINXP(i), xp); dispc_reg_out(OSD_WINYP(i), yp); dispc_reg_out(OSD_WINXL(i), xl); dispc_reg_out(OSD_WINYL(i), yl); DBGEXIT;}static inline void get_win_position(struct dm_win_info *w, u32 * xp, u32 * yp, u32 * xl, u32 * yl){ struct fb_var_screeninfo *v = &w->info.var; *xp = x_pos(w); *yp = y_pos(w); *xl = v->xres; *yl = v->yres;}/* Returns 1 if the windows overlap, 0 otherwise */static int window_overlap(struct dm_win_info *w, u32 xp, u32 yp, u32 xl, u32 yl){ u32 _xp = 0, _yp = 0, _xl = 0, _yl = 0;#define OVERLAP(x1, y1, x2, y2, x3, y3, x4, y4) \(!( ((x1)<(x3) && (x2)<(x3)) || ((x1)>(x4) && (x2)>(x4)) || \ ((y1)<(y3) && (y2)<(y3)) || ((y1)>(y4) && (y2)>(y4)) ) \) DBGENTER; if (!w) RETURN(0); get_win_position(w, &_xp, &_yp, &_xl, &_yl);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -