/*++
* linux/drivers/video/wmt/wmtfb.c
* WonderMedia frame buffer driver
*
* Copyright c 2013 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 = "wmtfb",
.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 = 0xD8050000,
.mmio_len = 0x0700,
.accel = FB_ACCEL_NONE
};
static struct fb_var_screeninfo wmtfb_var = {
.xres = VPP_HD_DISP_RESX,
.yres = VPP_HD_DISP_RESY,
.xres_virtual = VPP_HD_DISP_RESX,
.yres_virtual = VPP_HD_DISP_RESY,
#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 = (VPP_HD_DISP_RESX*VPP_HD_DISP_RESY*VPP_HD_DISP_FPS),
.left_margin = 40,
.right_margin = 24,
.upper_margin = 32,
.lower_margin = 11,
.hsync_len = 96,
.vsync_len = 2,
.vmode = FB_VMODE_NONINTERLACED
};
int wmtfb_fb1_probe;
/*--------------------- 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)
{
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,
(int)(PICOS2KHZ(var->pixclock) * 1000),
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
#ifdef CONFIG_PROC_FS
#define CONFIG_VPP_PROC
#ifdef CONFIG_VPP_PROC
unsigned int vpp_proc_value;
char vpp_proc_str[16];
static ctl_table vpp_table[];
static int vpp_do_proc(ctl_table *ctl, int write,
void *buffer, size_t *len, loff_t *ppos)
{
int ret;
int ctl_name;
ctl_name = (((int)ctl - (int)vpp_table) / sizeof(ctl_table)) + 1;
if (!write) {
switch (ctl_name) {
case 1:
vpp_proc_value = g_vpp.dbg_msg_level;
break;
case 2:
vpp_proc_value = g_vpp.dbg_wait;
break;
case 3:
vpp_proc_value = g_vpp.dbg_flag;
break;
case 8:
case 9:
vpp_proc_value = vpp_get_base_clock((ctl_name == 8) ?
VPP_MOD_GOVRH : VPP_MOD_GOVRH2);
break;
case 10:
vpp_proc_value = p_scl->scale_mode;
break;
case 11:
vpp_proc_value = p_scl->filter_mode;
break;
#ifdef CONFIG_WMT_HDMI
case 12:
vpp_proc_value = g_vpp.hdmi_cp_enable;
break;
case 13:
vpp_proc_value = g_vpp.hdmi_3d_type;
break;
case 14:
vpp_proc_value = g_vpp.hdmi_certify_flag;
break;
#endif
case 15:
vpp_proc_value = govrh_get_brightness(p_govrh);
break;
case 16:
vpp_proc_value = govrh_get_contrast(p_govrh);
break;
case 17:
vpp_proc_value = govrh_get_saturation(p_govrh);
break;
default:
break;
}
}
ret = proc_dointvec(ctl, write, buffer, len, ppos);
if (write) {
switch (ctl_name) {
case 1:
DPRINT("---------- VPP debug level ----------\n");
DPRINT("0-disable,255-show all\n");
DPRINT("1-scale,2-disp fb,3-interrupt,4-timer\n");
DPRINT("5-ioctl,6-diag,7-stream\n");
DPRINT("-------------------------------------\n");
g_vpp.dbg_msg_level = vpp_proc_value;
break;
case 2:
g_vpp.dbg_wait = vpp_proc_value;
vpp_dbg_wake_up();
break;
case 3:
g_vpp.dbg_flag = vpp_proc_value;
break;
#ifdef CONFIG_WMT_EDID
case 6:
{
vout_t *vo;
vo = vout_get_entry_adapter(vpp_proc_value);
if ((vo->inf) && (vo->inf->get_edid)) {
vo->status &= ~VPP_VOUT_STS_EDID;
if (vout_get_edid(vo->num)) {
int i;
vo->edid_info.option = 0;
edid_dump(vo->edid);
for (i = 1; i <= vo->edid[126]; i++)
edid_dump(vo->edid + 128 * i);
if (!edid_parse(vo->edid,
&vo->edid_info))
DBG_ERR("parse EDID\n");
} else {
DBG_ERR("read EDID\n");
}
}
}
break;
#endif
case 8:
case 9:
govrh_set_clock((ctl_name == 8) ? p_govrh : p_govrh2,
vpp_proc_value);
DPRINT("set govr pixclk %d\n", vpp_proc_value);
break;
case 10:
DPRINT("---------- scale mode ----------\n");
DPRINT("0-recursive normal\n");
DPRINT("1-recursive sw bilinear\n");
DPRINT("2-recursive hw bilinear\n");
DPRINT("3-realtime noraml (quality but x/32 limit)\n");
DPRINT("4-realtime bilinear (fit edge w skip line)\n");
DPRINT("-------------------------------------\n");
p_scl->scale_mode = vpp_proc_value;
break;
case 11:
p_scl->filter_mode = vpp_proc_value;
break;
#ifdef CONFIG_WMT_HDMI
case 12:
g_vpp.hdmi_cp_enable = vpp_proc_value;
hdmi_set_cp_enable(vpp_proc_value);
break;
case 13:
g_vpp.hdmi_3d_type = vpp_proc_value;
hdmi_tx_vendor_specific_infoframe_packet();
break;
case 14:
g_vpp.hdmi_certify_flag = vpp_proc_value;
break;
#endif
case 15:
govrh_set_brightness(p_govrh, vpp_proc_value);
break;
case 16:
govrh_set_contrast(p_govrh, vpp_proc_value);
break;
case 17:
govrh_set_saturation(p_govrh, vpp_proc_value);
break;
default:
break;
}
}
return ret;
}
struct proc_dir_entry *vpp_proc_dir;
static ctl_table vpp_table[] = {
{ /* .ctl_name = 1, */
.procname = "dbg_msg",
.data = &vpp_proc_value,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* .ctl_name = 2, */
.procname = "dbg_wait",
.data = &vpp_proc_value,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* .ctl_name = 3, */
.procname = "dbg_flag",
.data = &vpp_proc_value,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* .ctl_name = 4, */
.procname = "edid_disable",
.data = &edid_disable,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* .ctl_name = 5, */
.procname = "edid_msg",
.data = &edid_msg_enable,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* .ctl_name = 6, */
.procname = "vout_edid",
.data = &vpp_proc_value,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* .ctl_name = 7, */
.procname = "vo_mode",
.data = vpp_proc_str,
.maxlen = 12,
.mode = 0666,
.proc_handler = &proc_dostring,
},
{ /* .ctl_name = 8, */
.procname = "govr1_pixclk",
.data = &vpp_proc_value,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* .ctl_name = 9, */
.procname = "govr2_pixclk",
.data = &vpp_proc_value,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* .ctl_name = 10, */
.procname = "scl_scale_mode",
.data = &vpp_proc_value,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* .ctl_name = 11, */
.procname = "scl_filter",
.data = &vpp_proc_value,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* .ctl_name = 12 */
.procname = "hdmi_cp_enable",
.data = &vpp_proc_value,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* .ctl_name = 13 */
.procname = "hdmi_3d",
.data = &vpp_proc_value,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* .ctl_name = 14 */
.procname = "hdmi_certify",
.data = &vpp_proc_value,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* .ctl_name = 15 */
.procname = "brightness",
.data = &vpp_proc_value,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* .ctl_name = 16 */
.procname = "contrast",
.data = &vpp_proc_value,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* .ctl_name = 17 */
.procname = "saturation",
.data = &vpp_proc_value,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &vpp_do_proc,
},
{ /* end of table */
}
};
static ctl_table vpp_root_table[] = {
{
.procname = "vpp", /* create path ==> /proc/sys/vpp */
.mode = 0555,
.child = vpp_table
},
{ /* end of table */
}
};
static struct ctl_table_header *vpp_table_header;
#endif
static int vpp_sts_read_proc(char *buf, char **start, off_t offset,
int len, int *eof, void *data)
{
volatile struct govrh_regs *regs =
(volatile struct govrh_regs *) REG_GOVRH_BASE1_BEGIN;
unsigned int yaddr, caddr;
char *p = buf;
unsigned int reg;
p += sprintf(p, "--- VPP HW status ---\n");
#ifdef WMT_FTBLK_GOVRH
p += sprintf(p, "GOVRH memory read underrun error %d,cnt %d,cnt2 %d\n",
(regs->interrupt.val & 0x200) ? 1 : 0,
p_govrh->underrun_cnt, p_govrh2->underrun_cnt);
p_govrh->clr_sts(VPP_INT_ALL);
#endif
#ifdef WMT_FTBLK_SCL
p += sprintf(p, "---------------------------------------\n");
p += sprintf(p, "SCL TG error %d\n",
vppif_reg32_read(SCL_INTSTS_TGERR));
p += sprintf(p, "SCLR MIF1 read error %d\n",
vppif_reg32_read(SCLR_INTSTS_R1MIFERR));
p += sprintf(p, "SCLR MIF2 read error %d\n",
vppif_reg32_read(SCLR_INTSTS_R2MIFERR));
p += sprintf(p, "SCLW RGB fifo overflow %d\n",
vppif_reg32_read(SCLW_INTSTS_MIFRGBERR));
p += sprintf(p, "SCLW Y fifo overflow %d\n",
vppif_reg32_read(SCLW_INTSTS_MIFYERR));
p += sprintf(p, "SCLW C fifo overflow %d\n",
vppif_reg32_read(SCLW_INTSTS_MIFCERR));
p_scl->clr_sts(VPP_INT_ALL);
#endif
p += sprintf(p, "---------------------------------------\n");
p += sprintf(p, "(880.0)GOVRH Enable %d,(900.0)TG %d\n",
regs->mif.b.enable, regs->tg_enable.b.enable);
reg = REG32_VAL(PM_CTRL_BASE_ADDR + 0x258);
p += sprintf(p, "--- POWER CONTROL ---\n");
p += sprintf(p, "0x%x = 0x%x\n", PM_CTRL_BASE_ADDR + 0x258, reg);
p += sprintf(p, "HDCP %d,VPP %d,SCL %d,HDMI I2C %d\n",
(reg & BIT7) ? 1 : 0, (reg & BIT18) ? 1 : 0,
(reg & BIT21) ? 1 : 0, (reg & BIT22) ? 1 : 0);
p += sprintf(p, "HDMI %d,GOVR %d,NA12 %d\n",
(reg & BIT23) ? 1 : 0, (reg & BIT25) ? 1 : 0,
(reg & BIT16) ? 1 : 0);
p += sprintf(p, "DVO %d,HDMI OUT %d,LVDS %d\n", (reg & BIT29) ? 1 : 0,
(reg & BIT30) ? 1 : 0, (reg & BIT14) ? 1 : 0);
p += sprintf(p, "--- VPP fb Address ---\n");
p += sprintf(p, "GOV mb 0x%x 0x%x\n", g_vpp.mb[0], g_vpp.mb[1]);
#ifdef WMT_FTBLK_GOVRH
govrh_get_fb_addr(p_govrh, &yaddr, &caddr);
p += sprintf(p, "GOVRH fb addr Y(0x%x) 0x%x, C(0x%x) 0x%x\n",
REG_GOVRH_YSA, yaddr, REG_GOVRH_CSA, caddr);
govrh_get_fb_addr(p_govrh2, &yaddr, &caddr);
p += sprintf(p, "GOVRH2 fb addr Y(0x%x) 0x%x, C(0x%x) 0x%x\n",
REG_GOVRH2_YSA, yaddr, REG_GOVRH2_CSA, caddr);
#endif
p_govrh->underrun_cnt = 0;
p_govrh2->underrun_cnt = 0;
return p - buf;
} /* End of vpp_sts_read_proc */
static int vpp_reg_read_proc(char *buf, char **start, off_t offset,
int len, int *eof, void *data)
{
char *p = buf;
vpp_mod_base_t *mod_p;
int i;
DPRINT("Product ID:0x%x\n", vpp_get_chipid());
for (i = 0; i < VPP_MOD_MAX; i++) {
mod_p = vpp_mod_get_base(i);
if (mod_p && mod_p->dump_reg)
mod_p->dump_reg();
}
#ifdef WMT_FTBLK_HDMI
hdmi_reg_dump();
#endif
#ifdef WMT_FTBLK_LVDS
lvds_reg_dump();
#endif
return p - buf;
} /* End of vpp_reg_read_proc */
#endif
/*----------------------- fb ioctl --------------------------------------*/
int vpp_common_ioctl(unsigned int cmd, unsigned long arg)
{
vpp_mod_base_t *mod_p;
vpp_fb_base_t *mod_fb_p;
int retval = 0;
switch (cmd) {
case VPPIO_VPPGET_INFO:
{
int i;
vpp_cap_t parm;
parm.chip_id = vpp_get_chipid();
parm.version = 0x01;
parm.resx_max = VPP_HD_MAX_RESX;
parm.resy_max = VPP_HD_MAX_RESY;
parm.pixel_clk = 400000000;
parm.module = 0x0;
for (i = 0; i < VPP_MOD_MAX; i++) {
mod_p = vpp_mod_get_base(i);
if (mod_p)
parm.module |= (0x01 << i);
}
parm.option = VPP_CAP_DUAL_DISPLAY;
copy_to_user((void *)arg, (void *) &parm, sizeof(vpp_cap_t));
}
break;
case VPPIO_VPPSET_INFO:
{
vpp_cap_t parm;
copy_from_user((void *)&parm, (const void *)arg,
sizeof(vpp_cap_t));
}
break;
case VPPIO_I2CSET_BYTE:
{
vpp_i2c_t parm;
unsigned int id;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_i2c_t));
id = (parm.addr & 0x0000FF00) >> 8;
vpp_i2c_write(id, (parm.addr & 0xFF), parm.index,
(char *)&parm.val, 1);
}
break;
case VPPIO_I2CGET_BYTE:
{
vpp_i2c_t parm;
unsigned int id;
int len;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_i2c_t));
id = (parm.addr & 0x0000FF00) >> 8;
len = parm.val;
{
unsigned char buf[len];
vpp_i2c_read(id, (parm.addr & 0xFF), parm.index,
buf, len);
parm.val = buf[0];
}
copy_to_user((void *)arg, (void *) &parm, sizeof(vpp_i2c_t));
}
break;
case VPPIO_VPPSET_FBDISP:
break;
case VPPIO_VPPGET_FBDISP:
{
vpp_dispfb_info_t parm;
copy_to_user((void *)arg, (void *) &parm,
sizeof(vpp_dispfb_info_t));
}
break;
case VPPIO_WAIT_FRAME:
vpp_wait_vsync(0, arg);
break;
case VPPIO_MODULE_FRAMERATE:
{
vpp_mod_arg_t parm;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_mod_arg_t));
mod_fb_p = vpp_mod_get_fb_base(parm.mod);
if (!mod_fb_p)
break;
if (parm.read) {
parm.arg1 = mod_fb_p->framerate;
copy_to_user((void *)arg, (void *) &parm,
sizeof(vpp_mod_arg_t));
} else {
mod_fb_p->framerate = parm.arg1;
}
}
break;
case VPPIO_MODULE_ENABLE:
{
vpp_mod_arg_t parm;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_mod_arg_t));
mod_p = vpp_mod_get_base(parm.mod);
if (!mod_p)
break;
if (parm.read) {
/* not used */
} else {
mod_p->set_enable(parm.arg1);
#ifdef WMT_FTBLK_GOVRH_CURSOR
if (parm.mod == VPP_MOD_CURSOR) {
p_cursor->enable = parm.arg1;
if (p_cursor->enable) {
vpp_irqproc_work(VPP_INT_GOVRH_PVBI,
(void *)govrh_CUR_irqproc, 0,
0, 0);
} else {
vpp_irqproc_del_work(VPP_INT_GOVRH_PVBI,
(void *)govrh_CUR_irqproc);
}
}
#endif
}
}
break;
case VPPIO_MODULE_TIMING:
DPRINT("remove for new arch\n");
break;
case VPPIO_MODULE_FBADDR:
{
vpp_mod_arg_t parm;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_mod_arg_t));
mod_fb_p = vpp_mod_get_fb_base(parm.mod);
if (!mod_fb_p)
break;
mod_p = vpp_mod_get_base(parm.mod);
if (parm.read) {
mod_fb_p->get_addr(&parm.arg1, &parm.arg2);
copy_to_user((void *)arg, (void *) &parm,
sizeof(vpp_mod_arg_t));
} else {
mod_fb_p->set_addr(parm.arg1, parm.arg2);
}
}
break;
case VPPIO_MODULE_FBINFO:
{
vpp_mod_fbinfo_t parm;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_mod_fbinfo_t));
if (g_vpp.virtual_display)
parm.mod = (hdmi_get_plugin()) ?
VPP_MOD_GOVRH : VPP_MOD_GOVRH2;
mod_fb_p = vpp_mod_get_fb_base(parm.mod);
if (!mod_fb_p)
break;
mod_p = vpp_mod_get_base(parm.mod);
if (parm.read) {
parm.fb = mod_fb_p->fb;
switch (parm.mod) {
case VPP_MOD_GOVRH:
case VPP_MOD_GOVRH2:
govrh_get_framebuffer((govrh_mod_t *)mod_p,
&parm.fb);
break;
default:
break;
}
copy_to_user((void *)arg, (void *) &parm,
sizeof(vpp_mod_fbinfo_t));
} else {
mod_fb_p->fb = parm.fb;
mod_fb_p->set_framebuf(&parm.fb);
if (g_vpp.virtual_display) {
#ifdef CONFIG_VPP_STREAM_CAPTURE
if (g_vpp.stream_enable)
vpp_mb_scale_bitblit(&mod_fb_p->fb);
else
#endif
{
if(g_vpp.mb[0] != 0)
vpp_free_framebuffer();
}
if (vpp_check_dbg_level(VPP_DBGLVL_FPS))
vpp_dbg_timer(
&vout_info[1].pandisp_timer,
"fbinfo", 2);
}
}
}
break;
case VPPIO_MODULE_VIEW:
{
vpp_mod_view_t parm;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_mod_view_t));
mod_fb_p = vpp_mod_get_fb_base(parm.mod);
if (!mod_fb_p)
break;
mod_p = vpp_mod_get_base(parm.mod);
if (parm.read) {
mod_fb_p->fn_view(VPP_FLAG_RD, &parm.view);
copy_to_user((void *)arg, (void *) &parm,
sizeof(vpp_mod_view_t));
} else {
mod_fb_p->fn_view(0, &parm.view);
}
}
break;
#ifdef CONFIG_VPP_STREAM_CAPTURE
case VPPIO_STREAM_ENABLE:
g_vpp.stream_enable = arg;
g_vpp.stream_mb_sync_flag = 0;
MSG("[VPP] VPPIO_STREAM_ENABLE %d\n", g_vpp.stream_enable);
#ifdef CONFIG_VPP_STREAM_FIX_RESOLUTION
memset(&g_vpp.stream_fb, 0, sizeof(vdo_framebuf_t));
g_vpp.stream_fb.img_w = 1280;
g_vpp.stream_fb.img_h = 720;
g_vpp.stream_fb.fb_w =
vpp_calc_align(g_vpp.stream_fb.img_w, 64);
g_vpp.stream_fb.fb_h = g_vpp.stream_fb.img_h;
g_vpp.stream_fb.bpp = 16;
g_vpp.stream_fb.col_fmt = VDO_COL_FMT_YUV422H;
g_vpp.stream_fb.y_size = g_vpp.stream_fb.fb_w *
g_vpp.stream_fb.fb_h;
g_vpp.stream_fb.c_size = g_vpp.stream_fb.fb_w *
g_vpp.stream_fb.fb_h;
#endif
#ifdef CONFIG_VPP_STREAM_ROTATE
g_vpp.stream_mb_lock= 0;
g_vpp.stream_mb_index = 0xFF;
vpp_netlink_notify(WP_PID, DEVICE_STREAM,
(g_vpp.stream_enable) ? 1 : 0);
#else
#ifndef CONFIG_VPP_DYNAMIC_ALLOC
if (g_vpp.alloc_framebuf == 0) {
#endif
{
if (arg) {
#ifdef CONFIG_VPP_STREAM_FIX_RESOLUTION
vpp_alloc_framebuffer(g_vpp.stream_fb.fb_w,
g_vpp.stream_fb.fb_h);
#else
vout_info_t *info;
info = vout_info_get_entry(0);
vpp_alloc_framebuffer(info->resx, info->resy);
#endif
} else {
vpp_free_framebuffer();
}
}
vpp_mod_set_clock(VPP_MOD_SCL, g_vpp.stream_enable, 0);
vpp_pan_display(0, 0);
if (!g_vpp.stream_enable) {
vpp_lock();
vpp_mb_put(0);
vpp_unlock();
}
#endif
{
#if 0
vpp_int_t type;
type = (vout_info[0].govr_mod == VPP_MOD_GOVRH) ?
VPP_INT_GOVRH_VBIS : VPP_INT_GOVRH2_VBIS;
if (g_vpp.stream_enable) {
vpp_irqproc_work(type, (void *)vpp_mb_irqproc_sync,
vout_info[0].govr, 0, 0);
} else {
vpp_irqproc_del_work(type, (void *)vpp_mb_irqproc_sync);
}
#endif
wmt_enable_mmfreq(WMT_MMFREQ_MIRACAST, g_vpp.stream_enable);
}
break;
case VPPIO_STREAM_GETFB:
{
vdo_framebuf_t fb;
vpp_lock();
#ifdef CONFIG_VPP_STREAM_ROTATE
if (g_vpp.stream_mb_index == 0xFF) { /* not avail */
retval = -1;
vpp_unlock();
break;
}
fb = vout_info[1].fb;
#else
#ifdef CONFIG_VPP_STREAM_FIX_RESOLUTION
fb = g_vpp.stream_fb;
#else
fb = vout_info[0].fb;
#endif
#endif
fb.fb_w = vpp_calc_align(fb.fb_w, 64);
fb.y_addr = g_vpp.stream_mb[g_vpp.stream_mb_index];
fb.y_size = fb.fb_w * fb.img_h;
fb.c_addr = fb.y_addr + fb.y_size;
fb.c_size = fb.y_size;
fb.col_fmt = VDO_COL_FMT_YUV422H;
copy_to_user((void *)arg, (void *) &fb, sizeof(vdo_framebuf_t));
retval = vpp_mb_get(fb.y_addr);
vpp_unlock();
}
break;
case VPPIO_STREAM_PUTFB:
{
vdo_framebuf_t fb;
copy_from_user((void *) &fb, (const void *)arg,
sizeof(vdo_framebuf_t));
vpp_lock();
vpp_mb_put(fb.y_addr);
vpp_unlock();
}
break;
#endif
case VPPIO_MODULE_CSC:
{
vpp_mod_arg_t parm;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_mod_arg_t));
mod_fb_p = vpp_mod_get_fb_base(parm.mod);
if (!mod_fb_p)
break;
mod_p = vpp_mod_get_base(parm.mod);
if (parm.read) {
parm.arg1 = mod_fb_p->csc_mode;
copy_to_user((void *)arg, (void *) &parm,
sizeof(vpp_mod_arg_t));
} else {
mod_fb_p->csc_mode = parm.arg1;
mod_fb_p->set_csc(mod_fb_p->csc_mode);
}
}
break;
case VPPIO_MULTIVD_ENABLE:
wmt_enable_mmfreq(WMT_MMFREQ_MULTI_VD, arg);
break;
default:
retval = -ENOTTY;
break;
}
return retval;
} /* End of vpp_common_ioctl */
int vout_ioctl(unsigned int cmd, unsigned long arg)
{
int retval = 0;
switch (cmd) {
case VPPIO_VOGET_INFO:
{
vpp_vout_info_t parm;
vout_t *vo;
int num;
vout_inf_t *inf;
int fb_num;
vout_info_t *vo_info;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_vout_info_t));
fb_num = (parm.num & 0xF0) >> 4;
num = parm.num & 0xF;
DBGMSG("VPPIO_VOGET_INFO %d,%d\n", fb_num, num);
if ((fb_num >= VPP_VOUT_INFO_NUM) || (num >= VOUT_MODE_MAX)) {
retval = -ENOTTY;
DBG_MSG("fb_num or num invalid\n");
break;
}
memset(&parm, 0, sizeof(vpp_vout_info_t));
vo = vout_get_entry_adapter(num);
if (vo == 0) {
retval = -ENOTTY;
DBG_MSG("vo null\n");
break;
}
inf = vout_get_inf_entry_adapter(num);
if (inf == 0) {
retval = -ENOTTY;
DBG_MSG("inf null\n");
break;
}
vo_info = vout_get_info_entry(vo->num);
if (vo_info == 0) {
retval = -ENOTTY;
DBG_MSG("vo_info null\n");
break;
}
if ((g_vpp.dual_display == 1) && (g_vpp.virtual_display == 0)
&& (fb_num != vo_info->num)) {
retval = -ENOTTY;
DBG_MSG("fb num not match\n");
break;
}
DBGMSG("VPPIO_VOGET_INFO %d,fb%d,0x%x,0x%x\n", num,
vo_info->num, (int)vo, (int)inf);
if ((vo == 0) || (inf == 0))
goto label_get_info;
parm.num = num;
parm.status = vo->status | VPP_VOUT_STS_REGISTER;
strncpy(parm.name, vout_adpt_str[num], 10);
switch (inf->mode) {
case VOUT_INF_DVI:
parm.status &= ~VPP_VOUT_STS_ACTIVE;
if (vo->dev) {
/* check current DVI is dvi/dvo2hdmi/lcd */
switch (num) {
case VOUT_DVI:
if (strcmp("VT1632",
vo->dev->name) == 0)
parm.status |=
VPP_VOUT_STS_ACTIVE;
parm.status |= VPP_VOUT_STS_ACTIVE;
break;
case VOUT_LCD:
if (strcmp("LCD",
vo->dev->name) == 0)
parm.status |=
VPP_VOUT_STS_ACTIVE;
break;
case VOUT_DVO2HDMI:
if (strcmp("SIL902X",
vo->dev->name) == 0)
parm.status |=
VPP_VOUT_STS_ACTIVE;
break;
default:
break;
}
} else { /* dvi hw mode */
if (num == VOUT_DVI)
parm.status |= VPP_VOUT_STS_ACTIVE;
}
if (g_vpp.virtual_display) {
if (vout_chkplug(VPP_VOUT_NUM_HDMI))
parm.status &= ~VPP_VOUT_STS_ACTIVE;
}
break;
case VOUT_INF_HDMI:
case VOUT_INF_LVDS:
/* check current HDMI is HDMI or LVDS */
if (vo->inf != inf)
parm.status = VPP_VOUT_STS_REGISTER;
break;
default:
break;
}
if (parm.status & VPP_VOUT_STS_ACTIVE) {
if (vout_chkplug(vo->num))
parm.status |= VPP_VOUT_STS_PLUGIN;
else
parm.status &= ~VPP_VOUT_STS_PLUGIN;
} else {
parm.status = VPP_VOUT_STS_REGISTER;
}
label_get_info:
copy_to_user((void *)arg, (const void *) &parm,
sizeof(vpp_vout_info_t));
}
break;
case VPPIO_VOSET_MODE:
{
vpp_vout_parm_t parm;
vout_t *vo;
vout_inf_t *inf;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_vout_parm_t));
vo = vout_get_entry_adapter(parm.num);
inf = vout_get_inf_entry_adapter(parm.num);
if (vo && inf) {
DBG_DETAIL("VPPIO_VOSET_MODE %d %d\n",
vo->num, parm.arg);
if (g_vpp.dual_display == 0) {
int plugin;
plugin = vout_chkplug(VPP_VOUT_NUM_HDMI);
vout_set_blank((0x1 << VPP_VOUT_NUM_HDMI),
(plugin) ? 0 : 1);
vout_set_blank((0x1 << VPP_VOUT_NUM_DVI),
(plugin) ? 1 : 0);
break;
}
vout_set_blank((0x1 << vo->num), (parm.arg) ? 0 : 1);
retval = vout_set_mode(vo->num, inf->mode);
}
}
break;
case VPPIO_VOSET_BLANK:
{
vpp_vout_parm_t parm;
vout_t *vo;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_vout_parm_t));
vo = vout_get_entry_adapter(parm.num);
if (vo)
retval = vout_set_blank((0x1 << vo->num), parm.arg);
}
break;
case VPPIO_VOSET_OPTION:
{
vpp_vout_option_t option;
vout_t *vo;
int num;
vout_inf_t *inf;
copy_from_user((void *) &option, (const void *)arg,
sizeof(vpp_vout_option_t));
num = option.num;
if (num >= VOUT_MODE_MAX) {
retval = -ENOTTY;
break;
}
vo = vout_get_entry_adapter(num);
inf = vout_get_inf_entry_adapter(num);
if (vo && inf) {
vo->option[0] = option.option[0];
vo->option[1] = option.option[1];
vo->option[2] = option.option[2];
vout_set_mode(vo->num, inf->mode);
}
}
break;
case VPPIO_VOGET_OPTION:
{
vpp_vout_option_t option;
vout_t *vo;
int num;
copy_from_user((void *) &option, (const void *)arg,
sizeof(vpp_vout_option_t));
num = option.num;
if (num >= VOUT_MODE_MAX) {
retval = -ENOTTY;
break;
}
memset(&option, 0, sizeof(vpp_vout_info_t));
vo = vout_get_entry_adapter(num);
if (vo) {
option.num = num;
option.option[0] = vo->option[0];
option.option[1] = vo->option[1];
option.option[2] = vo->option[2];
}
copy_to_user((void *)arg, (const void *) &option,
sizeof(vpp_vout_option_t));
}
break;
case VPPIO_VOUT_VMODE:
{
vpp_vout_vmode_t parm;
int i;
struct fb_videomode *vmode;
unsigned int resx, resy, fps;
unsigned int pre_resx, pre_resy, pre_fps;
int index, from_index;
int support;
unsigned int option, pre_option;
#ifdef CONFIG_WMT_EDID
edid_info_t *edid_info;
#endif
struct fb_videomode *edid_vmode;
vpp_vout_t mode;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_vout_vmode_t));
from_index = parm.num;
parm.num = 0;
mode = parm.mode & 0xF;
#ifdef CONFIG_VPP_DEMO
parm.parm[parm.num].resx = 1920;
parm.parm[parm.num].resy = 1080;
parm.parm[parm.num].fps = 60;
parm.parm[parm.num].option = 0;
parm.num++;
#else
#ifdef CONFIG_WMT_EDID
{
vout_t *vo;
vo = vout_get_entry_adapter(mode);
if (!vo)
goto vout_vmode_end;
if (!(vo->status & VPP_VOUT_STS_PLUGIN)) {
DPRINT("*W* not plugin\n");
goto vout_vmode_end;
}
if (vout_get_edid(vo->num) == 0) {
DPRINT("*W* read EDID fail\n");
goto vout_vmode_end;
}
if (edid_parse(vo->edid, &vo->edid_info) == 0) {
DPRINT("*W* parse EDID fail\n");
goto vout_vmode_end;
}
edid_info = &vo->edid_info;
}
#endif
index = 0;
resx = resy = fps = option = 0;
pre_resx = pre_resy = pre_fps = pre_option = 0;
for (i = 0; ; i++) {
vmode = (struct fb_videomode *) &vpp_videomode[i];
if (vmode->pixclock == 0)
break;
resx = vmode->xres;
resy = vmode->yres;
fps = vmode->refresh;
option = fps & EDID_TMR_FREQ;
option |= (vmode->vmode & FB_VMODE_INTERLACED) ?
EDID_TMR_INTERLACE : 0;
if ((pre_resx == resx) && (pre_resy == resy) &&
(pre_fps == fps) && (pre_option == option))
continue;
pre_resx = resx;
pre_resy = resy;
pre_fps = fps;
pre_option = option;
support = 0;
#ifdef CONFIG_WMT_EDID
if (edid_find_support(edid_info, resx, resy,
option, &edid_vmode))
#else
if (1)
#endif
support = 1;
switch (mode) {
case VPP_VOUT_HDMI:
case VPP_VOUT_DVO2HDMI:
if (g_vpp.hdmi_video_mode) {
if (resy > g_vpp.hdmi_video_mode)
support = 0;
}
break;
default:
break;
}
if (support) {
if (index >= from_index) {
parm.parm[parm.num].resx = resx;
parm.parm[parm.num].resy = resy;
parm.parm[parm.num].fps = fps;
parm.parm[parm.num].option =
vmode->vmode;
parm.num++;
}
index++;
if (parm.num >= VPP_VOUT_VMODE_NUM)
break;
}
}
#ifdef CONFIG_WMT_EDID
vout_vmode_end:
#endif
#endif
if(parm.num == 0) { /* if no EDID*/
if(g_vpp.virtual_display || (g_vpp.dual_display == 0)) {
if(mode == VPP_VOUT_DVI) {
char buf[40] = {0};
int varlen = 40;
if(wmt_getsyspara("wmt.display.tvformat", buf, &varlen) == 0) {
if(!strnicmp(buf, "PAL", 3) || !strnicmp(buf, "NTSC", 4)) {
parm.parm[0].resx = 720;
parm.parm[0].resy = 576;
parm.parm[0].fps = 50;
parm.parm[0].option = 0;
parm.parm[1].resx = 720;
parm.parm[1].resy = 480;
parm.parm[1].fps = 60;
parm.parm[1].option = 0;
parm.num = 2;
}
}
}
}
}
DBG_MSG("[VPP] get support vmode %d\n", parm.num);
copy_to_user((void *)arg, (const void *) &parm,
sizeof(vpp_vout_vmode_t));
}
break;
case VPPIO_VOGET_EDID:
{
vpp_vout_edid_t parm;
char *edid;
vout_t *vo;
int size;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_vout_edid_t));
size = 0;
#ifdef CONFIG_WMT_EDID
vo = vout_get_entry_adapter(parm.mode);
if (!vo)
goto vout_edid_end;
if (!(vo->status & VPP_VOUT_STS_PLUGIN)) {
DBG_ERR("*W* not plugin\n");
goto vout_edid_end;
}
edid = vout_get_edid(vo->num);
if (edid == 0) {
DBG_ERR("*W* read EDID fail\n");
goto vout_edid_end;
}
size = (edid[0x7E] + 1) * 128;
if (size > parm.size)
size = parm.size;
copy_to_user((void *) parm.buf, (void *) edid, size);
vout_edid_end:
#endif
parm.size = size;
copy_to_user((void *)arg, (const void *) &parm,
sizeof(vpp_vout_edid_t));
}
break;
case VPPIO_VOGET_CP_INFO:
{
vpp_vout_cp_info_t parm;
int num;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_vout_cp_info_t));
num = parm.num;
if (num >= VOUT_MODE_MAX) {
retval = -ENOTTY;
break;
}
memset(&parm, 0, sizeof(vpp_vout_cp_info_t));
switch (num) {
case VOUT_HDMI:
if (!g_vpp.hdmi_certify_flag) {
if (g_vpp.hdmi_bksv[0] == 0) {
hdmi_get_bksv(&g_vpp.hdmi_bksv[0]);
DBG_MSG("get BKSV 0x%x 0x%x\n",
g_vpp.hdmi_bksv[0],
g_vpp.hdmi_bksv[1]);
}
}
case VOUT_DVO2HDMI:
parm.bksv[0] = g_vpp.hdmi_bksv[0];
parm.bksv[1] = g_vpp.hdmi_bksv[1];
break;
default:
parm.bksv[0] = parm.bksv[1] = 0;
break;
}
copy_to_user((void *)arg, (const void *) &parm,
sizeof(vpp_vout_cp_info_t));
}
break;
case VPPIO_VOSET_CP_KEY:
if (g_vpp.hdmi_cp_p == 0) {
g_vpp.hdmi_cp_p =
(char *)kmalloc(sizeof(vpp_vout_cp_key_t),
GFP_KERNEL);
}
if (g_vpp.hdmi_cp_p) {
copy_from_user((void *) g_vpp.hdmi_cp_p,
(const void *)arg, sizeof(vpp_vout_cp_key_t));
if (hdmi_cp)
hdmi_cp->init();
}
break;
#ifdef WMT_FTBLK_HDMI
case VPPIO_VOSET_AUDIO_PASSTHRU:
vppif_reg32_write(HDMI_AUD_SUB_PACKET, (arg) ? 0xF : 0x0);
break;
#endif
#ifdef CONFIG_VPP_VIRTUAL_DISPLAY
case VPPIO_VOSET_VIRTUAL_FBDEV:
g_vpp.fb0_bitblit = (arg) ? 0 : 1;
MSG("[VPP] virtual display %d\n", (int)arg);
break;
#endif
default:
retval = -ENOTTY;
break;
}
return retval;
} /* End of vout_ioctl */
int govr_ioctl(unsigned int cmd, unsigned long arg)
{
int retval = 0;
switch (cmd) {
#ifdef WMT_FTBLK_GOVRH
case VPPIO_GOVRSET_DVO:
{
vdo_dvo_parm_t parm;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vdo_dvo_parm_t));
govrh_set_dvo_enable(p_govrh, parm.enable);
govrh_set_dvo_color_format(p_govrh, parm.color_fmt);
govrh_set_dvo_clock_delay(p_govrh, parm.clk_inv,
parm.clk_delay);
govrh_set_dvo_outdatw(p_govrh, parm.data_w);
govrh_set_dvo_sync_polar(p_govrh, parm.sync_polar,
parm.vsync_polar);
p_govrh->fb_p->set_csc(p_govrh->fb_p->csc_mode);
}
break;
#endif
#ifdef WMT_FTBLK_GOVRH_CURSOR
case VPPIO_GOVRSET_CUR_COLKEY:
{
vpp_mod_arg_t parm;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vdo_dvo_parm_t));
govrh_CUR_set_color_key(p_cursor, VPP_FLAG_ENABLE,
0, parm.arg1);
}
break;
case VPPIO_GOVRSET_CUR_HOTSPOT:
{
vpp_mod_arg_t parm;
vdo_view_t view;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vdo_dvo_parm_t));
p_cursor->hotspot_x = parm.arg1;
p_cursor->hotspot_y = parm.arg2;
view.posx = p_cursor->posx;
view.posy = p_cursor->posy;
p_cursor->fb_p->fn_view(0, &view);
}
break;
#endif
default:
retval = -ENOTTY;
break;
}
return retval;
} /* End of govr_ioctl */
int scl_ioctl(unsigned int cmd, unsigned long arg)
{
int retval = 0;
extern int ge_do_alpha_bitblt(vdo_framebuf_t *src,
vdo_framebuf_t *src2, vdo_framebuf_t *dst);
switch (cmd) {
case VPPIO_SCL_SCALE_OVERLAP:
{
vpp_scale_overlap_t parm;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_scale_overlap_t));
if (vpp_check_dbg_level(VPP_DBGLVL_FPS))
vpp_dbg_timer(&p_scl->overlap_timer, 0, 1);
p_scl->scale_sync = 1;
retval = scl_set_scale_overlap(&parm.src_fb,
&parm.src2_fb, &parm.dst_fb);
if (vpp_check_dbg_level(VPP_DBGLVL_FPS))
vpp_dbg_timer(&p_scl->overlap_timer, "overlap", 2);
}
break;
case VPPIO_SCL_SCALE_ASYNC:
case VPPIO_SCL_SCALE:
{
vpp_scale_t parm;
p_scl->scale_sync = (cmd == VPPIO_SCL_SCALE) ? 1 : 0;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_scale_t));
if (vpp_check_dbg_level(VPP_DBGLVL_FPS))
vpp_dbg_timer(&p_scl->scale_timer, 0, 1);
vpp_set_NA12_hiprio(1);
retval = vpp_set_recursive_scale(&parm.src_fb, &parm.dst_fb);
vpp_set_NA12_hiprio(0);
if (vpp_check_dbg_level(VPP_DBGLVL_FPS))
vpp_dbg_timer(&p_scl->scale_timer, "scale", 2);
copy_to_user((void *)arg, (void *)&parm, sizeof(vpp_scale_t));
}
break;
#ifdef WMT_FTBLK_SCL
case VPPIO_SCL_DROP_LINE_ENABLE:
scl_set_drop_line(arg);
break;
#endif
case VPPIO_SCL_SCALE_FINISH:
retval = p_scl->scale_finish();
break;
case VPPIO_SCLSET_OVERLAP:
{
vpp_overlap_t parm;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(vpp_overlap_t));
vpp_mod_set_clock(VPP_MOD_SCL, VPP_FLAG_ENABLE, 0);
scl_set_overlap(&parm);
vpp_mod_set_clock(VPP_MOD_SCL, VPP_FLAG_DISABLE, 0);
}
break;
default:
retval = -ENOTTY;
break;
}
return retval;
} /* End of scl_ioctl */
int vpp_ioctl(unsigned int cmd, unsigned long arg)
{
int retval = 0;
int err = 0;
int skip_mutex = 0;
/* DBGMSG("vpp_ioctl\n"); */
switch (_IOC_TYPE(cmd)) {
case VPPIO_MAGIC:
break;
default:
return -ENOTTY;
}
/* check argument area */
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void __user *) arg,
_IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void __user *) arg,
_IOC_SIZE(cmd));
if (err)
return -EFAULT;
if (vpp_check_dbg_level(VPP_DBGLVL_IOCTL)) {
switch (cmd) {
case VPPIO_VPPSET_FBDISP:
case VPPIO_VPPGET_FBDISP:
case VPPIO_MODULE_VIEW: /* cursor pos */
break;
default:
DPRINT("[VPP] ioctl cmd 0x%x,arg 0x%x\n",
_IOC_NR(cmd), (int)arg);
break;
}
}
switch (cmd) {
case VPPIO_STREAM_GETFB: /* block mode should wait vsync */
skip_mutex = 1;
break;
default:
/* scale should wait complete */
if ((_IOC_NR(cmd) >= VPPIO_SCL_BASE) &&
(_IOC_NR(cmd) < VPPIO_MAX))
skip_mutex = 1;
break;
}
if (!skip_mutex)
vpp_set_mutex(1, 1);
switch (_IOC_NR(cmd)) {
case VPPIO_VPP_BASE ... (VPPIO_VOUT_BASE-1):
/* DBGMSG("VPP command ioctl\n"); */
retval = vpp_common_ioctl(cmd, arg);
break;
case VPPIO_VOUT_BASE ... (VPPIO_GOVR_BASE-1):
/* DBGMSG("VOUT ioctl\n"); */
retval = vout_ioctl(cmd, arg);
break;
case VPPIO_GOVR_BASE ... (VPPIO_SCL_BASE-1):
/* DBGMSG("GOVR ioctl\n"); */
retval = govr_ioctl(cmd, arg);
break;
case VPPIO_SCL_BASE ... (VPPIO_MAX-1):
/* DBGMSG("SCL ioctl\n"); */
retval = scl_ioctl(cmd, arg);
break;
default:
retval = -ENOTTY;
break;
}
if (!skip_mutex)
vpp_set_mutex(1, 0);
if (vpp_check_dbg_level(VPP_DBGLVL_IOCTL)) {
switch (cmd) {
case VPPIO_VPPSET_FBDISP:
case VPPIO_VPPGET_FBDISP:
case VPPIO_MODULE_VIEW:
break;
default:
DPRINT("[VPP] ioctl cmd 0x%x,ret 0x%x\n",
_IOC_NR(cmd), (int)retval);
break;
}
}
return retval;
} /* End of vpp_ioctl */
int vpp_set_blank(struct fb_info *info, int blank)
{
vout_info_t *vo_info;
unsigned int vout_blank_mask = 0, vout_unblank_mask = 0;
if ((g_vpp.dual_display == 0) && (info->node == 1))
return 0;
DBG_MSG("(%d,%d)\n", info->node, blank);
vo_info = vout_info_get_entry(info->node);
if (blank)
vout_blank_mask = vout_get_mask(vo_info);
else
vout_unblank_mask = vout_get_mask(vo_info);
if (g_vpp.virtual_display || (g_vpp.dual_display == 0)) {
if (blank) {
vout_blank_mask = ~0;
vout_unblank_mask = 0;
} else {
int plugin;
plugin = vout_chkplug(VPP_VOUT_NUM_HDMI);
vout_blank_mask &= ~((0x1 << VPP_VOUT_NUM_DVI) +
(0x1 << VPP_VOUT_NUM_HDMI));
vout_unblank_mask &= ~((0x1 << VPP_VOUT_NUM_DVI) +
(0x1 << VPP_VOUT_NUM_HDMI));
vout_blank_mask |= (plugin) ?
(0x1 << VPP_VOUT_NUM_DVI) :
(0x1 << VPP_VOUT_NUM_HDMI);
vout_unblank_mask |= (plugin) ?
(0x1 << VPP_VOUT_NUM_HDMI) :
(0x1 << VPP_VOUT_NUM_DVI);
}
}
vout_set_blank(vout_blank_mask, 1);
vout_set_blank(vout_unblank_mask, 0);
return 0;
}
void vpp_set_lvds_blank(int blank)
{
if (lcd_get_lvds_id() != LCD_LVDS_1024x600)
return;
if (blank == 0) {
REG32_VAL(GPIO_BASE_ADDR+0xC0) |= 0x400;
REG32_VAL(GPIO_BASE_ADDR+0x80) |= 0x400;
mdelay(6);
}
if (blank)
msleep(50);
vout_set_blank((0x1 << 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 */
REG32_VAL(GPIO_BASE_ADDR + 0xC0) &= ~0x400;
}
}
irqreturn_t vpp_interrupt_routine(int irq, void *dev_id)
{
vpp_int_t int_sts;
switch (irq) {
case VPP_IRQ_VPPM: /* VPP */
int_sts = p_vppm->get_sts();
p_vppm->clr_sts(int_sts);
vpp_dbg_show_val1(VPP_DBGLVL_INT, 0, "[VPP] VPPM INT", int_sts);
{
int i;
unsigned int mask;
vpp_irqproc_t *irqproc;
for (i = 0, mask = 0x1; (i < 32) && int_sts; i++, mask <<= 1) {
if ((int_sts & mask) == 0)
continue;
irqproc = vpp_irqproc_get_entry(mask);
if (irqproc) {
if (list_empty(&irqproc->list) == 0)
tasklet_schedule(&irqproc->tasklet);
} else {
irqproc = vpp_irqproc_get_entry(VPP_INT_MAX);
if (list_empty(&irqproc->list) == 0) {
vpp_proc_t *entry;
struct list_head *ptr;
ptr = (&irqproc->list)->next;
entry = list_entry(ptr, vpp_proc_t,
list);
if (entry->type == mask)
tasklet_schedule(
&irqproc->tasklet);
}
}
int_sts &= ~mask;
}
}
break;
#ifdef WMT_FTBLK_SCL
case VPP_IRQ_SCL: /* SCL */
int_sts = p_scl->get_sts();
p_scl->clr_sts(int_sts);
vpp_dbg_show_val1(VPP_DBGLVL_INT, 0, "[VPP] SCL INT", int_sts);
break;
#endif
#ifdef WMT_FTBLK_GOVRH
case VPP_IRQ_GOVR: /* GOVR */
case VPP_IRQ_GOVR2:
{
govrh_mod_t *govr;
govr = (irq == VPP_IRQ_GOVR) ? p_govrh : p_govrh2;
int_sts = govr->get_sts();
govr->clr_sts(int_sts);
vpp_dbg_show_val1(VPP_DBGLVL_INT, 0, "[VPP] GOVR INT", int_sts);
govr->underrun_cnt++;
#ifdef VPP_DBG_DIAG_NUM
vpp_dbg_show(VPP_DBGLVL_DIAG, 3, "GOVR MIF Err");
vpp_dbg_diag_delay = 10;
#endif
}
break;
#endif
default:
DPRINT("*E* invalid vpp isr\n");
break;
}
return IRQ_HANDLED;
} /* End of vpp_interrupt_routine */
int vpp_dev_init(void)
{
g_vpp.alloc_framebuf = vpp_alloc_framebuffer;
vpp_irqproc_init();
vpp_netlink_init();
vpp_init();
#ifdef CONFIG_VPP_PROC
/* init system proc */
if (vpp_proc_dir == 0) {
struct proc_dir_entry *res;
vpp_proc_dir = proc_mkdir("driver/vpp", NULL);
res = create_proc_entry("sts", 0, vpp_proc_dir);
if (res)
res->read_proc = vpp_sts_read_proc;
res = create_proc_entry("reg", 0, vpp_proc_dir);
if (res)
res->read_proc = vpp_reg_read_proc;
vpp_table_header = register_sysctl_table(vpp_root_table);
}
#endif
/* init interrupt service routine */
#ifdef WMT_FTBLK_SCL
if (vpp_request_irq(VPP_IRQ_SCL, vpp_interrupt_routine,
SA_INTERRUPT, "scl", (void *)&g_vpp)) {
DPRINT("*E* request VPP ISR fail\n");
return -1;
}
#endif
if (vpp_request_irq(VPP_IRQ_VPPM, vpp_interrupt_routine,
SA_INTERRUPT, "vpp", (void *)&g_vpp)) {
DPRINT("*E* request VPP ISR fail\n");
return -1;
}
#ifdef WMT_FTBLK_GOVRH
if (vpp_request_irq(VPP_IRQ_GOVR, vpp_interrupt_routine,
SA_INTERRUPT, "govr", (void *)&g_vpp)) {
DPRINT("*E* request VPP ISR fail\n");
return -1;
}
if (vpp_request_irq(VPP_IRQ_GOVR2, vpp_interrupt_routine,
SA_INTERRUPT, "govr2", (void *)&g_vpp)) {
DPRINT("*E* request VPP ISR fail\n");
return -1;
}
#endif
vpp_switch_state_init();
return 0;
}
int vpp_exit(struct fb_info *info)
{
DBG_MSG("vpp_exit\n");
vout_exit();
#ifdef CONFIG_VPP_PROC
unregister_sysctl_table(vpp_table_header);
#endif
return 0;
}
void vpp_wait_vsync(int no, int cnt)
{
int govr_mask = 0;
if (g_vpp.virtual_display || (g_vpp.dual_display == 0)) {
if (govrh_get_MIF_enable(p_govrh))
govr_mask |= BIT0;
if (govrh_get_MIF_enable(p_govrh2))
govr_mask |= BIT1;
} else {
vout_info_t *vo_info;
vout_t *vo;
int vo_mask;
int i;
vo_info = vout_info_get_entry(no);
vo_mask = vout_get_mask(vo_info);
for (i = 0; i < VPP_VOUT_NUM; i++) {
if ((vo_mask & (0x1 << i)) == 0)
continue;
vo = vout_get_entry(i);
if (vo) {
if (vo->govr == p_govrh)
govr_mask |= BIT0;
if (vo->govr == p_govrh2)
govr_mask |= BIT1;
}
}
}
if (govr_mask) {
#if 0
if (vpp_check_dbg_level(VPP_DBGLVL_FPS))
MSG("[VPP] vpp_wait_vsync %d\n", no);
#endif
vpp_irqproc_work((govr_mask & BIT0) ? VPP_INT_GOVRH_VBIS :
VPP_INT_GOVRH2_VBIS, 0, 0, 100 * cnt, cnt);
}
} /* End of vpp_wait_vsync */
/* struct list_head *vpp_modelist; */
void vpp_get_info(int fbn, struct fb_var_screeninfo *var)
{
static int vpp_init_flag = 1;
vout_info_t *info;
govrh_mod_t *govr;
if (vpp_init_flag) {
#if 0
int num;
INIT_LIST_HEAD(vpp_modelist);
for (num = 0; ; num++) {
if (vpp_videomode[num].xres == 0)
break;
}
fb_videomode_to_modelist(vpp_videomode, num, vpp_modelist);
#endif
vpp_dev_init();
vpp_init_flag = 0;
}
info = vout_info_get_entry(fbn);
govr = vout_info_get_govr(fbn);
if (govr) {
struct fb_videomode vmode;
govrh_get_framebuffer(govr, &info->fb);
govrh_get_videomode(govr, &vmode);
fb_videomode_to_var(var, &vmode);
} else {
var->pixclock =
KHZ2PICOS((info->resx * info->resy * 60) / 1000);
}
var->xres = info->resx;
var->yres = info->resy;
var->xres_virtual = info->fb.fb_w;
var->yres_virtual = var->yres * VPP_MB_ALLOC_NUM;
if (g_vpp.mb_colfmt == VDO_COL_FMT_ARGB) {
var->bits_per_pixel = 32;
var->red.offset = 16;
var->red.length = 8;
var->red.msb_right = 0;
var->green.offset = 8;
var->green.length = 8;
var->green.msb_right = 0;
var->green.offset = 0;
var->green.length = 8;
var->green.msb_right = 0;
var->transp.offset = 24;
var->transp.length = 8;
var->transp.msb_right = 0;
}
DBG_MSG("(%d,%dx%d,%dx%d,%d,%d)\n", fbn, var->xres, var->yres,
var->xres_virtual, var->yres_virtual,
var->pixclock, var->bits_per_pixel);
#ifdef DEBUG
wmtfb_show_var("get_info", var);
#endif
}
void vpp_var_to_fb(struct fb_var_screeninfo *var,
struct fb_info *info, vdo_framebuf_t *fb)
{
extern unsigned int fb_egl_swap;
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;
}
var->xres_virtual = vpp_calc_fb_width(fb->col_fmt, var->xres);
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);
}
if (info && (info->node == 0) && (fb_egl_swap != 0)) /* for Android */
fb->y_addr = fb_egl_swap;
}
int vpp_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
{
vout_info_t *vo_info;
vdo_framebuf_t *uboot_fb = 0;
if (g_vpp.hdmi_certify_flag)
return 0;
vo_info = vout_info_get_entry((info) ? info->node : 0);
DBG_DETAIL("fb %d\n", (info) ? info->node : 0);
if (wmtfb_fb1_probe == 0) {
uboot_fb = kmalloc(sizeof(vdo_framebuf_t), GFP_KERNEL);
*uboot_fb = vo_info->fb;
uboot_fb->img_h = g_vpp.govrh_init_yres;
}
vpp_var_to_fb(var, info, &vo_info->fb);
if (uboot_fb) {
DMSG("uboot copy fb%d\n", info->node);
p_scl->scale_sync = 1;
vpp_set_recursive_scale(uboot_fb, &vo_info->fb);
kfree(uboot_fb);
}
/* for 8:1:0 mode change resolution garbage frame */
if ((g_vpp.virtual_display_mode == 1) && (info->node == 1)) {
vdo_framebuf_t fb;
govrh_get_framebuffer(p_govrh, &fb);
vo_info->fb.y_addr = fb.y_addr;
vo_info->fb.c_addr = 0;
}
vout_set_framebuffer(vout_get_mask(vo_info), &vo_info->fb);
if (g_vpp.fb0_bitblit) {
vdo_framebuf_t src, dst;
struct fb_videomode vmode;
govrh_mod_t *govr;
src = vo_info->fb;
govr = vout_info_get_govr(1);
p_scl->scale_sync = 1;
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 / 8;
#ifdef CONFIG_VPP_DYNAMIC_ALLOC
if (g_vpp.mb[0] == 0)
vpp_alloc_framebuffer(dst.img_w, dst.img_h);
#endif
g_vpp.stream_mb_index = (g_vpp.stream_mb_index) ? 0 : 1;
dst.y_addr = g_vpp.mb[0] + (dst.y_size * g_vpp.stream_mb_index);
dst.c_addr = 0;
vpp_set_recursive_scale(&src, &dst);
vout_set_framebuffer(VPP_VOUT_ALL, &dst);
}
#ifdef CONFIG_VPP_STREAM_CAPTURE
if (g_vpp.stream_enable) {
#ifdef CONFIG_VPP_STREAM_ROTATE
if (info && (info->node == 1)) {
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);
}
#else
if ((info && (info->node == 0)) || (info == 0))
vpp_mb_scale_bitblit(&vo_info->fb);
#endif
}
#endif
if (vo_info->govr && vpp_check_dbg_level(VPP_DBGLVL_DISPFB)) {
char buf[50];
unsigned int yaddr, caddr;
govrh_get_fb_addr(vo_info->govr, &yaddr, &caddr);
sprintf(buf, "pan_display %d,%s,0x%x", vo_info->num,
vpp_mod_str[vo_info->govr_mod], yaddr);
vpp_dbg_show(VPP_DBGLVL_DISPFB, vo_info->num + 1, buf);
}
if (vo_info->govr && vpp_check_dbg_level(VPP_DBGLVL_FPS))
vpp_dbg_timer(&vo_info->pandisp_timer,
(vo_info->num == 0) ? "fb0" : "fb1", 2);
return 0;
}
int vpp_set_par(struct fb_info *info)
{
vout_info_t *vo_info;
unsigned int mask;
int i;
static int count = 2;
if (g_vpp.hdmi_certify_flag)
return 0;
if ((g_vpp.dual_display == 0) && (info->node == 1))
return 0;
if (g_vpp.govrh_preinit)
g_vpp.govrh_preinit = 0;
vpp_set_mutex(info->node, 1);
for (i = 0; i < VPP_VOUT_INFO_NUM; i++) {
vo_info = vout_info_get_entry(i);
mask = vout_get_mask(vo_info);
/* set frame buffer */
if (mask) {
vdo_framebuf_t fb;
vpp_var_to_fb(&info->var, info, &fb);
vo_info->fb.fb_h = fb.fb_h;
if (memcmp(&fb.img_w, &vo_info->fb.img_w, 32)) {
#ifdef DEBUG
DPRINT("[wmtfb_set_par] set_par %d : set framebuf\n",
info->node);
vpp_show_framebuf("cur", &vo_info->fb);
vpp_show_framebuf("new", &fb);
#endif
if (count < 0) {
vo_info->fb = fb;
vout_set_framebuffer(mask, &vo_info->fb);
}
count--;
}
}
/* set timing */
if (vo_info->govr && !(g_vpp.virtual_display && (info->node == 0))) {
struct fb_videomode var, cur;
govrh_mod_t *govr;
unsigned int cur_pixclk, new_pixclk;
govr = vout_info_get_govr(info->node);
fb_var_to_videomode(&var, &info->var);
if(g_vpp.virtual_display) {
if (vout_find_match_mode(info->node, &var, 1)) {
DPRINT("[wmtfb] not support\n");
vpp_set_mutex(info->node, 0);
return -1;
}
fb_videomode_to_var(&info->var, &var);
var.flag = FB_MODE_IS_FROM_VAR;
}
govrh_get_videomode(govr, &cur);
if ((cur.xres == var.xres) && (cur.yres == var.yres)) {
/* 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;
}
if (abs(var.refresh - cur.refresh) <= 2) /* diff less than 2 */
var.refresh = cur.refresh;
}
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
vout_config(mask, vo_info, &var);
}
}
}
vpp_set_mutex(info->node, 0);
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;
} /* End of wmtfb_open */
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;
} /* End of wmtfb_release */
int wmtfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
int temp;
int force = 0;
DBG_DETAIL("Enter %d\n", info->node);
var->xres_virtual = vpp_calc_fb_width((var->bits_per_pixel == 16) ?
VDO_COL_FMT_RGB_565 : VDO_COL_FMT_ARGB, var->xres);
if ((info->node == 1) && g_vpp.alloc_framebuf) {
if( (g_vpp.mb[0] == 0) || ((var->xres != info->var.xres) ||
(var->yres != info->var.yres))) {
if (var->nonstd) {
g_vpp.mb_colfmt = WMT_FB_COLFMT(var->nonstd);
} else {
g_vpp.mb_colfmt = (var->bits_per_pixel == 16) ?
VDO_COL_FMT_RGB_565 : VDO_COL_FMT_ARGB;
}
if (g_vpp.virtual_display_mode == 1) {
#ifdef CONFIG_VPP_DYNAMIC_ALLOC
vpp_free_framebuffer();
#endif
} else {
if (g_vpp.alloc_framebuf(var->xres, var->yres))
return -ENOMEM;
info->fix.smem_start = g_vpp.mb[0];
info->fix.smem_len =
g_vpp.mb_fb_size * VPP_MB_ALLOC_NUM;
info->screen_base =
mb_phys_to_virt(info->fix.smem_start);
}
}
}
if ((var->xres == 0) || (var->yres == 0))
return -1;
temp = (info->fix.smem_len /
(var->xres_virtual * var->yres * (var->bits_per_pixel >> 3)));
if (temp < 2) {
DBG_MSG("smem_len %d,%d\n", info->fix.smem_len, temp);
temp = 2;
}
if (var->yres_virtual > (var->yres * temp))
var->yres_virtual = var->yres * temp;
/* more than 1M is khz not picos (for ut_vpp) */
if (var->pixclock > 1000000) {
temp = KHZ2PICOS(var->pixclock / 1000);
DBG_MSG("pixclock patch(>1000000)%d-->%d(%d)\n",
var->pixclock, temp, (int)PICOS2KHZ(temp) * 1000);
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;
temp = KHZ2PICOS(temp / 1000);
DBG_MSG("pixclock patch(<100)%d-->%d(%d)\n",
var->pixclock, temp, (int)PICOS2KHZ(temp) * 1000);
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 (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;
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;
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;
#ifdef DEBUG
wmtfb_show_var("[wmtfb] time change", var);
#endif
}
return 0;
} /* End of wmtfb_check_var */
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;
info->node = 1;
vpp_set_par(info);
info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
return 0;
} /* End of vt8430fb_set_par */
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;
} /* End of wmtfb_setcolreg */
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");
vpp_set_mutex(1, 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);
vpp_set_mutex(1, 0);
DBG_DETAIL("Exit wmtfb_pan_display\n");
return 0;
} /* End of wmtfb_pan_display */
static int wmtfb_ioctl
(
struct fb_info *info, /*!<; // a pointer point to struct fb_info */
unsigned int cmd, /*!<; // ioctl command */
unsigned long arg /*!<; // a argument pointer */
)
{
int retval = 0;
/* printk("Enter wmtfb_ioctl %x\n",cmd); */
if (_IOC_TYPE(cmd) != VPPIO_MAGIC)
return retval;
unlock_fb_info(info);
retval = vpp_ioctl(cmd, arg);
lock_fb_info(info);
return retval;
} /* End of wmtfb_ioctl */
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;
return 0;
} /* End of wmtfb_mmap */
int wmtfb_hw_cursor(struct fb_info *info, struct fb_cursor *cursor)
{
return 0;
} /* End of wmtfb_hw_cursor */
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;
int cmap_len;
u32 map_size;
char mode_option[20];
sprintf(mode_option, "%dx%d@%d",
VPP_HD_DISP_RESX, VPP_HD_DISP_RESY, VPP_HD_DISP_FPS);
/* Dynamically allocate memory for fb_info and par.*/
info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);
if (!info) {
release_mem_region(info->fix.smem_start, info->fix.smem_len);
return -ENOMEM;
}
/* Set default fb_info */
info->fbops = &wmtfb_ops;
info->fix = wmtfb_fix;
#if 1
if (g_vpp.alloc_framebuf) {
info->fix.smem_start = g_vpp.mb[0];
info->fix.smem_len = g_vpp.mb_fb_size * VPP_MB_ALLOC_NUM;
info->screen_base = mb_phys_to_virt(info->fix.smem_start);
}
#endif
#if 0
/* Set video memory */
if (!request_mem_region(info->fix.smem_start,
info->fix.smem_len, "wmtfb")) {
printk(KERN_WARNING
"wmtfb: abort, cannot reserve video memory at 0x%lx\n",
info->fix.smem_start);
}
info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
if (!info->screen_base) {
printk(KERN_ERR
"wmtfb: abort, cannot ioremap video memory 0x%x @ 0x%lx\n",
info->fix.smem_len, info->fix.smem_start);
return -EIO;
}
#endif
printk(KERN_INFO "wmtfb: framebuffer at 0x%lx, mapped to 0x%p, "
"using %d, total %d\n",
info->fix.smem_start, info->screen_base,
info->fix.smem_len, info->fix.smem_len);
/*
* Do as a normal fbdev does, but allocate a larger memory for GE.
*/
map_size = PAGE_ALIGN(info->fix.smem_len);
/*
* The pseudopalette is an 16-member array for fbcon.
*/
info->pseudo_palette = info->par;
info->par = NULL;
info->flags = FBINFO_DEFAULT; /* flag for fbcon. */
/*
* This has to been done !!!
*/
cmap_len = 256; /* Be the same as VESA. */
fb_alloc_cmap(&info->cmap, cmap_len, 0);
/*
* The following is done in the case of
* having hardware with a static mode.
*/
info->var = wmtfb_var;
vpp_get_info(1, &info->var);
/*
* This should give a reasonable default video mode.
*/
/*
* For drivers that can...
*/
wmtfb_check_var(&info->var, info);
/*
* It's safe to allow fbcon to do it for you.
* But in this case, we need it here.
*/
wmtfb_set_par(info);
if (register_framebuffer(info) < 0)
return -EINVAL;
info->dev->power.async_suspend = 1;
printk(KERN_INFO "fb%d: %s frame buffer device\n",
info->node, info->fix.id);
dev_set_drvdata(&dev->dev, info);
wmtfb_fb1_probe = 1;
return 0;
} /* End of wmtfb_probe */
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
unsigned int vpp_vout_blank_mask;
static int wmtfb_suspend
(
struct platform_device *pDev, /*!<; // a pointer struct device */
pm_message_t state /*!<; // suspend state */
)
{
vpp_mod_base_t *mod_p;
vout_t *vo;
int i;
DBGMSG("Enter wmtfb_suspend\n");
vpp_vout_blank_mask = 0;
for (i = 0; i <= VPP_VOUT_NUM; i++) {
vo = vout_get_entry(i);
if (vo && !(vo->status & VPP_VOUT_STS_BLANK))
vpp_vout_blank_mask |= (0x1 << i);
}
vout_set_blank(VPP_VOUT_ALL, VOUT_BLANK_POWERDOWN);
if (vout_check_plugin(1))
vpp_netlink_notify_plug(VPP_VOUT_ALL ,0);
else
wmt_set_mmfreq(0);
/* disable module */
for (i = 0; i < VPP_MOD_MAX; i++) {
mod_p = vpp_mod_get_base(i);
if (mod_p && mod_p->suspend)
mod_p->suspend(0);
}
#ifdef WMT_FTBLK_HDMI
hdmi_suspend(0);
#endif
wmt_suspend_mmfreq();
#ifdef WMT_FTBLK_LVDS
lvds_suspend(0);
#endif
/* disable tg */
for (i = 0; i < VPP_MOD_MAX; i++) {
mod_p = vpp_mod_get_base(i);
if (mod_p && mod_p->suspend)
mod_p->suspend(1);
}
#ifdef WMT_FTBLK_HDMI
hdmi_suspend(1);
#endif
#ifdef WMT_FTBLK_LVDS
lvds_suspend(1);
#endif
/* backup registers */
for (i = 0; i < VPP_MOD_MAX; i++) {
mod_p = vpp_mod_get_base(i);
if (mod_p && mod_p->suspend)
mod_p->suspend(2);
}
#ifdef WMT_FTBLK_HDMI
hdmi_suspend(2);
#endif
#ifdef WMT_FTBLK_LVDS
lvds_suspend(2);
#endif
#if 0
if (lcd_get_lvds_id() == LCD_LVDS_1024x600) {
mdelay(5);
/* GPIO10 off 8ms -> clock -> off */
REG32_VAL(GPIO_BASE_ADDR + 0xC0) &= ~0x400;
}
#endif
return 0;
} /* End of wmtfb_suspend */
static int wmtfb_resume
(
struct platform_device *pDev /*!<; // a pointer struct device */
)
{
vpp_mod_base_t *mod_p;
int i;
#if 0
if (lcd_get_lvds_id() == LCD_LVDS_1024x600) {
/* GPIO10 6ms -> clock r0.02.04 */
REG32_VAL(GPIO_BASE_ADDR+0x80) |= 0x400;
REG32_VAL(GPIO_BASE_ADDR+0xC0) |= 0x400;
}
#endif
DBGMSG("Enter wmtfb_resume\n");
/* restore registers */
for (i = 0; i < VPP_MOD_MAX; i++) {
mod_p = vpp_mod_get_base(i);
if (mod_p && mod_p->resume)
mod_p->resume(0);
}
#ifdef WMT_FTBLK_LVDS
lvds_resume(0);
#endif
#ifdef WMT_FTBLK_HDMI
hdmi_check_plugin(0);
hdmi_resume(0);
#endif
/* enable tg */
for (i = 0; i < VPP_MOD_MAX; i++) {
mod_p = vpp_mod_get_base(i);
if (mod_p && mod_p->resume)
mod_p->resume(1);
}
#ifdef WMT_FTBLK_LVDS
lvds_resume(1);
#endif
#ifdef WMT_FTBLK_HDMI
hdmi_resume(1);
#endif
/* wait */
if(!(g_vpp.virtual_display || g_vpp.dual_display == 0))
msleep(150);
/* enable module */
for (i = 0; i < VPP_MOD_MAX; i++) {
mod_p = vpp_mod_get_base(i);
if (mod_p && mod_p->resume)
mod_p->resume(2);
}
#ifdef WMT_FTBLK_LVDS
lvds_resume(2);
#endif
if (lcd_get_lvds_id() != LCD_LVDS_1024x600)
vout_set_blank(vpp_vout_blank_mask, VOUT_BLANK_UNBLANK);
wmt_resume_mmfreq();
if (vout_check_plugin(0))
vpp_netlink_notify_plug(VPP_VOUT_ALL, 1);
else {
wmt_set_mmfreq(0);
}
#ifdef WMT_FTBLK_HDMI
hdmi_resume(2);
#endif
return 0;
} /* End of wmtfb_resume */
static void wmtfb_shutdown
(
struct platform_device *pDev /*!<; // a pointer struct device */
)
{
DPRINT("wmtfb_shutdown\n");
hdmi_set_power_down(1);
lvds_set_power_down(1);
}
#else
#define wmtfb_suspend NULL
#define wmtfb_resume NULL
#define wmtfb_shutdown 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,
.shutdown = wmtfb_shutdown,
};
/***************************************************************************
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;
} /* End of wmtfb_init */
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;
} /* End of wmtfb_exit */
module_exit(wmtfb_exit);
MODULE_AUTHOR("WonderMedia SW Team");
MODULE_DESCRIPTION("wmtfb device driver");
MODULE_LICENSE("GPL");
/*--------------------End of Function Body -----------------------------------*/
#undef WMTFB_C