summaryrefslogtreecommitdiff
path: root/drivers/video/wmt/vpp.c
diff options
context:
space:
mode:
authorSrikant Patnaik2015-01-11 12:28:04 +0530
committerSrikant Patnaik2015-01-11 12:28:04 +0530
commit871480933a1c28f8a9fed4c4d34d06c439a7a422 (patch)
tree8718f573808810c2a1e8cb8fb6ac469093ca2784 /drivers/video/wmt/vpp.c
parent9d40ac5867b9aefe0722bc1f110b965ff294d30d (diff)
downloadFOSSEE-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-xdrivers/video/wmt/vpp.c1413
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", &param) == 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__ */