diff options
author | Srikant Patnaik | 2015-01-11 12:28:04 +0530 |
---|---|---|
committer | Srikant Patnaik | 2015-01-11 12:28:04 +0530 |
commit | 871480933a1c28f8a9fed4c4d34d06c439a7a422 (patch) | |
tree | 8718f573808810c2a1e8cb8fb6ac469093ca2784 /drivers/video/wmt/vpp.c | |
parent | 9d40ac5867b9aefe0722bc1f110b965ff294d30d (diff) | |
download | FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.gz FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.bz2 FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.zip |
Moved, renamed, and deleted files
The original directory structure was scattered and unorganized.
Changes are basically to make it look like kernel structure.
Diffstat (limited to 'drivers/video/wmt/vpp.c')
-rwxr-xr-x | drivers/video/wmt/vpp.c | 1413 |
1 files changed, 1413 insertions, 0 deletions
diff --git a/drivers/video/wmt/vpp.c b/drivers/video/wmt/vpp.c new file mode 100755 index 00000000..544d9dbf --- /dev/null +++ b/drivers/video/wmt/vpp.c @@ -0,0 +1,1413 @@ +/*++ + * linux/drivers/video/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 <http://www.gnu.org/licenses/>. + * + * WonderMedia Technologies, Inc. + * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C +--*/ + +#define VPP_C +#undef DEBUG +/* #define DEBUG */ +/* #define DEBUG_DETAIL */ + +#include "vpp.h" + +struct vpp_mod_base_t *vpp_mod_base_list[VPP_MOD_MAX]; + +unsigned int vpp_get_chipid(void) +{ + /* byte 3,2: chip id, byte 1:ver id, byte 0:sub id */ + /* ex: 0x34290101 (0x3429 A0), 0x34290102 (0x3429 A1) */ + return inl(SYSTEM_CFG_CTRL_BASE_ADDR); +} + +inline void vpp_cache_sync(void) +{ + /* TODO */ +} + +void vpp_set_clock_enable(enum dev_id dev, int enable, int force) +{ +#ifdef CONFIG_VPP_DISABLE_PM + return; +#else + int cnt; + + do { + cnt = auto_pll_divisor(dev, + (enable) ? CLK_ENABLE : CLK_DISABLE, 0, 0); + if (enable) { + if (cnt) + break; + } else { + if (cnt == 0) + break; + } + } while (force); +/* MSG("%s(%d,%d,%d)\n", __FUNCTION__, dev, enable, cnt); */ +#endif +} + +/*----------------------- vpp module --------------------------------------*/ +void vpp_mod_unregister(vpp_mod_t mod) +{ + struct vpp_mod_base_t *mod_p; + + if (mod >= VPP_MOD_MAX) + return; + + mod_p = vpp_mod_base_list[mod]; + if (!mod_p) + return; + + kfree(mod_p->fb_p); + kfree(mod_p); + vpp_mod_base_list[mod] = 0; +} + +struct vpp_mod_base_t *vpp_mod_register(vpp_mod_t mod, + int size, unsigned int flags) +{ + struct vpp_mod_base_t *mod_p; + + if (mod >= VPP_MOD_MAX) + return 0; + + if (vpp_mod_base_list[mod]) + vpp_mod_unregister(mod); + + mod_p = kmalloc(size, GFP_KERNEL); + if (!mod_p) + return 0; + + vpp_mod_base_list[mod] = mod_p; + memset(mod_p, 0, size); + mod_p->mod = mod; + + if (flags & VPP_MOD_FLAG_FRAMEBUF) { + mod_p->fb_p = kmalloc(sizeof(struct vpp_fb_base_t), GFP_KERNEL); + if (!mod_p->fb_p) + goto error; + memset(mod_p->fb_p, 0, sizeof(struct vpp_fb_base_t)); + } + DBG_DETAIL(" %d,0x%x,0x%x\n", mod, (int)mod_p, (int)mod_p->fb_p); + return mod_p; +error: + vpp_mod_unregister(mod); + DPRINT("vpp mod register NG %d\n", mod); + return 0; +} + +struct vpp_mod_base_t *vpp_mod_get_base(vpp_mod_t mod) +{ + if (mod >= VPP_MOD_MAX) + return 0; + return vpp_mod_base_list[mod]; +} + +struct vpp_fb_base_t *vpp_mod_get_fb_base(vpp_mod_t mod) +{ + struct vpp_mod_base_t *mod_p; + mod_p = vpp_mod_get_base(mod); + if (mod_p) + return mod_p->fb_p; + return 0; +} + +vdo_framebuf_t *vpp_mod_get_framebuf(vpp_mod_t mod) +{ + struct vpp_mod_base_t *mod_p; + + mod_p = vpp_mod_get_base(mod); + if (mod_p && mod_p->fb_p) + return &mod_p->fb_p->fb; + return 0; +} + +void vpp_mod_set_clock(vpp_mod_t mod, vpp_flag_t enable, int force) +{ + struct vpp_mod_base_t *base; + enum dev_id pll_dev; + int cur_sts; + int ret; + +#ifdef CONFIG_VPP_DISABLE_PM + return; +#endif + + base = vpp_mod_get_base(mod); + if (base == 0) + return; + + pll_dev = (base->pm & 0xFF); + if (pll_dev == 0) + return; + + enable = (enable) ? VPP_FLAG_ENABLE : VPP_FLAG_DISABLE; + if (force) { + ret = auto_pll_divisor(pll_dev, + (enable) ? CLK_ENABLE : CLK_DISABLE, 0, 0); + DBG_DETAIL("[VPP] clk force(%s,%d),ret %d\n", + vpp_mod_str[mod], enable, ret); + return; + } + + cur_sts = (base->pm & VPP_MOD_CLK_ON) ? 1 : 0; + if (cur_sts != enable) { + ret = auto_pll_divisor(pll_dev, + (enable) ? CLK_ENABLE : CLK_DISABLE, 0, 0); + base->pm = (enable) ? (base->pm | VPP_MOD_CLK_ON) + : (base->pm & ~VPP_MOD_CLK_ON); + DBG_MSG("[VPP] clk enable(%s,%d,cur %d),ret %d\n", + vpp_mod_str[mod], enable, cur_sts, ret); + } +} + +vpp_display_format_t vpp_get_fb_field(vdo_framebuf_t *fb) +{ + if (fb->flag & VDO_FLAG_INTERLACE) + return VPP_DISP_FMT_FIELD; + return VPP_DISP_FMT_FRAME; +} + +unsigned int vpp_get_base_clock(vpp_mod_t mod) +{ + unsigned int clock = 0; + + switch (mod) { + default: + clock = auto_pll_divisor(DEV_VPP, GET_FREQ, 0, 0); + break; + case VPP_MOD_GOVRH: + clock = (p_govrh->vo_clock == 0) ? + auto_pll_divisor(DEV_HDMILVDS, GET_FREQ, 0, 0) : + p_govrh->vo_clock; + break; + case VPP_MOD_GOVRH2: + clock = (p_govrh2->vo_clock == 0) ? + auto_pll_divisor(DEV_DVO, GET_FREQ, 0, 0) : + p_govrh2->vo_clock; + break; + } + DBG_DETAIL("%d %d\n", mod, clock); + return clock; +} + +void vpp_show_timing(char *str, struct fb_videomode *vmode, + vpp_clock_t *clk) +{ + DPRINT("----- %s timing -----\n", str); + if (vmode) { + int pixclk; + + pixclk = (vmode->pixclock) ? PICOS2KHZ(vmode->pixclock) : 0; + pixclk *= 1000; + + DPRINT("res(%d,%d),fps %d\n", + vmode->xres, vmode->yres, vmode->refresh); + DPRINT("pixclk %d(%d),hsync %d,vsync %d\n", vmode->pixclock, + pixclk, vmode->hsync_len, vmode->vsync_len); + DPRINT("left %d,right %d,upper %d,lower %d\n", + vmode->left_margin, vmode->right_margin, + vmode->upper_margin, vmode->lower_margin); + DPRINT("vmode 0x%x,sync 0x%x\n", vmode->vmode, vmode->sync); + } + + if (clk) { + DPRINT("H beg %d,end %d,total %d\n", clk->begin_pixel_of_active, + clk->end_pixel_of_active, clk->total_pixel_of_line); + DPRINT("V beg %d,end %d,total %d\n", clk->begin_line_of_active, + clk->end_line_of_active, clk->total_line_of_frame); + DPRINT("Hsync %d, Vsync %d\n", clk->hsync, clk->vsync); + DPRINT("VBIE %d,PVBI %d\n", clk->line_number_between_VBIS_VBIE, + clk->line_number_between_PVBI_VBIS); + } + DPRINT("-----------------------\n"); +} + +void vpp_show_framebuf(char *str, vdo_framebuf_t *fb) +{ + if (fb == 0) + return; + DPRINT("----- %s framebuf -----\n", str); + DPRINT("Y addr 0x%x, size %d\n", fb->y_addr, fb->y_size); + DPRINT("C addr 0x%x, size %d\n", fb->c_addr, fb->c_size); + DPRINT("W %d, H %d, FB W %d, H %d\n", fb->img_w, fb->img_h, + fb->fb_w, fb->fb_h); + DPRINT("bpp %d, color fmt %s\n", fb->bpp, vpp_colfmt_str[fb->col_fmt]); + DPRINT("H crop %d, V crop %d, flag 0x%x\n", + fb->h_crop, fb->v_crop, fb->flag); + DPRINT("-----------------------\n"); +} + +void vpp_show_videomode(char *str, struct fb_videomode *v) +{ + if (v == 0) + return; + DPRINT("----- %s videomode -----\n", str); + DPRINT("%dx%d@%d,%d\n", v->xres, v->yres, v->refresh, v->pixclock); + DPRINT("h sync %d,bp %d,fp %d\n", v->hsync_len, + v->left_margin, v->right_margin); + DPRINT("v sync %d,bp %d,fp %d\n", v->vsync_len, + v->upper_margin, v->lower_margin); + DPRINT("sync 0x%x,vmode 0x%x,flag 0x%x\n", v->sync, v->vmode, v->flag); + DPRINT("hsync %s,vsync %s\n", + (v->sync & FB_SYNC_HOR_HIGH_ACT) ? "hi" : "lo", + (v->sync & FB_SYNC_VERT_HIGH_ACT) ? "hi" : "lo"); + DPRINT("interlace %d,double %d\n", + (v->vmode & FB_VMODE_INTERLACED) ? 1 : 0, + (v->vmode & FB_VMODE_DOUBLE) ? 1 : 0); + DPRINT("-----------------------\n"); +} + +vpp_csc_t vpp_check_csc_mode(vpp_csc_t mode, vdo_color_fmt src_fmt, + vdo_color_fmt dst_fmt, unsigned int flags) +{ + if (mode >= VPP_CSC_MAX) + return VPP_CSC_BYPASS; + + mode = (mode >= VPP_CSC_RGB2YUV_MIN) ? + (mode - VPP_CSC_RGB2YUV_MIN) : mode; + if (src_fmt >= VDO_COL_FMT_ARGB) { + mode = VPP_CSC_RGB2YUV_MIN + mode; + src_fmt = VDO_COL_FMT_ARGB; + } else { + src_fmt = VDO_COL_FMT_YUV444; + } + dst_fmt = (dst_fmt >= VDO_COL_FMT_ARGB) ? + VDO_COL_FMT_ARGB : VDO_COL_FMT_YUV444; + if (flags == 0) + mode = (src_fmt != dst_fmt) ? mode : VPP_CSC_BYPASS; + return mode; +} + +int vpp_get_gcd(int A, int B) +{ + while (A != B) { + if (A > B) + A = A - B; + else + B = B - A; + } + return A; +} + +int vpp_set_recursive_scale(vdo_framebuf_t *src_fb, + vdo_framebuf_t *dst_fb) +{ +#ifdef WMT_FTBLK_SCL + int ret; + + ret = p_scl->scale(src_fb, dst_fb); + return ret; +#else + DBG_ERR("No scale\n"); + return 0; +#endif +} + +unsigned int vpp_convert_colfmt(int yuv2rgb, unsigned int data) +{ + unsigned int r, g, b; + unsigned int y, u, v; + unsigned int alpha; + + alpha = data & 0xff000000; + if (yuv2rgb) { + y = (data & 0xff0000) >> 16; + u = (data & 0xff00) >> 8; + v = (data & 0xff) >> 0; + + r = ((1000 * y) + 1402 * (v - 128)) / 1000; + if (r > 0xFF) + r = 0xFF; + g = ((100000 * y) - (71414 * (v - 128)) + - (34414 * (u - 128))) / 100000; + if (g > 0xFF) + g = 0xFF; + b = ((1000 * y) + (1772 * (u - 128))) / 1000; + if (b > 0xFF) + b = 0xFF; + + data = ((r << 16) + (g << 8) + b); + } else { + r = (data & 0xff0000) >> 16; + g = (data & 0xff00) >> 8; + b = (data & 0xff) >> 0; + + y = ((2990 * r) + (5870 * g) + (1440 * b)) / 10000; + if (y > 0xFF) + y = 0xFF; + u = (1280000 - (1687 * r) - (3313 * g) + (5000 * b)) / 10000; + if (u > 0xFF) + u = 0xFF; + v = (1280000 + (5000 * r) - (4187 * g) - (813 * b)) / 10000; + if (v > 0xFF) + v = 0xFF; + + data = ((y << 16) + (v << 8) + u); + } + data = data + alpha; + return data; +} + +void vpp_get_sys_parameter(void) +{ +#ifndef CONFIG_VPOST + char buf[40]; + int varlen = 40; +#else + struct env_para_def param; +#endif + + /* vpp attribute by default */ + g_vpp.dbg_msg_level = 0; + g_vpp.hdmi_audio_interface = VPP_HDMI_AUDIO_SPDIF; + g_vpp.hdmi_cp_enable = 1; + +#ifdef CONFIG_KERNEL + if (govrh_get_MIF_enable(p_govrh)) + g_vpp.govrh_preinit = 1; + if (govrh_get_MIF_enable(p_govrh2)) + g_vpp.govrh_preinit = 1; + MSG("[VPP] govrh preinit %d\n", g_vpp.govrh_preinit); +#else + g_vpp.govrh_preinit = 0; + p_scl->scale_sync = 1; +#endif + +#ifndef CONFIG_VPOST + if (wmt_getsyspara("wmt.display.hdmi_audio_inf", buf, &varlen) == 0) { + if (memcmp(buf, "i2s", 3) == 0) + g_vpp.hdmi_audio_interface = VPP_HDMI_AUDIO_I2S; + else if (memcmp(buf, "spdif", 5) == 0) + g_vpp.hdmi_audio_interface = VPP_HDMI_AUDIO_SPDIF; + } + + g_vpp.mb_colfmt = VPP_UBOOT_COLFMT; + /* [uboot parameter] fb param : no:xresx:yres:xoffset:yoffset */ + if (wmt_getsyspara("wmt.gralloc.param", buf, &varlen) == 0) { + unsigned int parm[1]; + + vpp_parse_param(buf, (unsigned int *)parm, 1, 0x1); + if (parm[0] == 32) + g_vpp.mb_colfmt = VDO_COL_FMT_ARGB; + MSG("mb colfmt : %s,%s\n", buf, + vpp_colfmt_str[g_vpp.mb_colfmt]); + } + p_govrh->fb_p->fb.col_fmt = g_vpp.mb_colfmt; + p_govrh2->fb_p->fb.col_fmt = g_vpp.mb_colfmt; + + if (wmt_getsyspara("wmt.display.hdmi", buf, &varlen) == 0) { + unsigned int parm[1]; + + MSG("hdmi sp mode : %s\n", buf); + vpp_parse_param(buf, (unsigned int *)parm, 1, 0); + g_vpp.hdmi_sp_mode = (parm[0]) ? 1 : 0; + } + + if (wmt_getsyspara("wmt.hdmi.disable", buf, &varlen) == 0) + g_vpp.hdmi_disable = 1; + + /* [uboot parameter] reg operation : addr op val */ + if (wmt_getsyspara("wmt.display.regop", buf, &varlen) == 0) { + unsigned int addr; + unsigned int val; + char op; + char *p, *endp; + + p = buf; + while (1) { + addr = simple_strtoul(p, &endp, 16); + if (*endp == '\0') + break; + + op = *endp; + if (endp[1] == '~') { + val = simple_strtoul(endp + 2, &endp, 16); + val = ~val; + } else + val = simple_strtoul(endp + 1, &endp, 16); + + DBG_DETAIL(" reg op: 0x%X %c 0x%X\n", addr, op, val); + switch (op) { + case '|': + outl(inl(addr) | val, addr); + break; + case '=': + outl(val, addr); + break; + case '&': + outl(inl(addr) & val, addr); + break; + default: + DBG_ERR("Error, Unknown operator %c\n", op); + } + + if (*endp == '\0') + break; + p = endp + 1; + } + } +#else + if (env_read_para("wmt.display.hdmi", ¶m) == 0) { + g_vpp.hdmi_sp_mode = strtoul(param.value, 0, 16); + free(param.value); + } +#endif +} /* End of vpp_get_sys_parameter */ + +void vpp_init(void) +{ + struct vpp_mod_base_t *mod_p; + unsigned int mod_mask; + int i; + + auto_pll_divisor(DEV_NA12, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_VPP, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_HDCE, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_HDMII2C, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_HDMI, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_GOVRHD, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_DVO, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_LVDS, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_HDMILVDS, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_SCL444U, CLK_ENABLE, 0, 0); + + vpp_get_sys_parameter(); + + /* init video out module first */ + if (g_vpp.govrh_preinit == 0) { + mod_mask = BIT(VPP_MOD_GOVRH2) | BIT(VPP_MOD_GOVRH) + | BIT(VPP_MOD_DISP) | BIT(VPP_MOD_LCDC); + for (i = 0; i < VPP_MOD_MAX; i++) { + if (!(mod_mask & (0x01 << i))) + continue; + mod_p = vpp_mod_get_base(i); + if (mod_p && mod_p->init) + mod_p->init(mod_p); + } + } + +#ifdef CONFIG_UBOOT + mod_mask = BIT(VPP_MOD_SCL) | BIT(VPP_MOD_SCLW); +#else + /* init other module */ + mod_mask = BIT(VPP_MOD_GOVW) | BIT(VPP_MOD_GOVM) | BIT(VPP_MOD_SCL) + | BIT(VPP_MOD_SCLW) | BIT(VPP_MOD_VPU) | BIT(VPP_MOD_VPUW) + | BIT(VPP_MOD_PIP) | BIT(VPP_MOD_VPPM); +#endif + for (i = 0; i < VPP_MOD_MAX; i++) { + if (!(mod_mask & (0x01 << i))) + continue; + mod_p = vpp_mod_get_base(i); + if (mod_p && mod_p->init) + mod_p->init(mod_p); + } + +#ifdef WMT_FTBLK_LVDS + if (!g_vpp.govrh_preinit) + lvds_init(); +#endif +#ifdef WMT_FTBLK_VOUT_HDMI + hdmi_init(); +#endif + +#ifndef CONFIG_VPOST + /* init vout device & get default resolution */ + vout_init(); +#endif + vpp_set_clock_enable(DEV_SCL444U, 0, 1); + vpp_set_clock_enable(DEV_HDMII2C, 0, 0); + vpp_set_clock_enable(DEV_HDMI, 0, 0); + vpp_set_clock_enable(DEV_HDCE, 0, 0); + vpp_set_clock_enable(DEV_LVDS, 0, 0); +} + +void vpp_get_colfmt_bpp(vdo_color_fmt colfmt, int *y_bpp, int *c_bpp) +{ + switch (colfmt) { + case VDO_COL_FMT_YUV420: + *y_bpp = 8; + *c_bpp = 4; + break; + case VDO_COL_FMT_YUV422H: + case VDO_COL_FMT_YUV422V: + *y_bpp = 8; + *c_bpp = 8; + break; + case VDO_COL_FMT_RGB_565: + case VDO_COL_FMT_RGB_1555: + case VDO_COL_FMT_RGB_5551: + *y_bpp = 16; + *c_bpp = 0; + break; + case VDO_COL_FMT_YUV444: + *y_bpp = 8; + *c_bpp = 16; + break; + case VDO_COL_FMT_RGB_888: + case VDO_COL_FMT_RGB_666: + *y_bpp = 24; + *c_bpp = 0; + break; + case VDO_COL_FMT_ARGB: + *y_bpp = 32; + *c_bpp = 0; + break; + default: + break; + } +} + +int vpp_calc_refresh(int pixclk, int xres, int yres) +{ + int refresh = 60; + int temp; + + temp = xres * yres; + if (temp) { + refresh = pixclk / temp; + if (pixclk % temp) + refresh += 1; + } + return refresh; +} + +int vpp_calc_align(int value, int align) +{ + if (value % align) { + value &= ~(align - 1); + value += align; + } + return value; +} + +int vpp_calc_fb_width(vdo_color_fmt colfmt, int width) +{ + int y_bpp, c_bpp; + + vpp_get_colfmt_bpp(colfmt, &y_bpp, &c_bpp); + return vpp_calc_align(width, VPP_FB_WIDTH_ALIGN / (y_bpp / 8)); +} + +void vpp_set_NA12_hiprio(int type) +{ +#if 0 + static int reg1, reg2; + + switch (type) { + case 0: /* restore NA12 priority */ + outl(reg1, MEMORY_CTRL_V4_CFG_BASE_ADDR + 0x8); + outl(reg2, MEMORY_CTRL_V4_CFG_BASE_ADDR + 0xC); + break; + case 1: /* set NA12 to high priority */ + reg1 = inl(MEMORY_CTRL_V4_CFG_BASE_ADDR + 0x8); + reg2 = inl(MEMORY_CTRL_V4_CFG_BASE_ADDR + 0xC); + outl(0x600000, MEMORY_CTRL_V4_CFG_BASE_ADDR + 0x8); + outl(0x0ff00000, MEMORY_CTRL_V4_CFG_BASE_ADDR + 0xC); + break; + case 2: + reg1 = inl(MEMORY_CTRL_V4_CFG_BASE_ADDR + 0x8); + reg2 = inl(MEMORY_CTRL_V4_CFG_BASE_ADDR + 0xC); + outl(0x20003f, MEMORY_CTRL_V4_CFG_BASE_ADDR + 0x8); + outl(0x00ffff00, MEMORY_CTRL_V4_CFG_BASE_ADDR + 0xC); + break; + default: + break; + } +#endif +} + +#ifdef __KERNEL__ +int vpp_set_audio(int format, int sample_rate, int channel) +{ + struct vout_audio_t info; + + MSG("set audio(fmt %d,rate %d,ch %d)\n", + format, sample_rate, channel); + info.fmt = format; + info.sample_rate = sample_rate; + info.channel = channel; + return vout_set_audio(&info); +} +#endif + +/*----------------------- vpp mb for stream ---------------------------------*/ +#ifdef CONFIG_VPP_STREAM_CAPTURE +#ifdef CONFIG_VPP_STREAM_BLOCK +DECLARE_WAIT_QUEUE_HEAD(vpp_mb_event); +#endif + +unsigned int vpp_mb_get_mask(unsigned int phy) +{ + int i; + unsigned int mask; + + for (i = 0; i < g_vpp.stream_mb_cnt; i++) { + if (g_vpp.stream_mb[i] == phy) + break; + } + if (i >= g_vpp.stream_mb_cnt) + return 0; + mask = 0x1 << i; + return mask; +} + +int vpp_mb_get(unsigned int phy) +{ + unsigned int mask; + int i, cnt; + +#ifdef CONFIG_VPP_STREAM_BLOCK + vpp_unlock(); + if (g_vpp.stream_mb_sync_flag) + vpp_dbg_show(VPP_DBGLVL_STREAM, 0, "mb_get wait"); + + i = wait_event_interruptible_timeout(vpp_mb_event, + (g_vpp.stream_mb_sync_flag != 1), HZ / 20); + vpp_lock(); +#else /* non-block */ + if (g_vpp.stream_mb_sync_flag) { /* not new mb updated */ + vpp_dbg_show(VPP_DBGLVL_STREAM, 0, + "*W* mb_get addr not update"); + return -1; + } +#endif + g_vpp.stream_mb_sync_flag = 1; + for (i = 0, cnt = 0; i < g_vpp.stream_mb_cnt; i++) { + if (g_vpp.stream_mb_lock & (0x1 << i)) + cnt++; + } + +#if 0 + if (cnt >= (g_vpp.stream_mb_cnt - 2)) { + vpp_dbg_show(VPP_DBGLVL_STREAM, 0, "*W* mb_get addr not free"); + return -1; + } +#endif + + mask = vpp_mb_get_mask(phy); + if (mask == 0) { + vpp_dbg_show(VPP_DBGLVL_STREAM, 0, "*W* mb_get invalid addr"); + return -1; + } + if (g_vpp.stream_mb_lock & mask) { + vpp_dbg_show(VPP_DBGLVL_STREAM, 0, "*W* mb_get lock addr"); + return -1; + } + g_vpp.stream_mb_lock |= mask; + if (vpp_check_dbg_level(VPP_DBGLVL_STREAM)) { + char buf[50]; + + sprintf(buf, "stream mb get 0x%x,mask 0x%x(0x%x)", + phy, mask, g_vpp.stream_mb_lock); + vpp_dbg_show(VPP_DBGLVL_STREAM, 1, buf); + } + return 0; +} + +int vpp_mb_put(unsigned int phy) +{ + unsigned int mask; + + if (phy == 0) { + g_vpp.stream_mb_lock = 0; + g_vpp.stream_mb_index = 0; + return 0; + } + + mask = vpp_mb_get_mask(phy); + if (mask == 0) { + DPRINT("[VPP] *W* mb_put addr 0x%x\n", phy); + return 1; + } + if (!(g_vpp.stream_mb_lock & mask)) + DPRINT("[VPP] *W* mb_put nonlock addr 0x%x\n", phy); + g_vpp.stream_mb_lock &= ~mask; + if (vpp_check_dbg_level(VPP_DBGLVL_STREAM)) { + char buf[50]; + + sprintf(buf, "stream mb put 0x%x,mask 0x%x(0x%x)", + phy, mask, g_vpp.stream_mb_lock); + vpp_dbg_show(VPP_DBGLVL_STREAM, 2, buf); + } + return 0; +} + +int vpp_mb_irqproc_sync(int arg) +{ + if (!g_vpp.stream_enable) + return 0; + + g_vpp.stream_sync_cnt++; + if ((g_vpp.stream_sync_cnt % 2) == 0) { + g_vpp.stream_mb_sync_flag = 0; +#ifdef CONFIG_VPP_STREAM_BLOCK + wake_up_interruptible(&vpp_mb_event); +#endif + } + return 0; +} + +/*----------------------- irq proc --------------------------------------*/ +struct vpp_irqproc_t *vpp_irqproc_array[32]; +struct list_head vpp_irqproc_free_list; +struct vpp_proc_t vpp_proc_array[VPP_PROC_NUM]; +static void vpp_irqproc_do_tasklet(unsigned long data); + +void vpp_irqproc_init(void) +{ + int i; + + INIT_LIST_HEAD(&vpp_irqproc_free_list); + + for (i = 0; i < VPP_PROC_NUM; i++) + list_add_tail(&vpp_proc_array[i].list, &vpp_irqproc_free_list); +} + +struct vpp_irqproc_t *vpp_irqproc_get_entry(enum vpp_int_t vpp_int) +{ + int no; + + if (vpp_int == 0) + return 0; + + for (no = 0; no < 32; no++) { + if (vpp_int & (0x1 << no)) + break; + } + + if (vpp_irqproc_array[no] == 0) { /* will create in first use */ + struct vpp_irqproc_t *irqproc; + + irqproc = kmalloc(sizeof(struct vpp_irqproc_t), GFP_KERNEL); + vpp_irqproc_array[no] = irqproc; + INIT_LIST_HEAD(&irqproc->list); + tasklet_init(&irqproc->tasklet, + vpp_irqproc_do_tasklet, vpp_int); + irqproc->ref = 0; + } + return vpp_irqproc_array[no]; +} /* End of vpp_irqproc_get_entry */ + +void vpp_irqproc_set_ref(struct vpp_irqproc_t *irqproc, + enum vpp_int_t type, int enable) +{ + if (enable) { + irqproc->ref++; + if (vppm_get_int_enable(type) == 0) + vppm_set_int_enable(1, type); + } else { + irqproc->ref--; + if (irqproc->ref == 0) + vppm_set_int_enable(0, type); + } +} + +static void vpp_irqproc_do_tasklet +( + unsigned long data /*!<; // tasklet input data */ +) +{ + struct vpp_irqproc_t *irqproc; + + vpp_lock(); + irqproc = vpp_irqproc_get_entry(data); + if (irqproc) { + struct list_head *cur; + struct list_head *next; + struct vpp_proc_t *entry; + + next = (&irqproc->list)->next; + while (next != &irqproc->list) { + cur = next; + next = cur->next; + entry = list_entry(cur, struct vpp_proc_t, list); + if (entry->func) { + if (entry->func(entry->arg)) + continue; + } + + if (entry->work_cnt == 0) + continue; + + entry->work_cnt--; + if (entry->work_cnt == 0) { + if (entry->wait_ms == 0) + vpp_irqproc_set_ref(irqproc, data, 0); + else + up(&entry->sem); + list_del_init(cur); + list_add_tail(&entry->list, + &vpp_irqproc_free_list); + } + } + } + vpp_unlock(); +} /* End of vpp_irqproc_do_tasklet */ + +int vpp_irqproc_work( + enum vpp_int_t type, /* interrupt type */ + int (*func)(void *argc), /* proc function pointer */ + void *arg, /* proc argument */ + int wait_ms, /* wait complete timeout (ms) */ + int work_cnt /* 0 - forever */ +) +{ + int ret; + struct vpp_proc_t *entry; + struct list_head *ptr; + struct vpp_irqproc_t *irqproc; + +#if 0 + DPRINT("[VPP] vpp_irqproc_work(type 0x%x,wait %d,cnt %d)\n", + type, wait_ms, work_cnt); +#endif + if ((vpp_irqproc_free_list.next == 0) || + list_empty(&vpp_irqproc_free_list)) { + if (func) + func(arg); + return 0; + } + + ret = 0; + vpp_lock(); + + ptr = vpp_irqproc_free_list.next; + entry = list_entry(ptr, struct vpp_proc_t, list); + list_del_init(ptr); + entry->func = func; + entry->arg = arg; + entry->type = type; + entry->wait_ms = wait_ms; + entry->work_cnt = work_cnt; + sema_init(&entry->sem, 1); + down(&entry->sem); + + irqproc = vpp_irqproc_get_entry(type); + if (irqproc) { + list_add_tail(&entry->list, &irqproc->list); + } else { + irqproc = vpp_irqproc_array[31]; + list_add_tail(&entry->list, &irqproc->list); + } + vpp_irqproc_set_ref(irqproc, type, 1); + vpp_unlock(); + + if (wait_ms) { + unsigned int tmr_cnt; + + tmr_cnt = (wait_ms * HZ) / 1000; + ret = down_timeout(&entry->sem, tmr_cnt); + if (ret) { + DPRINT("*W* vpp_irqproc_work timeout(type 0x%x,%d)\n", + type, wait_ms); + vpp_lock(); + list_del_init(ptr); + list_add_tail(ptr, &vpp_irqproc_free_list); + vpp_unlock(); + if (func) + func(arg); + } + } + + if ((work_cnt == 0) || (wait_ms == 0)) { + /* don't clear ref, forever will do in delete, + no wait will do in proc complete */ + } else { + vpp_lock(); + vpp_irqproc_set_ref(irqproc, type, 0); + vpp_unlock(); + } + return ret; +} /* End of vpp_irqproc_work */ + +void vpp_irqproc_del_work( + enum vpp_int_t type, /* interrupt type */ + int (*func)(void *argc) /* proc function pointer */ +) +{ + struct vpp_irqproc_t *irqproc; + + vpp_lock(); + irqproc = vpp_irqproc_get_entry(type); + if (irqproc) { + struct list_head *cur; + struct list_head *next; + struct vpp_proc_t *entry; + + next = (&irqproc->list)->next; + while (next != &irqproc->list) { + cur = next; + next = cur->next; + entry = list_entry(cur, struct vpp_proc_t, list); + if (entry->func == func) { + vpp_irqproc_set_ref(irqproc, type, 0); + list_del_init(cur); + list_add_tail(&entry->list, + &vpp_irqproc_free_list); + } + } + } + vpp_unlock(); +} + +/*----------------------- Linux Netlink --------------------------------------*/ +#ifdef CONFIG_VPP_NOTIFY +#define VPP_NETLINK_PROC_MAX 2 + +struct vpp_netlink_proc_t { + __u32 pid; + rwlock_t lock; +}; + +struct switch_dev vpp_sdev = { + .name = "hdmi", +}; + +static struct switch_dev vpp_sdev_hdcp = { + .name = "hdcp", +}; + +static struct switch_dev vpp_sdev_audio = { + .name = "hdmi_audio", +}; + +struct vpp_netlink_proc_t vpp_netlink_proc[VPP_NETLINK_PROC_MAX]; +static struct sock *vpp_nlfd; +static DEFINE_SEMAPHORE(vpp_netlink_receive_sem); + +struct vpp_netlink_proc_t *vpp_netlink_get_proc(int no) +{ + if (no == 0) + return 0; + if (no > VPP_NETLINK_PROC_MAX) + return 0; + return &vpp_netlink_proc[no - 1]; +} + +static void vpp_netlink_receive(struct sk_buff *skb) +{ + struct nlmsghdr *nlh = NULL; + struct vpp_netlink_proc_t *proc; + + if (down_trylock(&vpp_netlink_receive_sem)) + return; + + if (skb->len >= sizeof(struct nlmsghdr)) { + nlh = nlmsg_hdr(skb); + if ((nlh->nlmsg_len >= sizeof(struct nlmsghdr)) + && (skb->len >= nlh->nlmsg_len)) { + proc = vpp_netlink_get_proc(nlh->nlmsg_type); + if (proc) { + write_lock_bh(&proc->lock); + proc->pid = nlh->nlmsg_pid; + write_unlock_bh(&proc->lock); + DPRINT("[VPP] rx user pid 0x%x\n", proc->pid); + } + } + } + up(&vpp_netlink_receive_sem); + wmt_enable_mmfreq(WMT_MMFREQ_HDMI_PLUG, hdmi_get_plugin()); +} + +void vpp_netlink_init(void) +{ + vpp_netlink_proc[0].pid = 0; + vpp_netlink_proc[1].pid = 0; + rwlock_init(&(vpp_netlink_proc[0].lock)); + rwlock_init(&(vpp_netlink_proc[1].lock)); + vpp_nlfd = netlink_kernel_create(&init_net, NETLINK_CEC_TEST, 0, + vpp_netlink_receive, NULL, THIS_MODULE); + if (!vpp_nlfd) + DPRINT(KERN_ERR "can not create a netlink socket\n"); +} + +static ssize_t attr_show_parsed_edid(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int i, j; + unsigned char audio_format, sample_freq, bitrate; + int sample_freq_num, bitrate_num; + +#ifdef DEBUG + DPRINT("------- EDID Parsed ------\n"); + if(strlen(edid_parsed.tv_name.vendor_name) != 0) + DPRINT("Vendor Name: %s\n", edid_parsed.tv_name.vendor_name); + + if(strlen(edid_parsed.tv_name.monitor_name) != 0) + DPRINT("Monitor Name: %s\n", edid_parsed.tv_name.monitor_name); + + for (i = 0; i < AUD_SAD_NUM; i++) { + if (edid_parsed.sad[i].flag == 0) { + if (i == 0) + DPRINT("No SAD Data\n"); + break; + } + DPRINT("SAD %d: 0x%02X 0x%02X 0x%02X\n", i, + edid_parsed.sad[i].sad_byte[0], + edid_parsed.sad[i].sad_byte[1], + edid_parsed.sad[i].sad_byte[2]); + } + DPRINT("--------------------------\n"); +#endif + /* print Vendor Name */ + if (strlen(edid_parsed.tv_name.vendor_name) != 0) { + len += sprintf(buf + len, "%-16s", "Vendor Name"); + len += sprintf(buf + len, ": %s\n", edid_parsed.tv_name.vendor_name); + } + + /* print Monitor Name */ + if (strlen(edid_parsed.tv_name.monitor_name) != 0) { + len += sprintf(buf + len, "%-16s", "Monitor Name"); + len += sprintf(buf + len, ": %s\n", edid_parsed.tv_name.monitor_name); + } + + for (i = 0; i < AUD_SAD_NUM; i++) { + if (edid_parsed.sad[i].flag == 0) + break; + /* + SAD Byte 1 (format and number of channels): + bit 7: Reserved (0) + bit 6..3: Audio format code + 1 = Linear Pulse Code Modulation (LPCM) + 2 = AC-3 + 3 = MPEG1 (Layers 1 and 2) + 4 = MP3 + 5 = MPEG2 + 6 = AAC + 7 = DTS + 8 = ATRAC + 0, 15: Reserved + 9 = One-bit audio aka SACD + 10 = DD+ + 11 = DTS-HD + 12 = MLP/Dolby TrueHD + 13 = DST Audio + 14 = Microsoft WMA Pro + bit 2..0: number of channels minus 1 + (i.e. 000 = 1 channel; 001 = 2 channels; 111 = + 8 channels) + */ + audio_format = (edid_parsed.sad[i].sad_byte[0] & 0x78) >> 3; + if (audio_format == 0 || audio_format == 15) + continue; + + /* print header */ + len += sprintf(buf + len, "%-16s", "Audio Format"); + len += sprintf(buf + len, ": "); + + switch (audio_format) { + case 1: + len += sprintf(buf + len, "pcm"); + break; + case 2: + len += sprintf(buf + len, "ac3"); + break; + case 3: + len += sprintf(buf + len, "mpeg1"); + break; + case 4: + len += sprintf(buf + len, "mp3"); + break; + case 5: + len += sprintf(buf + len, "mpeg2"); + break; + case 6: + len += sprintf(buf + len, "aac"); + break; + case 7: + len += sprintf(buf + len, "dts"); + break; + case 8: + len += sprintf(buf + len, "atrac"); + break; + case 9: + len += sprintf(buf + len, "one_bit_audio"); + break; + case 10: + len += sprintf(buf + len, "eac3"); + break; + case 11: + len += sprintf(buf + len, "dts-hd"); + break; + case 12: + len += sprintf(buf + len, "mlp"); + break; + case 13: + len += sprintf(buf + len, "dst"); + break; + case 14: + len += sprintf(buf + len, "wmapro"); + break; + default: + break; + } + + /* separator */ + len += sprintf(buf + len, ","); + + /* number of channels */ + len += sprintf(buf + len, "%d", + (edid_parsed.sad[i].sad_byte[0] & 0x7) + 1); + + /* separator */ + len += sprintf(buf + len, ","); + + /* + SAD Byte 2 (sampling frequencies supported): + bit 7: Reserved (0) + bit 6: 192kHz + bit 5: 176kHz + bit 4: 96kHz + bit 3: 88kHz + bit 2: 48kHz + bit 1: 44kHz + bit 0: 32kHz + */ + sample_freq = edid_parsed.sad[i].sad_byte[1]; + sample_freq_num = 0; + for (j = 0; j < 7; j++) { + if (sample_freq & (1 << j)) { + if (sample_freq_num != 0) + len += sprintf(buf + len, + "|"); /* separator */ + switch (j) { + case 0: + len += sprintf(buf + len, "32KHz"); + break; + case 1: + len += sprintf(buf + len, "44KHz"); + break; + case 2: + len += sprintf(buf + len, "48KHz"); + break; + case 3: + len += sprintf(buf + len, "88KHz"); + break; + case 4: + len += sprintf(buf + len, "96KHz"); + break; + case 5: + len += sprintf(buf + len, "176KHz"); + break; + case 6: + len += sprintf(buf + len, "192KHz"); + break; + default: + break; + } + sample_freq_num++; + } + } + + if (sample_freq_num == 0) + len += sprintf(buf + len, "0"); + + /* separator */ + len += sprintf(buf + len, ","); + + /* + SAD Byte 3 (bitrate): + For LPCM, bits 7:3 are reserved and the remaining bits + define bit depth + bit 2: 24 bit + bit 1: 20 bit + bit 0: 16 bit + For all other sound formats, bits 7..0 designate the maximum + supported bitrate divided by 8 kbit/s. + */ + bitrate = edid_parsed.sad[i].sad_byte[2]; + bitrate_num = 0; + if (audio_format == 1) { /* for LPCM */ + for (j = 0; j < 3; j++) { + if (bitrate & (1 << j)) { + if (bitrate_num != 0) + len += sprintf(buf + len, + "|"); /* separator */ + switch (j) { + case 0: + len += sprintf(buf + len, + "16bit"); + break; + case 1: + len += sprintf(buf + len, + "20bit"); + break; + case 2: + len += sprintf(buf + len, + "24bit"); + break; + default: + break; + } + bitrate_num++; + } + } + } else if (audio_format >= 2 && + audio_format <= 8) /* From AC3 to ATRAC */ + len += sprintf(buf + len, "%dkbps", bitrate * 8); + else /* From One-bit-audio to WMA Pro*/ + len += sprintf(buf + len, "%d", bitrate); + + len += sprintf(buf + len, "\n"); + } + + if (len == 0) + len += sprintf(buf + len, "\n"); + + return len; +} + +static DEVICE_ATTR(edid_parsed, 0444, attr_show_parsed_edid, NULL); + +void vpp_switch_state_init(void) +{ + /* /sys/class/switch/hdmi/state */ + switch_dev_register(&vpp_sdev); + switch_set_state(&vpp_sdev, hdmi_get_plugin() ? 1 : 0); + switch_dev_register(&vpp_sdev_hdcp); + switch_set_state(&vpp_sdev_hdcp, 0); + switch_dev_register(&vpp_sdev_audio); + device_create_file(vpp_sdev.dev, &dev_attr_edid_parsed); +} + +void vpp_netlink_notify(int no, int cmd, int arg) +{ + int ret; + int size; + unsigned char *old_tail; + struct sk_buff *skb; + struct nlmsghdr *nlh; + struct vpp_netlink_proc_t *proc; + + proc = vpp_netlink_get_proc(no); + if (!proc) + return; + + MSG("[VPP] netlink notify %d,cmd %d,0x%x\n", no, cmd, arg); + + switch (cmd) { + case DEVICE_RX_DATA: + size = NLMSG_SPACE(sizeof(struct wmt_cec_msg)); + break; + case DEVICE_PLUG_IN: + case DEVICE_PLUG_OUT: + case DEVICE_STREAM: + size = NLMSG_SPACE(sizeof(struct wmt_cec_msg)); + break; + default: + return; + } + + skb = alloc_skb(size, GFP_ATOMIC); + if (skb == NULL) + return; + old_tail = skb->tail; + nlh = NLMSG_PUT(skb, 0, 0, 0, size-sizeof(*nlh)); + nlh->nlmsg_len = skb->tail - old_tail; + + switch (cmd) { + case DEVICE_RX_DATA: + nlh->nlmsg_type = DEVICE_RX_DATA; + memcpy(NLMSG_DATA(nlh), (struct wmt_cec_msg *)arg, + sizeof(struct wmt_cec_msg)); + break; + case DEVICE_PLUG_IN: + case DEVICE_PLUG_OUT: + { + static int cnt; + struct wmt_cec_msg *msg; + + msg = (struct wmt_cec_msg *)NLMSG_DATA(nlh); + msg->msgdata[0] = cnt; + cnt++; + } + if (arg) { + nlh->nlmsg_type = DEVICE_PLUG_IN; + nlh->nlmsg_flags = edid_get_hdmi_phy_addr(); + } else { + nlh->nlmsg_type = DEVICE_PLUG_OUT; + } + size = NLMSG_SPACE(sizeof(0)); + break; + case DEVICE_STREAM: + nlh->nlmsg_type = cmd; + nlh->nlmsg_flags = arg; + break; + default: + return; + } + + NETLINK_CB(skb).pid = 0; + NETLINK_CB(skb).dst_group = 0; + + if (proc->pid != 0) { + ret = netlink_unicast(vpp_nlfd, skb, proc->pid, MSG_DONTWAIT); + return; + } +nlmsg_failure: /* NLMSG_PUT go to */ + if (skb != NULL) + kfree_skb(skb); + return; +} + +void vpp_netlink_notify_plug(int vo_num, int plugin) +{ + /* set unplug flag for check_var */ + if (plugin == 0) { + int mask = 0; + + if (vo_num == VPP_VOUT_ALL) + mask = ~0; + else { + struct vout_info_t *vo_info; + vo_info = vout_get_info_entry(vo_num); + if (vo_info) + mask = 0x1 << (vo_info->num); + } + g_vpp.fb_recheck |= mask; + } + + if ((vpp_netlink_proc[0].pid == 0) && (vpp_netlink_proc[1].pid == 0)) + return; + + /* if hdmi unplug, clear edid_parsed */ + if (hdmi_get_plugin() == 0) + memset(&edid_parsed, 0, sizeof(struct edid_parsed_t)); + + vpp_netlink_notify(USER_PID, DEVICE_PLUG_IN, plugin); + vpp_netlink_notify(WP_PID, DEVICE_PLUG_IN, plugin); + + /* hdmi plugin/unplug */ + plugin = hdmi_get_plugin(); + switch_set_state(&vpp_sdev, plugin ? 1 : 0); + wmt_enable_mmfreq(WMT_MMFREQ_HDMI_PLUG, plugin); +} + +void vpp_netlink_notify_cp(int enable) +{ + switch_set_state(&vpp_sdev_hdcp, enable); +} +EXPORT_SYMBOL(vpp_netlink_notify_cp); + +#endif /* CONFIG_VPP_NOTIFY */ +#endif /* __KERNEL__ */ |