/*++
* linux/drivers/video/wmt/wmt-vpp.c
* WonderMedia video post processor (VPP) 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 DEV_VPP_C
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#undef DEBUG
/* #define DEBUG */
/* #define DEBUG_DETAIL */
#include "vpp.h"
/*----------------------- PRIVATE MACRO --------------------------------------*/
/*----------------------- PRIVATE CONSTANTS ----------------------------------*/
/* #define WMT_VPP_XXXX 1 *//*Example*/
#define DEVICE_NAME "wmt-vpp"
/*----------------------- PRIVATE TYPE --------------------------------------*/
/* typedef xxxx wmt_vpp_xxx_t; *//*Example*/
/*----------------------- INTERNAL PRIVATE VARIABLES - -----------------------*/
/* int wmt_vpp_xxx; *//*Example*/
static DEFINE_SEMAPHORE(wmt_vpp_sem);
static struct class *wmt_vpp_class;
static int wmt_vpp_major;
/*--------------------- INTERNAL PRIVATE FUNCTIONS ---------------------------*/
/* void wmt_vpp_xxx(void); *//*Example*/
/*----------------------- Function Body --------------------------------------*/
#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;
case 18:
vpp_proc_value = g_vpp.fb_manual;
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:
{
struct 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;
case 18:
g_vpp.fb_manual = 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,
},
{ /* .ctl_name = 18 */
.procname = "fb_manual",
.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)
{
struct govrh_regs *regs = (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", scl_regs1->tg_sts.b.tgerr);
p += sprintf(p, "SCLR MIF1 read error %d\n",
scl_regs1->r_fifo_ctl.b.r1_mif_err);
p += sprintf(p, "SCLR MIF2 read error %d\n",
scl_regs1->r_fifo_ctl.b.r2_mif_err);
p += sprintf(p, "SCLW RGB fifo overflow %d\n",
scl_regs1->w_ff_ctl.b.mif_rgb_err);
p += sprintf(p, "SCLW Y fifo overflow %d\n",
scl_regs1->w_ff_ctl.b.mif_y_err);
p += sprintf(p, "SCLW C fifo overflow %d\n",
scl_regs1->w_ff_ctl.b.mif_c_err);
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 = inl(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");
#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;
}
static int vpp_reg_read_proc(char *buf, char **start, off_t offset,
int len, int *eof, void *data)
{
char *p = buf;
struct 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;
}
#endif
irqreturn_t vpp_interrupt_routine(int irq, void *dev_id)
{
enum 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;
struct 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) {
struct vpp_proc_t *entry;
struct list_head *ptr;
ptr = (&irqproc->list)->next;
entry = list_entry(ptr,
struct 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:
{
struct 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;
}
void vpp_wait_vsync(int no, int cnt)
{
struct govrh_mod_t *govr;
govr = vout_info_get_govr(no);
if (govr) {
if (govrh_get_MIF_enable(govr)) {
vpp_irqproc_work((govr->mod == VPP_MOD_GOVRH) ?
VPP_INT_GOVRH_VBIS : VPP_INT_GOVRH2_VBIS,
0, 0, 100 * cnt, cnt);
if (vpp_check_dbg_level(VPP_DBGLVL_DISPFB))
MSG("wait vsync(%d,%d)\n", no, cnt);
}
}
}
/*----------------------- vpp ioctl --------------------------------------*/
int vpp_common_ioctl(unsigned int cmd, unsigned long arg)
{
struct vpp_mod_base_t *mod_p;
struct 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_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(
(struct 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);
}
}
break;
#ifdef CONFIG_VPP_STREAM_CAPTURE
case VPPIO_STREAM_ENABLE:
g_vpp.stream_enable = arg;
g_vpp.stream_mb_sync_flag = 0;
g_vpp.stream_mb_lock = 0;
g_vpp.stream_mb_index = 0xFF;
MSG("VPPIO_STREAM_ENABLE %d\n", g_vpp.stream_enable);
vpp_netlink_notify(WP_PID, DEVICE_STREAM,
(g_vpp.stream_enable) ? 1 : 0);
wmt_enable_mmfreq(WMT_MMFREQ_MIRACAST, g_vpp.stream_enable);
break;
case VPPIO_STREAM_GETFB:
{
vdo_framebuf_t fb;
vpp_lock();
if (g_vpp.stream_mb_index == 0xFF) { /* not avail */
retval = -1;
vpp_unlock();
break;
}
fb = vout_info[g_vpp.stream_fb]->fb;
fb.fb_h = vpp_calc_align(fb.img_h, 16);
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_MULTIVD_ENABLE:
wmt_enable_mmfreq(WMT_MMFREQ_MULTI_VD, arg);
break;
case VPPIO_MODSET_CSC:
{
struct vpp_mod_parm_t parm;
copy_from_user((void *) &parm, (const void *)arg,
sizeof(struct vpp_mod_parm_t));
switch (parm.mod) {
case VPP_MOD_GOVRH:
p_govrh->csc_mode_force = parm.parm;
break;
case VPP_MOD_GOVRH2:
if (p_govrh2->csc_mode_force != parm.parm)
MSG("GOVRH2 CSC %d-->%d\n",
p_govrh2->csc_mode_force, parm.parm);
p_govrh2->csc_mode_force = parm.parm;
break;
default:
break;
}
}
break;
default:
retval = -ENOTTY;
break;
}
return retval;
}
int vpp_vout_ioctl(unsigned int cmd, unsigned long arg)
{
int retval = 0;
switch (cmd) {
case VPPIO_VOGET_INFO:
{
vpp_vout_info_t parm;
struct vout_info_t *info;
int fb_no, i, idx;
unsigned int status;
memset(&parm, 0, sizeof(vpp_vout_info_t));
for (fb_no = 0, idx = 0; ; fb_no++) {
info = vout_info_get_entry(fb_no);
if (!info)
break;
for (i = 0; i < VPP_VOUT_NUM; i++, idx++) {
if (info->vout[i] == 0)
break;
parm.mode[idx] = vout_get_mode_adapter(
info->vout[i]);
status = info->vout[i]->status |
(fb_no << 8);
DBGMSG("GET_INFO(fb %d,mode %d,sts 0x%x)",
fb_no, parm.mode[idx], status);
DBGMSG("plug %d\n",
(status & VPP_VOUT_STS_PLUGIN) ? 1 : 0);
parm.status[idx] = status;
}
}
copy_to_user((void *)arg, (const void *) &parm,
sizeof(vpp_vout_info_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
struct 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
{
struct 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++) {
int ret = 0;
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 (ret = edid_find_support(edid_info, resx, resy,
option, &edid_vmode))
#else
if (1)
#endif
support = 1;
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;
#ifdef CONFIG_WMT_EDID
if(ret == 3)
parm.parm[parm.num].option |= 0x80;
#endif
parm.num++;
}
index++;
if (parm.num >= VPP_VOUT_VMODE_NUM)
break;
}
}
#ifdef CONFIG_WMT_EDID
vout_vmode_end:
#endif
#endif
#if 0
if (parm.num == 0) { /* if no EDID*/
enum vout_tvformat_t tvformat = vout_get_tvformat();
if (g_vpp.virtual_display ||
(g_vpp.dual_display == 0)) {
if (mode == VPP_VOUT_DVI) {
switch (tvformat) {
case TV_PAL:
parm.parm[0].resx = 720;
parm.parm[0].resy = 576;
parm.parm[0].fps = 50;
parm.num = 1;
parm.parm[0].option = 0;
break;
case TV_NTSC:
parm.parm[0].resx = 720;
parm.parm[0].resy = 480;
parm.parm[0].fps = 60;
parm.num = 1;
parm.parm[0].option = 0;
break;
default:
break;
}
}
}
}
#endif
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;
struct 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 = 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:
hdmi_regs1->aud_mode.b.sub_packet = (arg) ? 0xF : 0x0;
break;
#endif
#ifdef CONFIG_VPP_VIRTUAL_DISPLAY
case VPPIO_VOSET_VIRTUAL_FBDEV:
{
struct vout_info_t *info;
int i;
g_vpp.fb0_bitblit = (arg) ? 0 : 1;
for (i = 1; ; i++) {
info = vout_info_get_entry(i);
if (!info)
break;
if (info->alloc_mode != VOUT_ALLOC_GE_OVERSCAN)
continue;
if (info->mb == 0)
continue;
MSG("fb%d mb free 0x%x\n", i, info->mb);
mb_free(info->mb);
info->mb = 0;
}
MSG("[VPP] virtual display %d\n", (int)arg);
}
break;
#endif
case VPPIO_VOGET_HDMI_3D:
{
struct vpp_vout_parm_s parm;
struct vout_t *vo;
vo = vout_get_entry(VPP_VOUT_NUM_HDMI);
parm.arg = edid_get_hdmi_3d_mask(&vo->edid_info, hdmi_info.vic);
copy_to_user((void *)arg, (const void *) &parm,
sizeof(struct vpp_vout_parm_s));
}
break;
default:
retval = -ENOTTY;
break;
}
return retval;
}
int vpp_scl_ioctl(unsigned int cmd, unsigned long arg)
{
int retval = 0;
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;
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;
}
static int wmt_vpp_open(struct inode *inode, struct file *filp)
{
DBG_MSG("\n");
return 0;
}
static int wmt_vpp_release(struct inode *inode, struct file *filp)
{
DBG_MSG("\n");
return 0;
}
static long wmt_vpp_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int ret = -EINVAL;
int skip_mutex = 0;
/* DBG_MSG("0x%x,0x%x\n", cmd, (int)arg); */
if (_IOC_TYPE(cmd) != VPPIO_MAGIC)
return -ENOTTY;
if (_IOC_NR(cmd) > VPPIO_MAX)
return -ENOTTY;
/* check argument area */
if (_IOC_DIR(cmd) & _IOC_READ)
ret = !access_ok(VERIFY_WRITE,
(void __user *) arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
ret = !access_ok(VERIFY_READ,
(void __user *) arg, _IOC_SIZE(cmd));
else
ret = 0;
if (ret)
return -EFAULT;
if (vpp_check_dbg_level(VPP_DBGLVL_IOCTL)) {
switch (cmd) {
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)
down(&wmt_vpp_sem);
switch (_IOC_NR(cmd)) {
case VPPIO_VPP_BASE ... (VPPIO_VOUT_BASE-1):
/* DBGMSG("VPP command ioctl\n"); */
ret = vpp_common_ioctl(cmd, arg);
break;
case VPPIO_VOUT_BASE ... (VPPIO_SCL_BASE - 1):
ret = vpp_vout_ioctl(cmd, arg);
break;
case VPPIO_SCL_BASE ... (VPPIO_MAX-1):
/* DBGMSG("SCL ioctl\n"); */
ret = vpp_scl_ioctl(cmd, arg);
break;
default:
DBG_ERR("*W* cmd 0x%x\n", cmd);
break;
}
if (!skip_mutex)
up(&wmt_vpp_sem);
if (vpp_check_dbg_level(VPP_DBGLVL_IOCTL)) {
switch (cmd) {
default:
DPRINT("[VPP] ioctl cmd 0x%x,ret 0x%x\n",
_IOC_NR(cmd), (int)ret);
break;
}
}
return ret;
}
const struct file_operations wmt_vpp_fops = {
.owner = THIS_MODULE,
.open = wmt_vpp_open,
.release = wmt_vpp_release,
.unlocked_ioctl = wmt_vpp_ioctl,
};
static int wmt_vpp_probe(struct platform_device *dev)
{
DBG_MSG("\n");
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;
}
static int wmt_vpp_remove(struct platform_device *dev)
{
return 0;
}
#ifdef CONFIG_PM
unsigned int wmt_vpp_vout_blank_mask;
static int wmt_vpp_suspend(struct device *dev)
{
struct vpp_mod_base_t *mod_p;
struct vout_t *vo;
int i;
DPRINT("Enter wmt_vpp_suspend\n");
wmt_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))
wmt_vpp_vout_blank_mask |= (0x1 << i);
vout_set_blank(i, VOUT_BLANK_POWERDOWN);
if (vo && vo->dev && vo->dev->suspend)
vo->dev->suspend();
}
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 */
outl(inl(GPIO_BASE_ADDR + 0xC0) & ~BIT10,
GPIO_BASE_ADDR + 0xC0);
}
#endif
return 0;
}
static int wmt_vpp_freeze(struct device *dev)
{
return 0;
}
#ifdef CONFIG_VPP_SHENZHEN
extern int lcd_spi_resume(void);
#endif
static int wmt_vpp_resume(struct device *dev)
{
struct vpp_mod_base_t *mod_p;
int i;
#if 0
if (lcd_get_lvds_id() == LCD_LVDS_1024x600) {
/* GPIO10 6ms -> clock r0.02.04 */
outl(inl(GPIO_BASE_ADDR + 0x80) | BIT10,
GPIO_BASE_ADDR + 0x80);
outl(inl(GPIO_BASE_ADDR + 0xC0) | BIT10,
GPIO_BASE_ADDR + 0xC0);
}
#endif
DPRINT("Enter wmt_vpp_resume\n");
#ifdef CONFIG_VPP_SHENZHEN
lcd_spi_resume();
#endif
/* 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 */
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) {
for (i = 0; i < VPP_VOUT_NUM; i++) {
struct vout_t *vo;
vo = vout_get_entry(i);
if (vo && vo->dev) {
if (lcd_get_type() == 0) /* DVI type */
govrh_set_dvo_enable(vo->govr, 1);
vo->dev->init(vo);
vo->dev->set_mode(&vo->option[0]);
vo->inf->init(vo, 0);
}
if (wmt_vpp_vout_blank_mask & (0x1 << i))
vout_set_blank(i, VOUT_BLANK_UNBLANK);
if (vo && vo->dev && vo->dev->resume)
vo->dev->resume();
}
}
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;
}
static int wmt_vpp_restore(struct device *dev)
{
return 0;
}
static void wmt_vpp_shutdown
(
struct platform_device *pDev /*!<; // a pointer struct device */
)
{
DPRINT("wmt_vpp_shutdown\n");
hdmi_set_power_down(1);
lvds_set_power_down(1);
}
#else
#define wmt_vpp_suspend NULL
#define wmt_vpp_resume NULL
#define wmt_vpp_shutdown NULL
#endif
/********************************************************************
device driver struct define
**********************************************************************/
static struct dev_pm_ops wmt_vpp_pm_ops = {
.suspend = wmt_vpp_suspend,
.resume = wmt_vpp_resume,
.freeze = wmt_vpp_freeze,
.thaw = wmt_vpp_restore,
.restore = wmt_vpp_restore,
};
static struct platform_driver wmt_vpp_driver = {
.driver.name = DEVICE_NAME, /* equal to platform device name. */
/* .bus = &platform_bus_type, */
.probe = wmt_vpp_probe,
.remove = wmt_vpp_remove,
/* .suspend = wmt_vpp_suspend, */
/* .resume = wmt_vpp_resume, */
.shutdown = wmt_vpp_shutdown,
.driver.pm = &wmt_vpp_pm_ops
};
static void wmt_vpp_platform_release(struct device *device)
{
}
/********************************************************************
platform device struct define
*********************************************************************/
/* static u64 wmt_vpp_dma_mask = 0xffffffffUL; */
static struct platform_device wmt_vpp_device = {
.name = DEVICE_NAME,
.id = 0,
.dev = {
.release = wmt_vpp_platform_release,
#if 0
.dma_mask = &wmt_vpp_dma_mask,
.coherent_dma_mask = ~0,
#endif
},
.num_resources = 0, /* ARRAY_SIZE(cipher_resources), */
.resource = NULL, /* cipher_resources, */
};
static int __init wmt_vpp_init(void)
{
int ret;
dev_t dev_no;
wmt_vpp_major = register_chrdev(0, DEVICE_NAME, &wmt_vpp_fops);
if (wmt_vpp_major < 0) {
DBG_ERR("get major failed\n");
return -EFAULT;
}
wmt_vpp_class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(wmt_vpp_class)) {
ret = PTR_ERR(wmt_vpp_class);
DBG_ERR("Can't create class : %s !!\n", DEVICE_NAME);
return ret;
}
dev_no = MKDEV(wmt_vpp_major, 0);
device_create(wmt_vpp_class, NULL, dev_no, NULL, DEVICE_NAME);
ret = platform_device_register(&wmt_vpp_device);
if (ret != 0)
return -ENODEV;
ret = platform_driver_register(&wmt_vpp_driver);
if (ret != 0) {
platform_device_unregister(&wmt_vpp_device);
return -ENODEV;
}
return 0;
}
static void __exit wmt_vpp_exit(void)
{
dev_t dev_no;
DBG_MSG("\n");
vout_exit();
#ifdef CONFIG_VPP_PROC
unregister_sysctl_table(vpp_table_header);
#endif
platform_driver_unregister(&wmt_vpp_driver);
platform_device_unregister(&wmt_vpp_device);
dev_no = MKDEV(wmt_vpp_major, 0);
device_destroy(wmt_vpp_class, dev_no);
class_destroy(wmt_vpp_class);
unregister_chrdev(wmt_vpp_major, DEVICE_NAME);
return;
}
module_init(wmt_vpp_init);
module_exit(wmt_vpp_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("WMT VPP driver");
MODULE_AUTHOR("WMT TECH");
MODULE_VERSION("1.0.0");