/*++ * 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 . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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:' 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