/*++
* linux/drivers/video/wmt/osif.c
* WonderMedia video post processor (VPP) 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 VPP_OSIF_C
#undef DEBUG
/* #define DEBUG */
/* #define DEBUG_DETAIL */
/*----------------------- DEPENDENCE -----------------------------------------*/
#include "vpp.h"
/*----------------------- PRIVATE MACRO --------------------------------------*/
/*----------------------- PRIVATE CONSTANTS ----------------------------------*/
/* #define LVDS_XXXX 1 *//*Example*/
/*----------------------- PRIVATE TYPE --------------------------------------*/
/* typedef xxxx lvds_xxx_t; *//*Example*/
/*----------EXPORTED PRIVATE VARIABLES are defined in lvds.h -------------*/
/*----------------------- INTERNAL PRIVATE VARIABLES - -----------------------*/
/* int lvds_xxx; *//*Example*/
#ifdef __KERNEL__
static DEFINE_SPINLOCK(vpp_irqlock);
static unsigned long vpp_lock_flags;
#endif
int vpp_dvi_i2c_id;
/*--------------------- INTERNAL PRIVATE FUNCTIONS ---------------------------*/
/* void lvds_xxx(void); *//*Example*/
/*----------------------- Function Body --------------------------------------*/
#ifdef __KERNEL__
#include
#include
#else
__inline__ U32 inl(U32 offset)
{
return REG32_VAL(offset);
}
__inline__ void outl(U32 val, U32 offset)
{
REG32_VAL(offset) = val;
}
static __inline__ U16 inw(U32 offset)
{
return REG16_VAL(offset);
}
static __inline__ void outw(U16 val, U32 offset)
{
REG16_VAL(offset) = val;
}
static __inline__ U8 inb(U32 offset)
{
return REG8_VAL(offset);
}
static __inline__ void outb(U8 val, U32 offset)
{
REG8_VAL(offset) = val;
}
#ifndef CFG_LOADER
int get_key(void)
{
int key;
key = get_num(0, 256, "Input:", 5);
DPRINT("\n");
return key;
}
void udelay(int us)
{
vpp_post_delay(us);
}
void mdelay(int ms)
{
udelay(ms * 1000);
}
#endif
#endif
extern unsigned int wmt_read_oscr(void);
void vpp_udelay(unsigned int us)
{
#if 1
udelay(us);
#else
unsigned int cur;
unsigned int cross;
if (us == 0)
return;
cur = wmt_read_oscr();
us = cur + us * 3;
cross = (us < cur) ? cur : 0; /* check overflow */
while (1) {
if (cross) {
if (cur < cross)
cross = 0;
} else {
if (us < cur)
break;
}
cur = wmt_read_oscr();
}
#endif
}
/* Internal functions */
U8 vppif_reg8_in(U32 offset)
{
return inb(offset);
}
U8 vppif_reg8_out(U32 offset, U8 val)
{
outb(val, offset);
return val;
}
U16 vppif_reg16_in(U32 offset)
{
return inw(offset);
}
U16 vppif_reg16_out(U32 offset, U16 val)
{
outw(val, offset);
return val;
}
U32 vppif_reg32_in(U32 offset)
{
return inl(offset);
}
U32 vppif_reg32_out(U32 offset, U32 val)
{
outl(val, offset);
return val;
}
U32 vppif_reg32_write(U32 offset, U32 mask, U32 shift, U32 val)
{
U32 new_val;
#if 0
if (val > (mask >> shift))
MSG("*E* check the parameter 0x%x 0x%x 0x%x 0x%x\n",
offset, mask, shift, val);
#endif
new_val = (inl(offset) & ~(mask)) | (((val) << (shift)) & mask);
outl(new_val, offset);
return new_val;
}
U32 vppif_reg32_read(U32 offset, U32 mask, U32 shift)
{
return (inl(offset) & mask) >> shift;
}
U32 vppif_reg32_mask(U32 offset, U32 mask, U32 shift)
{
return mask;
}
int vpp_request_irq(unsigned int irq_no, void *routine,
unsigned int flags, char *name, void *arg)
{
#if 0 /* disable irq */
return 0;
#endif
#ifdef __KERNEL__
if (request_irq(irq_no, routine, flags, name, arg)) {
DPRINT("[VPP] *E* request irq %s fail\n", name);
return -1;
}
#endif
return 0;
}
void vpp_free_irq(unsigned int irq_no, void *arg)
{
#ifdef __KERNEL__
free_irq(irq_no, arg);
#endif
}
#ifndef __KERNEL__
int wmt_getsyspara(char *varname, char *varval, int *varlen)
{
int i = 0;
char *p;
p = getenv(varname);
if (!p) {
printf("## Warning: %s not defined\n", varname);
return -1;
}
while (p[i] != '\0') {
varval[i] = p[i];
i++;
}
varval[i] = '\0';
*varlen = i;
/* printf("getsyspara: %s,len %d\n", p, *varlen); */
return 0;
}
#endif
#ifndef CONFIG_VPOST
int vpp_parse_param(char *buf, unsigned int *param,
int cnt, unsigned int hex_mask)
{
char *p;
char *endp;
int i = 0;
if (*buf == '\0')
return 0;
for (i = 0; i < cnt; i++)
param[i] = 0;
p = (char *)buf;
for (i = 0; i < cnt; i++) {
param[i] = simple_strtoul(p, &endp,
(hex_mask & (0x1 << i)) ? 16 : 0);
if (*endp == '\0')
break;
p = endp + 1;
if (*p == '\0')
break;
}
return i + 1;
}
#endif
unsigned int vpp_lock_cnt;
void vpp_lock_l(void)
{
#ifdef __KERNEL__
#if 0
vpp_lock_cnt++;
DPRINT("vpp_lock(%d)\n", vpp_lock_cnt);
#endif
spin_lock_irqsave(&vpp_irqlock, vpp_lock_flags);
#else
#endif
}
void vpp_unlock(void)
{
#ifdef __KERNEL__
#if 0
vpp_lock_cnt--;
DPRINT("vpp_unlock(%d)\n", vpp_lock_cnt);
#endif
spin_unlock_irqrestore(&vpp_irqlock, vpp_lock_flags);
#else
#endif
}
#ifdef __KERNEL__
struct i2c_adapter *vpp_i2c_adapter;
struct i2c_client *vpp_i2c_client;
struct vpp_i2c_priv {
unsigned int var;
};
static int __devinit vpp_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct vpp_i2c_priv *vpp_i2c;
int ret = 0;
DBGMSG("\n");
if (vpp_i2c_client == 0)
return -ENODEV;
if (!i2c_check_functionality(vpp_i2c_client->adapter, I2C_FUNC_I2C)) {
DBG_ERR("need I2C_FUNC_I2C\n");
return -ENODEV;
}
vpp_i2c = kzalloc(sizeof(struct vpp_i2c_priv), GFP_KERNEL);
if (vpp_i2c == NULL) {
DBG_ERR("kzalloc fail\n");
return -ENOMEM;
}
i2c_set_clientdata(i2c, vpp_i2c);
return ret;
}
static int vpp_i2c_remove(struct i2c_client *client)
{
kfree(i2c_get_clientdata(client));
return 0;
}
static const struct i2c_device_id vpp_i2c_id[] = {
{ "vpp_i2c", 0},
{ }
};
MODULE_DEVICE_TABLE(i2c, vpp_i2c_id);
static struct i2c_driver vpp_i2c_driver = {
.probe = vpp_i2c_probe,
.remove = vpp_i2c_remove,
.id_table = vpp_i2c_id,
.driver = { .name = "vpp_i2c", },
};
static struct i2c_board_info vpp_i2c_board_info = {
.type = "vpp_i2c",
.flags = 0x00,
.platform_data = NULL,
.archdata = NULL,
.irq = -1,
};
#endif
#ifdef __KERNEL__
int vpp_i2c_xfer(struct i2c_msg *msg, unsigned int num, int bus_id)
#else
int vpp_i2c_xfer(struct i2c_msg_s *msg, unsigned int num, int bus_id)
#endif
{
int ret = 1;
#ifdef __KERNEL__
int i = 0;
#if 0
if (bus_id == 1) {
ret = wmt_i2c_xfer_continue_if_4(msg, num, bus_id);
return ret;
}
#endif
if (num > 1) {
for (i = 0; i < num - 1; ++i)
msg[i].flags |= I2C_M_NOSTART;
}
ret = i2c_transfer(vpp_i2c_adapter, msg, num);
if (ret <= 0) {
DBG_ERR("i2c fail\n");
return ret;
}
#elif defined(CONFIG_VPOST)
ret = i2c_transfer(msg, 1);
#else
ret = wmt_i2c_transfer(msg, num, bus_id);
#endif
return ret;
}
int vpp_i2c_init(int i2c_id, unsigned short addr)
{
#ifdef __KERNEL__
struct i2c_board_info *vpp_i2c_bi = &vpp_i2c_board_info;
int ret = 0;
DBGMSG("id %d,addr 0x%x\n", i2c_id, addr);
vpp_dvi_i2c_id = i2c_id;
if (i2c_id & VPP_DVI_I2C_SW_BIT)
return 0;
i2c_id &= VPP_DVI_I2C_ID_MASK;
vpp_i2c_adapter = i2c_get_adapter(i2c_id);
if (vpp_i2c_adapter == NULL) {
DBG_ERR("can not get i2c adapter, client address error\n");
return -ENODEV;
}
vpp_i2c_bi->addr = addr >> 1;
vpp_i2c_client = i2c_new_device(vpp_i2c_adapter, vpp_i2c_bi);
if (vpp_i2c_client == NULL) {
DBG_ERR("allocate i2c client failed\n");
return -ENOMEM;
}
i2c_put_adapter(vpp_i2c_adapter);
ret = i2c_add_driver(&vpp_i2c_driver);
if (ret)
DBG_ERR("i2c_add_driver fail\n");
return ret;
#else
vpp_dvi_i2c_id = i2c_id & 0xF;
return 0;
#endif
}
int vpp_i2c_release(void)
{
#ifdef __KERNEL__
if (vpp_i2c_client) {
i2c_del_driver(&vpp_i2c_driver);
i2c_unregister_device(vpp_i2c_client);
vpp_i2c_remove(vpp_i2c_client);
vpp_i2c_client = 0;
}
#else
#endif
return 0;
}
#ifdef __KERNEL__
static DEFINE_SEMAPHORE(vpp_i2c_sem);
#endif
void vpp_i2c_set_lock(int lock)
{
#ifdef __KERNEL__
if (lock)
down(&vpp_i2c_sem);
else
up(&vpp_i2c_sem);
#endif
}
int vpp_i2c_lock;
int vpp_i2c_enhanced_ddc_read(int id, unsigned int addr,
unsigned int index, char *pdata, int len)
{
#ifdef __KERNEL__
struct i2c_msg msg[3];
#else
struct i2c_msg_s msg[3];
#endif
unsigned char buf[len + 1];
unsigned char buf2[2];
int ret = 0;
vpp_i2c_set_lock(1);
if (vpp_i2c_lock)
DBG_ERR("in lock\n");
vpp_i2c_lock = 1;
if (id & VPP_DVI_I2C_BIT)
id = vpp_dvi_i2c_id;
id = id & VPP_DVI_I2C_ID_MASK;
buf2[0] = 0x1;
buf2[1] = 0x0;
msg[0].addr = (0x60 >> 1);
msg[0].flags = 0 ;
msg[0].flags &= ~(I2C_M_RD);
msg[0].len = 1;
msg[0].buf = buf2;
addr = (addr >> 1);
memset(buf, 0x55, len + 1);
buf[0] = index;
buf[1] = 0x0;
msg[1].addr = addr;
msg[1].flags = 0 ;
msg[1].flags &= ~(I2C_M_RD);
msg[1].len = 1;
msg[1].buf = buf;
msg[2].addr = addr;
msg[2].flags = 0 ;
msg[2].flags |= (I2C_M_RD);
msg[2].len = len;
msg[2].buf = buf;
ret = vpp_i2c_xfer(msg, 3, id);
memcpy(pdata, buf, len);
vpp_i2c_lock = 0;
vpp_i2c_set_lock(0);
return ret;
} /* End of vpp_i2c_enhanced_ddc_read */
EXPORT_SYMBOL(vpp_i2c_enhanced_ddc_read);
int vpp_i2c_read(int id, unsigned int addr,
unsigned int index, char *pdata, int len)
{
int ret = 0;
DBG_DETAIL("(%d,0x%x,%d,%d)\n", id, addr, index, len);
vpp_i2c_set_lock(1);
if (vpp_i2c_lock)
DBG_ERR("in lock\n");
vpp_i2c_lock = 1;
if (id & VPP_DVI_I2C_BIT)
id = vpp_dvi_i2c_id;
id = id & VPP_DVI_I2C_ID_MASK;
switch (id) {
case 0 ... 0xF: /* hw i2c */
{
#ifdef CONFIG_KERNEL
struct i2c_msg msg[2];
#else
struct i2c_msg_s msg[2] ;
#endif
unsigned char buf[len + 1];
addr = (addr >> 1);
buf[0] = index;
buf[1] = 0x0;
msg[0].addr = addr; /* slave address */
msg[0].flags = 0 ;
msg[0].flags &= ~(I2C_M_RD);
msg[0].len = 1;
msg[0].buf = buf;
msg[1].addr = addr;
msg[1].flags = I2C_M_RD;
msg[1].len = len;
msg[1].buf = buf;
ret = vpp_i2c_xfer(msg, 2, id);
memcpy(pdata, buf, len);
}
break;
default:
#ifdef CONFIG_KERNEL
ret = vo_i2c_proc((id & 0xF), (addr | BIT0), index, pdata, len);
#endif
break;
}
#ifdef DEBUG
{
int i;
DBGMSG("vpp_i2c_read(addr 0x%x,index 0x%x,len %d\n", addr, index, len);
for (i = 0; i < len; i += 8) {
DBGMSG("%d : 0x%02x 0x%02x 0x%02x 0x%02x",
i, pdata[i], pdata[i + 1], pdata[i + 2], pdata[i + 3]);
DBGMSG(" 0x%02x 0x%02x 0x%02x 0x%02x\n",
pdata[i + 4], pdata[i + 5], pdata[i + 6], pdata[i + 7]);
}
}
#endif
vpp_i2c_lock = 0;
vpp_i2c_set_lock(0);
return ret;
}
EXPORT_SYMBOL(vpp_i2c_read);
int vpp_i2c_write(int id, unsigned int addr, unsigned int index,
char *pdata, int len)
{
int ret = 0;
DBG_DETAIL("(%d,0x%x,%d,%d)\n", id, addr, index, len);
vpp_i2c_set_lock(1);
if (vpp_i2c_lock)
DBG_ERR("in lock\n");
vpp_i2c_lock = 1;
if (id & VPP_DVI_I2C_BIT)
id = vpp_dvi_i2c_id;
id = id & VPP_DVI_I2C_ID_MASK;
switch (id) {
case 0 ... 0xF: /* hw i2c */
{
#ifdef CONFIG_KERNEL
struct i2c_msg msg[1];
#else
struct i2c_msg_s msg[1] ;
#endif
unsigned char buf[len + 1];
addr = (addr >> 1);
buf[0] = index;
memcpy(&buf[1], pdata, len);
msg[0].addr = addr; /* slave address */
msg[0].flags = 0 ;
msg[0].flags &= ~(I2C_M_RD);
msg[0].len = len + 1;
msg[0].buf = buf;
ret = vpp_i2c_xfer(msg, 1, id);
}
break;
default:
#ifdef CONFIG_KERNEL
vo_i2c_proc((id & 0xF), (addr & ~BIT0), index, pdata, len);
#endif
break;
}
#ifdef DEBUG
{
int i;
DBGMSG("vpp_i2c_write(addr 0x%x,index 0x%x,len %d\n", addr, index, len);
for (i = 0; i < len; i += 8) {
DBGMSG("%d : 0x%02x 0x%02x 0x%02x 0x%02x",
i, pdata[i], pdata[i + 1], pdata[i + 2], pdata[i + 3]);
DBGMSG(" 0x%02x 0x%02x 0x%02x 0x%02x\n",
pdata[i + 4], pdata[i + 5], pdata[i + 6], pdata[i + 7]);
}
}
#endif
vpp_i2c_lock = 0;
vpp_i2c_set_lock(0);
return ret;
}
EXPORT_SYMBOL(vpp_i2c_write);
void DelayMS(int ms)
{
mdelay(ms);
}
EXPORT_SYMBOL(DelayMS);
/*----------------------- VPP debug --------------------------------------*/
#define VPP_DEBUG_FUNC
#ifdef VPP_DEBUG_FUNC
#define VPP_DBG_TMR_NUM 3
/* #define VPP_DBG_DIAG_NUM 100 */
#ifdef VPP_DBG_DIAG_NUM
char vpp_dbg_diag_str[VPP_DBG_DIAG_NUM][100];
int vpp_dbg_diag_index;
int vpp_dbg_diag_delay;
#endif
int vpp_check_dbg_level(vpp_dbg_level_t level)
{
if (level == VPP_DBGLVL_ALL)
return 1;
switch (g_vpp.dbg_msg_level) {
case VPP_DBGLVL_DISABLE:
break;
case VPP_DBGLVL_ALL:
return 1;
default:
if (g_vpp.dbg_msg_level == level)
return 1;
break;
}
return 0;
}
void vpp_dbg_show(int level, int tmr, char *str)
{
#ifdef __KERNEL__
static struct timeval pre_tv[VPP_DBG_TMR_NUM];
struct timeval tv;
unsigned int tm_usec = 0;
if (vpp_check_dbg_level(level) == 0)
return;
if (tmr && (tmr <= VPP_DBG_TMR_NUM)) {
do_gettimeofday(&tv);
if (pre_tv[tmr - 1].tv_sec)
tm_usec = (tv.tv_sec == pre_tv[tmr - 1].tv_sec) ?
(tv.tv_usec - pre_tv[tmr - 1].tv_usec) :
(1000000 + tv.tv_usec - pre_tv[tmr - 1].tv_usec);
pre_tv[tmr - 1] = tv;
}
#ifdef VPP_DBG_DIAG_NUM
if (level == VPP_DBGLVL_DIAG) {
if (str) {
char *ptr = &vpp_dbg_diag_str[vpp_dbg_diag_index][0];
sprintf(ptr, "%s (%d,%d)(T%d %d usec)", str,
(int)tv.tv_sec, (int)tv.tv_usec, tmr,
(int) tm_usec);
vpp_dbg_diag_index = (vpp_dbg_diag_index + 1)
% VPP_DBG_DIAG_NUM;
}
if (vpp_dbg_diag_delay) {
vpp_dbg_diag_delay--;
if (vpp_dbg_diag_delay == 0) {
int i;
DPRINT("----- VPP DIAG -----\n");
for (i = 0; i < VPP_DBG_DIAG_NUM; i++) {
DPRINT("%02d : %s\n", i,
&vpp_dbg_diag_str[vpp_dbg_diag_index][0]);
vpp_dbg_diag_index = (vpp_dbg_diag_index + 1)
% VPP_DBG_DIAG_NUM;
}
}
}
return;
}
#endif
if (str) {
if (tmr)
DPRINT("[VPP] %s (T%d period %d usec)\n", str,
tmr - 1, (int) tm_usec);
else
DPRINT("[VPP] %s\n", str);
}
#else
if (vpp_check_dbg_level(level) == 0)
return;
if (str)
DPRINT("[VPP] %s\n", str);
#endif
} /* End of vpp_dbg_show */
void vpp_dbg_show_val1(int level, int tmr, char *str, int val)
{
if (vpp_check_dbg_level(level)) {
char buf[50];
sprintf(buf, "%s 0x%x", str, val);
vpp_dbg_show(level, tmr, buf);
}
}
#ifdef __KERNEL__
static DECLARE_WAIT_QUEUE_HEAD(vpp_dbg_wq);
void vpp_dbg_wait(char *str)
{
DPRINT("[VPP] vpp_dbg_wait(%s)\n", str);
wait_event_interruptible(vpp_dbg_wq, (g_vpp.dbg_wait));
g_vpp.dbg_wait = 0;
DPRINT("[VPP] Exit vpp_dbg_wait\n");
}
void vpp_dbg_wake_up(void)
{
wake_up(&vpp_dbg_wq);
}
int vpp_dbg_get_period_usec(vpp_dbg_period_t *p, int cmd)
{
struct timeval tv;
int tm_usec = 0;
if (p == 0)
return 0;
do_gettimeofday(&tv);
if (p->pre_tv.tv_sec)
tm_usec = (tv.tv_sec == p->pre_tv.tv_sec) ?
(tv.tv_usec - p->pre_tv.tv_usec) :
(1000000 + tv.tv_usec - p->pre_tv.tv_usec);
p->pre_tv = tv;
if (cmd == 0) { /* reset */
p->index = 0;
memset(&p->period_us, 0, VPP_DBG_PERIOD_NUM);
} else if (p->index < VPP_DBG_PERIOD_NUM) {
p->period_us[p->index] = tm_usec;
p->index++;
}
if (cmd == 2) { /* show */
int i, sum = 0;
DPRINT("[VPP] period");
for (i = 0; i < VPP_DBG_PERIOD_NUM; i++) {
DPRINT(" %d", p->period_us[i]);
sum += p->period_us[i];
}
DPRINT(",sum %d\n", sum);
}
return tm_usec;
}
void vpp_dbg_timer(vpp_dbg_timer_t *p, char *str, int cmd)
{
struct timeval tv;
int tm_usec = 0;
int initial = 0;
if (p == 0)
return;
if (p->reset == 0) { /* default */
p->reset = 150;
initial = 1;
}
do_gettimeofday(&tv);
if (p->pre_tv.tv_sec)
tm_usec = (tv.tv_sec == p->pre_tv.tv_sec) ?
(tv.tv_usec - p->pre_tv.tv_usec) :
(1000000 + tv.tv_usec - p->pre_tv.tv_usec);
p->pre_tv = tv;
switch (cmd) {
case 0: /* initial */
initial = 1;
break;
case 1: /* start */
break;
case 2: /* end */
p->cnt++;
p->sum += tm_usec;
if (p->min > tm_usec)
p->min = tm_usec;
if (p->max < tm_usec)
p->max = tm_usec;
if (p->threshold && (tm_usec >= p->threshold))
MSG("%s tmr %d over %d\n", str, tm_usec, p->threshold);
if (p->cnt >= p->reset) {
int us_1t;
us_1t = p->sum / p->cnt;
MSG("%s(Cnt %d)Sum %d us,Avg %d,Min %d,Max %d,fps %d.%02d\n",
str, p->cnt, p->sum, us_1t,
p->min, p->max, 1000000 / us_1t,
(100000000 / us_1t) % 100);
initial = 1;
}
break;
default:
break;
}
if (initial) {
p->cnt = 0;
p->sum = 0;
p->min = ~0;
p->max = 0;
}
}
#endif
#else
void vpp_dbg_show(int level, int tmr, char *str) {}
static void vpp_dbg_show_val1(int level, int tmr, char *str, int val) {}
void vpp_dbg_wait(char *str) {}
#endif
#if 0
static void load_regs(struct pt_regs *ptr)
{
asm volatile(
"stmia %0, {r0 - r15}\n\t"
:
: "r" (ptr)
: "memory"
);
}
void vpp_dbg_back_trace(void)
{
struct pt_regs *ptr;
unsigned int fp;
unsigned long flags;
ptr = kmalloc(sizeof(struct pt_regs), GFP_KERNEL);
local_irq_save(flags);
MSG("\n\nstart back trace...\n");
load_regs(ptr);
fp = ptr->ARM_fp;
c_backtrace(fp, 0x1f);
MSG("back trace end...\n\n");
local_irq_restore(flags);
kfree(ptr);
}
EXPORT_SYMBOL(vpp_dbg_back_trace);
#endif