📄 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 "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 0x0//0x80
#define BASEY 0x22//0x12
#define DISP_XRES 720
#define DISP_YRES 480
#define DISP_MEMY 576
// default LCD timing parameters
static struct lcd_timing_params default_lcd_timing = {
.h_interval = 800, // number of pixel clocks between active hsync edges
.v_interval = 524, // number of hsyncs between active vsync edges
.h_valid = 640, // x resolution
.v_valid = 480, // y resolution
.h_pulse = 96, // width in pixel clocks of hsync
.v_pulse = 32, // width in hsync pulses of vsync
.h_start = 23, // time in pixel clocks from active hsync to valid data
.v_start = 32, // time in hsync pulses from active vsync to new frame
.h_delay = 0,
.v_delay = 0,
.hsync_pol = 0, // 0 = active low, 1 = active high
.vsync_pol = 0, // 0 = active low, 1 = active high
.oe_pol = 1, // 0 = active low, 1 = active high
};
/* Random value chosen for now. Should be within the panel's supported range */
//#define LCD_PANEL_CLOCK 180000
#define LCD_PANEL_CLOCK 37037 // 27MHz Pixel Clock
/* 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) {
//update the framebuffer address here if a new pan ioctls is pending
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 = NULL;
}
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 = NULL;
}
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 = NULL;
}
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 = NULL;
}
return IRQ_HANDLED;
}
else
{
++dm->vsync_cnt;
wake_up_interruptible(&dm->vsync_wait);
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);
RETURN(OVERLAP(xp, yp, xp + xl, yp + yl,
_xp, _yp, _xp + _xl, _yp + _yl));
#undef OVERLAP
}
/* Returns 1 if the window parameters are within VID0, 0 otherwise */
static int within_vid0_limits(u32 xp, u32 yp, u32 xl, u32 yl)
{
u32 vid0_xp = 0, vid0_yp = 0, vid0_xl = 0, vid0_yl = 0;
DBGENTER;
if (!dm->vid0)
RETURN(1);
get_win_position(dm->vid0, &vid0_xp, &vid0_yp, &vid0_xl, &vid0_yl);
if ((xp >= vid0_xp) && (yp >= vid0_yp) &&
(xp + xl <= vid0_xp + vid0_xl) && (yp + yl <= vid0_yp + vid0_yl))
RETURN(1);
RETURN(0);
}
/* VID0 must be large enough to hold all other windows */
static int check_new_vid0_size(u32 xp0, u32 yp0, u32 xl0, u32 yl0)
{
u32 _xp = 0, _yp = 0, _xl = 0, _yl = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -