diff options
Diffstat (limited to 'ANDROID_3.4.5/drivers/video/wmt/wmtfb.c')
-rwxr-xr-x | ANDROID_3.4.5/drivers/video/wmt/wmtfb.c | 1110 |
1 files changed, 1110 insertions, 0 deletions
diff --git a/ANDROID_3.4.5/drivers/video/wmt/wmtfb.c b/ANDROID_3.4.5/drivers/video/wmt/wmtfb.c new file mode 100755 index 00000000..72029bbc --- /dev/null +++ b/ANDROID_3.4.5/drivers/video/wmt/wmtfb.c @@ -0,0 +1,1110 @@ +/*++ + * linux/drivers/video/wmt/wmtfb.c + * WonderMedia frame buffer driver + * + * Copyright c 2014 WonderMedia Technologies, Inc. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * WonderMedia Technologies, Inc. + * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C +--*/ + +#define WMTFB_C +/* #define DEBUG */ +/* #define DEBUG_DETAIL */ + +/*----------------------- DEPENDENCE -----------------------------------------*/ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/errno.h> +#include <asm/uaccess.h> +#include <linux/kdev_t.h> +#include <linux/cdev.h> +#include <linux/fb.h> +#include <linux/dma-mapping.h> +#include <asm/page.h> +#include <linux/mm.h> +#include <linux/proc_fs.h> + +#include "vpp.h" + +/*----------------------- PRIVATE MACRO --------------------------------------*/ + +/*----------------------- PRIVATE CONSTANTS ----------------------------------*/ +/* #define FBUT_XXXX 1 *//*Example*/ + +/*----------------------- PRIVATE TYPE --------------------------------------*/ +/* typedef xxxx fbut_xxx_t; *//*Example*/ + +/*----------------------- INTERNAL PRIVATE VARIABLES - -----------------------*/ +/* int fbut_xxx; *//*Example*/ + +static struct fb_fix_screeninfo wmtfb_fix = { + .id = "wmtfbx", + .smem_start = 0, + .smem_len = 0, + .type = FB_TYPE_PACKED_PIXELS, + .type_aux = 0, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 1, + .ypanstep = 1, + .ywrapstep = 1, + .line_length = 0, + .mmio_start = 0, + .mmio_len = 0, + .accel = FB_ACCEL_NONE +}; + +static struct fb_var_screeninfo wmtfb_var = { + .xres = 0, + .yres = 0, + .xres_virtual = 0, + .yres_virtual = 0, +#if 0 + .bits_per_pixel = 32, + .red = {16, 8, 0}, + .green = {8, 8, 0}, + .blue = {0, 8, 0}, + .transp = {24, 8, 0}, +#else + .bits_per_pixel = 16, + .red = {11, 5, 0}, + .green = {5, 6, 0}, + .blue = {0, 5, 0}, + .transp = {0, 0, 0}, +#endif + .activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE, + .height = -1, + .width = -1, + .pixclock = 0, + .left_margin = 40, + .right_margin = 24, + .upper_margin = 32, + .lower_margin = 11, + .hsync_len = 96, + .vsync_len = 2, + .vmode = FB_VMODE_NONINTERLACED +}; + +int wmtfb_probe_ready; +/*--------------------- INTERNAL PRIVATE FUNCTIONS ---------------------------*/ +/* void fbut_xxx(void); *//*Example*/ + +/*----------------------- Function Body --------------------------------------*/ +/*----------------------- Linux Proc --------------------------------------*/ +#ifdef DEBUG +void wmtfb_show_var(char *str, struct fb_var_screeninfo *var) +{ + int pixclk; + + pixclk = (var->pixclock) ? (PICOS2KHZ(var->pixclock) * 1000) : 0; + DPRINT("----- %s ------------------------\n", str); + DPRINT("res(%d,%d),vir(%d,%d),offset(%d,%d)\n", + var->xres, var->yres, var->xres_virtual, + var->yres_virtual, var->xoffset, var->yoffset); + DPRINT("pixclk %d(%d),hsync %d,vsync %d\n", var->pixclock, pixclk, + var->hsync_len, var->vsync_len); + DPRINT("left %d,right %d,upper %d,lower %d\n", var->left_margin, + var->right_margin, var->upper_margin, var->lower_margin); + DPRINT("bpp %d, grayscale %d\n", var->bits_per_pixel, var->grayscale); + DPRINT("nonstd %d, activate %d, height %d, width %d\n", + var->nonstd, var->activate, var->height, var->width); + DPRINT("vmode 0x%x,sync 0x%x,rotate %d,accel %d\n", + var->vmode, var->sync, var->rotate, var->accel_flags); + DPRINT("-----------------------------\n"); + return; +} +#endif + +static DEFINE_SEMAPHORE(vpp_sem); +static DEFINE_SEMAPHORE(vpp_sem2); +void wmtfb_set_mutex(struct fb_info *info, int lock) +{ + struct vout_info_t *par = (struct vout_info_t *) info->par; + + if (lock) + down(&par->sem); + else + up(&par->sem); +} + +int vpp_set_blank(struct fb_info *info, int blank) +{ + struct vout_info_t *par = (struct vout_info_t *) info->par; + int i; + + DBG_MSG("(%d,%d)\n", info->node, blank); + + for (i = 0; i < VPP_VOUT_NUM; i++) { + if (par->vout[i] == 0) + break; + vout_set_blank(par->vout[i]->num, blank); + } + return 0; +} + +void vpp_set_lvds_blank(int blank) +{ + if (lcd_get_lvds_id() != LCD_LVDS_1024x600) + return; + + if (blank == 0) { + outl(inl(GPIO_BASE_ADDR + 0xC0) | BIT10, GPIO_BASE_ADDR + 0xC0); + outl(inl(GPIO_BASE_ADDR + 0x80) | BIT10, GPIO_BASE_ADDR + 0x80); + mdelay(6); + } + + if (blank) + msleep(50); + + vout_set_blank(VPP_VOUT_NUM_LVDS, + (blank) ? VOUT_BLANK_POWERDOWN : VOUT_BLANK_UNBLANK); + lvds_set_power_down((blank) ? 1 : 0); + + if (blank) { + mdelay(6); + /* GPIO10 off 8ms -> clock -> off */ + outl(inl(GPIO_BASE_ADDR + 0xC0) & ~BIT10, + GPIO_BASE_ADDR + 0xC0); + } +} + +void vpp_var_to_fb(struct fb_var_screeninfo *var, + struct fb_info *info, vdo_framebuf_t *fb) +{ + unsigned int addr; + int y_bpp, c_bpp; + + if (var) { + fb->col_fmt = WMT_FB_COLFMT(var->nonstd); + if (!fb->col_fmt) { + switch (var->bits_per_pixel) { + case 16: + if ((info->var.red.length == 5) && + (info->var.green.length == 6) && + (info->var.blue.length == 5)) { + fb->col_fmt = VDO_COL_FMT_RGB_565; + } else if ((info->var.red.length == 5) && + (info->var.green.length == 5) && + (info->var.blue.length == 5)) { + fb->col_fmt = VDO_COL_FMT_RGB_1555; + } else { + fb->col_fmt = VDO_COL_FMT_RGB_5551; + } + break; + case 32: + fb->col_fmt = VDO_COL_FMT_ARGB; + break; + default: + fb->col_fmt = VDO_COL_FMT_RGB_565; + break; + } + y_bpp = var->bits_per_pixel; + c_bpp = 0; + } else { + y_bpp = 8; + c_bpp = 8; + } + + fb->img_w = var->xres; + fb->img_h = var->yres; + fb->fb_w = var->xres_virtual; + fb->fb_h = var->yres_virtual; + fb->h_crop = 0; + fb->v_crop = 0; + fb->flag = 0; + fb->bpp = var->bits_per_pixel; + + addr = info->fix.smem_start + + (var->yoffset * var->xres_virtual * ((y_bpp + c_bpp) >> 3)); + addr += var->xoffset * ((y_bpp) >> 3); + fb->y_addr = addr; + fb->y_size = var->xres_virtual * var->yres * (y_bpp >> 3); + fb->c_addr = fb->y_addr + fb->y_size; + fb->c_size = var->xres_virtual * var->yres * (c_bpp >> 3); + } +} + +void vpp_pan_display_bitblit(struct vout_info_t *par) +{ + vdo_framebuf_t src, dst; + struct fb_videomode vmode; + struct govrh_mod_t *govr; + struct fb_info *dfb_info; + struct vout_info_t *d_info; + int fb_no; + + /* DBG_MSG("fb0 bitblit\n"); */ + src = par->fb; + for (fb_no = 1; ; fb_no++) { + d_info = vout_info_get_entry(fb_no); + if (!d_info) + break; + + govr = vout_info_get_govr(fb_no); + if (govr == 0) + break; + govrh_get_framebuffer(govr, &dst); + govrh_get_videomode(govr, &vmode); + dst.img_w = vmode.xres; + dst.fb_w = vpp_calc_fb_width(dst.col_fmt, dst.img_w); + dst.img_h = vmode.yres; + dst.fb_h = vmode.yres; + dst.y_size = dst.fb_w * dst.img_h * (dst.bpp >> 3); + + dfb_info = (struct fb_info *) d_info->fb_info_p; + if (dfb_info) { + dfb_info->var.yoffset = + (dfb_info->var.yoffset) ? 0 : dst.img_h; + dst.y_addr = dfb_info->fix.smem_start + + (dst.fb_w * dfb_info->var.yoffset * + (dst.bpp >> 3)); + dst.c_addr = 0; + } else { + govrh_get_fb_addr(govr, &dst.y_addr, &dst.c_addr); + } + + if (d_info->alloc_mode == VOUT_ALLOC_GE_OVERSCAN) { + if (!d_info->mb) { + int size = dst.y_size * VPP_MB_ALLOC_NUM; + + d_info->mb = mb_alloc(size); + if (d_info->mb) { + MSG("mb alloc 0x%x,%d\n", + d_info->mb, size); + } else { + DBG_ERR("alloc fail\n"); + return; + } + } + dst.y_addr = d_info->mb + + (dst.fb_w * dfb_info->var.yoffset * + (dst.bpp >> 3)); + } + + if (dst.y_addr) { + p_scl->scale_sync = 1; + vpp_set_recursive_scale(&src, &dst); + vout_set_framebuffer(d_info, &dst); + } + } +} + +int vpp_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct vout_info_t *par = (struct vout_info_t *) info->par; + + if (g_vpp.hdmi_certify_flag) + return 0; + + DBG_DETAIL("fb %d\n", (info) ? info->node : 0); + vpp_var_to_fb(var, info, &par->fb); + if (wmtfb_probe_ready && g_vpp.fb0_bitblit && (info->node == 0)) + vpp_pan_display_bitblit(par); + else + vout_set_framebuffer(par, &par->fb); + +#ifdef CONFIG_VPP_STREAM_CAPTURE + if (g_vpp.stream_enable && (par->hwc_mode == VOUT_HWC_VIRTUAL)) { + g_vpp.stream_mb_index = var->yoffset / var->yres; + vpp_dbg_show_val1(VPP_DBGLVL_STREAM, 0, + "stream pan disp", g_vpp.stream_mb_index); + } +#endif + if (vpp_check_dbg_level(VPP_DBGLVL_DISPFB)) { + char buf[50]; + unsigned int yaddr = 0, caddr = 0; + struct govrh_mod_t *govr; + + govr = vout_info_get_govr(info->node); + if (govr) + govrh_get_fb_addr(govr, &yaddr, &caddr); + sprintf(buf, "pan_display %d,0x%x", par->num, yaddr); + vpp_dbg_show(VPP_DBGLVL_DISPFB, par->num + 1, buf); + } + + if (vpp_check_dbg_level(VPP_DBGLVL_FPS)) { + char buf[10]; + + sprintf(buf, "fb%d", par->num); + vpp_dbg_timer(&par->pandisp_timer, buf, 2); + } + return 0; +} + +int vpp_set_par(struct fb_info *info) +{ + struct vout_info_t *par = (struct vout_info_t *) info->par; + vdo_framebuf_t fb; + struct fb_videomode var, cur; + struct govrh_mod_t *govr; + + if (g_vpp.hdmi_certify_flag) + return 0; + + govr = vout_info_get_govr(info->node); + if (!govr) + return 0; + + wmtfb_set_mutex(info, 1); + + /* check frame buffer */ + vpp_var_to_fb(&info->var, info, &fb); + par->fb.fb_h = fb.fb_h; + if (memcmp(&fb.img_w, &par->fb.img_w, 32)) { + if ((wmtfb_probe_ready == 0) && g_vpp.govrh_preinit) { + MSG("[uboot logo] fb%d\n", info->node); + govrh_get_framebuffer(govr, &par->fb); + vpp_set_recursive_scale(&par->fb, &fb); + } +#ifdef DEBUG + MSG("set_par %d : set framebuf\n", + info->node); + vpp_show_framebuf("cur", &par->fb); + vpp_show_framebuf("new", &fb); +#endif + par->fb = fb; + } else { + fb.img_w = 0; + } + + /* check timing */ + fb_var_to_videomode(&var, &info->var); + govrh_get_videomode(govr, &cur); + if ((cur.xres == var.xres) && (cur.yres == var.yres)) { + unsigned int cur_pixclk, new_pixclk; + + /* diff less than 500K */ + cur_pixclk = PICOS2KHZ(cur.pixclock); + new_pixclk = PICOS2KHZ(var.pixclock); + if (abs(new_pixclk - cur_pixclk) < 500) { + var.pixclock = cur.pixclock; + var.refresh = cur.refresh; + } + /* diff less than 2 */ + if (abs(var.refresh - cur.refresh) <= 2) + var.refresh = cur.refresh; + } + + //var.sync &= (FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT); + var.sync = cur.sync; + if (memcmp(&var, &cur, sizeof(struct fb_videomode))) { +#ifdef DEBUG + DPRINT("[wmtfb] set_par %d: set timing\n", info->node); + vpp_show_timing("cur", &cur, 0); + vpp_show_timing("new", &var, 0); +#endif + } else { + var.xres = 0; + } + vout_config(par, (var.xres) ? &var : 0, (fb.img_w) ? &fb : 0); + wmtfb_set_mutex(info, 0); + return 0; +} + +int vpp_get_info(int fbn, struct fb_var_screeninfo *var) +{ + struct vout_info_t *par; + + par = vout_info_get_entry(fbn); + if (!par) + return -1; + + var->xres = par->resx; + var->yres = par->resy; + var->xres_virtual = par->resx_virtual; + var->yres_virtual = var->yres * VPP_MB_ALLOC_NUM; + var->bits_per_pixel = (g_vpp.mb_colfmt == VDO_COL_FMT_ARGB) ? 32 : 16; + var->pixclock = par->resx * par->resy * par->fps; + var->pixclock = (var->pixclock) ? (var->pixclock / 1000) : 0; + var->pixclock = (var->pixclock) ? KHZ2PICOS(var->pixclock) : 0; + var->left_margin = 0; + var->right_margin = 0; + var->upper_margin = 0; + var->lower_margin = 0; + var->hsync_len = 0; + var->vsync_len = 0; + if (par->option & VPP_OPT_INTERLACE) { + var->vmode |= FB_VMODE_INTERLACED; + var->pixclock *= 2; + } +#ifdef DEBUG + MSG("[get_info] fb%d\n", fbn); + wmtfb_show_var("get_info", var); +#endif + return 0; +} + +int wmtfb_alloc(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct vout_info_t *par = (struct vout_info_t *) info->par; + unsigned int size; + int no_alloc = 0; + vdo_color_fmt colfmt; + int y_bpp, c_bpp; + unsigned int mb_resx, mb_resy, fb_num; + int i; + + colfmt = (var->nonstd) ? WMT_FB_COLFMT(var->nonstd) : + ((var->bits_per_pixel == 16) ? + VDO_COL_FMT_RGB_565 : VDO_COL_FMT_ARGB); + vpp_get_colfmt_bpp(colfmt, &y_bpp, &c_bpp); + + switch (par->alloc_mode) { + case VOUT_ALLOC_FIX_MB: + if (info->fix.smem_start) { + no_alloc = 1; + break; + } + + { + char buf[100]; + int varlen = 100; + + if (wmt_getsyspara("wmt.display.mb", + (unsigned char *)buf, &varlen) == 0) { + unsigned int parm[10]; + + vpp_parse_param(buf, (unsigned int *)parm, 4, 0); + MSG("boot parm mb (%d,%d),bpp %d,fb %d\n", + parm[0], parm[1], parm[2], parm[3]); + mb_resx = parm[0]; + mb_resy = parm[1]; + y_bpp = parm[2] * 8; + c_bpp = 0; + fb_num = parm[3]; + } else { + mb_resx = VPP_HD_MAX_RESX; + mb_resy = VPP_HD_MAX_RESY; + fb_num = VPP_MB_ALLOC_NUM; + } + } + break; + case VOUT_ALLOC_DYNAMIC_MB: + if ((par->hwc_mode == VOUT_HWC_VIRTUAL) + && !wmtfb_probe_ready) { + no_alloc = 1; + break; + } + mb_resx = var->xres_virtual; + mb_resy = var->yres; + fb_num = VPP_MB_ALLOC_NUM; + break; + case VOUT_ALLOC_GE_OVERSCAN: + if (!info->fix.smem_start) { + struct vout_info_t *fb0_par; + struct fb_info *fb0_info; + + fb0_par = vout_info_get_entry(0); + fb0_info = (struct fb_info *) fb0_par->fb_info_p; + info->fix.smem_start = fb0_info->fix.smem_start; + info->fix.smem_len = fb0_info->fix.smem_len; + info->screen_base = fb0_info->screen_base; + info->screen_size = fb0_info->screen_size; + } + default: + no_alloc = 1; + break; + } +#if 0 + DBG_MSG("fb%d,mode %d,size %d,len %d,%dx%d,no alloc %d\n", + info->node, par->alloc_mode, size, info->fix.smem_len, + var->xres, var->yres, no_alloc); +#endif + if (no_alloc) + return 0; /* don't need */ + + size = mb_resx * mb_resy * ((y_bpp + c_bpp) / 8) * fb_num ; + if (size == info->fix.smem_len) + return 0; + + /* free pre mb */ + if (info->fix.smem_start) { + mb_free(info->fix.smem_start); + info->fix.smem_start = 0; + info->fix.smem_len = 0; + info->screen_base = 0; + info->screen_size = 0; + MSG("[wmtfb] fb%d free mb\n", info->node); + } + + if ((var->xres == 0) || (var->yres == 0)) + return -1; + + info->fix.smem_start = mb_alloc(size); + if (!info->fix.smem_start) { + DBG_ERR("fb%d alloc mb fail %d\n", info->node, size); + return -1; + } + info->fix.smem_len = size; + info->screen_base = mb_phys_to_virt(info->fix.smem_start); + info->screen_size = size; + MSG("[wmtfb] fb%d mb 0x%x,len %d,base 0x%x(%d,%d,%d)\n", info->node, + (int) info->fix.smem_start, info->fix.smem_len, + (int) info->screen_base, mb_resx, mb_resy, fb_num); + + if (wmtfb_probe_ready) { + int ysize, csize; + + size = size / fb_num; + ysize = mb_resx * mb_resy * y_bpp / 8; + csize = mb_resx * mb_resy * c_bpp / 8; + for (i = 0; i < fb_num; i++) { + memset(info->screen_base + i * size, 0x0, ysize); + if (c_bpp) + memset(info->screen_base + i * size + ysize, + 0x80, csize); + } + } + + if (par->hwc_mode == VOUT_HWC_VIRTUAL) { + vpp_lock(); + g_vpp.stream_mb_cnt = VPP_MB_ALLOC_NUM; + size = var->xres_virtual * var->yres * ((y_bpp + c_bpp) / 8); + for (i = 0; i < g_vpp.stream_mb_cnt; i++) + g_vpp.stream_mb[i] = info->fix.smem_start + size * i; + vpp_unlock(); + } + return 0; +} + +static int wmtfb_open +( + struct fb_info *info, /*!<; // a pointer point to struct fb_info */ + int user /*!<; // user space mode */ +) +{ + DBG_MSG("Enter wmtfb_open\n"); + return 0; +} + +static int wmtfb_release +( + struct fb_info *info, /*!<; // a pointer point to struct fb_info */ + int user /*!<; // user space mode */ +) +{ + DBG_MSG("Enter wmtfb_release\n"); + return 0; +} + +int wmtfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct vout_info_t *par; + int temp; + int force = 0; + +#ifdef DEBUG_DETAIL + DMSG("Enter %d\n", info->node); + wmtfb_show_var("[check_var beg] cur var", &info->var); + wmtfb_show_var("[check_var beg] new var", var); +#endif + if (!info->par) { /* link fb & vout info */ + par = vout_info_get_entry(info->node); + info->par = (void *) par; + par->fb_info_p = (void *) info; + } + + par = (struct vout_info_t *) info->par; + if (par->alloc_mode == VOUT_ALLOC_GE_OVERSCAN) { + struct vout_info_t *fb0_par; + + fb0_par = vout_info_get_entry(0); + var->xres_virtual = fb0_par->resx_virtual; + var->yres_virtual = fb0_par->resy_virtual * VPP_MB_ALLOC_NUM; + } else { + var->xres_virtual = vpp_calc_fb_width( + (var->bits_per_pixel == 16) ? + VDO_COL_FMT_RGB_565 : VDO_COL_FMT_ARGB, + (var->xres_virtual < var->xres) ? + var->xres : var->xres_virtual); + } + + if (wmtfb_alloc(var, info)) + return -ENOMEM; + + if ((var->xres == 0) || (var->yres == 0)) + return -1; + + temp = var->xres_virtual * (var->bits_per_pixel >> 3); + temp = (temp) ? (info->fix.smem_len / temp) : 0; + if (temp < var->yres_virtual) { + var->yres_virtual = temp; + } + + /* more than 1M is khz not picos (for ut_vpp) */ + if (var->pixclock > 1000000) { + temp = var->pixclock / 1000; + temp = (temp) ? KHZ2PICOS(temp) : 0; + DBG_MSG("pixclock patch(>1000000)%d-->%d\n", + var->pixclock, temp); + var->pixclock = temp; + } + + /* less than 100 is fps not picos (for ut_vpp) */ + if ((var->pixclock > 0) && (var->pixclock < 100)) { + unsigned int htotal, vtotal; + + htotal = var->xres + var->right_margin + var->hsync_len + + var->left_margin; + vtotal = var->yres + var->lower_margin + var->vsync_len + + var->upper_margin; + temp = (htotal * vtotal * var->pixclock) / 1000; + temp = (temp) ? KHZ2PICOS(temp) : 0; + DBG_MSG("pixclock patch(<100)%d-->%d\n", var->pixclock, temp); + var->pixclock = temp; + } +#ifdef DEBUG_DETAIL + wmtfb_show_var("cur var", &info->var); + wmtfb_show_var("new var", var); +#endif + switch (var->bits_per_pixel) { + case 1: + case 8: + if (var->red.offset > 8) { + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 0; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + } + break; + case 16: /* ARGB 1555 */ + if (var->transp.length) { + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 15; + var->transp.length = 1; + } else { /* RGB 565 */ + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + } + break; + case 24: /* RGB 888 */ + case 32: /* ARGB 8888 */ + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + break; + } + + if (g_vpp.fb_manual) + return 0; + + if (!wmtfb_probe_ready) + force = 1; + + if (g_vpp.fb_recheck & (0x1 << info->node)) { + force = 1; + g_vpp.fb_recheck &= ~(0x1 << info->node); + } + + if ((var->xres != info->var.xres) || (var->yres != info->var.yres) || + memcmp(&info->var.pixclock, &var->pixclock, 4 * 9) || force) { + struct fb_videomode varfbmode; + unsigned int yres_virtual; + unsigned int xres_virtual; + + DPRINT("[wmtfb_check_var] fb%d res(%d,%d)->(%d,%d),force %d\n", + info->node, info->var.xres, info->var.yres, + var->xres, var->yres, force); +#ifdef DEBUG + wmtfb_show_var("cur var", &info->var); + wmtfb_show_var("new var", var); +#endif + yres_virtual = var->yres_virtual; + xres_virtual = var->xres_virtual; + fb_var_to_videomode(&varfbmode, var); +#ifdef DEBUG + DPRINT("new fps %d\n", varfbmode.refresh); +#endif + if (vout_find_match_mode(info->node, &varfbmode, 1)) { + DPRINT("[wmtfb] not support\n"); + return -1; + } + fb_videomode_to_var(var, &varfbmode); + var->yres_virtual = yres_virtual; + var->xres_virtual = xres_virtual; +#ifdef DEBUG + wmtfb_show_var("[wmtfb] time change", var); +#endif + vout_get_width_height(info->node, &var->width, &var->height); + } + return 0; +} + +static int wmtfb_set_par +( + struct fb_info *info /*!<; // a pointer point to struct fb_info */ +) +{ + struct fb_var_screeninfo *var = &info->var; + + DBG_DETAIL("Enter fb%d(%dx%d)\n", info->node, var->xres, var->yres); + + /* init your hardware here */ + /* ... */ + if (var->bits_per_pixel == 8) + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + else + info->fix.visual = FB_VISUAL_TRUECOLOR; + vpp_set_par(info); + info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; + return 0; +} + +static int wmtfb_setcolreg +( + unsigned regno, /*!<; // register no */ + unsigned red, /*!<; // red color map */ + unsigned green, /*!<; // green color map */ + unsigned blue, /*!<; // blue color map */ + unsigned transp, /*!<; // transp map */ + struct fb_info *info /*!<; // a pointer point to struct fb_info */ +) +{ + return 0; + +} + +static int wmtfb_pan_display +( + struct fb_var_screeninfo *var, /*!<; // a pointer fb_var_screeninfo */ + struct fb_info *info /*!<; // a pointer fb_info */ +) +{ + static struct timeval tv1 = {0, 0}; + + DBG_DETAIL("Enter wmtfb_pan_display\n"); + + wmtfb_set_mutex(info, 1); + if (var->activate & FB_ACTIVATE_VBL) { + struct timeval tv2; + + do_gettimeofday(&tv2); + if (tv1.tv_sec) { + int us; + + us = (tv2.tv_sec == tv1.tv_sec) ? + (tv2.tv_usec - tv1.tv_usec) : + (1000000 + tv2.tv_usec - tv1.tv_usec); + if (us < 16667) + vpp_wait_vsync(1, 1); + } + } + vpp_pan_display(var, info); + do_gettimeofday(&tv1); + wmtfb_set_mutex(info, 0); + + DBG_DETAIL("Exit wmtfb_pan_display\n"); + return 0; +} + +#define UMP_INVALID_SECURE_ID ((unsigned int)-1) +#define GET_UMP_SECURE_ID _IOWR('m', 310, unsigned int) +#define GET_UMP_SECURE_ID_BUF1 _IOWR('m', 311, unsigned int) +#define GET_UMP_SECURE_ID_BUF2 _IOWR('m', 312, unsigned int) +int wmtfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + + switch (cmd) { + case FBIO_WAITFORVSYNC: + vpp_wait_vsync(info->node, 1); + break; + case GET_UMP_SECURE_ID: + case GET_UMP_SECURE_ID_BUF1: + case GET_UMP_SECURE_ID_BUF2: + { + unsigned int ump_id; + extern unsigned int (*mali_get_ump_secure_id) + (unsigned int addr, unsigned int size); + if (mali_get_ump_secure_id) + ump_id = (*mali_get_ump_secure_id)(info->fix.smem_start, + info->fix.smem_len); + else + ump_id = UMP_INVALID_SECURE_ID; + printk("[wmtfb] ump_id %d,0x%x,len %d\n", ump_id, + (int)info->fix.smem_start, info->fix.smem_len); + return put_user((unsigned int) ump_id, + (unsigned int __user *) arg); + } + break; + default: + break; + } + return retval; +} + +static int wmtfb_mmap +( + struct fb_info *info, /*!<; // a pointer fb_info */ + struct vm_area_struct *vma /*!<; // a pointer vm_area_struct */ +) +{ + unsigned long off; + unsigned long start; + u32 len; + + DBGMSG("Enter wmtfb_mmap\n"); + + /* frame buffer memory */ + start = info->fix.smem_start; + off = vma->vm_pgoff << PAGE_SHIFT; + len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); + if (off >= len) { + /* memory mapped io */ + off -= len; + if (info->var.accel_flags) + return -EINVAL; + start = info->fix.mmio_start; + len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len); + } + + start &= PAGE_MASK; + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + off += start; + vma->vm_pgoff = off >> PAGE_SHIFT; + vma->vm_flags |= VM_IO | VM_RESERVED; + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) + return -EAGAIN; + DBGMSG("Exit wmtfb_mmap\n"); + return 0; +} + +int wmtfb_hw_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + return 0; +} + +int wmtfb_blank(int blank, struct fb_info *info) +{ + DBGMSG("(%d,%d)\n", info->node, blank); + vpp_set_blank(info, blank); + return 0; +} + +/*************************************************************************** + driver file operations struct define +****************************************************************************/ +static struct fb_ops wmtfb_ops = { + .owner = THIS_MODULE, + .fb_open = wmtfb_open, + .fb_release = wmtfb_release, +#if 0 + .fb_read = wmtfb_read, + .fb_write = wmtfb_write, +#endif + .fb_check_var = wmtfb_check_var, + .fb_set_par = wmtfb_set_par, + .fb_setcolreg = wmtfb_setcolreg, + .fb_pan_display = wmtfb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_blank = wmtfb_blank, + .fb_cursor = wmtfb_hw_cursor, + .fb_ioctl = wmtfb_ioctl, + .fb_mmap = wmtfb_mmap, +}; + +static int __init wmtfb_probe +( + struct platform_device *dev /*!<; // a pointer point to struct device */ +) +{ + struct fb_info *info; + struct fb_var_screeninfo var; + int i; + + DBG_MSG("Enter\n"); + + for (i = 1; ; i++) { + memcpy(&var, &wmtfb_var, sizeof(struct fb_var_screeninfo)); + if (vpp_get_info(i, &var)) + break; + info = framebuffer_alloc(0, &dev->dev); + if (!info) + return -ENOMEM; + info->fbops = &wmtfb_ops; + memcpy(&info->fix, &wmtfb_fix, + sizeof(struct fb_fix_screeninfo)); + info->fix.id[5] = '0' + i; + info->flags = FBINFO_DEFAULT; + if (register_framebuffer(info) < 0) + return -EINVAL; + + MSG(KERN_INFO "fb%d: %s frame buffer device\n", + info->node, info->fix.id); + + wmtfb_check_var(&var, info); + memcpy(&info->var, &var, sizeof(struct fb_var_screeninfo)); + wmtfb_set_par(info); + wmtfb_pan_display(&info->var, info); + + if (info->node == 1) { + info->dev->power.async_suspend = 1; + dev_set_drvdata(&dev->dev, info); + } + } + + for (i = 0; i < VPP_VOUT_NUM; i++) { + int blank; + struct vout_t *vout; + + vout = vout_get_entry(i); + if (!vout) + continue; + blank = (vout->status & VPP_VOUT_STS_ACTIVE) ? + VOUT_BLANK_UNBLANK : VOUT_BLANK_POWERDOWN; + blank = (vout->status & VPP_VOUT_STS_BLANK) ? + VOUT_BLANK_NORMAL : blank; + vout_set_blank(i, blank); + } + DBG_MSG("Leave\n"); + wmtfb_probe_ready = 1; + g_vpp.govrh_preinit = 0; + return 0; +} + +static int wmtfb_remove +( + struct platform_device *dev /*!<; // a pointer point to struct device */ +) +{ + struct fb_info *info = dev_get_drvdata(&dev->dev); + + if (info) { + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); + } + return 0; +} + +#ifdef CONFIG_PM +static int wmtfb_suspend +( + struct platform_device *pDev, /*!<; // a pointer struct device */ + pm_message_t state /*!<; // suspend state */ +) +{ + return 0; +} + +static int wmtfb_resume +( + struct platform_device *pDev /*!<; // a pointer struct device */ +) +{ + return 0; +} +#else +#define wmtfb_suspend NULL +#define wmtfb_resume NULL +#endif + +/*************************************************************************** + device driver struct define +****************************************************************************/ +static struct platform_driver wmtfb_driver = { + .driver.name = "wmtfb", + .driver.bus = &platform_bus_type, + .probe = wmtfb_probe, + .remove = wmtfb_remove, + .suspend = wmtfb_suspend, + .resume = wmtfb_resume, +}; + +/*************************************************************************** + platform device struct define +****************************************************************************/ +static u64 wmtfb_dma_mask = 0xffffffffUL; +static struct platform_device wmtfb_device = { + .name = "wmtfb", + .dev = { + .dma_mask = &wmtfb_dma_mask, + .coherent_dma_mask = ~0, + .power.async_suspend = 1, + }, + +#if 0 + .id = 0, + .dev = { + .release = wmtfb_platform_release, + }, + .num_resources = 0, /* ARRAY_SIZE(wmtfb_resources), */ + .resource = NULL, /* wmtfb_resources, */ +#endif +}; + +static int __init wmtfb_init(void) +{ + int ret; + + /* + * For kernel boot options (in 'video=wmtfb:<options>' format) + */ + ret = platform_driver_register(&wmtfb_driver); + if (!ret) { + ret = platform_device_register(&wmtfb_device); + if (ret) + platform_driver_unregister(&wmtfb_driver); + } + return ret; + +} +module_init(wmtfb_init); + +static void __exit wmtfb_exit(void) +{ + printk(KERN_ALERT "Enter wmtfb_exit\n"); + + platform_driver_unregister(&wmtfb_driver); + platform_device_unregister(&wmtfb_device); + return; +} +module_exit(wmtfb_exit); + +MODULE_AUTHOR("WonderMedia SW Team"); +MODULE_DESCRIPTION("wmtfb device driver"); +MODULE_LICENSE("GPL"); +/*--------------------End of Function Body -----------------------------------*/ +#undef WMTFB_C |