/*++ * 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