lcd: add lcd driver for axg
authorEvoke Zhang <evoke.zhang@amlogic.com>
Fri, 9 Jun 2017 02:46:09 +0000 (10:46 +0800)
committerJianxin Pan <jianxin.pan@amlogic.com>
Fri, 16 Jun 2017 05:10:53 +0000 (22:10 -0700)
PD#142470: lcd: add lcd driver for axg

Change-Id: I58e2a2af474bafbbe3718f51493c9b46cb07cf31
Signed-off-by: Evoke Zhang <evoke.zhang@amlogic.com>
76 files changed:
MAINTAINERS
arch/arm64/boot/dts/amlogic/axg_s400.dts
arch/arm64/boot/dts/amlogic/mesonaxg_s400-panel.dtsi [new file with mode: 0644]
arch/arm64/boot/dts/amlogic/mesongxm.dtsi
arch/arm64/boot/dts/amlogic/mesongxm_q200-panel.dtsi [new file with mode: 0644]
arch/arm64/configs/meson64_defconfig
drivers/amlogic/clk/axg/axg_clk-pll.c
drivers/amlogic/media/enhancement/amvecm/amvecm.c
drivers/amlogic/media/vin/tvin/vdin/vdin_ctl.c
drivers/amlogic/media/vin/tvin/vdin/vdin_ctl.h
drivers/amlogic/media/vin/tvin/vdin/vdin_debug.c
drivers/amlogic/media/vin/tvin/vdin/vdin_drv.c
drivers/amlogic/media/vout/Kconfig
drivers/amlogic/media/vout/Makefile
drivers/amlogic/media/vout/backlight/Kconfig [new file with mode: 0644]
drivers/amlogic/media/vout/backlight/Makefile [new file with mode: 0644]
drivers/amlogic/media/vout/backlight/aml_bl.c [new file with mode: 0644]
drivers/amlogic/media/vout/backlight/aml_bl.dts [new file with mode: 0644]
drivers/amlogic/media/vout/backlight/aml_bl_reg.h [new file with mode: 0644]
drivers/amlogic/media/vout/backlight/aml_ldim/Kconfig [new file with mode: 0644]
drivers/amlogic/media/vout/backlight/bl_extern/Kconfig [new file with mode: 0644]
drivers/amlogic/media/vout/backlight/bl_extern/Makefile [new file with mode: 0644]
drivers/amlogic/media/vout/backlight/bl_extern/aml_bl_extern.dts [new file with mode: 0644]
drivers/amlogic/media/vout/backlight/bl_extern/bl_extern.c [new file with mode: 0644]
drivers/amlogic/media/vout/backlight/bl_extern/i2c_lp8556.c [new file with mode: 0644]
drivers/amlogic/media/vout/backlight/bl_extern/mipi_LT070ME05.c [new file with mode: 0644]
drivers/amlogic/media/vout/backlight/bl_extern/pmu_aml1218.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/Kconfig [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/Makefile [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_clk_config.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_clk_config.h [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_common.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_common.h [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_debug.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_extern/Kconfig [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_extern/Makefile [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_extern/ext_default.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_extern/i2c_DLPC3439.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_extern/i2c_T5800Q.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_extern/i2c_anx6345.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_extern/i2c_anx6345.h [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_extern/i2c_tc101.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.dts [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.h [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_extern/mipi_KD080D13.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_extern/mipi_N070ICN.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_extern/spi_LD070WS2.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_notify.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_reg.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_reg.h [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_tablet/Kconfig [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_tablet/Makefile [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_tablet/aml_lcd.dts [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_drv.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_tablet.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_tablet.h [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_tablet/mipi_dsi_util.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_tablet/mipi_dsi_util.h [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_tv/Makefile [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_tv/lcd_drv.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.dts [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.h [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_unifykey.c [new file with mode: 0644]
drivers/amlogic/media/vout/lcd/lcd_vout.c [new file with mode: 0644]
drivers/amlogic/pinctrl/pinctrl_gxl.c
include/linux/amlogic/media/vout/lcd/aml_bl.h [new file with mode: 0644]
include/linux/amlogic/media/vout/lcd/aml_bl_extern.h [new file with mode: 0644]
include/linux/amlogic/media/vout/lcd/aml_ldim.h [new file with mode: 0644]
include/linux/amlogic/media/vout/lcd/lcd_extern.h [new file with mode: 0644]
include/linux/amlogic/media/vout/lcd/lcd_mipi.h [new file with mode: 0644]
include/linux/amlogic/media/vout/lcd/lcd_notify.h [new file with mode: 0644]
include/linux/amlogic/media/vout/lcd/lcd_unifykey.h [new file with mode: 0644]
include/linux/amlogic/media/vout/lcd/lcd_vout.h [new file with mode: 0644]
include/linux/amlogic/media/vout/vinfo.h

index c8e71c6..bd8c7ff 100644 (file)
@@ -13948,3 +13948,11 @@ F: include/linux/amlogic/unifykey/*
 AMLOGIC AXG ADD OSD DRIVER
 M:     Pengcheng Chen <pengcheng.chen@amlogic.com>
 F:     drivers/amlogic/media/osd/osd_io.c
+
+AMLOGIC LCD driver
+M: Weiming Liu <weiming.liu@amlogic.com>
+F: drivers/amlogic/media/vout/backlight/*
+F: drivers/amlogic/media/vout/lcd/*
+F: include/linux/amlogic/media/vout/lcd/*
+F: arch/arm64/boot/dts/amlogic/mesongxm_q200-panel.dtsi
+F: arch/arm64/boot/dts/amlogic/mesonaxg_s400-panel.dtsi
index 083b862..0bff4a9 100644 (file)
@@ -18,7 +18,7 @@
 /dts-v1/;
 
 #include "mesonaxg.dtsi"
-
+#include "mesonaxg_s400-panel.dtsi"
 / {
        model = "Amlogic";
        compatible = "amlogic, axg";
                /* 0: 100.0M    1: 166.7M    2: 200.0M    3: 250.0M */
        };
 
+       vout {
+               compatible = "amlogic, vout";
+               dev_name = "vout";
+               status = "okay";
+       };
+
        /* Sound iomap */
        aml_snd_iomap {
                compatible = "amlogic, snd_iomap";
                        function = "pdm";
                };
        };
+
+       bl_pwm_on_pins: bl_pwm_on_pin {
+               mux {
+                       groups = "GPIOZ_4";
+                       function = "pwm_b";
+               };
+       };
 }; /* end of pinctrl_periphs */
 /* Audio Related End */
 
diff --git a/arch/arm64/boot/dts/amlogic/mesonaxg_s400-panel.dtsi b/arch/arm64/boot/dts/amlogic/mesonaxg_s400-panel.dtsi
new file mode 100644 (file)
index 0000000..9e48913
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * arch/arm64/boot/dts/amlogic/mesongxm_q200-panel.dtsi
+ *
+ * Copyright (C) 2016 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+/ {
+       lcd{
+               compatible = "amlogic, lcd";
+               dev_name = "lcd";
+               mode = "tablet";
+               status = "okay";
+               key_valid = <0>;
+               clocks = <&clkc CLKID_MIPI_DSI_HOST
+                       &clkc CLKID_MIPI_DSI_PHY
+                       &clkc CLKID_DSI_MEAS_COMP>;
+               clock-names = "dsi_host",
+                       "dsi_phy",
+                       "dsi_meas";
+               pinctrl_version = <1>; /* for uboot */
+
+               /* power type:
+                *    (0=cpu_gpio, 1=pmu_gpio, 2=signal,3=extern, 0xff=ending)
+                * power index:
+                *    (point gpios_index, or extern_index,0xff=invalid)
+                * power value:(0=output low, 1=output high, 2=input)
+                * power delay:(unit in ms)
+                */
+               lcd_cpu-gpios = <&gpio GPIOZ_6 GPIO_ACTIVE_HIGH>;
+               lcd_cpu_gpio_names = "GPIOZ_6";
+
+               lcd_0{
+                       model_name = "B080XAN01";
+                               /*interface(ttl,lvds,mipi)*/
+                       interface = "mipi";
+                               /* basic_setting:
+                                * h_active,v_active,h_period,v_period,
+                                * lcd_bits,screen_widht,screen_height
+                                */
+                       basic_setting = <768 1024 948 1140 8 119 159>;
+                               /* lcd_timing:
+                                * hs_width,hs_bp,hs_pol,vs_width,vs_bp,vs_pol
+                                */
+                       lcd_timing = <64 56 0 50 30 0>;
+                               /* clk_attr:
+                                * fr_adj_type(0=clock,1=htotal,2=vtotal),
+                                * clk_ss_level,clk_auto_generate,
+                                * pixel_clk(unit in Hz)
+                                */
+                       clk_attr = <0 0 1 64843200>;
+                               /* mipi_attr:
+                                * lane_num, bit_rate_max(MHz),
+                                * factor(*100, default 0 for auto),
+                                * operation_mode_init(0=video, 1=command),
+                                * operation_mode_display(0=video, 1=command),
+                                * video_mode_type
+                                *     (0=sync_pulse,1=sync_event,2=burst),
+                                * clk_lp_continuous(0=stop,1=continue),
+                                * phy_stop_wait(0=auto,1=standard,2=slow)
+                                */
+                       mipi_attr = <4 550 0 1 0 2 1 0>;
+                               /* dsi_init: data_type, num, data... */
+                       dsi_init_on = <0x05 1 0x11
+                                       0xff 20     /* delay(ms) */
+                                       0x05 1 0x29
+                                       0xff 20     /* delay(ms) */
+                                       0xff 0xff>; /* ending flag */
+                       dsi_init_off = <0x05 1 0x28
+                                       0xff 10     /* delay(ms) */
+                                       0x05 1 0x10
+                                       0xff 10     /* delay(ms) */
+                                       0xff 0xff>; /* ending flag */
+                               /* extern_init: 0xff for invalid */
+                       extern_init = <0xff>;
+                               /* power step: type,index,value,delay(ms) */
+                       power_on_step = <0 0 1 10
+                                       0 0 0 20
+                                       0 0 1 20
+                                       2 0 0 0
+                                       0xff 0 0 0>;
+                       power_off_step = <2 0 0 50
+                                       0 0 0 100
+                                       0xff 0 0 0>;
+                       backlight_index = <0>;
+               };
+       };
+
+       lcd_extern{
+               compatible = "amlogic, lcd_extern";
+               dev_name = "lcd_extern";
+               status = "okay";
+               key_valid = <0>;
+
+               extern_1{
+                       index = <1>;
+                       extern_name = "mipi_KD080D13";
+                       status = "disabled";
+                       type = <2>; /* 0=i2c, 1=spi, 2=mipi */
+               };
+       };
+
+       backlight{
+               compatible = "amlogic, backlight";
+               dev_name = "backlight";
+               status = "okay";
+               key_valid = <0>;
+               pinctrl-names = "pwm_on";
+               pinctrl-0 = <&bl_pwm_on_pins>;
+               pinctrl_version = <1>; /* for uboot */
+               /* power index:(point gpios_index, 0xff=invalid)
+                * power value:(0=output low, 1=output high, 2=input)
+                * power delay:(unit in ms)
+                */
+               bl-gpios = <&gpio GPIOZ_4 GPIO_ACTIVE_HIGH
+                       &gpio GPIOZ_5 GPIO_ACTIVE_HIGH>;
+               bl_gpio_names = "GPIOZ_4","GPIOZ_5";
+
+               backlight_0{
+                       index = <0>;
+                       bl_name = "backlight_pwm";
+                       bl_level_default_uboot_kernel = <100 100>;
+                               /* level_attr: max, min, mid, mid_mapping */
+                       bl_level_attr = <255 10 128 102>;
+
+                       bl_ctrl_method = <1>; /* 1=pwm, 2=pwm_combo, 3=extern */
+                               /* power_attr:
+                                * en_gpio_index, on_value, off_value,
+                                * on_delay(ms), off_delay(ms)
+                                */
+                       bl_power_attr = <1 1 0 200 200>;
+                               /* pwm_port: PWM_A,PWM_B,PWM_C,PWM_D,PWM_VS */
+                       bl_pwm_port = "PWM_B";
+                               /* pwm_attr:
+                                * pwm_method: 0=negative, 1=positive
+                                * pwm_freq: pwm_vs: 1~4(vfreq multiple),
+                                *           other pwm: real freq(Hz)
+                                * duty_max(%), duty_min(%)
+                                */
+                       bl_pwm_attr = <0 180 100 25>;
+                               /* pwm_power:
+                                * pwm_gpio_index, pwm_gpio_off,
+                                * pwm_on_delay(ms), pwm_off_delay(ms)
+                                */
+                       bl_pwm_power = <0 0 10 10>;
+               };
+       };
+};/* end of panel */
+
index 024c59d..abf72ba 100644 (file)
                };
        };
 
+       lcd_ttl_rgb_6bit_pins_on:lcd_ttl_rgb_6bit_on{
+               mux {
+                       groups = "lcd_r2_7",
+                               "lcd_g2_7",
+                               "lcd_b2_7";
+                       function = "lcd_ttl";
+               };
+       };
+
+       lcd_ttl_rgb_8bit_pins_on:lcd_ttl_rgb_8bit_on{
+               mux {
+                       groups = "lcd_r0_1", "lcd_r2_7",
+                               "lcd_g0_1", "lcd_g2_7",
+                               "lcd_b0_1", "lcd_b2_7";
+                       function = "lcd_ttl";
+               };
+       };
+                /* DE + clk */
+       lcd_ttl_de_on_pins:lcd_ttl_de_on_pin{
+               mux {
+                       groups = "tcon_cph", /* clk */
+                               "tcon_oeh";  /* DE */
+                       function = "lcd_ttl";
+               };
+       };
+                /* hvsync + clk */
+       lcd_ttl_hvsync_on_pins:lcd_ttl_hvsync_on_pin{
+               mux {
+                       groups = "tcon_cph", /* clk */
+                               "tcon_stv1", /* vs */
+                               "tcon_sth1"; /* hs */
+                       function = "lcd_ttl";
+               };
+       };
+                /* DE + hvsync + clk */
+       lcd_ttl_de_hvsync_on_pins:lcd_ttl_de_hvsync_on_pin{
+               mux {
+                       groups = "tcon_cph", /* clk */
+                               "tcon_oeh",  /* DE */
+                               "tcon_stv1", /* vs */
+                               "tcon_sth1"; /* hs */
+                       function = "lcd_ttl";
+               };
+       };
 }; /* end of pinctrl_periphs */
 
 &periphs {
diff --git a/arch/arm64/boot/dts/amlogic/mesongxm_q200-panel.dtsi b/arch/arm64/boot/dts/amlogic/mesongxm_q200-panel.dtsi
new file mode 100644 (file)
index 0000000..c0a888f
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * arch/arm64/boot/dts/amlogic/mesongxm_q200-panel.dtsi
+ *
+ * Copyright (C) 2016 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+/ {
+       lcd{
+               compatible = "amlogic, lcd";
+               dev_name = "lcd";
+               mode = "tablet";
+               status = "okay";
+               key_valid = <0>;
+               /* clocks = <&clkc CLKID_VCLK2_ENCL
+                *      &clkc CLKID_VCLK2_VENCL>;
+                * clock-names = "vencl_top",
+                * "vencl_int";
+                */
+               pinctrl_version = <1>; /* for uboot */
+               pinctrl-names = "ttl_6bit_hvsync_de_on","ttl_6bit_hvsync_on",
+                       "ttl_6bit_de_on","ttl_8bit_hvsync_de_on",
+                       "ttl_8bit_hvsync_on","ttl_8bit_de_on";
+               pinctrl-0 = <&lcd_ttl_rgb_6bit_pins_on
+                       &lcd_ttl_de_hvsync_on_pins>;
+               pinctrl-1 = <&lcd_ttl_rgb_6bit_pins_on
+                       &lcd_ttl_hvsync_on_pins>;
+               pinctrl-2 = <&lcd_ttl_rgb_6bit_pins_on
+                       &lcd_ttl_de_on_pins>;
+               pinctrl-3 = <&lcd_ttl_rgb_8bit_pins_on
+                       &lcd_ttl_de_hvsync_on_pins>;
+               pinctrl-4 = <&lcd_ttl_rgb_8bit_pins_on
+                       &lcd_ttl_hvsync_on_pins>;
+               pinctrl-5 = <&lcd_ttl_rgb_8bit_pins_on
+                       &lcd_ttl_de_on_pins>;
+
+               /* power type:
+                *    (0=cpu_gpio, 1=pmu_gpio, 2=signal,3=extern, 0xff=ending)
+                * power index:
+                *    (point gpios_index, or extern_index,0xff=invalid)
+                * power value:(0=output low, 1=output high, 2=input)
+                * power delay:(unit in ms)
+                */
+               /*lcd_cpu-gpios = <&gpio GPIOX_3 1>;*/
+               /*lcd_cpu_gpio_names = "GPIOX_3";*/
+
+               lcd_0{
+                       model_name = "LCD720P";
+                               /*interface(ttl,lvds,mipi)*/
+                       interface = "ttl";
+                               /* basic_setting:
+                                * h_active,v_active,h_period,v_period,
+                                * lcd_bits,screen_widht,screen_height
+                                */
+                       basic_setting = <1280 720 1650 750 8 16 9>;
+                               /* lcd_timing:
+                                * hs_width,hs_bp,hs_pol,vs_width,vs_bp,vs_pol
+                                */
+                       lcd_timing = <40 220 1 5 20 1>;
+                               /* clk_attr:
+                                * fr_adj_type(0=clock,1=htotal,2=vtotal),
+                                * clk_ss_level,clk_auto_generate,
+                                * pixel_clk(unit in Hz)
+                                */
+                       clk_attr = <0 0 1 74250000>;
+                               /* ttl_attr:
+                                * clk_pol, de_valid, hvsync_valid,
+                                * rb_swap, bit_swap
+                                */
+                       ttl_attr = <0 1 1 0 0>;
+                               /* power step: type,index,value,delay(ms) */
+                       power_on_step = <2 0 0 0
+                                       0xff 0 0 0>;            
+                       power_off_step = <2 0 0 50
+                                       0xff 0 0 0>;
+                       backlight_index = <0xff>;
+               };
+       };
+
+       lcd_extern{
+               compatible = "amlogic, lcd_extern";
+               dev_name = "lcd_extern";
+               status = "okay";
+               key_valid = <0>;
+
+               extern_0{
+                       index = <0>;
+                       extern_name = "ext_default";
+                       status = "disabled";
+                       type = <0>; /* 0=i2c, 1=spi, 2=mipi */
+                       i2c_address = <0x1c>; /* 7bit i2c address */
+                       i2c_second_address = <0xff>; /* 7bit i2c address, 0xff for none */
+                       i2c_bus = "i2c_bus_d";
+                       cmd_size = <9>;
+                       /* init on/off: (type, value..., delay), must match cmd_size for every group */
+                       /* type: 0x00=cmd(bit[3:0]=1 for second_addr), 0x10=gpio, 0xff=ending*/
+                       /* value: i2c or spi cmd, or gpio index & level, fill 0x0 for no use */
+                       /* delay: unit ms */
+                       init_on = <0x00 0x20 0x01 0x02 0x00 0x40 0xFF 0x00 0x00
+                               0x00 0x80 0x02 0x00 0x40 0x62 0x51 0x73 0x00
+                               0x00 0x61 0x06 0x00 0x00 0x00 0x00 0x00 0x00
+                               0x00 0xC1 0x05 0x0F 0x00 0x08 0x70 0x00 0x00
+                               0x00 0x13 0x01 0x00 0x00 0x00 0x00 0x00 0x00
+                               0x00 0x3D 0x02 0x01 0x00 0x00 0x00 0x00 0x00
+                               0x00 0xED 0x0D 0x01 0x00 0x00 0x00 0x00 0x00
+                               0x00 0x23 0x02 0x00 0x00 0x00 0x00 0x00 0x0A
+                               0xff 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00>;
+                       init_off = <0xff 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00>;
+               };
+
+               extern_1{
+                       index = <2>;
+                       extern_name = "i2c_DLPC3439";
+                       status = "disabled";
+                       type = <0>; /* 0=i2c, 1=spi, 2=mipi */
+                       i2c_address = <0x1b>; /* 7bit i2c address */
+                       i2c_bus = "i2c_bus_d";
+               };
+       };
+};/* end of panel */
+
index 968802e..7daed21 100644 (file)
@@ -248,6 +248,12 @@ CONFIG_AMLOGIC_CVBS_OUTPUT=y
 CONFIG_AMLOGIC_WSS=y
 CONFIG_AMLOGIC_VDAC=y
 CONFIG_AMLOGIC_HDMITX=y
+CONFIG_AMLOGIC_LCD=y
+CONFIG_AMLOGIC_LCD_TV=y
+CONFIG_AMLOGIC_LCD_TABLET=y
+CONFIG_AMLOGIC_LCD_EXTERN=y
+CONFIG_AMLOGIC_LCD_EXTERN_MIPI_KD080D13=y
+CONFIG_AMLOGIC_BACKLIGHT=y
 CONFIG_AMLOGIC_VOUT_SERVE=y
 CONFIG_AMLOGIC_MEDIA_FB=y
 CONFIG_AMLOGIC_MEDIA_FB_OSD_SYNC_FENCE=y
@@ -365,6 +371,8 @@ CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
 CONFIG_MEDIA_USB_SUPPORT=y
 CONFIG_USB_VIDEO_CLASS=y
 CONFIG_FB=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
 CONFIG_SOUND=y
 CONFIG_SND=y
 CONFIG_SND_USB_AUDIO=y
index 625b129..e5f4663 100644 (file)
@@ -57,7 +57,8 @@
 #define GXL_GP0_CNTL4 0xc000004d
 #define GXL_GP0_CNTL5 0x00078000
 /* AXG */
-#define AXG_MIPI_CNTL0 0xa5b80000
+#define AXG_MIPI_CNTL0_ENABLE   BIT(29)
+#define AXG_MIPI_CNTL0_BANDGAP  BIT(26)
 #define AXG_PCIE_PLL_CNTL 0x40010242
 #define AXG_PCIE_PLL_CNTL1 0xc084b2ab
 #define AXG_PCIE_PLL_CNTL2 0xb75020be
@@ -188,7 +189,9 @@ static int meson_axg_pll_set_rate(struct clk_hw *hw, unsigned long rate,
                void *cntlbase = pll->base + p->reg_off;
 
                if (!strcmp(clk_hw_get_name(hw), "pcie_pll")) {
-                       writel(AXG_MIPI_CNTL0, pll->base);
+                       reg = readl(pll->base);
+                       reg |= (AXG_MIPI_CNTL0_ENABLE | AXG_MIPI_CNTL0_BANDGAP);
+                       writel(reg, pll->base);
                        writel(AXG_PCIE_PLL_CNTL, cntlbase + (u64)(0*4));
                        writel(AXG_PCIE_PLL_CNTL1, cntlbase + (u64)(1*4));
                        writel(AXG_PCIE_PLL_CNTL2, cntlbase + (u64)(2*4));
index 23ea3a2..2c09cef 100644 (file)
@@ -39,8 +39,8 @@
 #include <linux/amlogic/media/vout/vout_notify.h>
 #include <linux/io.h>
 
-#ifdef CONFIG_AML_LCD
-#include <linux/amlogic/media/vout/lcd_notify.h>
+#ifdef CONFIG_AMLOGIC_LCD
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
 #endif
 
 #include "arch/vpp_regs.h"
@@ -2923,7 +2923,7 @@ static void aml_vecm_dt_parse(struct platform_device *pdev)
        /* WRITE_VPP_REG_BITS(VPP_MISC, cm_en, 28, 1); */
 }
 
-#ifdef CONFIG_AML_LCD
+#ifdef CONFIG_AMLOGIC_LCD
 static int aml_lcd_gamma_notifier(struct notifier_block *nb,
                unsigned long event, void *data)
 {
@@ -2983,7 +2983,7 @@ static int aml_vecm_probe(struct platform_device *pdev)
        }
 
        spin_lock_init(&vpp_lcd_gamma_lock);
-#ifdef CONFIG_AML_LCD
+#ifdef CONFIG_AMLOGIC_LCD
        ret = aml_lcd_notifier_register(&aml_lcd_gamma_nb);
        if (ret)
                pr_info("register aml_lcd_gamma_notifier failed\n");
@@ -3047,7 +3047,7 @@ static int __exit aml_vecm_remove(struct platform_device *pdev)
        class_destroy(devp->clsp);
        unregister_chrdev_region(devp->devno, 1);
        kfree(devp);
-#ifdef CONFIG_AML_LCD
+#ifdef CONFIG_AMLOGIC_LCD
        aml_lcd_notifier_unregister(&aml_lcd_gamma_nb);
 #endif
        probe_ok = 0;
index c6eba2a..c4be691 100644 (file)
@@ -1553,7 +1553,7 @@ void vdin_set_def_wr_canvas(struct vdin_dev_s *devp)
                wr(offset, VDIN_WR_CTRL, (0x0bc01000 | def_canvas));
 }
 
-#ifdef CONFIG_AML_LOCAL_DIMMING
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
 void vdin_set_ldim_max_init(unsigned int offset,
                int pic_h, int pic_v, int blk_vnum, int blk_hnum)
 {
@@ -1660,7 +1660,7 @@ void vdin_set_vframe_prop_info(struct vframe_s *vf,
 {
        unsigned int offset = devp->addr_offset;
        struct vframe_bbar_s bbar = {0};
-#ifdef CONFIG_AML_LOCAL_DIMMING
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
        /*int i;*/
 #endif
        /* fetch hist info */
@@ -1854,7 +1854,7 @@ void vdin_set_vframe_prop_info(struct vframe_s *vf,
        vf->prop.meas.vs_stamp = devp->stamp;
        vf->prop.meas.vs_cycle = devp->cycle;
 #if 0
-/*#ifdef CONFIG_AML_LOCAL_DIMMING*/
+/*#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING*/
        /* get ldim max */
        if (vdin_ldim_max_en && is_meson_gxtvbb_cpu()) {
                wr_bits(offset, VDIN_LDIM_STTS_HIST_REGION_IDX, 0,
@@ -1884,7 +1884,7 @@ void vdin_set_all_regs(struct vdin_dev_s *devp)
 
        /* bbar sub-module */
        vdin_set_bbar(devp->addr_offset, devp->v_active, devp->h_active);
-#ifdef CONFIG_AML_LOCAL_DIMMING
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
        /* ldim sub-module */
        /* vdin_set_ldim_max_init(devp->addr_offset, 1920, 1080, 8, 2); */
        vdin_set_ldim_max_init(devp->addr_offset, devp->h_active,
index a0785b6..6c808e7 100644 (file)
@@ -97,7 +97,7 @@ struct vdin_stat_s {
        unsigned int   sum_pixel; /* VDIN_HIST_PIX_CNT_REG */
 };
 
-#ifdef CONFIG_AML_LOCAL_DIMMING
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
 struct ldim_max_s {
     /* general parameters */
        int ld_pic_rowmax;
index ddc0e6d..6393d15 100644 (file)
@@ -360,7 +360,7 @@ static void vdin_write_mem(struct vdin_dev_s *devp, char *type, char *path)
                        NULL);
 }
 
-#ifdef CONFIG_AML_LOCAL_DIMMING
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
 
 static void vdin_dump_histgram_ldim(struct vdin_dev_s *devp,
        unsigned int hnum, unsigned int vnum)
@@ -682,7 +682,7 @@ start_chk:
        } else if (!strcmp(parm[0], "histgram")) {
                vdin_dump_histgram(devp);
        }
-#ifdef CONFIG_AML_LOCAL_DIMMING
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
        else if (!strcmp(parm[0], "histgram_ldim")) {
                unsigned int hnum, vnum;
 
index 42a6316..807cba2 100644 (file)
@@ -1443,7 +1443,7 @@ irqreturn_t vdin_isr_simple(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 #if 0
-/*#ifdef CONFIG_AML_LOCAL_DIMMING*/
+/*#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING*/
 unsigned int   vdin_ldim_max_global[100] = {0};
 static void vdin_backup_histgram_ldim(struct vframe_s *vf,
                struct vdin_dev_s *devp)
@@ -1696,7 +1696,7 @@ irqreturn_t vdin_isr(int irq, void *dev_id)
        vdin_set_drm_data(devp, curr_wr_vf);
        vdin_set_vframe_prop_info(curr_wr_vf, devp);
        vdin_backup_histgram(curr_wr_vf, devp);
-       #ifdef CONFIG_AML_LOCAL_DIMMING
+       #ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
        /*vdin_backup_histgram_ldim(curr_wr_vf, devp);*/
        #endif
        if ((devp->parm.port >= TVIN_PORT_HDMI0) &&
index e1f107c..c610744 100644 (file)
@@ -17,6 +17,8 @@ if AMLOGIC_VOUT
 source "drivers/amlogic/media/vout/cvbs/Kconfig"
 source "drivers/amlogic/media/vout/vdac/Kconfig"
 source "drivers/amlogic/media/vout/hdmitx/Kconfig"
+source "drivers/amlogic/media/vout/lcd/Kconfig"
+source "drivers/amlogic/media/vout/backlight/Kconfig"
 source "drivers/amlogic/media/vout/vout_serve/Kconfig"
 
 endif
index 5eea3dc..ec7ca51 100644 (file)
@@ -1,4 +1,7 @@
 obj-$(CONFIG_AMLOGIC_CVBS_OUTPUT)      += cvbs/
 obj-$(CONFIG_AMLOGIC_VDAC)             += vdac/
 obj-$(CONFIG_AMLOGIC_HDMITX)           += hdmitx/
+obj-$(CONFIG_AMLOGIC_LCD)              += lcd/
+obj-$(CONFIG_AMLOGIC_BACKLIGHT)                += backlight/
 obj-$(CONFIG_AMLOGIC_VOUT_SERVE)       += vout_serve/
+
diff --git a/drivers/amlogic/media/vout/backlight/Kconfig b/drivers/amlogic/media/vout/backlight/Kconfig
new file mode 100644 (file)
index 0000000..dd4debe
--- /dev/null
@@ -0,0 +1,19 @@
+
+menu "Amlogic Backlight Support"
+
+config AMLOGIC_BACKLIGHT
+       bool "Amlogic backlight support"
+       depends on BACKLIGHT_LCD_SUPPORT
+       depends on BACKLIGHT_CLASS_DEVICE
+       default n
+       help
+               Say Y here if you want to use the Amlogic backlight management.
+               Backlight have five ways, including gpio, pwm, pwm_combo,
+               local dimming, extern.
+
+if AMLOGIC_BACKLIGHT
+source "drivers/amlogic/media/vout/backlight/bl_extern/Kconfig"
+source "drivers/amlogic/media/vout/backlight/aml_ldim/Kconfig"
+endif
+
+endmenu
diff --git a/drivers/amlogic/media/vout/backlight/Makefile b/drivers/amlogic/media/vout/backlight/Makefile
new file mode 100644 (file)
index 0000000..27b6297
--- /dev/null
@@ -0,0 +1,2 @@
+obj-$(CONFIG_AMLOGIC_BACKLIGHT)                += aml_bl.o
+obj-$(CONFIG_AMLOGIC_BL_EXTERN)                += bl_extern/
\ No newline at end of file
diff --git a/drivers/amlogic/media/vout/backlight/aml_bl.c b/drivers/amlogic/media/vout/backlight/aml_bl.c
new file mode 100644 (file)
index 0000000..7278b4a
--- /dev/null
@@ -0,0 +1,2836 @@
+/*
+ * drivers/amlogic/media/vout/backlight/aml_bl.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/backlight.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/pwm.h>
+#include <linux/amlogic/pwm_meson.h>
+#include <linux/amlogic/cpu_version.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/amlogic/media/vout/lcd/aml_bl.h>
+#ifdef CONFIG_AMLOGIC_LCD
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#endif
+#ifdef CONFIG_AMLOGIC_BL_EXTERN
+#include <linux/amlogic/media/vout/lcd/aml_bl_extern.h>
+#endif
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+#include <linux/amlogic/media/vout/lcd/aml_ldim.h>
+#endif
+
+#include "aml_bl_reg.h"
+
+/* #define AML_BACKLIGHT_DEBUG */
+static unsigned int bl_debug_print_flag;
+
+static enum bl_chip_type_e bl_chip_type = BL_CHIP_MAX;
+static struct aml_bl_drv_s *bl_drv;
+
+static unsigned int bl_key_valid;
+static unsigned char bl_config_load;
+
+/* bl_off_policy support */
+static int aml_bl_off_policy_cnt;
+
+static unsigned int bl_off_policy;
+module_param(bl_off_policy, uint, 0664);
+MODULE_PARM_DESC(bl_off_policy, "bl_off_policy");
+
+static unsigned int bl_level_uboot;
+static unsigned int brightness_bypass;
+module_param(brightness_bypass, uint, 0664);
+MODULE_PARM_DESC(brightness_bypass, "bl_brightness_bypass");
+
+static unsigned char bl_pwm_bypass; /* debug flag */
+static unsigned char bl_pwm_duty_free; /* debug flag */
+static unsigned char bl_on_request; /* for lcd power sequence */
+static unsigned int bl_on_level;
+
+static DEFINE_MUTEX(bl_power_mutex);
+static DEFINE_MUTEX(bl_level_mutex);
+
+static struct bl_config_s bl_config = {
+       .level_default = 128,
+       .level_mid = 128,
+       .level_mid_mapping = 128,
+       .level_min = 10,
+       .level_max = 255,
+       .power_on_delay = 100,
+       .power_off_delay = 30,
+       .method = BL_CTRL_MAX,
+
+       .bl_pwm = NULL,
+       .bl_pwm_combo0 = NULL,
+       .bl_pwm_combo1 = NULL,
+       .pwm_on_delay = 0,
+       .pwm_off_delay = 0,
+
+       .bl_gpio = {
+               {.flag = 0,},
+               {.flag = 0,},
+               {.flag = 0,},
+               {.flag = 0,},
+               {.flag = 0,},
+       },
+};
+
+const char *bl_chip_table[] = {
+       "GXTVBB",
+       "GXM",
+       "GXL",
+       "TXL",
+       "TXLX",
+       "AXG",
+       "invalid",
+};
+
+struct bl_method_match_s {
+       char *name;
+       enum bl_ctrl_method_e type;
+};
+
+static struct bl_method_match_s bl_method_match_table[] = {
+       {"gpio",         BL_CTRL_GPIO},
+       {"pwm",          BL_CTRL_PWM},
+       {"pwm_combo",    BL_CTRL_PWM_COMBO},
+       {"local_diming", BL_CTRL_LOCAL_DIMING},
+       {"extern",       BL_CTRL_EXTERN},
+       {"invalid",      BL_CTRL_MAX},
+};
+
+static char *bl_method_type_to_str(int type)
+{
+       int i;
+       char *str = bl_method_match_table[BL_CTRL_MAX].name;
+
+       for (i = 0; i < BL_CTRL_MAX; i++) {
+               if (type == bl_method_match_table[i].type) {
+                       str = bl_method_match_table[i].name;
+                       break;
+               }
+       }
+       return str;
+}
+
+static unsigned int *pwm_reg;
+static unsigned int pwm_reg_gxtvbb[6] = {
+       PWM_PWM_A,
+       PWM_PWM_B,
+       PWM_PWM_C,
+       PWM_PWM_D,
+       PWM_PWM_E,
+       PWM_PWM_F,
+};
+
+static unsigned int pwm_reg_txlx[6] = {
+       PWM_PWM_A_TXLX,
+       PWM_PWM_B_TXLX,
+       PWM_PWM_C_TXLX,
+       PWM_PWM_D_TXLX,
+       PWM_PWM_E_TXLX,
+       PWM_PWM_F_TXLX,
+};
+
+enum bl_chip_type_e aml_bl_check_chip(void)
+{
+       unsigned int cpu_type;
+       enum bl_chip_type_e bl_chip = BL_CHIP_MAX;
+
+       cpu_type = get_cpu_type();
+       switch (cpu_type) {
+       case MESON_CPU_MAJOR_ID_GXTVBB:
+               bl_chip = BL_CHIP_GXTVBB;
+               pwm_reg = pwm_reg_gxtvbb;
+               break;
+       case MESON_CPU_MAJOR_ID_GXM:
+               bl_chip = BL_CHIP_GXM;
+               pwm_reg = pwm_reg_gxtvbb;
+               break;
+       case MESON_CPU_MAJOR_ID_GXL:
+               bl_chip = BL_CHIP_GXL;
+               pwm_reg = pwm_reg_gxtvbb;
+               break;
+       case MESON_CPU_MAJOR_ID_TXL:
+               bl_chip = BL_CHIP_TXL;
+               pwm_reg = pwm_reg_gxtvbb;
+               break;
+       case MESON_CPU_MAJOR_ID_TXLX:
+               bl_chip = BL_CHIP_TXLX;
+               pwm_reg = pwm_reg_txlx;
+               break;
+       case MESON_CPU_MAJOR_ID_AXG:
+               bl_chip = BL_CHIP_AXG;
+               pwm_reg = pwm_reg_txlx;
+               break;
+       default:
+               bl_chip = BL_CHIP_MAX;
+               pwm_reg = pwm_reg_gxtvbb;
+       }
+
+       if (bl_debug_print_flag)
+               BLPR("BL driver check chip : %s\n", bl_chip_table[bl_chip]);
+       return bl_chip;
+}
+
+static int aml_bl_check_driver(void)
+{
+       int ret = 0;
+
+       if (bl_drv == NULL) {
+               BLERR("no bl driver\n");
+               return -1;
+       }
+       switch (bl_drv->bconf->method) {
+       case BL_CTRL_PWM:
+               if (bl_drv->bconf->bl_pwm == NULL) {
+                       ret = -1;
+                       BLERR("no bl_pwm struct\n");
+               }
+               break;
+       case BL_CTRL_PWM_COMBO:
+               if (bl_drv->bconf->bl_pwm_combo0 == NULL) {
+                       ret = -1;
+                       BLERR("no bl_pwm_combo_0 struct\n");
+               }
+               if (bl_drv->bconf->bl_pwm_combo1 == NULL) {
+                       ret = -1;
+                       BLERR("no bl_pwm_combo_1 struct\n");
+               }
+               break;
+       case BL_CTRL_MAX:
+               ret = -1;
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+struct aml_bl_drv_s *aml_bl_get_driver(void)
+{
+       if (bl_drv == NULL)
+               BLERR("no bl driver");
+
+       return bl_drv;
+}
+
+static void bl_gpio_register(int index)
+{
+       struct bl_gpio_s *bl_gpio;
+       const char *str;
+       int ret;
+
+       if (index >= BL_GPIO_NUM_MAX) {
+               BLERR("gpio index %d, exit\n", index);
+               return;
+       }
+       bl_gpio = &bl_drv->bconf->bl_gpio[index];
+       if (bl_gpio->flag) {
+               if (bl_debug_print_flag) {
+                       BLPR("gpio %s[%d] is already registered\n",
+                               bl_gpio->name, index);
+               }
+               return;
+       }
+
+       /* get gpio name */
+       ret = of_property_read_string_index(bl_drv->dev->of_node,
+               "bl_gpio_names", index, &str);
+       if (ret) {
+               BLERR("failed to get bl_gpio_names: %d\n", index);
+               str = "unknown";
+       }
+       strcpy(bl_gpio->name, str);
+
+       /* request gpio */
+       bl_gpio->gpio = devm_gpiod_get_index(
+               bl_drv->dev, "bl", index, GPIOD_OUT_HIGH);
+       if (IS_ERR(bl_gpio->gpio)) {
+               BLERR("register gpio %s[%d]: %p, err: %d\n",
+                       bl_gpio->name, index, bl_gpio->gpio,
+                       IS_ERR(bl_gpio->gpio));
+       } else {
+               bl_gpio->flag = 1;
+               if (bl_debug_print_flag) {
+                       BLPR("register gpio %s[%d]: %p\n",
+                               bl_gpio->name, index, bl_gpio->gpio);
+               }
+       }
+}
+
+static void bl_gpio_set(int index, int value)
+{
+       struct bl_gpio_s *bl_gpio;
+
+       if (index >= BL_GPIO_NUM_MAX) {
+               BLERR("gpio index %d, exit\n", index);
+               return;
+       }
+       bl_gpio = &bl_drv->bconf->bl_gpio[index];
+       if (bl_gpio->flag == 0) {
+               BLERR("gpio [%d] is not registered\n", index);
+               return;
+       }
+       if (IS_ERR(bl_gpio->gpio)) {
+               BLERR("gpio %s[%d]: %p, err: %ld\n",
+                       bl_gpio->name, index, bl_gpio->gpio,
+                       PTR_ERR(bl_gpio->gpio));
+               return;
+       }
+
+       switch (value) {
+       case BL_GPIO_OUTPUT_LOW:
+       case BL_GPIO_OUTPUT_HIGH:
+               gpiod_direction_output(bl_gpio->gpio, value);
+               break;
+       case BL_GPIO_INPUT:
+       default:
+               gpiod_direction_input(bl_gpio->gpio);
+               break;
+       }
+       if (bl_debug_print_flag) {
+               BLPR("set gpio %s[%d] value: %d\n",
+                       bl_gpio->name, index, value);
+       }
+}
+
+/* ****************************************************** */
+static char *bl_pinmux_str[] = {
+       "pwm_on",               /* 0 */
+       "pwm_vs_on",            /* 1 */
+       "pwm_combo_0_1_on",     /* 2 */
+       "pwm_combo_0_vs_1_on",  /* 3 */
+       "pwm_combo_0_1_vs_on",  /* 4 */
+};
+
+static void bl_pwm_pinmux_set(struct bl_config_s *bconf)
+{
+       int index = 0xff;
+
+       if (bl_debug_print_flag)
+               BLPR("%s\n", __func__);
+
+       switch (bconf->method) {
+       case BL_CTRL_PWM:
+               if (bconf->bl_pwm->pwm_port == BL_PWM_VS)
+                       index = 1;
+               else
+                       index = 0;
+               break;
+       case BL_CTRL_PWM_COMBO:
+               if (bconf->bl_pwm_combo0->pwm_port == BL_PWM_VS)
+                       index = 3;
+               else {
+                       if (bconf->bl_pwm_combo1->pwm_port == BL_PWM_VS)
+                               index = 4;
+                       else
+                               index = 2;
+               }
+               break;
+       default:
+               BLERR("%s: wrong ctrl_mothod=%d\n", __func__, bconf->method);
+               break;
+       }
+
+       /* request pwm pinmux */
+       bconf->pin = devm_pinctrl_get_select(bl_drv->dev, bl_pinmux_str[index]);
+       if (IS_ERR(bconf->pin)) {
+               BLERR("set %s pinmux error\n", bl_pinmux_str[index]);
+       } else {
+               if (bl_debug_print_flag) {
+                       BLPR("request %s pinmux: %p\n",
+                               bl_pinmux_str[index], bconf->pin);
+               }
+       }
+}
+
+static void bl_pwm_request(struct bl_pwm_config_s *bl_pwm)
+{
+       unsigned int pwm_num = bl_pwm->pwm_port;
+
+       switch (bl_pwm->pwm_port) {
+       case BL_PWM_A:
+       case BL_PWM_B:
+       case BL_PWM_C:
+       case BL_PWM_D:
+       case BL_PWM_E:
+       case BL_PWM_F:
+               bl_pwm->bl_pwm_ch = pwm_request(pwm_num, NULL);
+               if (IS_ERR(bl_pwm->bl_pwm_ch)) {
+                       BLERR("request pwm %d failed\n", bl_pwm->pwm_port);
+                       return;
+               }
+
+               bl_pwm->bl_pwm_chip = to_aml_pwm_chip(bl_pwm->bl_pwm_ch->chip);
+               pwm_enable(bl_pwm->bl_pwm_ch);
+               break;
+       default:
+               break;
+       }
+}
+/* ****************************************************** */
+
+static int bl_pwm_out_level_check(struct bl_pwm_config_s *bl_pwm)
+{
+       int out_level = 0xff;
+
+       switch (bl_pwm->pwm_method) {
+       case BL_PWM_POSITIVE:
+               if (bl_pwm->pwm_duty == 0)
+                       out_level = 0;
+               else if (bl_pwm->pwm_duty == 100)
+                       out_level = 1;
+               else
+                       out_level = 0xff;
+               break;
+       case BL_PWM_NEGATIVE:
+               if (bl_pwm->pwm_duty == 0)
+                       out_level = 1;
+               else if (bl_pwm->pwm_duty == 100)
+                       out_level = 0;
+               else
+                       out_level = 0xff;
+               break;
+       default:
+               BLERR("%s: port %d: invalid pwm_method %d\n",
+                       __func__, bl_pwm->pwm_port, bl_pwm->pwm_method);
+               break;
+       }
+
+       return out_level;
+}
+
+static void bl_set_pwm_vs(struct bl_pwm_config_s *bl_pwm, int out_level)
+{
+       unsigned int pwm_hi, n, sw;
+       unsigned int vs[4], ve[4];
+       unsigned int pol = 0;
+       int i;
+
+       if (bl_debug_print_flag) {
+               BLPR("%s: pwm_duty=%d, out_level=%d\n",
+                       __func__, bl_pwm->pwm_duty, out_level);
+       }
+
+       if (out_level == 0) {
+               for (i = 0; i < 4; i++) {
+                       vs[i] = 0x1fff;
+                       ve[i] = 0;
+               }
+       } else if (out_level == 1) {
+               for (i = 0; i < 4; i++) {
+                       vs[i] = 0;
+                       ve[i] = 0x1fff;
+               }
+       } else {
+               if (bl_pwm->pwm_method == BL_PWM_NEGATIVE)
+                       pol = 1;
+               pwm_hi = bl_pwm->pwm_level;
+               n = bl_pwm->pwm_freq;
+               sw = (bl_pwm->pwm_cnt * 10 / n + 5) / 10;
+               pwm_hi = (pwm_hi * 10 / n + 5) / 10;
+               pwm_hi = (pwm_hi > 1) ? pwm_hi : 1;
+               if (bl_debug_print_flag)
+                       BLPR("n=%d, sw=%d, pwm_high=%d\n", n, sw, pwm_hi);
+               for (i = 0; i < n; i++) {
+                       vs[i] = 1 + (sw * i);
+                       ve[i] = vs[i] + pwm_hi - 1;
+               }
+               for (i = n; i < 4; i++) {
+                       vs[i] = 0xffff;
+                       ve[i] = 0xffff;
+               }
+       }
+       if (bl_debug_print_flag) {
+               for (i = 0; i < 4; i++) {
+                       BLPR("vs[%d]=%d, ve[%d]=%d\n",
+                               i, vs[i], i, ve[i]);
+               }
+       }
+
+       bl_vcbus_write(VPU_VPU_PWM_V0, (pol << 31) |
+                       (2 << 14) | /* vsync latch */
+                       (ve[0] << 16) | (vs[0]));
+       bl_vcbus_write(VPU_VPU_PWM_V1, (ve[1] << 16) | (vs[1]));
+       bl_vcbus_write(VPU_VPU_PWM_V2, (ve[2] << 16) | (vs[2]));
+       bl_vcbus_write(VPU_VPU_PWM_V3, (ve[3] << 16) | (vs[3]));
+
+       if (bl_debug_print_flag) {
+               BLPR("VPU_VPU_PWM_V0=0x%08x\n", bl_vcbus_read(VPU_VPU_PWM_V0));
+               BLPR("VPU_VPU_PWM_V1=0x%08x\n", bl_vcbus_read(VPU_VPU_PWM_V1));
+               BLPR("VPU_VPU_PWM_V2=0x%08x\n", bl_vcbus_read(VPU_VPU_PWM_V2));
+               BLPR("VPU_VPU_PWM_V3=0x%08x\n", bl_vcbus_read(VPU_VPU_PWM_V3));
+       }
+}
+
+void bl_pwm_ctrl(struct bl_pwm_config_s *bl_pwm, int status)
+{
+       int out_level;
+
+       if (status) {
+               /* enable pwm */
+               switch (bl_pwm->pwm_port) {
+               case BL_PWM_A:
+               case BL_PWM_B:
+               case BL_PWM_C:
+               case BL_PWM_D:
+               case BL_PWM_E:
+               case BL_PWM_F:
+                       if (!IS_ERR(bl_pwm->bl_pwm_ch))
+                               pwm_enable(bl_pwm->bl_pwm_ch);
+                       else
+                               BLERR("%s: invalid bl_pwm_ch\n", __func__);
+                       break;
+               case BL_PWM_VS:
+                       out_level = bl_pwm_out_level_check(bl_pwm);
+                       bl_set_pwm_vs(bl_pwm, out_level);
+                       break;
+               default:
+                       break;
+               }
+       } else {
+               /* disable pwm */
+               switch (bl_pwm->pwm_port) {
+               case BL_PWM_A:
+               case BL_PWM_B:
+               case BL_PWM_C:
+               case BL_PWM_D:
+               case BL_PWM_E:
+               case BL_PWM_F:
+                       if (!IS_ERR(bl_pwm->bl_pwm_ch))
+                               pwm_disable(bl_pwm->bl_pwm_ch);
+                       else
+                               BLERR("%s: invalid bl_pwm_ch\n", __func__);
+                       break;
+               case BL_PWM_VS:
+                       bl_set_pwm_vs(bl_pwm, 0);
+               default:
+                       break;
+               }
+       }
+}
+
+static void bl_power_en_ctrl(struct bl_config_s *bconf, int status)
+{
+       if (status) {
+               if (bconf->en_gpio < BL_GPIO_NUM_MAX)
+                       bl_gpio_set(bconf->en_gpio, bconf->en_gpio_on);
+       } else {
+               if (bconf->en_gpio < BL_GPIO_NUM_MAX)
+                       bl_gpio_set(bconf->en_gpio, bconf->en_gpio_off);
+       }
+}
+
+static void bl_power_on(void)
+{
+       struct bl_config_s *bconf = bl_drv->bconf;
+#ifdef CONFIG_AMLOGIC_BL_EXTERN
+       struct aml_bl_extern_driver_s *bl_ext;
+#endif
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+       struct aml_ldim_driver_s *ldim_drv;
+#endif
+       int ret;
+
+       if (aml_bl_check_driver())
+               return;
+
+       /* bl_off_policy */
+       if (bl_off_policy != BL_OFF_POLICY_NONE) {
+               BLPR("bl_off_policy=%d for bl_off\n", bl_off_policy);
+               return;
+       }
+
+       mutex_lock(&bl_power_mutex);
+
+       if (brightness_bypass == 0) {
+               if ((bl_drv->level == 0) ||
+                       (bl_drv->state & BL_STATE_BL_ON)) {
+                       goto exit_power_on_bl;
+               }
+       }
+
+       ret = 0;
+       switch (bconf->method) {
+       case BL_CTRL_GPIO:
+               bl_power_en_ctrl(bconf, 1);
+               break;
+       case BL_CTRL_PWM:
+               /* step 1: power on pwm */
+               bl_pwm_ctrl(bconf->bl_pwm, 1);
+               if (bconf->pwm_on_delay > 0)
+                       mdelay(bconf->pwm_on_delay);
+               /* step 2: power on enable */
+               bl_power_en_ctrl(bconf, 1);
+               break;
+       case BL_CTRL_PWM_COMBO:
+               /* step 1: power on pwm_combo */
+               bl_pwm_ctrl(bconf->bl_pwm_combo0, 1);
+               bl_pwm_ctrl(bconf->bl_pwm_combo1, 1);
+               if (bconf->pwm_on_delay > 0)
+                       mdelay(bconf->pwm_on_delay);
+               /* step 2: power on enable */
+               bl_power_en_ctrl(bconf, 1);
+               break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+       case BL_CTRL_LOCAL_DIMING:
+               /* step 1: power on enable */
+               bl_power_en_ctrl(bconf, 1);
+               /* step 2: power on ldim */
+               ldim_drv = aml_ldim_get_driver();
+               if (ldim_drv == NULL) {
+                       BLERR("no ldim driver\n");
+               } else {
+                       if (ldim_drv->power_on) {
+                               ret = ldim_drv->power_on();
+                               if (ret) {
+                                       BLERR("ldim: power on error\n");
+                                       goto exit_power_on_bl;
+                               }
+                       } else {
+                               BLPR("ldim: power on is null\n");
+                       }
+               }
+               break;
+#endif
+#ifdef CONFIG_AMLOGIC_BL_EXTERN
+       case BL_CTRL_EXTERN:
+               /* step 1: power on enable */
+               bl_power_en_ctrl(bconf, 1);
+               /* step 2: power on bl_extern */
+               bl_ext = aml_bl_extern_get_driver();
+               if (bl_ext == NULL) {
+                       BLERR("no bl_extern driver\n");
+               } else {
+                       if (bl_ext->power_on) {
+                               ret = bl_ext->power_on();
+                               if (ret) {
+                                       BLERR("bl_extern: power on error\n");
+                                       goto exit_power_on_bl;
+                               }
+                       } else {
+                               BLERR("bl_extern: power on is null\n");
+                       }
+               }
+               break;
+#endif
+       default:
+               BLPR("invalid backlight control method\n");
+               goto exit_power_on_bl;
+       }
+       bl_drv->state |= BL_STATE_BL_ON;
+       BLPR("backlight power on\n");
+
+exit_power_on_bl:
+       mutex_unlock(&bl_power_mutex);
+}
+
+static void bl_power_off(void)
+{
+       struct bl_config_s *bconf = bl_drv->bconf;
+#ifdef CONFIG_AMLOGIC_BL_EXTERN
+       struct aml_bl_extern_driver_s *bl_ext;
+#endif
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+       struct aml_ldim_driver_s *ldim_drv;
+#endif
+       int ret;
+
+       if (aml_bl_check_driver())
+               return;
+       mutex_lock(&bl_power_mutex);
+
+       if ((bl_drv->state & BL_STATE_BL_ON) == 0) {
+               mutex_unlock(&bl_power_mutex);
+               return;
+       }
+
+       ret = 0;
+       switch (bconf->method) {
+       case BL_CTRL_GPIO:
+               bl_power_en_ctrl(bconf, 0);
+               break;
+       case BL_CTRL_PWM:
+               /* step 1: power off enable */
+               bl_power_en_ctrl(bconf, 0);
+               /* step 2: power off pwm */
+               if (bconf->pwm_off_delay > 0)
+                       mdelay(bconf->pwm_off_delay);
+               bl_pwm_ctrl(bconf->bl_pwm, 0);
+               break;
+       case BL_CTRL_PWM_COMBO:
+               /* step 1: power off enable */
+               bl_power_en_ctrl(bconf, 0);
+               /* step 2: power off pwm_combo */
+               if (bconf->pwm_off_delay > 0)
+                       mdelay(bconf->pwm_off_delay);
+               bl_pwm_ctrl(bconf->bl_pwm_combo0, 0);
+               bl_pwm_ctrl(bconf->bl_pwm_combo1, 0);
+               break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+       case BL_CTRL_LOCAL_DIMING:
+               /* step 1: power off ldim */
+               ldim_drv = aml_ldim_get_driver();
+               if (ldim_drv == NULL) {
+                       BLERR("no ldim driver\n");
+               } else {
+                       if (ldim_drv->power_off) {
+                               ret = ldim_drv->power_off();
+                               if (ret)
+                                       BLERR("ldim: power off error\n");
+                       } else {
+                               BLERR("ldim: power off is null\n");
+                       }
+               }
+               /* step 2: power off enable */
+               bl_power_en_ctrl(bconf, 0);
+               break;
+#endif
+#ifdef CONFIG_AMLOGIC_BL_EXTERN
+       case BL_CTRL_EXTERN:
+               /* step 1: power off bl_extern */
+               bl_ext = aml_bl_extern_get_driver();
+               if (bl_ext == NULL) {
+                       BLERR("no bl_extern driver\n");
+               } else {
+                       if (bl_ext->power_off) {
+                               ret = bl_ext->power_off();
+                               if (ret)
+                                       BLERR("bl_extern: power off error\n");
+                       } else {
+                               BLERR("bl_extern: power off is null\n");
+                       }
+               }
+               /* step 2: power off enable */
+               bl_power_en_ctrl(bconf, 0);
+               break;
+#endif
+       default:
+               BLPR("invalid backlight control method\n");
+               break;
+       }
+       if (bconf->power_off_delay > 0)
+               mdelay(bconf->power_off_delay);
+
+       bl_drv->state &= ~BL_STATE_BL_ON;
+       BLPR("backlight power off\n");
+       mutex_unlock(&bl_power_mutex);
+}
+
+static unsigned int bl_level_mapping(unsigned int level)
+{
+       unsigned int mid = bl_drv->bconf->level_mid;
+       unsigned int mid_map = bl_drv->bconf->level_mid_mapping;
+       unsigned int max = bl_drv->bconf->level_max;
+       unsigned int min = bl_drv->bconf->level_min;
+
+       if (mid == mid_map)
+               return level;
+
+       level = level > max ? max : level;
+       if ((level >= mid) && (level <= max)) {
+               level = (((level - mid) * (max - mid_map)) / (max - mid)) +
+                       mid_map;
+       } else if ((level >= min) && (level < mid)) {
+               level = (((level - min) * (mid_map - min)) / (mid - min)) + min;
+       } else {
+               level = 0;
+       }
+       return level;
+}
+
+static void bl_set_pwm(struct bl_pwm_config_s *bl_pwm)
+{
+       unsigned int port = bl_pwm->pwm_port;
+       unsigned int pol = 0;
+       unsigned int pwm_period, pwm_duty, out_level = 0xff;
+
+       out_level = bl_pwm_out_level_check(bl_pwm);
+       if (bl_pwm->pwm_method == BL_PWM_NEGATIVE)
+               pol = 1;
+       if (bl_debug_print_flag) {
+               BLPR("port %d: pwm_duty=%d, out_level=%d, pol=%s\n",
+                       port, bl_pwm->pwm_duty, out_level,
+                       (pol ? "negative":"positive"));
+       }
+
+       switch (port) {
+       case BL_PWM_A:
+       case BL_PWM_B:
+       case BL_PWM_C:
+       case BL_PWM_D:
+       case BL_PWM_E:
+       case BL_PWM_F:
+               pwm_period = 1000000000 / bl_pwm->pwm_freq;
+               pwm_duty = (pwm_period * bl_pwm->pwm_duty) / 100;
+               if (!IS_ERR(bl_pwm->bl_pwm_ch)) {
+                       pwm_set_period(bl_pwm->bl_pwm_ch, pwm_period);
+                       pwm_config(bl_pwm->bl_pwm_ch, pwm_duty, pwm_period);
+                       pwm_set_polarity(bl_pwm->bl_pwm_ch, pol);
+                       if (out_level == 0xff) {
+                               pwm_constant_disable(bl_pwm->bl_pwm_chip,
+                                       bl_pwm->pwm_port);
+                       } else {
+                               /* pwm duty 100% or 0% special control */
+                               pwm_constant_enable(bl_pwm->bl_pwm_chip,
+                                       bl_pwm->pwm_port);
+                       }
+               } else {
+                       BLERR("%s: invalid bl_pwm_ch\n", __func__);
+               }
+               break;
+       case BL_PWM_VS:
+               bl_set_pwm_vs(bl_pwm, out_level);
+               break;
+       default:
+               break;
+       }
+}
+
+static void bl_set_duty_pwm(struct bl_pwm_config_s *bl_pwm)
+{
+       if (bl_pwm_bypass)
+               return;
+
+       if (bl_pwm_duty_free) {
+               if (bl_pwm->pwm_duty > 100) {
+                       BLERR("pwm_duty %d%% is bigger than 100%%\n",
+                               bl_pwm->pwm_duty);
+                       bl_pwm->pwm_duty = 100;
+                       BLPR("reset to 100%%\n");
+               }
+       } else {
+               if (bl_pwm->pwm_duty > bl_pwm->pwm_duty_max) {
+                       BLERR("pwm_duty %d%% is bigger than duty_max %d%%\n",
+                               bl_pwm->pwm_duty, bl_pwm->pwm_duty_max);
+                       bl_pwm->pwm_duty = bl_pwm->pwm_duty_max;
+                       BLPR("reset to duty_max\n");
+               } else if (bl_pwm->pwm_duty < bl_pwm->pwm_duty_min) {
+                       BLERR("pwm_duty %d%% is smaller than duty_min %d%%\n",
+                               bl_pwm->pwm_duty, bl_pwm->pwm_duty_min);
+                       bl_pwm->pwm_duty = bl_pwm->pwm_duty_min;
+                       BLPR("reset to duty_min\n");
+               }
+       }
+
+       bl_pwm->pwm_level =
+               (((bl_pwm->pwm_cnt * bl_pwm->pwm_duty / 10) + 5) / 10);
+       if (bl_debug_print_flag) {
+               BLPR("pwm port %d: duty=%d%%, duty_max=%d, duty_min=%d\n",
+                       bl_pwm->pwm_port, bl_pwm->pwm_duty,
+                       bl_pwm->pwm_duty_max, bl_pwm->pwm_duty_min);
+       }
+       bl_set_pwm(bl_pwm);
+}
+
+static void bl_set_level_pwm(struct bl_pwm_config_s *bl_pwm, unsigned int level)
+{
+       unsigned int min = bl_pwm->level_min;
+       unsigned int max = bl_pwm->level_max;
+       unsigned int pwm_max = bl_pwm->pwm_max;
+       unsigned int pwm_min = bl_pwm->pwm_min;
+
+       if (bl_pwm_bypass)
+               return;
+
+       level = bl_level_mapping(level);
+       max = bl_level_mapping(max);
+       min = bl_level_mapping(min);
+       if ((max <= min) || (level < min)) {
+               bl_pwm->pwm_level = pwm_min;
+       } else {
+               bl_pwm->pwm_level = ((pwm_max - pwm_min) * (level - min) /
+                       (max - min)) + pwm_min;
+       }
+
+       bl_pwm->pwm_duty =
+               (((bl_pwm->pwm_level * 1000 / bl_pwm->pwm_cnt) + 5) / 10);
+       if (bl_debug_print_flag) {
+               BLPR("port %d mapping: level=%d, level_max=%d, level_min=%d\n",
+                       bl_pwm->pwm_port, level, max, min);
+               BLPR("port %d: pwm_max=%d, pwm_min=%d, pwm_level=%d\n",
+                       bl_pwm->pwm_port, pwm_max, pwm_min, bl_pwm->pwm_level);
+               BLPR("port %d: duty=%d%%\n",
+                       bl_pwm->pwm_port, bl_pwm->pwm_duty);
+       }
+
+       bl_set_pwm(bl_pwm);
+}
+
+#ifdef CONFIG_AMLOGIC_BL_EXTERN
+static void bl_set_level_extern(unsigned int level)
+{
+       struct aml_bl_extern_driver_s *bl_ext;
+       int ret;
+
+       bl_ext = aml_bl_extern_get_driver();
+       if (bl_ext == NULL) {
+               BLERR("no bl_extern driver\n");
+       } else {
+               if (bl_ext->set_level) {
+                       ret = bl_ext->set_level(level);
+                       if (ret)
+                               BLERR("bl_extern: set_level error\n");
+               } else {
+                       BLERR("bl_extern: set_level is null\n");
+               }
+       }
+}
+#endif
+
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+static void bl_set_level_ldim(unsigned int level)
+{
+       struct aml_ldim_driver_s *ldim_drv;
+       int ret = 0;
+
+       ldim_drv = aml_ldim_get_driver();
+       if (ldim_drv == NULL) {
+               BLERR("no ldim driver\n");
+       } else {
+               if (ldim_drv->set_level) {
+                       ret = ldim_drv->set_level(level);
+                       if (ret)
+                               BLERR("ldim: set_level error\n");
+               } else {
+                       BLERR("ldim: set_level is null\n");
+               }
+       }
+}
+#endif
+
+static void aml_bl_set_level(unsigned int level)
+{
+       unsigned int temp;
+       struct bl_pwm_config_s *pwm0, *pwm1;
+
+       if (aml_bl_check_driver())
+               return;
+
+       if (bl_debug_print_flag) {
+               BLPR("aml_bl_set_level=%u, last_level=%u, state=0x%x\n",
+                       level, bl_drv->level, bl_drv->state);
+       }
+
+       /* level range check */
+       if (level > bl_drv->bconf->level_max)
+               level = bl_drv->bconf->level_max;
+       if (level < bl_drv->bconf->level_min) {
+               if (level < BL_LEVEL_OFF)
+                       level = 0;
+               else
+                       level = bl_drv->bconf->level_min;
+       }
+       bl_drv->level = level;
+
+       if (level == 0)
+               return;
+
+       switch (bl_drv->bconf->method) {
+       case BL_CTRL_GPIO:
+               break;
+       case BL_CTRL_PWM:
+               bl_set_level_pwm(bl_drv->bconf->bl_pwm, level);
+               break;
+       case BL_CTRL_PWM_COMBO:
+               pwm0 = bl_drv->bconf->bl_pwm_combo0;
+               pwm1 = bl_drv->bconf->bl_pwm_combo1;
+               if ((level >= pwm0->level_min) &&
+                       (level <= pwm0->level_max)) {
+                       temp = (pwm0->level_min > pwm1->level_min) ?
+                               pwm1->level_max : pwm1->level_min;
+                       if (bl_debug_print_flag) {
+                               BLPR("pwm0 region, level=%u, pwm1_level=%u\n",
+                                       level, temp);
+                       }
+                       bl_set_level_pwm(pwm0, level);
+                       bl_set_level_pwm(pwm1, temp);
+               } else if ((level >= pwm1->level_min) &&
+                       (level <= pwm1->level_max)) {
+                       temp = (pwm1->level_min > pwm0->level_min) ?
+                               pwm0->level_max : pwm0->level_min;
+                       if (bl_debug_print_flag) {
+                               BLPR("pwm1 region, level=%u, pwm0_level=%u\n",
+                                       level, temp);
+                       }
+                       bl_set_level_pwm(pwm0, temp);
+                       bl_set_level_pwm(pwm1, level);
+               }
+               break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+       case BL_CTRL_LOCAL_DIMING:
+               bl_set_level_ldim(level);
+               break;
+#endif
+#ifdef CONFIG_AML_BL_TABLET_EXTERN
+       case BL_CTRL_EXTERN:
+               bl_set_level_extern(level);
+               break;
+#endif
+       default:
+               if (bl_debug_print_flag)
+                       BLPR("invalid backlight control method\n");
+               break;
+       }
+}
+
+static unsigned int aml_bl_get_level(void)
+{
+       if (aml_bl_check_driver())
+               return 0;
+
+       BLPR("aml bl state: 0x%x\n", bl_drv->state);
+       return bl_drv->level;
+}
+
+static int aml_bl_update_status(struct backlight_device *bd)
+{
+       int brightness = bd->props.brightness;
+
+       if (brightness_bypass)
+               return 0;
+
+       mutex_lock(&bl_level_mutex);
+       if (brightness < 0)
+               brightness = 0;
+       else if (brightness > 255)
+               brightness = 255;
+
+       if (((bl_drv->state & BL_STATE_LCD_ON) == 0) ||
+               ((bl_drv->state & BL_STATE_BL_POWER_ON) == 0))
+               brightness = 0;
+
+       if (bl_debug_print_flag) {
+               BLPR("%s: %u, real brightness: %u, state: 0x%x\n",
+                       __func__, bd->props.brightness,
+                       brightness, bl_drv->state);
+       }
+       if (brightness == 0) {
+               if (bl_drv->state & BL_STATE_BL_ON)
+                       bl_power_off();
+       } else {
+               aml_bl_set_level(brightness);
+               if ((bl_drv->state & BL_STATE_BL_ON) == 0)
+                       bl_power_on();
+       }
+       mutex_unlock(&bl_level_mutex);
+       return 0;
+}
+
+static int aml_bl_get_brightness(struct backlight_device *bd)
+{
+       return aml_bl_get_level();
+}
+
+static const struct backlight_ops aml_bl_ops = {
+       .get_brightness = aml_bl_get_brightness,
+       .update_status  = aml_bl_update_status,
+};
+
+#ifdef CONFIG_OF
+static char *bl_pwm_name[] = {
+       "PWM_A",
+       "PWM_B",
+       "PWM_C",
+       "PWM_D",
+       "PWM_E",
+       "PWM_F",
+       "PWM_VS",
+};
+
+enum bl_pwm_port_e bl_pwm_str_to_pwm(const char *str)
+{
+       enum bl_pwm_port_e pwm_port = BL_PWM_MAX;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(bl_pwm_name); i++) {
+               if (strcmp(str, bl_pwm_name[i]) == 0) {
+                       pwm_port = i;
+                       break;
+               }
+       }
+
+       return pwm_port;
+}
+
+void bl_pwm_config_init(struct bl_pwm_config_s *bl_pwm)
+{
+       unsigned int freq, cnt, pre_div;
+       int i;
+
+       if (bl_debug_print_flag) {
+               BLPR("%s pwm_port %d: freq = %u\n",
+                       __func__, bl_pwm->pwm_port, bl_pwm->pwm_freq);
+       }
+       freq = bl_pwm->pwm_freq;
+       switch (bl_pwm->pwm_port) {
+       case BL_PWM_VS:
+               cnt = bl_vcbus_read(ENCL_VIDEO_MAX_LNCNT) + 1;
+               bl_pwm->pwm_cnt = cnt;
+               bl_pwm->pwm_pre_div = 0;
+               if (bl_debug_print_flag)
+                       BLPR("pwm_cnt = %u\n", bl_pwm->pwm_cnt);
+               break;
+       default:
+               for (i = 0; i < 0x7f; i++) {
+                       pre_div = i;
+                       cnt = XTAL_FREQ_HZ / (freq * (pre_div + 1)) - 2;
+                       if (cnt <= 0xffff)
+                               break;
+               }
+               bl_pwm->pwm_cnt = cnt;
+               bl_pwm->pwm_pre_div = pre_div;
+               if (bl_debug_print_flag)
+                       BLPR("pwm_cnt = %u, pwm_pre_div = %u\n", cnt, pre_div);
+               break;
+       }
+       bl_pwm->pwm_max = (bl_pwm->pwm_cnt * bl_pwm->pwm_duty_max / 100);
+       bl_pwm->pwm_min = (bl_pwm->pwm_cnt * bl_pwm->pwm_duty_min / 100);
+
+       if (bl_debug_print_flag) {
+               BLPR("pwm_max = %u, pwm_min = %u\n",
+                       bl_pwm->pwm_max, bl_pwm->pwm_min);
+       }
+}
+
+static void aml_bl_config_print(struct bl_config_s *bconf)
+{
+       struct bl_pwm_config_s *bl_pwm;
+
+       if (bconf->method == BL_CTRL_MAX) {
+               BLPR("no backlight exist\n");
+               return;
+       }
+
+       BLPR("name              = %s\n", bconf->name);
+       BLPR("method            = %s(%d)\n",
+               bl_method_type_to_str(bconf->method), bconf->method);
+
+       if (bl_debug_print_flag == 0)
+               return;
+
+       BLPR("level_default     = %d\n", bconf->level_default);
+       BLPR("level_min         = %d\n", bconf->level_min);
+       BLPR("level_max         = %d\n", bconf->level_max);
+       BLPR("level_mid         = %d\n", bconf->level_mid);
+       BLPR("level_mid_mapping = %d\n", bconf->level_mid_mapping);
+
+       BLPR("en_gpio           = %d\n", bconf->en_gpio);
+       BLPR("en_gpio_on        = %d\n", bconf->en_gpio_on);
+       BLPR("en_gpio_off       = %d\n", bconf->en_gpio_off);
+       BLPR("power_on_delay    = %dms\n", bconf->power_on_delay);
+       BLPR("power_off_delay   = %dms\n\n", bconf->power_off_delay);
+
+       switch (bconf->method) {
+       case BL_CTRL_PWM:
+               BLPR("pwm_on_delay        = %dms\n", bconf->pwm_on_delay);
+               BLPR("pwm_off_delay       = %dms\n", bconf->pwm_off_delay);
+               if (bconf->bl_pwm) {
+                       bl_pwm = bconf->bl_pwm;
+                       BLPR("pwm_index     = %d\n", bl_pwm->index);
+                       BLPR("pwm_method    = %d\n", bl_pwm->pwm_method);
+                       BLPR("pwm_port      = %d\n", bl_pwm->pwm_port);
+                       if (bl_pwm->pwm_port == BL_PWM_VS) {
+                               BLPR("pwm_freq      = %d x vfreq\n",
+                                       bl_pwm->pwm_freq);
+                               BLPR("pwm_cnt       = %u\n", bl_pwm->pwm_cnt);
+                       } else {
+                               BLPR("pwm_freq      = %uHz\n",
+                                       bl_pwm->pwm_freq);
+                               BLPR("pwm_cnt       = %u\n", bl_pwm->pwm_cnt);
+                               BLPR("pwm_pre_div   = %u\n",
+                                       bl_pwm->pwm_pre_div);
+                       }
+                       BLPR("pwm_level_max = %u\n", bl_pwm->level_max);
+                       BLPR("pwm_level_min = %u\n", bl_pwm->level_min);
+                       BLPR("pwm_duty_max  = %d%%\n", bl_pwm->pwm_duty_max);
+                       BLPR("pwm_duty_min  = %d%%\n", bl_pwm->pwm_duty_min);
+                       BLPR("pwm_max       = %u\n", bl_pwm->pwm_max);
+                       BLPR("pwm_min       = %u\n", bl_pwm->pwm_min);
+               }
+               break;
+       case BL_CTRL_PWM_COMBO:
+               BLPR("pwm_on_delay        = %dms\n", bconf->pwm_on_delay);
+               BLPR("pwm_off_delay       = %dms\n", bconf->pwm_off_delay);
+               /* pwm_combo_0 */
+               if (bconf->bl_pwm_combo0) {
+                       bl_pwm = bconf->bl_pwm_combo0;
+                       BLPR("pwm_combo0_index     = %d\n", bl_pwm->index);
+                       BLPR("pwm_combo0_method    = %d\n", bl_pwm->pwm_method);
+                       BLPR("pwm_combo0_port      = %d\n", bl_pwm->pwm_port);
+                       if (bl_pwm->pwm_port == BL_PWM_VS) {
+                               BLPR("pwm_combo0_freq      = %d x vfreq\n",
+                                       bl_pwm->pwm_freq);
+                               BLPR("pwm_combo0_cnt       = %u\n",
+                                       bl_pwm->pwm_cnt);
+                       } else {
+                               BLPR("pwm_combo0_freq      = %uHz\n",
+                                       bl_pwm->pwm_freq);
+                               BLPR("pwm_combo0_cnt       = %u\n",
+                                       bl_pwm->pwm_cnt);
+                               BLPR("pwm_combo0_pre_div   = %u\n",
+                                       bl_pwm->pwm_pre_div);
+                       }
+                       BLPR("pwm_combo0_level_max = %u\n", bl_pwm->level_max);
+                       BLPR("pwm_combo0_level_min = %u\n", bl_pwm->level_min);
+                       BLPR("pwm_combo0_duty_max  = %d\n",
+                               bl_pwm->pwm_duty_max);
+                       BLPR("pwm_combo0_duty_min  = %d\n",
+                               bl_pwm->pwm_duty_min);
+                       BLPR("pwm_combo0_max       = %u\n", bl_pwm->pwm_max);
+                       BLPR("pwm_combo0_min       = %u\n", bl_pwm->pwm_min);
+               }
+               /* pwm_combo_1 */
+               if (bconf->bl_pwm_combo1) {
+                       bl_pwm = bconf->bl_pwm_combo1;
+                       BLPR("pwm_combo1_index     = %d\n", bl_pwm->index);
+                       BLPR("pwm_combo1_method    = %d\n", bl_pwm->pwm_method);
+                       BLPR("pwm_combo1_port      = %d\n", bl_pwm->pwm_port);
+                       if (bl_pwm->pwm_port == BL_PWM_VS) {
+                               BLPR("pwm_combo1_freq      = %d x vfreq\n",
+                                       bl_pwm->pwm_freq);
+                               BLPR("pwm_combo1_cnt       = %u\n",
+                                       bl_pwm->pwm_cnt);
+                       } else {
+                               BLPR("pwm_combo1_freq      = %uHz\n",
+                                       bl_pwm->pwm_freq);
+                               BLPR("pwm_combo1_cnt       = %u\n",
+                                       bl_pwm->pwm_cnt);
+                               BLPR("pwm_combo1_pre_div   = %u\n",
+                                       bl_pwm->pwm_pre_div);
+                       }
+                       BLPR("pwm_combo1_level_max = %u\n", bl_pwm->level_max);
+                       BLPR("pwm_combo1_level_min = %u\n", bl_pwm->level_min);
+                       BLPR("pwm_combo1_duty_max  = %d\n",
+                               bl_pwm->pwm_duty_max);
+                       BLPR("pwm_combo1_duty_min  = %d\n",
+                               bl_pwm->pwm_duty_min);
+                       BLPR("pwm_combo1_max       = %u\n", bl_pwm->pwm_max);
+                       BLPR("pwm_combo1_min       = %u\n", bl_pwm->pwm_min);
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static int aml_bl_config_load_from_dts(struct bl_config_s *bconf,
+               struct platform_device *pdev)
+{
+       int ret = 0;
+       int val;
+       const char *str;
+       unsigned int bl_para[10];
+       char bl_propname[20];
+       int index = BL_INDEX_DEFAULT;
+       struct device_node *child;
+       struct bl_pwm_config_s *bl_pwm;
+       struct bl_pwm_config_s *pwm_combo0, *pwm_combo1;
+
+       /* select backlight by index */
+#ifdef CONFIG_AMLOGIC_LCD
+       aml_lcd_notifier_call_chain(LCD_EVENT_BACKLIGHT_SEL, &index);
+#endif
+       bl_drv->index = index;
+       if (bl_drv->index == 0xff) {
+               bconf->method = BL_CTRL_MAX;
+               return -1;
+       }
+       sprintf(bl_propname, "backlight_%d", index);
+       BLPR("load: %s\n", bl_propname);
+       child = of_get_child_by_name(pdev->dev.of_node, bl_propname);
+       if (child == NULL) {
+               BLERR("failed to get %s\n", bl_propname);
+               return -1;
+       }
+
+       ret = of_property_read_string(child, "bl_name", &str);
+       if (ret) {
+               BLERR("failed to get bl_name\n");
+               str = "backlight";
+       }
+       strcpy(bconf->name, str);
+
+       ret = of_property_read_u32_array(child, "bl_level_default_uboot_kernel",
+               &bl_para[0], 2);
+       if (ret) {
+               BLERR("failed to get bl_level_default_uboot_kernel\n");
+               bl_level_uboot = BL_LEVEL_DEFAULT;
+               bconf->level_default = BL_LEVEL_DEFAULT;
+       } else {
+               bl_level_uboot = bl_para[0];
+               bconf->level_default = bl_para[1];
+       }
+       ret = of_property_read_u32_array(child, "bl_level_attr",
+               &bl_para[0], 4);
+       if (ret) {
+               BLERR("failed to get bl_level_attr\n");
+               bconf->level_min = BL_LEVEL_MIN;
+               bconf->level_max = BL_LEVEL_MAX;
+               bconf->level_mid = BL_LEVEL_MID;
+               bconf->level_mid_mapping = BL_LEVEL_MID_MAPPED;
+       } else {
+               bconf->level_max = bl_para[0];
+               bconf->level_min = bl_para[1];
+               bconf->level_mid = bl_para[2];
+               bconf->level_mid_mapping = bl_para[3];
+       }
+       /* adjust brightness_bypass by level_default */
+       if (bconf->level_default > bconf->level_max) {
+               brightness_bypass = 1;
+               BLPR("level_default > level_max, enable brightness_bypass\n");
+       }
+
+       ret = of_property_read_u32(child, "bl_ctrl_method", &val);
+       if (ret) {
+               BLERR("failed to get bl_ctrl_method\n");
+               bconf->method = BL_CTRL_MAX;
+       } else {
+               bconf->method = (val >= BL_CTRL_MAX) ? BL_CTRL_MAX : val;
+       }
+       ret = of_property_read_u32_array(child, "bl_power_attr",
+               &bl_para[0], 5);
+       if (ret) {
+               BLERR("failed to get bl_power_attr\n");
+               bconf->en_gpio = BL_GPIO_MAX;
+               bconf->en_gpio_on = BL_GPIO_OUTPUT_HIGH;
+               bconf->en_gpio_off = BL_GPIO_OUTPUT_LOW;
+               bconf->power_on_delay = 100;
+               bconf->power_off_delay = 30;
+       } else {
+               if (bl_para[0] >= BL_GPIO_NUM_MAX) {
+                       bconf->en_gpio = BL_GPIO_MAX;
+               } else {
+                       bconf->en_gpio = bl_para[0];
+                       bl_gpio_register(bconf->en_gpio);
+               }
+               bconf->en_gpio_on = bl_para[1];
+               bconf->en_gpio_off = bl_para[2];
+               bconf->power_on_delay = bl_para[3];
+               bconf->power_off_delay = bl_para[4];
+       }
+
+       switch (bconf->method) {
+       case BL_CTRL_PWM:
+               bconf->bl_pwm = kzalloc(sizeof(struct bl_pwm_config_s),
+                               GFP_KERNEL);
+               if (bconf->bl_pwm == NULL) {
+                       BLERR("bl_pwm struct malloc error\n");
+                       return -1;
+               }
+               bl_pwm = bconf->bl_pwm;
+               bl_pwm->index = 0;
+
+               bl_pwm->level_max = bconf->level_max;
+               bl_pwm->level_min = bconf->level_min;
+
+               ret = of_property_read_string(child, "bl_pwm_port", &str);
+               if (ret) {
+                       BLERR("failed to get bl_pwm_port\n");
+                       bl_pwm->pwm_port = BL_PWM_MAX;
+               } else {
+                       bl_pwm->pwm_port = bl_pwm_str_to_pwm(str);
+                       BLPR("bl pwm_port: %s(%u)\n", str, bl_pwm->pwm_port);
+               }
+               ret = of_property_read_u32_array(child, "bl_pwm_attr",
+                       &bl_para[0], 4);
+               if (ret) {
+                       BLERR("failed to get bl_pwm_attr\n");
+                       bl_pwm->pwm_method = BL_PWM_POSITIVE;
+                       if (bl_pwm->pwm_port == BL_PWM_VS)
+                               bl_pwm->pwm_freq = BL_FREQ_VS_DEFAULT;
+                       else
+                               bl_pwm->pwm_freq = BL_FREQ_DEFAULT;
+                       bl_pwm->pwm_duty_max = 80;
+                       bl_pwm->pwm_duty_min = 20;
+               } else {
+                       bl_pwm->pwm_method = bl_para[0];
+                       bl_pwm->pwm_freq = bl_para[1];
+                       bl_pwm->pwm_duty_max = bl_para[2];
+                       bl_pwm->pwm_duty_min = bl_para[3];
+               }
+               if (bl_pwm->pwm_port == BL_PWM_VS) {
+                       if (bl_pwm->pwm_freq > 4) {
+                               BLERR("bl_pwm_vs wrong freq %d\n",
+                                       bl_pwm->pwm_freq);
+                               bl_pwm->pwm_freq = BL_FREQ_VS_DEFAULT;
+                       }
+               } else {
+                       if (bl_pwm->pwm_freq > XTAL_HALF_FREQ_HZ)
+                               bl_pwm->pwm_freq = XTAL_HALF_FREQ_HZ;
+                       if (bl_pwm->pwm_freq < 50)
+                               bl_pwm->pwm_freq = 50;
+               }
+               ret = of_property_read_u32_array(child, "bl_pwm_power",
+                       &bl_para[0], 4);
+               if (ret) {
+                       BLERR("failed to get bl_pwm_power\n");
+                       bconf->pwm_on_delay = 0;
+                       bconf->pwm_off_delay = 0;
+               } else {
+                       bconf->pwm_on_delay = bl_para[2];
+                       bconf->pwm_off_delay = bl_para[3];
+               }
+
+               bl_pwm->pwm_duty = bl_pwm->pwm_duty_min;
+               /* init pwm config */
+               bl_pwm_config_init(bl_pwm);
+               break;
+       case BL_CTRL_PWM_COMBO:
+               bconf->bl_pwm_combo0 = kzalloc(sizeof(struct bl_pwm_config_s),
+                               GFP_KERNEL);
+               if (bconf->bl_pwm_combo0 == NULL) {
+                       BLERR("bl_pwm_combo0 struct malloc error\n");
+                       return -1;
+               }
+               bconf->bl_pwm_combo1 = kzalloc(sizeof(struct bl_pwm_config_s),
+                               GFP_KERNEL);
+               if (bconf->bl_pwm_combo1 == NULL) {
+                       BLERR("bl_pwm_combo1 struct malloc error\n");
+                       return -1;
+               }
+               pwm_combo0 = bconf->bl_pwm_combo0;
+               pwm_combo1 = bconf->bl_pwm_combo1;
+
+               pwm_combo0->index = 0;
+               pwm_combo1->index = 1;
+
+               ret = of_property_read_string_index(child, "bl_pwm_combo_port",
+                       0, &str);
+               if (ret) {
+                       BLERR("failed to get bl_pwm_combo_port\n");
+                       pwm_combo0->pwm_port = BL_PWM_MAX;
+               } else {
+                       pwm_combo0->pwm_port = bl_pwm_str_to_pwm(str);
+               }
+               ret = of_property_read_string_index(child, "bl_pwm_combo_port",
+                       1, &str);
+               if (ret) {
+                       BLERR("failed to get bl_pwm_combo_port\n");
+                       pwm_combo1->pwm_port = BL_PWM_MAX;
+               } else {
+                       pwm_combo1->pwm_port = bl_pwm_str_to_pwm(str);
+               }
+               BLPR("pwm_combo_port: %s(%u), %s(%u)\n",
+                       bl_pwm_name[pwm_combo0->pwm_port], pwm_combo0->pwm_port,
+                       bl_pwm_name[pwm_combo1->pwm_port],
+                       pwm_combo1->pwm_port);
+               ret = of_property_read_u32_array(child,
+                       "bl_pwm_combo_level_mapping", &bl_para[0], 4);
+               if (ret) {
+                       BLERR("failed to get bl_pwm_combo_level_mapping\n");
+                       pwm_combo0->level_max = BL_LEVEL_MAX;
+                       pwm_combo0->level_min = BL_LEVEL_MID;
+                       pwm_combo1->level_max = BL_LEVEL_MID;
+                       pwm_combo1->level_min = BL_LEVEL_MIN;
+               } else {
+                       pwm_combo0->level_max = bl_para[0];
+                       pwm_combo0->level_min = bl_para[1];
+                       pwm_combo1->level_max = bl_para[2];
+                       pwm_combo1->level_min = bl_para[3];
+               }
+               ret = of_property_read_u32_array(child, "bl_pwm_combo_attr",
+                       &bl_para[0], 8);
+               if (ret) {
+                       BLERR("failed to get bl_pwm_combo_attr\n");
+                       pwm_combo0->pwm_method = BL_PWM_POSITIVE;
+                       if (pwm_combo0->pwm_port == BL_PWM_VS)
+                               pwm_combo0->pwm_freq = BL_FREQ_VS_DEFAULT;
+                       else
+                               pwm_combo0->pwm_freq = BL_FREQ_DEFAULT;
+                       pwm_combo0->pwm_duty_max = 80;
+                       pwm_combo0->pwm_duty_min = 20;
+                       pwm_combo1->pwm_method = BL_PWM_NEGATIVE;
+                       if (pwm_combo1->pwm_port == BL_PWM_VS)
+                               pwm_combo1->pwm_freq = BL_FREQ_VS_DEFAULT;
+                       else
+                               pwm_combo1->pwm_freq = BL_FREQ_DEFAULT;
+                       pwm_combo1->pwm_duty_max = 80;
+                       pwm_combo1->pwm_duty_min = 20;
+               } else {
+                       pwm_combo0->pwm_method = bl_para[0];
+                       pwm_combo0->pwm_freq = bl_para[1];
+                       pwm_combo0->pwm_duty_max = bl_para[2];
+                       pwm_combo0->pwm_duty_min = bl_para[3];
+                       pwm_combo1->pwm_method = bl_para[4];
+                       pwm_combo1->pwm_freq = bl_para[5];
+                       pwm_combo1->pwm_duty_max = bl_para[6];
+                       pwm_combo1->pwm_duty_min = bl_para[7];
+               }
+               if (pwm_combo0->pwm_port == BL_PWM_VS) {
+                       if (pwm_combo0->pwm_freq > 4) {
+                               BLERR("bl_pwm_0_vs wrong freq %d\n",
+                                       pwm_combo0->pwm_freq);
+                               pwm_combo0->pwm_freq = BL_FREQ_VS_DEFAULT;
+                       }
+               } else {
+                       if (pwm_combo0->pwm_freq > XTAL_HALF_FREQ_HZ)
+                               pwm_combo0->pwm_freq = XTAL_HALF_FREQ_HZ;
+                       if (pwm_combo0->pwm_freq < 50)
+                               pwm_combo0->pwm_freq = 50;
+               }
+               if (pwm_combo1->pwm_port == BL_PWM_VS) {
+                       if (pwm_combo1->pwm_freq > 4) {
+                               BLERR("bl_pwm_1_vs wrong freq %d\n",
+                                       pwm_combo1->pwm_freq);
+                               pwm_combo1->pwm_freq = BL_FREQ_VS_DEFAULT;
+                       }
+               } else {
+                       if (pwm_combo1->pwm_freq > XTAL_HALF_FREQ_HZ)
+                               pwm_combo1->pwm_freq = XTAL_HALF_FREQ_HZ;
+                       if (pwm_combo1->pwm_freq < 50)
+                               pwm_combo1->pwm_freq = 50;
+               }
+               ret = of_property_read_u32_array(child, "bl_pwm_combo_power",
+                       &bl_para[0], 6);
+               if (ret) {
+                       BLERR("failed to get bl_pwm_combo_power\n");
+                       bconf->pwm_on_delay = 0;
+                       bconf->pwm_off_delay = 0;
+               } else {
+                       bconf->pwm_on_delay = bl_para[4];
+                       bconf->pwm_off_delay = bl_para[5];
+               }
+
+               pwm_combo0->pwm_duty = pwm_combo0->pwm_duty_min;
+               pwm_combo1->pwm_duty = pwm_combo1->pwm_duty_min;
+               /* init pwm config */
+               bl_pwm_config_init(pwm_combo0);
+               bl_pwm_config_init(pwm_combo1);
+               break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+       case BL_CTRL_LOCAL_DIMING:
+               break;
+#endif
+       case BL_CTRL_EXTERN:
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+#endif
+
+static int aml_bl_config_load_from_unifykey(struct bl_config_s *bconf)
+{
+       unsigned char *para;
+       int key_len, len;
+       unsigned char *p;
+       const char *str;
+       unsigned char temp;
+       struct aml_lcd_unifykey_header_s bl_header;
+       struct bl_pwm_config_s *bl_pwm;
+       struct bl_pwm_config_s *pwm_combo0, *pwm_combo1;
+       int ret;
+
+       para = kmalloc((sizeof(unsigned char) * LCD_UKEY_BL_SIZE), GFP_KERNEL);
+       if (!para) {
+               BLERR("%s: Not enough memory\n", __func__);
+               return -1;
+       }
+
+       key_len = LCD_UKEY_BL_SIZE;
+       memset(para, 0, (sizeof(unsigned char) * key_len));
+       ret = lcd_unifykey_get("backlight", para, &key_len);
+       if (ret < 0) {
+               kfree(para);
+               return -1;
+       }
+
+       /* check backlight unifykey length */
+       len = 10 + 30 + 12 + 8 + 32;
+       ret = lcd_unifykey_len_check(key_len, len);
+       if (ret < 0) {
+               BLERR("unifykey length is not correct\n");
+               kfree(para);
+               return -1;
+       }
+
+       /* header: 10byte */
+       lcd_unifykey_header_check(para, &bl_header);
+       if (bl_debug_print_flag) {
+               BLPR("unifykey header:\n");
+               BLPR("crc32             = 0x%08x\n", bl_header.crc32);
+               BLPR("data_len          = %d\n", bl_header.data_len);
+               BLPR("version           = 0x%04x\n", bl_header.version);
+               BLPR("reserved          = 0x%04x\n", bl_header.reserved);
+       }
+
+       /* basic: 30byte */
+       p = para + LCD_UKEY_HEAD_SIZE;
+       *(p + LCD_UKEY_BL_NAME - 1) = '\0'; /* ensure string ending */
+       str = (const char *)p;
+       strcpy(bconf->name, str);
+       p += LCD_UKEY_BL_NAME;
+
+       /* level: 6byte */
+       bl_level_uboot = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_BL_LEVEL_UBOOT;
+       bconf->level_default = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_BL_LEVEL_KERNEL;  /* dummy pointer */
+       bconf->level_max = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_BL_LEVEL_MAX;
+       bconf->level_min = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_BL_LEVEL_MIN;
+       bconf->level_mid = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_BL_LEVEL_MID;
+       bconf->level_mid_mapping = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_BL_LEVEL_MID_MAP;
+
+       /* adjust brightness_bypass by level_default */
+       if (bconf->level_default > bconf->level_max) {
+               brightness_bypass = 1;
+               BLPR("level_default > level_max, enable brightness_bypass\n");
+       }
+
+       /* method: 8byte */
+       temp = *p;
+       bconf->method = (temp >= BL_CTRL_MAX) ? BL_CTRL_MAX : temp;
+       p += LCD_UKEY_BL_METHOD;
+
+       temp = *p;
+       if (temp >= BL_GPIO_NUM_MAX) {
+               bconf->en_gpio = BL_GPIO_MAX;
+       } else {
+               bconf->en_gpio = temp;
+               bl_gpio_register(bconf->en_gpio);
+       }
+       p += LCD_UKEY_BL_EN_GPIO;
+       bconf->en_gpio_on = *p;
+       p += LCD_UKEY_BL_EN_GPIO_ON;
+       bconf->en_gpio_off = *p;
+       p += LCD_UKEY_BL_EN_GPIO_OFF;
+       bconf->power_on_delay = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_BL_ON_DELAY;
+       bconf->power_off_delay = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_BL_OFF_DELAY;
+
+       /* pwm: 24byte */
+       switch (bconf->method) {
+       case BL_CTRL_PWM:
+               bconf->bl_pwm = kzalloc(sizeof(struct bl_pwm_config_s),
+                               GFP_KERNEL);
+               if (bconf->bl_pwm == NULL) {
+                       BLERR("bl_pwm struct malloc error\n");
+                       kfree(para);
+                       return -1;
+               }
+               bl_pwm = bconf->bl_pwm;
+               bl_pwm->index = 0;
+
+               bl_pwm->level_max = bconf->level_max;
+               bl_pwm->level_min = bconf->level_min;
+
+               bconf->pwm_on_delay = (*p | ((*(p + 1)) << 8));
+               p += LCD_UKEY_BL_PWM_ON_DELAY;
+               bconf->pwm_off_delay = (*p | ((*(p + 1)) << 8));
+               p += LCD_UKEY_BL_PWM_OFF_DELAY;
+               bl_pwm->pwm_method =  *p;
+               p += LCD_UKEY_BL_PWM_METHOD;
+               bl_pwm->pwm_port = *p;
+               p += LCD_UKEY_BL_PWM_PORT;
+               bl_pwm->pwm_freq = (*p | ((*(p + 1)) << 8) |
+                               ((*(p + 2)) << 8) | ((*(p + 3)) << 8));
+               if (bl_pwm->pwm_port == BL_PWM_VS) {
+                       if (bl_pwm->pwm_freq > 4) {
+                               BLERR("bl_pwm_vs wrong freq %d\n",
+                                       bl_pwm->pwm_freq);
+                               bl_pwm->pwm_freq = BL_FREQ_VS_DEFAULT;
+                       }
+               } else {
+                       if (bl_pwm->pwm_freq > XTAL_HALF_FREQ_HZ)
+                               bl_pwm->pwm_freq = XTAL_HALF_FREQ_HZ;
+                       if (bl_pwm->pwm_freq < 50)
+                               bl_pwm->pwm_freq = 50;
+               }
+               p += LCD_UKEY_BL_PWM_FREQ;
+               bl_pwm->pwm_duty_max = *p;
+               p += LCD_UKEY_BL_PWM_DUTY_MAX;
+               bl_pwm->pwm_duty_min = *p;
+               p += LCD_UKEY_BL_PWM_DUTY_MIN;
+
+               p += LCD_UKEY_BL_PWM_GPIO;
+               p += LCD_UKEY_BL_PWM_GPIO_OFF;
+
+               bl_pwm->pwm_duty = bl_pwm->pwm_duty_min;
+               bl_pwm_config_init(bl_pwm);
+               break;
+       case BL_CTRL_PWM_COMBO:
+               bconf->bl_pwm_combo0 = kzalloc(sizeof(struct bl_pwm_config_s),
+                                       GFP_KERNEL);
+               if (bconf->bl_pwm_combo0 == NULL) {
+                       BLERR("bl_pwm_combo0 struct malloc error\n");
+                       kfree(para);
+                       return -1;
+               }
+               bconf->bl_pwm_combo1 = kzalloc(sizeof(struct bl_pwm_config_s),
+                                       GFP_KERNEL);
+               if (bconf->bl_pwm_combo1 == NULL) {
+                       BLERR("bl_pwm_combo1 struct malloc error\n");
+                       kfree(para);
+                       return -1;
+               }
+               pwm_combo0 = bconf->bl_pwm_combo0;
+               pwm_combo1 = bconf->bl_pwm_combo1;
+               pwm_combo0->index = 0;
+               pwm_combo1->index = 1;
+
+               bconf->pwm_on_delay = (*p | ((*(p + 1)) << 8));
+               p += LCD_UKEY_BL_PWM_ON_DELAY;
+               bconf->pwm_off_delay = (*p | ((*(p + 1)) << 8));
+               p += LCD_UKEY_BL_PWM_OFF_DELAY;
+
+               pwm_combo0->pwm_method = *p;
+               p += LCD_UKEY_BL_PWM_METHOD;
+               pwm_combo0->pwm_port = *p;
+               p += LCD_UKEY_BL_PWM_PORT;
+               pwm_combo0->pwm_freq = (*p | ((*(p + 1)) << 8) |
+                               ((*(p + 2)) << 8) | ((*(p + 3)) << 8));
+               if (pwm_combo0->pwm_port == BL_PWM_VS) {
+                       if (pwm_combo0->pwm_freq > 4) {
+                               BLERR("bl_pwm_0_vs wrong freq %d\n",
+                                       pwm_combo0->pwm_freq);
+                               pwm_combo0->pwm_freq = BL_FREQ_VS_DEFAULT;
+                       }
+               } else {
+                       if (pwm_combo0->pwm_freq > XTAL_HALF_FREQ_HZ)
+                               pwm_combo0->pwm_freq = XTAL_HALF_FREQ_HZ;
+                       if (pwm_combo0->pwm_freq < 50)
+                               pwm_combo0->pwm_freq = 50;
+               }
+               p += LCD_UKEY_BL_PWM_FREQ;
+               pwm_combo0->pwm_duty_max = *p;
+               p += LCD_UKEY_BL_PWM_DUTY_MAX;
+               pwm_combo0->pwm_duty_min = *p;
+               p += LCD_UKEY_BL_PWM_DUTY_MIN;
+
+               p += LCD_UKEY_BL_PWM_GPIO;
+               p += LCD_UKEY_BL_PWM_GPIO_OFF;
+
+               pwm_combo1->pwm_method = *p;
+               p += LCD_UKEY_BL_PWM2_METHOD;
+               pwm_combo1->pwm_port =  *p;
+               p += LCD_UKEY_BL_PWM2_PORT;
+               pwm_combo1->pwm_freq = (*p | ((*(p + 1)) << 8) |
+                               ((*(p + 2)) << 8) | ((*(p + 3)) << 8));
+               if (pwm_combo1->pwm_port == BL_PWM_VS) {
+                       if (pwm_combo1->pwm_freq > 4) {
+                               BLERR("bl_pwm_1_vs wrong freq %d\n",
+                                       pwm_combo1->pwm_freq);
+                               pwm_combo1->pwm_freq = BL_FREQ_VS_DEFAULT;
+                       }
+               } else {
+                       if (pwm_combo1->pwm_freq > XTAL_HALF_FREQ_HZ)
+                               pwm_combo1->pwm_freq = XTAL_HALF_FREQ_HZ;
+                       if (pwm_combo1->pwm_freq < 50)
+                               pwm_combo1->pwm_freq = 50;
+               }
+               p += LCD_UKEY_BL_PWM2_FREQ;
+               pwm_combo1->pwm_duty_max = *p;
+               p += LCD_UKEY_BL_PWM2_DUTY_MAX;
+               pwm_combo1->pwm_duty_min = *p;
+               p += LCD_UKEY_BL_PWM2_DUTY_MIN;
+
+               p += LCD_UKEY_BL_PWM2_GPIO;
+               p += LCD_UKEY_BL_PWM2_GPIO_OFF;
+
+               pwm_combo0->level_max = (*p | ((*(p + 1)) << 8));
+               p += LCD_UKEY_BL_PWM_LEVEL_MAX;
+               pwm_combo0->level_min = (*p | ((*(p + 1)) << 8));
+               p += LCD_UKEY_BL_PWM_LEVEL_MIN;
+               pwm_combo1->level_max = (*p | ((*(p + 1)) << 8));
+               p += LCD_UKEY_BL_PWM2_LEVEL_MAX;
+               pwm_combo1->level_min = (*p | ((*(p + 1)) << 8));
+               p += LCD_UKEY_BL_PWM2_LEVEL_MIN;
+
+               pwm_combo0->pwm_duty = pwm_combo0->pwm_duty_min;
+               pwm_combo1->pwm_duty = pwm_combo1->pwm_duty_min;
+               bl_pwm_config_init(pwm_combo0);
+               bl_pwm_config_init(pwm_combo1);
+               break;
+       default:
+               break;
+       }
+
+       kfree(para);
+       return 0;
+}
+
+static int aml_bl_config_load(struct bl_config_s *bconf,
+               struct platform_device *pdev)
+{
+       int load_id = 0;
+       int ret = 0;
+
+       if (pdev->dev.of_node == NULL) {
+               BLERR("no backlight of_node exist\n");
+               return -1;
+       }
+       ret = of_property_read_u32(pdev->dev.of_node,
+                       "key_valid", &bl_key_valid);
+       if (ret) {
+               if (bl_debug_print_flag)
+                       BLPR("failed to get key_valid\n");
+               bl_key_valid = 0;
+       }
+       BLPR("key_valid: %d\n", bl_key_valid);
+
+       if (bl_key_valid) {
+               ret = lcd_unifykey_check("backlight");
+               if (ret < 0)
+                       load_id = 0;
+               else
+                       load_id = 1;
+       }
+       if (load_id) {
+               BLPR("%s from unifykey\n", __func__);
+               bl_config_load = 1;
+               ret = aml_bl_config_load_from_unifykey(bconf);
+       } else {
+#ifdef CONFIG_OF
+               BLPR("%s from dts\n", __func__);
+               bl_config_load = 0;
+               ret = aml_bl_config_load_from_dts(bconf, pdev);
+#endif
+       }
+
+       aml_bl_config_print(bconf);
+
+       switch (bconf->method) {
+       case BL_CTRL_PWM:
+               bl_pwm_request(bconf->bl_pwm);
+               bl_pwm_pinmux_set(bconf);
+               break;
+       case BL_CTRL_PWM_COMBO:
+               bl_pwm_request(bconf->bl_pwm_combo0);
+               bl_pwm_request(bconf->bl_pwm_combo1);
+               bl_pwm_pinmux_set(bconf);
+               break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+       case BL_CTRL_LOCAL_DIMING:
+               aml_ldim_probe(pdev);
+               break;
+#endif
+       default:
+               break;
+       }
+       return ret;
+}
+
+/* lcd notify */
+static void aml_bl_on_function(void)
+{
+       /* lcd power on backlight flag */
+       bl_drv->state |= (BL_STATE_LCD_ON | BL_STATE_BL_POWER_ON);
+       BLPR("%s: bl_level=%u, state=0x%x\n",
+               __func__, bl_drv->level, bl_drv->state);
+       if (brightness_bypass) {
+               if ((bl_drv->state & BL_STATE_BL_ON) == 0)
+                       bl_power_on();
+       } else
+               aml_bl_update_status(bl_drv->bldev);
+}
+
+static void aml_bl_delayd_on(struct work_struct *work)
+{
+       if (aml_bl_check_driver())
+               return;
+
+       if (bl_on_request == 0)
+               return;
+
+       aml_bl_on_function();
+}
+
+static int aml_bl_on_notifier(struct notifier_block *nb,
+               unsigned long event, void *data)
+{
+       struct bl_config_s *bconf;
+
+       if ((event & LCD_EVENT_BL_ON) == 0)
+               return NOTIFY_DONE;
+       if (bl_debug_print_flag)
+               BLPR("%s: 0x%lx\n", __func__, event);
+
+       if (aml_bl_check_driver())
+               return NOTIFY_DONE;
+
+       bconf = bl_drv->bconf;
+       bl_on_request = 1;
+       /* lcd power on sequence control */
+       if (bconf->method < BL_CTRL_MAX) {
+               if (bl_drv->workqueue) {
+                       queue_delayed_work(bl_drv->workqueue,
+                               &bl_drv->bl_delayed_work,
+                               msecs_to_jiffies(bconf->power_on_delay));
+               } else {
+                       BLPR("Warning: no bl workqueue\n");
+                       if (bconf->power_on_delay)
+                               msleep(bconf->power_on_delay);
+                       aml_bl_on_function();
+               }
+       } else {
+               BLERR("wrong backlight control method\n");
+       }
+
+       return NOTIFY_OK;
+}
+
+static int aml_bl_off_notifier(struct notifier_block *nb,
+               unsigned long event, void *data)
+{
+       if ((event & LCD_EVENT_BL_OFF) == 0)
+               return NOTIFY_DONE;
+       if (bl_debug_print_flag)
+               BLPR("%s: 0x%lx\n", __func__, event);
+
+       if (aml_bl_check_driver())
+               return NOTIFY_DONE;
+
+       bl_on_request = 0;
+       bl_drv->state &= ~(BL_STATE_LCD_ON | BL_STATE_BL_POWER_ON);
+       if (brightness_bypass) {
+               if (bl_drv->state & BL_STATE_BL_ON)
+                       bl_power_off();
+       } else {
+               aml_bl_update_status(bl_drv->bldev);
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block aml_bl_on_nb = {
+       .notifier_call = aml_bl_on_notifier,
+       .priority = LCD_PRIORITY_POWER_BL_ON,
+};
+
+static struct notifier_block aml_bl_off_nb = {
+       .notifier_call = aml_bl_off_notifier,
+       .priority = LCD_PRIORITY_POWER_BL_OFF,
+};
+
+static int aml_bl_lcd_update_notifier(struct notifier_block *nb,
+               unsigned long event, void *data)
+{
+       struct bl_pwm_config_s *bl_pwm = NULL;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+       struct aml_ldim_driver_s *ldim_drv = aml_ldim_get_driver();
+#endif
+
+       /* If we aren't interested in this event, skip it immediately */
+       if (event != LCD_EVENT_BACKLIGHT_UPDATE)
+               return NOTIFY_DONE;
+
+       if (aml_bl_check_driver())
+               return NOTIFY_DONE;
+       if (bl_debug_print_flag)
+               BLPR("bl_lcd_update_notifier for pwm_vs\n");
+       switch (bl_drv->bconf->method) {
+       case BL_CTRL_PWM:
+               if (bl_drv->bconf->bl_pwm->pwm_port == BL_PWM_VS) {
+                       bl_pwm = bl_drv->bconf->bl_pwm;
+                       if (bl_pwm) {
+                               bl_pwm_config_init(bl_pwm);
+                               if (brightness_bypass)
+                                       bl_set_duty_pwm(bl_pwm);
+                               else
+                                       aml_bl_update_status(bl_drv->bldev);
+                       }
+               }
+               break;
+       case BL_CTRL_PWM_COMBO:
+               if (bl_drv->bconf->bl_pwm_combo0->pwm_port == BL_PWM_VS)
+                       bl_pwm = bl_drv->bconf->bl_pwm_combo0;
+               else if (bl_drv->bconf->bl_pwm_combo1->pwm_port == BL_PWM_VS)
+                       bl_pwm = bl_drv->bconf->bl_pwm_combo1;
+               if (bl_pwm) {
+                       bl_pwm_config_init(bl_pwm);
+                       if (brightness_bypass)
+                               bl_set_duty_pwm(bl_pwm);
+                       else
+                               aml_bl_update_status(bl_drv->bldev);
+               }
+               break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+       case BL_CTRL_LOCAL_DIMING:
+               if (ldim_drv->pwm_vs_update)
+                       ldim_drv->pwm_vs_update();
+               break;
+#endif
+
+       default:
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block aml_bl_lcd_update_nb = {
+       .notifier_call = aml_bl_lcd_update_notifier,
+};
+
+static int aml_bl_lcd_test_notifier(struct notifier_block *nb,
+               unsigned long event, void *data)
+{
+       int *flag;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+       struct aml_ldim_driver_s *ldim_drv = aml_ldim_get_driver();
+#endif
+
+       /* If we aren't interested in this event, skip it immediately */
+       if (event != LCD_EVENT_TEST_PATTERN)
+               return NOTIFY_DONE;
+
+       if (aml_bl_check_driver())
+               return NOTIFY_DONE;
+       if (bl_debug_print_flag)
+               BLPR("bl_lcd_test_notifier for lcd test_pattern\n");
+
+       flag = (int *)data;
+       switch (bl_drv->bconf->method) {
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+       case BL_CTRL_LOCAL_DIMING:
+               if (ldim_drv->test_ctrl)
+                       ldim_drv->test_ctrl(*flag);
+               break;
+#endif
+       default:
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block aml_bl_lcd_test_nb = {
+       .notifier_call = aml_bl_lcd_test_notifier,
+};
+
+/* bl debug calss */
+struct class *bl_debug_class;
+
+static const char *bl_debug_usage_str = {
+"Usage:\n"
+"    cat status ; dump backlight config\n"
+"\n"
+"    echo freq <index> <pwm_freq> > pwm ; set pwm frequency(unit in Hz for pwm, vfreq multiple for pwm_vs)\n"
+"    echo duty <index> <pwm_duty> > pwm ; set pwm duty cycle(unit: %)\n"
+"    echo pol <index> <pwm_pol> > pwm ; set pwm polarity(unit: %)\n"
+"       echo max <index> <duty_max> > pwm ; set pwm duty_max(unit: %)\n"
+"       echo min <index> <duty_min> > pwm ; set pwm duty_min(unit: %)\n"
+"    cat pwm ; dump pwm state\n"
+"       echo free <0|1> > pwm ; set bl_pwm_duty_free enable or disable\n"
+"\n"
+"    echo <0|1> > power ; backlight power ctrl\n"
+"    cat power ; print backlight power state\n"
+"\n"
+"    echo <0|1> > print ; 0=disable debug print; 1=enable debug print\n"
+"    cat print ; read current debug print flag\n"
+};
+
+static ssize_t bl_debug_help(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%s\n", bl_debug_usage_str);
+}
+
+static ssize_t bl_status_read(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       struct bl_config_s *bconf = bl_drv->bconf;
+       struct bl_pwm_config_s *bl_pwm;
+       struct bl_pwm_config_s *pwm_combo0, *pwm_combo1;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+       struct aml_ldim_driver_s *ldim_drv = aml_ldim_get_driver();
+#endif
+       ssize_t len = 0;
+
+       len = sprintf(buf, "read backlight status:\n"
+               "key_valid:          %d\n"
+               "config_load:        %d\n"
+               "index:              %d\n"
+               "name:               %s\n"
+               "state:              0x%x\n"
+               "level:              %d\n"
+               "level_uboot:        %d\n"
+               "brightness_bypass:  %d\n\n"
+               "level_max:          %d\n"
+               "level_min:          %d\n"
+               "level_mid:          %d\n"
+               "level_mid_mapping:  %d\n\n"
+               "method:             %s\n"
+               "en_gpio:            %s(%d)\n"
+               "en_gpio_on:         %d\n"
+               "en_gpio_off:        %d\n"
+               "power_on_delay:     %d\n"
+               "power_off_delay:    %d\n\n",
+               bl_key_valid, bl_config_load,
+               bl_drv->index, bconf->name, bl_drv->state,
+               bl_drv->level,  bl_level_uboot, brightness_bypass,
+               bconf->level_max, bconf->level_min,
+               bconf->level_mid, bconf->level_mid_mapping,
+               bl_method_type_to_str(bconf->method),
+               bconf->bl_gpio[bconf->en_gpio].name,
+               bconf->en_gpio, bconf->en_gpio_on, bconf->en_gpio_off,
+               bconf->power_on_delay, bconf->power_off_delay);
+       switch (bconf->method) {
+       case BL_CTRL_GPIO:
+               len += sprintf(buf+len, "to do\n");
+               break;
+       case BL_CTRL_PWM:
+               bl_pwm = bconf->bl_pwm;
+               len += sprintf(buf+len,
+                       "pwm_method:         %d\n"
+                       "pwm_port:           %d\n"
+                       "pwm_freq:           %d\n"
+                       "pwm_duty_max:       %d\n"
+                       "pwm_duty_min:       %d\n"
+                       "pwm_on_delay:       %d\n"
+                       "pwm_off_delay:      %d\n\n",
+                       bl_pwm->pwm_method, bl_pwm->pwm_port, bl_pwm->pwm_freq,
+                       bl_pwm->pwm_duty_max, bl_pwm->pwm_duty_min,
+                       bconf->pwm_on_delay, bconf->pwm_off_delay);
+               break;
+       case BL_CTRL_PWM_COMBO:
+               pwm_combo0 = bconf->bl_pwm_combo0;
+               pwm_combo1 = bconf->bl_pwm_combo1;
+               len += sprintf(buf+len,
+                       "pwm_0_level_max:    %d\n"
+                       "pwm_0_level_min:    %d\n"
+                       "pwm_0_method:       %d\n"
+                       "pwm_0_port:         %d\n"
+                       "pwm_0_freq:         %d\n"
+                       "pwm_0_duty_max:     %d\n"
+                       "pwm_0_duty_min:     %d\n"
+                       "pwm_1_level_max:    %d\n"
+                       "pwm_1_level_min:    %d\n"
+                       "pwm_1_method:       %d\n"
+                       "pwm_1_port:         %d\n"
+                       "pwm_1_freq:         %d\n"
+                       "pwm_1_duty_max:     %d\n"
+                       "pwm_1_duty_min:     %d\n"
+                       "pwm_on_delay:       %d\n"
+                       "pwm_off_delay:      %d\n\n",
+                       pwm_combo0->level_max, pwm_combo0->level_min,
+                       pwm_combo0->pwm_method, pwm_combo0->pwm_port,
+                       pwm_combo0->pwm_freq,
+                       pwm_combo0->pwm_duty_max, pwm_combo0->pwm_duty_min,
+                       pwm_combo1->level_max, pwm_combo1->level_min,
+                       pwm_combo1->pwm_method, pwm_combo1->pwm_port,
+                       pwm_combo1->pwm_freq,
+                       pwm_combo1->pwm_duty_max, pwm_combo1->pwm_duty_min,
+                       bconf->pwm_on_delay, bconf->pwm_off_delay);
+               break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+       case BL_CTRL_LOCAL_DIMING:
+               if (ldim_drv->config_print)
+                       ldim_drv->config_print();
+               break;
+#endif
+       case BL_CTRL_EXTERN:
+               break;
+       default:
+               len += sprintf(buf+len, "wrong backlight control method\n");
+               break;
+       }
+       return len;
+}
+
+static ssize_t bl_debug_pwm_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       struct bl_config_s *bconf = bl_drv->bconf;
+       struct bl_pwm_config_s *bl_pwm;
+       unsigned int value;
+       ssize_t len = 0;
+
+       len = sprintf(buf, "read backlight pwm state:\n");
+       switch (bconf->method) {
+       case BL_CTRL_PWM:
+               len += sprintf(buf+len,
+                       "bl_pwm_bypass:      %d\n"
+                       "bl_pwm_duty_free:   %d\n",
+                       bl_pwm_bypass, bl_pwm_duty_free);
+               if (bconf->bl_pwm) {
+                       bl_pwm = bconf->bl_pwm;
+                       len += sprintf(buf+len,
+                               "pwm_index:          %d\n"
+                               "pwm_method:         %d\n"
+                               "pwm_port:           %d\n"
+                               "pwm_freq:           %d\n"
+                               "pwm_duty_max:       %d\n"
+                               "pwm_duty_min:       %d\n"
+                               "pwm_cnt:            %d\n"
+                               "pwm_duty:           %d%%\n",
+                               bl_pwm->index, bl_pwm->pwm_method,
+                               bl_pwm->pwm_port, bl_pwm->pwm_freq,
+                               bl_pwm->pwm_duty_max, bl_pwm->pwm_duty_min,
+                               bl_pwm->pwm_cnt, bl_pwm->pwm_duty);
+                       switch (bl_pwm->pwm_port) {
+                       case BL_PWM_A:
+                       case BL_PWM_B:
+                       case BL_PWM_C:
+                       case BL_PWM_D:
+                       case BL_PWM_E:
+                       case BL_PWM_F:
+                               value = bl_cbus_read(pwm_reg[bl_pwm->pwm_port]);
+                               len += sprintf(buf+len,
+                                       "pwm_reg:            0x%08x\n",
+                                       value);
+                               break;
+                       case BL_PWM_VS:
+                               len += sprintf(buf+len,
+                                       "pwm_reg0:            0x%08x\n"
+                                       "pwm_reg1:            0x%08x\n"
+                                       "pwm_reg2:            0x%08x\n"
+                                       "pwm_reg3:            0x%08x\n",
+                                       bl_vcbus_read(VPU_VPU_PWM_V0),
+                                       bl_vcbus_read(VPU_VPU_PWM_V1),
+                                       bl_vcbus_read(VPU_VPU_PWM_V2),
+                                       bl_vcbus_read(VPU_VPU_PWM_V3));
+                               break;
+                       default:
+                               break;
+                       }
+               }
+               break;
+       case BL_CTRL_PWM_COMBO:
+               len += sprintf(buf+len,
+                       "bl_pwm_bypass:      %d\n"
+                       "bl_pwm_duty_free:   %d\n",
+                       bl_pwm_bypass, bl_pwm_duty_free);
+               if (bconf->bl_pwm_combo0) {
+                       bl_pwm = bconf->bl_pwm_combo0;
+                       len += sprintf(buf+len,
+                               "pwm_0_index:        %d\n"
+                               "pwm_0_method:       %d\n"
+                               "pwm_0_port:         %d\n"
+                               "pwm_0_freq:         %d\n"
+                               "pwm_0_duty_max:     %d\n"
+                               "pwm_0_duty_min:     %d\n"
+                               "pwm_0_cnt:          %d\n"
+                               "pwm_0_duty:         %d%%\n",
+                               bl_pwm->index, bl_pwm->pwm_method,
+                               bl_pwm->pwm_port, bl_pwm->pwm_freq,
+                               bl_pwm->pwm_duty_max, bl_pwm->pwm_duty_min,
+                               bl_pwm->pwm_cnt, bl_pwm->pwm_duty);
+                       switch (bl_pwm->pwm_port) {
+                       case BL_PWM_A:
+                       case BL_PWM_B:
+                       case BL_PWM_C:
+                       case BL_PWM_D:
+                       case BL_PWM_E:
+                       case BL_PWM_F:
+                               value = bl_cbus_read(pwm_reg[bl_pwm->pwm_port]);
+                               len += sprintf(buf+len,
+                                       "pwm_0_reg:          0x%08x\n",
+                                       value);
+                               break;
+                       case BL_PWM_VS:
+                               len += sprintf(buf+len,
+                                       "pwm_0_reg0:         0x%08x\n"
+                                       "pwm_0_reg1:         0x%08x\n"
+                                       "pwm_0_reg2:         0x%08x\n"
+                                       "pwm_0_reg3:         0x%08x\n",
+                                       bl_vcbus_read(VPU_VPU_PWM_V0),
+                                       bl_vcbus_read(VPU_VPU_PWM_V1),
+                                       bl_vcbus_read(VPU_VPU_PWM_V2),
+                                       bl_vcbus_read(VPU_VPU_PWM_V3));
+                               break;
+                       default:
+                               break;
+                       }
+               }
+               if (bconf->bl_pwm_combo1) {
+                       bl_pwm = bconf->bl_pwm_combo1;
+                       len += sprintf(buf+len,
+                               "pwm_1_index:        %d\n"
+                               "pwm_1_method:       %d\n"
+                               "pwm_1_port:         %d\n"
+                               "pwm_1_freq:         %d\n"
+                               "pwm_1_duty_max:     %d\n"
+                               "pwm_1_duty_min:     %d\n"
+                               "pwm_1_cnt:          %d\n"
+                               "pwm_1_duty:         %d%%\n",
+                               bl_pwm->index, bl_pwm->pwm_method,
+                               bl_pwm->pwm_port, bl_pwm->pwm_freq,
+                               bl_pwm->pwm_duty_max, bl_pwm->pwm_duty_min,
+                               bl_pwm->pwm_cnt, bl_pwm->pwm_duty);
+                       switch (bl_pwm->pwm_port) {
+                       case BL_PWM_A:
+                       case BL_PWM_B:
+                       case BL_PWM_C:
+                       case BL_PWM_D:
+                       case BL_PWM_E:
+                       case BL_PWM_F:
+                               value = bl_cbus_read(pwm_reg[bl_pwm->pwm_port]);
+                               len += sprintf(buf+len,
+                                       "pwm_1_reg:          0x%08x\n",
+                                       value);
+                               break;
+                       case BL_PWM_VS:
+                               len += sprintf(buf+len,
+                                       "pwm_1_reg0:         0x%08x\n"
+                                       "pwm_1_reg1:         0x%08x\n"
+                                       "pwm_1_reg2:         0x%08x\n"
+                                       "pwm_1_reg3:         0x%08x\n",
+                                       bl_vcbus_read(VPU_VPU_PWM_V0),
+                                       bl_vcbus_read(VPU_VPU_PWM_V1),
+                                       bl_vcbus_read(VPU_VPU_PWM_V2),
+                                       bl_vcbus_read(VPU_VPU_PWM_V3));
+                               break;
+                       default:
+                               break;
+                       }
+               }
+               break;
+       default:
+               len += sprintf(buf+len, "not pwm control method\n");
+               break;
+       }
+       return len;
+}
+
+#define BL_DEBUG_PWM_FREQ    0
+#define BL_DEBUG_PWM_DUTY    1
+#define BL_DEBUG_PWM_POL     2
+#define BL_DEBUG_PWM_DUTY_MAX 3
+#define BL_DEBUG_PWM_DUTY_MIN 4
+static void bl_debug_pwm_set(unsigned int index, unsigned int value, int state)
+{
+       struct bl_config_s *bconf = bl_drv->bconf;
+       struct bl_pwm_config_s *bl_pwm = NULL;
+
+       if (aml_bl_check_driver())
+               return;
+
+       switch (bconf->method) {
+       case BL_CTRL_PWM:
+               bl_pwm = bconf->bl_pwm;
+               break;
+       case BL_CTRL_PWM_COMBO:
+               if (index == 0)
+                       bl_pwm = bconf->bl_pwm_combo0;
+               else
+                       bl_pwm = bconf->bl_pwm_combo1;
+               break;
+       default:
+               BLERR("not pwm control method\n");
+               break;
+       }
+       if (bl_pwm) {
+               switch (state) {
+               case BL_DEBUG_PWM_FREQ:
+                       bl_pwm->pwm_freq = value;
+                       if (bl_pwm->pwm_freq < 50)
+                               bl_pwm->pwm_freq = 50;
+                       bl_pwm_config_init(bl_pwm);
+                       bl_set_duty_pwm(bl_pwm);
+                       if (bl_debug_print_flag) {
+                               BLPR("set index(%d) pwm_port(%d) freq: %dHz\n",
+                               index, bl_pwm->pwm_port, bl_pwm->pwm_freq);
+                       }
+                       break;
+               case BL_DEBUG_PWM_DUTY:
+                       bl_pwm->pwm_duty = value;
+                       bl_set_duty_pwm(bl_pwm);
+                       if (bl_debug_print_flag) {
+                               BLPR("set index(%d) pwm_port(%d) duty: %d%%\n",
+                               index, bl_pwm->pwm_port, bl_pwm->pwm_duty);
+                       }
+                       break;
+               case BL_DEBUG_PWM_POL:
+                       bl_pwm->pwm_method = value;
+                       bl_pwm_config_init(bl_pwm);
+                       bl_set_duty_pwm(bl_pwm);
+                       if (bl_debug_print_flag) {
+                               BLPR("set index(%d) pwm_port(%d) method: %d\n",
+                               index, bl_pwm->pwm_port, bl_pwm->pwm_method);
+                       }
+                       break;
+               case BL_DEBUG_PWM_DUTY_MAX:
+                       bl_pwm->pwm_duty_max = value;
+                       bl_set_duty_pwm(bl_pwm);
+                       if (bl_debug_print_flag) {
+                               BLPR("set index(%d) pwm_port(%d) duty: %d%%\n",
+                               index, bl_pwm->pwm_port, bl_pwm->pwm_duty_max);
+                       }
+                       break;
+               case BL_DEBUG_PWM_DUTY_MIN:
+                       bl_pwm->pwm_duty_min = value;
+                       bl_set_duty_pwm(bl_pwm);
+                       if (bl_debug_print_flag) {
+                               BLPR("set index(%d) pwm_port(%d) duty: %d%%\n",
+                               index, bl_pwm->pwm_port, bl_pwm->pwm_duty_min);
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+static ssize_t bl_debug_pwm_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       unsigned int ret;
+       unsigned int index = 0, val = 0;
+
+       switch (buf[0]) {
+       case 'f':
+               if (buf[3] == 'q') { /* frequency */
+                       ret = sscanf(buf, "freq %d %d", &index, &val);
+                       bl_debug_pwm_set(index, val, BL_DEBUG_PWM_FREQ);
+               } else if (buf[3] == 'e') { /* duty free */
+                       ret = sscanf(buf, "free %d", &val);
+                       bl_pwm_duty_free = (unsigned char)val;
+                       BLPR("set bl_pwm_duty_free: %d\n", bl_pwm_duty_free);
+               }
+               break;
+       case 'd': /* duty */
+               ret = sscanf(buf, "duty %d %d", &index, &val);
+               bl_debug_pwm_set(index, val, BL_DEBUG_PWM_DUTY);
+               break;
+       case 'p': /* polarity */
+               ret = sscanf(buf, "pol %d %d", &index, &val);
+               bl_debug_pwm_set(index, val, BL_DEBUG_PWM_POL);
+               break;
+       case 'b': /* bypass */
+               ret = sscanf(buf, "bypass %d", &val);
+               bl_pwm_bypass = (unsigned char)val;
+               BLPR("set bl_pwm_bypass: %d\n", bl_pwm_bypass);
+               break;
+       case 'm':
+               if (buf[1] == 'a') { /* max */
+                       ret = sscanf(buf, "max %d %d", &index, &val);
+                       bl_debug_pwm_set(index, val, BL_DEBUG_PWM_DUTY_MAX);
+               } else if (buf[1] == 'i') { /* min */
+                       ret = sscanf(buf, "min %d %d", &index, &val);
+                       bl_debug_pwm_set(index, val, BL_DEBUG_PWM_DUTY_MIN);
+               }
+               break;
+       default:
+               BLERR("wrong command\n");
+               break;
+       }
+
+       return count;
+}
+
+static ssize_t bl_debug_power_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       int state;
+
+       if ((bl_drv->state & BL_STATE_LCD_ON) == 0) {
+               state = 0;
+       } else {
+               if (bl_drv->state & BL_STATE_BL_POWER_ON)
+                       state = 1;
+               else
+                       state = 0;
+       }
+       return sprintf(buf, "backlight power state: %d\n", state);
+}
+
+static ssize_t bl_debug_power_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       unsigned int ret;
+       unsigned int temp = 0;
+
+       ret = kstrtouint(buf, 10, &temp);
+       if (ret != 0)
+               return -EINVAL;
+
+       BLPR("power control: %u\n", temp);
+       if ((bl_drv->state & BL_STATE_LCD_ON) == 0) {
+               temp = 0;
+               BLPR("backlight force off for lcd is off\n");
+       }
+       if (temp == 0) {
+               bl_drv->state &= ~BL_STATE_BL_POWER_ON;
+               if (bl_drv->state & BL_STATE_BL_ON)
+                       bl_power_off();
+       } else {
+               bl_drv->state |= BL_STATE_BL_POWER_ON;
+               if ((bl_drv->state & BL_STATE_BL_ON) == 0)
+                       bl_power_on();
+       }
+
+       return count;
+}
+
+static ssize_t bl_debug_delay_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       struct bl_config_s *bconf = bl_drv->bconf;
+
+       return sprintf(buf, "bl power delay: on=%dms, off=%dms\n",
+               bconf->power_on_delay, bconf->power_off_delay);
+}
+
+static ssize_t bl_debug_delay_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       unsigned int ret;
+       unsigned int val[2];
+       struct bl_config_s *bconf = bl_drv->bconf;
+
+       ret = sscanf(buf, "%d %d", &val[0], &val[1]);
+       if (ret == 2) {
+               bconf->power_on_delay = val[0];
+               bconf->power_off_delay = val[1];
+               pr_info("set bl power_delay: on=%dms, off=%dms\n",
+                       val[0], val[1]);
+       } else {
+               pr_info("invalid data\n");
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+static ssize_t bl_debug_key_valid_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d\n", bl_key_valid);
+}
+
+static ssize_t bl_debug_config_load_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d\n", bl_config_load);
+}
+
+static ssize_t bl_debug_print_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "show bl_debug_print_flag: %d\n",
+               bl_debug_print_flag);
+}
+
+static ssize_t bl_debug_print_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       unsigned int ret;
+       unsigned int temp = 0;
+
+       ret = kstrtouint(buf, 10, &temp);
+       if (ret == 0) {
+               bl_debug_print_flag = temp;
+               BLPR("set bl_debug_print_flag: %u\n", bl_debug_print_flag);
+       } else {
+               pr_info("invalid data\n");
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+static struct class_attribute bl_debug_class_attrs[] = {
+       __ATTR(help, 0644, bl_debug_help, NULL),
+       __ATTR(status, 0644, bl_status_read, NULL),
+       __ATTR(pwm, 0644, bl_debug_pwm_show,
+                       bl_debug_pwm_store),
+       __ATTR(power, 0644, bl_debug_power_show,
+                       bl_debug_power_store),
+       __ATTR(delay, 0644, bl_debug_delay_show,
+                       bl_debug_delay_store),
+       __ATTR(key_valid,   0644, bl_debug_key_valid_show, NULL),
+       __ATTR(config_load, 0644,
+               bl_debug_config_load_show, NULL),
+       __ATTR(print, 0644, bl_debug_print_show,
+                       bl_debug_print_store),
+};
+
+static int aml_bl_creat_class(void)
+{
+       int i;
+
+       bl_debug_class = class_create(THIS_MODULE, "aml_bl");
+       if (IS_ERR(bl_debug_class)) {
+               BLERR("create aml_bl debug class fail\n");
+               return -1;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(bl_debug_class_attrs); i++) {
+               if (class_create_file(bl_debug_class,
+                               &bl_debug_class_attrs[i])) {
+                       BLERR("create aml_bl debug attribute %s fail\n",
+                               bl_debug_class_attrs[i].attr.name);
+               }
+       }
+       return 0;
+}
+
+static int aml_bl_remove_class(void)
+{
+       int i;
+
+       if (bl_debug_class == NULL)
+               return -1;
+
+       for (i = 0; i < ARRAY_SIZE(bl_debug_class_attrs); i++)
+               class_remove_file(bl_debug_class, &bl_debug_class_attrs[i]);
+       class_destroy(bl_debug_class);
+       bl_debug_class = NULL;
+       return 0;
+}
+/* **************************************** */
+
+#ifdef CONFIG_PM
+static int aml_bl_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       switch (bl_off_policy) {
+       case BL_OFF_POLICY_ONCE:
+               aml_bl_off_policy_cnt += 1;
+               break;
+       default:
+               break;
+       }
+
+       if (aml_bl_off_policy_cnt == 2) {
+               aml_bl_off_policy_cnt = 0;
+               bl_off_policy = BL_OFF_POLICY_NONE;
+               BLPR("change bl_off_policy: %d\n", bl_off_policy);
+       }
+
+       BLPR("aml_bl_suspend: bl_off_policy=%d, aml_bl_off_policy_cnt=%d\n",
+               bl_off_policy, aml_bl_off_policy_cnt);
+       return 0;
+}
+
+static int aml_bl_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+#endif
+
+static int aml_bl_probe(struct platform_device *pdev)
+{
+       struct backlight_properties props;
+       struct backlight_device *bldev;
+       struct bl_config_s *bconf;
+       int ret;
+
+#ifdef AML_BACKLIGHT_DEBUG
+       bl_debug_print_flag = 1;
+#else
+       bl_debug_print_flag = 0;
+#endif
+
+       aml_bl_off_policy_cnt = 0;
+
+       /* init backlight parameters */
+       brightness_bypass = 0;
+       bl_pwm_bypass = 0;
+       bl_pwm_duty_free = 0;
+
+       bl_chip_type = aml_bl_check_chip();
+       bl_drv = kzalloc(sizeof(struct aml_bl_drv_s), GFP_KERNEL);
+       if (!bl_drv) {
+               BLERR("driver malloc error\n");
+               return -ENOMEM;
+       }
+
+       bconf = &bl_config;
+       bl_drv->dev = &pdev->dev;
+       bl_drv->bconf = bconf;
+       ret = aml_bl_config_load(bconf, pdev);
+       if (ret)
+               goto err;
+
+       memset(&props, 0, sizeof(struct backlight_properties));
+       props.type = BACKLIGHT_RAW;
+       props.power = FB_BLANK_UNBLANK; /* full on */
+       props.max_brightness = (bconf->level_max > 0 ?
+                       bconf->level_max : BL_LEVEL_MAX);
+       props.brightness = (bconf->level_default > 0 ?
+                       bconf->level_default : BL_LEVEL_DEFAULT);
+
+       bldev = backlight_device_register(AML_BL_NAME, &pdev->dev,
+                                       bl_drv, &aml_bl_ops, &props);
+       if (IS_ERR(bldev)) {
+               BLERR("failed to register backlight\n");
+               ret = PTR_ERR(bldev);
+               goto err;
+       }
+       bl_drv->bldev = bldev;
+       /* platform_set_drvdata(pdev, bl_drv); */
+
+       /* init workqueue */
+       INIT_DELAYED_WORK(&bl_drv->bl_delayed_work, aml_bl_delayd_on);
+       bl_drv->workqueue = create_workqueue("bl_power_on_queue");
+       if (bl_drv->workqueue == NULL)
+               BLERR("can't create bl work queue\n");
+
+#ifdef CONFIG_AMLOGIC_LCD
+       ret = aml_lcd_notifier_register(&aml_bl_on_nb);
+       if (ret)
+               BLERR("register aml_bl_on_notifier failed\n");
+       ret = aml_lcd_notifier_register(&aml_bl_off_nb);
+       if (ret)
+               BLERR("register aml_bl_off_notifier failed\n");
+       ret = aml_lcd_notifier_register(&aml_bl_lcd_update_nb);
+       if (ret)
+               BLERR("register aml_bl_lcd_update_nb failed\n");
+       ret = aml_lcd_notifier_register(&aml_bl_lcd_test_nb);
+       if (ret)
+               BLERR("register aml_bl_lcd_test_nb failed\n");
+#endif
+       aml_bl_creat_class();
+
+       /* update bl status */
+       bl_drv->state = (BL_STATE_LCD_ON |
+                       BL_STATE_BL_POWER_ON | BL_STATE_BL_ON);
+       bl_on_request = 1;
+
+       if (brightness_bypass) {
+               aml_bl_set_level(bl_level_uboot);
+               bl_on_level = bl_level_uboot;
+       } else {
+               aml_bl_update_status(bl_drv->bldev);
+               bl_on_level = bconf->level_default;
+       }
+
+       BLPR("probe OK\n");
+       return 0;
+err:
+       kfree(bl_drv);
+       bl_drv = NULL;
+       return ret;
+}
+
+static int __exit aml_bl_remove(struct platform_device *pdev)
+{
+       int ret;
+       /*struct aml_bl *bl_drv = platform_get_drvdata(pdev);*/
+
+       aml_bl_remove_class();
+
+       ret = cancel_delayed_work_sync(&bl_drv->bl_delayed_work);
+       if (bl_drv->workqueue)
+               destroy_workqueue(bl_drv->workqueue);
+
+       backlight_device_unregister(bl_drv->bldev);
+#ifdef CONFIG_AMLOGIC_LCD
+       aml_lcd_notifier_unregister(&aml_bl_lcd_update_nb);
+       aml_lcd_notifier_unregister(&aml_bl_on_nb);
+       aml_lcd_notifier_unregister(&aml_bl_off_nb);
+#endif
+       /* platform_set_drvdata(pdev, NULL); */
+       switch (bl_drv->bconf->method) {
+       case BL_CTRL_PWM:
+               kfree(bl_drv->bconf->bl_pwm);
+               break;
+       case BL_CTRL_PWM_COMBO:
+               kfree(bl_drv->bconf->bl_pwm_combo0);
+               kfree(bl_drv->bconf->bl_pwm_combo1);
+               break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+       case BL_CTRL_LOCAL_DIMING:
+               aml_ldim_remove();
+               break;
+#endif
+       default:
+               break;
+       }
+       kfree(bl_drv);
+       bl_drv = NULL;
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id aml_bl_dt_match[] = {
+       {
+               .compatible = "amlogic, backlight",
+       },
+       {},
+};
+#endif
+
+static struct platform_driver aml_bl_driver = {
+       .driver = {
+               .name  = AML_BL_NAME,
+               .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+               .of_match_table = aml_bl_dt_match,
+#endif
+       },
+       .probe   = aml_bl_probe,
+       .remove  = __exit_p(aml_bl_remove),
+#ifdef CONFIG_PM
+       .suspend = aml_bl_suspend,
+       .resume  = aml_bl_resume,
+#endif
+};
+
+static int __init aml_bl_init(void)
+{
+       if (platform_driver_register(&aml_bl_driver)) {
+               BLPR("failed to register bl driver module\n");
+               return -ENODEV;
+       }
+       return 0;
+}
+
+static void __exit aml_bl_exit(void)
+{
+       platform_driver_unregister(&aml_bl_driver);
+}
+
+late_initcall(aml_bl_init);
+module_exit(aml_bl_exit);
+
+static int __init aml_bl_boot_para_setup(char *s)
+{
+       char bl_off_policy_str[10] = "none";
+
+       bl_off_policy = BL_OFF_POLICY_NONE;
+       aml_bl_off_policy_cnt = 0;
+       if (s != NULL)
+               sprintf(bl_off_policy_str, "%s", s);
+
+       if (strncmp(bl_off_policy_str, "none", 2) == 0)
+               bl_off_policy = BL_OFF_POLICY_NONE;
+       else if (strncmp(bl_off_policy_str, "always", 2) == 0)
+               bl_off_policy = BL_OFF_POLICY_ALWAYS;
+       else if (strncmp(bl_off_policy_str, "once", 2) == 0)
+               bl_off_policy = BL_OFF_POLICY_ONCE;
+       BLPR("bl_off_policy: %s\n", bl_off_policy_str);
+
+       return 0;
+}
+__setup("bl_off=", aml_bl_boot_para_setup);
+
+MODULE_DESCRIPTION("AML Backlight Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Amlogic, Inc.");
diff --git a/drivers/amlogic/media/vout/backlight/aml_bl.dts b/drivers/amlogic/media/vout/backlight/aml_bl.dts
new file mode 100644 (file)
index 0000000..125d3fd
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * drivers/amlogic/media/vout/backlight/aml_bl.dts
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+backlight{
+       compatible = "amlogic, backlight";
+       dev_name = "backlight";
+       status = "okay";
+       pinctrl-names = "pwm_on","pwm_off","pwm_vs_on","pwm_vs_off",
+                       "pwm_combo_on", "pwm_combo_off";
+       pinctrl-0 = <&bl_pwm_on_pins>;
+       pinctrl-1 = <&bl_pwm_off_pins>;
+       pinctrl-2 = <&bl_pwm_vs_on_pins>;
+       pinctrl-3 = <&bl_pwm_vs_off_pins>;
+       pinctrl-4 = <&bl_pwm_combo_on_pins>;
+       pinctrl-5 = <&bl_pwm_combo_off_pins>;
+
+       /* power index:(point gpios_index, 0xff=invalid) */
+       /* power value:(0=output low, 1=output high, 2=input) */
+       /* power delay:(unit in ms) */
+       bl-gpios = <&gpio GPIOAO_4 GPIO_ACTIVE_HIGH
+               &gpio GPIOY_13 GPIO_ACTIVE_HIGH
+               &gpio GPIOY_8 GPIO_ACTIVE_HIGH>;
+       bl_gpio_names = "GPIOAO_4","GPIOY_13","GPIOY_8";
+
+       backlight_0{
+               index = <0>;
+               bl_name = "backlight_0";
+               bl_level_default_uboot_kernel = <100 100>;
+               bl_level_attr = <255 10 128 102>;
+                       /* max, min, mid, mid_mapping */
+
+               bl_ctrl_method = <1>;
+                       /* 0=gpio, 1=pwm, 2=pwm_combo, 3=ldim, 4=extern */
+               bl_power_attr = <0 1 0 200 200>;
+                       /* en_gpio_index, on_value, off_value,
+                        * on_delay, off_delay
+                        */
+
+               /* pwm_method:(0=negative, 1=positive) */
+               /* pwm_freq:(pwm_vs: 1~4 vfreq multiple,
+                * other pwm: real freq unit: Hz)
+                */
+               /* pwm_duty:(unit in %) */
+
+               bl_pwm_port = "PWM_B";
+                       /* PWM_A, PWM_B, PWM_C, PWM_D, PWM_VS */
+               bl_pwm_attr = <1 180 100 25>;
+                       /* pwm_method, pwm_freq, duty_max, duty_min */
+               bl_pwm_power = <1 0 10 10>;
+                       /* pwm_gpio_index, pwm_gpio_off,
+                        * pwm_on_delay, pwm_off_delay
+                        */
+       };
+       backlight_1{
+               index = <1>;
+               bl_name = "backlight_1";
+               bl_level_default_uboot_kernel = <100 100>;
+               bl_level_attr = <255 10 128 102>;
+                       /* max, min, mid, mid_mapping */
+
+               bl_ctrl_method = <1>;
+                       /* 0=gpio, 1=pwm, 2=pwm_combo, 3=ldim, 4=extern */
+               bl_power_attr = <0 1 0 200 200>;
+                       /* en_gpio_index, on_value, off_value,
+                        * on_delay, off_delay
+                        */
+
+               /* pwm_method: 0=negative, 1=positive */
+               /* pwm_freq: pwm_vs: 1~4(vfreq multiple),
+                * other pwm: real freq(unit: Hz)
+                */
+               /* duty_max, duty_min: unit in % */
+
+               bl_pwm_port = "PWM_VS";
+                       /* PWM_A, PWM_B, PWM_C, PWM_D, PWM_VS */
+               bl_pwm_attr = <1 2 100 25>;
+                       /* pwm_method, pwm_freq, duty_max, duty_min */
+               bl_pwm_power = <1 0 10 10>;
+                       /* pwm_gpio_index, pwm_off_value,
+                        * pwm_on_delay, pwm_off_delay
+                        */
+       };
+       backlight_2{
+               index = <2>;
+               bl_name = "backlight_2";
+               bl_level_attr = <255 10 128 102>;
+                       /* max, min, mid, mid_mapping */
+
+               bl_ctrl_method = <3>;
+                       /* 0=gpio, 1=pwm, 2=pwm_combo, 3=ldim, 4=extern */
+               bl_power_attr = <0 1 0 200 200>;
+                       /* en_gpio_index, on_value,
+                        * off_value, on_delay, off_delay
+                        */
+       };
+       backlight_3{
+               index = <3>;
+               bl_name = "backlight_3";
+               bl_level_default_uboot_kernel = <100 100>;
+               bl_level_attr = <255 10 128 102>;
+                       /* max, min, mid, mid_mapping */
+
+               bl_ctrl_method = <2>;
+                       /* 0=gpio, 1=pwm, 2=pwm_combo, 3=ldim, 4=extern */
+               bl_power_attr = <0 1 0 200 200>;
+                       /* en_gpio_index, on_value,
+                        *      off_value, on_delay, off_delay
+                        */
+
+               /* pwm_method: 0=negative, 1=positive */
+               /* pwm_freq: pwm_vs: 1~4(vfreq multiple),
+                * other pwm: real freq(unit: Hz)
+                */
+               /* duty_max, duty_min: unit in % */
+
+               bl_pwm_combo_level_mapping = <255 128 128 10>;
+                       /* level_max, level_min */
+               bl_pwm_combo_port = "PWM_B","PWM_D";
+                       /* PWM_A, PWM_B, PWM_C, PWM_D, PWM_VS */
+               bl_pwm_combo_attr = <1 180 100 25
+                       0 18000 100 25>;
+                       /* pwm_method, pwm_freq, duty_max, duty_min */
+               bl_pwm_combo_power = <1 0 2 0 10 10>;
+                       /* pwm0_gpio_index, pwm0_gpio_off,
+                        * pwm1_gpio_index, pwm1_gpio_off,
+                        * pwm_on_delay, pwm_off_delay
+                        */
+       };
+};
+
diff --git a/drivers/amlogic/media/vout/backlight/aml_bl_reg.h b/drivers/amlogic/media/vout/backlight/aml_bl_reg.h
new file mode 100644 (file)
index 0000000..8e38f8c
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * drivers/amlogic/media/vout/backlight/aml_bl_reg.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __AML_BL_REG_H__
+#define __AML_BL_REG_H__
+#include <linux/amlogic/iomap.h>
+
+/* normal pwm reg: cbus */
+#define PWM_PWM_A                       0x2154
+#define PWM_PWM_B                       0x2155
+#define PWM_MISC_REG_AB                 0x2156
+#define PWM_PWM_C                       0x2190
+#define PWM_PWM_D                       0x2191
+#define PWM_MISC_REG_CD                 0x2192
+#define PWM_PWM_E                       0x21b0
+#define PWM_PWM_F                       0x21b1
+#define PWM_MISC_REG_EF                 0x21b2
+/* normal pwm reg: cbus, txlx */
+#define PWM_PWM_A_TXLX                  0x6c00
+#define PWM_PWM_B_TXLX                  0x6c01
+#define PWM_MISC_REG_AB_TXLX            0x6c02
+#define PWM_PWM_C_TXLX                  0x6800
+#define PWM_PWM_D_TXLX                  0x6801
+#define PWM_MISC_REG_CD_TXLX            0x6802
+#define PWM_PWM_E_TXLX                  0x6400
+#define PWM_PWM_F_TXLX                  0x6401
+#define PWM_MISC_REG_EF_TXLX            0x6402
+
+/* pwm_vs reg: vcbus */
+#define VPU_VPU_PWM_V0                  0x2730
+#define VPU_VPU_PWM_V1                  0x2731
+#define VPU_VPU_PWM_V2                  0x2732
+#define VPU_VPU_PWM_V3                  0x2733
+
+#define ENCL_VIDEO_MAX_LNCNT            0x1cbb
+
+
+static inline unsigned int bl_cbus_read(unsigned int reg)
+{
+       return aml_read_cbus(reg);
+};
+
+static inline void bl_cbus_write(unsigned int reg, unsigned int value)
+{
+       aml_write_cbus(reg, value);
+};
+
+static inline void bl_cbus_setb(unsigned int reg, unsigned int value,
+               unsigned int _start, unsigned int _len)
+{
+       bl_cbus_write(reg, ((bl_cbus_read(reg) &
+                               (~(((1L << _len)-1) << _start))) |
+                               ((value & ((1L << _len)-1)) << _start)));
+
+}
+
+static inline unsigned int bl_vcbus_read(unsigned int reg)
+{
+       return aml_read_vcbus(reg);
+};
+
+static inline void bl_vcbus_write(unsigned int reg, unsigned int value)
+{
+       aml_write_vcbus(reg, value);
+};
+
+static inline void bl_vcbus_setb(unsigned int reg, unsigned int value,
+               unsigned int _start, unsigned int _len)
+{
+       bl_vcbus_write(reg, ((bl_vcbus_read(reg) &
+                               (~(((1L << _len)-1) << _start))) |
+                               ((value & ((1L << _len)-1)) << _start)));
+
+}
+
+#endif
+
diff --git a/drivers/amlogic/media/vout/backlight/aml_ldim/Kconfig b/drivers/amlogic/media/vout/backlight/aml_ldim/Kconfig
new file mode 100644 (file)
index 0000000..f8548e2
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# LDIM Device Driver Configuration
+#
+config AMLOGIC_LOCAL_DIMMING
+       bool "Amlogic Local dimming"
+       default n
+       help
+         Say Y if you want to use Amlogic Local dimming.
+         The role of local dimming is according to each
+         image information, calculate the corresponding
+         brightness values, to improve the effect of image quality,
+
diff --git a/drivers/amlogic/media/vout/backlight/bl_extern/Kconfig b/drivers/amlogic/media/vout/backlight/bl_extern/Kconfig
new file mode 100644 (file)
index 0000000..6873fe6
--- /dev/null
@@ -0,0 +1,40 @@
+
+config AMLOGIC_BL_EXTERN
+       bool "Amlogic backlight extern driver support"
+       default n
+       help
+               Say Y if you want to use Amlogic backlight external controller.
+               Amlogic backlight external driver support,
+               such as i2c or mipi initial
+               According to choose the corresponding bl_extern inside index bl_extern driver
+
+config AMLOGIC_BL_EXTERN_PMU_AML1218
+       bool "Amlogic backlight pmu aml1218"
+       depends on AMLOGIC_BL_EXTERN
+       default n
+       help
+               pmu aml1218 backlight controller support.
+               Once the backlight power on, according to the timing requirements,
+               through the aml1218 to write data to the backlight,
+               make its initialization
+
+
+config AMLOGIC_BL_EXTERN_I2C_LP8556
+       bool "Amlogic backlight i2c lp8556"
+       depends on AMLOGIC_BL_EXTERN
+       default n
+       help
+               i2c lp8556 backlight controller support.
+               Once the backlight power on, according to the timing requirements,
+               through the i2c_lp8556 to write data to the backlight,
+               make its initialization
+
+config AMLOGIC_BL_EXTERN_MIPI_LT070ME05
+       bool "Amlogic backlight mipi LT070ME05"
+       depends on AMLOGIC_BL_EXTERN
+       default n
+       help
+               mipi LT070ME05 backlight controller support.
+               Once the backlight power on, according to the timing requirements,
+               through the mipi_LT070ME05 to write data to the backlight,
+               make its initialization
\ No newline at end of file
diff --git a/drivers/amlogic/media/vout/backlight/bl_extern/Makefile b/drivers/amlogic/media/vout/backlight/bl_extern/Makefile
new file mode 100644 (file)
index 0000000..8c260b5
--- /dev/null
@@ -0,0 +1,6 @@
+
+obj-$(CONFIG_AMLOGIC_BL_EXTERN)                        += bl_extern.o
+obj-$(CONFIG_AMLOGIC_BL_EXTERN_I2C_LP8556)             += i2c_lp8556.o
+obj-$(CONFIG_AMLOGIC_BL_EXTERN_PMU_AML1218)            += pmu_aml1218.o
+obj-$(CONFIG_AMLOGIC_BL_EXTERN_MIPI_LT070ME05) += mipi_LT070ME05.o
+
diff --git a/drivers/amlogic/media/vout/backlight/bl_extern/aml_bl_extern.dts b/drivers/amlogic/media/vout/backlight/bl_extern/aml_bl_extern.dts
new file mode 100644 (file)
index 0000000..1e55875
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * drivers/amlogic/media/vout/backlight/bl_extern/aml_bl_extern.dts
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+bl_extern_pmu_aml1218{
+       compatible = "amlogic, bl_pmu_aml1218";
+       dev_name ="bl_pmu_aml1218";
+       status = "disabled"; /** "disabled" or "okay" */
+       GPIODV_28-gpios = <&gpio GPIODV_28 0>;
+       gpio_enable_on_off = "GPIODV_28","1","0";
+               /* gpio("n"for none),
+                * on/off(1=output high, 0=output low, 2=input)
+                */
+       type = <2>;     /** bl_extern_driver type: 0=i2c, 1=spi, 2=other */
+       dim_max_min = <0x1 0x1b>;
+};
+
+
+bl_extern_i2c_lp8556{
+       compatible = "amlogic, bl_i2c_lp8556";
+       dev_name ="bl_i2c_lp8556";
+       status = "disabled"; /** "disabled" or "okay" */
+       GPIODV_28-gpios = <&gpio GPIODV_28 0>;
+       gpio_enable_on_off = "GPIODV_28","1","0";
+               /* gpio("n"for none),
+                * on/off(1=output high, 0=output low, 2=input)
+                */
+       type = <0>;     /** bl_extern_driver type: 0=i2c, 1=spi, 2=other */
+       i2c_address = <0x2c>; /** 7bit i2c address */
+       i2c_bus = "i2c_bus_b";
+       dim_max_min = <255 10>;
+};
+
+
+bl_extern_mipi_LT070ME05{
+       compatible = "amlogic, bl_mipi_LT070ME05";
+       dev_name ="bl_mipi_LT070ME056";
+       status = "disabled"; /** "disabled" or "okay" */
+       GPIODV_28-gpios = <&gpio GPIODV_28 0>;
+       gpio_enable_on_off = "GPIODV_28","1","0";
+               /* gpio("n"for none),
+                * on/off(1=output high, 0=output low, 2=input)
+                */
+       type = <2>;     /** bl_extern_driver type: 0=i2c, 1=spi, 2=other */
+       dim_max_min = <255 10>;
+};
diff --git a/drivers/amlogic/media/vout/backlight/bl_extern/bl_extern.c b/drivers/amlogic/media/vout/backlight/bl_extern/bl_extern.c
new file mode 100644 (file)
index 0000000..1626e25
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * drivers/amlogic/media/vout/backlight/bl_extern/bl_extern.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/aml_bl_extern.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+
+#ifdef BL_EXT_DEBUG_INFO
+#define DBG_PRINT(...)         pr_info(__VA_ARGS__)
+#else
+#define DBG_PRINT(...)
+#endif
+
+static struct aml_bl_extern_driver_t bl_ext_driver = {
+       .type = BL_EXTERN_MAX,
+       .name = NULL,
+       .power_on = NULL,
+       .power_off = NULL,
+       .set_level = NULL,
+
+};
+
+struct aml_bl_extern_driver_t *aml_bl_extern_get_driver(void)
+{
+       return &bl_ext_driver;
+}
+
+int bl_extern_driver_check(void)
+{
+       struct aml_bl_extern_driver_t *bl_ext;
+
+       bl_ext = aml_bl_extern_get_driver();
+       if (bl_ext) {
+               if (bl_ext->type < BL_EXTERN_MAX) {
+                       pr_err("[warning]: bl_extern has already exist (%s)\n",
+                                                               bl_ext->name);
+                       return -1;
+               }
+       } else {
+               pr_err("get bl_extern_driver failed\n");
+       }
+       return 0;
+}
+
+int get_bl_extern_dt_data(struct device dev, struct bl_extern_config_t *pdata)
+{
+       int ret;
+       struct device_node *of_node = dev.of_node;
+       u32 bl_para[2];
+       const char *str;
+       struct gpio_desc *bl_extern_gpio;
+
+       ret = of_property_read_string(of_node, "dev_name",
+                                       (const char **)&pdata->name);
+       if (ret) {
+               pdata->name = "aml_bl_extern";
+               pr_err("warning: get dev_name failed\n");
+       }
+
+       ret = of_property_read_u32(of_node, "type", &pdata->type);
+       if (ret) {
+               pdata->type = BL_EXTERN_MAX;
+               pr_err("%s warning: get type failed, exit\n", pdata->name);
+               return -1;
+       }
+       pdata->gpio_used = 0;
+       ret = of_property_read_string_index(of_node,
+                                               "gpio_enable_on_off", 0, &str);
+       if (ret) {
+               pr_warn("%s warning: get gpio_enable failed\n", pdata->name);
+       } else {
+               if (strncmp(str, "G", 1) == 0) {
+                       pdata->gpio_used = 1;
+                       bl_extern_gpio = gpiod_get(&dev, str);
+                       if (bl_extern_gpio) {
+                               pr_warn("%s warning:failed to alloc gpio (%s)\n",
+                                                       pdata->name, str);
+                       }
+                       pdata->gpio = bl_extern_gpio;
+               }
+               DBG_PRINT("%s: gpio_enable %s\n",
+                               pdata->name, ((pdata->gpio_used) ? str:"none"));
+       }
+       ret = of_property_read_string_index(of_node,
+                                       "gpio_enable_on_off", 1, &str);
+       if (ret) {
+               pr_warn("%s warning: get gpio_enable_on failed\n", pdata->name);
+       } else {
+               if (strncmp(str, "2", 1) == 0)
+                       pdata->gpio_on = LCD_POWER_GPIO_INPUT;
+               else if (strncmp(str, "0", 1) == 0)
+                       pdata->gpio_on = LCD_POWER_GPIO_OUTPUT_LOW;
+               else
+                       pdata->gpio_on = LCD_POWER_GPIO_OUTPUT_HIGH;
+       }
+       ret = of_property_read_string_index(of_node,
+                                       "gpio_enable_on_off", 2, &str);
+       if (ret) {
+               pr_warn("%s warning:get gpio_enable_off failed\n", pdata->name);
+       } else {
+               if (strncmp(str, "2", 1) == 0)
+                       pdata->gpio_off = LCD_POWER_GPIO_INPUT;
+               else if (strncmp(str, "1", 1) == 0)
+                       pdata->gpio_off = LCD_POWER_GPIO_OUTPUT_HIGH;
+               else
+                       pdata->gpio_off = LCD_POWER_GPIO_OUTPUT_LOW;
+       }
+       DBG_PRINT("%s: gpio_on = %d,", pdata->name, pdata->gpio_on);
+       DBG_PRINT("gpio_off = %d\n", pdata->gpio_off);
+       switch (pdata->type) {
+       case BL_EXTERN_I2C:
+               ret = of_property_read_u32(of_node, "i2c_address",
+                                                       &pdata->i2c_addr);
+               if (ret) {
+                       pr_warn("%s warning: get i2c_address failed\n",
+                                                               pdata->name);
+                       pdata->i2c_addr = 0;
+               }
+               DBG_PRINT("%s: i2c_address=", pdata->name);
+               DBG_PRINT("0x%02x\n", pdata->i2c_addr);
+               ret = of_property_read_string(of_node, "i2c_bus", &str);
+               if (ret) {
+                       pr_warn("%s warning: get i2c_bus failed,", pdata->name);
+                       pr_warn("use default i2c bus\n");
+                       pdata->i2c_bus = AML_I2C_MASTER_A;
+               } else {
+                       if (strncmp(str, "i2c_bus_a", 9) == 0)
+                               pdata->i2c_bus = AML_I2C_MASTER_A;
+                       else if (strncmp(str, "i2c_bus_b", 9) == 0)
+                               pdata->i2c_bus = AML_I2C_MASTER_B;
+                       else if (strncmp(str, "i2c_bus_c", 9) == 0)
+                               pdata->i2c_bus = AML_I2C_MASTER_C;
+                       else if (strncmp(str, "i2c_bus_d", 9) == 0)
+                               pdata->i2c_bus = AML_I2C_MASTER_D;
+                       else if (strncmp(str, "i2c_bus_ao", 10) == 0)
+                               pdata->i2c_bus = AML_I2C_MASTER_AO;
+                       else
+                               pdata->i2c_bus = AML_I2C_MASTER_A;
+               }
+               DBG_PRINT("%s: i2c_bus=%s[%d]\n", pdata->name,
+                                               str, pdata->i2c_bus);
+               break;
+       case BL_EXTERN_SPI:
+               ret = of_property_read_string(of_node, "gpio_spi_cs", &str);
+               if (ret) {
+                       pr_warn("%s warning: get spi gpio_spi_cs failed\n",
+                                                               pdata->name);
+                       pdata->spi_cs = NULL;
+               } else {
+                       bl_extern_gpio = gpiod_get(&dev, str);
+                       if (bl_extern_gpio) {
+                               pdata->spi_cs = bl_extern_gpio;
+                               DBG_PRINT("spi_cs gpio = %s\n", str);
+                       } else {
+                               pr_warn("%s warning:failed to alloc gpio (%s)\n",
+                                                       pdata->name, str);
+                               pdata->spi_cs = NULL;
+                       }
+               }
+               ret = of_property_read_string(of_node, "gpio_spi_clk", &str);
+               if (ret) {
+                       pr_warn("%s warning: get spi gpio_spi_clk failed\n",
+                                                               pdata->name);
+                       pdata->spi_clk = NULL;
+               } else {
+                       bl_extern_gpio = gpiod_get(&dev, str);
+                       if (bl_extern_gpio) {
+                               pdata->spi_clk = bl_extern_gpio;
+                               DBG_PRINT("spi_cs gpio = %s\n", str);
+                       } else {
+                               pdata->spi_clk = NULL;
+                               pr_warn("%s warning:failed to alloc gpio (%s)\n",
+                                                       pdata->name, str);
+                       }
+               }
+               ret = of_property_read_string(of_node, "gpio_spi_data", &str);
+               if (ret) {
+                       pr_warn("%s warning: get spi gpio_spi_data failed\n",
+                                                               pdata->name);
+                       pdata->spi_data = NULL;
+               }  else {
+                       bl_extern_gpio = gpiod_get(&dev, str);
+                       if (bl_extern_gpio) {
+                               pdata->spi_data = bl_extern_gpio;
+                               DBG_PRINT("spi_cs gpio = %s\n", str);
+                       } else {
+                               pdata->spi_data = NULL;
+                               pr_warn("%s warning:failed to alloc gpio (%s)\n",
+                                                       pdata->name, str);
+                       }
+               }
+               break;
+       case BL_EXTERN_OTHER:
+               break;
+       default:
+               break;
+       }
+       ret = of_property_read_u32_array(of_node, "dim_max_min",
+                                                       &bl_para[0], 2);
+       if (ret) {
+               pr_warn("%s warning: get dim_max_min failed\n", pdata->name);
+               pdata->dim_max = 0;
+               pdata->dim_min = 0;
+       }  else {
+               pdata->dim_max = bl_para[0];
+               pdata->dim_min = bl_para[1];
+       }
+       DBG_PRINT("%s dim_min = %d, dim_max = %d\n", pdata->name,
+                       pdata->dim_min, pdata->dim_max);
+       return 0;
+}
diff --git a/drivers/amlogic/media/vout/backlight/bl_extern/i2c_lp8556.c b/drivers/amlogic/media/vout/backlight/bl_extern/i2c_lp8556.c
new file mode 100644 (file)
index 0000000..472eaf0
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * drivers/amlogic/media/vout/backlight/bl_extern/i2c_lp8556.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/sysctl.h>
+#include <linux/uaccess.h>
+#include <linux/amlogic/media/vout/lcd/aml_bl_extern.h>
+
+static struct bl_extern_config_t *bl_ext_config;
+
+static struct i2c_client *aml_lp8556_i2c_client;
+
+
+#ifdef BL_EXT_DEBUG_INFO
+#define DBG_PRINT(...)         pr_info(__VA_ARGS__)
+#else
+#define DBG_PRINT(...)
+#endif
+
+#define BL_EXTERN_NAME                 "bl_i2c_lp8556"
+static unsigned int bl_status = 1;
+static unsigned int bl_level;
+
+static unsigned char i2c_init_table[][2] = {
+       {0xa1, 0x76}, /* hight bit(8~11)(0~0X66e set backlight) */
+       {0xa0, 0x66},  /* low bit(0~7)  20mA */
+       {0x16, 0x1F}, /* 5channel LED enable 0x1F */
+       {0xa9, 0xA0}, /* VBOOST_MAX 25V */
+       {0x9e, 0x12},
+       {0xa2, 0x23},
+       {0x01, 0x05}, /* 0x03 pwm+I2c set brightness,0x5 I2c set brightness */
+       {0xff, 0xff}, /* ending flag */
+};
+
+static int aml_i2c_write(struct i2c_client *i2client,
+                               unsigned char *buff, unsigned int len)
+{
+       int res = 0;
+       struct i2c_msg msg[] = {
+               {
+                       .addr = i2client->addr,
+                       .flags = 0,
+                       .len = len,
+                       .buf = buff,
+               }
+       };
+
+       res = i2c_transfer(i2client->adapter, msg, 1);
+       if (res < 0)
+               pr_err("%s: i2c transfer failed [addr 0x%02x]\n",
+                                               __func__, i2client->addr);
+
+       return res;
+}
+#if 0
+static int aml_i2c_read(struct i2c_client *i2client,
+                               unsigned char *buff, unsigned int len)
+{
+       int res = 0;
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = i2client->addr,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = buff,
+               },
+               {
+                       .addr = i2client->addr,
+                       .flags = I2C_M_RD,
+                       .len = len,
+                       .buf = buff,
+               }
+       };
+       res = i2c_transfer(i2client->adapter, msgs, 2);
+       if (res < 0)
+               pr_err("%s: i2c transfer failed [addr 0x%02x]\n",
+                                               __func__, i2client->addr);
+       return res;
+}
+#endif
+
+static int bl_extern_set_level(unsigned int level)
+{
+       unsigned char tData[3];
+       int ret = 0;
+       struct aml_bl_drv_s *bl_drv = aml_bl_get_driver();
+       unsigned int level_max, level_min;
+       unsigned int dim_max, dim_min;
+
+       bl_level = level;
+
+       if (bl_ext_config == NULL) {
+               pr_err("no %s driver\n", BL_EXTERN_NAME);
+               return -1;
+       }
+
+       if (bl_drv == NULL)
+               return -1;
+       level_max = bl_drv->bconf->level_max;
+       level_min = bl_drv->bconf->level_min;
+       dim_max = bl_ext_config->dim_max;
+       dim_min = bl_ext_config->dim_min;
+       level = dim_min - ((level - level_min) * (dim_min - dim_max)) /
+                       (level_max - level_min);
+       level &= 0xff;
+
+       if (bl_status) {
+               tData[0] = 0x0;
+               tData[1] = level;
+               ret = aml_i2c_write(aml_lp8556_i2c_client, tData, 2);
+       }
+       return ret;
+}
+
+static int bl_extern_power_on(void)
+{
+       unsigned char tData[3];
+       int i = 0, ending_flag = 0;
+       int ret = 0;
+
+       if (bl_ext_config->gpio) {
+               if (bl_ext_config->gpio_on == 2) {
+                       bl_gpio_input(bl_ext_config->gpio);
+               } else {
+                       bl_gpio_output(bl_ext_config->gpio,
+                                       bl_ext_config->gpio_on);
+               }
+       }
+
+       while (ending_flag == 0) {
+               if (i2c_init_table[i][0] == 0xff) {
+                       if (i2c_init_table[i][1] == 0xff)
+                               ending_flag = 1;
+                       else
+                               mdelay(i2c_init_table[i][1]);
+               } else {
+                       tData[0] = i2c_init_table[i][0];
+                       tData[1] = i2c_init_table[i][1];
+                       ret = aml_i2c_write(aml_lp8556_i2c_client, tData, 2);
+               }
+               i++;
+       }
+       bl_status = 1;
+       bl_extern_set_level(bl_level);
+       BLPR("%s\n", __func__);
+       return ret;
+}
+
+static int bl_extern_power_off(void)
+{
+       bl_status = 0;
+       if (bl_ext_config->gpio) {
+               if (bl_ext_config->gpio_off == 2) {
+                       bl_gpio_input(bl_ext_config->gpio);
+               } else {
+                       bl_gpio_output(bl_ext_config->gpio,
+                                       bl_ext_config->gpio_off);
+               }
+       }
+       BLPR("%s\n", __func__);
+       return 0;
+}
+
+static int get_bl_extern_config(struct device dev,
+               struct bl_extern_config_t *bl_ext_cfg)
+{
+       int ret = 0;
+       struct aml_bl_extern_driver_t *bl_ext;
+
+       ret = get_bl_extern_dt_data(dev, bl_ext_cfg);
+       if (ret) {
+               BLPR("error %s: failed to get dt data\n", BL_EXTERN_NAME);
+               return ret;
+       }
+
+       if (bl_ext_cfg->dim_min > 0xff)
+               bl_ext_cfg->dim_min = 0xff;
+       if (bl_ext_cfg->dim_max > 0xff)
+               bl_ext_cfg->dim_max = 0xff;
+
+       bl_ext = aml_bl_extern_get_driver();
+       if (bl_ext) {
+               bl_ext->type      = bl_ext_cfg->type;
+               bl_ext->name      = bl_ext_cfg->name;
+               bl_ext->power_on  = bl_extern_power_on;
+               bl_ext->power_off = bl_extern_power_off;
+               bl_ext->set_level = bl_extern_set_level;
+       } else {
+               BLPR("error %s get bl_extern_driver failed\n",
+                       bl_ext_cfg->name);
+               ret = -1;
+       }
+       return ret;
+}
+
+static int aml_lp8556_i2c_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+               BLPR("error %s: functionality check failed\n",
+                                                               __func__);
+       else
+               aml_lp8556_i2c_client = client;
+       BLPR("%s OK\n", __func__);
+
+       return 0;
+}
+
+static int aml_lp8556_i2c_remove(struct i2c_client *client)
+{
+       return 0;
+}
+
+static const struct i2c_device_id aml_lp8556_i2c_id[] = {
+       {BL_EXTERN_NAME, 0},
+       { }
+};
+
+static struct i2c_driver aml_lp8556_i2c_driver = {
+       .probe    = aml_lp8556_i2c_probe,
+       .remove   = aml_lp8556_i2c_remove,
+       .id_table = aml_lp8556_i2c_id,
+       .driver = {
+               .name = BL_EXTERN_NAME,
+               .owner = THIS_MODULE,
+       },
+};
+
+static int aml_lp8556_probe(struct platform_device *pdev)
+{
+       struct i2c_board_info i2c_info;
+       struct i2c_adapter *adapter;
+       struct i2c_client *i2c_client;
+       int ret = 0;
+
+       if (bl_extern_driver_check())
+               return -1;
+       if (bl_ext_config == NULL)
+               bl_ext_config = kzalloc(sizeof(*bl_ext_config), GFP_KERNEL);
+       if (bl_ext_config == NULL) {
+               BLPR("error %s probe: failed to alloc data\n", BL_EXTERN_NAME);
+               return -1;
+       }
+
+       pdev->dev.platform_data = bl_ext_config;
+       ret = get_bl_extern_config(pdev->dev, bl_ext_config);
+       if (ret)
+               goto bl_extern_probe_failed;
+
+       memset(&i2c_info, 0, sizeof(i2c_info));
+       adapter = i2c_get_adapter(bl_ext_config->i2c_bus);
+       if (!adapter) {
+               BLPR("error %s£ºfailed get i2c adapter\n", BL_EXTERN_NAME);
+               goto bl_extern_probe_failed;
+       }
+
+       strncpy(i2c_info.type, bl_ext_config->name, I2C_NAME_SIZE);
+       i2c_info.addr = bl_ext_config->i2c_addr;
+       i2c_info.platform_data = bl_ext_config;
+       i2c_info.flags = 0;
+       if (i2c_info.addr > 0x7f)
+               i2c_info.flags = 0x10;
+       i2c_client = i2c_new_device(adapter, &i2c_info);
+       if (!i2c_client) {
+               BLPR("error %s :failed new i2c device\n", BL_EXTERN_NAME);
+               goto bl_extern_probe_failed;
+       } else{
+               DBG_PRINT("error %s: new i2c device succeed\n",
+               ((struct bl_extern_data_t *)(bl_ext_config))->name);
+       }
+
+       if (!aml_lp8556_i2c_client) {
+               ret = i2c_add_driver(&aml_lp8556_i2c_driver);
+               if (ret) {
+                       BLPR("error %s probe: add i2c_driver failed\n",
+                                                               BL_EXTERN_NAME);
+                       goto bl_extern_probe_failed;
+               }
+       }
+       BLPR("%s ok\n", __func__);
+       return ret;
+
+bl_extern_probe_failed:
+       kfree(bl_ext_config);
+       bl_ext_config = NULL;
+
+       return -1;
+}
+
+static int aml_lp8556_remove(struct platform_device *pdev)
+{
+       kfree(pdev->dev.platform_data);
+       return 0;
+}
+
+#ifdef CONFIG_USE_OF
+static const struct of_device_id aml_lp8556_dt_match[] = {
+       {
+               .compatible = "amlogic, bl_i2c_lp8556",
+       },
+       {},
+};
+#else
+#define aml_lp8556_dt_match NULL
+#endif
+
+static struct platform_driver aml_lp8556_driver = {
+       .probe  = aml_lp8556_probe,
+       .remove = aml_lp8556_remove,
+       .driver = {
+               .name  = BL_EXTERN_NAME,
+               .owner = THIS_MODULE,
+#ifdef CONFIG_USE_OF
+               .of_match_table = aml_lp8556_dt_match,
+#endif
+       },
+};
+
+static int __init aml_lp8556_init(void)
+{
+       int ret;
+
+       BLPR("%s\n", __func__);
+       ret = platform_driver_register(&aml_lp8556_driver);
+       if (ret) {
+               BLPR("error %s failed to register bl extern driver\n",
+                       __func__);
+               return -ENODEV;
+       }
+       return ret;
+}
+
+static void __exit aml_lp8556_exit(void)
+{
+       platform_driver_unregister(&aml_lp8556_driver);
+}
+
+module_init(aml_lp8556_init);
+module_exit(aml_lp8556_exit);
+
+MODULE_AUTHOR("AMLOGIC");
+MODULE_DESCRIPTION("BL Extern driver for LP8556");
+MODULE_LICENSE("GPL");
diff --git a/drivers/amlogic/media/vout/backlight/bl_extern/mipi_LT070ME05.c b/drivers/amlogic/media/vout/backlight/bl_extern/mipi_LT070ME05.c
new file mode 100644 (file)
index 0000000..d47ed5c
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * drivers/amlogic/media/vout/backlight/bl_extern/mipi_LT070ME05.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/sysctl.h>
+#include <linux/uaccess.h>
+#include <linux/amlogic/media/vout/lcd/aml_bl_extern.h>
+
+#ifdef CONFIG_LCD_IF_MIPI_VALID
+static struct bl_extern_config_t *bl_ext_config;
+
+#ifdef BL_EXT_DEBUG_INFO
+#define DBG_PRINT(...)         pr_info(__VA_ARGS__)
+#else
+#define DBG_PRINT(...)
+#endif
+
+#define BL_EXTERN_NAME                 "bl_mipi_LT070ME05"
+static unsigned int bl_status = 1;
+static unsigned int bl_level;
+
+/******************** mipi command ********************
+ * format:  data_type, num, data....
+ * special: data_type=0xff, num<0xff means delay ms, num=0xff means ending.
+ */
+static int bl_extern_set_level(unsigned int level)
+{
+       unsigned char payload[] = {0x15, 2, 0x51, 0xe6, 0xff, 0xff};
+
+       bl_level = level;
+
+       if (bl_ext_config == NULL) {
+               pr_err("no %s driver\n", BL_EXTERN_NAME);
+               return -1;
+       }
+       get_bl_ext_level(bl_ext_config);
+       level = bl_ext_config->dim_min -
+               ((level - bl_ext_config->level_min) *
+                       (bl_ext_config->dim_min - bl_ext_config->dim_max)) /
+                       (bl_ext_config->level_max - bl_ext_config->level_min);
+       level &= 0xff;
+
+       if (bl_status) {
+               payload[3] = level;
+               dsi_write_cmd(payload);
+       }
+       return 0;
+}
+
+static int bl_extern_power_on(void)
+{
+       if (bl_ext_config->gpio_used > 0) {
+               if (bl_ext_config->gpio_on == 2) {
+                       bl_extern_gpio_input(bl_ext_config->gpio);
+               } else {
+                       bl_extern_gpio_output(bl_ext_config->gpio,
+                                                       bl_ext_config->gpio_on);
+               }
+       }
+       bl_status = 1;
+       bl_extern_set_level(bl_level);
+       pr_info("%s\n", __func__);
+       return 0;
+}
+
+static int bl_extern_power_off(void)
+{
+       if (bl_ext_config->gpio_used > 0) {
+               if (bl_ext_config->gpio_off == 2) {
+                       bl_extern_gpio_input(bl_ext_config->gpio);
+               } else {
+                       bl_extern_gpio_output(bl_ext_config->gpio,
+                                               bl_ext_config->gpio_off);
+               }
+       }
+       pr_info("%s\n", __func__);
+       return 0;
+}
+
+static int get_bl_extern_config(struct device dev,
+                               struct bl_extern_config_t *bl_ext_cfg)
+{
+       int ret = 0;
+       struct aml_bl_extern_driver_t *bl_ext;
+
+       ret = get_bl_extern_dt_data(dev, bl_ext_cfg);
+       if (ret) {
+               pr_err("[error] %s: failed to get dt data\n", BL_EXTERN_NAME);
+               return ret;
+       }
+
+       if (bl_ext_cfg->dim_min > 0xff)
+               bl_ext_cfg->dim_min = 0xff;
+       if (bl_ext_cfg->dim_max > 0xff)
+               bl_ext_cfg->dim_max = 0xff;
+
+       bl_ext = aml_bl_extern_get_driver();
+       if (bl_ext) {
+               bl_ext->type      = bl_ext_cfg->type;
+               bl_ext->name      = bl_ext_cfg->name;
+               bl_ext->power_on  = bl_extern_power_on;
+               bl_ext->power_off = bl_extern_power_off;
+               bl_ext->set_level = bl_extern_set_level;
+       } else {
+               pr_err("[error] %s get bl_extern_driver failed\n",
+                                                       bl_ext_cfg->name);
+               ret = -1;
+       }
+       return ret;
+}
+
+static int aml_LT070ME05_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+
+       if (bl_extern_driver_check())
+               return -1;
+       if (bl_ext_config == NULL)
+               bl_ext_config = kzalloc(sizeof(*bl_ext_config), GFP_KERNEL);
+       if (bl_ext_config == NULL) {
+               pr_err("[error] %s probe: failed to alloc data\n",
+                                                               BL_EXTERN_NAME);
+               return -1;
+       }
+
+       pdev->dev.platform_data = bl_ext_config;
+       ret = get_bl_extern_config(pdev->dev, bl_ext_config);
+       if (ret)
+               goto bl_extern_probe_failed;
+
+       pr_info("%s ok\n", __func__);
+       return ret;
+
+bl_extern_probe_failed:
+       kfree(bl_ext_config);
+       bl_ext_config = NULL;
+       return -1;
+}
+
+static int aml_LT070ME05_remove(struct platform_device *pdev)
+{
+       kfree(pdev->dev.platform_data);
+       return 0;
+}
+
+#ifdef CONFIG_USE_OF
+static const struct of_device_id aml_LT070ME05_dt_match[] = {
+       {
+               .compatible = "amlogic, bl_mipi_LT070ME05",
+       },
+       {},
+};
+#else
+#define aml_LT070ME05_dt_match NULL
+#endif
+
+static struct platform_driver aml_LT070ME05_driver = {
+       .probe  = aml_LT070ME05_probe,
+       .remove = aml_LT070ME05_remove,
+       .driver = {
+               .name  = BL_EXTERN_NAME,
+               .owner = THIS_MODULE,
+#ifdef CONFIG_USE_OF
+               .of_match_table = aml_LT070ME05_dt_match,
+#endif
+       },
+};
+
+static int __init aml_LT070ME05_init(void)
+{
+       int ret;
+
+       DBG_PRINT("%s\n", __func__);
+
+       ret = platform_driver_register(&aml_LT070ME05_driver);
+       if (ret) {
+               pr_err("[error] %s failed to register bl extern driver\n",
+                                                               __func__);
+               return -ENODEV;
+       }
+       return ret;
+}
+
+static void __exit aml_LT070ME05_exit(void)
+{
+       platform_driver_unregister(&aml_LT070ME05_driver);
+}
+
+rootfs_initcall(aml_LT070ME05_init);
+module_exit(aml_LT070ME05_exit);
+
+MODULE_AUTHOR("AMLOGIC");
+MODULE_DESCRIPTION("BL Extern driver for LT070ME05");
+MODULE_LICENSE("GPL");
+
+#endif
diff --git a/drivers/amlogic/media/vout/backlight/bl_extern/pmu_aml1218.c b/drivers/amlogic/media/vout/backlight/bl_extern/pmu_aml1218.c
new file mode 100644 (file)
index 0000000..efe4994
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * drivers/amlogic/media/vout/backlight/bl_extern/pmu_aml1218.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/sysctl.h>
+#include <linux/uaccess.h>
+#include <linux/amlogic/media/vout/lcd/aml_bl_extern.h>
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+#include <linux/amlogic/aml_pmu_common.h>
+#endif
+
+static struct bl_extern_config_t *bl_ext_config;
+
+
+#ifdef BL_EXT_DEBUG_INFO
+#define DBG_PRINT(...)         pr_info(__VA_ARGS__)
+#else
+#define DBG_PRINT(...)
+#endif
+
+#define BL_EXTERN_NAME                 "bl_pmu_aml1218"
+
+static int bl_extern_set_level(unsigned int level)
+{
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+       struct aml_pmu_driver *pmu_driver;
+       unsigned char temp;
+#endif
+       int ret = 0;
+
+       if (bl_ext_config == NULL) {
+               pr_err("no %s driver\n", BL_EXTERN_NAME);
+               return -1;
+       }
+       get_bl_ext_level(bl_ext_config);
+       level = bl_ext_config->dim_min -
+               ((level - bl_ext_config->level_min) *
+                       (bl_ext_config->dim_min - bl_ext_config->dim_max)) /
+                       (bl_ext_config->level_max - bl_ext_config->level_min);
+       level &= 0x1f;
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+       pmu_driver = aml_pmu_get_driver();
+       if (pmu_driver == NULL) {
+               pr_err("no pmu driver\n");
+       } else {
+               if ((pmu_driver->pmu_reg_write) && (pmu_driver->pmu_reg_read)) {
+                       ret = pmu_driver->pmu_reg_read(0x005f, &temp);
+                       temp &= ~(0x3f << 2);
+                       temp |= (level << 2);
+                       ret = pmu_driver->pmu_reg_write(0x005f, temp);
+               } else {
+                       pr_err("no pmu_reg_read/write\n");
+                       return -1;
+               }
+       }
+#endif
+       return ret;
+}
+
+static int bl_extern_power_on(void)
+{
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+       struct aml_pmu_driver *pmu_driver;
+       unsigned char temp;
+#endif
+       int ret = 0;
+
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+       pmu_driver = aml_pmu_get_driver();
+       if (pmu_driver == NULL) {
+               pr_err("no pmu driver\n");
+       } else {
+               if ((pmu_driver->pmu_reg_write) && (pmu_driver->pmu_reg_read)) {
+                       ret = pmu_driver->pmu_reg_read(0x005e, &temp);
+                       temp |= (1 << 7);
+                       ret = pmu_driver->pmu_reg_write(0x005e, temp);
+               } else {
+                       pr_err("no pmu_reg_read/write\n");
+                       return -1;
+               }
+       }
+#endif
+       if (bl_ext_config->gpio_used > 0) {
+               if (bl_ext_config->gpio_on == 2)
+                       bl_extern_gpio_input(bl_ext_config->gpio);
+               else
+                       bl_extern_gpio_output(bl_ext_config->gpio,
+                                                       bl_ext_config->gpio_on);
+       }
+       pr_info("%s\n", __func__);
+       return ret;
+}
+
+static int bl_extern_power_off(void)
+{
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+       struct aml_pmu_driver *pmu_driver;
+       unsigned char temp;
+#endif
+       int ret = 0;
+
+       if (bl_ext_config->gpio_used > 0) {
+               if (bl_ext_config->gpio_off == 2)
+                       bl_extern_gpio_input(bl_ext_config->gpio);
+               else
+                       bl_extern_gpio_output(bl_ext_config->gpio,
+                                               bl_ext_config->gpio_off);
+       }
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+       pmu_driver = aml_pmu_get_driver();
+       if (pmu_driver == NULL) {
+               pr_err("no pmu driver\n");
+       }  else {
+               if ((pmu_driver->pmu_reg_write) && (pmu_driver->pmu_reg_read)) {
+                       ret = pmu_driver->pmu_reg_read(0x005e, &temp);
+                       temp &= ~(1 << 7);
+                       ret = pmu_driver->pmu_reg_write(0x005e, temp);
+               }  else {
+                       pr_err("no pmu_reg_read/write\n");
+                       return -1;
+               }
+       }
+#endif
+       pr_info("%s\n", __func__);
+       return ret;
+}
+
+static ssize_t bl_extern_debug(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+       struct aml_pmu_driver *pmu_driver;
+       unsigned char temp;
+       unsigned int t[2];
+#endif
+       int ret = 0;
+
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+       pmu_driver = aml_pmu_get_driver();
+       if (pmu_driver == NULL) {
+               pr_err("no pmu driver\n");
+               return -EINVAL;
+       }
+
+       switch (buf[0]) {
+       case 'r':
+               ret = sscanf(buf, "r %x", &t[0]);
+               if ((pmu_driver->pmu_reg_write) && (pmu_driver->pmu_reg_read)) {
+                       ret = pmu_driver->pmu_reg_read(t[0], &temp);
+                       pr_info("read pmu reg: 0x%x=0x%x\n", t[0], temp);
+               }
+               break;
+       case 'w':
+               ret = sscanf(buf, "w %x %x", &t[0], &t[1]);
+               if ((pmu_driver->pmu_reg_write) && (pmu_driver->pmu_reg_read)) {
+                       ret = pmu_driver->pmu_reg_write(t[0], t[1]);
+                       ret = pmu_driver->pmu_reg_read(t[0], &temp);
+                       pr_info("write pmu reg 0x%x: 0x%x, readback: 0x%x\n",
+                                                       t[0], t[1], temp);
+               }
+               break;
+       default:
+               pr_err("wrong format of command.\n");
+               break;
+       }
+#endif
+       if (ret != 1 || ret != 2)
+               return -EINVAL;
+       return count;
+}
+
+static struct class_attribute bl_extern_debug_class_attrs[] = {
+       __ATTR(debug, 0644, NULL, bl_extern_debug),
+       __ATTR_NULL
+};
+
+static struct class bl_extern_debug_class = {
+       .name = "bl_ext",
+       .class_attrs = bl_extern_debug_class_attrs,
+};
+
+static int get_bl_extern_config(struct device dev,
+                               struct bl_extern_config_t *bl_ext_cfg)
+{
+       int ret = 0;
+       struct aml_bl_extern_driver_t *bl_ext;
+
+       ret = get_bl_extern_dt_data(dev, bl_ext_cfg);
+       if (ret) {
+               pr_err("[error] %s: failed to get dt data\n", BL_EXTERN_NAME);
+               return ret;
+       }
+
+       if (bl_ext_cfg->dim_min > 0x1f)
+               bl_ext_cfg->dim_min = 0x1f;
+       if (bl_ext_cfg->dim_max > 0x1f)
+               bl_ext_cfg->dim_max = 0x1f;
+
+       bl_ext = aml_bl_extern_get_driver();
+       if (bl_ext) {
+               bl_ext->type      = bl_ext_cfg->type;
+               bl_ext->name      = bl_ext_cfg->name;
+               bl_ext->power_on  = bl_extern_power_on;
+               bl_ext->power_off = bl_extern_power_off;
+               bl_ext->set_level = bl_extern_set_level;
+       } else {
+               pr_err("[error] %s get bl_extern_driver failed\n",
+                                                       bl_ext_cfg->name);
+               ret = -1;
+       }
+       return ret;
+}
+
+static int aml_aml1218_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+
+       if (bl_extern_driver_check())
+               return -1;
+       if (bl_ext_config == NULL)
+               bl_ext_config = kzalloc(sizeof(*bl_ext_config), GFP_KERNEL);
+       if (bl_ext_config == NULL) {
+               pr_err("[error] %s probe: failed to alloc data\n",
+                                                       BL_EXTERN_NAME);
+               return -1;
+       }
+
+       pdev->dev.platform_data = bl_ext_config;
+
+       ret = get_bl_extern_config(pdev->dev, bl_ext_config);
+       if (ret)
+               goto bl_extern_probe_failed;
+
+       ret = class_register(&bl_extern_debug_class);
+       if (ret)
+               pr_err("class register bl_extern_debug_class fail!\n");
+
+       pr_err("%s ok\n", __func__);
+       return ret;
+
+bl_extern_probe_failed:
+       kfree(bl_ext_config);
+       bl_ext_config = NULL;
+       return -1;
+}
+
+static int aml_aml1218_remove(struct platform_device *pdev)
+{
+       kfree(pdev->dev.platform_data);
+       return 0;
+}
+
+#ifdef CONFIG_USE_OF
+static const struct of_device_id aml_aml1218_dt_match[] = {
+       {
+               .compatible = "amlogic, bl_pmu_aml1218",
+       },
+       {},
+};
+#else
+#define aml_aml1218_dt_match NULL
+#endif
+
+static struct platform_driver aml_aml1218_driver = {
+       .probe  = aml_aml1218_probe,
+       .remove = aml_aml1218_remove,
+       .driver = {
+               .name  = BL_EXTERN_NAME,
+               .owner = THIS_MODULE,
+#ifdef CONFIG_USE_OF
+               .of_match_table = aml_aml1218_dt_match,
+#endif
+       },
+};
+
+static int __init aml_aml1218_init(void)
+{
+       int ret;
+
+       DBG_PRINT("%s\n", __func__);
+
+       ret = platform_driver_register(&aml_aml1218_driver);
+       if (ret) {
+               pr_err("[error] %s failed to register bl extern driver\n",
+                                                               __func__);
+               return -ENODEV;
+       }
+       return ret;
+}
+
+static void __exit aml_aml1218_exit(void)
+{
+       platform_driver_unregister(&aml_aml1218_driver);
+}
+
+rootfs_initcall(aml_aml1218_init);
+module_exit(aml_aml1218_exit);
+
+MODULE_AUTHOR("AMLOGIC");
+MODULE_DESCRIPTION("BL Extern driver for aml1218");
+MODULE_LICENSE("GPL");
diff --git a/drivers/amlogic/media/vout/lcd/Kconfig b/drivers/amlogic/media/vout/lcd/Kconfig
new file mode 100644 (file)
index 0000000..ae77c24
--- /dev/null
@@ -0,0 +1,37 @@
+#
+# Video output configuration
+#
+menu "Amlogic LCD Output Module"
+
+config AMLOGIC_LCD
+       bool "LCD Output Module"
+       default n
+       help
+               LCD output module
+               support tv mode and tablet mode
+               Through the inside of the DTS file "mode" is
+               to choose a TV mode or tablet mode
+
+config AMLOGIC_LCD_TV
+       bool "LCD TV Output Module"
+       default n
+       depends on AMLOGIC_LCD
+       help
+               LCD TV output module
+               Configure the LCD output model for TV output
+               TV mode mainly includes vbyone and an LVDS interface display
+               Through the environment variable is to choose panel_type lcd_index
+
+config AMLOGIC_LCD_TABLET
+       bool "LCD Tablet Output Module"
+       default n
+       depends on AMLOGIC_LCD
+       help
+               LCD Tablet output module
+               Configure the LCD output model for Tablet output
+
+if AMLOGIC_LCD
+source "drivers/amlogic/media/vout/lcd/lcd_extern/Kconfig"
+endif
+
+endmenu
diff --git a/drivers/amlogic/media/vout/lcd/Makefile b/drivers/amlogic/media/vout/lcd/Makefile
new file mode 100644 (file)
index 0000000..8018bfd
--- /dev/null
@@ -0,0 +1,4 @@
+obj-$(CONFIG_AMLOGIC_LCD) += lcd_vout.o lcd_reg.o lcd_common.o lcd_notify.o lcd_debug.o lcd_clk_config.o lcd_unifykey.o
+obj-$(CONFIG_AMLOGIC_LCD_TV) += lcd_tv/
+obj-$(CONFIG_AMLOGIC_LCD_TABLET) += lcd_tablet/
+obj-$(CONFIG_AMLOGIC_LCD_EXTERN) += lcd_extern/
diff --git a/drivers/amlogic/media/vout/lcd/lcd_clk_config.c b/drivers/amlogic/media/vout/lcd/lcd_clk_config.c
new file mode 100644 (file)
index 0000000..b35f057
--- /dev/null
@@ -0,0 +1,1971 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_clk_config.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include "lcd_reg.h"
+#include "lcd_clk_config.h"
+
+static spinlock_t lcd_clk_lock;
+
+static const unsigned int od_fb_table[2] = {1, 2};
+
+static const unsigned int od_table[4] = {
+       1, 2, 4, 8
+};
+
+static const unsigned int div_pre_table[6] = {
+       1, 2, 3, 4, 5, 6
+};
+
+static char *lcd_clk_div_sel_table[] = {
+       "1",
+       "2",
+       "3",
+       "3.5",
+       "3.75",
+       "4",
+       "5",
+       "6",
+       "6.25",
+       "7",
+       "7.5",
+       "12",
+       "14",
+       "15",
+       "2.5",
+       "invalid",
+};
+
+static char *lcd_pll_ss_table_gxtvbb[] = {
+       "0, disable",
+       "1, +/-0.3%",
+       "2, +/-0.5%",
+       "3, +/-0.9%",
+       "4, +/-1.2%",
+};
+
+static char *lcd_pll_ss_table_txl[] = {
+       "0, disable",
+       "1, +/-0.3%",
+       "2, +/-0.4%",
+       "3, +/-0.9%",
+       "4, +/-1.2%",
+};
+
+static char *lcd_pll_ss_table_txlx[] = {
+       "0, disable",
+       "1, +/-0.3%",
+       "2, +/-0.5%",
+       "3, +/-1.0%",
+       "4, +/-1.6%",
+       "5, +/-3.0%",
+};
+
+static struct lcd_clk_config_s clk_conf = { /* unit: kHz */
+       /* IN-OUT parameters */
+       .fin = FIN_FREQ,
+       .fout = 0,
+
+       /* pll parameters */
+       .od_fb = 0,
+       .pll_m = 0,
+       .pll_n = 0,
+       .pll_od1_sel = 0,
+       .pll_od2_sel = 0,
+       .pll_od3_sel = 0,
+       .pll_level = 0,
+       .ss_level = 0,
+       .edp_div0 = 0,
+       .edp_div1 = 0,
+       .div_pre = 0, /* m6, m8, m8b */
+       .div_post = 0, /* m6, m8, m8b */
+       .div_sel = 0, /* g9tv, g9bb, gxtvbb */
+       .xd = 0,
+       .pll_fout = 0,
+
+       /* clk path node parameters */
+       .pll_m_max = 0,
+       .pll_m_min = 0,
+       .pll_n_max = 0,
+       .pll_n_min = 0,
+       .pll_frac_range = 0,
+       .pll_od_sel_max = 0,
+       .ss_level_max = 0,
+       .div_pre_sel_max = 0, /* m6, m8, m8b */
+       .div_post_sel_max = 0, /* m6, m8, m8b */
+       .div_sel_max = 0, /* g9tv, g9bb, gxtvbb */
+       .xd_max = 0,
+       .pll_ref_fmax = 0,
+       .pll_ref_fmin = 0,
+       .pll_vco_fmax = 0,
+       .pll_vco_fmin = 0,
+       .pll_out_fmax = 0,
+       .pll_out_fmin = 0,
+       .div_post_out_fmax = 0, /* m6, m8, m8b */
+       .div_in_fmax = 0, /* g9tv, g9bb, gxtvbb */
+       .div_out_fmax = 0, /* g9tv, g9bb, gxtvbb */
+       .xd_out_fmax = 0,
+};
+
+struct lcd_clk_config_s *get_lcd_clk_config(void)
+{
+       return &clk_conf;
+}
+
+static void lcd_clk_config_init_print(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_AXG:
+               LCDPR("lcd clk config init:\n"
+                       "pll_m_max:         %d\n"
+                       "pll_m_min:         %d\n"
+                       "pll_n_max:         %d\n"
+                       "pll_n_min:         %d\n"
+                       "pll_frac_range:    %d\n"
+                       "pll_od_sel_max:    %d\n"
+                       "ss_level_max:      %d\n"
+                       "pll_ref_fmax:      %d\n"
+                       "pll_ref_fmin:      %d\n"
+                       "pll_vco_fmax:      %d\n"
+                       "pll_vco_fmin:      %d\n"
+                       "pll_out_fmax:      %d\n"
+                       "pll_out_fmin:      %d\n"
+                       "xd_out_fmax:       %d\n\n",
+                       clk_conf.pll_m_max, clk_conf.pll_m_min,
+                       clk_conf.pll_n_max, clk_conf.pll_n_min,
+                       clk_conf.pll_frac_range,
+                       clk_conf.pll_od_sel_max, clk_conf.ss_level_max,
+                       clk_conf.pll_ref_fmax, clk_conf.pll_ref_fmin,
+                       clk_conf.pll_vco_fmax, clk_conf.pll_vco_fmin,
+                       clk_conf.pll_out_fmax, clk_conf.pll_out_fmin,
+                        clk_conf.xd_out_fmax);
+               break;
+       default:
+               LCDPR("lcd clk config:\n"
+                       "pll_m_max:         %d\n"
+                       "pll_m_min:         %d\n"
+                       "pll_n_max:         %d\n"
+                       "pll_n_min:         %d\n"
+                       "pll_frac_range:    %d\n"
+                       "pll_od_sel_max:    %d\n"
+                       "ss_level_max:      %d\n"
+                       "pll_ref_fmax:      %d\n"
+                       "pll_ref_fmin:      %d\n"
+                       "pll_vco_fmax:      %d\n"
+                       "pll_vco_fmin:      %d\n"
+                       "pll_out_fmax:      %d\n"
+                       "pll_out_fmin:      %d\n"
+                       "div_in_fmax:       %d\n"
+                       "div_out_fmax:      %d\n"
+                       "xd_out_fmax:       %d\n\n",
+                       clk_conf.pll_m_max, clk_conf.pll_m_min,
+                       clk_conf.pll_n_max, clk_conf.pll_n_min,
+                       clk_conf.pll_frac_range,
+                       clk_conf.pll_od_sel_max, clk_conf.ss_level_max,
+                       clk_conf.pll_ref_fmax, clk_conf.pll_ref_fmin,
+                       clk_conf.pll_vco_fmax, clk_conf.pll_vco_fmin,
+                       clk_conf.pll_out_fmax, clk_conf.pll_out_fmin,
+                       clk_conf.div_in_fmax, clk_conf.div_out_fmax,
+                       clk_conf.xd_out_fmax);
+               break;
+       }
+}
+
+void lcd_clk_config_print(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_AXG:
+               LCDPR("lcd clk config:\n"
+                       "pll_m:        %d\n"
+                       "pll_n:        %d\n"
+                       "pll_frac:     0x%03x\n"
+                       "pll_fvco:     %dkHz\n"
+                       "pll_od:       %d\n"
+                       "pll_out:      %dkHz\n"
+                       "xd:           %d\n"
+                       "fout:         %dkHz\n"
+                       "ss_level:     %d\n\n",
+                       clk_conf.pll_m, clk_conf.pll_n,
+                       clk_conf.pll_frac, clk_conf.pll_fvco,
+                       clk_conf.pll_od1_sel, clk_conf.pll_fout,
+                       clk_conf.xd, clk_conf.fout, clk_conf.ss_level);
+               break;
+       default:
+               LCDPR("lcd clk config:\n"
+                       "pll_m:        %d\n"
+                       "pll_n:        %d\n"
+                       "pll_frac:     0x%03x\n"
+                       "pll_fvco:     %dkHz\n"
+                       "pll_od1:      %d\n"
+                       "pll_od2:      %d\n"
+                       "pll_od3:      %d\n"
+                       "pll_out:      %dkHz\n"
+                       "div_sel:      %s(index %d)\n"
+                       "xd:           %d\n"
+                       "fout:         %dkHz\n"
+                       "ss_level:     %d\n\n",
+                       clk_conf.pll_m, clk_conf.pll_n,
+                       clk_conf.pll_frac, clk_conf.pll_fvco,
+                       clk_conf.pll_od1_sel, clk_conf.pll_od2_sel,
+                       clk_conf.pll_od3_sel, clk_conf.pll_fout,
+                       lcd_clk_div_sel_table[clk_conf.div_sel],
+                       clk_conf.div_sel, clk_conf.xd,
+                       clk_conf.fout, clk_conf.ss_level);
+               break;
+       }
+}
+
+static void lcd_clk_config_chip_init(void)
+{
+       struct lcd_clk_config_s *cConf;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       cConf = get_lcd_clk_config();
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_GXTVBB:
+               cConf->od_fb = PLL_FRAC_OD_FB_GXTVBB;
+               cConf->ss_level_max = SS_LEVEL_MAX_GXTVBB;
+               cConf->pll_m_max = PLL_M_MAX_GXTVBB;
+               cConf->pll_m_min = PLL_M_MIN_GXTVBB;
+               cConf->pll_n_max = PLL_N_MAX_GXTVBB;
+               cConf->pll_n_min = PLL_N_MIN_GXTVBB;
+               cConf->pll_frac_range = PLL_FRAC_RANGE_GXTVBB;
+               cConf->pll_od_sel_max = PLL_OD_SEL_MAX_GXTVBB;
+               cConf->pll_ref_fmax = PLL_FREF_MAX_GXTVBB;
+               cConf->pll_ref_fmin = PLL_FREF_MIN_GXTVBB;
+               cConf->pll_vco_fmax = PLL_VCO_MAX_GXTVBB;
+               cConf->pll_vco_fmin = PLL_VCO_MIN_GXTVBB;
+               cConf->pll_out_fmax = CLK_DIV_IN_MAX_GXTVBB;
+               cConf->pll_out_fmin = cConf->pll_vco_fmin / 16;
+               cConf->div_in_fmax = CLK_DIV_IN_MAX_GXTVBB;
+               cConf->div_out_fmax = CRT_VID_CLK_IN_MAX_GXTVBB;
+               cConf->xd_out_fmax = ENCL_CLK_IN_MAX_GXTVBB;
+               break;
+       case LCD_CHIP_GXL:
+               cConf->od_fb = PLL_FRAC_OD_FB_GXL;
+               cConf->ss_level_max = SS_LEVEL_MAX_GXL;
+               cConf->pll_m_max = PLL_M_MAX_GXL;
+               cConf->pll_m_min = PLL_M_MIN_GXL;
+               cConf->pll_n_max = PLL_N_MAX_GXL;
+               cConf->pll_n_min = PLL_N_MIN_GXL;
+               cConf->pll_frac_range = PLL_FRAC_RANGE_GXL;
+               cConf->pll_od_sel_max = PLL_OD_SEL_MAX_GXL;
+               cConf->pll_ref_fmax = PLL_FREF_MAX_GXL;
+               cConf->pll_ref_fmin = PLL_FREF_MIN_GXL;
+               cConf->pll_vco_fmax = PLL_VCO_MAX_GXL;
+               cConf->pll_vco_fmin = PLL_VCO_MIN_GXL;
+               cConf->pll_out_fmax = CLK_DIV_IN_MAX_GXL;
+               cConf->pll_out_fmin = cConf->pll_vco_fmin / 16;
+               cConf->div_in_fmax = CLK_DIV_IN_MAX_GXL;
+               cConf->div_out_fmax = CRT_VID_CLK_IN_MAX_GXL;
+               cConf->xd_out_fmax = ENCL_CLK_IN_MAX_GXL;
+               break;
+       case LCD_CHIP_GXM:
+               cConf->od_fb = PLL_FRAC_OD_FB_GXM;
+               cConf->ss_level_max = SS_LEVEL_MAX_GXM;
+               cConf->pll_m_max = PLL_M_MAX_GXM;
+               cConf->pll_m_min = PLL_M_MIN_GXM;
+               cConf->pll_n_max = PLL_N_MAX_GXM;
+               cConf->pll_n_min = PLL_N_MIN_GXM;
+               cConf->pll_frac_range = PLL_FRAC_RANGE_GXM;
+               cConf->pll_od_sel_max = PLL_OD_SEL_MAX_GXM;
+               cConf->pll_ref_fmax = PLL_FREF_MAX_GXM;
+               cConf->pll_ref_fmin = PLL_FREF_MIN_GXM;
+               cConf->pll_vco_fmax = PLL_VCO_MAX_GXM;
+               cConf->pll_vco_fmin = PLL_VCO_MIN_GXM;
+               cConf->pll_out_fmax = CLK_DIV_IN_MAX_GXM;
+               cConf->pll_out_fmin = cConf->pll_vco_fmin / 16;
+               cConf->div_in_fmax = CLK_DIV_IN_MAX_GXM;
+               cConf->div_out_fmax = CRT_VID_CLK_IN_MAX_GXM;
+               cConf->xd_out_fmax = ENCL_CLK_IN_MAX_GXM;
+               break;
+       case LCD_CHIP_TXL:
+               cConf->od_fb = PLL_FRAC_OD_FB_TXL;
+               cConf->ss_level_max = SS_LEVEL_MAX_TXL;
+               cConf->pll_m_max = PLL_M_MAX_TXL;
+               cConf->pll_m_min = PLL_M_MIN_TXL;
+               cConf->pll_n_max = PLL_N_MAX_TXL;
+               cConf->pll_n_min = PLL_N_MIN_TXL;
+               cConf->pll_frac_range = PLL_FRAC_RANGE_TXL;
+               cConf->pll_od_sel_max = PLL_OD_SEL_MAX_TXL;
+               cConf->pll_ref_fmax = PLL_FREF_MAX_TXL;
+               cConf->pll_ref_fmin = PLL_FREF_MIN_TXL;
+               cConf->pll_vco_fmax = PLL_VCO_MAX_TXL;
+               cConf->pll_vco_fmin = PLL_VCO_MIN_TXL;
+               cConf->pll_out_fmax = CLK_DIV_IN_MAX_TXL;
+               cConf->pll_out_fmin = cConf->pll_vco_fmin / 16;
+               cConf->div_in_fmax = CLK_DIV_IN_MAX_TXL;
+               cConf->div_out_fmax = CRT_VID_CLK_IN_MAX_TXL;
+               cConf->xd_out_fmax = ENCL_CLK_IN_MAX_TXL;
+               break;
+       case LCD_CHIP_TXLX:
+               cConf->od_fb = PLL_FRAC_OD_FB_TXLX;
+               cConf->ss_level_max = SS_LEVEL_MAX_TXLX;
+               cConf->pll_m_max = PLL_M_MAX_TXLX;
+               cConf->pll_m_min = PLL_M_MIN_TXLX;
+               cConf->pll_n_max = PLL_N_MAX_TXLX;
+               cConf->pll_n_min = PLL_N_MIN_TXLX;
+               cConf->pll_frac_range = PLL_FRAC_RANGE_TXLX;
+               cConf->pll_od_sel_max = PLL_OD_SEL_MAX_TXLX;
+               cConf->pll_ref_fmax = PLL_FREF_MAX_TXLX;
+               cConf->pll_ref_fmin = PLL_FREF_MIN_TXLX;
+               cConf->pll_vco_fmax = PLL_VCO_MAX_TXLX;
+               cConf->pll_vco_fmin = PLL_VCO_MIN_TXLX;
+               cConf->pll_out_fmax = CLK_DIV_IN_MAX_TXLX;
+               cConf->pll_out_fmin = cConf->pll_vco_fmin / 16;
+               cConf->div_in_fmax = CLK_DIV_IN_MAX_TXLX;
+               cConf->div_out_fmax = CRT_VID_CLK_IN_MAX_TXLX;
+               cConf->xd_out_fmax = ENCL_CLK_IN_MAX_TXLX;
+               break;
+       case LCD_CHIP_AXG:
+               cConf->od_fb = PLL_FRAC_OD_FB_AXG;
+               cConf->ss_level_max = SS_LEVEL_MAX_AXG;
+               cConf->pll_m_max = PLL_M_MAX_AXG;
+               cConf->pll_m_min = PLL_M_MIN_AXG;
+               cConf->pll_n_max = PLL_N_MAX_AXG;
+               cConf->pll_n_min = PLL_N_MIN_AXG;
+               cConf->pll_frac_range = PLL_FRAC_RANGE_AXG;
+               cConf->pll_od_sel_max = PLL_OD_SEL_MAX_AXG;
+               cConf->pll_ref_fmax = PLL_FREF_MAX_AXG;
+               cConf->pll_ref_fmin = PLL_FREF_MIN_AXG;
+               cConf->pll_vco_fmax = PLL_VCO_MAX_AXG;
+               cConf->pll_vco_fmin = PLL_VCO_MIN_AXG;
+               cConf->pll_out_fmax = CRT_VID_CLK_IN_MAX_AXG;
+               cConf->pll_out_fmin = cConf->pll_vco_fmin /
+                       od_table[cConf->pll_od_sel_max - 1];
+               cConf->div_post_out_fmax = CRT_VID_CLK_IN_MAX_AXG;
+               cConf->xd_out_fmax = ENCL_CLK_IN_MAX_AXG;
+               break;
+       default:
+               LCDPR("%s invalid chip type\n", __func__);
+               break;
+       }
+       if (lcd_debug_print_flag > 0)
+               lcd_clk_config_init_print();
+}
+
+/* lcd controller operation */
+static int lcd_pll_wait_lock(unsigned int reg, unsigned int lock_bit)
+{
+       unsigned int pll_lock;
+       int wait_loop = PLL_WAIT_LOCK_CNT; /* 200 */
+       int ret = 0;
+
+       do {
+               udelay(50);
+               pll_lock = lcd_hiu_getb(reg, lock_bit, 1);
+               wait_loop--;
+       } while ((pll_lock == 0) && (wait_loop > 0));
+       if (pll_lock == 0)
+               ret = -1;
+       LCDPR("%s: pll_lock=%d, wait_loop=%d\n",
+               __func__, pll_lock, (PLL_WAIT_LOCK_CNT - wait_loop));
+       return ret;
+}
+
+static void lcd_set_pll_ss_gxtvbb(struct lcd_clk_config_s *cConf)
+{
+       if ((cConf->pll_fvco >= 5500000) && (cConf->pll_fvco <= 6000000)) {
+               switch (cConf->ss_level) {
+               case 1: /* +/- 0.3% */
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x12dc5080);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xb01da72c);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+                       break;
+               case 2: /* +/- 0.5% */
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x12dc5080);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xa85da72c);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+                       break;
+               case 3: /* +/- 0.9% */
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x12dc5080);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xb09da72c);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+                       break;
+               case 4: /* +/- 1.2% */
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x12dc5080);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xb0dda72c);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+                       break;
+               default: /* disable */
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x12dc5081);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0x801da72c);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x71486980);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00002a55);
+                       break;
+               }
+       } else {
+               switch (cConf->ss_level) {
+               case 1: /* +/- 0.3% */
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x0d1c5090);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xb01da72c);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+                       break;
+               case 2: /* +/- 0.5% */
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x0d1c5090);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xa85da72c);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+                       break;
+               case 3: /* +/- 0.9% */
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x0d1c5090);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xb09da72c);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+                       break;
+               case 4: /* +/- 1.2% */
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x0d1c5090);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xb0dda72c);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+                       break;
+               default: /* disable */
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0x801da72c);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x71486980);
+                       lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00002a55);
+                       break;
+               }
+       }
+       LCDPR("set pll spread spectrum: %s\n",
+               lcd_pll_ss_table_gxtvbb[cConf->ss_level]);
+}
+
+static void lcd_pll_reset_gxtvbb(void)
+{
+       lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 1, LCD_PLL_RST_GXTVBB, 1);
+       udelay(10);
+       lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 0, LCD_PLL_RST_GXTVBB, 1);
+}
+
+static void lcd_set_pll_gxtvbb(struct lcd_clk_config_s *cConf)
+{
+       unsigned int pll_ctrl, pll_ctrl2;
+       int ret;
+
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s\n", __func__);
+       pll_ctrl = ((1 << LCD_PLL_EN_GXTVBB) |
+               (1 << 27) | /* DPLL_BGP_EN */
+               (cConf->pll_n << LCD_PLL_N_GXTVBB) |
+               (cConf->pll_m << LCD_PLL_M_GXTVBB));
+
+       pll_ctrl2 = ((cConf->pll_od3_sel << LCD_PLL_OD3_GXTVBB) |
+               (cConf->pll_od2_sel << LCD_PLL_OD2_GXTVBB) |
+               (cConf->pll_od1_sel << LCD_PLL_OD1_GXTVBB));
+       pll_ctrl2 |= ((1 << 14) | (cConf->pll_frac << 0));
+
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL, pll_ctrl | (1 << LCD_PLL_RST_GXTVBB));
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL2, pll_ctrl2);
+       if ((cConf->pll_fvco >= 5500000) && (cConf->pll_fvco <= 6000000)) {
+               lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x12dc5081);
+               lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0x801da72c);
+               lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x71486980);
+               lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00002a55);
+       } else {
+               lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
+               lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0x801da72c);
+               lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x71486980);
+               lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00002a55);
+       }
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL, pll_ctrl);
+
+       ret = lcd_pll_wait_lock(HHI_HDMI_PLL_CNTL, LCD_PLL_LOCK_GXTVBB);
+       if (ret)
+               LCDERR("hpll lock failed\n");
+
+       if (cConf->ss_level > 0)
+               lcd_set_pll_ss_gxtvbb(cConf);
+}
+
+static void lcd_update_pll_frac_gxtvbb(struct lcd_clk_config_s *cConf)
+{
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s\n", __func__);
+
+       lcd_hiu_setb(HHI_HDMI_PLL_CNTL2, cConf->pll_frac, 0, 12);
+}
+
+static void lcd_set_pll_ss_txl(struct lcd_clk_config_s *cConf)
+{
+       unsigned int pll_ctrl3, pll_ctrl4;
+
+       pll_ctrl3 = lcd_hiu_read(HHI_HDMI_PLL_CNTL3);
+       pll_ctrl4 = lcd_hiu_read(HHI_HDMI_PLL_CNTL4);
+
+       switch (cConf->ss_level) {
+       case 1: /* +/- 0.3% */
+               pll_ctrl3 &= ~(0xf << 10);
+               pll_ctrl3 |= ((1 << 14) | (0xc << 10));
+               pll_ctrl4 &= ~(0x3 << 2);
+               break;
+       case 2: /* +/- 0.4% */
+               pll_ctrl3 &= ~(0xf << 10);
+               pll_ctrl3 |= ((1 << 14) | (0x8 << 10));
+               pll_ctrl4 &= ~(0x3 << 2);
+               pll_ctrl4 |= (0x1 << 2);
+               break;
+       case 3: /* +/- 0.9% */
+               pll_ctrl3 &= ~(0xf << 10);
+               pll_ctrl3 |= ((1 << 14) | (0xc << 10));
+               pll_ctrl4 &= ~(0x3 << 2);
+               pll_ctrl4 |= (0x2 << 2);
+               break;
+       case 4: /* +/- 1.2% */
+               pll_ctrl3 &= ~(0xf << 10);
+               pll_ctrl3 |= ((1 << 14) | (0xc << 10));
+               pll_ctrl4 &= ~(0x3 << 2);
+               pll_ctrl4 |= (0x3 << 2);
+               break;
+       default: /* disable */
+               pll_ctrl3 &= ~((0xf << 10) | (1 << 14));
+               pll_ctrl4 &= ~(0x3 << 2);
+               break;
+       }
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL3, pll_ctrl3);
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL4, pll_ctrl4);
+
+       LCDPR("set pll spread spectrum: %s\n",
+               lcd_pll_ss_table_txl[cConf->ss_level]);
+}
+
+static void lcd_pll_reset_txl(void)
+{
+       lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 1, LCD_PLL_RST_TXL, 1);
+       udelay(10);
+       lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 0, LCD_PLL_RST_TXL, 1);
+}
+
+static void lcd_set_pll_txl(struct lcd_clk_config_s *cConf)
+{
+       unsigned int pll_ctrl, pll_ctrl2, pll_ctrl3;
+       int ret;
+
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s\n", __func__);
+       pll_ctrl = ((1 << LCD_PLL_EN_TXL) |
+               (cConf->pll_n << LCD_PLL_N_TXL) |
+               (cConf->pll_m << LCD_PLL_M_TXL));
+       pll_ctrl2 = 0x800ca000;
+       pll_ctrl2 |= ((1 << 12) | (cConf->pll_frac << 0));
+       pll_ctrl3 = 0x860330c4 | (cConf->od_fb << 30);
+       pll_ctrl3 |= ((cConf->pll_od3_sel << LCD_PLL_OD3_TXL) |
+               (cConf->pll_od2_sel << LCD_PLL_OD2_TXL) |
+               (cConf->pll_od1_sel << LCD_PLL_OD1_TXL));
+
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL, pll_ctrl);
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL2, pll_ctrl2);
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL3, pll_ctrl3);
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x001fa729);
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x01a31500);
+       lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 1, LCD_PLL_RST_TXL, 1);
+       lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 0, LCD_PLL_RST_TXL, 1);
+
+       ret = lcd_pll_wait_lock(HHI_HDMI_PLL_CNTL, LCD_PLL_LOCK_TXL);
+       if (ret)
+               LCDERR("hpll lock failed\n");
+
+       if (cConf->ss_level > 0)
+               lcd_set_pll_ss_txl(cConf);
+}
+
+static void lcd_update_pll_frac_txl(struct lcd_clk_config_s *cConf)
+{
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s\n", __func__);
+
+       lcd_hiu_setb(HHI_HDMI_PLL_CNTL2, cConf->pll_frac, 0, 12);
+}
+
+static void lcd_set_pll_ss_txlx(struct lcd_clk_config_s *cConf)
+{
+       unsigned int pll_ctrl3, pll_ctrl4, pll_ctrl5;
+
+       pll_ctrl3 = lcd_hiu_read(HHI_HDMI_PLL_CNTL3);
+       pll_ctrl4 = lcd_hiu_read(HHI_HDMI_PLL_CNTL4);
+       pll_ctrl5 = lcd_hiu_read(HHI_HDMI_PLL_CNTL5);
+
+       switch (cConf->ss_level) {
+       case 1: /* +/- 0.3% */
+               pll_ctrl3 &= ~(0xf << 10);
+               pll_ctrl3 |= ((1 << 14) | (0x6 << 10));
+               pll_ctrl4 &= ~(0x3 << 2);
+               pll_ctrl4 |= (0x1 << 2);
+               pll_ctrl5 &= ~(0x3 << 30);
+               break;
+       case 2: /* +/- 0.5% */
+               pll_ctrl3 &= ~(0xf << 10);
+               pll_ctrl3 |= ((1 << 14) | (0xa << 10));
+               pll_ctrl4 &= ~(0x3 << 2);
+               pll_ctrl4 |= (0x1 << 2);
+               pll_ctrl5 &= ~(0x3 << 30);
+               break;
+       case 3: /* +/- 1.0% */
+               pll_ctrl3 &= ~(0xf << 10);
+               pll_ctrl3 |= ((1 << 14) | (0xa << 10));
+               pll_ctrl4 &= ~(0x3 << 2);
+               pll_ctrl4 |= (0x3 << 2);
+               pll_ctrl5 &= ~(0x3 << 30);
+               break;
+       case 4: /* +/- 1.6% */
+               pll_ctrl3 &= ~(0xf << 10);
+               pll_ctrl3 |= ((1 << 14) | (0x8 << 10));
+               pll_ctrl4 &= ~(0x3 << 2);
+               pll_ctrl4 |= (0x3 << 2);
+               pll_ctrl5 &= ~(0x3 << 30);
+               pll_ctrl5 |= (0x1 << 30);
+               break;
+       case 5: /* +/- 3.0% */
+               pll_ctrl3 &= ~(0xf << 10);
+               pll_ctrl3 |= ((1 << 14) | (0xa << 10));
+               pll_ctrl4 &= ~(0x3 << 2);
+               pll_ctrl4 |= (0x3 << 2);
+               pll_ctrl5 &= ~(0x3 << 30);
+               pll_ctrl5 |= (0x2 << 30);
+               break;
+       default: /* disable */
+               pll_ctrl3 &= ~((0xf << 10) | (1 << 14));
+               pll_ctrl4 &= ~(0x3 << 2);
+               pll_ctrl5 &= ~(0x3 << 30);
+               break;
+       }
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL3, pll_ctrl3);
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL4, pll_ctrl4);
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL5, pll_ctrl5);
+
+       LCDPR("set pll spread spectrum: %s\n",
+               lcd_pll_ss_table_txlx[cConf->ss_level]);
+}
+
+static void lcd_set_pll_txlx(struct lcd_clk_config_s *cConf)
+{
+       unsigned int pll_ctrl, pll_ctrl2, pll_ctrl3;
+       int ret;
+
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s\n", __func__);
+       pll_ctrl = ((1 << LCD_PLL_EN_TXL) |
+               (cConf->pll_n << LCD_PLL_N_TXL) |
+               (cConf->pll_m << LCD_PLL_M_TXL));
+       pll_ctrl2 = 0x800ca000;
+       pll_ctrl2 |= ((1 << 12) | (cConf->pll_frac << 0));
+       pll_ctrl3 = 0x860030c4 | (cConf->od_fb << 30);
+       pll_ctrl3 |= ((cConf->pll_od3_sel << LCD_PLL_OD3_TXL) |
+               (cConf->pll_od2_sel << LCD_PLL_OD2_TXL) |
+               (cConf->pll_od1_sel << LCD_PLL_OD1_TXL));
+
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL, pll_ctrl);
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL2, pll_ctrl2);
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL3, pll_ctrl3);
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x001fa729);
+       lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x01a31500);
+       lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 1, LCD_PLL_RST_TXL, 1);
+       lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 0, LCD_PLL_RST_TXL, 1);
+
+       ret = lcd_pll_wait_lock(HHI_HDMI_PLL_CNTL, LCD_PLL_LOCK_TXL);
+       if (ret)
+               LCDERR("hpll lock failed\n");
+
+       if (cConf->ss_level > 0)
+               lcd_set_pll_ss_txlx(cConf);
+}
+
+static unsigned int lcd_clk_div_g9_gxtvbb[][3] = {
+       /* divider,        shift_val,  shift_sel */
+       {CLK_DIV_SEL_1,    0xffff,     0,},
+       {CLK_DIV_SEL_2,    0x0aaa,     0,},
+       {CLK_DIV_SEL_3,    0x0db6,     0,},
+       {CLK_DIV_SEL_3p5,  0x36cc,     1,},
+       {CLK_DIV_SEL_3p75, 0x6666,     2,},
+       {CLK_DIV_SEL_4,    0x0ccc,     0,},
+       {CLK_DIV_SEL_5,    0x739c,     2,},
+       {CLK_DIV_SEL_6,    0x0e38,     0,},
+       {CLK_DIV_SEL_6p25, 0x0000,     3,},
+       {CLK_DIV_SEL_7,    0x3c78,     1,},
+       {CLK_DIV_SEL_7p5,  0x78f0,     2,},
+       {CLK_DIV_SEL_12,   0x0fc0,     0,},
+       {CLK_DIV_SEL_14,   0x3f80,     1,},
+       {CLK_DIV_SEL_15,   0x7f80,     2,},
+       {CLK_DIV_SEL_2p5,  0x5294,     2,},
+       {CLK_DIV_SEL_MAX,  0xffff,     0,},
+};
+
+static void lcd_set_clk_div_g9_gxtvbb(struct lcd_clk_config_s *cConf)
+{
+       unsigned int shift_val, shift_sel;
+       int i;
+
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s\n", __func__);
+
+       lcd_hiu_setb(HHI_VIID_CLK_CNTL, 0, VCLK2_EN, 1);
+       udelay(5);
+
+       /* Disable the div output clock */
+       lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 0, 19, 1);
+       lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 0, 15, 1);
+
+       i = 0;
+       while (lcd_clk_div_g9_gxtvbb[i][0] != CLK_DIV_SEL_MAX) {
+               if (cConf->div_sel == lcd_clk_div_g9_gxtvbb[i][0])
+                       break;
+               i++;
+       }
+       if (lcd_clk_div_g9_gxtvbb[i][0] == CLK_DIV_SEL_MAX)
+               LCDERR("invalid clk divider\n");
+       shift_val = lcd_clk_div_g9_gxtvbb[i][1];
+       shift_sel = lcd_clk_div_g9_gxtvbb[i][2];
+
+       if (shift_val == 0xffff) { /* if divide by 1 */
+               lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 1, 18, 1);
+       } else {
+               lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 0, 18, 1);
+               lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 0, 16, 2);
+               lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 0, 15, 1);
+               lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 0, 0, 14);
+
+               lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, shift_sel, 16, 2);
+               lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 1, 15, 1);
+               lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, shift_val, 0, 14);
+               lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 0, 15, 1);
+       }
+       /* Enable the final output clock */
+       lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 1, 19, 1);
+}
+
+static void lcd_set_vclk_crt(int lcd_type, struct lcd_clk_config_s *cConf)
+{
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s\n", __func__);
+
+       /* setup the XD divider value */
+       lcd_hiu_setb(HHI_VIID_CLK_DIV, (cConf->xd-1), VCLK2_XD, 8);
+       udelay(5);
+
+       /* select vid_pll_clk */
+       lcd_hiu_setb(HHI_VIID_CLK_CNTL, 0, VCLK2_CLK_IN_SEL, 3);
+       lcd_hiu_setb(HHI_VIID_CLK_CNTL, 1, VCLK2_EN, 1);
+       udelay(2);
+
+       /* [15:12] encl_clk_sel, select vclk2_div1 */
+       lcd_hiu_setb(HHI_VIID_CLK_DIV, 8, ENCL_CLK_SEL, 4);
+       /* release vclk2_div_reset and enable vclk2_div */
+       lcd_hiu_setb(HHI_VIID_CLK_DIV, 1, VCLK2_XD_EN, 2);
+       udelay(5);
+
+       lcd_hiu_setb(HHI_VIID_CLK_CNTL, 1, VCLK2_DIV1_EN, 1);
+       lcd_hiu_setb(HHI_VIID_CLK_CNTL, 1, VCLK2_SOFT_RST, 1);
+       udelay(10);
+       lcd_hiu_setb(HHI_VIID_CLK_CNTL, 0, VCLK2_SOFT_RST, 1);
+       udelay(5);
+
+       /* enable CTS_ENCL clk gate */
+       lcd_hiu_setb(HHI_VID_CLK_CNTL2, 1, ENCL_GATE_VCLK, 1);
+}
+
+static unsigned int clk_div_calc_g9_gxtvbb(unsigned int clk,
+               unsigned int div_sel, int dir)
+{
+       unsigned int clk_ret;
+
+       switch (div_sel) {
+       case CLK_DIV_SEL_1:
+               clk_ret = clk;
+               break;
+       case CLK_DIV_SEL_2:
+               if (dir == CLK_DIV_I2O)
+                       clk_ret = clk / 2;
+               else
+                       clk_ret = clk * 2;
+               break;
+       case CLK_DIV_SEL_3:
+               if (dir == CLK_DIV_I2O)
+                       clk_ret = clk / 3;
+               else
+                       clk_ret = clk * 3;
+               break;
+       case CLK_DIV_SEL_3p5:
+               if (dir == CLK_DIV_I2O)
+                       clk_ret = clk * 2 / 7;
+               else
+                       clk_ret = clk * 7 / 2;
+               break;
+       case CLK_DIV_SEL_3p75:
+               if (dir == CLK_DIV_I2O)
+                       clk_ret = clk * 4 / 15;
+               else
+                       clk_ret = clk * 15 / 4;
+               break;
+       case CLK_DIV_SEL_4:
+               if (dir == CLK_DIV_I2O)
+                       clk_ret = clk / 4;
+               else
+                       clk_ret = clk * 4;
+               break;
+       case CLK_DIV_SEL_5:
+               if (dir == CLK_DIV_I2O)
+                       clk_ret = clk / 5;
+               else
+                       clk_ret = clk * 5;
+               break;
+       case CLK_DIV_SEL_6:
+               if (dir == CLK_DIV_I2O)
+                       clk_ret = clk / 6;
+               else
+                       clk_ret = clk * 6;
+               break;
+       case CLK_DIV_SEL_6p25:
+               if (dir == CLK_DIV_I2O)
+                       clk_ret = clk * 4 / 25;
+               else
+                       clk_ret = clk * 25 / 4;
+               break;
+       case CLK_DIV_SEL_7:
+               if (dir == CLK_DIV_I2O)
+                       clk_ret = clk / 7;
+               else
+                       clk_ret = clk * 7;
+               break;
+       case CLK_DIV_SEL_7p5:
+               if (dir == CLK_DIV_I2O)
+                       clk_ret = clk * 2 / 15;
+               else
+                       clk_ret = clk * 15 / 2;
+               break;
+       case CLK_DIV_SEL_12:
+               if (dir == CLK_DIV_I2O)
+                       clk_ret = clk / 12;
+               else
+                       clk_ret = clk * 12;
+               break;
+       case CLK_DIV_SEL_14:
+               if (dir == CLK_DIV_I2O)
+                       clk_ret = clk / 14;
+               else
+                       clk_ret = clk * 14;
+               break;
+       case CLK_DIV_SEL_15:
+               if (dir == CLK_DIV_I2O)
+                       clk_ret = clk / 15;
+               else
+                       clk_ret = clk * 15;
+               break;
+       case CLK_DIV_SEL_2p5:
+               if (dir == CLK_DIV_I2O)
+                       clk_ret = clk * 2 / 5;
+               else
+                       clk_ret = clk * 5 / 2;
+               break;
+       default:
+               clk_ret = clk;
+               LCDERR("clk_div_sel: Invalid parameter\n");
+               break;
+       }
+
+       return clk_ret;
+}
+
+static unsigned int clk_div_get_g9_gxtvbb(unsigned int clk_div)
+{
+       unsigned int div_sel;
+
+       /* div * 100 */
+       switch (clk_div) {
+       case 375:
+               div_sel = CLK_DIV_SEL_3p75;
+               break;
+       case 750:
+               div_sel = CLK_DIV_SEL_7p5;
+               break;
+       case 1500:
+               div_sel = CLK_DIV_SEL_15;
+               break;
+       case 500:
+               div_sel = CLK_DIV_SEL_5;
+               break;
+       default:
+               div_sel = CLK_DIV_SEL_MAX;
+               break;
+       }
+       return div_sel;
+}
+
+static int check_pll_g9_gxtvbb(struct lcd_clk_config_s *cConf,
+               unsigned int pll_fout)
+{
+       unsigned int m, n;
+       unsigned int od1_sel, od2_sel, od3_sel, od1, od2, od3;
+       unsigned int pll_fod2_in, pll_fod3_in, pll_fvco;
+       unsigned int od_fb = 0, pll_frac;
+       int done;
+
+       done = 0;
+       if ((pll_fout > cConf->pll_out_fmax) ||
+               (pll_fout < cConf->pll_out_fmin)) {
+               return done;
+       }
+       for (od3_sel = cConf->pll_od_sel_max; od3_sel > 0; od3_sel--) {
+               od3 = od_table[od3_sel - 1];
+               pll_fod3_in = pll_fout * od3;
+               for (od2_sel = od3_sel; od2_sel > 0; od2_sel--) {
+                       od2 = od_table[od2_sel - 1];
+                       pll_fod2_in = pll_fod3_in * od2;
+                       for (od1_sel = od2_sel; od1_sel > 0; od1_sel--) {
+                               od1 = od_table[od1_sel - 1];
+                               pll_fvco = pll_fod2_in * od1;
+                               if ((pll_fvco < cConf->pll_vco_fmin) ||
+                                       (pll_fvco > cConf->pll_vco_fmax)) {
+                                       continue;
+                               }
+                               cConf->pll_od1_sel = od1_sel - 1;
+                               cConf->pll_od2_sel = od2_sel - 1;
+                               cConf->pll_od3_sel = od3_sel - 1;
+                               cConf->pll_fout = pll_fout;
+                               if (lcd_debug_print_flag == 2) {
+                                       LCDPR("od1=%d, od2=%d, od3=%d\n",
+                                               (od1_sel - 1), (od2_sel - 1),
+                                               (od3_sel - 1));
+                                       LCDPR("pll_fvco=%d\n", pll_fvco);
+                               }
+                               cConf->pll_fvco = pll_fvco;
+                               n = 1;
+                               od_fb = cConf->od_fb; /* pll default */
+                               pll_fvco = pll_fvco / od_fb_table[od_fb + 1];
+                               m = pll_fvco / cConf->fin;
+                               pll_frac = (pll_fvco % cConf->fin) *
+                                       cConf->pll_frac_range / cConf->fin;
+                               cConf->pll_m = m;
+                               cConf->pll_n = n;
+                               cConf->pll_frac = pll_frac;
+                               if (lcd_debug_print_flag == 2) {
+                                       LCDPR("m=%d, n=%d, frac=%d\n",
+                                               m, n, pll_frac);
+                               }
+                               done = 1;
+                               break;
+                       }
+               }
+       }
+       return done;
+}
+
+static void lcd_clk_generate_g9_gxtvbb(struct lcd_config_s *pconf)
+{
+       unsigned int pll_fout;
+       unsigned int clk_div_in, clk_div_out;
+       unsigned int clk_div_sel, xd;
+       struct lcd_clk_config_s *cConf;
+       int done;
+
+       done = 0;
+       cConf = get_lcd_clk_config();
+       cConf->fout = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+       cConf->err_fmin = MAX_ERROR;
+
+       if (cConf->fout > cConf->xd_out_fmax) {
+               LCDERR("%s: wrong lcd_clk value %dkHz\n",
+                       __func__, cConf->fout);
+               goto generate_clk_done_g9_gxtvbb;
+       }
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_TTL:
+               clk_div_sel = CLK_DIV_SEL_1;
+               cConf->xd_max = CRT_VID_DIV_MAX;
+               for (xd = 1; xd <= cConf->xd_max; xd++) {
+                       clk_div_out = cConf->fout * xd;
+                       if (clk_div_out > cConf->div_out_fmax)
+                               continue;
+                       if (lcd_debug_print_flag == 2) {
+                               LCDPR("fout=%d, xd=%d, clk_div_out=%d\n",
+                                       cConf->fout, xd, clk_div_out);
+                       }
+                       clk_div_in = clk_div_calc_g9_gxtvbb(clk_div_out,
+                                       clk_div_sel, CLK_DIV_O2I);
+                       if (clk_div_in > cConf->div_in_fmax)
+                               continue;
+                       cConf->xd = xd;
+                       cConf->div_sel = clk_div_sel;
+                       pll_fout = clk_div_in;
+                       if (lcd_debug_print_flag == 2) {
+                               LCDPR("clk_div_sel=%s(index %d), pll_fout=%d\n",
+                                       lcd_clk_div_sel_table[clk_div_sel],
+                                       clk_div_sel, pll_fout);
+                       }
+                       done = check_pll_g9_gxtvbb(cConf, pll_fout);
+                       if (done)
+                               goto generate_clk_done_g9_gxtvbb;
+               }
+               break;
+       case LCD_LVDS:
+               clk_div_sel = CLK_DIV_SEL_7;
+               xd = 1;
+               clk_div_out = cConf->fout * xd;
+               if (clk_div_out > cConf->div_out_fmax)
+                       goto generate_clk_done_g9_gxtvbb;
+               if (lcd_debug_print_flag == 2) {
+                       LCDPR("fout=%d, xd=%d, clk_div_out=%d\n",
+                               cConf->fout, xd, clk_div_out);
+               }
+               clk_div_in = clk_div_calc_g9_gxtvbb(clk_div_out,
+                               clk_div_sel, CLK_DIV_O2I);
+               if (clk_div_in > cConf->div_in_fmax)
+                       goto generate_clk_done_g9_gxtvbb;
+               cConf->xd = xd;
+               cConf->div_sel = clk_div_sel;
+               pll_fout = clk_div_in;
+               if (lcd_debug_print_flag == 2) {
+                       LCDPR("clk_div_sel=%s(index %d), pll_fout=%d\n",
+                               lcd_clk_div_sel_table[clk_div_sel],
+                               clk_div_sel, pll_fout);
+               }
+               done = check_pll_g9_gxtvbb(cConf, pll_fout);
+               if (done)
+                       goto generate_clk_done_g9_gxtvbb;
+               break;
+       case LCD_VBYONE:
+               cConf->div_sel_max = CLK_DIV_SEL_MAX;
+               cConf->xd_max = CRT_VID_DIV_MAX;
+               pll_fout = pconf->lcd_control.vbyone_config->bit_rate / 1000;
+               clk_div_in = pll_fout;
+               if (clk_div_in > cConf->div_in_fmax)
+                       goto generate_clk_done_g9_gxtvbb;
+               if (lcd_debug_print_flag == 2)
+                       LCDPR("pll_fout=%d\n", pll_fout);
+               if ((clk_div_in / cConf->fout) > 15)
+                       cConf->xd = 4;
+               else
+                       cConf->xd = 1;
+               clk_div_out = cConf->fout * cConf->xd;
+               if (lcd_debug_print_flag == 2) {
+                       LCDPR("clk_div_in=%d, fout=%d, xd=%d, clk_div_out=%d\n",
+                               clk_div_in, cConf->fout,
+                               clk_div_out, cConf->xd);
+               }
+               if (clk_div_out > cConf->div_out_fmax)
+                       goto generate_clk_done_g9_gxtvbb;
+               clk_div_sel = clk_div_get_g9_gxtvbb(
+                               clk_div_in * 100 / clk_div_out);
+               cConf->div_sel = clk_div_sel;
+               if (lcd_debug_print_flag == 2) {
+                       LCDPR("clk_div_sel=%s(index %d)\n",
+                               lcd_clk_div_sel_table[clk_div_sel],
+                               cConf->div_sel);
+               }
+               done = check_pll_g9_gxtvbb(cConf, pll_fout);
+               break;
+       default:
+               break;
+       }
+
+generate_clk_done_g9_gxtvbb:
+       if (done) {
+               pconf->lcd_timing.pll_ctrl =
+                       (cConf->pll_od1_sel << PLL_CTRL_OD1) |
+                       (cConf->pll_od2_sel << PLL_CTRL_OD2) |
+                       (cConf->pll_od3_sel << PLL_CTRL_OD3) |
+                       (cConf->pll_n << PLL_CTRL_N)         |
+                       (cConf->pll_m << PLL_CTRL_M);
+               pconf->lcd_timing.div_ctrl =
+                       (cConf->div_sel << DIV_CTRL_DIV_SEL) |
+                       (cConf->xd << DIV_CTRL_XD);
+               pconf->lcd_timing.clk_ctrl =
+                       (cConf->pll_frac << CLK_CTRL_FRAC);
+       } else {
+               pconf->lcd_timing.pll_ctrl =
+                       (1 << PLL_CTRL_OD1) |
+                       (1 << PLL_CTRL_OD2) |
+                       (1 << PLL_CTRL_OD3) |
+                       (1 << PLL_CTRL_N)   |
+                       (50 << PLL_CTRL_M);
+               pconf->lcd_timing.div_ctrl =
+                       (CLK_DIV_SEL_1 << DIV_CTRL_DIV_SEL) |
+                       (7 << DIV_CTRL_XD);
+               pconf->lcd_timing.clk_ctrl = (0 << CLK_CTRL_FRAC);
+               LCDERR("Out of clock range, reset to default setting\n");
+       }
+}
+
+static void lcd_pll_frac_generate_g9_gxtvbb(struct lcd_config_s *pconf)
+{
+       unsigned int pll_fout;
+       unsigned int clk_div_in, clk_div_out, clk_div_sel;
+       unsigned int od1, od2, od3, pll_fvco;
+       unsigned int m, n, od_fb, frac, offset, temp;
+       struct lcd_clk_config_s *cConf;
+
+       cConf = get_lcd_clk_config();
+       cConf->fout = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+       clk_div_sel = cConf->div_sel;
+       od1 = od_table[cConf->pll_od1_sel];
+       od2 = od_table[cConf->pll_od2_sel];
+       od3 = od_table[cConf->pll_od3_sel];
+       m = cConf->pll_m;
+       n = cConf->pll_n;
+
+       if (lcd_debug_print_flag == 2) {
+               LCDPR("m=%d, n=%d, od1=%d, od2=%d, od3=%d\n",
+                       m, n, cConf->pll_od1_sel, cConf->pll_od2_sel,
+                       cConf->pll_od3_sel);
+               LCDPR("clk_div_sel=%s(index %d), xd=%d\n",
+                       lcd_clk_div_sel_table[clk_div_sel],
+                       clk_div_sel, cConf->xd);
+       }
+       if (cConf->fout > cConf->xd_out_fmax) {
+               LCDERR("%s: wrong lcd_clk value %dkHz\n",
+                       __func__, cConf->fout);
+               return;
+       }
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s pclk=%d\n", __func__, cConf->fout);
+
+       clk_div_out = cConf->fout * cConf->xd;
+       if (clk_div_out > cConf->div_out_fmax) {
+               LCDERR("%s: wrong clk_div_out value %dkHz\n",
+                       __func__, clk_div_out);
+               return;
+       }
+
+       clk_div_in =
+               clk_div_calc_g9_gxtvbb(clk_div_out, clk_div_sel, CLK_DIV_O2I);
+       if (clk_div_in > cConf->div_in_fmax) {
+               LCDERR("%s: wrong clk_div_in value %dkHz\n",
+                       __func__, clk_div_in);
+               return;
+       }
+
+       pll_fout = clk_div_in;
+       if ((pll_fout > cConf->pll_out_fmax) ||
+               (pll_fout < cConf->pll_out_fmin)) {
+               LCDERR("%s: wrong pll_fout value %dkHz\n", __func__, pll_fout);
+               return;
+       }
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s pll_fout=%d\n", __func__, pll_fout);
+
+       pll_fvco = pll_fout * od1 * od2 * od3;
+       if ((pll_fvco < cConf->pll_vco_fmin) ||
+               (pll_fvco > cConf->pll_vco_fmax)) {
+               LCDERR("%s: wrong pll_fvco value %dkHz\n", __func__, pll_fvco);
+               return;
+       }
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s pll_fvco=%d\n", __func__, pll_fvco);
+
+       cConf->pll_fvco = pll_fvco;
+       od_fb = cConf->od_fb; /* pll default */
+       pll_fvco = pll_fvco / od_fb_table[od_fb + 1];
+       temp = cConf->fin * m / n;
+       if (pll_fvco >= temp) {
+               temp = pll_fvco - temp;
+               offset = 0;
+       } else {
+               temp = temp - pll_fvco;
+               offset = 1;
+       }
+       if (temp >= (2 * cConf->fin)) {
+               LCDERR("%s: pll changing %dkHz is too much\n",
+                       __func__, temp);
+               return;
+       }
+       frac = temp * cConf->pll_frac_range * n / cConf->fin;
+       cConf->pll_frac = frac | (offset << 11);
+       if (lcd_debug_print_flag)
+               LCDPR("lcd_pll_frac_generate frac=%d\n", frac);
+}
+
+static int check_pll_txl(struct lcd_clk_config_s *cConf,
+               unsigned int pll_fout)
+{
+       unsigned int m, n;
+       unsigned int od1_sel, od2_sel, od3_sel, od1, od2, od3;
+       unsigned int pll_fod2_in, pll_fod3_in, pll_fvco;
+       unsigned int od_fb = 0, pll_frac;
+       int done;
+
+       done = 0;
+       if ((pll_fout > cConf->pll_out_fmax) ||
+               (pll_fout < cConf->pll_out_fmin)) {
+               return done;
+       }
+       for (od3_sel = cConf->pll_od_sel_max; od3_sel > 0; od3_sel--) {
+               od3 = od_table[od3_sel - 1];
+               pll_fod3_in = pll_fout * od3;
+               for (od2_sel = od3_sel; od2_sel > 0; od2_sel--) {
+                       od2 = od_table[od2_sel - 1];
+                       pll_fod2_in = pll_fod3_in * od2;
+                       for (od1_sel = od2_sel; od1_sel > 0; od1_sel--) {
+                               od1 = od_table[od1_sel - 1];
+                               pll_fvco = pll_fod2_in * od1;
+                               if ((pll_fvco < cConf->pll_vco_fmin) ||
+                                       (pll_fvco > cConf->pll_vco_fmax)) {
+                                       continue;
+                               }
+                               cConf->pll_od1_sel = od1_sel - 1;
+                               cConf->pll_od2_sel = od2_sel - 1;
+                               cConf->pll_od3_sel = od3_sel - 1;
+                               cConf->pll_fout = pll_fout;
+                               if (lcd_debug_print_flag == 2) {
+                                       LCDPR("od1=%d, od2=%d, od3=%d\n",
+                                               (od1_sel - 1), (od2_sel - 1),
+                                               (od3_sel - 1));
+                                       LCDPR("pll_fvco=%d\n", pll_fvco);
+                               }
+                               cConf->pll_fvco = pll_fvco;
+                               n = 1;
+                               /* update od_fb to 1 for ss width */
+                               od_fb = cConf->od_fb; /* pll default */
+                               pll_fvco = pll_fvco / od_fb_table[od_fb];
+                               m = pll_fvco / cConf->fin;
+                               pll_frac = (pll_fvco % cConf->fin) *
+                                       cConf->pll_frac_range / cConf->fin;
+                               cConf->pll_m = m;
+                               cConf->pll_n = n;
+                               cConf->pll_frac = pll_frac;
+                               if (lcd_debug_print_flag == 2) {
+                                       LCDPR("m=%d, n=%d, frac=%d\n",
+                                               m, n, pll_frac);
+                               }
+                               done = 1;
+                               break;
+                       }
+               }
+       }
+       return done;
+}
+
+static void lcd_clk_generate_txl(struct lcd_config_s *pconf)
+{
+       unsigned int pll_fout;
+       unsigned int clk_div_in, clk_div_out;
+       unsigned int clk_div_sel, xd;
+       struct lcd_clk_config_s *cConf;
+       int done;
+
+       done = 0;
+       cConf = get_lcd_clk_config();
+       cConf->fout = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+       cConf->err_fmin = MAX_ERROR;
+
+       if (cConf->fout > cConf->xd_out_fmax) {
+               LCDERR("%s: wrong lcd_clk value %dkHz\n",
+                       __func__, cConf->fout);
+               goto generate_clk_done_txl;
+       }
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_TTL:
+               clk_div_sel = CLK_DIV_SEL_1;
+               cConf->xd_max = CRT_VID_DIV_MAX;
+               for (xd = 1; xd <= cConf->xd_max; xd++) {
+                       clk_div_out = cConf->fout * xd;
+                       if (clk_div_out > cConf->div_out_fmax)
+                               continue;
+                       if (lcd_debug_print_flag == 2) {
+                               LCDPR("fout=%d, xd=%d, clk_div_out=%d\n",
+                                       cConf->fout, xd, clk_div_out);
+                       }
+                       clk_div_in = clk_div_calc_g9_gxtvbb(clk_div_out,
+                                       clk_div_sel, CLK_DIV_O2I);
+                       if (clk_div_in > cConf->div_in_fmax)
+                               continue;
+                       cConf->xd = xd;
+                       cConf->div_sel = clk_div_sel;
+                       pll_fout = clk_div_in;
+                       if (lcd_debug_print_flag == 2) {
+                               LCDPR("clk_div_sel=%s(index %d), pll_fout=%d\n",
+                                       lcd_clk_div_sel_table[clk_div_sel],
+                                       clk_div_sel, pll_fout);
+                       }
+                       done = check_pll_txl(cConf, pll_fout);
+                       if (done)
+                               goto generate_clk_done_txl;
+               }
+               break;
+       case LCD_LVDS:
+               clk_div_sel = CLK_DIV_SEL_7;
+               xd = 1;
+               clk_div_out = cConf->fout * xd;
+               if (clk_div_out > cConf->div_out_fmax)
+                       goto generate_clk_done_txl;
+               if (lcd_debug_print_flag == 2) {
+                       LCDPR("fout=%d, xd=%d, clk_div_out=%d\n",
+                               cConf->fout, xd, clk_div_out);
+               }
+               clk_div_in = clk_div_calc_g9_gxtvbb(clk_div_out,
+                               clk_div_sel, CLK_DIV_O2I);
+               if (clk_div_in > cConf->div_in_fmax)
+                       goto generate_clk_done_txl;
+               cConf->xd = xd;
+               cConf->div_sel = clk_div_sel;
+               pll_fout = clk_div_in;
+               if (lcd_debug_print_flag == 2) {
+                       LCDPR("clk_div_sel=%s(index %d), pll_fout=%d\n",
+                               lcd_clk_div_sel_table[clk_div_sel],
+                               clk_div_sel, pll_fout);
+               }
+               done = check_pll_txl(cConf, pll_fout);
+               if (done)
+                       goto generate_clk_done_txl;
+               break;
+       case LCD_VBYONE:
+               cConf->div_sel_max = CLK_DIV_SEL_MAX;
+               cConf->xd_max = CRT_VID_DIV_MAX;
+               pll_fout = pconf->lcd_control.vbyone_config->bit_rate / 1000;
+               clk_div_in = pll_fout;
+               if (clk_div_in > cConf->div_in_fmax)
+                       goto generate_clk_done_txl;
+               if (lcd_debug_print_flag == 2)
+                       LCDPR("pll_fout=%d\n", pll_fout);
+               if ((clk_div_in / cConf->fout) > 15)
+                       cConf->xd = 4;
+               else
+                       cConf->xd = 1;
+               clk_div_out = cConf->fout * cConf->xd;
+               if (lcd_debug_print_flag == 2) {
+                       LCDPR("clk_div_in=%d, fout=%d, xd=%d, clk_div_out=%d\n",
+                               clk_div_in, cConf->fout,
+                               clk_div_out, cConf->xd);
+               }
+               if (clk_div_out > cConf->div_out_fmax)
+                       goto generate_clk_done_txl;
+               clk_div_sel = clk_div_get_g9_gxtvbb(
+                               clk_div_in * 100 / clk_div_out);
+               cConf->div_sel = clk_div_sel;
+               if (lcd_debug_print_flag == 2) {
+                       LCDPR("clk_div_sel=%s(index %d)\n",
+                               lcd_clk_div_sel_table[clk_div_sel],
+                               cConf->div_sel);
+               }
+               done = check_pll_txl(cConf, pll_fout);
+               break;
+       default:
+               break;
+       }
+
+generate_clk_done_txl:
+       if (done) {
+               pconf->lcd_timing.pll_ctrl =
+                       (cConf->pll_od1_sel << PLL_CTRL_OD1) |
+                       (cConf->pll_od2_sel << PLL_CTRL_OD2) |
+                       (cConf->pll_od3_sel << PLL_CTRL_OD3) |
+                       (cConf->pll_n << PLL_CTRL_N)         |
+                       (cConf->pll_m << PLL_CTRL_M);
+               pconf->lcd_timing.div_ctrl =
+                       (cConf->div_sel << DIV_CTRL_DIV_SEL) |
+                       (cConf->xd << DIV_CTRL_XD);
+               pconf->lcd_timing.clk_ctrl =
+                       (cConf->pll_frac << CLK_CTRL_FRAC);
+       } else {
+               pconf->lcd_timing.pll_ctrl =
+                       (1 << PLL_CTRL_OD1) |
+                       (1 << PLL_CTRL_OD2) |
+                       (1 << PLL_CTRL_OD3) |
+                       (1 << PLL_CTRL_N)   |
+                       (50 << PLL_CTRL_M);
+               pconf->lcd_timing.div_ctrl =
+                       (CLK_DIV_SEL_1 << DIV_CTRL_DIV_SEL) |
+                       (7 << DIV_CTRL_XD);
+               pconf->lcd_timing.clk_ctrl = (0 << CLK_CTRL_FRAC);
+               LCDERR("Out of clock range, reset to default setting\n");
+       }
+}
+
+static void lcd_pll_frac_generate_txl(struct lcd_config_s *pconf)
+{
+       unsigned int pll_fout;
+       unsigned int clk_div_in, clk_div_out, clk_div_sel;
+       unsigned int od1, od2, od3, pll_fvco;
+       unsigned int m, n, od_fb, frac, offset, temp;
+       struct lcd_clk_config_s *cConf;
+
+       cConf = get_lcd_clk_config();
+       cConf->fout = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+       clk_div_sel = cConf->div_sel;
+       od1 = od_table[cConf->pll_od1_sel];
+       od2 = od_table[cConf->pll_od2_sel];
+       od3 = od_table[cConf->pll_od3_sel];
+       m = cConf->pll_m;
+       n = cConf->pll_n;
+
+       if (lcd_debug_print_flag == 2) {
+               LCDPR("m=%d, n=%d, od1=%d, od2=%d, od3=%d\n",
+                       m, n, cConf->pll_od1_sel, cConf->pll_od2_sel,
+                       cConf->pll_od3_sel);
+               LCDPR("clk_div_sel=%s(index %d), xd=%d\n",
+                       lcd_clk_div_sel_table[clk_div_sel],
+                       clk_div_sel, cConf->xd);
+       }
+       if (cConf->fout > cConf->xd_out_fmax) {
+               LCDERR("%s: wrong lcd_clk value %dkHz\n",
+                       __func__, cConf->fout);
+               return;
+       }
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s pclk=%d\n", __func__, cConf->fout);
+
+       clk_div_out = cConf->fout * cConf->xd;
+       if (clk_div_out > cConf->div_out_fmax) {
+               LCDERR("%s: wrong clk_div_out value %dkHz\n",
+                       __func__, clk_div_out);
+               return;
+       }
+
+       clk_div_in =
+               clk_div_calc_g9_gxtvbb(clk_div_out, clk_div_sel, CLK_DIV_O2I);
+       if (clk_div_in > cConf->div_in_fmax) {
+               LCDERR("%s: wrong clk_div_in value %dkHz\n",
+                       __func__, clk_div_in);
+               return;
+       }
+
+       pll_fout = clk_div_in;
+       if ((pll_fout > cConf->pll_out_fmax) ||
+               (pll_fout < cConf->pll_out_fmin)) {
+               LCDERR("%s: wrong pll_fout value %dkHz\n", __func__, pll_fout);
+               return;
+       }
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s pll_fout=%d\n", __func__, pll_fout);
+
+       pll_fvco = pll_fout * od1 * od2 * od3;
+       if ((pll_fvco < cConf->pll_vco_fmin) ||
+               (pll_fvco > cConf->pll_vco_fmax)) {
+               LCDERR("%s: wrong pll_fvco value %dkHz\n", __func__, pll_fvco);
+               return;
+       }
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s pll_fvco=%d\n", __func__, pll_fvco);
+
+       cConf->pll_fvco = pll_fvco;
+       od_fb = cConf->od_fb; /* pll default */
+       pll_fvco = pll_fvco / od_fb_table[od_fb];
+       temp = cConf->fin * m / n;
+       if (pll_fvco >= temp) {
+               temp = pll_fvco - temp;
+               offset = 0;
+       } else {
+               temp = temp - pll_fvco;
+               offset = 1;
+       }
+       if (temp >= (2 * cConf->fin)) {
+               LCDERR("%s: pll changing %dkHz is too much\n",
+                       __func__, temp);
+               return;
+       }
+       frac = temp * cConf->pll_frac_range * n / cConf->fin;
+       cConf->pll_frac = frac | (offset << 11);
+       if (lcd_debug_print_flag)
+               LCDPR("lcd_pll_frac_generate: frac=0x%x\n", frac);
+}
+
+static int check_pll_axg(struct lcd_clk_config_s *cConf,
+               unsigned int pll_fout)
+{
+       unsigned int m, n, od_sel, od;
+       unsigned int pll_fvco;
+       unsigned int od_fb = 0, pll_frac;
+       int done = 0;
+
+       if ((pll_fout > cConf->pll_out_fmax) ||
+               (pll_fout < cConf->pll_out_fmin)) {
+               return done;
+       }
+       for (od_sel = cConf->pll_od_sel_max; od_sel > 0; od_sel--) {
+               od = od_table[od_sel - 1];
+               pll_fvco = pll_fout * od;
+               if ((pll_fvco < cConf->pll_vco_fmin) ||
+                       (pll_fvco > cConf->pll_vco_fmax)) {
+                       continue;
+               }
+               cConf->pll_od1_sel = od_sel - 1;
+               cConf->pll_fout = pll_fout;
+               if (lcd_debug_print_flag == 2) {
+                       LCDPR("od_sel=%d, pll_fvco=%d\n",
+                               (od_sel - 1), pll_fvco);
+               }
+
+               cConf->pll_fvco = pll_fvco;
+               n = 1;
+               od_fb = cConf->od_fb;
+               pll_fvco = pll_fvco / od_fb_table[od_fb];
+               m = pll_fvco / cConf->fin;
+               pll_frac = (pll_fvco % cConf->fin) *
+                                               cConf->pll_frac_range / cConf->fin;
+               cConf->pll_m = m;
+               cConf->pll_n = n;
+               cConf->pll_frac = pll_frac;
+               if (lcd_debug_print_flag == 2) {
+                       LCDPR("pll_m=%d, pll_n=%d\n", m, n);
+                       LCDPR("pll_frac=0x%03x\n",
+                               pll_frac);
+               }
+               done = 1;
+               break;
+       }
+       return done;
+}
+
+static void lcd_clk_generate_axg(struct lcd_config_s *pconf)
+{
+       unsigned int pll_fout;
+       unsigned int xd;
+       unsigned int dsi_bit_rate_max = 0, dsi_bit_rate_min = 0;
+       unsigned int tmp;
+       struct lcd_clk_config_s *cConf;
+       int done;
+
+       done = 0;
+       cConf = get_lcd_clk_config();
+       cConf->fout = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+       cConf->err_fmin = MAX_ERROR;
+
+       if (cConf->fout > cConf->xd_out_fmax) {
+               LCDERR("%s: wrong lcd_clk value %dkHz\n",
+                       __func__, cConf->fout);
+               goto generate_clk_done_axg;
+       }
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_MIPI:
+               cConf->xd_max = CRT_VID_DIV_MAX;
+               tmp = pconf->lcd_control.mipi_config->bit_rate_max;
+               dsi_bit_rate_max = tmp * 1000; /* change to kHz */
+               dsi_bit_rate_min = dsi_bit_rate_max - cConf->fout;
+
+               for (xd = 1; xd <= cConf->xd_max; xd++) {
+                       pll_fout = cConf->fout * xd;
+                       if ((pll_fout > dsi_bit_rate_max) ||
+                               (pll_fout < dsi_bit_rate_min)) {
+                               continue;
+                       }
+                       if (lcd_debug_print_flag == 2)
+                               LCDPR("fout=%d, xd=%d\n", cConf->fout, xd);
+
+                       pconf->lcd_control.mipi_config->bit_rate = pll_fout * 1000;
+                       cConf->xd = xd;
+                       done = check_pll_axg(cConf, pll_fout);
+                       if (done)
+                               goto generate_clk_done_axg;
+               }
+               break;
+       default:
+               break;
+       }
+
+generate_clk_done_axg:
+       if (done) {
+               pconf->lcd_timing.pll_ctrl =
+                       (cConf->pll_od1_sel << PLL_CTRL_OD1) |
+                       (cConf->pll_n << PLL_CTRL_N) |
+                       (cConf->pll_m << PLL_CTRL_M);
+               pconf->lcd_timing.div_ctrl =
+                       (cConf->edp_div1 << DIV_CTRL_EDP_DIV1) |
+                       (cConf->edp_div0 << DIV_CTRL_EDP_DIV0) |
+                       (cConf->div_post << DIV_CTRL_DIV_POST) |
+                       (cConf->div_pre << DIV_CTRL_DIV_PRE) |
+                       (cConf->xd << DIV_CTRL_XD);
+               tmp = (pconf->lcd_timing.clk_ctrl &
+                       ~((0x7 << CLK_CTRL_LEVEL) | (0xfff << CLK_CTRL_FRAC)));
+               pconf->lcd_timing.clk_ctrl = (tmp |
+                       (cConf->pll_level << CLK_CTRL_LEVEL) |
+                       (cConf->pll_frac << CLK_CTRL_FRAC));
+       } else {
+               pconf->lcd_timing.pll_ctrl = (1 << PLL_CTRL_OD1) |
+                       (1 << PLL_CTRL_N) | (50 << PLL_CTRL_M);
+               pconf->lcd_timing.div_ctrl =
+                       (0 << DIV_CTRL_EDP_DIV1) | (0 << DIV_CTRL_EDP_DIV0) |
+                       (1 << DIV_CTRL_DIV_PRE) | (1 << DIV_CTRL_DIV_PRE) |
+                       (7 << DIV_CTRL_XD);
+               tmp = (pconf->lcd_timing.clk_ctrl &
+                       ~((0x7 << CLK_CTRL_LEVEL) | (0xfff << CLK_CTRL_FRAC)));
+               pconf->lcd_timing.clk_ctrl |= (1 << CLK_CTRL_LEVEL);
+               LCDERR("Out of clock range, reset to default setting!\n");
+       }
+}
+
+static void lcd_pll_reset_axg(void)
+{
+       lcd_hiu_setb(HHI_GP0_PLL_CNTL, 1, LCD_PLL_RST_AXG, 1);
+       udelay(10);
+       lcd_hiu_setb(HHI_GP0_PLL_CNTL, 0, LCD_PLL_RST_AXG, 1);
+}
+
+static void lcd_set_pll_axg(struct lcd_clk_config_s *cConf)
+{
+       unsigned int pll_ctrl, pll_ctrl1, pll_ctrl2;
+       int ret;
+
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s\n", __func__);
+
+       pll_ctrl = ((1 << LCD_PLL_EN_AXG) |
+               (cConf->pll_n << LCD_PLL_N_AXG) |
+               (cConf->pll_m << LCD_PLL_M_AXG) |
+               (cConf->pll_od1_sel << LCD_PLL_OD_AXG));
+       pll_ctrl1 = 0xc084a000;
+       pll_ctrl1 |= ((1 << 12) | (cConf->pll_frac << 0));
+       pll_ctrl2 = 0xb75020be | (cConf->od_fb << 19);
+
+       lcd_hiu_write(HHI_GP0_PLL_CNTL, pll_ctrl);
+       lcd_hiu_write(HHI_GP0_PLL_CNTL1, pll_ctrl1);
+       lcd_hiu_write(HHI_GP0_PLL_CNTL2, pll_ctrl2);
+       lcd_hiu_write(HHI_GP0_PLL_CNTL3, 0x0a59a288);
+       lcd_hiu_write(HHI_GP0_PLL_CNTL4, 0xc000004d);
+       lcd_hiu_write(HHI_GP0_PLL_CNTL5, 0x00058000);
+       lcd_hiu_setb(HHI_GP0_PLL_CNTL, 1, LCD_PLL_RST_AXG, 1);
+       lcd_hiu_setb(HHI_GP0_PLL_CNTL, 0, LCD_PLL_RST_AXG, 1);
+
+       ret = lcd_pll_wait_lock(HHI_GP0_PLL_CNTL, LCD_PLL_LOCK_AXG);
+       if (ret)
+               LCDERR("gp0_pll lock failed\n");
+
+}
+
+static void lcd_pll_frac_generate_axg(struct lcd_config_s *pconf)
+{
+       unsigned int pll_fout;
+       unsigned int od, pll_fvco;
+       unsigned int m, n, od_fb, frac, offset, temp;
+       struct lcd_clk_config_s *cConf;
+
+       cConf = get_lcd_clk_config();
+       cConf->fout = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+       od = od_table[cConf->pll_od1_sel];
+       m = cConf->pll_m;
+       n = cConf->pll_n;
+
+       if (lcd_debug_print_flag == 2) {
+               LCDPR("m=%d, n=%d, od=%d, xd=%d\n",
+                       m, n, cConf->pll_od1_sel, cConf->xd);
+       }
+       if (cConf->fout > cConf->xd_out_fmax) {
+               LCDERR("%s: wrong lcd_clk value %dkHz\n",
+                       __func__, cConf->fout);
+               return;
+       }
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s pclk=%d\n", __func__, cConf->fout);
+
+       pll_fout = cConf->fout * cConf->xd;
+       if ((pll_fout > cConf->pll_out_fmax) ||
+               (pll_fout < cConf->pll_out_fmin)) {
+               LCDERR("%s: wrong pll_fout value %dkHz\n", __func__, pll_fout);
+               return;
+       }
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s pll_fout=%d\n", __func__, pll_fout);
+
+       pll_fvco = pll_fout * od;
+       if ((pll_fvco < cConf->pll_vco_fmin) ||
+               (pll_fvco > cConf->pll_vco_fmax)) {
+               LCDERR("%s: wrong pll_fvco value %dkHz\n", __func__, pll_fvco);
+               return;
+       }
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s pll_fvco=%d\n", __func__, pll_fvco);
+
+       cConf->pll_fvco = pll_fvco;
+       od_fb = cConf->od_fb; /* pll default */
+       pll_fvco = pll_fvco / od_fb_table[od_fb];
+       temp = cConf->fin * m / n;
+       if (pll_fvco >= temp) {
+               temp = pll_fvco - temp;
+               offset = 0;
+       } else {
+               temp = temp - pll_fvco;
+               offset = 1;
+       }
+       if (temp >= (2 * cConf->fin)) {
+               LCDERR("%s: pll changing %dkHz is too much\n",
+                       __func__, temp);
+               return;
+       }
+       frac = temp * cConf->pll_frac_range * n / cConf->fin;
+       cConf->pll_frac = frac | (offset << 11);
+       if (lcd_debug_print_flag)
+               LCDPR("lcd_pll_frac_generate: frac=0x%x\n", frac);
+}
+
+static void lcd_update_pll_frac_axg(struct lcd_clk_config_s *cConf)
+{
+       if (lcd_debug_print_flag == 2)
+               LCDPR("%s\n", __func__);
+
+       lcd_hiu_setb(HHI_GP0_PLL_CNTL1, cConf->pll_frac, 0, 12);
+}
+
+void lcd_clk_generate_parameter(struct lcd_config_s *pconf)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_GXTVBB:
+               lcd_clk_generate_g9_gxtvbb(pconf);
+               break;
+       case LCD_CHIP_GXL:
+       case LCD_CHIP_GXM:
+       case LCD_CHIP_TXL:
+       case LCD_CHIP_TXLX:
+               lcd_clk_generate_txl(pconf);
+               break;
+       case LCD_CHIP_AXG:
+               lcd_clk_generate_axg(pconf);
+               break;
+       default:
+               break;
+       }
+}
+
+char *lcd_get_spread_spectrum(void)
+{
+       char *ss_str;
+       int ss_level;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       ss_level = lcd_drv->lcd_config->lcd_timing.ss_level;
+       ss_level = (ss_level >= clk_conf.ss_level_max) ? 0 : ss_level;
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_GXTVBB:
+               ss_str = lcd_pll_ss_table_gxtvbb[ss_level];
+               break;
+       case LCD_CHIP_TXL:
+               ss_str = lcd_pll_ss_table_txl[ss_level];
+               break;
+       case LCD_CHIP_TXLX:
+               ss_str = lcd_pll_ss_table_txlx[ss_level];
+               break;
+       default:
+               ss_str = "unknown";
+               break;
+       }
+
+       return ss_str;
+}
+
+void lcd_set_spread_spectrum(void)
+{
+       unsigned long flags = 0;
+       int ss_level;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       spin_lock_irqsave(&lcd_clk_lock, flags);
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       ss_level = lcd_drv->lcd_config->lcd_timing.ss_level;
+       clk_conf.ss_level = (ss_level >= clk_conf.ss_level_max) ? 0 : ss_level;
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_GXTVBB:
+               lcd_set_pll_ss_gxtvbb(&clk_conf);
+               break;
+       case LCD_CHIP_TXL:
+               lcd_set_pll_ss_txl(&clk_conf);
+               break;
+       case LCD_CHIP_TXLX:
+               lcd_set_pll_ss_txlx(&clk_conf);
+               break;
+       default:
+               break;
+       }
+       spin_unlock_irqrestore(&lcd_clk_lock, flags);
+}
+
+int lcd_encl_clk_msr(void)
+{
+       unsigned int clk_mux;
+       int encl_clk = 0;
+
+       clk_mux = 9;
+       encl_clk = meson_clk_measure(clk_mux);
+
+       return encl_clk;
+}
+
+void lcd_pll_reset(void)
+{
+       unsigned long flags = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       spin_lock_irqsave(&lcd_clk_lock, flags);
+       LCDPR("%s\n", __func__);
+
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_GXTVBB:
+               lcd_pll_reset_gxtvbb();
+               break;
+       case LCD_CHIP_GXL:
+       case LCD_CHIP_GXM:
+       case LCD_CHIP_TXL:
+       case LCD_CHIP_TXLX:
+               lcd_pll_reset_txl();
+               break;
+       case LCD_CHIP_AXG:
+               lcd_pll_reset_axg();
+               break;
+       default:
+               break;
+       }
+
+       spin_unlock_irqrestore(&lcd_clk_lock, flags);
+}
+
+/* for frame rate change */
+void lcd_clk_update(struct lcd_config_s *pconf)
+{
+       unsigned long flags = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       spin_lock_irqsave(&lcd_clk_lock, flags);
+       LCDPR("%s\n", __func__);
+
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_GXTVBB:
+               lcd_pll_frac_generate_g9_gxtvbb(pconf);
+               lcd_update_pll_frac_gxtvbb(&clk_conf);
+               break;
+       case LCD_CHIP_GXL:
+       case LCD_CHIP_GXM:
+       case LCD_CHIP_TXL:
+       case LCD_CHIP_TXLX:
+               lcd_pll_frac_generate_txl(pconf);
+               lcd_update_pll_frac_txl(&clk_conf);
+               break;
+       case LCD_CHIP_AXG:
+               lcd_pll_frac_generate_axg(pconf);
+               lcd_update_pll_frac_axg(&clk_conf);
+               break;
+       default:
+               break;
+       }
+
+       spin_unlock_irqrestore(&lcd_clk_lock, flags);
+}
+
+/* for timing change */
+void lcd_clk_set(struct lcd_config_s *pconf)
+{
+       unsigned long flags = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       spin_lock_irqsave(&lcd_clk_lock, flags);
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_GXTVBB:
+               lcd_set_pll_gxtvbb(&clk_conf);
+               lcd_set_clk_div_g9_gxtvbb(&clk_conf);
+               break;
+       case LCD_CHIP_GXL:
+       case LCD_CHIP_GXM:
+       case LCD_CHIP_TXL:
+               lcd_set_pll_txl(&clk_conf);
+               lcd_set_clk_div_g9_gxtvbb(&clk_conf);
+               break;
+       case LCD_CHIP_TXLX:
+               lcd_set_pll_txlx(&clk_conf);
+               lcd_set_clk_div_g9_gxtvbb(&clk_conf);
+               break;
+       case LCD_CHIP_AXG:
+               lcd_set_pll_axg(&clk_conf);
+               break;
+       default:
+               break;
+       }
+       lcd_set_vclk_crt(pconf->lcd_basic.lcd_type, &clk_conf);
+       mdelay(10);
+       spin_unlock_irqrestore(&lcd_clk_lock, flags);
+}
+
+void lcd_clk_disable(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       /* disable CTS_ENCL clk gate, new added in m8m2 */
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_GXTVBB:
+       case LCD_CHIP_GXL:
+       case LCD_CHIP_GXM:
+       case LCD_CHIP_TXL:
+       case LCD_CHIP_TXLX:
+       case LCD_CHIP_AXG:
+               lcd_hiu_setb(HHI_VID_CLK_CNTL2, 0, ENCL_GATE_VCLK, 1);
+               break;
+       default:
+               break;
+       }
+
+       /* close vclk2_div gate: 0x104b[4:0] */
+       lcd_hiu_setb(HHI_VIID_CLK_CNTL, 0, 0, 5);
+       lcd_hiu_setb(HHI_VIID_CLK_CNTL, 0, VCLK2_EN, 1);
+
+       /* close vid2_pll gate: 0x104c[16] */
+       lcd_hiu_setb(HHI_VIID_DIVIDER_CNTL, 0, DIV_CLK_IN_EN, 1);
+
+       /* disable pll */
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_GXTVBB:
+               /* disable hdmi_pll: 0x10c8[30] */
+               lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 0, LCD_PLL_EN_GXTVBB, 1);
+               lcd_hiu_setb(HHI_HDMI_PLL_CNTL5, 0, 30, 1); /* bandgap */
+               break;
+       case LCD_CHIP_GXL:
+       case LCD_CHIP_GXM:
+       case LCD_CHIP_TXL:
+       case LCD_CHIP_TXLX:
+               /* disable hdmi_pll: 0x10c8[30] */
+               lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 0, LCD_PLL_EN_TXL, 1);
+               break;
+       case LCD_CHIP_AXG:
+               /* disable hdmi_pll: 0x10c8[30] */
+               lcd_hiu_setb(HHI_GP0_PLL_CNTL, 0, LCD_PLL_EN_AXG, 1);
+               break;
+       default:
+               break;
+       }
+}
+
+void lcd_clk_config_probe(void)
+{
+       spin_lock_init(&lcd_clk_lock);
+       lcd_clk_config_chip_init();
+}
diff --git a/drivers/amlogic/media/vout/lcd/lcd_clk_config.h b/drivers/amlogic/media/vout/lcd/lcd_clk_config.h
new file mode 100644 (file)
index 0000000..1db5e45
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_clk_config.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LCD_CLK_CONFIG_H
+#define _LCD_CLK_CONFIG_H
+
+#include <linux/types.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+
+/* clk config */
+struct lcd_clk_config_s { /* unit: kHz */
+       /* IN-OUT parameters */
+       unsigned int fin;
+       unsigned int fout;
+
+       /* pll parameters */
+       unsigned int od_fb;
+       unsigned int pll_m;
+       unsigned int pll_n;
+       unsigned int pll_fvco;
+       unsigned int pll_od1_sel;
+       unsigned int pll_od2_sel;
+       unsigned int pll_od3_sel;
+       unsigned int pll_level;
+       unsigned int pll_frac;
+       unsigned int pll_fout;
+       unsigned int ss_level;
+       unsigned int edp_div0;
+       unsigned int edp_div1;
+       unsigned int div_pre; /* m6, m8, m8b */
+       unsigned int div_post; /* m6, m8, m8b */
+       unsigned int div_sel; /* g9tv, g9bb, gxbb */
+       unsigned int xd;
+
+       /* clk path node parameters */
+       unsigned int ss_level_max;
+       unsigned int pll_m_max;
+       unsigned int pll_m_min;
+       unsigned int pll_n_max;
+       unsigned int pll_n_min;
+       unsigned int pll_frac_range;
+       unsigned int pll_od_sel_max;
+       unsigned int div_pre_sel_max; /* m6, m8, m8b */
+       unsigned int div_post_sel_max; /* m6, m8, m8b */
+       unsigned int div_sel_max; /* g9tv, g9bb, gxbb */
+       unsigned int xd_max;
+       unsigned int pll_ref_fmax;
+       unsigned int pll_ref_fmin;
+       unsigned int pll_vco_fmax;
+       unsigned int pll_vco_fmin;
+       unsigned int pll_out_fmax;
+       unsigned int pll_out_fmin;
+       unsigned int div_post_out_fmax; /* m6, m8, m8b */
+       unsigned int div_in_fmax; /* g9tv, g9bb, gxbb */
+       unsigned int div_out_fmax; /* g9tv, g9bb, gxbb */
+       unsigned int xd_out_fmax;
+       unsigned int err_fmin;
+};
+
+/* pll & clk parameter */
+/* ******** clk calculation ******** */
+#define PLL_WAIT_LOCK_CNT           200
+ /* frequency unit: kHz */
+#define FIN_FREQ                    (24 * 1000)
+/* clk max error */
+#define MAX_ERROR                   (2 * 1000)
+
+/* ******** register bit ******** */
+/* divider */
+#define CRT_VID_DIV_MAX             255
+
+#define DIV_PRE_SEL_MAX             6
+#define EDP_DIV0_SEL_MAX            15
+#define EDP_DIV1_SEL_MAX            8
+
+/* g9tv, g9bb, gxbb divider */
+#define CLK_DIV_I2O     0
+#define CLK_DIV_O2I     1
+enum div_sel_e {
+       CLK_DIV_SEL_1 = 0,
+       CLK_DIV_SEL_2,    /* 1 */
+       CLK_DIV_SEL_3,    /* 2 */
+       CLK_DIV_SEL_3p5,  /* 3 */
+       CLK_DIV_SEL_3p75, /* 4 */
+       CLK_DIV_SEL_4,    /* 5 */
+       CLK_DIV_SEL_5,    /* 6 */
+       CLK_DIV_SEL_6,    /* 7 */
+       CLK_DIV_SEL_6p25, /* 8 */
+       CLK_DIV_SEL_7,    /* 9 */
+       CLK_DIV_SEL_7p5,  /* 10 */
+       CLK_DIV_SEL_12,   /* 11 */
+       CLK_DIV_SEL_14,   /* 12 */
+       CLK_DIV_SEL_15,   /* 13 */
+       CLK_DIV_SEL_2p5,  /* 14 */
+       CLK_DIV_SEL_MAX,
+};
+
+
+/* GXTVBB */
+/* ******** register bit ******** */
+/* PLL_CNTL 0x10c8 */
+#define LCD_PLL_LOCK_GXTVBB         31
+#define LCD_PLL_EN_GXTVBB           30
+#define LCD_PLL_RST_GXTVBB          28
+#define LCD_PLL_N_GXTVBB            9
+#define LCD_PLL_M_GXTVBB            0
+
+#define LCD_PLL_OD3_GXTVBB          18
+#define LCD_PLL_OD2_GXTVBB          22
+#define LCD_PLL_OD1_GXTVBB          16
+
+/* ******** frequency limit (unit: kHz) ******** */
+/* pll */
+#define PLL_FRAC_OD_FB_GXTVBB       0
+#define SS_LEVEL_MAX_GXTVBB         5
+#define PLL_M_MIN_GXTVBB            2
+#define PLL_M_MAX_GXTVBB            511
+#define PLL_N_MIN_GXTVBB            1
+#define PLL_N_MAX_GXTVBB            1
+#define PLL_FRAC_RANGE_GXTVBB       (1 << 10)
+#define PLL_OD_SEL_MAX_GXTVBB       3
+#define PLL_FREF_MIN_GXTVBB         (5 * 1000)
+#define PLL_FREF_MAX_GXTVBB         (25 * 1000)
+#define PLL_VCO_MIN_GXTVBB          (3000 * 1000)
+#define PLL_VCO_MAX_GXTVBB          (6000 * 1000)
+
+/* video */
+#define CLK_DIV_IN_MAX_GXTVBB       (3100 * 1000)
+#define CRT_VID_CLK_IN_MAX_GXTVBB   (3100 * 1000)
+#define ENCL_CLK_IN_MAX_GXTVBB      (620 * 1000)
+
+/* GXL */
+/* ******** register bit ******** */
+/* PLL_CNTL 0x10c8 */
+#define LCD_PLL_LOCK_GXL            31
+#define LCD_PLL_EN_GXL              30
+#define LCD_PLL_RST_GXL             28
+#define LCD_PLL_N_GXL               9
+#define LCD_PLL_M_GXL               0
+
+#define LCD_PLL_OD3_GXL             19
+#define LCD_PLL_OD2_GXL             23
+#define LCD_PLL_OD1_GXL             21
+
+/* ******** frequency limit (unit: kHz) ******** */
+/* pll */
+#define PLL_FRAC_OD_FB_GXL                     1
+#define SS_LEVEL_MAX_GXL            5
+#define PLL_M_MIN_GXL               2
+#define PLL_M_MAX_GXL               511
+#define PLL_N_MIN_GXL               1
+#define PLL_N_MAX_GXL               1
+#define PLL_FRAC_RANGE_GXL          (1 << 10)
+#define PLL_OD_SEL_MAX_GXL          3
+#define PLL_FREF_MIN_GXL            (5 * 1000)
+#define PLL_FREF_MAX_GXL            (25 * 1000)
+#define PLL_VCO_MIN_GXL             (3000 * 1000)
+#define PLL_VCO_MAX_GXL             (6000 * 1000)
+
+/* video */
+#define CLK_DIV_IN_MAX_GXL          (3100 * 1000)
+#define CRT_VID_CLK_IN_MAX_GXL      (3100 * 1000)
+#define ENCL_CLK_IN_MAX_GXL         (620 * 1000)
+
+/* GXM */
+/* ******** register bit ******** */
+/* PLL_CNTL 0x10c8 */
+#define LCD_PLL_LOCK_GXM            31
+#define LCD_PLL_EN_GXM              30
+#define LCD_PLL_RST_GXM             28
+#define LCD_PLL_N_GXM               9
+#define LCD_PLL_M_GXM               0
+
+#define LCD_PLL_OD3_GXM             19
+#define LCD_PLL_OD2_GXM             23
+#define LCD_PLL_OD1_GXM             21
+
+/* ******** frequency limit (unit: kHz) ******** */
+/* pll */
+#define PLL_FRAC_OD_FB_GXM                     1
+#define SS_LEVEL_MAX_GXM            5
+#define PLL_M_MIN_GXM               2
+#define PLL_M_MAX_GXM               511
+#define PLL_N_MIN_GXM               1
+#define PLL_N_MAX_GXM               1
+#define PLL_FRAC_RANGE_GXM          (1 << 10)
+#define PLL_OD_SEL_MAX_GXM          3
+#define PLL_FREF_MIN_GXM            (5 * 1000)
+#define PLL_FREF_MAX_GXM            (25 * 1000)
+#define PLL_VCO_MIN_GXM             (3000 * 1000)
+#define PLL_VCO_MAX_GXM             (6000 * 1000)
+
+/* video */
+#define CLK_DIV_IN_MAX_GXM          (3100 * 1000)
+#define CRT_VID_CLK_IN_MAX_GXM      (3100 * 1000)
+#define ENCL_CLK_IN_MAX_GXM         (620 * 1000)
+
+/* TXL */
+/* ******** register bit ******** */
+/* PLL_CNTL 0x10c8 */
+#define LCD_PLL_LOCK_TXL            31
+#define LCD_PLL_EN_TXL              30
+#define LCD_PLL_RST_TXL             28
+#define LCD_PLL_N_TXL               9
+#define LCD_PLL_M_TXL               0
+
+#define LCD_PLL_OD3_TXL             19
+#define LCD_PLL_OD2_TXL             23
+#define LCD_PLL_OD1_TXL             21
+
+/* ******** frequency limit (unit: kHz) ******** */
+/* pll */
+#define PLL_FRAC_OD_FB_TXL                     1
+#define SS_LEVEL_MAX_TXL            5
+#define PLL_M_MIN_TXL               2
+#define PLL_M_MAX_TXL               511
+#define PLL_N_MIN_TXL               1
+#define PLL_N_MAX_TXL               1
+#define PLL_FRAC_RANGE_TXL          (1 << 10)
+#define PLL_OD_SEL_MAX_TXL          3
+#define PLL_FREF_MIN_TXL            (5 * 1000)
+#define PLL_FREF_MAX_TXL            (25 * 1000)
+#define PLL_VCO_MIN_TXL             (3000 * 1000)
+#define PLL_VCO_MAX_TXL             (6000 * 1000)
+
+/* video */
+#define CLK_DIV_IN_MAX_TXL          (3100 * 1000)
+#define CRT_VID_CLK_IN_MAX_TXL      (3100 * 1000)
+#define ENCL_CLK_IN_MAX_TXL         (620 * 1000)
+
+/*  TXLX */
+/* ******** register bit ******** */
+/* PLL_CNTL 0x10c8 */
+#define LCD_PLL_LOCK_TXLX            31
+#define LCD_PLL_EN_TXLX              30
+#define LCD_PLL_RST_TXLX             28
+#define LCD_PLL_N_TXLX               9
+#define LCD_PLL_M_TXLX               0
+
+#define LCD_PLL_OD3_TXLX             19
+#define LCD_PLL_OD2_TXLX             23
+#define LCD_PLL_OD1_TXLX             21
+
+/* ******** frequency limit (unit: kHz) ******** */
+/* pll */
+#define PLL_FRAC_OD_FB_TXLX                    0
+#define SS_LEVEL_MAX_TXLX            6
+#define PLL_M_MIN_TXLX               2
+#define PLL_M_MAX_TXLX               511
+#define PLL_N_MIN_TXLX               1
+#define PLL_N_MAX_TXLX               1
+#define PLL_FRAC_RANGE_TXLX          (1 << 10)
+#define PLL_OD_SEL_MAX_TXLX          3
+#define PLL_FREF_MIN_TXLX            (5 * 1000)
+#define PLL_FREF_MAX_TXLX            (25 * 1000)
+#define PLL_VCO_MIN_TXLX             (3000 * 1000)
+#define PLL_VCO_MAX_TXLX             (6000 * 1000)
+
+/* video */
+#define CLK_DIV_IN_MAX_TXLX          (3100 * 1000)
+#define CRT_VID_CLK_IN_MAX_TXLX      (3100 * 1000)
+#define ENCL_CLK_IN_MAX_TXLX         (620 * 1000)
+
+/* AXG */
+/* ******** register bit ******** */
+/* PLL_CNTL */
+#define LCD_PLL_LOCK_AXG            31
+#define LCD_PLL_EN_AXG              30
+#define LCD_PLL_RST_AXG             29
+#define LCD_PLL_OD_AXG              16
+#define LCD_PLL_N_AXG               9
+#define LCD_PLL_M_AXG               0
+
+/* ******** frequency limit (unit: kHz) ******** */
+/* pll */
+#define PLL_FRAC_OD_FB_AXG                     0
+#define SS_LEVEL_MAX_AXG            5
+#define PLL_M_MIN_AXG               2
+#define PLL_M_MAX_AXG               511
+#define PLL_N_MIN_AXG               1
+#define PLL_N_MAX_AXG               1
+#define PLL_FRAC_RANGE_AXG          (1 << 10)
+#define PLL_OD_SEL_MAX_AXG          3
+#define PLL_FREF_MIN_AXG            (5 * 1000)
+#define PLL_FREF_MAX_AXG            (25 * 1000)
+#define PLL_VCO_MIN_AXG             (1500 * 1000)
+#define PLL_VCO_MAX_AXG             (3000 * 1000)
+
+/* video */
+#define CRT_VID_CLK_IN_MAX_AXG      (3000 * 1000)
+#define ENCL_CLK_IN_MAX_AXG         (200 * 1000)
+
+
+extern int meson_clk_measure(unsigned int clk_mux);
+extern struct lcd_clk_config_s *get_lcd_clk_config(void);
+extern void lcd_clk_config_print(void);
+extern int lcd_encl_clk_msr(void);
+extern void lcd_pll_reset(void);
+extern char *lcd_get_spread_spectrum(void);
+extern void lcd_set_spread_spectrum(void);
+extern void lcd_clk_update(struct lcd_config_s *pconf);
+extern void lcd_clk_set(struct lcd_config_s *pconf);
+extern void lcd_clk_disable(void);
+extern void lcd_clk_generate_parameter(struct lcd_config_s *pconf);
+extern void lcd_clk_config_probe(void);
+
+#endif
diff --git a/drivers/amlogic/media/vout/lcd/lcd_common.c b/drivers/amlogic/media/vout/lcd/lcd_common.c
new file mode 100644 (file)
index 0000000..e614300
--- /dev/null
@@ -0,0 +1,1007 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_common.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#include <linux/clk.h>
+#ifdef CONFIG_AML_VPU
+#include <linux/amlogic/vpu.h>
+#endif
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#include <linux/amlogic/media/vout/vinfo.h>
+
+#include "lcd_common.h"
+#include "lcd_reg.h"
+
+/* lcd type */
+struct lcd_type_match_s {
+       char *name;
+       enum lcd_type_e type;
+};
+
+static struct lcd_type_match_s lcd_type_match_table[] = {
+       {"ttl",     LCD_TTL},
+       {"lvds",    LCD_LVDS},
+       {"vbyone",  LCD_VBYONE},
+       {"mipi",    LCD_MIPI},
+       {"invalid", LCD_TYPE_MAX},
+};
+
+int lcd_type_str_to_type(const char *str)
+{
+       int i;
+       int type = LCD_TYPE_MAX;
+
+       for (i = 0; i < LCD_TYPE_MAX; i++) {
+               if (!strcmp(str, lcd_type_match_table[i].name)) {
+                       type = lcd_type_match_table[i].type;
+                       break;
+               }
+       }
+       return type;
+}
+
+char *lcd_type_type_to_str(int type)
+{
+       int i;
+       char *str = lcd_type_match_table[LCD_TYPE_MAX].name;
+
+       for (i = 0; i < LCD_TYPE_MAX; i++) {
+               if (type == lcd_type_match_table[i].type) {
+                       str = lcd_type_match_table[i].name;
+                       break;
+               }
+       }
+       return str;
+}
+
+static char *lcd_mode_table[] = {
+       "tv",
+       "tablet",
+       "invalid",
+};
+
+unsigned char lcd_mode_str_to_mode(const char *str)
+{
+       unsigned char mode;
+
+       for (mode = 0; mode < ARRAY_SIZE(lcd_mode_table); mode++) {
+               if (!strcmp(str, lcd_mode_table[mode]))
+                       break;
+       }
+       return mode;
+}
+
+char *lcd_mode_mode_to_str(int mode)
+{
+       return lcd_mode_table[mode];
+}
+
+/* lcd gpio */
+#if 0
+#define lcd_gpio_request(dev, str)        gpiod_get(dev, str)
+#define lcd_gpio_free(gdesc)              gpiod_put(gdesc)
+#define lcd_gpio_input(gdesc)             gpiod_direction_input(gdesc)
+#define lcd_gpio_output(gdesc, val)       gpiod_direction_output(gdesc, val)
+#define lcd_gpio_get_value(gdesc)         gpiod_get_value(gdesc)
+#define lcd_gpio_set_value(gdesc, val)    gpiod_set_value(gdesc, val)
+#endif
+
+void lcd_cpu_gpio_register(unsigned int index)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_cpu_gpio_s *cpu_gpio;
+       const char *str;
+       int ret;
+
+       if (index >= LCD_CPU_GPIO_NUM_MAX) {
+               LCDERR("gpio index %d, exit\n", index);
+               return;
+       }
+       cpu_gpio = &lcd_drv->lcd_config->lcd_power->cpu_gpio[index];
+       if (cpu_gpio->flag) {
+               if (lcd_debug_print_flag) {
+                       LCDPR("gpio %s[%d] is already registered\n",
+                               cpu_gpio->name, index);
+               }
+               return;
+       }
+
+       /* get gpio name */
+       ret = of_property_read_string_index(lcd_drv->dev->of_node,
+               "lcd_cpu_gpio_names", index, &str);
+       if (ret) {
+               LCDERR("failed to get lcd_cpu_gpio_names: %d\n", index);
+               str = "unknown";
+       }
+       strcpy(cpu_gpio->name, str);
+
+       /* request gpio */
+       cpu_gpio->gpio = devm_gpiod_get_index(lcd_drv->dev,
+               "lcd_cpu", index, GPIOD_OUT_HIGH);
+       if (IS_ERR(cpu_gpio->gpio)) {
+               LCDERR("register gpio %s[%d]: %p, err: %d\n",
+                       cpu_gpio->name, index, cpu_gpio->gpio,
+                       IS_ERR(cpu_gpio->gpio));
+       } else {
+               cpu_gpio->flag = 1;
+               if (lcd_debug_print_flag) {
+                       LCDPR("register gpio %s[%d]: %p\n",
+                               cpu_gpio->name, index, cpu_gpio->gpio);
+               }
+       }
+}
+
+void lcd_cpu_gpio_set(unsigned int index, int value)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_cpu_gpio_s *cpu_gpio;
+
+       if (index >= LCD_CPU_GPIO_NUM_MAX) {
+               LCDERR("gpio index %d, exit\n", index);
+               return;
+       }
+       cpu_gpio = &lcd_drv->lcd_config->lcd_power->cpu_gpio[index];
+       if (cpu_gpio->flag == 0) {
+               LCDERR("gpio [%d] is not registered\n", index);
+               return;
+       }
+       if (IS_ERR(cpu_gpio->gpio)) {
+               LCDERR("gpio %s[%d]: %p, err: %ld\n",
+                       cpu_gpio->name, index, cpu_gpio->gpio,
+                       PTR_ERR(cpu_gpio->gpio));
+               return;
+       }
+
+       switch (value) {
+       case LCD_GPIO_OUTPUT_LOW:
+       case LCD_GPIO_OUTPUT_HIGH:
+               gpiod_direction_output(cpu_gpio->gpio, value);
+               break;
+       case LCD_GPIO_INPUT:
+       default:
+               gpiod_direction_input(cpu_gpio->gpio);
+               break;
+       }
+       if (lcd_debug_print_flag) {
+               LCDPR("set gpio %s[%d] value: %d\n",
+                       cpu_gpio->name, index, value);
+       }
+}
+
+unsigned int lcd_cpu_gpio_get(unsigned int index)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_cpu_gpio_s *cpu_gpio;
+
+       cpu_gpio = &lcd_drv->lcd_config->lcd_power->cpu_gpio[index];
+       if (cpu_gpio->flag == 0) {
+               LCDERR("gpio[%d] is not registered\n", index);
+               return -1;
+       }
+       if (IS_ERR(cpu_gpio->gpio)) {
+               LCDERR("gpio[%d]: %p, err: %ld\n",
+                       index, cpu_gpio->gpio, PTR_ERR(cpu_gpio->gpio));
+               return -1;
+       }
+
+       return gpiod_get_value(cpu_gpio->gpio);
+}
+
+const char *lcd_ttl_pinmux_str[] = {
+       "ttl_6bit_hvsync_on",      /* 0 */
+       "ttl_6bit_de_on",          /* 1 */
+       "ttl_6bit_hvsync_de_on",   /* 2 */
+       "ttl_6bit_hvsync_de_off",  /* 3 */
+       "ttl_8bit_hvsync_on",      /* 4 */
+       "ttl_8bit_de_on",          /* 5 */
+       "ttl_8bit_hvsync_de_on",   /* 6 */
+       "ttl_8bit_hvsync_de_off",  /* 7 */
+};
+
+void lcd_ttl_pinmux_set(int status)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       unsigned int index, num;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s: %d\n", __func__, status);
+
+       pconf = lcd_drv->lcd_config;
+       if (pconf->lcd_basic.lcd_bits == 6)
+               index = 0;
+       else
+               index = 4;
+
+       if (status) {
+               if (pconf->pinmux_flag == 0) {
+                       pconf->pinmux_flag = 1;
+                       switch (pconf->lcd_control.ttl_config->sync_valid) {
+                       case 1: /* hvsync */
+                               num = index + 0;
+                               break;
+                       case 2: /* DE */
+                               num = index + 1;
+                               break;
+                       default:
+                       case 3: /* DE + hvsync */
+                               num = index + 2;
+                               break;
+                       }
+               } else {
+                       LCDPR("ttl pinmux is already selected\n");
+                       return;
+               }
+       } else {
+               if (pconf->pinmux_flag) {
+                       pconf->pinmux_flag = 0;
+                       num = index + 3;
+               } else {
+                       LCDPR("ttl pinmux is already released\n");
+                       return;
+               }
+       }
+
+       /* request pinmux */
+       pconf->pin = devm_pinctrl_get_select(lcd_drv->dev,
+               lcd_ttl_pinmux_str[num]);
+       if (IS_ERR(pconf->pin))
+               LCDERR("set ttl pinmux error\n");
+}
+
+/* set VX1_LOCKN && VX1_HTPDN */
+void lcd_vbyone_pinmux_set(int status)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s: %d\n", __func__, status);
+
+#if 1
+       pconf = lcd_drv->lcd_config;
+       if (status) {
+               if (pconf->pinmux_flag == 0) {
+                       pconf->pinmux_flag = 1;
+                       /* request pinmux */
+                       pconf->pin = devm_pinctrl_get_select(lcd_drv->dev,
+                               "vbyone");
+                       if (IS_ERR(pconf->pin))
+                               LCDERR("set vbyone pinmux error\n");
+               } else {
+                       LCDPR("vbyone pinmux is already selected\n");
+               }
+       } else {
+               if (pconf->pinmux_flag) {
+                       pconf->pinmux_flag = 0;
+                       /* release pinmux */
+                       devm_pinctrl_put(pconf->pin);
+               } else {
+                       LCDPR("vbyone pinmux is already released\n");
+               }
+       }
+#else
+       if (status) {
+               lcd_pinmux_clr_mask(7,
+                       ((1 << 1) | (1 << 2) | (1 << 9) | (1 << 10)));
+               lcd_pinmux_set_mask(7, ((1 << 11) | (1 << 12)));
+       } else {
+               lcd_pinmux_clr_mask(7, ((1 << 11) | (1 << 12)));
+       }
+#endif
+}
+
+unsigned int lcd_lvds_channel_on_value(struct lcd_config_s *pconf)
+{
+       unsigned int channel_on = 0;
+
+       if (pconf->lcd_control.lvds_config->dual_port == 0) {
+               if (pconf->lcd_control.lvds_config->lane_reverse == 0) {
+                       switch (pconf->lcd_basic.lcd_bits) {
+                       case 6:
+                               channel_on = 0xf;
+                               break;
+                       case 8:
+                               channel_on = 0x1f;
+                               break;
+                       case 10:
+                       default:
+                               channel_on = 0x3f;
+                               break;
+                       }
+               } else {
+                       switch (pconf->lcd_basic.lcd_bits) {
+                       case 6:
+                               channel_on = 0x3c;
+                               break;
+                       case 8:
+                               channel_on = 0x3e;
+                               break;
+                       case 10:
+                       default:
+                               channel_on = 0x3f;
+                               break;
+                       }
+               }
+               if (pconf->lcd_control.lvds_config->port_swap == 1)
+                       channel_on = (channel_on << 6); /* use channel B */
+       } else {
+               if (pconf->lcd_control.lvds_config->lane_reverse == 0) {
+                       switch (pconf->lcd_basic.lcd_bits) {
+                       case 6:
+                               channel_on = 0x3cf;
+                               break;
+                       case 8:
+                               channel_on = 0x7df;
+                               break;
+                       case 10:
+                       default:
+                               channel_on = 0xfff;
+                               break;
+                       }
+               } else {
+                       switch (pconf->lcd_basic.lcd_bits) {
+                       case 6:
+                               channel_on = 0xf3c;
+                               break;
+                       case 8:
+                               channel_on = 0xfbe;
+                               break;
+                       case 10:
+                       default:
+                               channel_on = 0xfff;
+                               break;
+                       }
+               }
+       }
+       return channel_on;
+}
+
+int lcd_power_load_from_dts(struct lcd_config_s *pconf,
+               struct device_node *child)
+{
+       int ret = 0;
+       unsigned int para[5];
+       unsigned int val;
+       struct lcd_power_ctrl_s *lcd_power = pconf->lcd_power;
+       int i, j;
+       unsigned int index;
+
+       if (child == NULL) {
+               LCDPR("error: failed to get %s\n", pconf->lcd_propname);
+               return -1;
+       }
+
+       ret = of_property_read_u32_array(child, "power_on_step", &para[0], 4);
+       if (ret) {
+               LCDPR("failed to get power_on_step\n");
+               lcd_power->power_on_step[0].type = LCD_POWER_TYPE_MAX;
+       } else {
+               i = 0;
+               while (i < LCD_PWR_STEP_MAX) {
+                       lcd_power->power_on_step_max = i;
+                       j = 4 * i;
+                       ret = of_property_read_u32_index(child, "power_on_step",
+                               j, &val);
+                       lcd_power->power_on_step[i].type = (unsigned char)val;
+                       if (val == 0xff) /* ending */
+                               break;
+                       j = 4 * i + 1;
+                       ret = of_property_read_u32_index(child,
+                               "power_on_step", j, &val);
+                       lcd_power->power_on_step[i].index = val;
+                       j = 4 * i + 2;
+                       ret = of_property_read_u32_index(child,
+                               "power_on_step", j, &val);
+                       lcd_power->power_on_step[i].value = val;
+                       j = 4 * i + 3;
+                       ret = of_property_read_u32_index(child,
+                               "power_on_step", j, &val);
+                       lcd_power->power_on_step[i].delay = val;
+
+                       /* gpio request */
+                       switch (lcd_power->power_on_step[i].type) {
+                       case LCD_POWER_TYPE_CPU:
+                               index = lcd_power->power_on_step[i].index;
+                               if (index < LCD_CPU_GPIO_NUM_MAX)
+                                       lcd_cpu_gpio_register(index);
+                               break;
+                       default:
+                               break;
+                       }
+                       if (lcd_debug_print_flag) {
+                               LCDPR("power_on %d type: %d\n", i,
+                                       lcd_power->power_on_step[i].type);
+                               LCDPR("power_on %d index: %d\n", i,
+                                       lcd_power->power_on_step[i].index);
+                               LCDPR("power_on %d value: %d\n", i,
+                                       lcd_power->power_on_step[i].value);
+                               LCDPR("power_on %d delay: %d\n", i,
+                                       lcd_power->power_on_step[i].delay);
+                       }
+                       i++;
+               }
+       }
+
+       ret = of_property_read_u32_array(child, "power_off_step", &para[0], 4);
+       if (ret) {
+               LCDPR("failed to get power_off_step\n");
+               lcd_power->power_off_step[0].type = LCD_POWER_TYPE_MAX;
+       } else {
+               i = 0;
+               while (i < LCD_PWR_STEP_MAX) {
+                       lcd_power->power_off_step_max = i;
+                       j = 4 * i;
+                       ret = of_property_read_u32_index(child,
+                               "power_off_step", j, &val);
+                       lcd_power->power_off_step[i].type = (unsigned char)val;
+                       if (val == 0xff) /* ending */
+                               break;
+                       j = 4 * i + 1;
+                       ret = of_property_read_u32_index(child,
+                               "power_off_step", j, &val);
+                       lcd_power->power_off_step[i].index = val;
+                       j = 4 * i + 2;
+                       ret = of_property_read_u32_index(child,
+                               "power_off_step", j, &val);
+                       lcd_power->power_off_step[i].value = val;
+                       j = 4 * i + 3;
+                       ret = of_property_read_u32_index(child,
+                               "power_off_step", j, &val);
+                       lcd_power->power_off_step[i].delay = val;
+
+                       /* gpio request */
+                       switch (lcd_power->power_off_step[i].type) {
+                       case LCD_POWER_TYPE_CPU:
+                               index = lcd_power->power_off_step[i].index;
+                               if (index < LCD_CPU_GPIO_NUM_MAX)
+                                       lcd_cpu_gpio_register(index);
+                               break;
+                       default:
+                               break;
+                       }
+                       if (lcd_debug_print_flag) {
+                               LCDPR("power_off %d type: %d\n", i,
+                                       lcd_power->power_off_step[i].type);
+                               LCDPR("power_off %d index: %d\n", i,
+                                       lcd_power->power_off_step[i].index);
+                               LCDPR("power_off %d value: %d\n", i,
+                                       lcd_power->power_off_step[i].value);
+                               LCDPR("power_off %d delay: %d\n", i,
+                                       lcd_power->power_off_step[i].delay);
+                       }
+                       i++;
+               }
+       }
+
+       ret = of_property_read_u32(child, "backlight_index", &para[0]);
+       if (ret) {
+               LCDPR("failed to get backlight_index\n");
+               pconf->backlight_index = 0xff;
+       } else {
+               pconf->backlight_index = para[0];
+       }
+
+       return ret;
+}
+
+int lcd_power_load_from_unifykey(struct lcd_config_s *pconf,
+               unsigned char *buf, int key_len, int len)
+{
+       int i;
+       unsigned char *p;
+       unsigned int index;
+       int ret;
+
+       /* power: (5byte * n) */
+       p = buf + len;
+       if (lcd_debug_print_flag)
+               LCDPR("power_on step:\n");
+       i = 0;
+       while (i < LCD_PWR_STEP_MAX) {
+               pconf->lcd_power->power_on_step_max = i;
+               len += 5;
+               ret = lcd_unifykey_len_check(key_len, len);
+               if (ret < 0) {
+                       pconf->lcd_power->power_on_step[i].type = 0xff;
+                       pconf->lcd_power->power_on_step[i].index = 0;
+                       pconf->lcd_power->power_on_step[i].value = 0;
+                       pconf->lcd_power->power_on_step[i].delay = 0;
+                       LCDERR("unifykey power_on length is incorrect\n");
+                       return -1;
+               }
+               pconf->lcd_power->power_on_step[i].type = *p;
+               p += LCD_UKEY_PWR_TYPE;
+               pconf->lcd_power->power_on_step[i].index = *p;
+               p += LCD_UKEY_PWR_INDEX;
+               pconf->lcd_power->power_on_step[i].value = *p;
+               p += LCD_UKEY_PWR_VAL;
+               pconf->lcd_power->power_on_step[i].delay =
+                               (*p | ((*(p + 1)) << 8));
+               p += LCD_UKEY_PWR_DELAY;
+
+               /* gpio request */
+               switch (pconf->lcd_power->power_on_step[i].type) {
+               case LCD_POWER_TYPE_CPU:
+                       index = pconf->lcd_power->power_on_step[i].index;
+                       if (index < LCD_CPU_GPIO_NUM_MAX)
+                               lcd_cpu_gpio_register(index);
+                       break;
+               default:
+                       break;
+               }
+               if (lcd_debug_print_flag) {
+                       LCDPR("%d: type=%d, index=%d, value=%d, delay=%d\n",
+                               i, pconf->lcd_power->power_on_step[i].type,
+                               pconf->lcd_power->power_on_step[i].index,
+                               pconf->lcd_power->power_on_step[i].value,
+                               pconf->lcd_power->power_on_step[i].delay);
+               }
+               if (pconf->lcd_power->power_on_step[i].type <
+                       LCD_POWER_TYPE_MAX)
+                       i++;
+       }
+
+       if (lcd_debug_print_flag)
+               LCDPR("power_off step:\n");
+       i = 0;
+       while (i < LCD_PWR_STEP_MAX) {
+               pconf->lcd_power->power_off_step_max = i;
+               len += 5;
+               ret = lcd_unifykey_len_check(key_len, len);
+               if (ret < 0) {
+                       pconf->lcd_power->power_off_step[i].type = 0xff;
+                       pconf->lcd_power->power_off_step[i].index = 0;
+                       pconf->lcd_power->power_off_step[i].value = 0;
+                       pconf->lcd_power->power_off_step[i].delay = 0;
+                       LCDERR("unifykey power_off length is incorrect\n");
+                       return -1;
+               }
+               pconf->lcd_power->power_off_step[i].type = *p;
+               p += LCD_UKEY_PWR_TYPE;
+               pconf->lcd_power->power_off_step[i].index = *p;
+               p += LCD_UKEY_PWR_INDEX;
+               pconf->lcd_power->power_off_step[i].value = *p;
+               p += LCD_UKEY_PWR_VAL;
+               pconf->lcd_power->power_off_step[i].delay =
+                               (*p | ((*(p + 1)) << 8));
+               p += LCD_UKEY_PWR_DELAY;
+
+               /* gpio request */
+               switch (pconf->lcd_power->power_off_step[i].type) {
+               case LCD_POWER_TYPE_CPU:
+                       index = pconf->lcd_power->power_off_step[i].index;
+                       if (index < LCD_CPU_GPIO_NUM_MAX)
+                               lcd_cpu_gpio_register(index);
+                       break;
+               default:
+                       break;
+               }
+               if (lcd_debug_print_flag) {
+                       LCDPR("%d: type=%d, index=%d, value=%d, delay=%d\n",
+                               i, pconf->lcd_power->power_off_step[i].type,
+                               pconf->lcd_power->power_off_step[i].index,
+                               pconf->lcd_power->power_off_step[i].value,
+                               pconf->lcd_power->power_off_step[i].delay);
+               }
+               if (pconf->lcd_power->power_off_step[i].type <
+                       LCD_POWER_TYPE_MAX)
+                       i++;
+       }
+
+       return 0;
+}
+
+void lcd_hdr_vinfo_update(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       struct master_display_info_s *hdr_vinfo;
+
+       pconf = lcd_drv->lcd_config;
+       hdr_vinfo = &lcd_drv->lcd_info->master_display_info;
+       hdr_vinfo->present_flag = pconf->hdr_info.hdr_support;
+       hdr_vinfo->features = pconf->hdr_info.features;
+       hdr_vinfo->primaries[0][0] = pconf->hdr_info.primaries_g_x;
+       hdr_vinfo->primaries[0][1] = pconf->hdr_info.primaries_g_y;
+       hdr_vinfo->primaries[1][0] = pconf->hdr_info.primaries_b_x;
+       hdr_vinfo->primaries[1][1] = pconf->hdr_info.primaries_b_y;
+       hdr_vinfo->primaries[2][0] = pconf->hdr_info.primaries_r_x;
+       hdr_vinfo->primaries[2][1] = pconf->hdr_info.primaries_r_y;
+       hdr_vinfo->white_point[0] = pconf->hdr_info.white_point_x;
+       hdr_vinfo->white_point[1] = pconf->hdr_info.white_point_y;
+       hdr_vinfo->luminance[0] = pconf->hdr_info.luma_max;
+       hdr_vinfo->luminance[1] = pconf->hdr_info.luma_min;
+
+       lcd_drv->lcd_info->hdr_info.sink_flag = 1;
+       lcd_drv->lcd_info->hdr_info.lumi_max = pconf->hdr_info.luma_max;
+}
+
+void lcd_tcon_config(struct lcd_config_s *pconf)
+{
+       unsigned short h_period, v_period, h_active, v_active;
+       unsigned short hsync_bp, hsync_width, vsync_bp, vsync_width;
+       unsigned short de_hstart, de_vstart;
+       unsigned short hstart, hend, vstart, vend;
+       unsigned short h_delay;
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_TTL:
+               h_delay = TTL_DELAY;
+               break;
+       default:
+               h_delay = 0;
+               break;
+       }
+       /* use period_dft to avoid period changing offset */
+       h_period = pconf->lcd_timing.h_period_dft;
+       v_period = pconf->lcd_timing.v_period_dft;
+       h_active = pconf->lcd_basic.h_active;
+       v_active = pconf->lcd_basic.v_active;
+       hsync_bp = pconf->lcd_timing.hsync_bp;
+       hsync_width = pconf->lcd_timing.hsync_width;
+       vsync_bp = pconf->lcd_timing.vsync_bp;
+       vsync_width = pconf->lcd_timing.vsync_width;
+
+       de_hstart = h_period - h_active - 1;
+       de_vstart = v_period - v_active;
+
+       pconf->lcd_timing.video_on_pixel = de_hstart - h_delay;
+       pconf->lcd_timing.video_on_line = de_vstart;
+
+       pconf->lcd_timing.de_hs_addr = de_hstart;
+       pconf->lcd_timing.de_he_addr = de_hstart + h_active;
+       pconf->lcd_timing.de_vs_addr = de_vstart;
+       pconf->lcd_timing.de_ve_addr = de_vstart + v_active - 1;
+
+       hstart = (de_hstart + h_period - hsync_bp - hsync_width) % h_period;
+       hend = (de_hstart + h_period - hsync_bp) % h_period;
+       pconf->lcd_timing.hs_hs_addr = hstart;
+       pconf->lcd_timing.hs_he_addr = hend;
+       pconf->lcd_timing.hs_vs_addr = 0;
+       pconf->lcd_timing.hs_ve_addr = v_period - 1;
+
+       pconf->lcd_timing.vs_hs_addr = (hstart + h_period) % h_period;
+       pconf->lcd_timing.vs_he_addr = pconf->lcd_timing.vs_hs_addr;
+       vstart = (de_vstart + v_period - vsync_bp - vsync_width) % v_period;
+       vend = (de_vstart + v_period - vsync_bp) % v_period;
+       pconf->lcd_timing.vs_vs_addr = vstart;
+       pconf->lcd_timing.vs_ve_addr = vend;
+
+       if (lcd_debug_print_flag) {
+               LCDPR("hs_hs_addr=%d, hs_he_addr=%d\n"
+               "hs_vs_addr=%d, hs_ve_addr=%d\n"
+               "vs_hs_addr=%d, vs_he_addr=%d\n"
+               "vs_vs_addr=%d, vs_ve_addr=%d\n",
+               pconf->lcd_timing.hs_hs_addr, pconf->lcd_timing.hs_he_addr,
+               pconf->lcd_timing.hs_vs_addr, pconf->lcd_timing.hs_ve_addr,
+               pconf->lcd_timing.vs_hs_addr, pconf->lcd_timing.vs_he_addr,
+               pconf->lcd_timing.vs_vs_addr, pconf->lcd_timing.vs_ve_addr);
+       }
+}
+
+#if 0
+/* change frame_rate for different vmode */
+int lcd_vmode_change(struct lcd_config_s *pconf)
+{
+       unsigned int pclk = pconf->lcd_timing.lcd_clk_dft; /* avoid offset */
+       unsigned char type = pconf->lcd_timing.fr_adjust_type;
+       unsigned int h_period = pconf->lcd_basic.h_period;
+       unsigned int v_period = pconf->lcd_basic.v_period;
+       unsigned int sync_duration_num = pconf->lcd_timing.sync_duration_num;
+       unsigned int sync_duration_den = pconf->lcd_timing.sync_duration_den;
+
+       /* frame rate adjust */
+       switch (type) {
+       case 1: /* htotal adjust */
+               h_period = ((pclk / v_period) * sync_duration_den * 10) /
+                               sync_duration_num;
+               h_period = (h_period + 5) / 10; /* round off */
+               if (pconf->lcd_basic.h_period != h_period) {
+                       LCDPR("%s: adjust h_period %u -> %u\n",
+                               __func__, pconf->lcd_basic.h_period, h_period);
+                       pconf->lcd_basic.h_period = h_period;
+                       /* check clk frac update */
+                       pclk = (h_period * v_period) / sync_duration_den *
+                               sync_duration_num;
+                       if (pconf->lcd_timing.lcd_clk != pclk)
+                               pconf->lcd_timing.lcd_clk = pclk;
+               }
+               break;
+       case 2: /* vtotal adjust */
+               v_period = ((pclk / h_period) * sync_duration_den * 10) /
+                               sync_duration_num;
+               v_period = (v_period + 5) / 10; /* round off */
+               if (pconf->lcd_basic.v_period != v_period) {
+                       LCDPR("%s: adjust v_period %u -> %u\n",
+                               __func__, pconf->lcd_basic.v_period, v_period);
+                       pconf->lcd_basic.v_period = v_period;
+                       /* check clk frac update */
+                       pclk = (h_period * v_period) / sync_duration_den *
+                               sync_duration_num;
+                       if (pconf->lcd_timing.lcd_clk != pclk)
+                               pconf->lcd_timing.lcd_clk = pclk;
+               }
+               break;
+       case 0: /* pixel clk adjust */
+       default:
+               pclk = (h_period * v_period) / sync_duration_den *
+                       sync_duration_num;
+               if (pconf->lcd_timing.lcd_clk != pclk) {
+                       LCDPR("%s: adjust pclk %u.%03uMHz -> %u.%03uMHz\n",
+                               __func__, (pconf->lcd_timing.lcd_clk / 1000000),
+                               ((pconf->lcd_timing.lcd_clk / 1000) % 1000),
+                               (pclk / 1000000), ((pclk / 1000) % 1000));
+                       pconf->lcd_timing.lcd_clk = pclk;
+               }
+               break;
+       }
+
+       return 0;
+}
+#else
+int lcd_vmode_change(struct lcd_config_s *pconf)
+{
+       unsigned char type = pconf->lcd_timing.fr_adjust_type;
+        /* use default value to avoid offset */
+       unsigned int pclk = pconf->lcd_timing.lcd_clk_dft;
+       unsigned int h_period = pconf->lcd_timing.h_period_dft;
+       unsigned int v_period = pconf->lcd_timing.v_period_dft;
+       unsigned int pclk_min = pconf->lcd_basic.lcd_clk_min;
+       unsigned int pclk_max = pconf->lcd_basic.lcd_clk_max;
+       unsigned int duration_num = pconf->lcd_timing.sync_duration_num;
+       unsigned int duration_den = pconf->lcd_timing.sync_duration_den;
+       char str[100];
+       int len = 0;
+
+       pconf->lcd_timing.clk_change = 0; /* clear clk flga */
+       switch (type) {
+       case 0: /* pixel clk adjust */
+               pclk = (h_period * v_period) / duration_den * duration_num;
+               if (pconf->lcd_timing.lcd_clk != pclk)
+                       pconf->lcd_timing.clk_change = LCD_CLK_PLL_CHANGE;
+               break;
+       case 1: /* htotal adjust */
+               h_period = ((pclk / v_period) * duration_den * 10) /
+                               duration_num;
+               h_period = (h_period + 5) / 10; /* round off */
+               if (pconf->lcd_basic.h_period != h_period) {
+                       /* check clk frac update */
+                       pclk = (h_period * v_period) / duration_den *
+                               duration_num;
+                       if (pconf->lcd_timing.lcd_clk != pclk) {
+                               pconf->lcd_timing.clk_change =
+                                       LCD_CLK_FRAC_UPDATE;
+                       }
+               }
+               break;
+       case 2: /* vtotal adjust */
+               v_period = ((pclk / h_period) * duration_den * 10) /
+                               duration_num;
+               v_period = (v_period + 5) / 10; /* round off */
+               if (pconf->lcd_basic.v_period != v_period) {
+                       /* check clk frac update */
+                       pclk = (h_period * v_period) / duration_den *
+                               duration_num;
+                       if (pconf->lcd_timing.lcd_clk != pclk) {
+                               pconf->lcd_timing.clk_change =
+                                       LCD_CLK_FRAC_UPDATE;
+                       }
+               }
+               break;
+       case 3: /* free adjust, use min/max range to calculate */
+       default:
+               v_period = ((pclk / h_period) * duration_den * 10) /
+                       duration_num;
+               v_period = (v_period + 5) / 10; /* round off */
+               if (v_period > pconf->lcd_basic.v_period_max) {
+                       v_period = pconf->lcd_basic.v_period_max;
+                       h_period = ((pclk / v_period) * duration_den * 10) /
+                               duration_num;
+                       h_period = (h_period + 5) / 10; /* round off */
+                       if (h_period > pconf->lcd_basic.h_period_max) {
+                               h_period = pconf->lcd_basic.h_period_max;
+                               pclk = (h_period * v_period) / duration_den *
+                                       duration_num;
+                               if (pconf->lcd_timing.lcd_clk != pclk) {
+                                       if (pclk > pclk_max) {
+                                               pclk = pclk_max;
+                                               LCDERR("invalid vmode\n");
+                                               return -1;
+                                       }
+                                       pconf->lcd_timing.clk_change =
+                                               LCD_CLK_PLL_CHANGE;
+                               }
+                       }
+               } else if (v_period < pconf->lcd_basic.v_period_min) {
+                       v_period = pconf->lcd_basic.v_period_min;
+                       h_period = ((pclk / v_period) * duration_den * 10) /
+                               duration_num;
+                       h_period = (h_period + 5) / 10; /* round off */
+                       if (h_period < pconf->lcd_basic.h_period_min) {
+                               h_period = pconf->lcd_basic.h_period_min;
+                               pclk = (h_period * v_period) / duration_den *
+                                       duration_num;
+                               if (pconf->lcd_timing.lcd_clk != pclk) {
+                                       if (pclk < pclk_min) {
+                                               pclk = pclk_min;
+                                               LCDERR("invalid vmode\n");
+                                               return -1;
+                                       }
+                                       pconf->lcd_timing.clk_change =
+                                               LCD_CLK_PLL_CHANGE;
+                               }
+                       }
+               }
+               /* check clk frac update */
+               if ((pconf->lcd_timing.clk_change & LCD_CLK_PLL_CHANGE) == 0) {
+                       pclk = (h_period * v_period) / duration_den *
+                               duration_num;
+                       if (pconf->lcd_timing.lcd_clk != pclk) {
+                               pconf->lcd_timing.clk_change =
+                                       LCD_CLK_FRAC_UPDATE;
+                       }
+               }
+               break;
+       }
+
+       if (pconf->lcd_basic.v_period != v_period) {
+               len += sprintf(str+len, "v_period %u->%u",
+                       pconf->lcd_basic.v_period, v_period);
+               /* update v_period */
+               pconf->lcd_basic.v_period = v_period;
+       }
+       if (pconf->lcd_basic.h_period != h_period) {
+               if (len > 0)
+                       len += sprintf(str+len, ", ");
+               len += sprintf(str+len, "h_period %u->%u",
+                       pconf->lcd_basic.h_period, h_period);
+               /* update h_period */
+               pconf->lcd_basic.h_period = h_period;
+       }
+       if (pconf->lcd_timing.lcd_clk != pclk) {
+               if (len > 0)
+                       len += sprintf(str+len, ", ");
+               len += sprintf(str+len, "pclk %u.%03uMHz->%u.%03uMHz",
+                       (pconf->lcd_timing.lcd_clk / 1000000),
+                       ((pconf->lcd_timing.lcd_clk / 1000) % 1000),
+                       (pclk / 1000000), ((pclk / 1000) % 1000));
+               pconf->lcd_timing.lcd_clk = pclk;
+       }
+       if (lcd_debug_print_flag) {
+               if (len > 0)
+                       LCDPR("%s: %s\n", __func__, str);
+       }
+
+       return 0;
+}
+#endif
+
+void lcd_venc_change(struct lcd_config_s *pconf)
+{
+       unsigned int htotal, vtotal;
+
+       htotal = lcd_vcbus_read(ENCL_VIDEO_MAX_PXCNT) + 1;
+       vtotal = lcd_vcbus_read(ENCL_VIDEO_MAX_LNCNT) + 1;
+       if (pconf->lcd_basic.h_period != htotal) {
+               lcd_vcbus_write(ENCL_VIDEO_MAX_PXCNT,
+                       pconf->lcd_basic.h_period - 1);
+       }
+       if (pconf->lcd_basic.v_period != vtotal) {
+               lcd_vcbus_write(ENCL_VIDEO_MAX_LNCNT,
+                       pconf->lcd_basic.v_period - 1);
+       }
+       if (lcd_debug_print_flag) {
+               LCDPR("venc changed: %d,%d\n",
+                       pconf->lcd_basic.h_period,
+                       pconf->lcd_basic.v_period);
+       }
+
+       if (pconf->lcd_basic.v_period != vtotal)
+               aml_lcd_notifier_call_chain(LCD_EVENT_BACKLIGHT_UPDATE, NULL);
+}
+
+void lcd_clk_gate_switch(int status)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_AXG:
+               break;
+       default:
+               if (status) {
+                       if (IS_ERR(lcd_drv->vencl_top))
+                               LCDERR("%s: vencl_top\n", __func__);
+                       else
+                               clk_prepare_enable(lcd_drv->vencl_top);
+
+                       if (IS_ERR(lcd_drv->vencl_int))
+                               LCDERR("%s: vencl_int\n", __func__);
+                       else
+                               clk_prepare_enable(lcd_drv->vencl_int);
+               } else {
+                       if (IS_ERR(lcd_drv->vencl_int))
+                               LCDERR("%s: vencl_int\n", __func__);
+                       else
+                               clk_disable_unprepare(lcd_drv->vencl_int);
+
+                       if (IS_ERR(lcd_drv->vencl_top))
+                               LCDERR("%s: vencl_top\n", __func__);
+                       else
+                               clk_disable_unprepare(lcd_drv->vencl_top);
+               }
+               break;
+       }
+}
+
+void lcd_clktree_probe(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_AXG:
+               lcd_drv->dsi_host = devm_clk_get(lcd_drv->dev, "dsi_host");
+               if (IS_ERR(lcd_drv->dsi_host))
+                       LCDERR("%s: clk dsi_host\n", __func__);
+               else
+                       clk_prepare_enable(lcd_drv->dsi_host);
+
+               lcd_drv->dsi_phy = devm_clk_get(lcd_drv->dev, "dsi_phy");
+               if (IS_ERR(lcd_drv->dsi_phy))
+                       LCDERR("%s: clk dsi_phy\n", __func__);
+               else
+                       clk_prepare_enable(lcd_drv->dsi_phy);
+
+               lcd_drv->dsi_meas = devm_clk_get(lcd_drv->dev, "dsi_meas");
+               if (IS_ERR(lcd_drv->dsi_meas))
+                       LCDERR("%s: clk dsi_meas\n", __func__);
+               else
+                       clk_prepare_enable(lcd_drv->dsi_meas);
+               break;
+       default:
+               lcd_drv->vencl_top = devm_clk_get(lcd_drv->dev, "vencl_top");
+               if (IS_ERR(lcd_drv->vencl_top))
+                       LCDERR("%s: clk vencl_top\n", __func__);
+               else
+                       clk_prepare_enable(lcd_drv->vencl_top);
+
+               lcd_drv->vencl_int = devm_clk_get(lcd_drv->dev, "vencl_int");
+               if (IS_ERR(lcd_drv->vencl_int))
+                       LCDERR("%s: clk vencl_int\n", __func__);
+               else
+                       clk_prepare_enable(lcd_drv->vencl_int);
+               break;
+       }
+
+       LCDPR("%s\n", __func__);
+}
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_common.h b/drivers/amlogic/media/vout/lcd/lcd_common.h
new file mode 100644 (file)
index 0000000..08ae6af
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_common.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __AML_LCD_COMMON_H__
+#define __AML_LCD_COMMON_H__
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include "lcd_clk_config.h"
+
+/* 20170505: add a113 support to linux4.9 */
+#define LCD_DRV_VERSION    "20170505"
+
+#define VPP_OUT_SATURATE            (1 << 0)
+
+extern struct mutex lcd_power_mutex;
+extern struct mutex lcd_vout_mutex;
+extern unsigned char lcd_resume_flag;
+extern int lcd_vout_serve_bypass;
+
+/* lcd common */
+extern int lcd_type_str_to_type(const char *str);
+extern char *lcd_type_type_to_str(int type);
+extern unsigned char lcd_mode_str_to_mode(const char *str);
+extern char *lcd_mode_mode_to_str(int mode);
+
+extern void lcd_cpu_gpio_register(unsigned int index);
+extern void lcd_cpu_gpio_set(unsigned int index, int value);
+extern unsigned int lcd_cpu_gpio_get(unsigned int index);
+extern void lcd_ttl_pinmux_set(int status);
+extern void lcd_vbyone_pinmux_set(int status);
+extern unsigned int lcd_lvds_channel_on_value(struct lcd_config_s *pconf);
+extern int lcd_power_load_from_dts(struct lcd_config_s *pconf,
+               struct device_node *child);
+extern int lcd_power_load_from_unifykey(struct lcd_config_s *pconf,
+               unsigned char *buf, int key_len, int len);
+
+extern void lcd_hdr_vinfo_update(void);
+extern void lcd_tcon_config(struct lcd_config_s *pconf);
+extern int lcd_vmode_change(struct lcd_config_s *pconf);
+extern void lcd_venc_change(struct lcd_config_s *pconf);
+extern void lcd_clk_gate_switch(int status);
+extern void lcd_clktree_probe(void);
+
+/* lcd debug */
+extern int lcd_class_creat(void);
+extern int lcd_class_remove(void);
+
+/* lcd driver */
+#ifdef CONFIG_AMLOGIC_LCD_TV
+extern void lcd_vbyone_interrupt_enable(int flag);
+extern void lcd_tv_clk_update(struct lcd_config_s *pconf);
+extern void lcd_tv_vout_server_init(void);
+extern int lcd_tv_probe(struct device *dev);
+extern int lcd_tv_remove(struct device *dev);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_TABLET
+extern void lcd_tablet_clk_update(struct lcd_config_s *pconf);
+extern void lcd_tablet_vout_server_init(void);
+extern int lcd_tablet_probe(struct device *dev);
+extern int lcd_tablet_remove(struct device *dev);
+#endif
+
+#endif
diff --git a/drivers/amlogic/media/vout/lcd/lcd_debug.c b/drivers/amlogic/media/vout/lcd/lcd_debug.c
new file mode 100644 (file)
index 0000000..99a9c76
--- /dev/null
@@ -0,0 +1,2067 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_debug.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#include "lcd_reg.h"
+#include "lcd_common.h"
+#ifdef CONFIG_AMLOGIC_LCD_TABLET
+#include "lcd_tablet/mipi_dsi_util.h"
+#endif
+
+
+static const char *lcd_common_usage_str = {
+"Usage:\n"
+"    echo <0|1> > enable ; 0=disable lcd; 1=enable lcd\n"
+"\n"
+"    echo type <adj_type> > frame_rate ; set lcd frame rate adjust type\n"
+"    echo set <frame_rate> > frame_rate ; set lcd frame rate(unit in 1/100Hz)\n"
+"    cat frame_rate ; read current lcd frame rate\n"
+"\n"
+"    echo <num> > test ; show lcd bist pattern(1~7), 0=disable bist\n"
+"\n"
+"    echo w<v|h|c|p> <reg> <data> > reg ; write data to vcbus|hiu|cbus|periphs reg\n"
+"    echo r<v|h|c|p> <reg> > reg ; read vcbus|hiu|cbus|periphs reg\n"
+"    echo d<v|h|c|p> <reg> <num> > reg ; dump vcbus|hiu|cbus|periphs regs\n"
+"\n"
+"    echo <0|1> > print ; 0=disable debug print; 1=enable debug print\n"
+"    cat print ; read current debug print flag\n"
+"\n"
+"    echo <cmd> ... > debug ; lcd common debug, use 'cat debug' for help\n"
+"    cat debug ; print help information for debug command\n"
+"\n"
+"    echo <on|off> <step_num> <delay> > power ; set power on/off step delay(unit: ms)\n"
+"    cat power ; print lcd power on/off step\n"
+"\n"
+"    cat key_valid ; print lcd_key_valid setting\n"
+"    cat config_load ; print lcd_config load_id(0=dts, 1=unifykey)\n"
+};
+
+static const char *lcd_debug_usage_str = {
+"Usage:\n"
+"    echo clk <freq> > debug ; set lcd pixel clock, unit in Hz\n"
+"    echo bit <lcd_bits> > debug ; set lcd bits\n"
+"    echo basic <h_active> <v_active> <h_period> <v_period> <lcd_bits> > debug ; set lcd basic config\n"
+"    echo sync <hs_width> <hs_bp> <hs_pol> <vs_width> <vs_bp> <vs_pol> > debug ; set lcd sync timing\n"
+"data format:\n"
+"    <xx_pol>       : 0=negative, 1=positive\n"
+"\n"
+"    echo info > debug ; show lcd information\n"
+"    echo reg > debug ; show lcd registers\n"
+"    echo dump > debug ; show lcd information & registers\n"
+"       echo dith <dither_en> <rounding_en> <dither_md>  > debug ; set vpu_vencl_dith_ctrl\n"
+"    echo key > debug ; show lcd_key_valid config, and lcd unifykey raw data\n"
+"\n"
+"    echo reset > debug; reset lcd driver\n"
+"    echo power <0|1> > debug ; lcd power control: 0=power off, 1=power on\n"
+};
+
+static ssize_t lcd_debug_common_help(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%s\n", lcd_common_usage_str);
+}
+
+static ssize_t lcd_debug_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%s\n", lcd_debug_usage_str);
+}
+
+static void lcd_cpu_gpio_register_print(struct lcd_config_s *pconf)
+{
+       int i;
+       struct lcd_cpu_gpio_s *cpu_gpio;
+
+       pr_info("cpu_gpio register:\n");
+
+       i = 0;
+       while (i < LCD_CPU_GPIO_NUM_MAX) {
+               cpu_gpio = &pconf->lcd_power->cpu_gpio[i];
+               if (cpu_gpio->flag) {
+                       pr_info("%d: name=%s, gpio=%p\n",
+                               i, cpu_gpio->name, cpu_gpio->gpio);
+               }
+               i++;
+       }
+}
+
+static void lcd_power_info_print(struct lcd_config_s *pconf, int status)
+{
+       int i;
+       struct lcd_power_step_s *power_step;
+
+       if (status)
+               pr_info("power on step:\n");
+       else
+               pr_info("power off step:\n");
+
+       i = 0;
+       while (i < LCD_PWR_STEP_MAX) {
+               if (status)
+                       power_step = &pconf->lcd_power->power_on_step[i];
+               else
+                       power_step = &pconf->lcd_power->power_off_step[i];
+
+               if (power_step->type >= LCD_POWER_TYPE_MAX)
+                       break;
+               switch (power_step->type) {
+               case LCD_POWER_TYPE_CPU:
+               case LCD_POWER_TYPE_PMU:
+                       pr_info("%d: type=%d, index=%d, value=%d, delay=%d\n",
+                               i, power_step->type, power_step->index,
+                               power_step->value, power_step->delay);
+                       break;
+               case LCD_POWER_TYPE_EXTERN:
+                       pr_info("%d: type=%d, index=%d, delay=%d\n",
+                               i, power_step->type, power_step->index,
+                               power_step->delay);
+                       break;
+               case LCD_POWER_TYPE_SIGNAL:
+                       pr_info("%d: type=%d, delay=%d\n",
+                               i, power_step->type, power_step->delay);
+                       break;
+               default:
+                       break;
+               }
+               i++;
+       }
+}
+
+static void lcd_info_print(void)
+{
+       unsigned int lcd_clk, sync_duration;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+
+       pconf = lcd_drv->lcd_config;
+       lcd_clk = (pconf->lcd_timing.lcd_clk / 1000);
+       sync_duration = pconf->lcd_timing.sync_duration_num * 100;
+       sync_duration = sync_duration / pconf->lcd_timing.sync_duration_den;
+
+       LCDPR("panel_type: %s\n", pconf->lcd_propname);
+       LCDPR("key_valid: %d, config_load: %d\n",
+               lcd_drv->lcd_key_valid, lcd_drv->lcd_config_load);
+       LCDPR("chip: %d, mode : %s, status: %d, fr_auto_policy: %d\n",
+               lcd_drv->chip_type, lcd_mode_mode_to_str(lcd_drv->lcd_mode),
+               lcd_drv->lcd_status, lcd_drv->fr_auto_policy);
+
+       LCDPR("%s, %s %ubit, %ux%u@%u.%02uHz\n",
+               pconf->lcd_basic.model_name,
+               lcd_type_type_to_str(pconf->lcd_basic.lcd_type),
+               pconf->lcd_basic.lcd_bits,
+               pconf->lcd_basic.h_active, pconf->lcd_basic.v_active,
+               (sync_duration / 100), (sync_duration % 100));
+
+       pr_info("lcd_clk           %u.%03uMHz\n"
+               "ss_level          %d\n"
+               "clk_auto          %d\n"
+               "fr_adj_type       %d\n\n",
+               (lcd_clk / 1000), (lcd_clk % 1000),
+               pconf->lcd_timing.ss_level, pconf->lcd_timing.clk_auto,
+               pconf->lcd_timing.fr_adjust_type);
+
+       pr_info("h_period          %d\n"
+               "v_period          %d\n"
+               "hs_width          %d\n"
+               "hs_backporch      %d\n"
+               "hs_pol            %d\n"
+               "vs_width          %d\n"
+               "vs_backporch      %d\n"
+               "vs_pol            %d\n\n",
+               pconf->lcd_basic.h_period, pconf->lcd_basic.v_period,
+               pconf->lcd_timing.hsync_width, pconf->lcd_timing.hsync_bp,
+               pconf->lcd_timing.hsync_pol,
+               pconf->lcd_timing.vsync_width, pconf->lcd_timing.vsync_bp,
+               pconf->lcd_timing.vsync_pol);
+       pr_info("h_period_min      %d\n"
+               "h_period_max      %d\n"
+               "v_period_min      %d\n"
+               "v_period_max      %d\n"
+               "pclk_min          %d\n"
+               "pclk_max          %d\n\n",
+               pconf->lcd_basic.h_period_min, pconf->lcd_basic.h_period_max,
+               pconf->lcd_basic.v_period_min, pconf->lcd_basic.v_period_max,
+               pconf->lcd_basic.lcd_clk_min, pconf->lcd_basic.lcd_clk_max);
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_TTL:
+               pr_info("clk_pol           %u\n"
+                       "hvsync_valid      %u\n"
+                       "de_valid          %u\n"
+                       "rb_swap           %u\n"
+                       "bit_swap          %u\n\n",
+                       pconf->lcd_control.ttl_config->clk_pol,
+                       ((pconf->lcd_control.ttl_config->sync_valid >> 0) & 1),
+                       ((pconf->lcd_control.ttl_config->sync_valid >> 1) & 1),
+                       ((pconf->lcd_control.ttl_config->swap_ctrl >> 1) & 1),
+                       ((pconf->lcd_control.ttl_config->swap_ctrl >> 0) & 1));
+               break;
+       case LCD_LVDS:
+               pr_info("lvds_repack       %u\n"
+                       "dual_port         %u\n"
+                       "pn_swap           %u\n"
+                       "port_swap         %u\n"
+                       "lane_reverse      %u\n"
+                       "phy_vswing        0x%x\n"
+                       "phy_preem         0x%x\n"
+                       "phy_clk_vswing    0x%x\n"
+                       "phy_clk_preem     0x%x\n\n",
+                       pconf->lcd_control.lvds_config->lvds_repack,
+                       pconf->lcd_control.lvds_config->dual_port,
+                       pconf->lcd_control.lvds_config->pn_swap,
+                       pconf->lcd_control.lvds_config->port_swap,
+                       pconf->lcd_control.lvds_config->lane_reverse,
+                       pconf->lcd_control.lvds_config->phy_vswing,
+                       pconf->lcd_control.lvds_config->phy_preem,
+                       pconf->lcd_control.lvds_config->phy_clk_vswing,
+                       pconf->lcd_control.lvds_config->phy_clk_preem);
+               break;
+       case LCD_VBYONE:
+               pr_info("lane_count        %u\n"
+                       "region_num        %u\n"
+                       "byte_mode         %u\n"
+                       "color_fmt         %u\n"
+                       "bit_rate          %u\n"
+                       "phy_vswing        0x%x\n"
+                       "phy_preemphasis   0x%x\n"
+                       "intr_en           %u\n"
+                       "vsync_intr_en     %u\n\n",
+                       pconf->lcd_control.vbyone_config->lane_count,
+                       pconf->lcd_control.vbyone_config->region_num,
+                       pconf->lcd_control.vbyone_config->byte_mode,
+                       pconf->lcd_control.vbyone_config->color_fmt,
+                       pconf->lcd_control.vbyone_config->bit_rate,
+                       pconf->lcd_control.vbyone_config->phy_vswing,
+                       pconf->lcd_control.vbyone_config->phy_preem,
+                       pconf->lcd_control.vbyone_config->intr_en,
+                       pconf->lcd_control.vbyone_config->vsync_intr_en);
+               break;
+       case LCD_MIPI:
+#ifdef CONFIG_AMLOGIC_LCD_TABLET
+               mipi_dsi_print_info(pconf);
+#endif
+               break;
+       default:
+               break;
+       }
+
+       pr_info("pll_ctrl          0x%08x\n"
+               "div_ctrl          0x%08x\n"
+               "clk_ctrl          0x%08x\n"
+               "video_on_pixel    %d\n"
+               "video_on_line     %d\n\n",
+               pconf->lcd_timing.pll_ctrl, pconf->lcd_timing.div_ctrl,
+               pconf->lcd_timing.clk_ctrl,
+               pconf->lcd_timing.video_on_pixel,
+               pconf->lcd_timing.video_on_line);
+
+       lcd_power_info_print(pconf, 1);
+       lcd_power_info_print(pconf, 0);
+       lcd_cpu_gpio_register_print(pconf);
+}
+
+static unsigned int lcd_reg_dump_clk[] = {
+       HHI_HDMI_PLL_CNTL,
+       HHI_HDMI_PLL_CNTL2,
+       HHI_HDMI_PLL_CNTL3,
+       HHI_HDMI_PLL_CNTL4,
+       HHI_HDMI_PLL_CNTL5,
+       HHI_HDMI_PLL_CNTL6,
+       HHI_VID_PLL_CLK_DIV,
+       HHI_VIID_CLK_DIV,
+       HHI_VIID_CLK_CNTL,
+       HHI_VID_CLK_CNTL2,
+};
+
+static unsigned int lcd_reg_dump_clk_axg[] = {
+       HHI_GP0_PLL_CNTL,
+       HHI_GP0_PLL_CNTL2,
+       HHI_GP0_PLL_CNTL3,
+       HHI_GP0_PLL_CNTL4,
+       HHI_GP0_PLL_CNTL5,
+       HHI_GP0_PLL_CNTL1,
+       HHI_VID_PLL_CLK_DIV,
+       HHI_VIID_CLK_DIV,
+       HHI_VIID_CLK_CNTL,
+       HHI_VID_CLK_CNTL2,
+};
+
+static unsigned int lcd_reg_dump_encl[] = {
+       VPU_VIU_VENC_MUX_CTRL,
+       ENCL_VIDEO_EN,
+       ENCL_VIDEO_MODE,
+       ENCL_VIDEO_MODE_ADV,
+       ENCL_VIDEO_MAX_PXCNT,
+       ENCL_VIDEO_MAX_LNCNT,
+       ENCL_VIDEO_HAVON_BEGIN,
+       ENCL_VIDEO_HAVON_END,
+       ENCL_VIDEO_VAVON_BLINE,
+       ENCL_VIDEO_VAVON_ELINE,
+       ENCL_VIDEO_HSO_BEGIN,
+       ENCL_VIDEO_HSO_END,
+       ENCL_VIDEO_VSO_BEGIN,
+       ENCL_VIDEO_VSO_END,
+       ENCL_VIDEO_VSO_BLINE,
+       ENCL_VIDEO_VSO_ELINE,
+       ENCL_VIDEO_RGBIN_CTRL,
+       L_GAMMA_CNTL_PORT,
+       L_RGB_BASE_ADDR,
+       L_RGB_COEFF_ADDR,
+       L_POL_CNTL_ADDR,
+       L_DITH_CNTL_ADDR,
+};
+
+static void lcd_ttl_reg_print(void)
+{
+       unsigned int reg;
+
+       pr_info("\nttl registers:\n");
+       reg = L_DUAL_PORT_CNTL_ADDR;
+       pr_info("PORT_CNTL           [0x%04x] = 0x%08x\n",
+               reg, lcd_vcbus_read(reg));
+       reg = L_STH1_HS_ADDR;
+       pr_info("STH1_HS_ADDR        [0x%04x] = 0x%08x\n",
+               reg, lcd_vcbus_read(reg));
+       reg = L_STH1_HE_ADDR;
+       pr_info("STH1_HE_ADDR        [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+       reg = L_STH1_VS_ADDR;
+       pr_info("STH1_VS_ADDR        [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+       reg = L_STH1_VE_ADDR;
+       pr_info("STH1_VE_ADDR        [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+       reg = L_STV1_HS_ADDR;
+       pr_info("STV1_HS_ADDR        [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+       reg = L_STV1_HE_ADDR;
+       pr_info("STV1_HE_ADDR        [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+       reg = L_STV1_VS_ADDR;
+       pr_info("STV1_VS_ADDR        [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+       reg = L_STV1_VE_ADDR;
+       pr_info("STV1_VE_ADDR        [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+       reg = L_OEH_HS_ADDR;
+       pr_info("OEH_HS_ADDR         [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+       reg = L_OEH_HE_ADDR;
+       pr_info("OEH_HE_ADDR         [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+       reg = L_OEH_VS_ADDR;
+       pr_info("OEH_VS_ADDR         [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+       reg = L_OEH_VE_ADDR;
+       pr_info("OEH_VE_ADDR         [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+}
+
+static void lcd_lvds_reg_print(void)
+{
+       unsigned int reg;
+
+       pr_info("\nlvds registers:\n");
+       reg = LVDS_PACK_CNTL_ADDR;
+       pr_info("LVDS_PACK_CNTL      [0x%04x] = 0x%08x\n",
+               reg, lcd_vcbus_read(reg));
+       reg = LVDS_GEN_CNTL;
+       pr_info("LVDS_GEN_CNTL       [0x%04x] = 0x%08x\n",
+               reg, lcd_vcbus_read(reg));
+       reg = LCD_PORT_SWAP;
+       pr_info("LCD_PORT_SWAP       [0x%04x] = 0x%08x\n",
+               reg, lcd_vcbus_read(reg));
+       reg = HHI_LVDS_TX_PHY_CNTL0;
+       pr_info("LVDS_PHY_CNTL0      [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+       reg = HHI_LVDS_TX_PHY_CNTL1;
+       pr_info("LVDS_PHY_CNTL1      [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+}
+
+static void lcd_vbyone_reg_print(void)
+{
+       unsigned int reg;
+
+       pr_info("\nvbyone registers:\n");
+       reg = PERIPHS_PIN_MUX_7;
+       pr_info("VX1_PINMUX          [0x%04x] = 0x%08x\n",
+               reg, lcd_periphs_read(reg));
+       reg = VBO_STATUS_L;
+       pr_info("VX1_STATUS          [0x%04x] = 0x%08x\n",
+               reg, lcd_vcbus_read(reg));
+       reg = VBO_FSM_HOLDER_L;
+       pr_info("VX1_FSM_HOLDER_L    [0x%04x] = 0x%08x\n",
+               reg, lcd_vcbus_read(reg));
+       reg = VBO_FSM_HOLDER_H;
+       pr_info("VX1_FSM_HOLDER_H    [0x%04x] = 0x%08x\n",
+               reg, lcd_vcbus_read(reg));
+       reg = VBO_INTR_STATE_CTRL;
+       pr_info("VX1_INTR_STATE_CTRL [0x%04x] = 0x%08x\n",
+               reg, lcd_vcbus_read(reg));
+       reg = VBO_INTR_UNMASK;
+       pr_info("VX1_INTR_UNMASK     [0x%04x] = 0x%08x\n",
+               reg, lcd_vcbus_read(reg));
+       reg = VBO_INTR_STATE;
+       pr_info("VX1_INTR_STATE      [0x%04x] = 0x%08x\n",
+               reg, lcd_vcbus_read(reg));
+       reg = HHI_LVDS_TX_PHY_CNTL0;
+       pr_info("VX1_PHY_CNTL0       [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+}
+
+static void lcd_mipi_reg_print(void)
+{
+       unsigned int reg;
+
+       pr_info("\nmipi_dsi_host registers:\n");
+       reg = MIPI_DSI_DWC_PWR_UP_OS;
+       pr_info("MIPI_DSI_DWC_PWR_UP_OS          [0x%04x] = 0x%08x\n",
+               reg, dsi_host_read(reg));
+       reg = MIPI_DSI_TOP_SW_RESET;
+       pr_info("MIPI_DSI_TOP_SW_RESET           [0x%04x] = 0x%08x\n",
+               reg, dsi_host_read(reg));
+       reg = MIPI_DSI_TOP_CLK_CNTL;
+       pr_info("MIPI_DSI_TOP_CLK_CNTL           [0x%04x] = 0x%08x\n",
+               reg, dsi_host_read(reg));
+       reg = MIPI_DSI_TOP_CNTL;
+       pr_info("MIPI_DSI_TOP_CNTL               [0x%04x] = 0x%08x\n",
+               reg, dsi_host_read(reg));
+       reg = MIPI_DSI_TOP_STAT;
+       pr_info("MIPI_DSI_TOP_STAT               [0x%04x] = 0x%08x\n",
+               reg, dsi_host_read(reg));
+       reg = MIPI_DSI_TOP_INTR_CNTL_STAT;
+       pr_info("MIPI_DSI_TOP_INTR_CNTL_STAT     [0x%04x] = 0x%08x\n",
+               reg, dsi_host_read(reg));
+       reg = MIPI_DSI_TOP_MEM_PD;
+       pr_info("MIPI_DSI_TOP_MEM_PD             [0x%04x] = 0x%08x\n",
+               reg, dsi_host_read(reg));
+       //reg = HHI_LVDS_TX_PHY_CNTL0;
+       //pr_info("VX1_PHY_CNTL0       [0x%04x] = 0x%08x\n",
+       //      reg, lcd_hiu_read(reg));
+}
+
+static void lcd_phy_analog_reg_print(void)
+{
+       unsigned int reg;
+
+       pr_info("\nphy analog registers:\n");
+       reg = HHI_DIF_CSI_PHY_CNTL1;
+       pr_info("PHY_CNTL1           [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+       reg = HHI_DIF_CSI_PHY_CNTL2;
+       pr_info("PHY_CNTL2           [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+       reg = HHI_DIF_CSI_PHY_CNTL3;
+       pr_info("PHY_CNTL3           [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+}
+
+static void lcd_mipi_phy_analog_reg_print(void)
+{
+       unsigned int reg;
+
+       pr_info("\nmipi_dsi_phy analog registers:\n");
+       reg = HHI_MIPI_CNTL0;
+       pr_info("HHI_MIPI_CNTL0   [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+       reg = HHI_MIPI_CNTL1;
+       pr_info("HHI_MIPI_CNTL1   [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+       reg = HHI_MIPI_CNTL2;
+       pr_info("HHI_MIPI_CNTL2   [0x%04x] = 0x%08x\n",
+               reg, lcd_hiu_read(reg));
+}
+
+static void lcd_reg_print(void)
+{
+       int i;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+
+       pconf = lcd_drv->lcd_config;
+       pr_info("clk registers:\n");
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_AXG:
+               for (i = 0; i < ARRAY_SIZE(lcd_reg_dump_clk_axg); i++) {
+                       pr_info("hiu     [0x%04x] = 0x%08x\n",
+                               lcd_reg_dump_clk_axg[i],
+                               lcd_hiu_read(lcd_reg_dump_clk_axg[i]));
+               }
+               break;
+       default:
+               for (i = 0; i < ARRAY_SIZE(lcd_reg_dump_clk); i++) {
+                       pr_info("hiu     [0x%04x] = 0x%08x\n",
+                               lcd_reg_dump_clk[i],
+                               lcd_hiu_read(lcd_reg_dump_clk[i]));
+               }
+               break;
+       }
+
+       pr_info("\nencl registers:\n");
+       for (i = 0; i < ARRAY_SIZE(lcd_reg_dump_encl); i++) {
+               pr_info("vcbus   [0x%04x] = 0x%08x\n",
+                       lcd_reg_dump_encl[i],
+                       lcd_vcbus_read(lcd_reg_dump_encl[i]));
+       }
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_TTL:
+               lcd_ttl_reg_print();
+               break;
+       case LCD_LVDS:
+               lcd_lvds_reg_print();
+               lcd_phy_analog_reg_print();
+               break;
+       case LCD_VBYONE:
+               lcd_vbyone_reg_print();
+               lcd_phy_analog_reg_print();
+               break;
+       case LCD_MIPI:
+               lcd_mipi_reg_print();
+               lcd_mipi_phy_analog_reg_print();
+               break;
+       default:
+               break;
+       }
+}
+
+static void lcd_hdr_info_print(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+
+       pconf = lcd_drv->lcd_config;
+       pr_info("lcd hdr info:\n"
+               "hdr_support          %d\n"
+               "features             %d\n"
+               "primaries_r_x        %d\n"
+               "primaries_r_y        %d\n"
+               "primaries_g_x        %d\n"
+               "primaries_g_y        %d\n"
+               "primaries_b_x        %d\n"
+               "primaries_b_y        %d\n"
+               "white_point_x        %d\n"
+               "white_point_y        %d\n"
+               "luma_max             %d\n"
+               "luma_min             %d\n\n",
+               pconf->hdr_info.hdr_support, pconf->hdr_info.features,
+               pconf->hdr_info.primaries_r_x, pconf->hdr_info.primaries_r_y,
+               pconf->hdr_info.primaries_g_x, pconf->hdr_info.primaries_g_y,
+               pconf->hdr_info.primaries_b_x, pconf->hdr_info.primaries_b_y,
+               pconf->hdr_info.white_point_x, pconf->hdr_info.white_point_y,
+               pconf->hdr_info.luma_max, pconf->hdr_info.luma_min);
+}
+
+#define LCD_ENC_TST_NUM_MAX    9
+static char *lcd_enc_tst_str[] = {
+       "0-None",        /* 0 */
+       "1-Color Bar",   /* 1 */
+       "2-Thin Line",   /* 2 */
+       "3-Dot Grid",    /* 3 */
+       "4-Gray",        /* 4 */
+       "5-Red",         /* 5 */
+       "6-Green",       /* 6 */
+       "7-Blue",        /* 7 */
+       "8-Black",       /* 8 */
+};
+
+static unsigned int lcd_enc_tst[][8] = {
+/*tst_mode,    Y,       Cb,     Cr,     tst_en,  vfifo_en  rgbin*/
+       {0,    0x200,   0x200,  0x200,   0,      1,        3},  /* 0 */
+       {1,    0x200,   0x200,  0x200,   1,      0,        1},  /* 1 */
+       {2,    0x200,   0x200,  0x200,   1,      0,        1},  /* 2 */
+       {3,    0x200,   0x200,  0x200,   1,      0,        1},  /* 3 */
+       {0,    0x1ff,   0x1ff,  0x1ff,   1,      0,        3},  /* 4 */
+       {0,    0x3ff,     0x0,    0x0,   1,      0,        3},  /* 5 */
+       {0,      0x0,   0x3ff,    0x0,   1,      0,        3},  /* 6 */
+       {0,      0x0,     0x0,  0x3ff,   1,      0,        3},  /* 7 */
+       {0,      0x0,     0x0,    0x0,   1,      0,        3},  /* 8 */
+};
+
+static void lcd_debug_test(unsigned int num)
+{
+       unsigned int h_active, video_on_pixel;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       int flag;
+
+       num = (num >= LCD_ENC_TST_NUM_MAX) ? 0 : num;
+       flag = (num > 0) ? 1 : 0;
+       aml_lcd_notifier_call_chain(LCD_EVENT_TEST_PATTERN, &flag);
+
+       h_active = lcd_drv->lcd_config->lcd_basic.h_active;
+       video_on_pixel = lcd_drv->lcd_config->lcd_timing.video_on_pixel;
+       if (num >= 0) {
+               lcd_vcbus_write(ENCL_VIDEO_RGBIN_CTRL, lcd_enc_tst[num][6]);
+               lcd_vcbus_write(ENCL_TST_MDSEL, lcd_enc_tst[num][0]);
+               lcd_vcbus_write(ENCL_TST_Y, lcd_enc_tst[num][1]);
+               lcd_vcbus_write(ENCL_TST_CB, lcd_enc_tst[num][2]);
+               lcd_vcbus_write(ENCL_TST_CR, lcd_enc_tst[num][3]);
+               lcd_vcbus_write(ENCL_TST_CLRBAR_STRT, video_on_pixel);
+               lcd_vcbus_write(ENCL_TST_CLRBAR_WIDTH, (h_active / 9));
+               lcd_vcbus_write(ENCL_TST_EN, lcd_enc_tst[num][4]);
+               lcd_vcbus_setb(ENCL_VIDEO_MODE_ADV, lcd_enc_tst[num][5], 3, 1);
+               LCDPR("show test pattern: %s\n", lcd_enc_tst_str[num]);
+       } else {
+               LCDPR("disable test pattern\n");
+       }
+}
+
+static void lcd_mute_setting(unsigned char flag)
+{
+       LCDPR("set lcd mute: %d\n", flag);
+       if (flag) {
+               lcd_vcbus_write(ENCL_VIDEO_RGBIN_CTRL, 3);
+               lcd_vcbus_write(ENCL_TST_MDSEL, 0);
+               lcd_vcbus_write(ENCL_TST_Y, 0);
+               lcd_vcbus_write(ENCL_TST_CB, 0);
+               lcd_vcbus_write(ENCL_TST_CR, 0);
+               lcd_vcbus_write(ENCL_TST_EN, 1);
+               lcd_vcbus_setb(ENCL_VIDEO_MODE_ADV, 0, 3, 1);
+       } else {
+               lcd_vcbus_setb(ENCL_VIDEO_MODE_ADV, 1, 3, 1);
+               lcd_vcbus_write(ENCL_TST_EN, 0);
+       }
+}
+
+static void lcd_test_check(void)
+{
+       unsigned int h_active, video_on_pixel;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       unsigned int num;
+       int flag;
+
+       lcd_drv->lcd_mute = 0;
+
+       num = lcd_drv->lcd_test_flag;
+       num = (num >= LCD_ENC_TST_NUM_MAX) ? 0 : num;
+       flag = (num > 0) ? 1 : 0;
+       aml_lcd_notifier_call_chain(LCD_EVENT_TEST_PATTERN, &flag);
+
+       h_active = lcd_drv->lcd_config->lcd_basic.h_active;
+       video_on_pixel = lcd_drv->lcd_config->lcd_timing.video_on_pixel;
+       if (num >= 0) {
+               lcd_vcbus_write(ENCL_VIDEO_RGBIN_CTRL, lcd_enc_tst[num][6]);
+               lcd_vcbus_write(ENCL_TST_MDSEL, lcd_enc_tst[num][0]);
+               lcd_vcbus_write(ENCL_TST_Y, lcd_enc_tst[num][1]);
+               lcd_vcbus_write(ENCL_TST_CB, lcd_enc_tst[num][2]);
+               lcd_vcbus_write(ENCL_TST_CR, lcd_enc_tst[num][3]);
+               lcd_vcbus_write(ENCL_TST_CLRBAR_STRT, video_on_pixel);
+               lcd_vcbus_write(ENCL_TST_CLRBAR_WIDTH, (h_active / 9));
+               lcd_vcbus_write(ENCL_TST_EN, lcd_enc_tst[num][4]);
+               lcd_vcbus_setb(ENCL_VIDEO_MODE_ADV, lcd_enc_tst[num][5], 3, 1);
+               if (num > 0)
+                       LCDPR("show test pattern: %s\n", lcd_enc_tst_str[num]);
+       }
+}
+
+static void lcd_debug_config_update(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       lcd_drv->module_reset();
+}
+
+static void lcd_debug_clk_change(unsigned int pclk)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       unsigned int sync_duration;
+
+       pconf = lcd_drv->lcd_config;
+       sync_duration = pclk / pconf->lcd_basic.h_period;
+       sync_duration = sync_duration * 100 / pconf->lcd_basic.v_period;
+       pconf->lcd_timing.lcd_clk = pclk;
+       pconf->lcd_timing.sync_duration_num = sync_duration;
+       pconf->lcd_timing.sync_duration_den = 100;
+
+       /* update vinfo */
+       lcd_drv->lcd_info->sync_duration_num = sync_duration;
+       lcd_drv->lcd_info->sync_duration_den = 100;
+       lcd_drv->lcd_info->video_clk = pclk;
+
+       switch (lcd_drv->lcd_mode) {
+#ifdef CONFIG_AMLOGIC_LCD_TV
+       case LCD_MODE_TV:
+               lcd_tv_clk_update(pconf);
+               break;
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_TABLET
+       case LCD_MODE_TABLET:
+               lcd_tablet_clk_update(pconf);
+               break;
+#endif
+       default:
+               LCDPR("invalid lcd mode\n");
+               break;
+       }
+
+       vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE,
+               &lcd_drv->lcd_info->mode);
+}
+
+static ssize_t lcd_debug_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       int ret = 0;
+       unsigned int temp, val[6];
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+
+       pconf = lcd_drv->lcd_config;
+       switch (buf[0]) {
+       case 'c': /* clk */
+               ret = sscanf(buf, "clk %d", &temp);
+               if (ret == 1) {
+                       if (temp > 200) {
+                               pr_info("set clk: %dHz\n", temp);
+                       } else {
+                               pr_info("set frame_rate: %dHz\n", temp);
+                               temp = pconf->lcd_basic.h_period *
+                                       pconf->lcd_basic.v_period * temp;
+                               pr_info("set clk: %dHz\n", temp);
+                       }
+                       lcd_debug_clk_change(temp);
+               } else {
+                       LCDERR("invalid data\n");
+                       return -EINVAL;
+               }
+               break;
+       case 'b':
+               if (buf[1] == 'a') { /* basic */
+                       ret = sscanf(buf, "basic %d %d %d %d %d",
+                               &val[0], &val[1], &val[2], &val[3], &val[4]);
+                       if (ret == 4) {
+                               pconf->lcd_basic.h_active = val[0];
+                               pconf->lcd_basic.v_active = val[1];
+                               pconf->lcd_basic.h_period = val[2];
+                               pconf->lcd_basic.v_period = val[3];
+                               pr_info("set h_active=%d, v_active=%d\n",
+                                       val[0], val[1]);
+                               pr_info("set h_period=%d, v_period=%d\n",
+                                       val[2], val[3]);
+                               lcd_tcon_config(pconf);
+                               lcd_debug_config_update();
+                       } else if (ret == 5) {
+                               pconf->lcd_basic.h_active = val[0];
+                               pconf->lcd_basic.v_active = val[1];
+                               pconf->lcd_basic.h_period = val[2];
+                               pconf->lcd_basic.v_period = val[3];
+                               pconf->lcd_basic.lcd_bits = val[4];
+                               pr_info("set h_active=%d, v_active=%d\n",
+                                       val[0], val[1]);
+                               pr_info("set h_period=%d, v_period=%d\n",
+                                       val[2], val[3]);
+                               pr_info("set lcd_bits=%d\n", val[4]);
+                               lcd_tcon_config(pconf);
+                               lcd_debug_config_update();
+                       } else {
+                               LCDERR("invalid data\n");
+                               return -EINVAL;
+                       }
+               } else if (buf[1] == 'i') { /* bit */
+                       ret = sscanf(buf, "bit %d", &val[0]);
+                       if (ret == 1) {
+                               pconf->lcd_basic.lcd_bits = val[4];
+                               pr_info("set lcd_bits=%d\n", val[4]);
+                               lcd_debug_config_update();
+                       } else {
+                               LCDERR("invalid data\n");
+                               return -EINVAL;
+                       }
+               }
+               break;
+       case 's': /* sync */
+               ret = sscanf(buf, "sync %d %d %d %d %d %d",
+                       &val[0], &val[1], &val[2], &val[3], &val[4], &val[5]);
+               if (ret == 6) {
+                       pconf->lcd_timing.hsync_width = val[0];
+                       pconf->lcd_timing.hsync_bp =    val[1];
+                       pconf->lcd_timing.hsync_pol =   val[2];
+                       pconf->lcd_timing.vsync_width = val[3];
+                       pconf->lcd_timing.vsync_bp =    val[4];
+                       pconf->lcd_timing.vsync_pol =   val[5];
+                       pr_info("set hsync width=%d, bp=%d, pol=%d\n",
+                               val[0], val[1], val[2]);
+                       pr_info("set vsync width=%d, bp=%d, pol=%d\n",
+                               val[3], val[4], val[5]);
+                       lcd_tcon_config(pconf);
+                       lcd_debug_config_update();
+               } else {
+                       LCDERR("invalid data\n");
+                       return -EINVAL;
+               }
+               break;
+       case 't': /* test */
+               ret = sscanf(buf, "test %d", &temp);
+               if (ret == 1) {
+                       lcd_drv->lcd_test_flag = (unsigned char)temp;
+                       lcd_debug_test(temp);
+               } else {
+                       LCDERR("invalid data\n");
+                       return -EINVAL;
+               }
+               break;
+       case 'i': /* info */
+               LCDPR("driver version: %s\n", lcd_drv->version);
+               lcd_info_print();
+               break;
+       case 'r':
+               if (buf[2] == 'g') { /* reg */
+                       LCDPR("driver version: %s\n", lcd_drv->version);
+                       lcd_reg_print();
+               } else if (buf[2] == 's') { /* reset */
+                       lcd_drv->module_reset();
+               } else if (buf[2] == 'n') { /* range */
+                       ret = sscanf(buf, "range %d %d %d %d %d %d",
+                               &val[0], &val[1], &val[2], &val[3],
+                               &val[4], &val[5]);
+                       if (ret == 6) {
+                               pconf->lcd_basic.h_period_min = val[0];
+                               pconf->lcd_basic.h_period_max = val[1];
+                               pconf->lcd_basic.h_period_min = val[2];
+                               pconf->lcd_basic.v_period_max = val[3];
+                               pconf->lcd_basic.lcd_clk_min  = val[4];
+                               pconf->lcd_basic.lcd_clk_max  = val[5];
+                               pr_info("set h_period min=%d, max=%d\n",
+                                       pconf->lcd_basic.h_period_min,
+                                       pconf->lcd_basic.h_period_max);
+                               pr_info("set v_period min=%d, max=%d\n",
+                                       pconf->lcd_basic.v_period_min,
+                                       pconf->lcd_basic.v_period_max);
+                               pr_info("set pclk min=%d, max=%d\n",
+                                       pconf->lcd_basic.lcd_clk_min,
+                                       pconf->lcd_basic.lcd_clk_max);
+                       } else {
+                               LCDERR("invalid data\n");
+                               return -EINVAL;
+                       }
+               }
+               break;
+       case 'd': /* dump */
+               LCDPR("driver version: %s\n", lcd_drv->version);
+               lcd_info_print();
+               pr_info("\n");
+               lcd_reg_print();
+               pr_info("\n");
+               lcd_hdr_info_print();
+               break;
+       case 'k': /* key */
+               LCDPR("key_valid: %d, config_load: %d\n",
+                       lcd_drv->lcd_key_valid, lcd_drv->lcd_config_load);
+               if (lcd_drv->lcd_key_valid)
+                       lcd_unifykey_print();
+               break;
+       case 'h': /* hdr */
+               lcd_hdr_info_print();
+               break;
+       case 'p': /* power */
+               ret = sscanf(buf, "power %d", &temp);
+               if (ret == 1) {
+                       mutex_lock(&lcd_power_mutex);
+                       LCDPR("power: %d\n", temp);
+                       if (temp) {
+                               aml_lcd_notifier_call_chain(
+                                       LCD_EVENT_IF_POWER_ON, NULL);
+                       } else {
+                               aml_lcd_notifier_call_chain(
+                                       LCD_EVENT_IF_POWER_OFF, NULL);
+                       }
+                       mutex_unlock(&lcd_power_mutex);
+               } else {
+                       LCDERR("invalid data\n");
+                       return -EINVAL;
+               }
+               break;
+       case 'v':
+               ret = sscanf(buf, "vout %d", &temp);
+               if (ret == 1) {
+                       LCDPR("vout_serve bypass: %d\n", temp);
+                       lcd_vout_serve_bypass = temp;
+               } else {
+                       LCDERR("invalid data\n");
+                       return -EINVAL;
+               }
+               break;
+       default:
+               LCDERR("wrong command\n");
+               break;
+       }
+
+       return count;
+}
+
+static ssize_t lcd_debug_enable_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       int ret = 0;
+       unsigned int temp = 1;
+
+       ret = kstrtouint(buf, 10, &temp);
+       if (temp) {
+               mutex_lock(&lcd_power_mutex);
+               aml_lcd_notifier_call_chain(LCD_EVENT_POWER_ON, NULL);
+               mutex_unlock(&lcd_power_mutex);
+       } else {
+               mutex_lock(&lcd_power_mutex);
+               aml_lcd_notifier_call_chain(LCD_EVENT_POWER_OFF, NULL);
+               mutex_unlock(&lcd_power_mutex);
+       }
+
+       return count;
+}
+
+static ssize_t lcd_debug_power_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       lcd_power_info_print(lcd_drv->lcd_config, 1);
+       lcd_power_info_print(lcd_drv->lcd_config, 0);
+
+       return sprintf(buf, "\n");
+}
+
+static ssize_t lcd_debug_power_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       int ret = 0;
+       unsigned int i, delay;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_power_ctrl_s *lcd_power;
+
+       lcd_power = lcd_drv->lcd_config->lcd_power;
+       switch (buf[1]) {
+       case 'n': /* on */
+               ret = sscanf(buf, "on %d %d", &i, &delay);
+               if (ret == 2) {
+                       if (i >= lcd_power->power_on_step_max) {
+                               pr_info("invalid power_on step: %d\n", i);
+                               return -EINVAL;
+                       }
+                       lcd_power->power_on_step[i].delay = delay;
+                       pr_info("set power_on step %d delay: %dms\n",
+                               i, delay);
+               } else {
+                       pr_info("invalid data\n");
+                       return -EINVAL;
+               }
+               break;
+       case 'f': /* off */
+               ret = sscanf(buf, "off %d %d", &i, &delay);
+               if (ret == 1) {
+                       if (i >= lcd_power->power_off_step_max) {
+                               pr_info("invalid power_off step: %d\n", i);
+                               return -EINVAL;
+                       }
+                       lcd_power->power_off_step[i].delay = delay;
+                       pr_info("set power_off step %d delay: %dms\n",
+                               i, delay);
+               } else {
+                       pr_info("invalid data\n");
+                       return -EINVAL;
+               }
+               break;
+       default:
+               pr_info("wrong command\n");
+               break;
+       }
+
+       return count;
+}
+
+static ssize_t lcd_debug_frame_rate_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       unsigned int sync_duration;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+
+       pconf = lcd_drv->lcd_config;
+       sync_duration = pconf->lcd_timing.sync_duration_num * 100;
+       sync_duration = sync_duration / pconf->lcd_timing.sync_duration_den;
+
+       return sprintf(buf, "get frame_rate: %u.%02uHz, fr_adjust_type: %d\n",
+               (sync_duration / 100), (sync_duration % 100),
+               pconf->lcd_timing.fr_adjust_type);
+}
+
+static ssize_t lcd_debug_frame_rate_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       int ret = 0;
+       unsigned int temp = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       switch (buf[0]) {
+       case 't':
+               ret = sscanf(buf, "type %d", &temp);
+               if (ret == 1) {
+                       lcd_drv->lcd_config->lcd_timing.fr_adjust_type = temp;
+                       pr_info("set fr_adjust_type: %d\n", temp);
+               } else {
+                       pr_info("invalid data\n");
+                       return -EINVAL;
+               }
+               break;
+       case 's':
+               ret = sscanf(buf, "set %d", &temp);
+               if (ret == 1) {
+                       pr_info("set frame rate(*100): %d\n", temp);
+                       aml_lcd_notifier_call_chain(
+                               LCD_EVENT_FRAME_RATE_ADJUST, &temp);
+               } else {
+                       pr_info("invalid data\n");
+                       return -EINVAL;
+               }
+               break;
+       default:
+               pr_info("wrong command\n");
+               break;
+       }
+
+       return count;
+}
+
+static ssize_t lcd_debug_fr_policy_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       return sprintf(buf, "fr_auto_policy: %d\n", lcd_drv->fr_auto_policy);
+}
+
+static ssize_t lcd_debug_fr_policy_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       int ret = 0;
+       unsigned int temp = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       ret = kstrtouint(buf, 10, &temp);
+       lcd_drv->fr_auto_policy = temp;
+       pr_info("set fr_auto_policy: %d\n", temp);
+
+       return count;
+}
+
+static ssize_t lcd_debug_ss_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "get lcd pll spread spectrum: %s\n",
+                       lcd_get_spread_spectrum());
+}
+
+static ssize_t lcd_debug_ss_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       int ret = 0;
+       unsigned int temp = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       ret = kstrtouint(buf, 10, &temp);
+       lcd_drv->lcd_config->lcd_timing.ss_level = temp;
+       lcd_set_spread_spectrum();
+
+       return count;
+}
+
+static ssize_t lcd_debug_clk_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       lcd_clk_config_print();
+       return sprintf(buf, "\n");
+}
+
+static ssize_t lcd_debug_test_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       int ret = 0;
+       unsigned int temp = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       ret = kstrtouint(buf, 10, &temp);
+       lcd_drv->lcd_test_flag = (unsigned char)temp;
+       lcd_debug_test(temp);
+
+       return count;
+}
+
+static ssize_t lcd_debug_mute_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       return sprintf(buf, "get lcd mute state: %d\n", lcd_drv->lcd_mute);
+}
+
+static ssize_t lcd_debug_mute_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       int ret = 0;
+       unsigned int temp = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       ret = kstrtouint(buf, 10, &temp);
+       lcd_drv->lcd_mute = (unsigned char)temp;
+       lcd_mute_setting(lcd_drv->lcd_mute);
+
+       return count;
+}
+
+static void lcd_debug_reg_write(unsigned int reg, unsigned int data,
+               unsigned int type)
+{
+       switch (type) {
+       case 0: /* vcbus */
+               lcd_vcbus_write(reg, data);
+               pr_info("write vcbus [0x%04x] = 0x%08x, readback 0x%08x\n",
+                       reg, data, lcd_vcbus_read(reg));
+               break;
+       case 1: /* hiu */
+               lcd_hiu_write(reg, data);
+               pr_info("write hiu [0x%04x] = 0x%08x, readback 0x%08x\n",
+                       reg, data, lcd_hiu_read(reg));
+               break;
+       case 2: /* cbus */
+               lcd_cbus_write(reg, data);
+               pr_info("write cbus [0x%04x] = 0x%08x, readback 0x%08x\n",
+                       reg, data, lcd_cbus_read(reg));
+               break;
+       case 3: /* periphs */
+               lcd_periphs_write(reg, data);
+               pr_info("write periphs [0x%04x] = 0x%08x, readback 0x%08x\n",
+                       reg, data, lcd_periphs_read(reg));
+               break;
+       case 4: /* mipi_dsi_host */
+               dsi_host_write(reg, data);
+               pr_info("write mipi_dsi_host [0x%04x] = 0x%08x, readback 0x%08x\n",
+                       reg, data, dsi_host_read(reg));
+               break;
+       case 5: /* mipi_dsi_phy */
+               dsi_phy_write(reg, data);
+               pr_info("write mipi_dsi_phy [0x%04x] = 0x%08x, readback 0x%08x\n",
+                       reg, data, dsi_phy_read(reg));
+               break;
+       default:
+               break;
+       }
+}
+
+static void lcd_debug_reg_read(unsigned int reg, unsigned int bus)
+{
+       switch (bus) {
+       case 0: /* vcbus */
+               pr_info("read vcbus [0x%04x] = 0x%08x\n",
+                       reg, lcd_vcbus_read(reg));
+               break;
+       case 1: /* hiu */
+               pr_info("read hiu [0x%04x] = 0x%08x\n",
+                       reg, lcd_hiu_read(reg));
+               break;
+       case 2: /* cbus */
+               pr_info("read cbus [0x%04x] = 0x%08x\n",
+                       reg, lcd_cbus_read(reg));
+               break;
+       case 3: /* periphs */
+               pr_info("read periphs [0x%04x] = 0x%08x\n",
+                       reg, lcd_periphs_read(reg));
+               break;
+       case 4: /* mipi_dsi_host */
+               pr_info("read mipi_dsi_host [0x%04x] = 0x%08x\n",
+                       reg, dsi_host_read(reg));
+               break;
+       case 5: /* mipi_dsi_phy */
+               pr_info("read mipi_dsi_phy [0x%04x] = 0x%08x\n",
+                       reg, dsi_phy_read(reg));
+               break;
+       default:
+               break;
+       }
+}
+
+static void lcd_debug_reg_dump(unsigned int reg, unsigned int num,
+               unsigned int bus)
+{
+       int i;
+
+       switch (bus) {
+       case 0: /* vcbus */
+               pr_info("dump vcbus regs:\n");
+               for (i = 0; i < num; i++) {
+                       pr_info("[0x%04x] = 0x%08x\n",
+                               (reg + i), lcd_vcbus_read(reg + i));
+               }
+               break;
+       case 1: /* hiu */
+               pr_info("dump hiu-bus regs:\n");
+               for (i = 0; i < num; i++) {
+                       pr_info("[0x%04x] = 0x%08x\n",
+                               (reg + i), lcd_hiu_read(reg + i));
+               }
+               break;
+       case 2: /* cbus */
+               pr_info("dump cbus regs:\n");
+               for (i = 0; i < num; i++) {
+                       pr_info("[0x%04x] = 0x%08x\n",
+                               (reg + i), lcd_cbus_read(reg + i));
+               }
+               break;
+       case 3: /* periphs */
+               pr_info("dump periphs-bus regs:\n");
+               for (i = 0; i < num; i++) {
+                       pr_info("[0x%04x] = 0x%08x\n",
+                               (reg + i), lcd_periphs_read(reg + i));
+               }
+               break;
+       case 4: /* mipi_dsi_host */
+               pr_info("dump mipi_dsi_host regs:\n");
+               for (i = 0; i < num; i++) {
+                       pr_info("[0x%04x] = 0x%08x\n",
+                               (reg + i), dsi_host_read(reg + i));
+               }
+               break;
+       case 5: /* mipi_dsi_phy */
+               pr_info("dump mipi_dsi_phy regs:\n");
+               for (i = 0; i < num; i++) {
+                       pr_info("[0x%04x] = 0x%08x\n",
+                               (reg + i), dsi_phy_read(reg + i));
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static ssize_t lcd_debug_reg_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       int ret = 0;
+       unsigned int bus = 0;
+       unsigned int reg32 = 0, data32 = 0;
+
+       switch (buf[0]) {
+       case 'w':
+               if (buf[1] == 'v') {
+                       ret = sscanf(buf, "wv %x %x", &reg32, &data32);
+                       bus = 0;
+               } else if (buf[1] == 'h') {
+                       ret = sscanf(buf, "wh %x %x", &reg32, &data32);
+                       bus = 1;
+               } else if (buf[1] == 'c') {
+                       ret = sscanf(buf, "wc %x %x", &reg32, &data32);
+                       bus = 2;
+               } else if (buf[1] == 'p') {
+                       ret = sscanf(buf, "wp %x %x", &reg32, &data32);
+                       bus = 3;
+               } else if (buf[1] == 'm') {
+                       if (buf[2] == 'h') { /* mipi host */
+                               ret = sscanf(buf, "wmh %x %x", &reg32, &data32);
+                               bus = 4;
+                       } else if (buf[2] == 'p') { /* mipi phy */
+                               ret = sscanf(buf, "wmp %x %x", &reg32, &data32);
+                               bus = 5;
+                       }
+               }
+               if (ret == 2) {
+                       lcd_debug_reg_write(reg32, data32, bus);
+               } else {
+                       pr_info("invalid data\n");
+                       return -EINVAL;
+               }
+               break;
+       case 'r':
+               if (buf[1] == 'v') {
+                       ret = sscanf(buf, "rv %x", &reg32);
+                       bus = 0;
+               } else if (buf[1] == 'h') {
+                       ret = sscanf(buf, "rh %x", &reg32);
+                       bus = 1;
+               } else if (buf[1] == 'c') {
+                       ret = sscanf(buf, "rc %x", &reg32);
+                       bus = 2;
+               } else if (buf[1] == 'p') {
+                       ret = sscanf(buf, "rp %x", &reg32);
+                       bus = 3;
+               } else if (buf[1] == 'm') {
+                       if (buf[2] == 'h') { /* mipi host */
+                               ret = sscanf(buf, "rmh %x", &reg32);
+                               bus = 4;
+                       } else if (buf[2] == 'p') { /* mipi phy */
+                               ret = sscanf(buf, "rmp %x", &reg32);
+                               bus = 5;
+                       }
+               }
+               if (ret == 1) {
+                       lcd_debug_reg_read(reg32, bus);
+               } else {
+                       pr_info("invalid data\n");
+                       return -EINVAL;
+               }
+               break;
+       case 'd':
+               if (buf[1] == 'v') {
+                       ret = sscanf(buf, "dv %x %d", &reg32, &data32);
+                       bus = 0;
+               } else if (buf[1] == 'h') {
+                       ret = sscanf(buf, "dh %x %d", &reg32, &data32);
+                       bus = 1;
+               } else if (buf[1] == 'c') {
+                       ret = sscanf(buf, "dc %x %d", &reg32, &data32);
+                       bus = 2;
+               } else if (buf[1] == 'p') {
+                       ret = sscanf(buf, "dp %x %d", &reg32, &data32);
+                       bus = 3;
+               } else if (buf[1] == 'm') {
+                       if (buf[2] == 'h') { /* mipi host */
+                               ret = sscanf(buf, "dmh %x %x", &reg32, &data32);
+                               bus = 4;
+                       } else if (buf[2] == 'p') { /* mipi phy */
+                               ret = sscanf(buf, "dmp %x %x", &reg32, &data32);
+                               bus = 5;
+                       }
+               }
+               if (ret == 2) {
+                       lcd_debug_reg_dump(reg32, data32, bus);
+               } else {
+                       pr_info("invalid data\n");
+                       return -EINVAL;
+               }
+               break;
+       default:
+               pr_info("wrong command\n");
+               break;
+       }
+
+       return count;
+}
+
+static unsigned int lcd_dither_en = 1;
+static unsigned int lcd_dither_round_en = 0;
+static unsigned int lcd_dither_md = 0;
+static void lcd_vpu_dither_setting(unsigned int lcd_dither_en,
+               unsigned int lcd_dither_round_en, unsigned int lcd_dither_md)
+{
+       unsigned int data32;
+
+       data32 = lcd_vcbus_read(VPU_VENCL_DITH_CTRL);
+       data32 &= ~((1 << 0) | (1 << 1) | (1 << 2));
+       data32 |= ((lcd_dither_en << 0) |
+               (lcd_dither_round_en << 1) |
+               (lcd_dither_md << 2));
+       lcd_vcbus_write(VPU_VENCL_DITH_CTRL, data32);
+}
+
+static ssize_t lcd_debug_dither_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       ssize_t len = 0;
+
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_TXLX:
+               len = sprintf(buf, "get dither status:\n"
+                       "dither_en:        %d\n"
+                       "dither_round_en:  %d\n"
+                       "dither_md:        %d\n",
+                       lcd_dither_en, lcd_dither_round_en, lcd_dither_md);
+               break;
+       default:
+               len = sprintf(buf,
+                       "don't support dither function for current chip\n");
+               break;
+       }
+
+       return len;
+}
+
+static ssize_t lcd_debug_dither_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       int ret = -1;
+       unsigned int temp = 0;
+
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_TXLX:
+               ret = 0;
+               break;
+       default:
+               ret = -1;
+               LCDPR("don't support dither function for current chip\n");
+               break;
+       }
+       if (ret)
+               return count;
+
+       switch (buf[0]) {
+       case 'e': /* en */
+               ret = sscanf(buf, "en %d", &temp);
+               if (ret == 1) {
+                       if (temp)
+                               lcd_dither_en = 1;
+                       else
+                               lcd_dither_en = 0;
+                       lcd_vpu_dither_setting(lcd_dither_en,
+                               lcd_dither_round_en, lcd_dither_md);
+               } else {
+                       LCDERR("invalid data\n");
+                       return -EINVAL;
+               }
+               break;
+       case 'r': /* round */
+               ret = sscanf(buf, "round %d", &temp);
+               if (ret == 1) {
+                       if (temp)
+                               lcd_dither_round_en = 1;
+                       else
+                               lcd_dither_round_en = 0;
+                       lcd_vpu_dither_setting(lcd_dither_en,
+                               lcd_dither_round_en, lcd_dither_md);
+               } else {
+                       LCDERR("invalid data\n");
+                       return -EINVAL;
+               }
+               break;
+       case 'm': /* md */
+               ret = sscanf(buf, "method %d", &temp);
+               if (ret == 1) {
+                       if (temp)
+                               lcd_dither_md = 1;
+                       else
+                               lcd_dither_md = 0;
+                       lcd_vpu_dither_setting(lcd_dither_en,
+                               lcd_dither_round_en, lcd_dither_md);
+               } else {
+                       LCDERR("invalid data\n");
+                       return -EINVAL;
+               }
+               break;
+       default:
+               LCDERR("wrong command\n");
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+static ssize_t lcd_debug_print_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "get debug print flag: %d\n", lcd_debug_print_flag);
+}
+
+static ssize_t lcd_debug_print_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       int ret = 0;
+       unsigned int temp = 0;
+
+       ret = kstrtouint(buf, 10, &temp);
+       lcd_debug_print_flag = (unsigned char)temp;
+       LCDPR("set debug print flag: %d\n", lcd_debug_print_flag);
+
+       return count;
+}
+
+static struct class_attribute lcd_debug_class_attrs[] = {
+       __ATTR(help,        0644, lcd_debug_common_help, NULL),
+       __ATTR(debug,       0644, lcd_debug_show, lcd_debug_store),
+       __ATTR(enable,      0644, NULL, lcd_debug_enable_store),
+       __ATTR(power,       0644, lcd_debug_power_show, lcd_debug_power_store),
+       __ATTR(frame_rate,  0644,
+               lcd_debug_frame_rate_show, lcd_debug_frame_rate_store),
+       __ATTR(fr_policy,   0644,
+               lcd_debug_fr_policy_show, lcd_debug_fr_policy_store),
+       __ATTR(ss,          0644, lcd_debug_ss_show, lcd_debug_ss_store),
+       __ATTR(clk,         0644, lcd_debug_clk_show, NULL),
+       __ATTR(test,        0644, NULL, lcd_debug_test_store),
+       __ATTR(mute,        0644, lcd_debug_mute_show, lcd_debug_mute_store),
+       __ATTR(reg,         0644, NULL, lcd_debug_reg_store),
+       __ATTR(dither,      0644,
+               lcd_debug_dither_show, lcd_debug_dither_store),
+       __ATTR(print,       0644, lcd_debug_print_show, lcd_debug_print_store),
+};
+
+static const char *lcd_ttl_debug_usage_str = {
+"Usage:\n"
+"    echo <clk_pol> <de_valid> <hvsync_valid> <rb_swpa> <bit_swap> > ttl ; set ttl config\n"
+"data format:\n"
+"    <clk_pol>      : 0=negative, 1=positive\n"
+"    <de_valid>     : for DE, 0=invalid, 1=valid\n"
+"    <hvsync_valid> : for hvsync, 0=invalid, 1=valid\n"
+"    <rb_swpa>      : for R/B port, 0=normal, 1=swap\n"
+"    <bit_swap>     : for RGB MSB/LSB, 0=normal, 1=swap\n"
+"\n"
+};
+
+static const char *lcd_lvds_debug_usage_str = {
+"Usage:\n"
+"    echo <repack> <dual_port> <pn_swap> <port_swap> <lane_reverse> > lvds ; set lvds config\n"
+"data format:\n"
+"    <repack>    : 0=JEIDA mode, 1=VESA mode\n"
+"    <dual_port> : 0=single port, 1=dual port\n"
+"    <pn_swap>   : 0=normal, 1=swap p/n channels\n"
+"    <port_swap> : 0=normal, 1=swap A/B port\n"
+"       <lane_reverse> : 0=normal, 1=swap A0-A4/B0-B4\n"
+"\n"
+"    echo <vswing> <preem> > phy ; set vbyone phy config\n"
+"data format:\n"
+"    <vswing> : vswing level, support 0~7\n"
+"    <preem>  : preemphasis level, support 0~7\n"
+"\n"
+};
+
+static const char *lcd_vbyone_debug_usage_str = {
+"Usage:\n"
+"    echo <lane_count> <region_num> <byte_mode> > vbyone ; set vbyone config\n"
+"data format:\n"
+"    <lane_count> : 4/8/16\n"
+"    <region_num> : 1/2\n"
+"    <byte_mode>  : 3/4/5\n"
+"\n"
+"    echo <vswing> <preem> > phy ; set vbyone phy config\n"
+"data format:\n"
+"    <vswing> : vswing level, support 0~7\n"
+"    <preem>  : preemphasis level, support 0~7\n"
+"    <byte_mode>  : 3/4/5\n"
+"\n"
+"    echo intr <state> <en> > vbyone; enable or disable vbyone interrupt\n"
+"data format:\n"
+"    <state> : 0=temp no use intr, 1=temp use intr. keep effect until reset lcd driver\n"
+"    <en>    : 0=disable intr, 1=enable intr\n"
+"\n"
+"    echo vintr <en> > vbyone; enable or disable vbyone interrupt\n"
+"data format:\n"
+"    <en>    : 0=disable vsync monitor intr, 1=enable vsync monitor intr\n"
+"\n"
+};
+
+static const char *lcd_mipi_debug_usage_str = {
+"Usage:\n"
+"    echo <lane_num> <bit_rate_max> <factor> <op_mode_init> <op_mode_disp> <vid_mode_type> <clk_lp_continuous> <phy_stop_wait> > mipi ; set mpi config\n"
+"data format:\n"
+"    <lane_num>          : 1/2/3/4\n"
+"    <bit_rate_max>      : unit in MHz\n"
+"    <factor>:           : special adjust, 0 for default\n"
+"    <op_mode_init>      : operation mode for init (0=video mode, 1=command mode)\n"
+"    <op_mode_disp>      : operation mode for display (0=video mode, 1=command mode)\n"
+"    <vid_mode_type>     : video mode type (0=sync_pulse, 1=sync_event, 2=burst)\n"
+"    <clk_lp_continuous> : 0=stop, 1=continue\n"
+"    <phy_stop_wait>     : 0=auto, 1=standard, 2=slow\n"
+"\n"
+};
+
+static const char *lcd_edp_debug_usage_str = {
+"Usage:\n"
+"    echo <link_rate> <lane_count> <edid_timing_used> <sync_clock_mode> > edp ; set edp config\n"
+"data format:\n"
+"    <link_rate>        : 0=1.62G, 1=2.7G\n"
+"    <lane_count>       : 1/2/4\n"
+"    <edid_timing_used> : 0=no use, 1=use, default=0\n"
+"    <sync_clock_mode>  : 0=asyncronous, 1=synchronous, default=1\n"
+"\n"
+};
+
+static ssize_t lcd_ttl_debug_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%s\n", lcd_ttl_debug_usage_str);
+}
+
+static ssize_t lcd_lvds_debug_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%s\n", lcd_lvds_debug_usage_str);
+}
+
+static ssize_t lcd_vx1_debug_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%s\n", lcd_vbyone_debug_usage_str);
+}
+
+static ssize_t lcd_mipi_debug_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%s\n", lcd_mipi_debug_usage_str);
+}
+
+static ssize_t lcd_edp_debug_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%s\n", lcd_edp_debug_usage_str);
+}
+
+static ssize_t lcd_ttl_debug_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       int ret = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct ttl_config_s *ttl_conf;
+       unsigned int temp[5];
+
+       ttl_conf = lcd_drv->lcd_config->lcd_control.ttl_config;
+       ret = sscanf(buf, "%d %d %d %d %d",
+               &temp[0], &temp[1], &temp[2], &temp[3], &temp[4]);
+       if (ret == 5) {
+               pr_info("set ttl config:\n"
+                       "clk_pol=%d, de_valid=%d, de_valid=%d\n"
+                       "rb_swap=%d, bit_swap=%d\n",
+                       temp[0], temp[1], temp[2], temp[3], temp[4]);
+               ttl_conf->clk_pol = temp[0];
+               ttl_conf->sync_valid = ((temp[1] << 1) | temp[2]);
+               ttl_conf->swap_ctrl = ((temp[3] << 1) | temp[4]);
+               lcd_debug_config_update();
+       } else {
+               pr_info("invalid data\n");
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+static ssize_t lcd_lvds_debug_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       int ret = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lvds_config_s *lvds_conf;
+
+       lvds_conf = lcd_drv->lcd_config->lcd_control.lvds_config;
+       ret = sscanf(buf, "%d %d %d %d %d",
+               &lvds_conf->lvds_repack, &lvds_conf->dual_port,
+               &lvds_conf->pn_swap, &lvds_conf->port_swap,
+               &lvds_conf->lane_reverse);
+       if (ret == 5 || ret == 4) {
+               pr_info("set lvds config:\n"
+                       "repack=%d, dual_port=%d, pn_swap=%d, port_swap=%d, lane_reverse=%d\n",
+                       lvds_conf->lvds_repack, lvds_conf->dual_port,
+                       lvds_conf->pn_swap, lvds_conf->port_swap,
+                       lvds_conf->lane_reverse);
+               lcd_debug_config_update();
+       } else {
+               pr_info("invalid data\n");
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+static int vx1_intr_state = 1;
+static ssize_t lcd_vx1_debug_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       int ret = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct vbyone_config_s *vx1_conf;
+       int val[2];
+
+       vx1_conf = lcd_drv->lcd_config->lcd_control.vbyone_config;
+       if (buf[0] == 'i') { /* intr */
+               ret = sscanf(buf, "intr %d %d", &val[0], &val[1]);
+               if (ret == 1) {
+                       pr_info("set vbyone interrupt enable: %d\n", val[0]);
+                       vx1_intr_state = val[0];
+                       lcd_vbyone_interrupt_enable(vx1_intr_state);
+               } else if (ret == 2) {
+                       pr_info("set vbyone interrupt enable: %d %d\n",
+                               val[0], val[1]);
+                       vx1_intr_state = val[0];
+                       vx1_conf->intr_en = val[1];
+                       lcd_vbyone_interrupt_enable(vx1_intr_state);
+               } else {
+                       pr_info("vx1_intr_enable: %d %d\n",
+                               vx1_intr_state, vx1_conf->intr_en);
+                       return -EINVAL;
+               }
+       } else if (buf[0] == 'v') { /* vintr */
+               ret = sscanf(buf, "vintr %d", &val[0]);
+               if (ret == 1) {
+                       pr_info("set vbyone vsync interrupt enable: %d\n",
+                               val[0]);
+                       vx1_conf->vsync_intr_en = val[0];
+                       lcd_vbyone_interrupt_enable(vx1_intr_state);
+               } else {
+                       pr_info("vx1_vsync_intr_enable: %d\n",
+                               vx1_conf->vsync_intr_en);
+                       return -EINVAL;
+               }
+       } else {
+               ret = sscanf(buf, "%d %d %d", &vx1_conf->lane_count,
+                       &vx1_conf->region_num, &vx1_conf->byte_mode);
+               if (ret == 3) {
+                       pr_info("set vbyone config:\n"
+                               "lane_count=%d, region_num=%d, byte_mode=%d\n",
+                               vx1_conf->lane_count, vx1_conf->region_num,
+                               vx1_conf->byte_mode);
+                       lcd_debug_config_update();
+               } else {
+                       pr_info("invalid data\n");
+                       return -EINVAL;
+               }
+       }
+
+       return count;
+}
+
+static ssize_t lcd_mipi_debug_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       int ret = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct dsi_config_s *dsi_conf;
+       int val[8];
+
+       dsi_conf = lcd_drv->lcd_config->lcd_control.mipi_config;
+       ret = sscanf(buf, "%d %d %d %d %d %d %d %d",
+               &val[0], &val[1], &val[2], &val[3],
+               &val[4], &val[5], &val[6], &val[7]);
+       if (ret >= 2) {
+               dsi_conf->lane_num = (unsigned char)val[0];
+               dsi_conf->bit_rate_max = val[1];
+               dsi_conf->factor_numerator = val[2];
+               dsi_conf->operation_mode_init = (unsigned char)val[3];
+               dsi_conf->operation_mode_display = (unsigned char)val[4];
+               dsi_conf->video_mode_type = (unsigned char)val[5];
+               dsi_conf->clk_lp_continuous = (unsigned char)val[6];
+               dsi_conf->phy_stop_wait = (unsigned char)val[7];
+               pr_info("set mipi_dsi config:\n"
+                       "lane_num=%d, bit_rate_max=%dMhz, factor_numerator=%d\n"
+                       "operation_mode_init=%d, operation_mode_display=%d\n"
+                       "video_mode_type=%d\n"
+                       "clk_lp_continuous=%d, phy_stop_wait=%d\n\n",
+                       dsi_conf->lane_num,
+                       dsi_conf->bit_rate_max,
+                       dsi_conf->factor_numerator,
+                       dsi_conf->operation_mode_init,
+                       dsi_conf->operation_mode_display,
+                       dsi_conf->video_mode_type,
+                       dsi_conf->clk_lp_continuous,
+                       dsi_conf->phy_stop_wait);
+               lcd_debug_config_update();
+       } else {
+               pr_info("invalid data\n");
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+static ssize_t lcd_edp_debug_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       pr_info("to do\n");
+
+       return count;
+}
+
+static void lcd_phy_config_update(unsigned int *para, int cnt)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       struct lvds_config_s *lvdsconf;
+       int type;
+       unsigned int data32, vswing, preem, ext_pullup;
+
+       pconf = lcd_drv->lcd_config;
+       type = pconf->lcd_basic.lcd_type;
+       switch (type) {
+       case LCD_LVDS:
+               lvdsconf = pconf->lcd_control.lvds_config;
+               if (cnt == 4) {
+                       if ((para[0] > 7) || (para[1] > 7) ||
+                               (para[2] > 3) || (para[3] > 7)) {
+                               LCDERR("%s: wrong value:\n", __func__);
+                               pr_info("vswing=0x%x, preemphasis=0x%x\n",
+                                       para[0], para[1]);
+                               pr_info("clk_vswing=0x%x, clk_preem=0x%x\n",
+                                       para[2], para[3]);
+                               return;
+                       }
+
+                       lvdsconf->phy_vswing = para[0];
+                       lvdsconf->phy_preem = para[1];
+                       lvdsconf->phy_clk_vswing = para[2];
+                       lvdsconf->phy_clk_preem = para[3];
+
+                       data32 = lcd_hiu_read(HHI_DIF_CSI_PHY_CNTL1);
+                       data32 &= ~((0x7 << 26) | (0x7 << 0));
+                       data32 |= ((para[0] << 26) | (para[1] << 0));
+                       lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, data32);
+                       data32 = lcd_hiu_read(HHI_DIF_CSI_PHY_CNTL3);
+                       data32 &= ~((0x3 << 8) | (0x7 << 5));
+                       data32 |= ((para[2] << 8) | (para[3] << 5));
+                       lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, data32);
+
+                       LCDPR("%s:\n", __func__);
+                       pr_info("vswing=0x%x, preemphasis=0x%x\n",
+                               para[0], para[1]);
+                       pr_info("clk_vswing=0x%x, clk_preemphasis=0x%x\n",
+                               para[2], para[3]);
+               } else if (cnt == 2) {
+                       if ((para[0] > 7) || (para[1] > 7)) {
+                               LCDERR("%s: wrong value:\n", __func__);
+                               pr_info("vswing=0x%x, preemphasis=0x%x\n",
+                                       para[0], para[1]);
+                               return;
+                       }
+
+                       lvdsconf->phy_vswing = para[0];
+                       lvdsconf->phy_preem = para[1];
+
+                       data32 = lcd_hiu_read(HHI_DIF_CSI_PHY_CNTL1);
+                       data32 &= ~((0x7 << 26) | (0x7 << 0));
+                       data32 |= ((para[0] << 26) | (para[1] << 0));
+                       lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, data32);
+
+                       LCDPR("%s: vswing=0x%x, preemphasis=0x%x\n",
+                               __func__, para[0], para[1]);
+               } else {
+                       LCDERR("%s: invalid parameters cnt: %d\n",
+                               __func__, cnt);
+               }
+               break;
+       case LCD_VBYONE:
+               if (cnt >= 2) {
+                       ext_pullup = (para[0] >> 4) & 1;
+                       vswing = para[0] & 0xf;
+                       preem = para[1];
+                       if ((vswing > 7) || (preem > 7)) {
+                               LCDERR("%s: wrong value:\n", __func__);
+                               pr_info("vswing=0x%x, preemphasis=0x%x\n",
+                                       vswing, preem);
+                               return;
+                       }
+
+                       pconf->lcd_control.vbyone_config->phy_vswing = para[0];
+                       pconf->lcd_control.vbyone_config->phy_preem = para[1];
+
+                       data32 = lcd_hiu_read(HHI_DIF_CSI_PHY_CNTL1);
+                       data32 &= ~((0x7 << 3) | (1 << 10));
+                       data32 |= ((vswing << 3) | (ext_pullup << 10));
+                       if (ext_pullup)
+                               data32 &= ~(1 << 15);
+                       lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, data32);
+                       data32 =  lcd_hiu_read(HHI_DIF_CSI_PHY_CNTL2);
+                       data32 &= ~(0x7 << 20);
+                       data32 |= (preem << 20);
+                       lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, data32);
+
+                       LCDPR("%s: vswing=0x%x, preemphasis=0x%x\n",
+                               __func__, para[0], para[1]);
+               } else {
+                       LCDERR("%s: invalid parameters cnt: %d\n",
+                               __func__, cnt);
+               }
+               break;
+       default:
+               LCDERR("%s: not support lcd_type: %s\n",
+                       __func__, lcd_type_type_to_str(type));
+               break;
+       }
+}
+
+static ssize_t lcd_phy_debug_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       unsigned int vswing = 0xff, preem = 0xff;
+       unsigned int clk_vswing = 0xff, clk_preem = 0xff;
+       ssize_t len = 0;
+
+       pconf = lcd_drv->lcd_config;
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_LVDS:
+               vswing = pconf->lcd_control.lvds_config->phy_vswing;
+               preem = pconf->lcd_control.lvds_config->phy_preem;
+               clk_vswing = pconf->lcd_control.lvds_config->phy_clk_vswing;
+               clk_preem = pconf->lcd_control.lvds_config->phy_clk_preem;
+               len += sprintf(buf+len, "%s:\n", __func__);
+               len += sprintf(buf+len, "vswing=0x%x, preemphasis=0x%x\n",
+                       vswing, preem);
+               len += sprintf(buf+len,
+                       "clk_vswing=0x%x, clk_preemphasis=0x%x\n",
+                       clk_vswing, clk_preem);
+               break;
+       case LCD_VBYONE:
+               vswing = pconf->lcd_control.vbyone_config->phy_vswing;
+               preem = pconf->lcd_control.vbyone_config->phy_preem;
+               len += sprintf(buf+len, "%s:\n", __func__);
+               len += sprintf(buf+len, "vswing=0x%x, preemphasis=0x%x\n",
+                       vswing, preem);
+               break;
+       default:
+               len = sprintf(buf, "%s: invalid lcd_type: %d\n",
+                       __func__, pconf->lcd_basic.lcd_type);
+               break;
+       }
+       return len;
+}
+
+static ssize_t lcd_phy_debug_store(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       int ret = 0;
+       unsigned int para[4];
+
+       ret = sscanf(buf, "%x %x %x %x",
+               &para[0], &para[1], &para[2], &para[3]);
+
+       if (ret == 4) {
+               lcd_phy_config_update(para, 4);
+       } else if (ret == 2) {
+               lcd_phy_config_update(para, 2);
+       } else {
+               pr_info("invalid data\n");
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+static struct class_attribute lcd_interface_debug_class_attrs[] = {
+       __ATTR(ttl,    0644,
+               lcd_ttl_debug_show, lcd_ttl_debug_store),
+       __ATTR(lvds,   0644,
+               lcd_lvds_debug_show, lcd_lvds_debug_store),
+       __ATTR(vbyone, 0644,
+               lcd_vx1_debug_show, lcd_vx1_debug_store),
+       __ATTR(mipi,   0644,
+               lcd_mipi_debug_show, lcd_mipi_debug_store),
+       __ATTR(edp,    0644,
+               lcd_edp_debug_show, lcd_edp_debug_store),
+};
+
+static struct class_attribute lcd_phy_debug_class_attrs[] = {
+       __ATTR(phy,    0644,
+               lcd_phy_debug_show, lcd_phy_debug_store),
+};
+
+static int lcd_black_screen_notifier(struct notifier_block *nb,
+               unsigned long event, void *data)
+{
+       if ((event & LCD_EVENT_BLACK_SCREEN) == 0)
+               return NOTIFY_DONE;
+       if (lcd_debug_print_flag)
+               LCDPR("%s: 0x%lx\n", __func__, event);
+
+       lcd_mute_setting(1);
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block lcd_black_screen_nb = {
+       .notifier_call = lcd_black_screen_notifier,
+       .priority = LCD_PRIORITY_BLACK_SCREEN,
+};
+
+int lcd_class_creat(void)
+{
+       int i;
+       int ret;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       int type;
+
+       ret = aml_lcd_notifier_register(&lcd_black_screen_nb);
+       if (ret)
+               LCDERR("register lcd_black_screen_notifier failed\n");
+
+       lcd_drv->lcd_test_pattern_restore = lcd_test_check;
+
+       lcd_drv->lcd_debug_class = class_create(THIS_MODULE, "lcd");
+       if (IS_ERR(lcd_drv->lcd_debug_class)) {
+               LCDERR("create lcd debug class fail\n");
+               return -1;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(lcd_debug_class_attrs); i++) {
+               if (class_create_file(lcd_drv->lcd_debug_class,
+                       &lcd_debug_class_attrs[i])) {
+                       LCDERR("create lcd debug attribute %s fail\n",
+                               lcd_debug_class_attrs[i].attr.name);
+               }
+       }
+
+       type = lcd_drv->lcd_config->lcd_basic.lcd_type;
+       for (i = 0; i < ARRAY_SIZE(lcd_interface_debug_class_attrs); i++) {
+               if (strcmp(lcd_interface_debug_class_attrs[i].attr.name,
+                       lcd_type_type_to_str(type)))
+                       continue;
+               if (class_create_file(lcd_drv->lcd_debug_class,
+                       &lcd_interface_debug_class_attrs[i])) {
+                       LCDERR("create lcd_interface debug attribute %s fail\n",
+                               lcd_interface_debug_class_attrs[i].attr.name);
+               }
+       }
+
+       switch (type) {
+       case LCD_LVDS:
+       case LCD_VBYONE:
+               for (i = 0; i < ARRAY_SIZE(lcd_phy_debug_class_attrs); i++) {
+                       if (class_create_file(lcd_drv->lcd_debug_class,
+                               &lcd_phy_debug_class_attrs[i])) {
+                               LCDERR("create phy debug attribute %s fail\n",
+                                       lcd_phy_debug_class_attrs[i].attr.name);
+                       }
+               }
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+int lcd_class_remove(void)
+{
+       int i;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       int type;
+
+       for (i = 0; i < ARRAY_SIZE(lcd_debug_class_attrs); i++) {
+               class_remove_file(lcd_drv->lcd_debug_class,
+                       &lcd_debug_class_attrs[i]);
+       }
+
+       type = lcd_drv->lcd_config->lcd_basic.lcd_type;
+       for (i = 0; i < ARRAY_SIZE(lcd_interface_debug_class_attrs); i++) {
+               if (strcmp(lcd_interface_debug_class_attrs[i].attr.name,
+                       lcd_type_type_to_str(type)))
+                       continue;
+               class_remove_file(lcd_drv->lcd_debug_class,
+                       &lcd_interface_debug_class_attrs[i]);
+       }
+
+       class_destroy(lcd_drv->lcd_debug_class);
+       lcd_drv->lcd_debug_class = NULL;
+
+       return 0;
+}
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_extern/Kconfig b/drivers/amlogic/media/vout/lcd/lcd_extern/Kconfig
new file mode 100644 (file)
index 0000000..d357b03
--- /dev/null
@@ -0,0 +1,77 @@
+
+menuconfig AMLOGIC_LCD_EXTERN
+       bool "LCD external driver support"
+       default n
+       help
+               Amlogic LCD external driver support, such as i2c initial
+
+config AMLOGIC_LCD_EXTERN_I2C_T5800Q
+       bool "lcd external i2c T5800Q init driver"
+       default n
+       depends on AMLOGIC_LCD_EXTERN
+       help
+               Amlogic LCD external i2c_T5800Q init driver support
+               Once the power on, according to the timing requirements,
+               through the i2c interface to write data to the LCD,
+               make its initialization
+
+config AMLOGIC_LCD_EXTERN_I2C_DLPC3439
+       bool "lcd external i2c DLPC3439 init driver"
+       default n
+       depends on AMLOGIC_LCD_EXTERN
+       help
+               Amlogic LCD external i2c_DLPC3439 init driver support
+               Once the power on, according to the timing requirements,
+               through the i2c interface to write data to the LCD,
+               make its initialization
+
+config AMLOGIC_LCD_EXTERN_I2C_ANX6345
+       bool "lcd external i2c ANX6345 init driver"
+       default n
+       depends on AMLOGIC_LCD_EXTERN
+       help
+               Amlogic LCD external i2c_ANX6345 init driver support
+               Once the power on, according to the timing requirements,
+               through the i2c interface to write data to the LCD,
+               make its initialization
+
+config AMLOGIC_LCD_EXTERN_I2C_TC101
+       bool "lcd external i2c TC101 init driver"
+       default n
+       depends on AMLOGIC_LCD_EXTERN
+       help
+               Amlogic LCD external i2c_TC101 init driver support
+               Once the power on, according to the timing requirements,
+               through the i2c interface to write data to the LCD,
+               make its initialization
+
+config AMLOGIC_LCD_EXTERN_SPI_LD070WS2
+       bool "lcd external spi LD070WS2 init driver"
+       default n
+       depends on AMLOGIC_LCD_EXTERN
+       help
+               Amlogic LCD external spi_LD070WS2 init driver support
+               Once the power on, according to the timing requirements,
+               through the spi interface to write data to the LCD,
+               make its initialization
+
+config AMLOGIC_LCD_EXTERN_MIPI_KD080D13
+       bool "lcd external mipi KD080D13 init driver"
+       default n
+       depends on AMLOGIC_LCD_EXTERN
+       help
+               Amlogic LCD external mipi_KD080D13 init driver support
+               Once the power on, according to the timing requirements,
+               through the mipi interface to write data to the LCD,
+               make its initialization
+
+config AMLOGIC_LCD_EXTERN_MIPI_N070ICN
+       bool "lcd external mipi N070ICN init driver"
+       default n
+       depends on AMLOGIC_LCD_EXTERN
+       help
+               Amlogic LCD external mipi_N070ICN init driver support
+               Once the power on, according to the timing requirements,
+               through the mipi interface to write data to the LCD,
+               make its initialization
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_extern/Makefile b/drivers/amlogic/media/vout/lcd/lcd_extern/Makefile
new file mode 100644 (file)
index 0000000..79facc6
--- /dev/null
@@ -0,0 +1,6 @@
+obj-$(CONFIG_AMLOGIC_LCD_EXTERN)                       += lcd_extern.o ext_default.o
+obj-$(CONFIG_AMLOGIC_LCD_EXTERN_I2C_T5800Q)            += i2c_T5800Q.o
+obj-$(CONFIG_AMLOGIC_LCD_EXTERN_SPI_LD070WS2)  += spi_LD070WS2.o
+obj-$(CONFIG_AMLOGIC_LCD_EXTERN_I2C_DLPC3439)          += i2c_DLPC3439.o
+obj-$(CONFIG_AMLOGIC_LCD_EXTERN_MIPI_KD080D13)         += mipi_KD080D13.o
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_extern/ext_default.c b/drivers/amlogic/media/vout/lcd/lcd_extern/ext_default.c
new file mode 100644 (file)
index 0000000..7c74074
--- /dev/null
@@ -0,0 +1,453 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/ext_default.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+
+#define LCD_EXTERN_INDEX          0
+#define LCD_EXTERN_NAME           "ext_default"
+
+#define LCD_EXTERN_TYPE           LCD_EXTERN_I2C
+
+#define LCD_EXTERN_I2C_ADDR       (0x1c) /* 7bit address */
+#define LCD_EXTERN_I2C_ADDR2      (0xff) /* 7bit address */
+#define LCD_EXTERN_I2C_BUS        AML_I2C_MASTER_A
+
+#define SPI_GPIO_CS               0 /* index */
+#define SPI_GPIO_CLK              1 /* index */
+#define SPI_GPIO_DATA             2 /* index */
+#define SPI_CLK_FREQ              10000 /* Hz */
+#define SPI_CLK_POL               1
+
+static struct i2c_client *aml_default_i2c_client;
+static struct i2c_client *aml_default_i2c2_client;
+static struct lcd_extern_config_s *ext_config;
+
+#define LCD_EXTERN_CMD_SIZE        9
+static unsigned char init_on_table[] = {
+       0x00, 0x20, 0x01, 0x02, 0x00, 0x40, 0xFF, 0x00, 0x00,
+       0x00, 0x80, 0x02, 0x00, 0x40, 0x62, 0x51, 0x73, 0x00,
+       0x00, 0x61, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0xC1, 0x05, 0x0F, 0x00, 0x08, 0x70, 0x00, 0x00,
+       0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x3D, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0xED, 0x0D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x23, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,  /* delay 10ms */
+       0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  /* ending */
+};
+
+static unsigned char init_off_table[] = {
+       0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  /* ending */
+};
+
+static int lcd_extern_i2c_write(struct i2c_client *i2client,
+               unsigned char *buff, unsigned int len)
+{
+       int ret = 0;
+       struct i2c_msg msg[] = {
+               {
+                       .addr = i2client->addr,
+                       .flags = 0,
+                       .len = len,
+                       .buf = buff,
+               }
+       };
+
+       ret = i2c_transfer(i2client->adapter, msg, 1);
+       if (ret < 0)
+               EXTERR("i2c write failed [addr 0x%02x]\n", i2client->addr);
+
+       return ret;
+}
+
+static int lcd_extern_reg_read(unsigned char reg, unsigned char *buf)
+{
+       int ret = 0;
+
+       return ret;
+}
+
+static int lcd_extern_reg_write(unsigned char reg, unsigned char value)
+{
+       int ret = 0;
+
+       return ret;
+}
+
+static int lcd_extern_spi_write(unsigned char *buf, int len)
+{
+       EXTPR("to do\n");
+       return 0;
+}
+
+static int lcd_extern_power_cmd(unsigned char *init_table)
+{
+       int i = 0, len;
+       int ret = 0;
+
+       len = ext_config->cmd_size;
+       if (len < 1) {
+               EXTERR("%s: cmd_size %d is invalid\n", __func__, len);
+               return -1;
+       }
+       if (len == LCD_EXTERN_DYNAMIC_LEN) {
+               switch (ext_config->type) {
+               case LCD_EXTERN_I2C:
+                       while (i <= LCD_EXTERN_INIT_TABLE_MAX) {
+                               if (init_table[i] == LCD_EXTERN_INIT_END) {
+                                       break;
+                               } else if (init_table[i] ==
+                                               LCD_EXTERN_INIT_NONE) {
+                                       /* do nothing, only for delay */
+                                       if (init_table[i+1] > 0)
+                                               mdelay(init_table[i+1]);
+                                       i += 2;
+                               } else if (init_table[i] ==
+                                               LCD_EXTERN_INIT_GPIO) {
+                                       if (init_table[i+1] < LCD_GPIO_MAX) {
+                                               lcd_extern_gpio_set(
+                                                       init_table[i+1],
+                                                       init_table[i+2]);
+                                       }
+                                       if (init_table[i+3] > 0)
+                                               mdelay(init_table[i+3]);
+                                       i += 4;
+                               } else if (init_table[i] ==
+                                               LCD_EXTERN_INIT_CMD) {
+                                       ret = lcd_extern_i2c_write(
+                                               aml_default_i2c_client,
+                                               &init_table[i+2],
+                                               init_table[i+1]-1);
+                                       if (init_table[i+init_table[i+1]+1] > 0)
+                                               mdelay(init_table[i+
+                                                       init_table[i+1]+1]);
+                                       i += (init_table[i+1] + 2);
+                               } else if (init_table[i] ==
+                                               LCD_EXTERN_INIT_CMD2) {
+                                       ret = lcd_extern_i2c_write(
+                                               aml_default_i2c2_client,
+                                               &init_table[i+2],
+                                               init_table[i+1]-1);
+                                       if (init_table[i+init_table[i+1]+1] > 0)
+                                               mdelay(init_table[i+
+                                                       init_table[i+1]+1]);
+                                       i += (init_table[i+1] + 2);
+                               } else {
+                                       EXTERR("%s(%d: %s): type %d invalid\n",
+                                               __func__, ext_config->index,
+                                               ext_config->name,
+                                               ext_config->type);
+                               }
+                       }
+                       break;
+               case LCD_EXTERN_SPI:
+                       while (i <= LCD_EXTERN_INIT_TABLE_MAX) {
+                               if (init_table[i] == LCD_EXTERN_INIT_END) {
+                                       break;
+                               } else if (init_table[i] ==
+                                       LCD_EXTERN_INIT_NONE) {
+                                       /* do nothing, only for delay */
+                                       if (init_table[i+1] > 0)
+                                               mdelay(init_table[i+1]);
+                                       i += 2;
+                               } else if (init_table[i] ==
+                                       LCD_EXTERN_INIT_GPIO) {
+                                       if (init_table[i+1] < LCD_GPIO_MAX) {
+                                               lcd_extern_gpio_set(
+                                                       init_table[i+1],
+                                                       init_table[i+2]);
+                                       }
+                                       if (init_table[i+3] > 0)
+                                               mdelay(init_table[i+3]);
+                                       i += 4;
+                               } else if (init_table[i] ==
+                                       LCD_EXTERN_INIT_CMD) {
+                                       ret = lcd_extern_spi_write(
+                                               &init_table[i+2],
+                                               init_table[i+1]-1);
+                                       if (init_table[i+init_table[i+1]+1] > 0)
+                                               mdelay(init_table[i+
+                                                       init_table[i+1]+1]);
+                                       i += (init_table[i+1] + 2);
+                               } else {
+                                       EXTERR("%s(%d: %s): type %d invalid\n",
+                                               __func__, ext_config->index,
+                                               ext_config->name,
+                                               ext_config->type);
+                               }
+                       }
+                       break;
+               default:
+                       EXTERR("%s(%d: %s): extern_type %d is not support\n",
+                               __func__, ext_config->index,
+                               ext_config->name, ext_config->type);
+                       break;
+               }
+       } else {
+               switch (ext_config->type) {
+               case LCD_EXTERN_I2C:
+                       while (i <= LCD_EXTERN_INIT_TABLE_MAX) {
+                               if (init_table[i] == LCD_EXTERN_INIT_END) {
+                                       break;
+                               } else if (init_table[i] ==
+                                       LCD_EXTERN_INIT_NONE) {
+                                       /* do nothing, only for delay */
+                               } else if (init_table[i] ==
+                                       LCD_EXTERN_INIT_GPIO) {
+                                       if (init_table[i+1] < LCD_GPIO_MAX) {
+                                               lcd_extern_gpio_set(
+                                                       init_table[i+1],
+                                                       init_table[i+2]);
+                                       }
+                               } else if (init_table[i] ==
+                                               LCD_EXTERN_INIT_CMD) {
+                                       ret = lcd_extern_i2c_write(
+                                               aml_default_i2c_client,
+                                               &init_table[i+1], (len-2));
+                               } else if (init_table[i] ==
+                                               LCD_EXTERN_INIT_CMD2) {
+                                       ret = lcd_extern_i2c_write(
+                                               aml_default_i2c2_client,
+                                               &init_table[i+1], (len-2));
+                               } else {
+                                       EXTERR("%s(%d: %s): type %d invalid\n",
+                                               __func__, ext_config->index,
+                                               ext_config->name,
+                                               ext_config->type);
+                               }
+                               if (init_table[i+len-1] > 0)
+                                       mdelay(init_table[i+len-1]);
+                               i += len;
+                       }
+                       break;
+               case LCD_EXTERN_SPI:
+                       while (i <= LCD_EXTERN_INIT_TABLE_MAX) {
+                               if (init_table[i] == LCD_EXTERN_INIT_END) {
+                                       break;
+                               } else if (init_table[i] ==
+                                               LCD_EXTERN_INIT_NONE) {
+                                       /* do nothing, only for delay */
+                               } else if (init_table[i] ==
+                                               LCD_EXTERN_INIT_GPIO) {
+                                       if (init_table[i+1] < LCD_GPIO_MAX) {
+                                               lcd_extern_gpio_set(
+                                                       init_table[i+1],
+                                                       init_table[i+2]);
+                                       }
+                               } else if (init_table[i] ==
+                                               LCD_EXTERN_INIT_CMD) {
+                                       ret = lcd_extern_spi_write(
+                                               &init_table[i+1], (len-1));
+                               } else {
+                                       EXTERR("%s(%d: %s): type %d invalid\n",
+                                               __func__, ext_config->index,
+                                               ext_config->name,
+                                               ext_config->type);
+                               }
+                               if (init_table[i+len-1] > 0)
+                                       mdelay(init_table[i+len-1]);
+                               i += len;
+                       }
+                       break;
+               default:
+                       EXTERR("%s(%d: %s): extern_type %d is not support\n",
+                               __func__, ext_config->index,
+                               ext_config->name, ext_config->type);
+                       break;
+               }
+               }
+       return ret;
+}
+
+static int lcd_extern_power_ctrl(int flag)
+{
+       int ret = 0;
+
+       if (flag)
+               ret = lcd_extern_power_cmd(ext_config->table_init_on);
+       else
+               ret = lcd_extern_power_cmd(ext_config->table_init_off);
+
+       EXTPR("%s(%d: %s): %d\n",
+               __func__, ext_config->index, ext_config->name, flag);
+       return ret;
+}
+
+static int lcd_extern_power_on(void)
+{
+       int ret;
+
+       ret = lcd_extern_power_ctrl(1);
+       return ret;
+}
+
+static int lcd_extern_power_off(void)
+{
+       int ret;
+
+       ret = lcd_extern_power_ctrl(0);
+       return ret;
+}
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       if (ext_drv == NULL) {
+               EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+               return -1;
+       }
+
+       if (ext_drv->config.type == LCD_EXTERN_MAX) { /* default for no dts */
+               ext_drv->config.index = LCD_EXTERN_INDEX;
+               ext_drv->config.type = LCD_EXTERN_TYPE;
+               strcpy(ext_drv->config.name, LCD_EXTERN_NAME);
+               ext_drv->config.cmd_size = LCD_EXTERN_CMD_SIZE;
+               switch (ext_drv->config.type) {
+               case LCD_EXTERN_I2C:
+                       ext_drv->config.i2c_addr = LCD_EXTERN_I2C_ADDR;
+                       ext_drv->config.i2c_addr2 = LCD_EXTERN_I2C_ADDR2;
+                       ext_drv->config.i2c_bus = LCD_EXTERN_I2C_BUS;
+                       break;
+               case LCD_EXTERN_SPI:
+                       ext_drv->config.spi_gpio_cs = SPI_GPIO_CS;
+                       ext_drv->config.spi_gpio_clk = SPI_GPIO_CLK;
+                       ext_drv->config.spi_gpio_data = SPI_GPIO_DATA;
+                       ext_drv->config.spi_clk_freq = SPI_CLK_FREQ;
+                       ext_drv->config.spi_clk_pol = SPI_CLK_POL;
+                       break;
+               default:
+                       break;
+               }
+       }
+       if (ext_drv->config.table_init_loaded == 0) {
+               ext_drv->config.table_init_on  = init_on_table;
+               ext_drv->config.table_init_off = init_off_table;
+       }
+       ext_drv->reg_read  = lcd_extern_reg_read;
+       ext_drv->reg_write = lcd_extern_reg_write;
+       ext_drv->power_on  = lcd_extern_power_on;
+       ext_drv->power_off = lcd_extern_power_off;
+
+       return 0;
+}
+
+static int aml_default_i2c_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+               EXTERR("%s: functionality check failed\n", __func__);
+       else
+               aml_default_i2c_client = client;
+
+       EXTPR("%s OK\n", __func__);
+       return 0;
+}
+
+static int aml_default_i2c_remove(struct i2c_client *client)
+{
+       return 0;
+}
+
+static const struct i2c_device_id aml_default_i2c_id[] = {
+       {LCD_EXTERN_NAME, 0},
+       { }
+};
+/* MODULE_DEVICE_TABLE(i2c, aml_T5800Q_id); */
+
+static struct i2c_driver aml_default_i2c_driver = {
+       .probe    = aml_default_i2c_probe,
+       .remove   = aml_default_i2c_remove,
+       .id_table = aml_default_i2c_id,
+       .driver = {
+               .name = LCD_EXTERN_NAME,
+               .owner = THIS_MODULE,
+       },
+};
+
+int aml_lcd_extern_default_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       struct i2c_board_info i2c_info;
+       struct i2c_adapter *adapter;
+       struct i2c_client *i2c_client;
+       int ret = 0;
+
+       ext_config = &ext_drv->config;
+
+       switch (ext_drv->config.type) {
+       case LCD_EXTERN_I2C:
+               aml_default_i2c_client = NULL;
+               aml_default_i2c2_client = NULL;
+               memset(&i2c_info, 0, sizeof(i2c_info));
+               adapter = i2c_get_adapter(ext_drv->config.i2c_bus);
+               if (!adapter) {
+                       EXTERR("%s failed to get i2c adapter\n",
+                               ext_drv->config.name);
+                       return -1;
+               }
+
+               strncpy(i2c_info.type, ext_drv->config.name, I2C_NAME_SIZE);
+               i2c_info.addr = ext_drv->config.i2c_addr;
+               i2c_info.platform_data = &ext_drv->config;
+               i2c_info.flags = 0;
+               if (i2c_info.addr > 0x7f) {
+                       EXTERR("%s invalid i2c address: 0x%02x\n",
+                               ext_drv->config.name, ext_drv->config.i2c_addr);
+                       return -1;
+               }
+               i2c_client = i2c_new_device(adapter, &i2c_info);
+               if (!i2c_client) {
+                       EXTERR("%s failed to new i2c device\n",
+                               ext_drv->config.name);
+               } else {
+                       if (lcd_debug_print_flag) {
+                               EXTPR("%s new i2c device succeed\n",
+                                       ext_drv->config.name);
+                       }
+               }
+
+               if (!aml_default_i2c_client) {
+                       ret = i2c_add_driver(&aml_default_i2c_driver);
+                       if (ret) {
+                               EXTERR("%s add i2c_driver failed\n",
+                                       ext_drv->config.name);
+                               return -1;
+                       }
+               }
+               break;
+       case LCD_EXTERN_SPI:
+               break;
+       default:
+               break;
+       }
+
+       ret = lcd_extern_driver_update(ext_drv);
+
+       if (lcd_debug_print_flag)
+               EXTPR("%s: %d\n", __func__, ret);
+       return ret;
+}
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_extern/i2c_DLPC3439.c b/drivers/amlogic/media/vout/lcd/lcd_extern/i2c_DLPC3439.c
new file mode 100644 (file)
index 0000000..d666df1
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/i2c_DLPC3439.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+
+#define LCD_EXTERN_NAME                        "i2c_DLPC3439"
+
+static struct i2c_client *aml_DLPC3439_i2c_client;
+static struct lcd_extern_config_s *ext_config;
+
+
+       /* Write: ImageCrop: 1920x1080
+        * W 36 10 00 00 00 00 80 07 38 04
+        */
+static unsigned char data_1[] = {0x10, 0x00, 0x00, 0x00, 0x00,
+               0x80, 0x07, 0x38, 0x04};
+       /* Write: DisplaySize: 1920x1080
+        * W 36 12 80 07 38 04
+        */
+static unsigned char data_2[] = {0x12, 0x80, 0x07, 0x38, 0x04};
+       /* Write: InputImageSize: 1920x1080
+        * W 36 2e 80 07 38 04
+        */
+static unsigned char data_3[] = {0x2e, 0x80, 0x07, 0x38, 0x04};
+       /* Write: InputSourceSelect; 0 = External Video Port
+        * W 36 05 00
+        */
+static unsigned char data_4[] = {0x05, 0x00};
+       /* Write: VideoSourceFormatSelect: 0x43=RGB888
+        * W 36 07 43
+        */
+static unsigned char data_5[] = {0x07, 0x43};
+
+static int lcd_extern_i2c_write(struct i2c_client *i2client,
+               unsigned char *buff, unsigned int len)
+{
+       int ret = 0;
+       struct i2c_msg msg[] = {
+               {
+                       .addr = i2client->addr,
+                       .flags = 0,
+                       .len = len,
+                       .buf = buff,
+               }
+       };
+
+       ret = i2c_transfer(i2client->adapter, msg, 1);
+       if (ret < 0)
+               EXTERR("i2c write failed [addr 0x%02x]\n", i2client->addr);
+
+       return ret;
+}
+
+static int lcd_extern_power_on(void)
+{
+       int ret = 0;
+
+       lcd_extern_i2c_write(aml_DLPC3439_i2c_client, data_1, 9);
+       lcd_extern_i2c_write(aml_DLPC3439_i2c_client, data_2, 5);
+       lcd_extern_i2c_write(aml_DLPC3439_i2c_client, data_3, 5);
+       lcd_extern_i2c_write(aml_DLPC3439_i2c_client, data_4, 2);
+       lcd_extern_i2c_write(aml_DLPC3439_i2c_client, data_5, 2);
+
+       EXTPR("%s\n", __func__);
+       return ret;
+}
+
+static int lcd_extern_power_off(void)
+{
+       int ret = 0;
+
+       return ret;
+}
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       int ret = 0;
+
+       if (ext_drv) {
+               ext_drv->power_on  = lcd_extern_power_on;
+               ext_drv->power_off = lcd_extern_power_off;
+       } else {
+               EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+               ret = -1;
+       }
+
+       return ret;
+}
+
+static int aml_DLPC3439_i2c_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+               EXTERR("%s: functionality check failed\n", __func__);
+       else
+               aml_DLPC3439_i2c_client = client;
+
+       EXTPR("%s OK\n", __func__);
+       return 0;
+}
+
+static int aml_DLPC3439_i2c_remove(struct i2c_client *client)
+{
+       return 0;
+}
+
+static const struct i2c_device_id aml_DLPC3439_i2c_id[] = {
+       {LCD_EXTERN_NAME, 0},
+       { }
+};
+/* MODULE_DEVICE_TABLE(i2c, aml_DLPC3439_id); */
+
+static struct i2c_driver aml_DLPC3439_i2c_driver = {
+       .probe    = aml_DLPC3439_i2c_probe,
+       .remove   = aml_DLPC3439_i2c_remove,
+       .id_table = aml_DLPC3439_i2c_id,
+       .driver = {
+               .name = LCD_EXTERN_NAME,
+               .owner = THIS_MODULE,
+       },
+};
+
+int aml_lcd_extern_i2c_DLPC3439_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       struct i2c_board_info i2c_info;
+       struct i2c_adapter *adapter;
+       struct i2c_client *i2c_client;
+       int ret = 0;
+
+       ext_config = &ext_drv->config;
+       memset(&i2c_info, 0, sizeof(i2c_info));
+
+       adapter = i2c_get_adapter(ext_drv->config.i2c_bus);
+       if (!adapter) {
+               EXTERR("%s failed to get i2c adapter\n", ext_drv->config.name);
+               return -1;
+       }
+
+       strncpy(i2c_info.type, ext_drv->config.name, I2C_NAME_SIZE);
+       i2c_info.addr = ext_drv->config.i2c_addr;
+       i2c_info.platform_data = &ext_drv->config;
+       i2c_info.flags = 0;
+       if (i2c_info.addr > 0x7f) {
+               EXTERR("%s invalid i2c address: 0x%02x\n",
+                       ext_drv->config.name, ext_drv->config.i2c_addr);
+               return -1;
+       }
+       i2c_client = i2c_new_device(adapter, &i2c_info);
+       if (!i2c_client) {
+               EXTERR("%s failed to new i2c device\n", ext_drv->config.name);
+       } else {
+               if (lcd_debug_print_flag) {
+                       EXTPR("%s new i2c device succeed\n",
+                               ext_drv->config.name);
+               }
+       }
+
+       if (!aml_DLPC3439_i2c_client) {
+               ret = i2c_add_driver(&aml_DLPC3439_i2c_driver);
+               if (ret) {
+                       EXTERR("%s add i2c_driver failed\n",
+                               ext_drv->config.name);
+                       return -1;
+               }
+       }
+
+       ret = lcd_extern_driver_update(ext_drv);
+
+       if (lcd_debug_print_flag)
+               EXTPR("%s: %d\n", __func__, ret);
+       return ret;
+}
diff --git a/drivers/amlogic/media/vout/lcd/lcd_extern/i2c_T5800Q.c b/drivers/amlogic/media/vout/lcd/lcd_extern/i2c_T5800Q.c
new file mode 100644 (file)
index 0000000..66d337b
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/i2c_T5800Q.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+
+#define LCD_EXTERN_NAME                        "i2c_T5800Q"
+
+static struct i2c_client *aml_T5800Q_i2c_client;
+static struct lcd_extern_config_s *ext_config;
+
+#define LCD_EXTERN_CMD_SIZE        9
+static unsigned char init_on_table[] = {
+       0x00, 0x20, 0x01, 0x02, 0x00, 0x40, 0xFF, 0x00, 0x00,
+       0x00, 0x80, 0x02, 0x00, 0x40, 0x62, 0x51, 0x73, 0x00,
+       0x00, 0x61, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0xC1, 0x05, 0x0F, 0x00, 0x08, 0x70, 0x00, 0x00,
+       0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x3D, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0xED, 0x0D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x23, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,  /* delay 10ms */
+       0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  /* ending */
+};
+
+static unsigned char init_off_table[] = {
+       0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  /* ending */
+};
+
+static int lcd_extern_i2c_write(struct i2c_client *i2client,
+               unsigned char *buff, unsigned int len)
+{
+       int ret = 0;
+       struct i2c_msg msg[] = {
+               {
+                       .addr = i2client->addr,
+                       .flags = 0,
+                       .len = len,
+                       .buf = buff,
+               }
+       };
+
+       ret = i2c_transfer(i2client->adapter, msg, 1);
+       if (ret < 0)
+               EXTERR("i2c write failed [addr 0x%02x]\n", i2client->addr);
+
+       return ret;
+}
+#if 0
+static int lcd_extern_i2c_read(struct i2c_client *i2client,
+               unsigned char *buff, unsigned int len)
+{
+       int ret = 0;
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = i2client->addr,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = buff,
+               },
+               {
+                       .addr = i2client->addr,
+                       .flags = I2C_M_RD,
+                       .len = len,
+                       .buf = buff,
+               }
+       };
+
+       ret = i2c_transfer(i2client->adapter, msgs, 2);
+       if (ret < 0)
+               EXTERR("i2c read failed [addr 0x%02x]\n", i2client->addr);
+
+       return ret;
+}
+#endif
+
+static int lcd_extern_reg_read(unsigned char reg, unsigned char *buf)
+{
+       int ret = 0;
+
+       return ret;
+}
+
+static int lcd_extern_reg_write(unsigned char reg, unsigned char value)
+{
+       int ret = 0;
+
+       return ret;
+}
+
+static int lcd_extern_power_cmd(unsigned char *init_table)
+{
+       int i = 0, len;
+       int ret = 0;
+
+       len = ext_config->cmd_size;
+       if (len < 1) {
+               EXTERR("%s: cmd_size %d is invalid\n", __func__, len);
+               return -1;
+       }
+
+       while (i <= LCD_EXTERN_INIT_TABLE_MAX) {
+               if (init_table[i] == LCD_EXTERN_INIT_END) {
+                       break;
+               } else if (init_table[i] == LCD_EXTERN_INIT_NONE) {
+                       /* do nothing, only for delay */
+               } else if (init_table[i] == LCD_EXTERN_INIT_GPIO) {
+                       if (init_table[i+1] < LCD_GPIO_MAX) {
+                               lcd_extern_gpio_set(init_table[i+1],
+                                       init_table[i+2]);
+                       }
+               } else if (init_table[i] == LCD_EXTERN_INIT_CMD) {
+                       ret = lcd_extern_i2c_write(aml_T5800Q_i2c_client,
+                               &init_table[i+1], (len-2));
+               } else {
+                       EXTERR("%s(%d: %s): power_type %d is invalid\n",
+                               __func__, ext_config->index,
+                               ext_config->name, ext_config->type);
+               }
+               if (init_table[i+len-1] > 0)
+                       mdelay(init_table[i+len-1]);
+               i += len;
+       }
+
+       return ret;
+}
+
+static int lcd_extern_power_ctrl(int flag)
+{
+       int ret = 0;
+
+       if (flag)
+               ret = lcd_extern_power_cmd(ext_config->table_init_on);
+       else
+               ret = lcd_extern_power_cmd(ext_config->table_init_off);
+
+       EXTPR("%s(%d: %s): %d\n",
+               __func__, ext_config->index, ext_config->name, flag);
+       return ret;
+}
+
+static int lcd_extern_power_on(void)
+{
+       int ret;
+
+       ret = lcd_extern_power_ctrl(1);
+       return ret;
+}
+
+static int lcd_extern_power_off(void)
+{
+       int ret;
+
+       ret = lcd_extern_power_ctrl(0);
+       return ret;
+}
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       if (ext_drv == NULL) {
+               EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+               return -1;
+       }
+
+       if (ext_drv->config.table_init_loaded == 0) {
+               ext_drv->config.table_init_on = init_on_table;
+               ext_drv->config.table_init_off = init_off_table;
+       }
+       ext_drv->reg_read  = lcd_extern_reg_read;
+       ext_drv->reg_write = lcd_extern_reg_write;
+       ext_drv->power_on  = lcd_extern_power_on;
+       ext_drv->power_off = lcd_extern_power_off;
+
+       return 0;
+}
+
+/* debug function */
+static const char *lcd_extern_debug_usage_str = {
+"Usage:\n"
+"    echo <reg> <> ... <> > write ; T5800Q i2c command write, 7 parameters without address\n"
+};
+
+static ssize_t lcd_extern_debug_help(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%s\n", lcd_extern_debug_usage_str);
+}
+
+static ssize_t lcd_extern_debug_write(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       unsigned int ret;
+       unsigned int temp[8];
+       unsigned char data[8];
+       int i;
+
+       memset(temp, 0, (sizeof(unsigned int) * 8));
+       ret = sscanf(buf, "%x %x %x %x %x %x %x",
+               &temp[0], &temp[1], &temp[2], &temp[3],
+               &temp[4], &temp[5], &temp[6]);
+       EXTPR("T5800Q i2c write:\n");
+       for (i = 0; i < 7; i++) {
+               data[i] = (unsigned char)temp[i];
+               pr_info("0x%02x ", data[i]);
+       }
+       pr_info("\n");
+       lcd_extern_i2c_write(aml_T5800Q_i2c_client, data, 7);
+
+       if (ret != 1 || ret != 2)
+               return -EINVAL;
+
+       return count;
+}
+
+static struct class_attribute lcd_extern_class_attrs[] = {
+       __ATTR(write, 0644,
+               lcd_extern_debug_help, lcd_extern_debug_write),
+};
+
+static struct class *debug_class;
+static int creat_lcd_extern_class(void)
+{
+       int i;
+
+       debug_class = class_create(THIS_MODULE, LCD_EXTERN_NAME);
+       if (IS_ERR(debug_class)) {
+               EXTERR("create debug class failed\n");
+               return -1;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(lcd_extern_class_attrs); i++) {
+               if (class_create_file(debug_class,
+                       &lcd_extern_class_attrs[i])) {
+                       EXTERR("create debug attribute %s failed\n",
+                               lcd_extern_class_attrs[i].attr.name);
+               }
+       }
+
+       return 0;
+}
+
+#if 0
+static int remove_lcd_extern_class(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(lcd_extern_class_attrs); i++)
+               class_remove_file(debug_class, &lcd_extern_class_attrs[i]);
+
+       class_destroy(debug_class);
+       debug_class = NULL;
+
+       return 0;
+}
+#endif
+/* ********************************************************* */
+
+static int aml_T5800Q_i2c_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+               EXTERR("%s: functionality check failed\n", __func__);
+       else
+               aml_T5800Q_i2c_client = client;
+
+       EXTPR("%s OK\n", __func__);
+       return 0;
+}
+
+static int aml_T5800Q_i2c_remove(struct i2c_client *client)
+{
+       return 0;
+}
+
+static const struct i2c_device_id aml_T5800Q_i2c_id[] = {
+       {LCD_EXTERN_NAME, 0},
+       { }
+};
+/* MODULE_DEVICE_TABLE(i2c, aml_T5800Q_id); */
+
+static struct i2c_driver aml_T5800Q_i2c_driver = {
+       .probe    = aml_T5800Q_i2c_probe,
+       .remove   = aml_T5800Q_i2c_remove,
+       .id_table = aml_T5800Q_i2c_id,
+       .driver = {
+               .name = LCD_EXTERN_NAME,
+               .owner = THIS_MODULE,
+       },
+};
+
+int aml_lcd_extern_i2c_T5800Q_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       struct i2c_board_info i2c_info;
+       struct i2c_adapter *adapter;
+       struct i2c_client *i2c_client;
+       int ret = 0;
+
+       ext_config = &ext_drv->config;
+       memset(&i2c_info, 0, sizeof(i2c_info));
+
+       adapter = i2c_get_adapter(ext_drv->config.i2c_bus);
+       if (!adapter) {
+               EXTERR("%s failed to get i2c adapter\n", ext_drv->config.name);
+               return -1;
+       }
+
+       strncpy(i2c_info.type, ext_drv->config.name, I2C_NAME_SIZE);
+       i2c_info.addr = ext_drv->config.i2c_addr;
+       i2c_info.platform_data = &ext_drv->config;
+       i2c_info.flags = 0;
+       if (i2c_info.addr > 0x7f) {
+               EXTERR("%s invalid i2c address: 0x%02x\n",
+                       ext_drv->config.name, ext_drv->config.i2c_addr);
+               return -1;
+       }
+       i2c_client = i2c_new_device(adapter, &i2c_info);
+       if (!i2c_client) {
+               EXTERR("%s failed to new i2c device\n", ext_drv->config.name);
+       } else {
+               if (lcd_debug_print_flag) {
+                       EXTPR("%s new i2c device succeed\n",
+                               ext_drv->config.name);
+               }
+       }
+
+       if (!aml_T5800Q_i2c_client) {
+               ret = i2c_add_driver(&aml_T5800Q_i2c_driver);
+               if (ret) {
+                       EXTERR("%s add i2c_driver failed\n",
+                               ext_drv->config.name);
+                       return -1;
+               }
+       }
+
+       ret = lcd_extern_driver_update(ext_drv);
+       creat_lcd_extern_class();
+
+       if (lcd_debug_print_flag)
+               EXTPR("%s: %d\n", __func__, ret);
+       return ret;
+}
diff --git a/drivers/amlogic/media/vout/lcd/lcd_extern/i2c_anx6345.c b/drivers/amlogic/media/vout/lcd/lcd_extern/i2c_anx6345.c
new file mode 100644 (file)
index 0000000..31280a6
--- /dev/null
@@ -0,0 +1,497 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/i2c_anx6345.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+#include "i2c_anx6345.h"
+
+static struct lcd_extern_config_s *ext_config;
+static struct i2c_client *aml_anx6345_70_client;
+static struct i2c_client *aml_anx6345_72_client;
+
+const char *anx_addr_name[2] = {"anx6345_70", "anx6345_72"};
+static unsigned int edp_tx_lane = 1;
+
+#define LCD_EXTERN_NAME                        "lcd_anx6345"
+
+static int aml_i2c_write(struct i2c_client *i2client, unsigned char *buff,
+               unsigned int len)
+{
+       int ret = 0;
+       struct i2c_msg msg[] = {
+               {
+                       .addr = i2client->addr,
+                       .flags = 0,
+                       .len = len,
+                       .buf = buff,
+               },
+       };
+
+       ret = i2c_transfer(i2client->adapter, msg, 1);
+       if (ret < 0) {
+               EXTERR("%s: i2c transfer failed [addr 0x%02x]\n",
+                       __func__, i2client->addr);
+       }
+
+       return ret;
+}
+
+static int aml_i2c_read(struct i2c_client *i2client, unsigned char *buff,
+               unsigned int len)
+{
+       int ret = 0;
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = i2client->addr,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = buff,
+               },
+               {
+                       .addr = i2client->addr,
+                       .flags = I2C_M_RD,
+                       .len = len,
+                       .buf = buff,
+               },
+       };
+
+       ret = i2c_transfer(i2client->adapter, msgs, 2);
+       if (ret < 0) {
+               EXTERR("%s: i2c transfer failed [addr 0x%02x]\n",
+                       __func__, i2client->addr);
+       }
+
+       return ret;
+}
+
+#if 0
+static int i2c_reg_read(unsigned char reg, unsigned char *buf)
+{
+       int ret = 0;
+
+       return ret;
+}
+
+static int i2c_reg_write(unsigned char reg, unsigned char value)
+{
+       int ret = 0;
+
+       return ret;
+}
+#endif
+
+static int SP_TX_Write_Reg(unsigned char addr, unsigned char reg,
+               unsigned char data)
+{
+       struct i2c_client *client = aml_anx6345_70_client;
+       unsigned char buff[2];
+       int ret;
+
+       buff[0] = reg;
+       buff[1] = data;
+       if (addr == 0x70)
+               client = aml_anx6345_70_client;
+       else if (addr == 0x72)
+               client = aml_anx6345_72_client;
+
+       ret = aml_i2c_write(client, buff, 1);
+       return ret;
+}
+
+static int SP_TX_Read_Reg(unsigned char addr, unsigned char reg,
+               unsigned char *data)
+{
+       struct i2c_client *client = aml_anx6345_70_client;
+       int ret;
+
+       *data = reg;
+       if (addr == 0x70)
+               client = aml_anx6345_70_client;
+       else if (addr == 0x72)
+               client = aml_anx6345_72_client;
+
+       ret = aml_i2c_read(client, data, 1);
+       return ret;
+}
+
+static int SP_TX_Wait_AUX_Finished(void)
+{
+       unsigned char c;
+       unsigned char cCnt;
+
+       cCnt = 0;
+       SP_TX_Read_Reg(0x70, SP_TX_AUX_STATUS, &c);
+       while (c & 0x10) {/* aux busy */
+               cCnt++;
+               SP_TX_Read_Reg(0x70, SP_TX_AUX_STATUS, &c);
+               if (cCnt > 100)
+                       return -1; /* aux fail */
+       }
+
+       return 0; /* aux ok */
+}
+
+static int SP_TX_AUX_DPCDRead_Bytes(unsigned char addrh, unsigned char addrm,
+               unsigned char addrl, unsigned char cCount, unsigned char *pBuf)
+{
+       unsigned char c, i;
+
+       /* clr buffer */
+       SP_TX_Write_Reg(0x70, SP_TX_BUF_DATA_COUNT_REG, 0x80);
+
+       /* set read cmd and count */
+       SP_TX_Write_Reg(0x70, SP_TX_AUX_CTRL_REG, (((cCount-1) << 4) | 0x09));
+
+       /* set aux address15:0 */
+       SP_TX_Write_Reg(0x70, SP_TX_AUX_ADDR_7_0_REG, addrl);
+       SP_TX_Write_Reg(0x70, SP_TX_AUX_ADDR_15_8_REG, addrm);
+
+       /* set address19:16 and enable aux */
+       SP_TX_Read_Reg(0x70, SP_TX_AUX_ADDR_19_16_REG, &c);
+       SP_TX_Write_Reg(0x70, SP_TX_AUX_ADDR_19_16_REG, ((c & 0xf0) | addrh));
+
+       /* Enable Aux */
+       SP_TX_Read_Reg(0x70, SP_TX_AUX_CTRL_REG2, &c);
+       SP_TX_Write_Reg(0x70, SP_TX_AUX_CTRL_REG2, (c | 0x01));
+
+       mdelay(5);
+       if (SP_TX_Wait_AUX_Finished())
+               return -1;
+
+       for (i = 0; i < cCount; i++) {
+               SP_TX_Read_Reg(0x70, SP_TX_BUF_DATA_0_REG+i, &c);
+               *(pBuf+i) = c;
+               if (i >= MAX_BUF_CNT)
+                       break;
+       }
+
+       return 0; /* aux ok */
+}
+
+static int lcd_extern_power_on(void)
+{
+       unsigned int lane_num;
+       unsigned int link_rate;
+       unsigned char bits;
+       unsigned char device_id;
+       unsigned char temp;
+       unsigned char temp1;
+       unsigned int count = 0;
+       unsigned int count1 = 0;
+
+       lane_num = edp_tx_lane; /* 1 lane */
+       link_rate = VAL_EDP_TX_LINK_BW_SET_270; /* 2.7G */
+       bits = 0; /* 0x00: 6bit;  0x10:8bit */
+       SP_TX_Write_Reg(0x72, 0x05, 0x00);
+
+       SP_TX_Read_Reg(0x72, 0x01, &device_id);
+       if (device_id == 0xaa) {
+               EXTPR("ANX6345 Chip found\n\n");
+       } else {
+               EXTERR("ANX6345 Chip not found\n\n");
+               return -1;
+       }
+       temp = device_id;
+       /* if aux read fail, do h/w reset */
+       while (SP_TX_AUX_DPCDRead_Bytes(0x00, 0x00, 0x00, 1, &temp1) &&
+               (count < 200)) {
+               /* read fail, h/w reset */
+               SP_TX_Write_Reg(0x72, 0x06, 0x01);
+               SP_TX_Write_Reg(0x72, 0x06, 0x00);
+               SP_TX_Write_Reg(0x72, 0x05, 0x00);
+               mdelay(10);
+               count++;
+       }
+
+       /* software reset */
+       SP_TX_Read_Reg(0x72, SP_TX_RST_CTRL_REG, &temp);
+       SP_TX_Write_Reg(0x72, SP_TX_RST_CTRL_REG, (temp | SP_TX_RST_SW_RST));
+       SP_TX_Write_Reg(0x72, SP_TX_RST_CTRL_REG, (temp & ~SP_TX_RST_SW_RST));
+
+       /* EDID address for AUX access */
+       SP_TX_Write_Reg(0x70, SP_TX_EXTRA_ADDR_REG, 0x50);
+       /* disable HDCP polling mode. */
+       SP_TX_Write_Reg(0x70, SP_TX_HDCP_CTRL, 0x00);
+       /* Enable HDCP polling mode. */
+       /* SP_TX_Write_Reg(0x70, SP_TX_HDCP_CTRL, 0x02); */
+       /* enable M value read out */
+       SP_TX_Write_Reg(0x70, SP_TX_LINK_DEBUG_REG, 0x30);
+
+       /* SP_TX_Read_Reg(0x70, SP_TX_DEBUG_REG1, &temp); */
+       SP_TX_Write_Reg(0x70, SP_TX_DEBUG_REG1, 0x00); /*disable polling HPD */
+
+       SP_TX_Read_Reg(0x70, SP_TX_HDCP_CONTROL_0_REG, &temp);
+       /* set KSV valid */
+       SP_TX_Write_Reg(0x70, SP_TX_HDCP_CONTROL_0_REG, (temp | 0x03));
+
+       SP_TX_Read_Reg(0x70, SP_TX_AUX_CTRL_REG2, &temp);
+       /* set double AUX output */
+       SP_TX_Write_Reg(0x70, SP_TX_AUX_CTRL_REG2, (temp | 0x08));
+
+       /* unmask pll change int */
+       SP_TX_Write_Reg(0x72, SP_COMMON_INT_MASK1, 0xbf);
+       SP_TX_Write_Reg(0x72, SP_COMMON_INT_MASK2, 0xff);/* mask all int */
+       SP_TX_Write_Reg(0x72, SP_COMMON_INT_MASK3, 0xff);/* mask all int */
+       SP_TX_Write_Reg(0x72, SP_COMMON_INT_MASK4, 0xff);/* mask all int */
+
+       /* reset AUX */
+       SP_TX_Read_Reg(0x72, SP_TX_RST_CTRL2_REG, &temp);
+       SP_TX_Write_Reg(0x72, SP_TX_RST_CTRL2_REG, temp | SP_TX_AUX_RST);
+       SP_TX_Write_Reg(0x72, SP_TX_RST_CTRL2_REG, temp & (~SP_TX_AUX_RST));
+
+       /* Chip initialization */
+       SP_TX_Write_Reg(0x70, SP_TX_SYS_CTRL1_REG, 0x00);
+       mdelay(10);
+
+       SP_TX_Write_Reg(0x72, SP_TX_VID_CTRL2_REG, bits);
+
+       /* ANX6345 chip analog setting */
+       SP_TX_Write_Reg(0x70, SP_TX_PLL_CTRL_REG, 0x00);/* UPDATE: 0x07->0x00 */
+
+       /* ANX chip analog setting */
+       /* UPDATE: 0xF0->0X70 */
+       /* SP_TX_Write_Reg(0x72, ANALOG_DEBUG_REG1, 0x70); */
+       SP_TX_Write_Reg(0x70, SP_TX_LINK_DEBUG_REG, 0x30);
+
+       /* force HPD */
+       SP_TX_Write_Reg(0x70, SP_TX_SYS_CTRL3_REG, 0x30);
+
+       /* enable ssc function */
+       SP_TX_Write_Reg(0x70, 0xA7, 0x00); /* disable SSC first */
+       SP_TX_Write_Reg(0x70, 0xD0, 0x5f); /* ssc d  0.4%, f0/4 mode */
+       SP_TX_Write_Reg(0x70, 0xD1, 0x00);
+       SP_TX_Write_Reg(0x70, 0xD2, 0x75); /* ctrl_th */
+       SP_TX_Read_Reg(0x70, 0xA7, &temp);
+       SP_TX_Write_Reg(0x70, 0xA7, (temp | 0x10)); /* enable SSC */
+       SP_TX_Read_Reg(0x72, 0x07, &temp); /* reset SSC */
+       SP_TX_Write_Reg(0x72, 0x07, (temp | 0x80));
+       SP_TX_Write_Reg(0x72, 0x07, (temp & (~0x80)));
+
+       /* Select 2.7G */
+       /* 2.7g:0x0a; 1.62g:0x06 */
+       SP_TX_Write_Reg(0x70, SP_TX_LINK_BW_SET_REG, link_rate);
+       /* Select 2 lanes */
+       SP_TX_Write_Reg(0x70, 0xa1, lane_num);
+
+       SP_TX_Write_Reg(0x70, SP_TX_LINK_TRAINING_CTRL_REG,
+               SP_TX_LINK_TRAINING_CTRL_EN);
+       mdelay(5);
+       SP_TX_Read_Reg(0x70, SP_TX_LINK_TRAINING_CTRL_REG, &temp);
+       /* UPDATE: FROM 0X01 TO 0X80 */
+       while ((temp & 0x80) != 0) {
+               /* debug_puts("Waiting...\n"); */
+               mdelay(5);
+               count1++;
+               if (count1 > 100) {
+                       EXTERR("ANX6345 Link training fail\n");
+                       break;
+               }
+               SP_TX_Read_Reg(0x70, SP_TX_LINK_TRAINING_CTRL_REG, &temp);
+       }
+
+       SP_TX_Write_Reg(0x72, 0x12, 0x2c);
+       SP_TX_Write_Reg(0x72, 0x13, 0x06);
+       SP_TX_Write_Reg(0x72, 0x14, 0x00);
+       SP_TX_Write_Reg(0x72, 0x15, 0x06);
+       SP_TX_Write_Reg(0x72, 0x16, 0x02);
+       SP_TX_Write_Reg(0x72, 0x17, 0x04);
+       SP_TX_Write_Reg(0x72, 0x18, 0x26);
+       SP_TX_Write_Reg(0x72, 0x19, 0x50);
+       SP_TX_Write_Reg(0x72, 0x1a, 0x04);
+       SP_TX_Write_Reg(0x72, 0x1b, 0x00);
+       SP_TX_Write_Reg(0x72, 0x1c, 0x04);
+       SP_TX_Write_Reg(0x72, 0x1d, 0x18);
+       SP_TX_Write_Reg(0x72, 0x1e, 0x00);
+       SP_TX_Write_Reg(0x72, 0x1f, 0x10);
+       SP_TX_Write_Reg(0x72, 0x20, 0x00);
+       SP_TX_Write_Reg(0x72, 0x21, 0x28);
+
+       SP_TX_Write_Reg(0x72, 0x11, 0x03);
+       /* enable BIST. In normal mode, don't need to config this reg
+        * if want to open BIST, must setting
+        * right dat 0x11-0x21 base lcd timing.
+        */
+       /* SP_TX_Write_Reg(0x72, 0x0b, 0x09); //colorbar:08,graystep:09 */
+       SP_TX_Write_Reg(0x72, 0x08, 0x81); /* SDR:0x81;DDR:0x8f */
+
+       /* force HPD and stream valid */
+       SP_TX_Write_Reg(0x70, 0x82, 0x33);
+       EXTPR("%s\n", __func__);
+       return 0;
+}
+
+static int lcd_extern_power_off(void)
+{
+       int ret = 0;
+
+       return ret;
+}
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       int ret = 0;
+
+       if (ext_drv) {
+               ext_drv->power_on  = lcd_extern_power_on;
+               ext_drv->power_off = lcd_extern_power_off;
+       } else {
+               EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+               ret = -1;
+       }
+
+       return ret;
+}
+
+static int aml_anx6345_70_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+               EXTERR("%s: functionality check failed\n", __func__);
+       else
+               aml_anx6345_70_client = client;
+
+       EXTPR("%s OK\n", __func__);
+       return 0;
+}
+
+static int aml_anx6345_72_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+               EXTERR("%s: functionality check failed\n", __func__);
+       else
+               aml_anx6345_72_client = client;
+
+       EXTPR("%s OK\n", __func__);
+       return 0;
+}
+
+static int aml_anx6345_70_remove(struct i2c_client *client)
+{
+       return 0;
+}
+static int aml_anx6345_72_remove(struct i2c_client *client)
+{
+       return 0;
+}
+
+static const struct i2c_device_id aml_anx6345_70_id[] = {
+       {"anx6345_70", 0},
+       { }
+};
+static const struct i2c_device_id aml_anx6345_72_id[] = {
+       {"anx6345_72", 0},
+       { }
+};
+
+/* MODULE_DEVICE_TABLE(i2c, aml_tc101_id); */
+
+static struct i2c_driver aml_anx6345_70_driver = {
+       .probe    = aml_anx6345_70_probe,
+       .remove   = aml_anx6345_70_remove,
+       .id_table = aml_anx6345_70_id,
+       .driver = {
+               .name = "anx6345_70",
+               .owner = THIS_MODULE,
+       },
+};
+
+static struct i2c_driver aml_anx6345_72_driver = {
+       .probe    = aml_anx6345_72_probe,
+       .remove   = aml_anx6345_72_remove,
+       .id_table = aml_anx6345_72_id,
+       .driver = {
+               .name = "anx6345_72",
+               .owner = THIS_MODULE,
+       },
+};
+
+int aml_lcd_extern_i2c_anx6345_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       struct i2c_board_info i2c_info[2];
+       struct i2c_adapter *adapter;
+       struct i2c_client *i2c_client;
+       int i = 0;
+       int ret = 0;
+
+       ext_config = &ext_drv->config;
+       for (i = 0; i < 2; i++)
+               memset(&i2c_info[i], 0, sizeof(i2c_info[i]));
+
+       adapter = i2c_get_adapter(ext_config->i2c_bus);
+       if (!adapter) {
+               EXTERR("%s failed to get i2c adapter\n", ext_drv->config.name);
+               return -1;
+       }
+
+       i2c_info[0].addr = 0x38; /* 0x70 >> 1; */
+       i2c_info[1].addr = 0x39; /* 0x72 >> 1; */
+       for (i = 0; i < 2; i++) {
+               strncpy(i2c_info[i].type, anx_addr_name[i], I2C_NAME_SIZE);
+               /* i2c_info[i].platform_data = &ext_drv->config; */
+               i2c_info[i].flags = 0;
+               if (i2c_info[i].addr > 0x7f) {
+                       EXTERR("%s invalid i2c address: 0x%02x\n",
+                               ext_drv->config.name, i2c_info[i].addr);
+                       return -1;
+               }
+               i2c_client = i2c_new_device(adapter, &i2c_info[i]);
+               if (!i2c_client) {
+                       EXTERR("%s failed to new i2c device\n",
+                               ext_drv->config.name);
+               } else {
+                       if (lcd_debug_print_flag) {
+                               EXTPR("%s new i2c device succeed\n",
+                                       ext_drv->config.name);
+                       }
+               }
+       }
+
+       if (!aml_anx6345_70_client) {
+               ret = i2c_add_driver(&aml_anx6345_70_driver);
+               if (ret) {
+                       EXTERR("%s add i2c_driver failed\n",
+                               ext_drv->config.name);
+                       return -1;
+               }
+       }
+       if (!aml_anx6345_72_client) {
+               ret = i2c_add_driver(&aml_anx6345_72_driver);
+               if (ret) {
+                       EXTERR("%s add i2c_driver failed\n",
+                               ext_drv->config.name);
+                       return -1;
+               }
+       }
+
+       ret = lcd_extern_driver_update(ext_drv);
+
+       if (lcd_debug_print_flag)
+               EXTPR("%s: %d\n", __func__, ret);
+       return ret;
+}
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_extern/i2c_anx6345.h b/drivers/amlogic/media/vout/lcd/lcd_extern/i2c_anx6345.h
new file mode 100644 (file)
index 0000000..462ba4b
--- /dev/null
@@ -0,0 +1,716 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/i2c_anx6345.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef EDP_ANX6345_H
+#define EDP_ANX6345_H
+
+/* ************************************************************* */
+#define SP_TX_PORT0_ADDR                       0x70
+#define SP_TX_PORT1_ADDR                       0x74
+#define SP_TX_PORT2_ADDR                       0x72
+#define MIPI_RX_PORT1_ADDR                     0x7A
+
+#define MAX_BUF_CNT                            6
+
+#define CR_LOOP_TIME                           5
+#define EQ_LOOP_TIME                           5
+
+#define SP_TX_AVI_SEL                          0x01
+#define SP_TX_SPD_SEL                          0x02
+#define SP_TX_MPEG_SEL                         0x04
+
+#define VAL_EDP_TX_LINK_BW_SET_162             0x06
+#define VAL_EDP_TX_LINK_BW_SET_270             0x0a
+#define VAL_EDP_TX_LINK_BW_SET_540             0x14
+
+/* End for DEV_addr 0x7A/0x7E */
+
+/* ************************************************************* */
+/* DEV_ADDR = 0x70 or 0x78 , Displayport mode and HDCP registers */
+#define SP_TX_HDCP_STATUS                              0x00
+/* bit position */
+#define SP_TX_HDCP_AUTH_PASS                           0x02
+
+#define SP_TX_HDCP_CONTROL_0_REG                       0x01
+/* bit position */
+#define SP_TX_HDCP_CONTROL_0_STORE_AN                  0x80
+#define SP_TX_HDCP_CONTROL_0_RX_REPEATER               0x40
+#define SP_TX_HDCP_CONTROL_0_RE_AUTH                   0x20
+#define SP_TX_HDCP_CONTROL_0_SW_AUTH_OK                        0x10
+#define SP_TX_HDCP_CONTROL_0_HARD_AUTH_EN              0x08
+#define SP_TX_HDCP_CONTROL_0_HDCP_ENC_EN               0x04
+#define SP_TX_HDCP_CONTROL_0_BKSV_SRM_PASS             0x02
+#define SP_TX_HDCP_CONTROL_0_KSVLIST_VLD               0x01
+
+#define SP_TX_HDCP_CONTROL_1_REG                       0x02
+/* bit position */
+#define SP_TX_HDCP_CONTROL_1_DDC_NO_STOP               0x20
+#define SP_TX_HDCP_CONTROL_1_DDC_NO_ACK                        0x10
+#define SP_TX_HDCP_CONTROL_1_EDDC_NO_ACK               0x08
+/* #define SP_TX_HDCP_CONTROL_1_HDCP_EMB_SCREEN_EN     0x04 */
+#define SP_TX_HDCP_CONTROL_1_RCV_11_EN                 0x02
+#define SP_TX_HDCP_CONTROL_1_HDCP_11_EN                        0x01
+
+#define SP_TX_HDCP_LINK_CHK_FRAME_NUM                  0x03
+#define SP_TX_HDCP_CONTROL_2_REG                       0x04
+
+#define SP_TX_HDCP_AKSV0                               0x05
+#define SP_TX_HDCP_AKSV1                               0x06
+#define SP_TX_HDCP_AKSV2                               0x07
+#define SP_TX_HDCP_AKSV3                               0x08
+#define SP_TX_HDCP_AKSV4                               0x09
+
+/* AKSV */
+#define SP_TX_HDCP_AN0                                 0x0A
+#define SP_TX_HDCP_AN1                                 0x0B
+#define SP_TX_HDCP_AN2                                 0x0C
+#define SP_TX_HDCP_AN3                                 0x0D
+#define SP_TX_HDCP_AN4                                 0x0E
+#define SP_TX_HDCP_AN5                                 0x0F
+#define SP_TX_HDCP_AN6                                 0x10
+#define SP_TX_HDCP_AN7                                 0x11
+
+/* BKSV */
+#define SP_TX_HDCP_BKSV0                               0x12
+#define SP_TX_HDCP_BKSV1                               0x13
+#define SP_TX_HDCP_BKSV2                               0x14
+#define SP_TX_HDCP_BKSV3                               0x15
+#define SP_TX_HDCP_BKSV4                               0x16
+
+#define SP_TX_HDCP_R0_L                                        0x17
+#define SP_TX_HDCP_R0_H                                        0x18
+
+#ifndef M_VID_0
+#define M_VID_0                                                0xC0
+#define M_VID_1                                                0xC1
+#define M_VID_2                                                0xC2
+#define N_VID_0                                                0xC3
+#define N_VID_1                                                0xC4
+#define N_VID_2                                                0xC5
+#endif
+
+#define SP_TX_HDCP_R0_WAIT_Timer                       0x40
+
+#define SP_TX_SYS_CTRL1_REG                            0x80
+/* bit position */
+/* #define SP_TX_SYS_CTRL1_PD_IO                       0x80 */
+/* #define SP_TX_SYS_CTRL1_PD_VID                      0x40 */
+/* #define SP_TX_SYS_CTRL1_PD_LINK                     0x20 */
+/* #define SP_TX_SYS_CTRL1_PD_TOTAL                    0x10 */
+/* #define SP_TX_SYS_CTRL1_MODE_SEL                    0x08 */
+#define SP_TX_SYS_CTRL1_DET_STA                                0x04
+#define SP_TX_SYS_CTRL1_FORCE_DET                      0x02
+#define SP_TX_SYS_CTRL1_DET_CTRL                       0x01
+
+#define SP_TX_SYS_CTRL2_REG                            0x81
+/* bit position */
+/* #define SP_TX_SYS_CTRL2_ENHANCED                    0x08 */
+#define SP_TX_SYS_CTRL2_CHA_STA                                0x04
+#define SP_TX_SYS_CTRL2_FORCE_CHA                      0x02
+#define SP_TX_SYS_CTRL2_CHA_CTRL                       0x01
+
+#define SP_TX_SYS_CTRL3_REG                            0x82
+/* bit position */
+#define SP_TX_SYS_CTRL3_HPD_STATUS                     0x40
+#define SP_TX_SYS_CTRL3_F_HPD                          0x20
+#define SP_TX_SYS_CTRL3_HPD_CTRL                       0x10
+#define SP_TX_SYS_CTRL3_STRM_VALID                     0x04
+#define SP_TX_SYS_CTRL3_F_VALID                                0x02
+#define SP_TX_SYS_CTRL3_VALID_CTRL                     0x01
+
+#define SP_TX_SYS_CTRL4_REG                            0x83
+/* bit position */
+#define SP_TX_SYS_CTRL4_ENHANCED                       0x08
+
+#define SP_TX_VID_CTRL                                 0x84
+
+#define SP_TX_AUD_CTRL                                 0x87
+/* bit position */
+#define SP_TX_AUD_CTRL_AUD_EN                          0x01
+
+#define SP_TX_PKT_EN_REG                               0x90
+/* bit position */
+#define SP_TX_PKT_AUD_UP                               0x80
+#define SP_TX_PKT_AVI_UD                               0x40
+#define SP_TX_PKT_MPEG_UD                              0x20
+#define SP_TX_PKT_SPD_UD                               0x10
+#define SP_TX_PKT_AUD_EN                               0x08
+#define SP_TX_PKT_AVI_EN                               0x04
+#define SP_TX_PKT_MPEG_EN                              0x02
+#define SP_TX_PKT_SPD_EN                               0x01
+
+#define SP_TX_HDCP_CTRL                                        0x92
+
+#define SP_TX_LINK_BW_SET_REG                          0xA0
+#define SP_TX_LANE_COUNT_SET_REG                       0xA1
+
+#define SP_TX_TRAINING_PTN_SET_REG                     0xA2
+/* bit position */
+#define SP_TX_SCRAMBLE_DISABLE                         0x20
+
+#define SP_TX_TRAINING_LANE0_SET_REG                   0xA3
+/* bit position */
+#define SP_TX_TRAINING_LANE0_SET_MAX_PRE_REACH         0x20
+#define SP_TX_TRAINING_LANE0_SET_MAX_DRIVE_REACH       0x04
+
+#define SP_TX_TRAINING_LANE1_SET_REG                   0xA4
+
+#define SSC_CTRL_REG1                                   0xA7
+/* bit position */
+#define SPREAD_AMP                                      0x10
+#define MODULATION_FREQ                                         0x01
+
+
+#define SP_TX_LINK_TRAINING_CTRL_REG                   0xA8
+/* bit position */
+#define SP_TX_LINK_TRAINING_CTRL_EN                    0x01
+
+#define SP_TX_DEBUG_REG1                               0xB0
+/* bit position */
+#define SP_TX_DEBUG_HPD_POLLING_DET                    0x40
+#define SP_TX_DEBUG_HPD_POLLING_EN                     0x20
+#define SP_TX_DEBUG_PLL_LOCK                           0x10
+
+#define SP_TX_LINK_DEBUG_REG                           0xB8
+/* bit position */
+#define SP_TX_LINK_DEBUG_INSERT_ER                     0x02
+#define SP_TX_LINK_DEBUG_PRBS31_EN                     0x01
+
+#define SP_TX_SINK_COUNT_REG                           0xB9
+
+#define SP_TX_LINK_STATUS_REG1                         0xBB
+
+#define SP_TX_SINK_STATUS_REG                          0xBE
+/* bit position */
+#define SP_TX_SINK_STATUS_SINK_STATUS_1                        0x02
+#define SP_TX_SINK_STATUS_SINK_STATUS_0                        0x01
+
+
+/* #define SP_TX_LINK_TEST_COUNT                       0xC0 */
+
+
+#define SP_TX_PLL_CTRL_REG                             0xC7
+/* bit position */
+#define SP_TX_PLL_CTRL_PLL_PD                          0x80
+#define SP_TX_PLL_CTRL_PLL_RESET                       0x40
+/* #define SP_TX_PLL_CTRL_CPREG_BLEED                  0x08 */
+
+#define SP_TX_ANALOG_POWER_DOWN_REG                    0xC8
+/* bit position */
+#define SP_TX_ANALOG_POWER_DOWN_MACRO_PD               0x20
+#define SP_TX_ANALOG_POWER_DOWN_AUX_PD                 0x10
+/* #define SP_TX_ANALOG_POWER_DOWN_CH3_PD              0x08 */
+/* #define SP_TX_ANALOG_POWER_DOWN_CH2_PD              0x04 */
+#define SP_TX_ANALOG_POWER_DOWN_CH1_PD                 0x02
+#define SP_TX_ANALOG_POWER_DOWN_CH0_PD                 0x01
+
+
+#define SP_TX_ANALOG_TEST_REG                          0xC9
+/* bit position */
+#define SP_TX_ANALOG_TEST_MACRO_RST                    0x20
+#define SP_TX_ANALOG_TEST_PLL_TEST                     0x10
+#define SP_TX_ANALOG_TEST_CH3_TEST                     0x08
+#define SP_TX_ANALOG_TEST_CH2_TEST                     0x04
+#define SP_TX_ANALOG_TEST_CH1_TEST                     0x02
+#define SP_TX_ANALOG_TEST_CH0_TEST                     0x01
+
+#define SP_TX_GNS_CTRL_REG                             0xCD
+/* bit position */
+#define SP_EQ_LOOP_CNT                                 0x40
+#define SP_TX_VIDEO_MAP_CTRL                           0x02
+#define SP_TX_RS_CTRL                                  0x01
+
+#define SP_TX_DOWN_SPREADING_CTRL1                     0xD0
+#define SP_TX_DOWN_SPREADING_CTRL2                     0xD1
+#define SP_TX_DOWN_SPREADING_CTRL3                     0xD2
+/* bit position */
+#define SP_TX_SSC_D_CTRL                               0x40
+#define SP_TX_FS_CTRL_TH_CTRL                          0x20
+
+#define SP_TX_M_CALCU_CTRL                             0xD9
+/* bit position */
+#define M_GEN_CLK_SEL                                  0x01
+
+
+#define SP_TX_EXTRA_ADDR_REG                           0xCE
+#define SP_TX_I2C_STRETCH_CTRL_REG                     0xDB
+#define SP_TX_AUX_STATUS                               0xE0
+#define SP_TX_DEFER_CTRL_REG                           0xE2
+/* bit position */
+#define SP_TXL_DEFER_CTRL_EN                           0x80
+
+#define SP_TX_BUF_DATA_COUNT_REG                       0xE4
+#define SP_TX_AUX_CTRL_REG                             0xE5
+/* bit position */
+#define SP_TX_MOT_BIT                                  0x04
+
+#define SP_TX_AUX_ADDR_7_0_REG                         0xE6
+#define SP_TX_AUX_ADDR_15_8_REG                                0xE7
+#define SP_TX_AUX_ADDR_19_16_REG                       0xE8
+
+#define SP_TX_AUX_CTRL_REG2                            0xE9
+/* bit position */
+#define SP_TX_ADDR_ONLY_BIT                            0x02
+
+#define SP_TX_BUF_DATA_0_REG                           0xf0
+#define SP_TX_BUF_DATA_1_REG                           0xf1
+#define SP_TX_BUF_DATA_2_REG                           0xf2
+#define SP_TX_BUF_DATA_3_REG                           0xf3
+#define SP_TX_BUF_DATA_4_REG                           0xf4
+#define SP_TX_BUF_DATA_5_REG                           0xf5
+#define SP_TX_BUF_DATA_6_REG                           0xf6
+#define SP_TX_BUF_DATA_7_REG                           0xf7
+#define SP_TX_BUF_DATA_8_REG                           0xf8
+#define SP_TX_BUF_DATA_9_REG                           0xf9
+#define SP_TX_BUF_DATA_10_REG                          0xfa
+#define SP_TX_BUF_DATA_11_REG                          0xfb
+#define SP_TX_BUF_DATA_12_REG                          0xfc
+#define SP_TX_BUF_DATA_13_REG                          0xfd
+#define SP_TX_BUF_DATA_14_REG                          0xfe
+#define SP_TX_BUF_DATA_15_REG                          0xff
+
+/* End for Address 0x70 or 0x78 */
+
+/* ************************************************************* */
+/* DEV_ADDR = 0x72 or 0x76, System control registers */
+#define SP_TX_VND_IDL_REG                      0x00
+#define SP_TX_VND_IDH_REG                      0x01
+#define SP_TX_DEV_IDL_REG                      0x02
+#define SP_TX_DEV_IDH_REG                      0x03
+#define SP_TX_DEV_REV_REG                      0x04
+
+#define SP_POWERD_CTRL_REG                     0x05
+/* bit position */
+#define SP_POWERD_REGISTER_REG                 0x80
+/* #define SP_POWERD_MISC_REG                  0x40 */
+#define SP_POWERD_IO_REG                       0x20
+#define SP_POWERD_AUDIO_REG                    0x10
+#define SP_POWERD_VIDEO_REG                    0x08
+#define SP_POWERD_LINK_REG                     0x04
+#define SP_POWERD_TOTAL_REG                    0x02
+#define SP_MODE_SEL_REG                                0x01
+
+#define SP_TX_RST_CTRL_REG                     0x06
+#define SP_TX_RST_MISC_REG                     0x80
+#define SP_TX_RST_VIDCAP_REG                   0x40
+#define SP_TX_RST_VIDFIF_REG                   0x20
+#define SP_TX_RST_AUDFIF_REG                   0x10
+#define SP_TX_RST_AUDCAP_REG                   0x08
+#define SP_TX_RST_HDCP_REG                     0x04
+#define SP_TX_RST_SW_RST                       0x02
+#define SP_TX_RST_HW_RST                       0x01
+
+#define SP_TX_RST_CTRL2_REG                    0x07
+#define SP_TX_RST_SSC                          0x80
+#define SP_TX_AC_MODE                          0x40
+/* #define SP_TX_DDC_RST                       0x10 */
+/* #define SP_TX_TMDS_BIST_RST                 0x08 */
+#define SP_TX_AUX_RST                          0x04
+#define SP_TX_SERDES_FIFO_RST                  0x02
+#define SP_TX_I2C_REG_RST                      0x01
+
+
+#define SP_TX_VID_CTRL1_REG                    0x08
+/* bit position */
+#define SP_TX_VID_CTRL1_VID_EN                 0x80
+#define SP_TX_VID_CTRL1_VID_MUTE               0x40
+#define SP_TX_VID_CTRL1_DE_GEN                 0x20
+#define SP_TX_VID_CTRL1_DEMUX                  0x10
+#define SP_TX_VID_CTRL1_IN_BIT                 0x04
+#define SP_TX_VID_CTRL1_DDRCTRL                        0x02
+#define SP_TX_VID_CTRL1_EDGE                   0x01
+
+#define SP_TX_VID_CTRL2_REG                    0x09
+/* bit position */
+#define SP_TX_VID_CTRL1_YCBIT_SEL              0x04
+
+#define SP_TX_VID_CTRL3_REG                    0x0A
+
+#define SP_TX_VID_CTRL4_REG                    0x0B
+/* bit position */
+#define SP_TX_VID_CTRL4_E_SYNC_EN              0x80
+#define SP_TX_VID_CTRL4_EX_E_SYNC              0x40
+#define SP_TX_VID_CTRL4_BIST                   0x08
+#define SP_TX_VID_CTRL4_BIST_WIDTH             0x04
+
+#define SP_TX_VID_CTRL5_REG                    0x0C
+
+#define SP_TX_VID_CTRL6_REG                    0x0D
+/* bit position */
+#define SP_TX_VID_UPSAMPLE                     0x02
+
+#define SP_TX_VID_CTRL7_REG                    0x0E
+#define SP_TX_VID_CTRL8_REG                    0x0F
+#define SP_TX_VID_CTRL9_REG                    0x10
+
+#define SP_TX_VID_CTRL10_REG                   0x11
+/* bit position */
+#define SP_TX_VID_CTRL10_INV_F                 0x08
+#define SP_TX_VID_CTRL10_I_SCAN                        0x04
+#define SP_TX_VID_CTRL10_VSYNC_POL             0x02
+#define SP_TX_VID_CTRL10_HSYNC_POL             0x01
+
+#define SP_TX_TOTAL_LINEL_REG                  0x12
+#define SP_TX_TOTAL_LINEH_REG                  0x13
+#define SP_TX_ACT_LINEL_REG                    0x14
+#define SP_TX_ACT_LINEH_REG                    0x15
+#define SP_TX_VF_PORCH_REG                     0x16
+#define SP_TX_VSYNC_CFG_REG                    0x17
+#define SP_TX_VB_PORCH_REG                     0x18
+#define SP_TX_TOTAL_PIXELL_REG                 0x19
+#define SP_TX_TOTAL_PIXELH_REG                 0x1A
+#define SP_TX_ACT_PIXELL_REG                   0x1B
+#define SP_TX_ACT_PIXELH_REG                   0x1C
+#define SP_TX_HF_PORCHL_REG                    0x1D
+#define SP_TX_HF_PORCHH_REG                    0x1E
+#define SP_TX_HSYNC_CFGL_REG                   0x1F
+#define SP_TX_HSYNC_CFGH_REG                   0x20
+#define SP_TX_HB_PORCHL_REG                    0x21
+#define SP_TX_HB_PORCHH_REG                    0x22
+
+#define SP_TX_VID_STATUS                       0x23
+
+#define SP_TX_TOTAL_LINE_STA_L                 0x24
+#define SP_TX_TOTAL_LINE_STA_H                 0x25
+#define SP_TX_ACT_LINE_STA_L                   0x26
+#define SP_TX_ACT_LINE_STA_H                   0x27
+#define SP_TX_V_F_PORCH_STA                    0x28
+#define SP_TX_V_SYNC_STA                       0x29
+#define SP_TX_V_B_PORCH_STA                    0x2A
+#define SP_TX_TOTAL_PIXEL_STA_L                        0x2B
+#define SP_TX_TOTAL_PIXEL_STA_H                        0x2C
+#define SP_TX_ACT_PIXEL_STA_L                  0x2D
+#define SP_TX_ACT_PIXEL_STA_H                  0x2E
+#define SP_TX_H_F_PORCH_STA_L                  0x2F
+#define SP_TX_H_F_PORCH_STA_H                  0x30
+#define SP_TX_H_SYNC_STA_L                     0x31
+#define SP_TX_H_SYNC_STA_H                     0x32
+#define SP_TX_H_B_PORCH_STA_L                  0x33
+#define SP_TX_H_B_PORCH_STA_H                  0x34
+
+#define SP_TX_Video_Interface_BIST             0x35
+
+#define SPDIF_AUDIO_CTRL0                      0x36
+/* bit position */
+#define SPDIF_AUDIO_CTRL0_SPDIF_IN             0x80
+
+#define SPDIF_AUDIO_STATUS0                    0x38
+#define SPDIF_AUDIO_STATUS0_CLK_DET            0x80
+#define SPDIF_AUDIO_STATUS0_AUD_DET            0x01
+
+#define SPDIF_AUDIO_STATUS1                    0x39
+
+#define AUDIO_BIST_CTRL                                0x3c
+#define AUDIO_BIST_EN                          0x01
+
+/* #define AUDIO_BIST_CHANNEL_STATUS1          0xd0 */
+/* #define AUDIO_BIST_CHANNEL_STATUS2          0xd1 */
+/* #define AUDIO_BIST_CHANNEL_STATUS3          0xd2 */
+/* #define AUDIO_BIST_CHANNEL_STATUS4          0xd3 */
+/* #define AUDIO_BIST_CHANNEL_STATUS5          0xd4 */
+
+#define SP_TX_VIDEO_BIT_CTRL_0_REG             0x40
+#define SP_TX_VIDEO_BIT_CTRL_1_REG             0x41
+#define SP_TX_VIDEO_BIT_CTRL_2_REG             0x42
+#define SP_TX_VIDEO_BIT_CTRL_3_REG             0x43
+#define SP_TX_VIDEO_BIT_CTRL_4_REG             0x44
+#define SP_TX_VIDEO_BIT_CTRL_5_REG             0x45
+#define SP_TX_VIDEO_BIT_CTRL_6_REG             0x46
+#define SP_TX_VIDEO_BIT_CTRL_7_REG             0x47
+#define SP_TX_VIDEO_BIT_CTRL_8_REG             0x48
+#define SP_TX_VIDEO_BIT_CTRL_9_REG             0x49
+#define SP_TX_VIDEO_BIT_CTRL_10_REG            0x4a
+#define SP_TX_VIDEO_BIT_CTRL_11_REG            0x4b
+#define SP_TX_VIDEO_BIT_CTRL_12_REG            0x4c
+#define SP_TX_VIDEO_BIT_CTRL_13_REG            0x4d
+#define SP_TX_VIDEO_BIT_CTRL_14_REG            0x4e
+#define SP_TX_VIDEO_BIT_CTRL_15_REG            0x4f
+#define SP_TX_VIDEO_BIT_CTRL_16_REG            0x50
+#define SP_TX_VIDEO_BIT_CTRL_17_REG            0x51
+#define SP_TX_VIDEO_BIT_CTRL_18_REG            0x52
+#define SP_TX_VIDEO_BIT_CTRL_19_REG            0x53
+#define SP_TX_VIDEO_BIT_CTRL_20_REG            0x54
+#define SP_TX_VIDEO_BIT_CTRL_21_REG            0x55
+#define SP_TX_VIDEO_BIT_CTRL_22_REG            0x56
+#define SP_TX_VIDEO_BIT_CTRL_23_REG            0x57
+#define SP_TX_VIDEO_BIT_CTRL_24_REG            0x58
+#define SP_TX_VIDEO_BIT_CTRL_25_REG            0x59
+#define SP_TX_VIDEO_BIT_CTRL_26_REG            0x5a
+#define SP_TX_VIDEO_BIT_CTRL_27_REG            0x5b
+#define SP_TX_VIDEO_BIT_CTRL_28_REG            0x5c
+#define SP_TX_VIDEO_BIT_CTRL_29_REG            0x5d
+#define SP_TX_VIDEO_BIT_CTRL_30_REG            0x5e
+#define SP_TX_VIDEO_BIT_CTRL_31_REG            0x5f
+#define SP_TX_VIDEO_BIT_CTRL_32_REG            0x60
+#define SP_TX_VIDEO_BIT_CTRL_33_REG            0x61
+#define SP_TX_VIDEO_BIT_CTRL_34_REG            0x62
+#define SP_TX_VIDEO_BIT_CTRL_35_REG            0x63
+#define SP_TX_VIDEO_BIT_CTRL_36_REG            0x64
+#define SP_TX_VIDEO_BIT_CTRL_37_REG            0x65
+#define SP_TX_VIDEO_BIT_CTRL_38_REG            0x66
+#define SP_TX_VIDEO_BIT_CTRL_39_REG            0x67
+#define SP_TX_VIDEO_BIT_CTRL_40_REG            0x68
+#define SP_TX_VIDEO_BIT_CTRL_41_REG            0x69
+#define SP_TX_VIDEO_BIT_CTRL_42_REG            0x6a
+#define SP_TX_VIDEO_BIT_CTRL_43_REG            0x6b
+#define SP_TX_VIDEO_BIT_CTRL_44_REG            0x6c
+#define SP_TX_VIDEO_BIT_CTRL_45_REG            0x6d
+#define SP_TX_VIDEO_BIT_CTRL_46_REG            0x6e
+#define SP_TX_VIDEO_BIT_CTRL_47_REG            0x6f
+
+/* AVI info frame */
+#define SP_TX_AVI_TYPE                         0x70
+#define SP_TX_AVI_VER                          0x71
+#define SP_TX_AVI_LEN                          0x72
+#define SP_TX_AVI_DB0                          0x73
+#define SP_TX_AVI_DB1                          0x74
+#define SP_TX_AVI_DB2                          0x75
+#define SP_TX_AVI_DB3                          0x76
+#define SP_TX_AVI_DB4                          0x77
+#define SP_TX_AVI_DB5                          0x78
+#define SP_TX_AVI_DB6                          0x79
+#define SP_TX_AVI_DB7                          0x7A
+#define SP_TX_AVI_DB8                          0x7B
+#define SP_TX_AVI_DB9                          0x7C
+#define SP_TX_AVI_DB10                         0x7D
+#define SP_TX_AVI_DB11                         0x7E
+#define SP_TX_AVI_DB12                         0x7F
+#define SP_TX_AVI_DB13                         0x80
+#define SP_TX_AVI_DB14                         0x81
+#define SP_TX_AVI_DB15                         0x82
+
+/* Audio info frame */
+#define SP_TX_AUD_TYPE                         0x83
+#define SP_TX_AUD_VER                          0x84
+#define SP_TX_AUD_LEN                          0x85
+#define SP_TX_AUD_DB0                          0x86
+#define SP_TX_AUD_DB1                          0x87
+#define SP_TX_AUD_DB2                          0x88
+#define SP_TX_AUD_DB3                          0x89
+#define SP_TX_AUD_DB4                          0x8A
+#define SP_TX_AUD_DB5                          0x8B
+#define SP_TX_AUD_DB6                          0x8C
+#define SP_TX_AUD_DB7                          0x8D
+#define SP_TX_AUD_DB8                          0x8E
+#define SP_TX_AUD_DB9                          0x8F
+#define SP_TX_AUD_DB10                         0x90
+
+/* SPD info frame */
+#define SP_TX_SPD_TYPE                         0x91
+#define SP_TX_SPD_VER                          0x92
+#define SP_TX_SPD_LEN                          0x93
+#define SP_TX_SPD_DATA0                                0x94
+#define SP_TX_SPD_DATA1                                0x95
+#define SP_TX_SPD_DATA2                                0x96
+#define SP_TX_SPD_DATA3                                0x97
+#define SP_TX_SPD_DATA4                                0x98
+#define SP_TX_SPD_DATA5                                0x99
+#define SP_TX_SPD_DATA6                                0x9A
+#define SP_TX_SPD_DATA7                                0x9B
+#define SP_TX_SPD_DATA8                                0x9C
+#define SP_TX_SPD_DATA9                                0x9D
+#define SP_TX_SPD_DATA10                       0x9E
+#define SP_TX_SPD_DATA11                       0x9F
+#define SP_TX_SPD_DATA12                       0xA0
+#define SP_TX_SPD_DATA13                       0xA1
+#define SP_TX_SPD_DATA14                       0xA2
+#define SP_TX_SPD_DATA15                       0xA3
+#define SP_TX_SPD_DATA16                       0xA4
+#define SP_TX_SPD_DATA17                       0xA5
+#define SP_TX_SPD_DATA18                       0xA6
+#define SP_TX_SPD_DATA19                       0xA7
+#define SP_TX_SPD_DATA20                       0xA8
+#define SP_TX_SPD_DATA21                       0xA9
+#define SP_TX_SPD_DATA22                       0xAA
+#define SP_TX_SPD_DATA23                       0xAB
+#define SP_TX_SPD_DATA24                       0xAC
+#define SP_TX_SPD_DATA25                       0xAD
+#define SP_TX_SPD_DATA26                       0xAE
+#define SP_TX_SPD_DATA27                       0xAF
+
+/* Mpeg source info frame */
+#define SP_TX_MPEG_TYPE                                0xB0
+#define SP_TX_MPEG_VER                         0xB1
+#define SP_TX_MPEG_LEN                         0xB2
+#define SP_TX_MPEG_DATA0                       0xB3
+#define SP_TX_MPEG_DATA1                       0xB4
+#define SP_TX_MPEG_DATA2                       0xB5
+#define SP_TX_MPEG_DATA3                       0xB6
+#define SP_TX_MPEG_DATA4                       0xB7
+#define SP_TX_MPEG_DATA5                       0xB8
+#define SP_TX_MPEG_DATA6                       0xB9
+#define SP_TX_MPEG_DATA7                       0xBA
+#define SP_TX_MPEG_DATA8                       0xBB
+#define SP_TX_MPEG_DATA9                       0xBC
+#define SP_TX_MPEG_DATA10                      0xBD
+#define SP_TX_MPEG_DATA11                      0xBE
+#define SP_TX_MPEG_DATA12                      0xBF
+#define SP_TX_MPEG_DATA13                      0xC0
+#define SP_TX_MPEG_DATA14                      0xC1
+#define SP_TX_MPEG_DATA15                      0xC2
+#define SP_TX_MPEG_DATA16                      0xC3
+#define SP_TX_MPEG_DATA17                      0xC4
+#define SP_TX_MPEG_DATA18                      0xC5
+#define SP_TX_MPEG_DATA19                      0xC6
+#define SP_TX_MPEG_DATA20                      0xC7
+#define SP_TX_MPEG_DATA21                      0xC8
+#define SP_TX_MPEG_DATA22                      0xC9
+#define SP_TX_MPEG_DATA23                      0xCA
+#define SP_TX_MPEG_DATA24                      0xCB
+#define SP_TX_MPEG_DATA25                      0xCC
+#define SP_TX_MPEG_DATA26                      0xCD
+#define SP_TX_MPEG_DATA27                      0xCE
+
+/* #define GNSS_CTRL_REG                       0xCD */
+/* bit position */
+/* #define ENABLE_SSC_FILTER                   0x80 */
+
+/* #define SSC_D_VALUE                         0xD0 */
+/* #define SSC_CTRL_REG2                       0xD1 */
+
+#define ANALOG_DEBUG_REG1                      0xDC
+/* bit position */
+#define ANALOG_SEL_BG                          0x40
+#define ANALOG_SWING_A_30PER                   0x08
+
+#define ANALOG_DEBUG_REG2                      0xDD
+/* bit position */
+#define ANALOG_24M_SEL                         0x08
+/* #define ANALOG_FILTER_ENABLED               0x10 */
+
+
+#define ANALOG_DEBUG_REG3                      0xDE
+
+#define PLL_FILTER_CTRL1                       0xDF
+/* bit position */
+#define PD_RING_OSC                            0x40
+
+#define PLL_FILTER_CTRL2                       0xE0
+#define PLL_FILTER_CTRL3                       0xE1
+#define PLL_FILTER_CTRL4                       0xE2
+#define PLL_FILTER_CTRL5                       0xE3
+#define PLL_FILTER_CTRL6                       0xE4
+
+#define SP_TX_I2S_CTRL                         0xE6
+#define SP_TX_I2S_FMT                          0xE7
+#define SP_TX_I2S_CH_Status1                   0xD0
+#define SP_TX_I2S_CH_Status2                   0xD1
+#define SP_TX_I2S_CH_Status3                   0xD2
+#define SP_TX_I2S_CH_Status4                   0xD3
+#define SP_TX_I2S_CH_Status5                   0xD4
+
+/* interrupt */
+#define SP_COMMON_INT_STATUS1                  0xF1
+/* bit position */
+#define SP_COMMON_INT1_PLL_LOCK_CHG            0x40
+#define SP_COMMON_INT1_VIDEO_FORMAT_CHG                0x08
+#define SP_COMMON_INT1_AUDIO_CLK_CHG           0x04
+#define SP_COMMON_INT1_VIDEO_CLOCK_CHG         0x02
+
+
+#define SP_COMMON_INT_STATUS2                  0xF2
+/* bit position */
+#define SP_COMMON_INT2_AUTHCHG                 0x02
+#define SP_COMMON_INT2_AUTHDONE                        0x01
+
+#define SP_COMMON_INT_STATUS3                  0xF3
+/* bit position */
+#define SP_COMMON_INT3_AFIFO_UNDER             0x80
+#define SP_COMMON_INT3_AFIFO_OVER              0x40
+
+#define SP_COMMON_INT_STATUS4                  0xF4
+/* bit position */
+#define SP_COMMON_INT4_PLUG                    0x01
+#define SP_COMMON_INT4_ESYNC_ERR               0x10
+#define SP_COMMON_INT4_HPDLOST                 0x02
+#define SP_COMMON_INT4_HPD_CHANGE              0x04
+
+
+#define SP_TX_INT_STATUS1                      0xF7
+/* bit position */
+#define SP_TX_INT_STATUS1_HPD                  0x40
+#define SP_TX_INT_STATUS1_TRAINING_Finish      0x20
+#define SP_TX_INT_STATUS1_POLLING_ERR          0x10
+#define SP_TX_INT_SINK_CHG                     0x08
+
+/* interrupt mask */
+#define SP_COMMON_INT_MASK1                    0xF8
+#define SP_COMMON_INT_MASK2                    0xF9
+#define SP_COMMON_INT_MASK3                    0xFA
+#define SP_COMMON_INT_MASK4                    0xFB
+#define SP_INT_MASK                            0xFE
+#define SP_TX_INT_CTRL_REG                     0xFF
+/* End for dev_addr 0x72 or 0x76 */
+
+/***************************************************************/
+/***************************************************************/
+
+/* DPCD regs */
+#define DPCD_DPCD_REV                          0x00
+#define DPCD_MAX_LINK_RATE                     0x01
+#define DPCD_MAX_LANE_COUNT                    0x02
+#define DPCD_MAX_DOWNSPREAD                    0x03
+#define DPCD_NORP                              0x04
+#define DPCD_DOWNSTREAMPORT_PRESENT            0x05
+
+#define DPCD_RECEIVE_PORT0_CAP_0               0x08
+#define DPCD_RECEIVE_PORT0_CAP_1               0x09
+#define DPCD_RECEIVE_PORT0_CAP_2               0x0a
+#define DPCD_RECEIVE_PORT0_CAP_3               0x0b
+
+#define DPCD_LINK_BW_SET                       0x00
+#define DPCD_LANE_COUNT_SET                    0x01
+#define DPCD_TRAINING_PATTERN_SET              0x02
+#define DPCD_TRAINNIG_LANE0_SET                        0x03
+#define DPCD_TRAINNIG_LANE1_SET                        0x04
+#define DPCD_TRAINNIG_LANE2_SET                        0x05
+#define DPCD_TRAINNIG_LANE3_SET                        0x06
+#define DPCD_DOWNSPREAD_CTRL                   0x07
+
+#define DPCD_SINK_COUNT                                0x00
+#define DPCD_DEVICE_SERVICE_IRQ_VECTOR         0x01
+#define DPCD_LANE0_1_STATUS                    0x02
+#define DPCD_LANE2_3_STATUS                    0x03
+#define DPCD_LANE_ALIGN_STATUS_UPDATED         0x04
+#define DPCD_SINK_STATUS                       0x05
+#define DPCD_ADJUST_REQUEST_LANE0_1            0x06
+#define DPCD_ADJUST_REQUEST_LANE2_3            0x07
+#define DPCD_TRAINING_SCORE_LANE0              0x08
+#define DPCD_TRAINING_SCORE_LANE1              0x09
+#define DPCD_TRAINING_SCORE_LANE2              0x0a
+#define DPCD_TRAINING_SCORE_LANE3              0x0b
+
+#define DPCD_TEST_REQUEST                      0x18
+#define DPCD_TEST_LINK_RATE                    0x19
+
+#define DPCD_TEST_LANE_COUNT                   0x20
+
+#define DPCD_TEST_Response                     0x60
+#define TEST_ACK                               0x01
+/* bit position */
+#define DPCD_TEST_EDID_Checksum_Write          0x04
+
+#define DPCD_TEST_EDID_Checksum                        0x61
+
+
+#define DPCD_SPECIFIC_INTERRUPT                        0x10
+/* define for downstream HDMI Rx sense detection */
+#define DPCD_USER_COMM1                                0x22
+
+
+#endif
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_extern/i2c_tc101.c b/drivers/amlogic/media/vout/lcd/lcd_extern/i2c_tc101.c
new file mode 100644 (file)
index 0000000..bbeb459
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/i2c_tc101.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+
+static struct lcd_extern_config_s *ext_config;
+static struct i2c_client *aml_tc101_i2c_client;
+
+#define LCD_EXTERN_NAME                        "lcd_i2c_tc101"
+
+static unsigned char i2c_init_table[][3] = {
+       /* {0xff, 0xff, 20},//delay mark(20ms) */
+       {0xf8, 0x30, 0xb2},
+       {0xf8, 0x33, 0xc2},
+       {0xf8, 0x31, 0xf0},
+       {0xf8, 0x40, 0x80},
+       {0xf8, 0x81, 0xec},
+       {0xff, 0xff, 0xff},/* end mark */
+};
+
+static int aml_i2c_write(struct i2c_client *i2client, unsigned char *buff,
+               unsigned int len)
+{
+       int ret = 0;
+       struct i2c_msg msg[] = {
+               {
+                       .addr = i2client->addr,
+                       .flags = 0,
+                       .len = len,
+                       .buf = buff,
+               },
+       };
+
+       ret = i2c_transfer(i2client->adapter, msg, 1);
+       if (ret < 0) {
+               EXTERR("%s: i2c transfer failed [addr 0x%02x]\n",
+                       __func__, i2client->addr);
+       }
+
+       return ret;
+}
+#if 0
+static int aml_i2c_read(struct i2c_client *i2client, unsigned char *buff,
+               unsigned int len)
+{
+       int ret = 0;
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = i2client->addr,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = buff,
+               },
+               {
+                       .addr = i2client->addr,
+                       .flags = I2C_M_RD,
+                       .len = len,
+                       .buf = buff,
+               },
+       };
+
+       ret = i2c_transfer(i2client->adapter, msgs, 2);
+       if (ret < 0) {
+               EXTERR("%s: i2c transfer failed [addr 0x%02x]\n",
+                       __func__, i2client->addr);
+       }
+
+       return ret;
+}
+
+static int i2c_reg_read(unsigned char reg, unsigned char *buf)
+{
+       int ret = 0;
+
+       return ret;
+}
+
+static int i2c_reg_write(unsigned char reg, unsigned char value)
+{
+       int ret = 0;
+
+       return ret;
+}
+#endif
+
+static int lcd_extern_power_on(void)
+{
+       unsigned char tData[4];
+       int i = 0, ending_flag = 0;
+       int ret = 0;
+
+       while (ending_flag == 0) {
+               if ((i2c_init_table[i][0] == 0xff) &&
+                       (i2c_init_table[i][1] == 0xff)) { /* special mark */
+                       if (i2c_init_table[i][2] == 0xff) /* ending flag */
+                               ending_flag = 1;
+                       else /* delay flag */
+                               mdelay(i2c_init_table[i][2]);
+               } else {
+                       tData[0] = i2c_init_table[i][0];
+                       tData[1] = i2c_init_table[i][1];
+                       tData[2] = i2c_init_table[i][2];
+                       aml_i2c_write(aml_tc101_i2c_client, tData, 3);
+               }
+               i++;
+       }
+       EXTPR("%s\n", __func__);
+       return ret;
+}
+
+static int lcd_extern_power_off(void)
+{
+       int ret = 0;
+
+       return ret;
+}
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       int ret = 0;
+
+       if (ext_drv) {
+               ext_drv->power_on  = lcd_extern_power_on;
+               ext_drv->power_off = lcd_extern_power_off;
+       } else {
+               EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+               ret = -1;
+       }
+
+       return ret;
+}
+
+static int aml_tc101_i2c_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+               EXTERR("%s: functionality check failed\n", __func__);
+       else
+               aml_tc101_i2c_client = client;
+
+       EXTPR("%s OK\n", __func__);
+       return 0;
+}
+
+static int aml_tc101_i2c_remove(struct i2c_client *client)
+{
+       return 0;
+}
+
+static const struct i2c_device_id aml_tc101_i2c_id[] = {
+       {LCD_EXTERN_NAME, 0},
+       { }
+};
+/* MODULE_DEVICE_TABLE(i2c, aml_tc101_id); */
+
+static struct i2c_driver aml_tc101_i2c_driver = {
+       .probe    = aml_tc101_i2c_probe,
+       .remove   = aml_tc101_i2c_remove,
+       .id_table = aml_tc101_i2c_id,
+       .driver = {
+               .name = LCD_EXTERN_NAME,
+               .owner = THIS_MODULE,
+       },
+};
+
+int aml_lcd_extern_i2c_tc101_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       struct i2c_board_info i2c_info;
+       struct i2c_adapter *adapter;
+       struct i2c_client *i2c_client;
+       int ret = 0;
+
+       ext_config = &ext_drv->config;
+       memset(&i2c_info, 0, sizeof(i2c_info));
+
+       adapter = i2c_get_adapter(ext_drv->config.i2c_bus);
+       if (!adapter) {
+               EXTERR("%s failed to get i2c adapter\n", ext_drv->config.name);
+               return -1;
+       }
+
+       strncpy(i2c_info.type, ext_drv->config.name, I2C_NAME_SIZE);
+       i2c_info.addr = ext_drv->config.i2c_addr;
+       i2c_info.platform_data = &ext_drv->config;
+       i2c_info.flags = 0;
+       if (i2c_info.addr > 0x7f) {
+               EXTERR("%s invalid i2c address: 0x%02x\n",
+                       ext_drv->config.name, ext_drv->config.i2c_addr);
+               return -1;
+       }
+       i2c_client = i2c_new_device(adapter, &i2c_info);
+       if (!i2c_client) {
+               EXTERR("%s failed to new i2c device\n", ext_drv->config.name);
+       } else {
+               if (lcd_debug_print_flag) {
+                       EXTPR("%s new i2c device succeed\n",
+                               ext_drv->config.name);
+               }
+       }
+
+       if (!aml_tc101_i2c_client) {
+               ret = i2c_add_driver(&aml_tc101_i2c_driver);
+               if (ret) {
+                       EXTERR("%s add i2c_driver failed\n",
+                               ext_drv->config.name);
+                       return -1;
+               }
+       }
+
+       ret = lcd_extern_driver_update(ext_drv);
+
+       if (lcd_debug_print_flag)
+               EXTPR("%s: %d\n", __func__, ret);
+       return ret;
+}
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.c b/drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.c
new file mode 100644 (file)
index 0000000..6615564
--- /dev/null
@@ -0,0 +1,1315 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#include "lcd_extern.h"
+
+static struct device *lcd_extern_dev;
+static int lcd_ext_driver_num;
+static struct aml_lcd_extern_driver_s *lcd_ext_driver[LCD_EXT_DRIVER_MAX];
+static int lcd_extern_add_driver(struct lcd_extern_config_s *extconf);
+
+static unsigned int lcd_ext_key_valid;
+static unsigned char lcd_ext_config_load;
+
+static unsigned char lcd_extern_init_on_table[LCD_EXTERN_INIT_TABLE_MAX] = {
+       0xff,
+};
+
+static unsigned char lcd_extern_init_off_table[LCD_EXTERN_INIT_TABLE_MAX] = {
+       0xff,
+};
+
+struct lcd_ext_gpio_s {
+       char name[15];
+       struct gpio_desc *gpio;
+       int flag;
+};
+
+static struct lcd_ext_gpio_s lcd_extern_gpio[LCD_EXTERN_GPIO_NUM_MAX] = {
+       {.flag = 0,},
+       {.flag = 0,},
+       {.flag = 0,},
+       {.flag = 0,},
+       {.flag = 0,},
+       {.flag = 0,},
+};
+
+struct aml_lcd_extern_driver_s *aml_lcd_extern_get_driver(int index)
+{
+       int i;
+       struct aml_lcd_extern_driver_s *ext_driver = NULL;
+
+       if (index >= LCD_EXTERN_INDEX_INVALID) {
+               EXTERR("invalid driver index: %d\n", index);
+               return NULL;
+       }
+       for (i = 0; i < lcd_ext_driver_num; i++) {
+               if (lcd_ext_driver[i]->config.index == index) {
+                       ext_driver = lcd_ext_driver[i];
+                       break;
+               }
+       }
+       if (ext_driver == NULL)
+               EXTERR("invalid driver index: %d\n", index);
+       return ext_driver;
+}
+
+#if 0
+static struct aml_lcd_extern_driver_s
+       *aml_lcd_extern_get_driver_by_name(char *name)
+{
+       int i;
+       struct aml_lcd_extern_driver_s *ext_driver = NULL;
+
+       for (i = 0; i < lcd_ext_driver_num; i++) {
+               if (strcmp(lcd_ext_driver[i]->config.name, name) == 0) {
+                       ext_driver = lcd_ext_driver[i];
+                       break;
+               }
+       }
+       if (ext_driver == NULL)
+               EXTERR("invalid driver name: %s\n", name);
+       return ext_driver;
+}
+#endif
+
+#ifdef CONFIG_OF
+void lcd_extern_gpio_register(unsigned char index)
+{
+       struct lcd_ext_gpio_s *ext_gpio;
+       const char *str;
+       int ret;
+
+       if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
+               EXTERR("gpio index %d, exit\n", index);
+               return;
+       }
+       ext_gpio = &lcd_extern_gpio[index];
+       if (ext_gpio->flag) {
+               if (lcd_debug_print_flag) {
+                       EXTPR("gpio %s[%d] is already registered\n",
+                               ext_gpio->name, index);
+               }
+               return;
+       }
+
+       /* get gpio name */
+       ret = of_property_read_string_index(lcd_extern_dev->of_node,
+               "extern_gpio_names", index, &str);
+       if (ret) {
+               EXTERR("failed to get extern_gpio_names: %d\n", index);
+               str = "unknown";
+       }
+       strcpy(ext_gpio->name, str);
+
+       /* request gpio */
+       ext_gpio->gpio = devm_gpiod_get_index(
+               lcd_extern_dev, "extern", index, GPIOD_OUT_HIGH);
+       if (IS_ERR(ext_gpio->gpio)) {
+               EXTERR("register gpio %s[%d]: %p, err: %d\n",
+                       ext_gpio->name, index, ext_gpio->gpio,
+                       IS_ERR(ext_gpio->gpio));
+               ext_gpio->gpio = NULL;
+       } else {
+               if (lcd_debug_print_flag) {
+                       EXTPR("register gpio %s[%d]: %p\n",
+                               ext_gpio->name, index, ext_gpio->gpio);
+               }
+       }
+}
+#endif
+
+void lcd_extern_gpio_set(unsigned char index, int value)
+{
+       struct lcd_ext_gpio_s *ext_gpio;
+
+       if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
+               EXTERR("gpio index %d, exit\n", index);
+               return;
+       }
+       ext_gpio = &lcd_extern_gpio[index];
+       if (ext_gpio->flag == 0) {
+               EXTERR("gpio [%d] is not registered\n", index);
+               return;
+       }
+       if (IS_ERR(ext_gpio->gpio)) {
+               EXTERR("gpio %s[%d]: %p, err: %ld\n",
+                       ext_gpio->name, index, ext_gpio->gpio,
+                       PTR_ERR(ext_gpio->gpio));
+               return;
+       }
+
+       switch (value) {
+       case LCD_GPIO_OUTPUT_LOW:
+       case LCD_GPIO_OUTPUT_HIGH:
+               gpiod_direction_output(ext_gpio->gpio, value);
+               break;
+       case LCD_GPIO_INPUT:
+       default:
+               gpiod_direction_input(ext_gpio->gpio);
+               break;
+       }
+       if (lcd_debug_print_flag) {
+               EXTPR("set gpio %s[%d] value: %d\n",
+               ext_gpio->name, index, value);
+       }
+}
+
+unsigned int lcd_extern_gpio_get(unsigned char index)
+{
+       struct lcd_ext_gpio_s *ext_gpio;
+
+       if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
+               EXTERR("gpio index %d, exit\n", index);
+               return -1;
+       }
+       ext_gpio = &lcd_extern_gpio[index];
+       if (ext_gpio->flag == 0) {
+               EXTERR("gpio [%d] is not registered\n", index);
+               return -1;
+       }
+       if (IS_ERR(ext_gpio->gpio)) {
+               EXTERR("gpio %s[%d]: %p, err: %ld\n",
+                       ext_gpio->name, index, ext_gpio->gpio,
+                       PTR_ERR(ext_gpio->gpio));
+               return -1;
+       }
+
+       return gpiod_get_value(ext_gpio->gpio);
+}
+
+#ifdef CONFIG_OF
+static unsigned char lcd_extern_get_i2c_bus_str(const char *str)
+{
+       unsigned char i2c_bus;
+
+       if (strncmp(str, "i2c_bus_ao", 10) == 0)
+               i2c_bus = AML_I2C_MASTER_AO;
+       else if (strncmp(str, "i2c_bus_a", 9) == 0)
+               i2c_bus = AML_I2C_MASTER_A;
+       else if (strncmp(str, "i2c_bus_b", 9) == 0)
+               i2c_bus = AML_I2C_MASTER_B;
+       else if (strncmp(str, "i2c_bus_c", 9) == 0)
+               i2c_bus = AML_I2C_MASTER_C;
+       else if (strncmp(str, "i2c_bus_d", 9) == 0)
+               i2c_bus = AML_I2C_MASTER_D;
+       else {
+               i2c_bus = AML_I2C_MASTER_A;
+               EXTERR("invalid i2c_bus: %s\n", str);
+       }
+
+       return i2c_bus;
+}
+
+struct device_node *aml_lcd_extern_get_dts_child(int index)
+{
+       char propname[30];
+       struct device_node *child;
+
+       sprintf(propname, "extern_%d", index);
+       child = of_get_child_by_name(lcd_extern_dev->of_node, propname);
+       return child;
+}
+
+static int lcd_extern_get_config_dts(struct device_node *of_node,
+               struct lcd_extern_config_s *extconf)
+{
+       int ret;
+       int val;
+       const char *str;
+       unsigned char cmd_size, index;
+       int i, j;
+
+       extconf->index = LCD_EXTERN_INDEX_INVALID;
+       extconf->type = LCD_EXTERN_MAX;
+       extconf->table_init_loaded = 0;
+       extconf->table_init_on = lcd_extern_init_on_table;
+       extconf->table_init_off = lcd_extern_init_off_table;
+
+       ret = of_property_read_u32(of_node, "index", &val);
+       if (ret)
+               EXTERR("get index failed, exit\n");
+       else
+               extconf->index = (unsigned char)val;
+
+       ret = of_property_read_string(of_node, "status", &str);
+       if (ret) {
+               EXTERR("get index %d status failed\n", extconf->index);
+       } else {
+               if (lcd_debug_print_flag)
+                       EXTPR("index %d status = %s\n", extconf->index, str);
+               if (strncmp(str, "okay", 2) == 0)
+                       extconf->status = 1;
+               else
+                       return -1;
+       }
+
+       ret = of_property_read_string(of_node, "extern_name", &str);
+       if (ret) {
+               str = "none";
+               EXTERR("get extern_name failed\n");
+       }
+       strcpy(extconf->name, str);
+       EXTPR("load config: %s[%d]\n", extconf->name, extconf->index);
+
+       ret = of_property_read_u32(of_node, "type", &extconf->type);
+       if (ret) {
+               extconf->type = LCD_EXTERN_MAX;
+               EXTERR("get type failed, exit\n");
+               return -1;
+       }
+
+       switch (extconf->type) {
+       case LCD_EXTERN_I2C:
+               ret = of_property_read_u32(of_node, "i2c_address", &val);
+               if (ret) {
+                       EXTERR("get %s i2c_address failed, exit\n",
+                               extconf->name);
+                       extconf->i2c_addr = 0xff;
+               } else {
+                       extconf->i2c_addr = (unsigned char)val;
+               }
+               if (lcd_debug_print_flag) {
+                       EXTPR("%s i2c_address=0x%02x\n",
+                               extconf->name, extconf->i2c_addr);
+               }
+               ret = of_property_read_u32(of_node, "i2c_second_address", &val);
+               if (ret) {
+                       EXTERR("get %s i2c_second_address failed, exit\n",
+                               extconf->name);
+                       extconf->i2c_addr2 = 0xff;
+               } else {
+                       extconf->i2c_addr = (unsigned char)val;
+               }
+               if (lcd_debug_print_flag) {
+                       EXTPR("%s i2c_second_address=0x%02x\n",
+                               extconf->name, extconf->i2c_addr2);
+               }
+
+               ret = of_property_read_string(of_node, "i2c_bus", &str);
+               if (ret) {
+                       EXTERR("get %s i2c_bus failed, exit\n", extconf->name);
+                       extconf->i2c_bus = AML_I2C_MASTER_A;
+               } else {
+                       extconf->i2c_bus = lcd_extern_get_i2c_bus_str(str);
+               }
+               if (lcd_debug_print_flag) {
+                       EXTPR("%s i2c_bus=%s[%d]\n",
+                               extconf->name, str, extconf->i2c_bus);
+               }
+
+               ret = of_property_read_u32(of_node, "cmd_size", &val);
+               if (ret) {
+                       EXTERR("get %s cmd_size failed\n", extconf->name);
+                       extconf->cmd_size = 0;
+               } else {
+                       extconf->cmd_size = (unsigned char)val;
+               }
+               if (lcd_debug_print_flag) {
+                       EXTPR("%s cmd_size=%d\n",
+                               extconf->name, extconf->cmd_size);
+               }
+               cmd_size = extconf->cmd_size;
+               if (cmd_size > 1) {
+                       i = 0;
+                       while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+                               for (j = 0; j < cmd_size; j++) {
+                                       ret = of_property_read_u32_index(
+                                               of_node, "init_on",
+                                               (i + j), &val);
+                                       if (ret) {
+                                               EXTERR("get init_on failed\n");
+                                               extconf->table_init_on[i] =
+                                                       LCD_EXTERN_INIT_END;
+                                               goto i2c_get_init_on_dts;
+                                       }
+                                       extconf->table_init_on[i + j] =
+                                               (unsigned char)val;
+                               }
+                               if (extconf->table_init_on[i] ==
+                                       LCD_EXTERN_INIT_END) {
+                                       break;
+                               } else if (extconf->table_init_on[i] ==
+                                       LCD_EXTERN_INIT_GPIO) {
+                                       /* gpio request */
+                                       index = extconf->table_init_on[i+1];
+                                       if (index < LCD_EXTERN_GPIO_NUM_MAX)
+                                               lcd_extern_gpio_register(index);
+                               }
+                               i += cmd_size;
+                       }
+                       extconf->table_init_loaded = 1;
+i2c_get_init_on_dts:
+                       i = 0;
+                       while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+                               for (j = 0; j < cmd_size; j++) {
+                                       ret = of_property_read_u32_index(
+                                               of_node, "init_off",
+                                               (i + j), &val);
+                                       if (ret) {
+                                               EXTERR("get init_off failed\n");
+                                               extconf->table_init_off[i] =
+                                                       LCD_EXTERN_INIT_END;
+                                               goto i2c_get_init_off_dts;
+                                       }
+                                       extconf->table_init_off[i + j] =
+                                               (unsigned char)val;
+                               }
+                               if (extconf->table_init_off[i] ==
+                                       LCD_EXTERN_INIT_END) {
+                                       break;
+                               } else if (extconf->table_init_off[i] ==
+                                       LCD_EXTERN_INIT_GPIO) {
+                                       /* gpio request */
+                                       index = extconf->table_init_off[i+1];
+                                       if (index < LCD_EXTERN_GPIO_NUM_MAX)
+                                               lcd_extern_gpio_register(index);
+                               }
+                               i += cmd_size;
+                       }
+               }
+i2c_get_init_off_dts:
+               break;
+       case LCD_EXTERN_SPI:
+               ret = of_property_read_u32(of_node, "gpio_spi_cs", &val);
+               if (ret) {
+                       EXTERR("get %s gpio_spi_cs failed, exit\n",
+                               extconf->name);
+                       extconf->spi_gpio_cs = LCD_EXTERN_GPIO_NUM_MAX;
+               } else {
+                       extconf->spi_gpio_cs = val;
+                       lcd_extern_gpio_register(val);
+                       if (lcd_debug_print_flag) {
+                               EXTPR("spi_gpio_cs: %d\n",
+                                       extconf->spi_gpio_cs);
+                       }
+               }
+               ret = of_property_read_u32(of_node, "gpio_spi_clk", &val);
+               if (ret) {
+                       EXTERR("get %s gpio_spi_clk failed, exit\n",
+                               extconf->name);
+                       extconf->spi_gpio_clk = LCD_EXTERN_GPIO_NUM_MAX;
+               } else {
+                       extconf->spi_gpio_clk = val;
+                       lcd_extern_gpio_register(val);
+                       if (lcd_debug_print_flag) {
+                               EXTPR("spi_gpio_clk: %d\n",
+                                       extconf->spi_gpio_clk);
+                       }
+               }
+               ret = of_property_read_u32(of_node, "gpio_spi_data", &val);
+               if (ret) {
+                       EXTERR("get %s gpio_spi_data failed, exit\n",
+                               extconf->name);
+                       extconf->spi_gpio_data = LCD_EXTERN_GPIO_NUM_MAX;
+               } else {
+                       extconf->spi_gpio_data = val;
+                       lcd_extern_gpio_register(val);
+                       if (lcd_debug_print_flag) {
+                               EXTPR("spi_gpio_data: %d\n",
+                                       extconf->spi_gpio_data);
+                       }
+               }
+               ret = of_property_read_u32(of_node, "spi_clk_freq", &val);
+               if (ret) {
+                       EXTERR("get %s spi_clk_freq failed, default to %dHz\n",
+                               extconf->name, LCD_EXTERN_SPI_CLK_FREQ_DFT);
+                       extconf->spi_clk_freq = LCD_EXTERN_SPI_CLK_FREQ_DFT;
+               } else {
+                       extconf->spi_clk_freq = val;
+                       if (lcd_debug_print_flag) {
+                               EXTPR("spi_clk_freq: %dHz\n",
+                                       extconf->spi_clk_freq);
+                       }
+               }
+               ret = of_property_read_u32(of_node, "spi_clk_pol", &val);
+               if (ret) {
+                       EXTERR("get %s spi_clk_pol failed, default to 1\n",
+                               extconf->name);
+                       extconf->spi_clk_pol = 1;
+               } else {
+                       extconf->spi_clk_pol = (unsigned char)val;
+                       if (lcd_debug_print_flag) {
+                               EXTPR("spi_clk_pol: %dHz\n",
+                                       extconf->spi_clk_pol);
+                       }
+               }
+               ret = of_property_read_u32(of_node, "cmd_size", &val);
+               if (ret) {
+                       EXTERR("get %s cmd_size failed\n", extconf->name);
+                       extconf->cmd_size = 0;
+               } else {
+                       extconf->cmd_size = (unsigned char)val;
+               }
+               if (lcd_debug_print_flag) {
+                       EXTPR("%s cmd_size=%d\n",
+                               extconf->name, extconf->cmd_size);
+               }
+               cmd_size = extconf->cmd_size;
+               if (cmd_size > 1) {
+                       i = 0;
+                       while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+                               for (j = 0; j < cmd_size; j++) {
+                                       ret = of_property_read_u32_index(
+                                               of_node, "init_on",
+                                               (i + j), &val);
+                                       if (ret) {
+                                               EXTERR("get init_on failed\n");
+                                               extconf->table_init_on[i] =
+                                                       LCD_EXTERN_INIT_END;
+                                               goto spi_get_init_on_dts;
+                                       }
+                                       extconf->table_init_on[i + j] =
+                                               (unsigned char)val;
+                               }
+                               if (extconf->table_init_on[i] ==
+                                       LCD_EXTERN_INIT_END) {
+                                       break;
+                               } else if (extconf->table_init_on[i] ==
+                                       LCD_EXTERN_INIT_GPIO) {
+                                       /* gpio request */
+                                       index = extconf->table_init_on[i+1];
+                                       if (index < LCD_EXTERN_GPIO_NUM_MAX)
+                                               lcd_extern_gpio_register(index);
+                               }
+                               i += cmd_size;
+                       }
+                       extconf->table_init_loaded = 1;
+spi_get_init_on_dts:
+                       i = 0;
+                       while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+                               for (j = 0; j < cmd_size; j++) {
+                                       ret = of_property_read_u32_index(
+                                               of_node, "init_off",
+                                               (i + j), &val);
+                                       if (ret) {
+                                               EXTERR("get init_off failed\n");
+                                               extconf->table_init_off[i] =
+                                                       LCD_EXTERN_INIT_END;
+                                               goto spi_get_init_off_dts;
+                                       }
+                                       extconf->table_init_off[i + j] =
+                                               (unsigned char)val;
+                               }
+                               if (extconf->table_init_off[i] ==
+                                       LCD_EXTERN_INIT_END) {
+                                       break;
+                               } else if (extconf->table_init_off[i] ==
+                                       LCD_EXTERN_INIT_GPIO) {
+                                       /* gpio request */
+                                       index = extconf->table_init_off[i+1];
+                                       if (index < LCD_EXTERN_GPIO_NUM_MAX)
+                                               lcd_extern_gpio_register(index);
+                               }
+                               i += cmd_size;
+                       }
+               }
+spi_get_init_off_dts:
+               break;
+       case LCD_EXTERN_MIPI:
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+#endif
+
+static unsigned char aml_lcd_extern_i2c_bus_table[][2] = {
+       {LCD_EXTERN_I2C_BUS_AO, AML_I2C_MASTER_AO},
+       {LCD_EXTERN_I2C_BUS_A, AML_I2C_MASTER_A},
+       {LCD_EXTERN_I2C_BUS_B, AML_I2C_MASTER_B},
+       {LCD_EXTERN_I2C_BUS_C, AML_I2C_MASTER_C},
+       {LCD_EXTERN_I2C_BUS_D, AML_I2C_MASTER_D},
+};
+
+static unsigned char lcd_extern_get_i2c_bus_unifykey(unsigned char val)
+{
+       unsigned char i2c_bus = LCD_EXTERN_I2C_BUS_INVALID;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(aml_lcd_extern_i2c_bus_table); i++) {
+               if (aml_lcd_extern_i2c_bus_table[i][0] == val) {
+                       i2c_bus = aml_lcd_extern_i2c_bus_table[i][1];
+                       break;
+               }
+       }
+
+       return i2c_bus;
+}
+
+static int lcd_extern_get_config_unifykey(struct lcd_extern_config_s *extconf)
+{
+       unsigned char *para;
+       int i, j, key_len, len;
+       unsigned char cmd_size;
+       unsigned char *p;
+       const char *str;
+       struct aml_lcd_unifykey_header_s ext_header;
+       unsigned char index;
+       int ret;
+
+       extconf->index = LCD_EXTERN_INDEX_INVALID;
+       extconf->type = LCD_EXTERN_MAX;
+       extconf->table_init_loaded = 0;
+       extconf->table_init_on = lcd_extern_init_on_table;
+       extconf->table_init_off = lcd_extern_init_off_table;
+
+       para = kmalloc((sizeof(unsigned char) * LCD_UKEY_LCD_EXT_SIZE),
+               GFP_KERNEL);
+       if (!para) {
+               EXTERR("%s: Not enough memory\n", __func__);
+               return -1;
+       }
+       key_len = LCD_UKEY_LCD_EXT_SIZE;
+       memset(para, 0, (sizeof(unsigned char) * key_len));
+       ret = lcd_unifykey_get("lcd_extern", para, &key_len);
+       if (ret) {
+               kfree(para);
+               return -1;
+       }
+
+       /* check lcd_extern unifykey length */
+       len = 10 + 33 + 10;
+       ret = lcd_unifykey_len_check(key_len, len);
+       if (ret) {
+               EXTERR("unifykey length is not correct\n");
+               kfree(para);
+               return -1;
+       }
+
+       /* header: 10byte */
+       lcd_unifykey_header_check(para, &ext_header);
+       if (lcd_debug_print_flag) {
+               EXTPR("unifykey header:\n");
+               EXTPR("crc32             = 0x%08x\n", ext_header.crc32);
+               EXTPR("data_len          = %d\n", ext_header.data_len);
+               EXTPR("version           = 0x%04x\n", ext_header.version);
+               EXTPR("reserved          = 0x%04x\n", ext_header.reserved);
+       }
+
+       /* basic: 33byte */
+       p = para + LCD_UKEY_HEAD_SIZE;
+       *(p + LCD_UKEY_EXT_NAME - 1) = '\0'; /* ensure string ending */
+       str = (const char *)p;
+       strcpy(extconf->name, str);
+       p += LCD_UKEY_EXT_NAME;
+       extconf->index = *p;
+       p += LCD_UKEY_EXT_INDEX;
+       extconf->type = *p;
+       p += LCD_UKEY_EXT_TYPE;
+       extconf->status = *p;
+       p += LCD_UKEY_EXT_STATUS;
+
+       /* type: 10byte */
+       switch (extconf->type) {
+       case LCD_EXTERN_I2C:
+               extconf->i2c_addr = *p;
+               p += LCD_UKEY_EXT_TYPE_VAL_0;
+               extconf->i2c_addr2 = *p;
+               p += LCD_UKEY_EXT_TYPE_VAL_1;
+               extconf->i2c_bus = lcd_extern_get_i2c_bus_unifykey(*p);
+               p += LCD_UKEY_EXT_TYPE_VAL_2;
+               extconf->cmd_size = *p;
+               p += LCD_UKEY_EXT_TYPE_VAL_3;
+               /* dummy pointer */
+               p += LCD_UKEY_EXT_TYPE_VAL_4;
+               p += LCD_UKEY_EXT_TYPE_VAL_5;
+               p += LCD_UKEY_EXT_TYPE_VAL_6;
+               p += LCD_UKEY_EXT_TYPE_VAL_7;
+               p += LCD_UKEY_EXT_TYPE_VAL_8;
+               p += LCD_UKEY_EXT_TYPE_VAL_9;
+
+               /* power */
+               cmd_size = extconf->cmd_size;
+               if (cmd_size >= 1) {
+                       i = 0;
+                       while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+                               len += cmd_size;
+                               ret = lcd_unifykey_len_check(key_len, len);
+                               if (ret) {
+                                       extconf->table_init_on[i] =
+                                               LCD_EXTERN_INIT_END;
+                                       for (j = 1; j < cmd_size; j++) {
+                                               extconf->table_init_on[i+j] =
+                                                       0x0;
+                                       }
+                                       kfree(para);
+                                       return -1;
+                               }
+                               for (j = 0; j < cmd_size; j++) {
+                                       extconf->table_init_on[i+j] = *p;
+                                       p++;
+                               }
+                               if (extconf->table_init_on[i] ==
+                                       LCD_EXTERN_INIT_END) {
+                                       break;
+                               } else if (extconf->table_init_on[i] ==
+                                       LCD_EXTERN_INIT_GPIO) {
+                                       /* gpio request */
+                                       index = extconf->table_init_on[i+1];
+                                       if (index < LCD_EXTERN_GPIO_NUM_MAX)
+                                               lcd_extern_gpio_register(index);
+                               }
+                               i += cmd_size;
+                       }
+                       extconf->table_init_loaded = 1;
+                       i = 0;
+                       while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+                               len += cmd_size;
+                               ret = lcd_unifykey_len_check(key_len, len);
+                               if (ret) {
+                                       extconf->table_init_off[i] =
+                                               LCD_EXTERN_INIT_END;
+                                       for (j = 1; j < cmd_size; j++) {
+                                               extconf->table_init_off[i+j] =
+                                                       0x0;
+                                       }
+                                       kfree(para);
+                                       return -1;
+                               }
+                               for (j = 0; j < cmd_size; j++) {
+                                       extconf->table_init_off[i+j] = *p;
+                                       p++;
+                               }
+                               if (extconf->table_init_off[i] ==
+                                       LCD_EXTERN_INIT_END) {
+                                       break;
+                               } else if (extconf->table_init_off[i] ==
+                                       LCD_EXTERN_INIT_GPIO) {
+                                       /* gpio request */
+                                       index = extconf->table_init_off[i+1];
+                                       if (index < LCD_EXTERN_GPIO_NUM_MAX)
+                                               lcd_extern_gpio_register(index);
+                               } else {
+                                       i += cmd_size;
+                               }
+                       }
+               }
+               break;
+       case LCD_EXTERN_SPI:
+               extconf->spi_gpio_cs = *p;
+               lcd_extern_gpio_register(*p);
+               p += LCD_UKEY_EXT_TYPE_VAL_0;
+               extconf->spi_gpio_clk = *p;
+               lcd_extern_gpio_register(*p);
+               p += LCD_UKEY_EXT_TYPE_VAL_1;
+               extconf->spi_gpio_data = *p;
+               lcd_extern_gpio_register(*p);
+               p += LCD_UKEY_EXT_TYPE_VAL_2;
+               extconf->spi_clk_freq = (*p | ((*(p + 1)) << 8) |
+                                       ((*(p + 2)) << 16) |
+                                       ((*(p + 3)) << 24));
+               p += LCD_UKEY_EXT_TYPE_VAL_3;
+               p += LCD_UKEY_EXT_TYPE_VAL_4;
+               p += LCD_UKEY_EXT_TYPE_VAL_5;
+               p += LCD_UKEY_EXT_TYPE_VAL_6;
+               extconf->spi_clk_pol = *p;
+               p += LCD_UKEY_EXT_TYPE_VAL_7;
+               extconf->cmd_size = *p;
+               p += LCD_UKEY_EXT_TYPE_VAL_8;
+               /* dummy pointer */
+               p += LCD_UKEY_EXT_TYPE_VAL_9;
+
+               /* init */
+               cmd_size = extconf->cmd_size;
+               if (cmd_size >= 1) {
+                       i = 0;
+                       while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+                               len += cmd_size;
+                               ret = lcd_unifykey_len_check(key_len, len);
+                               if (ret) {
+                                       extconf->table_init_on[i] =
+                                               LCD_EXTERN_INIT_END;
+                                       for (j = 1; j < cmd_size; j++) {
+                                               extconf->table_init_on[i+j] =
+                                                       0x0;
+                                       }
+                                       kfree(para);
+                                       return -1;
+                               }
+                               for (j = 0; j < cmd_size; j++) {
+                                       extconf->table_init_on[i+j] = *p;
+                                       p++;
+                               }
+                               if (extconf->table_init_on[i] ==
+                                       LCD_EXTERN_INIT_END) {
+                                       break;
+                               } else if (extconf->table_init_on[i] ==
+                                       LCD_EXTERN_INIT_GPIO) {
+                                       /* gpio request */
+                                       index = extconf->table_init_on[i+1];
+                                       if (index < LCD_EXTERN_GPIO_NUM_MAX)
+                                               lcd_extern_gpio_register(index);
+                               } else {
+                                       i += cmd_size;
+                               }
+                       }
+                       extconf->table_init_loaded = 1;
+                       i = 0;
+                       while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+                               len += cmd_size;
+                               ret = lcd_unifykey_len_check(key_len, len);
+                               if (ret) {
+                                       extconf->table_init_off[i] =
+                                               LCD_EXTERN_INIT_END;
+                                       for (j = 1; j < cmd_size; j++) {
+                                               extconf->table_init_off[i+j] =
+                                                       0x0;
+                                       }
+                                       kfree(para);
+                                       return -1;
+                               }
+                               for (j = 0; j < cmd_size; j++) {
+                                       extconf->table_init_off[i+j] = *p;
+                                       p++;
+                               }
+                               if (extconf->table_init_off[i] ==
+                                       LCD_EXTERN_INIT_END) {
+                                       break;
+                               } else if (extconf->table_init_off[i] ==
+                                       LCD_EXTERN_INIT_GPIO) {
+                                       /* gpio request */
+                                       index = extconf->table_init_off[i+1];
+                                       if (index < LCD_EXTERN_GPIO_NUM_MAX)
+                                               lcd_extern_gpio_register(index);
+                               } else {
+                                       i += cmd_size;
+                               }
+                       }
+               }
+               break;
+       case LCD_EXTERN_MIPI:
+               /* dummy pointer */
+               p += LCD_UKEY_EXT_TYPE_VAL_0;
+               p += LCD_UKEY_EXT_TYPE_VAL_1;
+               p += LCD_UKEY_EXT_TYPE_VAL_2;
+               p += LCD_UKEY_EXT_TYPE_VAL_3;
+               p += LCD_UKEY_EXT_TYPE_VAL_4;
+               p += LCD_UKEY_EXT_TYPE_VAL_5;
+               p += LCD_UKEY_EXT_TYPE_VAL_6;
+               p += LCD_UKEY_EXT_TYPE_VAL_7;
+               p += LCD_UKEY_EXT_TYPE_VAL_8;
+               p += LCD_UKEY_EXT_TYPE_VAL_9;
+
+               /* init */
+               /* to do */
+               break;
+       default:
+               break;
+       }
+
+       kfree(para);
+       return 0;
+}
+
+static int lcd_extern_get_config(void)
+{
+       struct device_node *child;
+       struct lcd_extern_config_s extconf;
+       int load_id = 0;
+       int ret;
+
+       if (lcd_extern_dev->of_node == NULL) {
+               EXTERR("no lcd_extern of_node exist\n");
+               return -1;
+       }
+       ret = of_property_read_u32(lcd_extern_dev->of_node,
+                       "key_valid", &lcd_ext_key_valid);
+       if (ret) {
+               if (lcd_debug_print_flag)
+                       EXTPR("failed to get key_valid\n");
+               lcd_ext_key_valid = 0;
+       }
+       EXTPR("key_valid: %d\n", lcd_ext_key_valid);
+
+       if (lcd_ext_key_valid) {
+               ret = lcd_unifykey_check("lcd_extern");
+               if (ret < 0)
+                       load_id = 0;
+               else
+                       load_id = 1;
+       }
+       if (load_id) {
+               EXTPR("%s from unifykey\n", __func__);
+               lcd_ext_config_load = 1;
+               memset(&extconf, 0, sizeof(struct lcd_extern_config_s));
+               ret = lcd_extern_get_config_unifykey(&extconf);
+               if (ret == 0)
+                       lcd_extern_add_driver(&extconf);
+       } else {
+#ifdef CONFIG_OF
+               EXTPR("%s from dts\n", __func__);
+               lcd_ext_config_load = 0;
+               for_each_child_of_node(lcd_extern_dev->of_node, child) {
+                       memset(&extconf, 0, sizeof(struct lcd_extern_config_s));
+                       ret = lcd_extern_get_config_dts(child, &extconf);
+                       if (ret == 0)
+                               lcd_extern_add_driver(&extconf);
+               }
+#endif
+       }
+       return 0;
+}
+
+static int lcd_extern_add_i2c(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       int ret = 0;
+
+#ifdef LCD_EXTERN_DEFAULT_ENABLE
+       if (ext_drv->config.index == 0) {
+               ret = aml_lcd_extern_default_probe(ext_drv);
+               return ret;
+       }
+#endif
+
+       if (strcmp(ext_drv->config.name, "i2c_T5800Q") == 0) {
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_T5800Q
+               ret = aml_lcd_extern_i2c_T5800Q_probe(ext_drv);
+#endif
+       } else if (strcmp(ext_drv->config.name, "i2c_tc101") == 0) {
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_TC101
+               ret = aml_lcd_extern_i2c_tc101_probe(ext_drv);
+#endif
+       } else if (strcmp(ext_drv->config.name, "i2c_anx6345") == 0) {
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_ANX6345
+               ret = aml_lcd_extern_i2c_anx6345_probe(ext_drv);
+#endif
+       } else if (strcmp(ext_drv->config.name, "i2c_DLPC3439") == 0) {
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_DLPC3439
+               ret = aml_lcd_extern_i2c_DLPC3439_probe(ext_drv);
+#endif
+       } else {
+               EXTERR("invalid driver name: %s\n", ext_drv->config.name);
+               ret = -1;
+       }
+       return ret;
+}
+
+static int lcd_extern_add_spi(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       int ret = 0;
+
+#ifdef LCD_EXTERN_DEFAULT_ENABLE
+       if (ext_drv->config.index == 0) {
+               ret = aml_lcd_extern_default_probe(ext_drv);
+               return ret;
+       }
+#endif
+
+       if (strcmp(ext_drv->config.name, "spi_LD070WS2") == 0) {
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_SPI_LD070WS2
+               ret = aml_lcd_extern_spi_LD070WS2_probe(ext_drv);
+#endif
+       } else {
+               EXTERR("invalid driver name: %s\n", ext_drv->config.name);
+               ret = -1;
+       }
+       return ret;
+}
+
+static int lcd_extern_add_mipi(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       int ret = 0;
+
+#ifdef LCD_EXTERN_DEFAULT_ENABLE
+       if (ext_drv->config.index == 0) {
+               ret = aml_lcd_extern_default_probe(ext_drv);
+               return ret;
+       }
+#endif
+
+       if (strcmp(ext_drv->config.name, "mipi_N070ICN") == 0) {
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_N070ICN
+               ret = aml_lcd_extern_mipi_N070ICN_probe(ext_drv);
+#endif
+       } else if (strcmp(ext_drv->config.name, "mipi_KD080D13") == 0) {
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_KD080D13
+               ret = aml_lcd_extern_mipi_KD080D13_probe(ext_drv);
+#endif
+       } else {
+               EXTERR("invalid driver name: %s\n", ext_drv->config.name);
+               ret = -1;
+       }
+       return ret;
+}
+
+static int lcd_extern_add_invalid(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       return -1;
+}
+
+static int lcd_extern_add_driver(struct lcd_extern_config_s *extconf)
+{
+       struct aml_lcd_extern_driver_s *ext_drv;
+       int i;
+       int ret = 0;
+
+       if (lcd_ext_driver_num >= LCD_EXT_DRIVER_MAX) {
+               EXTERR("driver num is out of support\n");
+               return -1;
+       }
+       if (extconf->status == 0) {
+               EXTERR("driver %s[%d] status is disabled\n",
+                       extconf->name, extconf->index);
+               return -1;
+       }
+
+       i = lcd_ext_driver_num;
+       lcd_ext_driver[i] =
+               kmalloc(sizeof(struct aml_lcd_extern_driver_s), GFP_KERNEL);
+       if (lcd_ext_driver[i] == NULL) {
+               EXTERR("failed to alloc driver %s[%d], not enough memory\n",
+                       extconf->name, extconf->index);
+               return -1;
+       }
+
+       ext_drv = lcd_ext_driver[i];
+       /* fill config parameters */
+       ext_drv->config.index = extconf->index;
+       strcpy(ext_drv->config.name, extconf->name);
+       ext_drv->config.type = extconf->type;
+       ext_drv->config.status = extconf->status;
+       ext_drv->config.table_init_loaded = extconf->table_init_loaded;
+       ext_drv->config.table_init_on = lcd_extern_init_on_table;
+       ext_drv->config.table_init_off = lcd_extern_init_off_table;
+
+       /* fill config parameters by different type */
+       switch (ext_drv->config.type) {
+       case LCD_EXTERN_I2C:
+               ext_drv->config.i2c_addr = extconf->i2c_addr;
+               ext_drv->config.i2c_addr2 = extconf->i2c_addr2;
+               ext_drv->config.i2c_bus = extconf->i2c_bus;
+               ext_drv->config.cmd_size = extconf->cmd_size;
+               ret = lcd_extern_add_i2c(ext_drv);
+               break;
+       case LCD_EXTERN_SPI:
+               ext_drv->config.spi_gpio_cs = extconf->spi_gpio_cs;
+               ext_drv->config.spi_gpio_clk = extconf->spi_gpio_clk;
+               ext_drv->config.spi_gpio_data = extconf->spi_gpio_data;
+               ext_drv->config.spi_clk_freq = extconf->spi_clk_freq;
+               ext_drv->config.spi_clk_pol = extconf->spi_clk_pol;
+               ext_drv->config.cmd_size = extconf->cmd_size;
+               ret = lcd_extern_add_spi(ext_drv);
+               break;
+       case LCD_EXTERN_MIPI:
+               ret = lcd_extern_add_mipi(ext_drv);
+               break;
+       default:
+               ret = lcd_extern_add_invalid(ext_drv);
+               EXTERR("don't support type %d\n", ext_drv->config.type);
+               break;
+       }
+       if (ret) {
+               EXTERR("add driver failed\n");
+               kfree(lcd_ext_driver[i]);
+               lcd_ext_driver[i] = NULL;
+               return -1;
+       }
+       lcd_ext_driver_num++;
+       EXTPR("add driver %s(%d)\n",
+               ext_drv->config.name, ext_drv->config.index);
+       return 0;
+}
+
+/* debug function */
+static void lcd_extern_config_dump(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       int i, j, len;
+       struct lcd_extern_config_s *econf;
+
+       if (ext_drv == NULL)
+               return;
+
+       econf = &ext_drv->config;
+       EXTPR("driver %s(%d) info:\n", econf->name, econf->index);
+       pr_info("status:          %d\n", econf->status);
+       switch (econf->type) {
+       case LCD_EXTERN_I2C:
+               pr_info("type:            i2c(%d)\n", econf->type);
+               pr_info("cmd_size:        %d\n"
+                       "i2c_addr:        0x%02x\n"
+                       "i2c_addr2:       0x%02x\n"
+                       "i2c_bus:         %d\n"
+                       "table_loaded:    %d\n",
+                       econf->cmd_size, econf->i2c_addr,
+                       econf->i2c_addr2, econf->i2c_bus,
+                       econf->table_init_loaded);
+               len = econf->cmd_size;
+               pr_info("power on:\n");
+               i = 0;
+               while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+                       if (econf->table_init_on[i] == LCD_EXTERN_INIT_END) {
+                               pr_info("\n");
+                       } else {
+                               for (j = 0; j < len; j++) {
+                                       pr_info("0x%02x ",
+                                               econf->table_init_on[i+j]);
+                               }
+                               pr_info("\n");
+                       }
+                       i += len;
+               }
+               pr_info("power off:\n");
+               i = 0;
+               while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+                       if (econf->table_init_off[i] == LCD_EXTERN_INIT_END) {
+                               pr_info("\n");
+                       } else {
+                               for (j = 0; j < len; j++) {
+                                       pr_info("0x%02x ",
+                                               econf->table_init_off[i+j]);
+                               }
+                               pr_info("\n");
+                       }
+                       i += len;
+               }
+               break;
+       case LCD_EXTERN_SPI:
+               pr_info("type:            spi(%d)\n", econf->type);
+               pr_info("cmd_size:        %d\n"
+                       "spi_gpio_cs:     %d\n"
+                       "spi_gpio_clk:    %d\n"
+                       "spi_gpio_data:   %d\n"
+                       "spi_clk_freq:    %dHz\n"
+                       "spi_clk_pol:     %d\n"
+                       "table_loaded:    %d\n",
+                       econf->cmd_size, econf->spi_gpio_cs,
+                       econf->spi_gpio_clk, econf->spi_gpio_data,
+                       econf->spi_clk_freq, econf->spi_clk_pol,
+                       econf->table_init_loaded);
+               len = econf->cmd_size;
+               i = 0;
+               while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+                       if (econf->table_init_on[i] == LCD_EXTERN_INIT_END) {
+                               pr_info("\n");
+                       } else {
+                               for (j = 0; j < len; j++) {
+                                       pr_info("0x%02x ",
+                                               econf->table_init_on[i+j]);
+                               }
+                               pr_info("\n");
+                       }
+                       i += len;
+               }
+               i = 0;
+               while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+                       if (econf->table_init_off[i] == LCD_EXTERN_INIT_END) {
+                               pr_info("\n");
+                       } else {
+                               for (j = 0; j < len; j++) {
+                                       pr_info("0x%02x ",
+                                               econf->table_init_off[i+j]);
+                               }
+                               pr_info("\n");
+                       }
+                       i += len;
+               }
+               break;
+       case LCD_EXTERN_MIPI:
+               pr_info("type:        mipi(%d)\n", econf->type);
+               break;
+       default:
+               pr_info("not support extern_type\n");
+               break;
+       }
+       pr_info("\n");
+}
+
+static const char *lcd_extern_debug_usage_str = {
+"Usage:\n"
+"    echo index <n> > info ; dump specified index driver config\n"
+"    echo all > info ; dump all driver config\n"
+};
+
+static ssize_t lcd_extern_debug_help(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%s\n", lcd_extern_debug_usage_str);
+}
+
+static ssize_t lcd_extern_info_dump(struct class *class,
+               struct class_attribute *attr, const char *buf, size_t count)
+{
+       unsigned int ret;
+       int i, index;
+       struct aml_lcd_extern_driver_s *ext_drv;
+
+       index = LCD_EXTERN_INDEX_INVALID;
+       switch (buf[0]) {
+       case 'i':
+               ret = sscanf(buf, "index %d", &index);
+               ext_drv = aml_lcd_extern_get_driver(index);
+               lcd_extern_config_dump(ext_drv);
+               break;
+       case 'a':
+               for (i = 0; i < lcd_ext_driver_num; i++)
+                       lcd_extern_config_dump(lcd_ext_driver[i]);
+               break;
+       default:
+               EXTERR("invalid command\n");
+               break;
+       }
+
+       if (ret != 1 || ret != 2)
+               return -EINVAL;
+
+       return count;
+}
+
+static ssize_t lcd_extern_debug_key_valid_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d\n", lcd_ext_key_valid);
+}
+
+static ssize_t lcd_extern_debug_config_load_show(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d\n", lcd_ext_config_load);
+}
+
+static struct class_attribute lcd_extern_class_attrs[] = {
+       __ATTR(info, 0644,
+               lcd_extern_debug_help, lcd_extern_info_dump),
+       __ATTR(key_valid,   0644,
+                lcd_extern_debug_key_valid_show, NULL),
+       __ATTR(config_load, 0644,
+               lcd_extern_debug_config_load_show, NULL),
+};
+
+static struct class *debug_class;
+static int creat_lcd_extern_class(void)
+{
+       int i;
+
+       debug_class = class_create(THIS_MODULE, "lcd_ext");
+       if (IS_ERR(debug_class)) {
+               EXTERR("create debug class failed\n");
+               return -1;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(lcd_extern_class_attrs); i++) {
+               if (class_create_file(debug_class,
+                       &lcd_extern_class_attrs[i])) {
+                       EXTERR("create debug attribute %s failed\n",
+                               lcd_extern_class_attrs[i].attr.name);
+               }
+       }
+
+       return 0;
+}
+
+static int remove_lcd_extern_class(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(lcd_extern_class_attrs); i++)
+               class_remove_file(debug_class, &lcd_extern_class_attrs[i]);
+
+       class_destroy(debug_class);
+       debug_class = NULL;
+
+       return 0;
+}
+/* ********************************************************* */
+
+static int aml_lcd_extern_probe(struct platform_device *pdev)
+{
+       lcd_extern_dev = &pdev->dev;
+       lcd_ext_driver_num = 0;
+       lcd_extern_get_config(); /* also add ext_driver */
+
+       creat_lcd_extern_class();
+
+       EXTPR("%s ok\n", __func__);
+       return 0;
+}
+
+static int aml_lcd_extern_remove(struct platform_device *pdev)
+{
+       int i;
+
+       remove_lcd_extern_class();
+       for (i = 0; i < lcd_ext_driver_num; i++) {
+               kfree(lcd_ext_driver[i]);
+               lcd_ext_driver[i] = NULL;
+       }
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id aml_lcd_extern_dt_match[] = {
+       {
+               .compatible = "amlogic, lcd_extern",
+       },
+       {},
+};
+#endif
+
+static struct platform_driver aml_lcd_extern_driver = {
+       .probe  = aml_lcd_extern_probe,
+       .remove = aml_lcd_extern_remove,
+       .driver = {
+               .name  = "lcd_extern",
+               .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+               .of_match_table = aml_lcd_extern_dt_match,
+#endif
+       },
+};
+
+static int __init aml_lcd_extern_init(void)
+{
+       int ret;
+
+       if (lcd_debug_print_flag)
+               EXTPR("%s\n", __func__);
+
+       ret = platform_driver_register(&aml_lcd_extern_driver);
+       if (ret) {
+               EXTERR("driver register failed\n");
+               return -ENODEV;
+       }
+       return ret;
+}
+
+static void __exit aml_lcd_extern_exit(void)
+{
+       platform_driver_unregister(&aml_lcd_extern_driver);
+}
+
+late_initcall(aml_lcd_extern_init);
+module_exit(aml_lcd_extern_exit);
+
+MODULE_AUTHOR("AMLOGIC");
+MODULE_DESCRIPTION("LCD extern driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.dts b/drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.dts
new file mode 100644 (file)
index 0000000..209b51a
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.dts
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+lcd_extern{
+       compatible = "amlogic, lcd_extern";
+       dev_name = "lcd_extern";
+       status = "okay";
+
+       extern-gpios = <&gpio GPIODV_1 GPIO_ACTIVE_HIGH
+                       &gpio GPIODV_16 GPIO_ACTIVE_HIGH
+                       &gpio GPIODV_17 GPIO_ACTIVE_HIGH>;
+       extern_gpio_names = "GPIODV_1","GPIODV_16","GPIODV_17";
+
+       extern_0{
+               index = <0>;
+               extern_name = "i2c_T5800Q";
+               status = "disabled";
+
+               type = <0>; /** lcd_extern_driver type: 0=i2c, 1=spi, 2=mipi */
+               i2c_address = <0x1c>; /** 7bit i2c address */
+               i2c_bus = "i2c_bus_d";
+       };
+
+       extern_1{
+               index = <1>;
+               extern_name = "i2c_tc101";
+               status = "disabled";
+
+               type = <0>; /** lcd_extern_driver type: 0=i2c, 1=spi, 2=mipi */
+               i2c_address = <0x7e>; /** 7bit i2c address */
+               i2c_bus = "i2c_bus_a";
+       };
+
+       extern_2{
+               index = <2>;
+               extern_name = "i2c_anx6345";
+               status = "disabled";
+
+               type = <0>; /** lcd_extern_driver type: 0=i2c, 1=spi, 2=mipi */
+               i2c_address = <0x38>; /** 7bit i2c address */
+               i2c_bus = "i2c_bus_b";
+               lane_num = <1>; /** edp lane_num: 1/2/4 */
+               bits = <0>;  /** lcd_bits(0=6bit, 1=8bit) */
+               link_rate = <1>; /** edp link rate: (0=1.62G, 1=27G, 2=5.4G) */
+       };
+
+       extern_3{
+               index = <3>;
+               extern_name = "spi_LD070WS2";
+               status = "disabled";
+
+               type = <1>; /** lcd_extern_driver type: 0=i2c, 1=spi, 2=mipi */
+
+               gpio_spi_cs = <0>; /* index in extern-gpios */
+               gpio_spi_clk = <1>; /* index in extern-gpios */
+               gpio_spi_data = <2>; /* index in extern-gpios */
+       };
+
+       extern_4{
+               index = <4>;
+               extern_name = "mipi_N070ICN";
+               status = "disabled";
+
+               type = <2>; /** lcd_extern_driver type: 0=i2c, 1=spi, 2=mipi */
+       };
+
+       extern_5{
+               index = <5>;
+               extern_name = "mipi_KD080D13";
+               status = "disabled";
+
+               type = <2>; /** lcd_extern_driver type: 0=i2c, 1=spi, 2=mipi */
+       };
+
+       extern_6{
+               index = <6>;
+               extern_name = "i2c_DLPC3439";
+               status = "disabled";
+
+               type = <0>; /** lcd_extern_driver type: 0=i2c, 1=spi, 2=mipi */
+               i2c_address = <0x1b>; /** 7bit i2c address */
+               i2c_bus = "i2c_bus_a";
+       };
+};
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.h b/drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.h
new file mode 100644 (file)
index 0000000..e0960b0
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LCD_EXTERN_H_
+#define _LCD_EXTERN_H_
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+
+#define EXTPR(fmt, args...)     pr_info("lcd extern: "fmt"", ## args)
+#define EXTERR(fmt, args...)    pr_info("lcd extern: error: "fmt"", ## args)
+
+#define LCD_EXTERN_DRIVER              "lcd_extern"
+
+#ifdef CONFIG_USE_OF
+extern struct device_node *aml_lcd_extern_get_dts_child(int index);
+#endif
+extern void lcd_extern_gpio_register(unsigned char index);
+extern void lcd_extern_gpio_set(unsigned char index, int value);
+extern unsigned int lcd_extern_gpio_get(unsigned char index);
+
+#define LCD_EXTERN_DEFAULT_ENABLE
+
+#ifdef LCD_EXTERN_DEFAULT_ENABLE
+extern int aml_lcd_extern_default_probe(
+       struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_T5800Q
+extern int aml_lcd_extern_i2c_T5800Q_probe(
+       struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_DLPC3439
+extern int aml_lcd_extern_i2c_DLPC3439_probe(
+       struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_TC101
+extern int aml_lcd_extern_i2c_tc101_probe(
+       struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_ANX6345
+extern int aml_lcd_extern_i2c_anx6345_probe(
+       struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_SPI_LD070WS2
+extern int aml_lcd_extern_spi_LD070WS2_probe(
+       struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_N070ICN
+extern int aml_lcd_extern_mipi_N070ICN_probe(
+       struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_KD080D13
+extern int aml_lcd_extern_mipi_KD080D13_probe(
+       struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+
+#endif
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_extern/mipi_KD080D13.c b/drivers/amlogic/media/vout/lcd/lcd_extern/mipi_KD080D13.c
new file mode 100644 (file)
index 0000000..80b15c2
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/mipi_KD080D13.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+
+#define LCD_EXTERN_NAME                        "lcd_mipi_KD080D13"
+
+/* ******************** mipi command ********************
+ * format:  data_type, num, data....
+ * special: data_type=0xff, num<0xff means delay ms, num=0xff means ending.
+ */
+static unsigned char mipi_init_on_table[] = {
+       0x39, 3, 0xf0, 0x5a, 0x5a,
+       0x39, 3, 0xf1, 0x5a, 0x5a,
+       0x39, 3, 0xfc, 0xa5, 0xa5,
+       0x39, 3, 0xd0, 0x00, 0x10,
+       0x15, 2, 0xb1, 0x10,
+       0x39, 5, 0xb2, 0x14, 0x22, 0x2f, 0x04,
+       0x39, 6, 0xf2, 0x02, 0x08, 0x08, 0x40, 0x10,
+       0x15, 2, 0xb0, 0x03,
+       0x39, 3, 0xfd, 0x23, 0x09,
+       0x39, 11, 0xf3, 0x01, 0x93, 0x20, 0x22, 0x80, 0x05, 0x25, 0x3c, 0x26,
+               0x00,
+       0x39, 46, 0xf4, 0x00, 0x02, 0x03, 0x26, 0x03, 0x02, 0x09, 0x00, 0x07,
+               0x16, 0x16, 0x03, 0x00, 0x08, 0x08, 0x03, 0x0E, 0x0F, 0x12,
+               0x1C, 0x1D, 0x1E, 0x0C, 0x09, 0x01, 0x04, 0x02, 0x61, 0x74,
+               0x75, 0x72, 0x83, 0x80, 0x80, 0xB0, 0x00, 0x01, 0x01, 0x28,
+               0x04, 0x03, 0x28, 0x01, 0xD1, 0x32,
+       0x39, 27, 0xf5, 0x84, 0x2F, 0x2F, 0x5F, 0xAB, 0x98, 0x52, 0x0F, 0x33,
+               0x43, 0x04, 0x59, 0x54, 0x52, 0x05, 0x40, 0x60, 0x4E, 0x60,
+               0x40, 0x27, 0x26, 0x52, 0x25, 0x6D, 0x18,
+       0x39, 9, 0xee, 0x25, 0x00, 0x25, 0x00, 0x25, 0x00, 0x25, 0x00,
+       0x39, 9, 0xef, 0x34, 0x12, 0x98, 0xBA, 0x20, 0x00, 0x24, 0x80,
+       0x39, 33, 0xf7, 0x0E, 0x0E, 0x0A, 0x0A, 0x0F, 0x0F, 0x0B, 0x0B, 0x05,
+               0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0C, 0x0C, 0x08,
+               0x08, 0x0D, 0x0D, 0x09, 0x09, 0x04, 0x06, 0x01, 0x01, 0x01,
+               0x01, 0x01, 0x01,
+       0x39, 4, 0xbc, 0x01, 0x4e, 0x0a,
+       0x39, 6, 0xe1, 0x03, 0x10, 0x1c, 0xa0, 0x10,
+       0x39, 7, 0xf6, 0x60, 0x21, 0xA6, 0x00, 0x00, 0x00,
+       0x39, 7, 0xfe, 0x00, 0x0D, 0x03, 0x21, 0x80, 0x48,
+       0x15, 2, 0xb0, 0x22,
+       0x39, 18, 0xfa, 0x02, 0x34, 0x09, 0x13, 0x0B, 0x0F, 0x16, 0x16, 0x17,
+               0x1E, 0x1D, 0x1C, 0x1E, 0x1D, 0x1D, 0x1F, 0x24,
+       0x15, 2, 0xb0, 0x22,
+       0x39, 18, 0xfb, 0x00, 0x34, 0x07, 0x11, 0x09, 0x0D, 0x14, 0x14, 0x15,
+               0x1C, 0x1F, 0x1C, 0x1D, 0x1D, 0x1D, 0x20, 0x26,
+       0x15, 2, 0xb0, 0x11,
+       0x39, 18, 0xfa, 0x20, 0x34, 0x24, 0x27, 0x19, 0x1B, 0x1F, 0x1E, 0x1B,
+               0x1F, 0x21, 0x1F, 0x1E, 0x20, 0x1E, 0x1E, 0x21,
+       0x15, 2, 0xb0, 0x11,
+       0x39, 18, 0xfb, 0x1E, 0x34, 0x22, 0x25, 0x17, 0x19, 0x1D, 0x1A, 0x19,
+               0x20, 0x1F, 0x1E, 0x20, 0x1E, 0x1E, 0x1F, 0x22,
+       0x39, 18, 0xfa, 0x1C, 0x34, 0x1C, 0x1F, 0x13, 0x17, 0x1A, 0x18, 0x18,
+               0x1E, 0x20, 0x21, 0x21, 0x21, 0x23, 0x22, 0x2A,
+       0x39, 18, 0xfb, 0x1A, 0x34, 0x1A, 0x1D, 0x11, 0x15, 0x18, 0x16, 0x16,
+               0x1C, 0x20, 0x20, 0x20, 0x1F, 0x23, 0x23, 0x2B,
+
+       0x05, 1, 0x11,
+       0xff, 20,
+       0x39, 4, 0xc3, 0x40, 0x00, 0x28,
+       0xff, 200,
+       0x15, 2, 0x35, 0x00,
+       0x05, 1, 0x29,
+       0xff, 30,   /* delay 30ms */
+       0xff, 0xff, /* ending flag */
+};
+
+static unsigned char mipi_init_off_table[] = {
+       0x05, 1, 0x28, /* display off */
+       0xff, 30,      /* delay 30ms */
+       0x05, 1, 0x10, /* sleep in */
+       0xff, 30,      /* delay 30ms */
+       0x39, 4, 0xc3, 0x40, 0x00, 0x20,
+       0xff, 10,
+       0xff, 0xff,   /* ending flag */
+};
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       int ret = 0;
+
+       if (ext_drv) {
+               ext_drv->config.table_init_on  = &mipi_init_on_table[0];
+               ext_drv->config.table_init_off = &mipi_init_off_table[0];
+       } else {
+               EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+               ret = -1;
+       }
+
+       return ret;
+}
+
+int aml_lcd_extern_mipi_KD080D13_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       int ret = 0;
+
+       ret = lcd_extern_driver_update(ext_drv);
+
+       if (lcd_debug_print_flag)
+               EXTPR("%s: %d\n", __func__, ret);
+       return ret;
+}
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_extern/mipi_N070ICN.c b/drivers/amlogic/media/vout/lcd/lcd_extern/mipi_N070ICN.c
new file mode 100644 (file)
index 0000000..1e7111a
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/mipi_N070ICN.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+
+#define LCD_EXTERN_NAME                        "lcd_mipi_N070ICN"
+
+/* ******************** mipi command ********************
+ * format:  data_type, num, data....
+ * special: data_type=0xff, num<0xff means delay ms, num=0xff means ending.
+ */
+static unsigned char mipi_init_on_table[] = {
+       /*  ========== Internal setting  ========== */
+       0x39, 5, 0xFF, 0xAA, 0x55, 0xA5, 0x80,
+
+       0x39, 3, 0x6F, 0x11, 0x00, /* MIPI related Timing Setting */
+       0x39, 3, 0xF7, 0x20, 0x00,
+
+       0x15, 2, 0x6F, 0x06,  /* Improve ESD option */
+       0x15, 2, 0xF7, 0xA0,
+       0x15, 2, 0x6F, 0x19,
+       0x15, 2, 0xF7, 0x12,
+
+       0x15, 2, 0x6F, 0x08,  /* Vcom floating */
+       0x15, 2, 0xFA, 0x40,
+       0x15, 2, 0x6F, 0x11,
+       0x15, 2, 0xF3, 0x01,
+
+       /* ========== page0 relative ========== */
+       0x39, 6, 0xF0, 0x55, 0xAA, 0x52, 0x08, 0x00,
+       0x15, 2, 0xC8, 0x80,
+
+       0x39, 3, 0xB1, 0x6C, 0x01, /* Set WXGA resolution */
+
+       0x15, 2, 0xB6, 0x08,   /* Set source output hold time */
+
+       0x15, 2, 0x6F, 0x02,   /* EQ control function */
+       0x15, 2, 0xB8, 0x08,
+
+       0x39, 3, 0xBB, 0x54, 0x54, /* Set bias current for GOP and SOP */
+
+       0x39, 3, 0xBC, 0x05, 0x05, /* Inversion setting */
+
+       0x15, 2, 0xC7, 0x01, /* zigzag setting */
+
+       /* DSP Timing Settings update for BIST */
+       0x39, 6, 0xBD, 0x02, 0xB0, 0x0C, 0x0A, 0x00,
+
+       /* ========== page1 relative ========== */
+       0x39, 6, 0xF0, 0x55, 0xAA, 0x52, 0x08, 0x01,
+
+       0x39, 3, 0xB0, 0x05, 0x05, /* Setting AVDD, AVEE clamp */
+       0x39, 3, 0xB1, 0x05, 0x05,
+
+       0x39, 3, 0xBC, 0x3A, 0x01, /* VGMP, VGMN, VGSP, VGSN setting */
+       0x39, 3, 0xBD, 0x3E, 0x01,
+
+       0x15, 2, 0xCA, 0x00,  /* gate signal control */
+
+       0x15, 2, 0xC0, 0x04,  /* power IC control */
+       0x15, 2, 0xB2, 0x00,
+       0x15, 2, 0xBE, 0x80,    /*vcom -1.88V */
+
+       0x39, 3, 0xB3, 0x19, 0x19,  /* Setting VGH=15V,  VGL=-11V */
+       0x39, 3, 0xB4, 0x12, 0x12,
+
+       0x39, 3, 0xB9, 0x24, 0x24,  /* power control for VGH,  VGL */
+       0x39, 3, 0xBA, 0x14, 0x14,
+
+       /* ========== page2 relative ========== */
+       0x39, 6, 0xF0, 0x55, 0xAA, 0x52, 0x08, 0x02,
+
+       0x15, 2, 0xEE, 0x01,   /* Gamma setting */
+       /* Gradient Control for Gamma Voltage */
+       0x39, 5, 0xEF, 0x09, 0x06, 0x15, 0x18,
+
+       /* ========== GOA relative ========== */
+       0x39, 7, 0xB0, 0x00, 0x00, 0x00, 0x08, 0x00, 0x17,
+       0x15, 2, 0x6F, 0x06,
+       0x39, 7, 0xB0, 0x00, 0x25, 0x00, 0x30, 0x00, 0x45,
+       0x15, 2, 0x6F, 0x0C,
+       0x39, 5, 0xB0, 0x00, 0x56, 0x00, 0x7A,
+       0x39, 7, 0xB1, 0x00, 0xA3, 0x00, 0xE7, 0x01, 0x20,
+       0x15, 2, 0x6F, 0x06,
+       0x39, 7, 0xB1, 0x01, 0x7A, 0x01, 0xC2, 0x01, 0xC5,
+       0x15, 2, 0x6F, 0x0C,
+       0x39, 5, 0xB1, 0x02, 0x06, 0x02, 0x5F,
+       0x39, 7, 0xB2, 0x02, 0x92, 0x02, 0xD0, 0x02, 0xFC,
+       0x15, 2, 0x6F, 0x06,
+       0x39, 7, 0xB2, 0x03, 0x35, 0x03, 0x5D, 0x03, 0x8B,
+       0x15, 2, 0x6F, 0x0C,
+       0x39, 5, 0xB2, 0x03, 0xA2, 0x03, 0xBF,
+       0x39, 5, 0xB3, 0x03, 0xD2, 0x03, 0xFF,
+
+       /* PAGE6 : GOUT Mapping,  VGLO select */
+       0x39, 6, 0xF0, 0x55, 0xAA, 0x52, 0x08, 0x06,
+       0x39, 3, 0xB0, 0x00, 0x17,
+       0x39, 3, 0xB1, 0x16, 0x15,
+       0x39, 3, 0xB2, 0x14, 0x13,
+       0x39, 3, 0xB3, 0x12, 0x11,
+       0x39, 3, 0xB4, 0x10, 0x2D,
+       0x39, 3, 0xB5, 0x01, 0x08,
+       0x39, 3, 0xB6, 0x09, 0x31,
+       0x39, 3, 0xB7, 0x31, 0x31,
+       0x39, 3, 0xB8, 0x31, 0x31,
+       0x39, 3, 0xB9, 0x31, 0x31,
+       0x39, 3, 0xBA, 0x31, 0x31,
+       0x39, 3, 0xBB, 0x31, 0x31,
+       0x39, 3, 0xBC, 0x31, 0x31,
+       0x39, 3, 0xBD, 0x31, 0x09,
+       0x39, 3, 0xBE, 0x08, 0x01,
+       0x39, 3, 0xBF, 0x2D, 0x10,
+       0x39, 3, 0xC0, 0x11, 0x12,
+       0x39, 3, 0xC1, 0x13, 0x14,
+       0x39, 3, 0xC2, 0x15, 0x16,
+       0x39, 3, 0xC3, 0x17, 0x00,
+       0x39, 3, 0xE5, 0x31, 0x31,
+       0x39, 3, 0xC4, 0x00, 0x17,
+       0x39, 3, 0xC5, 0x16, 0x15,
+       0x39, 3, 0xC6, 0x14, 0x13,
+       0x39, 3, 0xC7, 0x12, 0x11,
+       0x39, 3, 0xC8, 0x10, 0x2D,
+       0x39, 3, 0xC9, 0x01, 0x08,
+       0x39, 3, 0xCA, 0x09, 0x31,
+       0x39, 3, 0xCB, 0x31, 0x31,
+       0x39, 3, 0xCC, 0x31, 0x31,
+       0x39, 3, 0xCD, 0x31, 0x31,
+       0x39, 3, 0xCE, 0x31, 0x31,
+       0x39, 3, 0xCF, 0x31, 0x31,
+       0x39, 3, 0xD0, 0x31, 0x31,
+       0x39, 3, 0xD1, 0x31, 0x09,
+       0x39, 3, 0xD2, 0x08, 0x01,
+       0x39, 3, 0xD3, 0x2D, 0x10,
+       0x39, 3, 0xD4, 0x11, 0x12,
+       0x39, 3, 0xD5, 0x13, 0x14,
+       0x39, 3, 0xD6, 0x15, 0x16,
+       0x39, 3, 0xD7, 0x17, 0x00,
+       0x39, 3, 0xE6, 0x31, 0x31,
+       0x39, 6, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, /* VGL level select; */
+       0x39, 6, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x15, 2, 0xE7, 0x00,
+
+       /* ===page 3====//gate timing control */
+       0x39, 6, 0xF0, 0x55, 0xAA, 0x52, 0x08, 0x03,
+       0x39, 3, 0xB0, 0x20, 0x00,
+       0x39, 3, 0xB1, 0x20, 0x00,
+       0x39, 6, 0xB2, 0x05, 0x00, 0x42, 0x00, 0x00,
+       0x39, 6, 0xB6, 0x05, 0x00, 0x42, 0x00, 0x00,
+       0x39, 6, 0xBA, 0x53, 0x00, 0x42, 0x00, 0x00,
+       0x39, 6, 0xBB, 0x53, 0x00, 0x42, 0x00, 0x00,
+       0x15, 2, 0xC4, 0x40,
+
+       /* ===page 5==== */
+       0x39, 6, 0xF0, 0x55, 0xAA, 0x52, 0x08, 0x05,
+       0x39, 3, 0xB0, 0x17, 0x06,
+       0x15, 2, 0xB8, 0x00,
+       0x39, 6, 0xBD, 0x03, 0x01, 0x01, 0x00, 0x01,
+       0x39, 3, 0xB1, 0x17, 0x06,
+       0x39, 3, 0xB9, 0x00, 0x01,
+       0x39, 3, 0xB2, 0x17, 0x06,
+       0x39, 3, 0xBA, 0x00, 0x01,
+       0x39, 3, 0xB3, 0x17, 0x06,
+       0x39, 3, 0xBB, 0x0A, 0x00,
+       0x39, 3, 0xB4, 0x17, 0x06,
+       0x39, 3, 0xB5, 0x17, 0x06,
+       0x39, 3, 0xB6, 0x14, 0x03,
+       0x39, 3, 0xB7, 0x00, 0x00,
+       0x39, 3, 0xBC, 0x02, 0x01,
+       0x15, 2, 0xC0, 0x05,
+       0x15, 2, 0xC4, 0xA5,
+       0x39, 3, 0xC8, 0x03, 0x30,
+       0x39, 3, 0xC9, 0x03, 0x51,
+       0x39, 6, 0xD1, 0x00, 0x05, 0x03, 0x00, 0x00,
+       0x39, 6, 0xD2, 0x00, 0x05, 0x09, 0x00, 0x00,
+       0x15, 2, 0xE5, 0x02,
+       0x15, 2, 0xE6, 0x02,
+       0x15, 2, 0xE7, 0x02,
+       0x15, 2, 0xE9, 0x02,
+       0x15, 2, 0xED, 0x33,
+
+       0x05, 1, 0x11, /* sleep out */
+       0xff, 30,      /* delay 30ms */
+       0x05, 1, 0x29, /* display on */
+       0xff, 30,      /* delay 30ms */
+       0xff, 0xff,    /* ending flag */
+};
+
+static unsigned char mipi_init_off_table[] = {
+       0x05, 1, 0x28, /* display off */
+       0xff, 10,      /* delay 10ms */
+       0x05, 1, 0x10, /* sleep in */
+       0xff, 10,      /* delay 10ms */
+       0xff, 0xff,    /* ending flag */
+};
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       int ret = 0;
+
+       if (ext_drv) {
+               ext_drv->config.table_init_on  = &mipi_init_on_table[0];
+               ext_drv->config.table_init_off = &mipi_init_off_table[0];
+       } else {
+               EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+               ret = -1;
+       }
+
+       return ret;
+}
+
+int aml_lcd_extern_mipi_N070ICN_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       int ret = 0;
+
+       ret = lcd_extern_driver_update(ext_drv);
+
+       if (lcd_debug_print_flag)
+               EXTPR("%s: %d\n", __func__, ret);
+       return ret;
+}
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_extern/spi_LD070WS2.c b/drivers/amlogic/media/vout/lcd/lcd_extern/spi_LD070WS2.c
new file mode 100644 (file)
index 0000000..7c2678c
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/spi_LD070WS2.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/sysctl.h>
+#include <linux/uaccess.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+
+static struct lcd_extern_config_s *ext_config;
+
+#define LCD_EXTERN_NAME                "lcd_spi_LD070WS2"
+
+#define SPI_DELAY              30 /* unit: us */
+
+#define LCD_EXTERN_CMD_SIZE        4
+static unsigned char init_on_table[] = {
+       0x00, 0x00, 0x21, 0x00, /* reset */
+       0x00, 0x00, 0xa5, 0x00, /* standby */
+       0x00, 0x01, 0x30, 0x00, /* enable FRC/Dither */
+       0x00, 0x02, 0x40, 0x00, /* enable normally black */
+       0x00, 0x0e, 0x5f, 0x00, /* enable test mode1 */
+       0x00, 0x0f, 0xa4, 0x00, /* enable test mode2 */
+       0x00, 0x0d, 0x00, 0x00, /* enable SDRRS, enlarge OE width */
+       0x00, 0x02, 0x43, 0x00, /* adjust charge sharing time */
+       0x00, 0x0a, 0x28, 0x00, /* trigger bias reduction */
+       0x00, 0x10, 0x41, 50,   /* adopt 2 line/1 dot */ /* delay 50ms */
+       0x00, 0x00, 0xad, 0x00, /* display on */
+       0xff, 0x00, 0x00, 0x00, /* ending flag */
+};
+
+static unsigned char init_off_table[] = {
+       0x00, 0x00, 0xa5, 0x00, /* standby */
+       0xff, 0x00, 0x00, 0x00, /* ending flag */
+};
+
+static void set_lcd_csb(unsigned int v)
+{
+       lcd_extern_gpio_set(ext_config->spi_cs, v);
+       udelay(SPI_DELAY);
+}
+
+static void set_lcd_scl(unsigned int v)
+{
+       lcd_extern_gpio_set(ext_config->spi_clk, v);
+       udelay(SPI_DELAY);
+}
+
+static void set_lcd_sda(unsigned int v)
+{
+       lcd_extern_gpio_set(ext_config->spi_data, v);
+       udelay(SPI_DELAY);
+}
+
+static void spi_gpio_init(void)
+{
+       set_lcd_csb(1);
+       set_lcd_scl(1);
+       set_lcd_sda(1);
+}
+
+static void spi_gpio_off(void)
+{
+       set_lcd_sda(0);
+       set_lcd_scl(0);
+       set_lcd_csb(0);
+}
+
+static void spi_write_8(unsigned char addr, unsigned char data)
+{
+       int i;
+       unsigned int sdata;
+
+       sdata = (unsigned int)(addr & 0x3f);
+       sdata <<= 10;
+       sdata |= (data & 0xff);
+       sdata &= ~(1<<9); /* write flag */
+
+       set_lcd_csb(1);
+       set_lcd_scl(1);
+       set_lcd_sda(1);
+
+       set_lcd_csb(0);
+       for (i = 0; i < 16; i++) {
+               set_lcd_scl(0);
+               if (sdata & 0x8000)
+                       set_lcd_sda(1);
+               else
+                       set_lcd_sda(0);
+               sdata <<= 1;
+               set_lcd_scl(1);
+       }
+
+       set_lcd_csb(1);
+       set_lcd_scl(1);
+       set_lcd_sda(1);
+       udelay(SPI_DELAY);
+}
+
+static int lcd_extern_spi_write(unsigned char *buf, int len)
+{
+       if (len != 2) {
+               EXTERR("%s: len %d error\n", __func__, len);
+               return -1;
+       }
+       spi_write_8(buf[0], buf[1]);
+       return 0;
+}
+
+static int lcd_extern_power_cmd(unsigned char *init_table)
+{
+       int i = 0, len;
+       int ret = 0;
+
+       len = ext_config->cmd_size;
+       if (len < 1) {
+               EXTERR("%s: cmd_size %d is invalid\n", __func__, len);
+               return -1;
+       }
+
+       while (i <= LCD_EXTERN_INIT_TABLE_MAX) {
+               if (init_table[i] == LCD_EXTERN_INIT_END) {
+                       break;
+               } else if (init_table[i] == LCD_EXTERN_INIT_NONE) {
+                       /* do nothing, only for delay */
+               } else if (init_table[i] == LCD_EXTERN_INIT_GPIO) {
+                       if (init_table[i+1] < LCD_GPIO_MAX) {
+                               lcd_extern_gpio_set(init_table[i+1],
+                                       init_table[i+2]);
+                       }
+               } else if (init_table[i] == LCD_EXTERN_INIT_CMD) {
+                       ret = lcd_extern_spi_write(&init_table[i+1], (len-2));
+               } else {
+                       EXTERR("%s(%d: %s): power_type %d is invalid\n",
+                               __func__, ext_config->index,
+                               ext_config->name, ext_config->type);
+               }
+               if (init_table[i+len-1] > 0)
+                       mdelay(init_table[i+len-1]);
+               i += len;
+       }
+
+       return ret;
+}
+
+static int lcd_extern_power_ctrl(int flag)
+{
+       int ret = 0;
+
+       spi_gpio_init();
+       if (flag)
+               ret = lcd_extern_power_cmd(ext_config->table_init_on);
+       else
+               ret = lcd_extern_power_cmd(ext_config->table_init_off);
+       mdelay(10);
+       spi_gpio_off();
+
+       EXTPR("%s(%d: %s): %d\n",
+               __func__, ext_config->index, ext_config->name, flag);
+       return ret;
+}
+
+static int lcd_extern_power_on(void)
+{
+       int ret;
+
+       ret = lcd_extern_power_ctrl(1);
+       return ret;
+}
+
+static int lcd_extern_power_off(void)
+{
+       int ret;
+
+       ret = lcd_extern_power_ctrl(0);
+       return ret;
+}
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       if (ext_drv == NULL) {
+               EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+               return -1;
+       }
+
+       if (ext_drv->config.table_init_loaded == 0) {
+               ext_drv->config.table_init_on  = init_on_table;
+               ext_drv->config.table_init_off = init_off_table;
+       }
+       ext_drv->power_on  = lcd_extern_power_on;
+       ext_drv->power_off = lcd_extern_power_off;
+
+       return 0;
+}
+
+int aml_lcd_extern_spi_LD070WS2_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+       int ret = 0;
+
+       ext_config = &ext_drv->config;
+       ret = lcd_extern_driver_update(ext_drv);
+
+       if (lcd_debug_print_flag)
+               EXTPR("%s: %d\n", __func__, ret);
+       return ret;
+}
diff --git a/drivers/amlogic/media/vout/lcd/lcd_notify.c b/drivers/amlogic/media/vout/lcd/lcd_notify.c
new file mode 100644 (file)
index 0000000..23d7f05
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_notify.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+
+static BLOCKING_NOTIFIER_HEAD(lcd_notifier_list);
+
+/**
+ * aml_lcd_notifier_register - register a client notifier
+ * @nb: notifier block to callback on events
+ */
+int aml_lcd_notifier_register(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&lcd_notifier_list, nb);
+}
+EXPORT_SYMBOL(aml_lcd_notifier_register);
+
+/**
+ * aml_lcd_notifier_unregister - unregister a client notifier
+ * @nb: notifier block to callback on events
+ */
+int aml_lcd_notifier_unregister(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_unregister(&lcd_notifier_list, nb);
+}
+EXPORT_SYMBOL(aml_lcd_notifier_unregister);
+
+/**
+ * aml_lcd_notifier_call_chain - notify clients of lcd events
+ *
+ */
+int aml_lcd_notifier_call_chain(unsigned long event, void *v)
+{
+       return blocking_notifier_call_chain(&lcd_notifier_list, event, v);
+}
+EXPORT_SYMBOL_GPL(aml_lcd_notifier_call_chain);
diff --git a/drivers/amlogic/media/vout/lcd/lcd_reg.c b/drivers/amlogic/media/vout/lcd/lcd_reg.c
new file mode 100644 (file)
index 0000000..dc87e56
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_reg.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/amlogic/cpu_version.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include "lcd_common.h"
+#include "lcd_reg.h"
+
+/* ********************************************* */
+#define LCD_REG_GLOBAL_API     0
+#define LCD_REG_IOREMAP        1
+#define LCD_REG_IF             LCD_REG_IOREMAP
+/* ********************************************* */
+
+#if (LCD_REG_IF == LCD_REG_IOREMAP)
+struct reg_map_s {
+       unsigned int base_addr;
+       unsigned int size;
+       void __iomem *p;
+       char flag;
+       char dummy;
+};
+static struct reg_map_s *lcd_map;
+static int lcd_map_num;
+
+#define LCD_MAP_HIUBUS    0
+#define LCD_MAP_VCBUS     1
+#define LCD_MAP_PERIPHS   2
+#define LCD_MAP_CBUS      3
+#define LCD_MAP_DSI_HOST  4
+#define LCD_MAP_DSI_PHY   5
+
+static struct reg_map_s lcd_reg_maps_gxb[] = {
+       { /* HIU */
+               .base_addr = 0xc883c000,
+               .size = 0x400,
+               .flag = 0,
+               .dummy = 0,
+       },
+       { /* VCBUS */
+               .base_addr = 0xd0100000,
+               .size = 0x10000,
+               .flag = 0,
+               .dummy = 0,
+       },
+       { /* PERIPHS */
+               .base_addr = 0xc8834400,
+               .size = 0x100,
+               .flag = 0,
+               .dummy = 0,
+       },
+       { /* CBUS */
+               .base_addr = 0xc1100000,
+               .size = 0x8000,
+               .flag = 0,
+               .dummy = 0,
+       },
+};
+
+static struct reg_map_s lcd_reg_maps_txlx[] = {
+       { /* HIU */
+               .base_addr = 0xff63c000,
+               .size = 0x400,
+               .flag = 0,
+               .dummy = 0,
+       },
+       { /* VCBUS */
+               .base_addr = 0xff900000,
+               .size = 0xa000,
+               .flag = 0,
+               .dummy = 0,
+       },
+       { /* PERIPHS */
+               .base_addr = 0xff634000,
+               .size = 0x100,
+               .flag = 0,
+               .dummy = 0,
+       },
+};
+
+static struct reg_map_s lcd_reg_maps_axg[] = {
+       { /* HIU */
+               .base_addr = 0xff63c000,
+               .size = 0x400,
+               .flag = 0,
+               .dummy = 0,
+       },
+       { /* VCBUS */
+               .base_addr = 0xff900000,
+               .size = 0xa000,
+               .flag = 0,
+               .dummy = 0,
+       },
+       { /* PERIPHS */
+               .base_addr = 0xff634000,
+               .size = 0x100,
+               .flag = 0,
+               .dummy = 0,
+       },
+       { /* CBUS, dummy */
+               .base_addr = 0xffd00000,
+               .size = 0x10,
+               .flag = 0,
+               .dummy = 1,
+       },
+       { /* mipi_dsi_host */
+               .base_addr = 0xffd00000, /* 0xffd06000 */
+               .size = 0x6400,
+               .flag = 0,
+               .dummy = 0,
+       },
+       { /* mipi_dsi_phy */
+               .base_addr = 0xff640000,
+               .size = 0x100,
+               .flag = 0,
+               .dummy = 0,
+       },
+};
+
+int lcd_ioremap(void)
+{
+       int i;
+       int ret = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       lcd_map = NULL;
+       lcd_map_num = 0;
+
+       switch (lcd_drv->chip_type) {
+       case LCD_CHIP_TXLX:
+               lcd_map = lcd_reg_maps_txlx;
+               lcd_map_num = ARRAY_SIZE(lcd_reg_maps_txlx);
+               break;
+       case LCD_CHIP_AXG:
+               lcd_map = lcd_reg_maps_axg;
+               lcd_map_num = ARRAY_SIZE(lcd_reg_maps_axg);
+               break;
+       default:
+               lcd_map = lcd_reg_maps_gxb;
+               lcd_map_num = ARRAY_SIZE(lcd_reg_maps_gxb);
+               break;
+       }
+
+       for (i = 0; i < lcd_map_num; i++) {
+               if (lcd_map[i].dummy)
+                       continue;
+               lcd_map[i].p = ioremap(lcd_map[i].base_addr,
+                                       lcd_map[i].size);
+               if (lcd_map[i].p == NULL) {
+                       lcd_map[i].flag = 0;
+                       LCDPR("reg map failed: 0x%x\n",
+                               lcd_map[i].base_addr);
+                       ret = -1;
+               } else {
+                       lcd_map[i].flag = 1;
+                       if (lcd_debug_print_flag) {
+                               LCDPR("reg mapped: 0x%x -> %p\n",
+                                       lcd_map[i].base_addr, lcd_map[i].p);
+                       }
+               }
+       }
+       return ret;
+}
+
+static int check_lcd_ioremap(int n)
+{
+       if (lcd_map == NULL)
+               return -1;
+       if (n >= lcd_map_num)
+               return -1;
+
+       if (lcd_map[n].dummy) {
+               LCDERR("reg 0x%x is invalid(dummy)\n", lcd_map[n].base_addr);
+               return -1;
+       }
+       if (lcd_map[n].flag == 0) {
+               LCDERR("reg 0x%x mapped error\n", lcd_map[n].base_addr);
+               return -1;
+       }
+       return 0;
+}
+#else
+int lcd_ioremap(void)
+{
+       LCDPR("reg interface is global api\n");
+       return 0;
+}
+#endif
+
+/* register mapping check */
+#if (LCD_REG_IF == LCD_REG_IOREMAP)
+static inline void __iomem *check_lcd_vcbus_reg(unsigned int _reg)
+{
+       void __iomem *p;
+       int reg_bus;
+       unsigned int reg_offset;
+
+       reg_bus = LCD_MAP_VCBUS;
+       if (check_lcd_ioremap(reg_bus))
+               return NULL;
+
+       reg_offset = LCD_REG_OFFSET_VCBUS(_reg);
+       if (reg_offset >= lcd_map[reg_bus].size) {
+               LCDERR("invalid vcbus reg offset: 0x%04x\n", _reg);
+               return NULL;
+       }
+       p = lcd_map[reg_bus].p + reg_offset;
+       return p;
+}
+
+static inline void __iomem *check_lcd_hiu_reg(unsigned int _reg)
+{
+       void __iomem *p;
+       int reg_bus;
+       unsigned int reg_offset;
+
+       if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB)
+               reg_bus = LCD_MAP_HIUBUS;
+       else
+               reg_bus = LCD_MAP_CBUS;
+       if (check_lcd_ioremap(reg_bus))
+               return NULL;
+
+       if (reg_bus == LCD_MAP_HIUBUS)
+               reg_offset = LCD_REG_OFFSET_HIU(_reg);
+       else
+               reg_offset = LCD_REG_OFFSET_CBUS(_reg);
+       if (reg_offset >= lcd_map[reg_bus].size) {
+               LCDERR("invalid hiu reg offset: 0x%04x\n", _reg);
+               return NULL;
+       }
+       p = lcd_map[reg_bus].p + reg_offset;
+
+       return p;
+}
+
+static inline void __iomem *check_lcd_cbus_reg(unsigned int _reg)
+{
+       void __iomem *p;
+       int reg_bus;
+       unsigned int reg_offset;
+
+       reg_bus = LCD_MAP_CBUS;
+       if (check_lcd_ioremap(reg_bus))
+               return NULL;
+
+       reg_offset = LCD_REG_OFFSET_CBUS(_reg);
+       if (reg_offset >= lcd_map[reg_bus].size) {
+               LCDERR("invalid cbus reg offset: 0x%04x\n", _reg);
+               return NULL;
+       }
+       p = lcd_map[reg_bus].p + reg_offset;
+       return p;
+}
+
+static inline void __iomem *check_lcd_periphs_reg(unsigned int _reg)
+{
+       void __iomem *p;
+       int reg_bus;
+       unsigned int reg_offset;
+
+       if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB)
+               reg_bus = LCD_MAP_PERIPHS;
+       else
+               reg_bus = LCD_MAP_CBUS;
+       if (check_lcd_ioremap(reg_bus))
+               return NULL;
+
+       if (reg_bus == LCD_MAP_PERIPHS)
+               reg_offset = LCD_REG_OFFSET_PERIPHS(_reg);
+       else
+               reg_offset = LCD_REG_OFFSET_PERIPHS(_reg);
+       if (reg_offset >= lcd_map[reg_bus].size) {
+               LCDERR("invalid periphs reg offset: 0x%04x\n", _reg);
+               return NULL;
+       }
+       p = lcd_map[reg_bus].p + reg_offset;
+       return p;
+}
+
+static inline void __iomem *check_lcd_dsi_host_reg(unsigned int _reg)
+{
+       void __iomem *p;
+       int reg_bus;
+       unsigned int reg_offset;
+
+       reg_bus = LCD_MAP_DSI_HOST;
+       if (check_lcd_ioremap(reg_bus))
+               return NULL;
+
+       reg_offset = LCD_REG_OFFSET_DSI_HOST(_reg);
+       if (reg_offset >= lcd_map[reg_bus].size) {
+               LCDERR("invalid dsi_host reg offset: 0x%04x\n", _reg);
+               return NULL;
+       }
+       p = lcd_map[reg_bus].p + reg_offset;
+       return p;
+}
+
+static inline void __iomem *check_lcd_dsi_phy_reg(unsigned int _reg)
+{
+       void __iomem *p;
+       int reg_bus;
+       unsigned int reg_offset;
+
+       reg_bus = LCD_MAP_DSI_PHY;
+       if (check_lcd_ioremap(reg_bus))
+               return NULL;
+
+       reg_offset = LCD_REG_OFFSET_DSI_PHY(_reg);
+       if (reg_offset >= lcd_map[reg_bus].size) {
+               LCDERR("invalid dsi_phy reg offset: 0x%04x\n", _reg);
+               return NULL;
+       }
+       p = lcd_map[reg_bus].p + reg_offset;
+       return p;
+}
+#endif
+
+/* register access api */
+#if (LCD_REG_IF == LCD_REG_IOREMAP)
+unsigned int lcd_vcbus_read(unsigned int _reg)
+{
+       void __iomem *p;
+
+       p = check_lcd_vcbus_reg(_reg);
+       if (p)
+               return readl(p);
+       else
+               return -1;
+};
+
+void lcd_vcbus_write(unsigned int _reg, unsigned int _value)
+{
+       void __iomem *p;
+
+       p = check_lcd_vcbus_reg(_reg);
+       if (p)
+               writel(_value, p);
+};
+#else
+unsigned int lcd_vcbus_read(unsigned int reg)
+{
+       return aml_read_vcbus(reg);
+};
+
+void lcd_vcbus_write(unsigned int reg, unsigned int value)
+{
+       aml_write_vcbus(reg, value);
+};
+#endif
+
+void lcd_vcbus_setb(unsigned int reg, unsigned int value,
+               unsigned int _start, unsigned int _len)
+{
+       lcd_vcbus_write(reg, ((lcd_vcbus_read(reg) &
+               (~(((1L << _len)-1) << _start))) |
+               ((value & ((1L << _len)-1)) << _start)));
+}
+
+unsigned int lcd_vcbus_getb(unsigned int reg,
+               unsigned int _start, unsigned int _len)
+{
+       return (lcd_vcbus_read(reg) >> _start) & ((1L << _len)-1);
+}
+
+void lcd_vcbus_set_mask(unsigned int reg, unsigned int _mask)
+{
+       lcd_vcbus_write(reg, (lcd_vcbus_read(reg) | (_mask)));
+}
+
+void lcd_vcbus_clr_mask(unsigned int reg, unsigned int _mask)
+{
+       lcd_vcbus_write(reg, (lcd_vcbus_read(reg) & (~(_mask))));
+}
+
+#if (LCD_REG_IF == LCD_REG_IOREMAP)
+unsigned int lcd_hiu_read(unsigned int _reg)
+{
+       void __iomem *p;
+
+       p = check_lcd_hiu_reg(_reg);
+       if (p)
+               return readl(p);
+       else
+               return -1;
+};
+
+void lcd_hiu_write(unsigned int _reg, unsigned int _value)
+{
+       void __iomem *p;
+
+       p = check_lcd_hiu_reg(_reg);
+       if (p)
+               writel(_value, p);
+};
+#else
+unsigned int lcd_hiu_read(unsigned int _reg)
+{
+       return aml_read_cbus(_reg);
+};
+
+void lcd_hiu_write(unsigned int _reg, unsigned int _value)
+{
+       aml_write_cbus(_reg, _value);
+};
+#endif
+
+void lcd_hiu_setb(unsigned int _reg, unsigned int _value,
+               unsigned int _start, unsigned int _len)
+{
+       lcd_hiu_write(_reg, ((lcd_hiu_read(_reg) &
+                       ~(((1L << (_len))-1) << (_start))) |
+                       (((_value)&((1L<<(_len))-1)) << (_start))));
+}
+
+unsigned int lcd_hiu_getb(unsigned int _reg,
+               unsigned int _start, unsigned int _len)
+{
+       return (lcd_hiu_read(_reg) >> (_start)) & ((1L << (_len)) - 1);
+}
+
+void lcd_hiu_set_mask(unsigned int _reg, unsigned int _mask)
+{
+       lcd_hiu_write(_reg, (lcd_hiu_read(_reg) | (_mask)));
+}
+
+void lcd_hiu_clr_mask(unsigned int _reg, unsigned int _mask)
+{
+       lcd_hiu_write(_reg, (lcd_hiu_read(_reg) & (~(_mask))));
+}
+
+#if (LCD_REG_IF == LCD_REG_IOREMAP)
+unsigned int lcd_cbus_read(unsigned int _reg)
+{
+       void __iomem *p;
+
+       p = check_lcd_cbus_reg(_reg);
+       if (p)
+               return readl(p);
+       else
+               return -1;
+};
+
+void lcd_cbus_write(unsigned int _reg, unsigned int _value)
+{
+       void __iomem *p;
+
+       p = check_lcd_cbus_reg(_reg);
+       if (p)
+               writel(_value, p);
+};
+#else
+unsigned int lcd_cbus_read(unsigned int _reg)
+{
+       return aml_read_cbus(_reg);
+};
+
+void lcd_cbus_write(unsigned int _reg, unsigned int _value)
+{
+       aml_write_cbus(_reg, _value);
+};
+#endif
+
+void lcd_cbus_setb(unsigned int _reg, unsigned int _value,
+               unsigned int _start, unsigned int _len)
+{
+       lcd_cbus_write(_reg, ((lcd_cbus_read(_reg) &
+                       ~(((1L << (_len))-1) << (_start))) |
+                       (((_value)&((1L<<(_len))-1)) << (_start))));
+}
+
+#if (LCD_REG_IF == LCD_REG_IOREMAP)
+unsigned int lcd_periphs_read(unsigned int _reg)
+{
+       void __iomem *p;
+
+       p = check_lcd_periphs_reg(_reg);
+       if (p)
+               return readl(p);
+       else
+               return -1;
+};
+
+void lcd_periphs_write(unsigned int _reg, unsigned int _value)
+{
+       void __iomem *p;
+
+       p = check_lcd_periphs_reg(_reg);
+       if (p)
+               writel(_value, p);
+};
+#else
+unsigned int lcd_periphs_read(unsigned int _reg)
+{
+       return aml_read_cbus(_reg);
+};
+
+void lcd_periphs_write(unsigned int _reg, unsigned int _value)
+{
+       aml_write_cbus(_reg, _value);
+};
+#endif
+
+void lcd_pinmux_set_mask(unsigned int n, unsigned int _mask)
+{
+       unsigned int _reg = PERIPHS_PIN_MUX_0;
+
+       _reg += n;
+       lcd_periphs_write(_reg, (lcd_periphs_read(_reg) | (_mask)));
+}
+
+void lcd_pinmux_clr_mask(unsigned int n, unsigned int _mask)
+{
+       unsigned int _reg = PERIPHS_PIN_MUX_0;
+
+       _reg += n;
+       lcd_periphs_write(_reg, (lcd_periphs_read(_reg) & (~(_mask))));
+}
+
+#if (LCD_REG_IF == LCD_REG_IOREMAP)
+unsigned int dsi_host_read(unsigned int _reg)
+{
+       void __iomem *p;
+
+       p = check_lcd_dsi_host_reg(_reg);
+       if (p)
+               return readl(p);
+       else
+               return -1;
+};
+
+void dsi_host_write(unsigned int _reg, unsigned int _value)
+{
+       void __iomem *p;
+
+       p = check_lcd_dsi_host_reg(_reg);
+       if (p)
+               writel(_value, p);
+};
+
+void dsi_host_setb(unsigned int reg, unsigned int value,
+               unsigned int _start, unsigned int _len)
+{
+       dsi_host_write(reg, ((dsi_host_read(reg) &
+               (~(((1L << _len)-1) << _start))) |
+               ((value & ((1L << _len)-1)) << _start)));
+}
+
+unsigned int dsi_host_getb(unsigned int reg,
+               unsigned int _start, unsigned int _len)
+{
+       return (dsi_host_read(reg) >> _start) & ((1L << _len)-1);
+}
+
+void dsi_host_set_mask(unsigned int reg, unsigned int _mask)
+{
+       dsi_host_write(reg, (dsi_host_read(reg) | (_mask)));
+}
+
+void dsi_host_clr_mask(unsigned int reg, unsigned int _mask)
+{
+       dsi_host_write(reg, (dsi_host_read(reg) & (~(_mask))));
+}
+
+
+unsigned int dsi_phy_read(unsigned int _reg)
+{
+       void __iomem *p;
+
+       p = check_lcd_dsi_phy_reg(_reg);
+       if (p)
+               return readl(p);
+       else
+               return -1;
+};
+
+void dsi_phy_write(unsigned int _reg, unsigned int _value)
+{
+       void __iomem *p;
+
+       p = check_lcd_dsi_phy_reg(_reg);
+       if (p)
+               writel(_value, p);
+};
+
+void dsi_phy_setb(unsigned int reg, unsigned int value,
+               unsigned int _start, unsigned int _len)
+{
+       dsi_phy_write(reg, ((dsi_phy_read(reg) &
+               (~(((1L << _len)-1) << _start))) |
+               ((value & ((1L << _len)-1)) << _start)));
+}
+
+unsigned int dsi_phy_getb(unsigned int reg,
+               unsigned int _start, unsigned int _len)
+{
+       return (dsi_phy_read(reg) >> _start) & ((1L << _len)-1);
+}
+
+void dsi_phy_set_mask(unsigned int reg, unsigned int _mask)
+{
+       dsi_phy_write(reg, (dsi_phy_read(reg) | (_mask)));
+}
+
+void dsi_phy_clr_mask(unsigned int reg, unsigned int _mask)
+{
+       dsi_phy_write(reg, (dsi_phy_read(reg) & (~(_mask))));
+}
+#endif
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_reg.h b/drivers/amlogic/media/vout/lcd/lcd_reg.h
new file mode 100644 (file)
index 0000000..f7c4f09
--- /dev/null
@@ -0,0 +1,1483 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_reg.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __LCD_REG_H__
+#define __LCD_REG_H__
+#include <linux/amlogic/iomap.h>
+
+/* register offset address define */
+/* base & offset */
+#if 0
+#define LCD_REG_BASE_PERIPHS                      (0xc8834400L)
+#define LCD_REG_BASE_CBUS                         (0xc1100000L)
+#define LCD_REG_BASE_HIU                          (0xc883c000L)
+#define LCD_REG_BASE_VCBUS                        (0xd0100000L)
+#endif
+#define LCD_HIU_REG(reg)                          (reg & 0xff)
+#define LCD_PERIPHS_REG(reg)                      (reg & 0xff)
+
+#define LCD_REG_OFFSET_PERIPHS(reg)               ((LCD_PERIPHS_REG(reg) << 2))
+#define LCD_REG_OFFSET_CBUS(reg)                  ((reg << 2))
+#define LCD_REG_OFFSET_HIU(reg)                   ((LCD_HIU_REG(reg) << 2))
+#define LCD_REG_OFFSET_VCBUS(reg)                 ((reg << 2))
+#define LCD_REG_OFFSET_DSI_HOST(reg)              ((reg << 2))
+#define LCD_REG_OFFSET_DSI_PHY(reg)               ((reg << 2))
+
+
+/* PERIPHS: 0xc8834400 */
+#define PREG_PAD_GPIO1_EN_N                        0x0f
+#define PREG_PAD_GPIO1_O                           0x10
+#define PREG_PAD_GPIO1_I                           0x11
+#define PREG_PAD_GPIO2_EN_N                        0x12
+#define PREG_PAD_GPIO2_O                           0x13
+#define PREG_PAD_GPIO2_I                           0x14
+#define PREG_PAD_GPIO3_EN_N                        0x15
+#define PREG_PAD_GPIO3_O                           0x16
+#define PREG_PAD_GPIO3_I                           0x17
+#define PREG_PAD_GPIO4_EN_N                        0x18
+#define PREG_PAD_GPIO4_O                           0x19
+#define PREG_PAD_GPIO4_I                           0x1a
+#define PREG_PAD_GPIO5_EN_N                        0x1b
+#define PREG_PAD_GPIO5_O                           0x1c
+#define PREG_PAD_GPIO5_I                           0x1d
+
+#define PERIPHS_PIN_MUX_0                          0x2c
+#define PERIPHS_PIN_MUX_1                          0x2d
+#define PERIPHS_PIN_MUX_2                          0x2e
+#define PERIPHS_PIN_MUX_3                          0x2f
+#define PERIPHS_PIN_MUX_4                          0x30
+#define PERIPHS_PIN_MUX_5                          0x31
+#define PERIPHS_PIN_MUX_6                          0x32
+#define PERIPHS_PIN_MUX_7                          0x33
+#define PERIPHS_PIN_MUX_8                          0x34
+#define PERIPHS_PIN_MUX_9                          0x35
+#define PERIPHS_PIN_MUX_10                         0x36
+#define PERIPHS_PIN_MUX_11                         0x37
+#define PERIPHS_PIN_MUX_12                         0x38
+
+
+/* HIU:  HHI_CBUS_BASE = 0x10 */
+#define HHI_GCLK_MPEG0                             0x1050
+#define HHI_GCLK_MPEG1                             0x1051
+#define HHI_GCLK_MPEG2                             0x1052
+#define HHI_GCLK_OTHER                             0x1054
+
+#define HHI_VIID_PLL_CNTL4                         0x1046
+#define HHI_VIID_PLL_CNTL                          0x1047
+#define HHI_VIID_PLL_CNTL2                         0x1048
+#define HHI_VIID_PLL_CNTL3                         0x1049
+#define HHI_VIID_CLK_DIV                           0x104a
+    #define DAC0_CLK_SEL           28
+    #define DAC1_CLK_SEL           24
+    #define DAC2_CLK_SEL           20
+    #define VCLK2_XD_RST           17
+    #define VCLK2_XD_EN            16
+    #define ENCL_CLK_SEL           12
+    #define VCLK2_XD                0
+#define HHI_VIID_CLK_CNTL                          0x104b
+    #define VCLK2_EN               19
+    #define VCLK2_CLK_IN_SEL       16
+    #define VCLK2_SOFT_RST         15
+    #define VCLK2_DIV12_EN          4
+    #define VCLK2_DIV6_EN           3
+    #define VCLK2_DIV4_EN           2
+    #define VCLK2_DIV2_EN           1
+    #define VCLK2_DIV1_EN           0
+#define HHI_VIID_DIVIDER_CNTL                      0x104c
+    #define DIV_CLK_IN_EN          16
+    #define DIV_CLK_SEL            15
+    #define DIV_POST_TCNT          12
+    #define DIV_LVDS_CLK_EN        11
+    #define DIV_LVDS_DIV2          10
+    #define DIV_POST_SEL            8
+    #define DIV_POST_SOFT_RST       7
+    #define DIV_PRE_SEL             4
+    #define DIV_PRE_SOFT_RST        3
+    #define DIV_POST_RST            1
+    #define DIV_PRE_RST             0
+#define HHI_VID_CLK_DIV                            0x1059
+    #define ENCI_CLK_SEL           28
+    #define ENCP_CLK_SEL           24
+    #define ENCT_CLK_SEL           20
+    #define VCLK_XD_RST            17
+    #define VCLK_XD_EN             16
+    #define ENCL_CLK_SEL           12
+    #define VCLK_XD1                8
+    #define VCLK_XD0                0
+#define HHI_VID_CLK_CNTL                           0x105f
+#define HHI_VID_CLK_CNTL2                          0x1065
+    #define HDMI_TX_PIXEL_GATE_VCLK  5
+    #define VDAC_GATE_VCLK           4
+    #define ENCL_GATE_VCLK           3
+    #define ENCP_GATE_VCLK           2
+    #define ENCT_GATE_VCLK           1
+    #define ENCI_GATE_VCLK           0
+#define HHI_VID_DIVIDER_CNTL                       0x1066
+#define HHI_VID_PLL_CLK_DIV                        0x1068
+#define HHI_EDP_APB_CLK_CNTL                       0x107b
+#define HHI_EDP_APB_CLK_CNTL_M8M2                  0x1082
+#define HHI_EDP_TX_PHY_CNTL0                       0x109c
+#define HHI_EDP_TX_PHY_CNTL1                       0x109d
+/* m8b */
+#define HHI_VID_PLL_CNTL                           0x10c8
+#define HHI_VID_PLL_CNTL2                          0x10c9
+#define HHI_VID_PLL_CNTL3                          0x10ca
+#define HHI_VID_PLL_CNTL4                          0x10cb
+#define HHI_VID_PLL_CNTL5                          0x10cc
+#define HHI_VID_PLL_CNTL6                          0x10cd
+/* g9tv */
+#define HHI_HDMI_PLL_CNTL                          0x10c8
+#define HHI_HDMI_PLL_CNTL2                         0x10c9
+#define HHI_HDMI_PLL_CNTL3                         0x10ca
+#define HHI_HDMI_PLL_CNTL4                         0x10cb
+#define HHI_HDMI_PLL_CNTL5                         0x10cc
+#define HHI_HDMI_PLL_CNTL6                         0x10cd
+
+#define HHI_DSI_LVDS_EDP_CNTL0                     0x10d1
+#define HHI_DSI_LVDS_EDP_CNTL1                     0x10d2
+#define HHI_DIF_CSI_PHY_CNTL0                      0x10d8
+#define HHI_DIF_CSI_PHY_CNTL1                      0x10d9
+#define HHI_DIF_CSI_PHY_CNTL2                      0x10da
+#define HHI_DIF_CSI_PHY_CNTL3                      0x10db
+#define HHI_DIF_CSI_PHY_CNTL4                      0x10dc
+#define HHI_DIF_CSI_PHY_CNTL5                      0x10dd
+#define HHI_LVDS_TX_PHY_CNTL0                      0x10de
+#define HHI_LVDS_TX_PHY_CNTL1                      0x10df
+#define HHI_VID2_PLL_CNTL                          0x10e0
+#define HHI_VID2_PLL_CNTL2                         0x10e1
+#define HHI_VID2_PLL_CNTL3                         0x10e2
+#define HHI_VID2_PLL_CNTL4                         0x10e3
+#define HHI_VID2_PLL_CNTL5                         0x10e4
+#define HHI_VID2_PLL_CNTL6                         0x10e5
+#define HHI_VID_LOCK_CLK_CNTL                      0x10f2
+
+/* AXG use PLL   0xff63c000 */
+#define HHI_GP0_PLL_CNTL                           0x010
+#define HHI_GP0_PLL_CNTL2                          0x011
+#define HHI_GP0_PLL_CNTL3                          0x012
+#define HHI_GP0_PLL_CNTL4                          0x013
+#define HHI_GP0_PLL_CNTL5                          0x014
+#define HHI_GP0_PLL_CNTL1                          0x016
+
+#define        HHI_MIPI_CNTL0                             0x000
+#define        HHI_MIPI_CNTL1                             0x001
+#define        HHI_MIPI_CNTL2                             0x002
+
+/*  Global control:  RESET_CBUS_BASE = 0x11 */
+#define VERSION_CTRL                               0x1100
+#define RESET0_REGISTER                            0x1101
+#define RESET1_REGISTER                            0x1102
+#define RESET2_REGISTER                            0x1103
+#define RESET3_REGISTER                            0x1104
+#define RESET4_REGISTER                            0x1105
+#define RESET5_REGISTER                            0x1106
+#define RESET6_REGISTER                            0x1107
+#define RESET7_REGISTER                            0x1108
+#define RESET0_MASK                                0x1110
+#define RESET1_MASK                                0x1111
+#define RESET2_MASK                                0x1112
+#define RESET3_MASK                                0x1113
+#define RESET4_MASK                                0x1114
+#define RESET5_MASK                                0x1115
+#define RESET6_MASK                                0x1116
+#define CRT_MASK                                   0x1117
+#define RESET7_MASK                                0x1118
+
+/* ********************************
+ * TCON:  VCBUS_BASE = 0x14
+ */
+/* TCON_L register */
+#define L_GAMMA_CNTL_PORT                          0x1400
+#define L_GAMMA_DATA_PORT                          0x1401
+#define L_GAMMA_ADDR_PORT                          0x1402
+#define L_GAMMA_VCOM_HSWITCH_ADDR                  0x1403
+#define L_RGB_BASE_ADDR                            0x1405
+#define L_RGB_COEFF_ADDR                           0x1406
+#define L_POL_CNTL_ADDR                            0x1407
+#define L_DITH_CNTL_ADDR                           0x1408
+#define L_GAMMA_PROBE_CTRL                         0x1409
+/* read only */
+#define L_GAMMA_PROBE_COLOR_L                      0x140a
+#define L_GAMMA_PROBE_COLOR_H                      0x140b
+#define L_GAMMA_PROBE_HL_COLOR                     0x140c
+#define L_GAMMA_PROBE_POS_X                        0x140d
+#define L_GAMMA_PROBE_POS_Y                        0x140e
+#define L_STH1_HS_ADDR                             0x1410
+#define L_STH1_HE_ADDR                             0x1411
+#define L_STH1_VS_ADDR                             0x1412
+#define L_STH1_VE_ADDR                             0x1413
+#define L_STH2_HS_ADDR                             0x1414
+#define L_STH2_HE_ADDR                             0x1415
+#define L_STH2_VS_ADDR                             0x1416
+#define L_STH2_VE_ADDR                             0x1417
+#define L_OEH_HS_ADDR                              0x1418
+#define L_OEH_HE_ADDR                              0x1419
+#define L_OEH_VS_ADDR                              0x141a
+#define L_OEH_VE_ADDR                              0x141b
+#define L_VCOM_HSWITCH_ADDR                        0x141c
+#define L_VCOM_VS_ADDR                             0x141d
+#define L_VCOM_VE_ADDR                             0x141e
+#define L_CPV1_HS_ADDR                             0x141f
+#define L_CPV1_HE_ADDR                             0x1420
+#define L_CPV1_VS_ADDR                             0x1421
+#define L_CPV1_VE_ADDR                             0x1422
+#define L_CPV2_HS_ADDR                             0x1423
+#define L_CPV2_HE_ADDR                             0x1424
+#define L_CPV2_VS_ADDR                             0x1425
+#define L_CPV2_VE_ADDR                             0x1426
+#define L_STV1_HS_ADDR                             0x1427
+#define L_STV1_HE_ADDR                             0x1428
+#define L_STV1_VS_ADDR                             0x1429
+#define L_STV1_VE_ADDR                             0x142a
+#define L_STV2_HS_ADDR                             0x142b
+#define L_STV2_HE_ADDR                             0x142c
+#define L_STV2_VS_ADDR                             0x142d
+#define L_STV2_VE_ADDR                             0x142e
+#define L_OEV1_HS_ADDR                             0x142f
+#define L_OEV1_HE_ADDR                             0x1430
+#define L_OEV1_VS_ADDR                             0x1431
+#define L_OEV1_VE_ADDR                             0x1432
+#define L_OEV2_HS_ADDR                             0x1433
+#define L_OEV2_HE_ADDR                             0x1434
+#define L_OEV2_VS_ADDR                             0x1435
+#define L_OEV2_VE_ADDR                             0x1436
+#define L_OEV3_HS_ADDR                             0x1437
+#define L_OEV3_HE_ADDR                             0x1438
+#define L_OEV3_VS_ADDR                             0x1439
+#define L_OEV3_VE_ADDR                             0x143a
+#define L_LCD_PWR_ADDR                             0x143b
+#define L_LCD_PWM0_LO_ADDR                         0x143c
+#define L_LCD_PWM0_HI_ADDR                         0x143d
+#define L_LCD_PWM1_LO_ADDR                         0x143e
+#define L_LCD_PWM1_HI_ADDR                         0x143f
+#define L_INV_CNT_ADDR                             0x1440
+#define L_TCON_MISC_SEL_ADDR                       0x1441
+#define L_DUAL_PORT_CNTL_ADDR                      0x1442
+#define MLVDS_CLK_CTL1_HI                          0x1443
+#define MLVDS_CLK_CTL1_LO                          0x1444
+/* [31:30] enable mlvds clocks
+ * [24]    mlvds_clk_half_delay       24 // Bit 0
+ * [23:0]  mlvds_clk_pattern           0 // Bit 23:0
+ */
+#define L_TCON_DOUBLE_CTL                          0x1449
+#define L_TCON_PATTERN_HI                          0x144a
+#define L_TCON_PATTERN_LO                          0x144b
+#define LDIM_BL_ADDR_PORT                          0x144e
+#define LDIM_BL_DATA_PORT                          0x144f
+#define L_DE_HS_ADDR                               0x1451
+#define L_DE_HE_ADDR                               0x1452
+#define L_DE_VS_ADDR                               0x1453
+#define L_DE_VE_ADDR                               0x1454
+#define L_HSYNC_HS_ADDR                            0x1455
+#define L_HSYNC_HE_ADDR                            0x1456
+#define L_HSYNC_VS_ADDR                            0x1457
+#define L_HSYNC_VE_ADDR                            0x1458
+#define L_VSYNC_HS_ADDR                            0x1459
+#define L_VSYNC_HE_ADDR                            0x145a
+#define L_VSYNC_VS_ADDR                            0x145b
+#define L_VSYNC_VE_ADDR                            0x145c
+/* bit 8 -- vfifo_mcu_enable
+ * bit 7 -- halt_vs_de
+ * bit 6 -- R8G8B8_format
+ * bit 5 -- R6G6B6_format (round to 6 bits)
+ * bit 4 -- R5G6B5_format
+ * bit 3 -- dac_dith_sel
+ * bit 2 -- lcd_mcu_enable_de     -- ReadOnly
+ * bit 1 -- lcd_mcu_enable_vsync  -- ReadOnly
+ * bit 0 -- lcd_mcu_enable
+ */
+#define L_LCD_MCU_CTL                              0x145d
+
+/* **************************************************
+ *  Dual port mLVDS registers
+ */
+/* bit 3 - enable_u_dual_mlvds_dp_clk
+ * bit 2 - enable_u_map_mlvds_r_clk
+ * bit 1 - enable_u_map_mlvds_l_clk
+ * bit 0 - dual_mlvds_en
+ */
+#define DUAL_MLVDS_CTL                             0x1460
+/* bit[12:0] - dual_mlvds_line_start */
+#define DUAL_MLVDS_LINE_START                      0x1461
+/* bit[12:0] - dual_mlvds_line_end */
+#define DUAL_MLVDS_LINE_END                        0x1462
+/* bit[12:0] - dual_mlvds_w_pixel_start_l */
+#define DUAL_MLVDS_PIXEL_W_START_L                 0x1463
+/* bit[12:0] - dual_mlvds_w_pixel_end_l */
+#define DUAL_MLVDS_PIXEL_W_END_L                   0x1464
+/* bit[12:0] - dual_mlvds_w_pixel_start_r */
+#define DUAL_MLVDS_PIXEL_W_START_R                 0x1465
+/* bit[12:0] - dual_mlvds_w_pixel_end_r */
+#define DUAL_MLVDS_PIXEL_W_END_R                   0x1466
+/* bit[12:0] - dual_mlvds_r_pixel_start_l */
+#define DUAL_MLVDS_PIXEL_R_START_L                 0x1467
+/* bit[12:0] - dual_mlvds_r_pixel_cnt_l */
+#define DUAL_MLVDS_PIXEL_R_CNT_L                   0x1468
+/* bit[12:0] - dual_mlvds_r_pixel_start_r */
+#define DUAL_MLVDS_PIXEL_R_START_R                 0x1469
+/* bit[12:0] - dual_mlvds_r_pixel_cnt_r */
+#define DUAL_MLVDS_PIXEL_R_CNT_R                   0x146a
+/* bit[15]   - v_inversion_en
+ * bit[12:0] - v_inversion_pixel
+ */
+#define V_INVERSION_PIXEL                          0x1470
+/* bit[15]   - v_inversion_sync_en
+ * bit[12:0] - v_inversion_line
+ */
+#define V_INVERSION_LINE                           0x1471
+/* bit[15:12]  - v_loop_r
+ * bit[11:10]  - v_pattern_1_r
+ * bit[9:8]    - v_pattern_0_r
+ * bit[7:4]    - v_loop_l
+ * bit[3:2]    - v_pattern_1_l
+ * bit[1:0]    - v_pattern_0_l
+ */
+#define V_INVERSION_CONTROL                        0x1472
+#define MLVDS2_CONTROL                             0x1474
+   #define     mLVDS2_RESERVED  15
+   #define     mLVDS2_double_pattern  14
+   /* 13:8  // each channel has one bit */
+   #define     mLVDS2_ins_reset  8
+   #define     mLVDS2_dual_gate  7
+   /* 0=6Bits, 1=8Bits */
+   #define     mLVDS2_bit_num    6
+   /* 0=3Pairs, 1=6Pairs */
+   #define     mLVDS2_pair_num   5
+   #define     mLVDS2_msb_first  4
+   #define     mLVDS2_PORT_SWAP  3
+   #define     mLVDS2_MLSB_SWAP  2
+   #define     mLVDS2_PN_SWAP    1
+   #define     mLVDS2_en         0
+#define MLVDS2_CONFIG_HI                           0x1475
+#define MLVDS2_CONFIG_LO                           0x1476
+   /* Bit 31:29 */
+   #define     mLVDS2_reset_offset         29
+   /* Bit 28:23 */
+   #define     mLVDS2_reset_length         23
+   /* Bit 22:20 */
+   #define     mLVDS2_config_reserved      20
+   #define     mLVDS2_reset_start_bit12    19
+   #define     mLVDS2_data_write_toggle    18
+   #define     mLVDS2_data_write_ini       17
+   #define     mLVDS2_data_latch_1_toggle  16
+   #define     mLVDS2_data_latch_1_ini     15
+   #define     mLVDS2_data_latch_0_toggle  14
+   #define     mLVDS2_data_latch_0_ini     13
+   /* 0=same as reset_0, 1=1 clock delay of reset_0 */
+   #define     mLVDS2_reset_1_select       12
+   /* Bit 11:0 */
+   #define     mLVDS2_reset_start           0
+#define MLVDS2_DUAL_GATE_WR_START                  0x1477
+   /* Bit 12:0 */
+   #define     mlvds2_dual_gate_wr_start    0
+#define MLVDS2_DUAL_GATE_WR_END                    0x1478
+   /* Bit 12:0 */
+   #define     mlvds2_dual_gate_wr_end      0
+#define MLVDS2_DUAL_GATE_RD_START                  0x1479
+   /* Bit 12:0 */
+   #define     mlvds2_dual_gate_rd_start    0
+#define MLVDS2_DUAL_GATE_RD_END                    0x147a
+   /* Bit 12:0 */
+   #define     mlvds2_dual_gate_rd_end      0
+#define MLVDS2_SECOND_RESET_CTL                    0x147b
+   /* Bit 12:0 */
+   #define     mLVDS2_2nd_reset_start       0
+#define MLVDS2_DUAL_GATE_CTL_HI                    0x147c
+#define MLVDS2_DUAL_GATE_CTL_LO                    0x147d
+   /* Bit 7:0 */
+   #define     mlvds2_tcon_field_en        24
+   /* Bit 2:0 */
+   #define     mlvds2_dual_gate_reserved   21
+   #define     mlvds2_scan_mode_start_line_bit12 20
+   /* Bit 3:0 */
+   #define     mlvds2_scan_mode_odd        16
+   /* Bit 3:0 */
+   #define     mlvds2_scan_mode_even       12
+   /* Bit 11:0 */
+   #define     mlvds2_scan_mode_start_line  0
+#define MLVDS2_RESET_CONFIG_HI                     0x147e
+#define MLVDS2_RESET_CONFIG_LO                     0x147f
+   #define     mLVDS2_reset_range_enable   31
+   #define     mLVDS2_reset_range_inv      30
+   #define     mLVDS2_reset_config_res1    29
+   /* Bit 11:0 */
+   #define     mLVDS2_reset_range_line_0   16
+   /* Bit 2:0 */
+   #define     mLVDS2_reset_config_res3    13
+   /* Bit 11:0 */
+   #define     mLVDS2_reset_range_line_1    0
+
+/* ************************************
+ *  TCON register
+ */
+#define GAMMA_CNTL_PORT                            0x1480
+   #define  GAMMA_VCOM_POL    7
+   #define  GAMMA_RVS_OUT     6
+   /* Read Only */
+   #define  ADR_RDY           5
+   /* Read Only */
+   #define  WR_RDY            4
+   /* Read Only */
+   #define  RD_RDY            3
+   #define  GAMMA_TR          2
+   #define  GAMMA_SET         1
+   #define  GAMMA_EN          0
+#define GAMMA_DATA_PORT                            0x1481
+#define GAMMA_ADDR_PORT                            0x1482
+   #define  H_RD              12
+   #define  H_AUTO_INC        11
+   #define  H_SEL_R           10
+   #define  H_SEL_G           9
+   #define  H_SEL_B           8
+   /* 7:0 */
+   #define  HADR_MSB          7
+   #define  HADR              0
+#define GAMMA_VCOM_HSWITCH_ADDR                    0x1483
+#define RGB_BASE_ADDR                              0x1485
+#define RGB_COEFF_ADDR                             0x1486
+#define POL_CNTL_ADDR                              0x1487
+   /* FOR DCLK OUTPUT */
+   #define   DCLK_SEL             14
+   /* FOR RGB format DVI output */
+   #define   TCON_VSYNC_SEL_DVI   11
+   /* FOR RGB format DVI output */
+   #define   TCON_HSYNC_SEL_DVI   10
+   /* FOR RGB format DVI output */
+   #define   TCON_DE_SEL_DVI      9
+   #define   CPH3_POL         8
+   #define   CPH2_POL         7
+   #define   CPH1_POL         6
+   #define   TCON_DE_SEL      5
+   #define   TCON_VS_SEL      4
+   #define   TCON_HS_SEL      3
+   #define   DE_POL           2
+   #define   VS_POL           1
+   #define   HS_POL           0
+#define DITH_CNTL_ADDR                             0x1488
+   #define  DITH10_EN         10
+   #define  DITH8_EN          9
+   #define  DITH_MD           8
+   /* 7:4 */
+   #define  DITH10_CNTL_MSB   7
+   #define  DITH10_CNTL       4
+   /* 3:0 */
+   #define  DITH8_CNTL_MSB    3
+   #define  DITH8_CNTL        0
+/* Bit 1 highlight_en
+ * Bit 0 probe_en
+ */
+#define GAMMA_PROBE_CTRL                           0x1489
+/* read only
+ * Bit [15:0]  probe_color[15:0]
+ */
+#define GAMMA_PROBE_COLOR_L                        0x148a
+/* Read only
+ * Bit 15: if true valid probed color
+ * Bit [13:0]  probe_color[29:16]
+ */
+#define GAMMA_PROBE_COLOR_H                        0x148b
+/* bit 15:0, 5:6:5 color */
+#define GAMMA_PROBE_HL_COLOR                       0x148c
+/* 12:0 pos_x */
+#define GAMMA_PROBE_POS_X                          0x148d
+/* 12:0 pos_y */
+#define GAMMA_PROBE_POS_Y                          0x148e
+#define STH1_HS_ADDR                               0x1490
+#define STH1_HE_ADDR                               0x1491
+#define STH1_VS_ADDR                               0x1492
+#define STH1_VE_ADDR                               0x1493
+#define STH2_HS_ADDR                               0x1494
+#define STH2_HE_ADDR                               0x1495
+#define STH2_VS_ADDR                               0x1496
+#define STH2_VE_ADDR                               0x1497
+#define OEH_HS_ADDR                                0x1498
+#define OEH_HE_ADDR                                0x1499
+#define OEH_VS_ADDR                                0x149a
+#define OEH_VE_ADDR                                0x149b
+#define VCOM_HSWITCH_ADDR                          0x149c
+#define VCOM_VS_ADDR                               0x149d
+#define VCOM_VE_ADDR                               0x149e
+#define CPV1_HS_ADDR                               0x149f
+#define CPV1_HE_ADDR                               0x14a0
+#define CPV1_VS_ADDR                               0x14a1
+#define CPV1_VE_ADDR                               0x14a2
+#define CPV2_HS_ADDR                               0x14a3
+#define CPV2_HE_ADDR                               0x14a4
+#define CPV2_VS_ADDR                               0x14a5
+#define CPV2_VE_ADDR                               0x14a6
+#define STV1_HS_ADDR                               0x14a7
+#define STV1_HE_ADDR                               0x14a8
+#define STV1_VS_ADDR                               0x14a9
+#define STV1_VE_ADDR                               0x14aa
+#define STV2_HS_ADDR                               0x14ab
+#define STV2_HE_ADDR                               0x14ac
+#define STV2_VS_ADDR                               0x14ad
+#define STV2_VE_ADDR                               0x14ae
+#define OEV1_HS_ADDR                               0x14af
+#define OEV1_HE_ADDR                               0x14b0
+#define OEV1_VS_ADDR                               0x14b1
+#define OEV1_VE_ADDR                               0x14b2
+#define OEV2_HS_ADDR                               0x14b3
+#define OEV2_HE_ADDR                               0x14b4
+#define OEV2_VS_ADDR                               0x14b5
+#define OEV2_VE_ADDR                               0x14b6
+#define OEV3_HS_ADDR                               0x14b7
+#define OEV3_HE_ADDR                               0x14b8
+#define OEV3_VS_ADDR                               0x14b9
+#define OEV3_VE_ADDR                               0x14ba
+#define LCD_PWR_ADDR                               0x14bb
+   #define      LCD_VDD        5
+   #define      LCD_VBL        4
+   #define      LCD_GPI_MSB    3
+   #define      LCD_GPIO       0
+#define LCD_PWM0_LO_ADDR                           0x14bc
+#define LCD_PWM0_HI_ADDR                           0x14bd
+#define LCD_PWM1_LO_ADDR                           0x14be
+#define LCD_PWM1_HI_ADDR                           0x14bf
+#define INV_CNT_ADDR                               0x14c0
+   #define     INV_EN          4
+   #define     INV_CNT_MSB     3
+   #define     INV_CNT         0
+#define TCON_MISC_SEL_ADDR                         0x14c1
+   #define     STH2_SEL        12
+   #define     STH1_SEL        11
+   #define     OEH_SEL         10
+   #define     VCOM_SEL         9
+   #define     DB_LINE_SW       8
+   #define     CPV2_SEL         7
+   #define     CPV1_SEL         6
+   #define     STV2_SEL         5
+   #define     STV1_SEL         4
+   #define     OEV_UNITE        3
+   #define     OEV3_SEL         2
+   #define     OEV2_SEL         1
+   #define     OEV1_SEL         0
+#define DUAL_PORT_CNTL_ADDR                        0x14c2
+   #define     OUTPUT_YUV       15
+   /* 14:12 */
+   #define     DUAL_IDF         12
+   /* 11:9 */
+   #define     DUAL_ISF         9
+   #define     LCD_ANALOG_SEL_CPH3   8
+   #define     LCD_ANALOG_3PHI_CLK_SEL   7
+   #define     LCD_LVDS_SEL54   6
+   #define     LCD_LVDS_SEL27   5
+   #define     LCD_TTL_SEL      4
+   #define     DUAL_LVDC_EN     3
+   #define     PORT_SWP         2
+   #define     RGB_SWP          1
+   #define     BIT_SWP          0
+#define MLVDS_CONTROL                              0x14c3
+   #define     mLVDS_RESERVED   15
+   #define     mLVDS_double_pattern  14
+   /* 13:8  // each channel has one bit */
+   #define     mLVDS_ins_reset  8
+   #define     mLVDS_dual_gate  7
+   /* 0=6Bits, 1=8Bits */
+   #define     mLVDS_bit_num    6
+   /* 0=3Pairs, 1=6Pairs */
+   #define     mLVDS_pair_num   5
+   #define     mLVDS_msb_first  4
+   #define     mLVDS_PORT_SWAP  3
+   #define     mLVDS_MLSB_SWAP  2
+   #define     mLVDS_PN_SWAP    1
+   #define     mLVDS_en         0
+#define MLVDS_RESET_PATTERN_HI                     0x14c4
+#define MLVDS_RESET_PATTERN_LO                     0x14c5
+   /* Bit 47:16 */
+   #define     mLVDS_reset_pattern      0
+#define MLVDS_RESET_PATTERN_EXT                    0x14c6
+   /* Bit 15:0 */
+   #define     mLVDS_reset_pattern_ext  0
+#define MLVDS_CONFIG_HI                            0x14c7
+#define MLVDS_CONFIG_LO                            0x14c8
+   /* Bit 31:29 */
+   #define     mLVDS_reset_offset         29
+   /* Bit 28:23 */
+   #define     mLVDS_reset_length         23
+   /* Bit 22:20 */
+   #define     mLVDS_config_reserved      20
+   #define     mLVDS_reset_start_bit12    19
+   #define     mLVDS_data_write_toggle    18
+   #define     mLVDS_data_write_ini       17
+   #define     mLVDS_data_latch_1_toggle  16
+   #define     mLVDS_data_latch_1_ini     15
+   #define     mLVDS_data_latch_0_toggle  14
+   #define     mLVDS_data_latch_0_ini     13
+   /* 0 - same as reset_0, 1 - 1 clock delay of reset_0 */
+   #define     mLVDS_reset_1_select       12
+   /* Bit 11:0 */
+   #define     mLVDS_reset_start           0
+#define TCON_DOUBLE_CTL                            0x14c9
+   /* Bit 7:0 */
+   #define     tcon_double_ini             8
+   /* Bit 7:0 */
+   #define     tcon_double_inv             0
+#define TCON_PATTERN_HI                            0x14ca
+#define TCON_PATTERN_LO                            0x14cb
+   /* Bit 15:0 */
+   #define     tcon_pattern_loop_data     16
+   /* Bit 3:0 */
+   #define     tcon_pattern_loop_start    12
+   /* Bit 3:0 */
+   #define     tcon_pattern_loop_end       8
+   /* Bit 7:0 */
+   #define     tcon_pattern_enable         0
+#define TCON_CONTROL_HI                            0x14cc
+#define TCON_CONTROL_LO                            0x14cd
+   /* Bit 5:0 (enable pclk on TCON channel 7 to 2) */
+   #define     tcon_pclk_enable           26
+   /* Bit 1:0 (control phy clok divide 2,4,6,8) */
+   #define     tcon_pclk_div              24
+   /* Bit 23:0 (3 bit for each channel) */
+   #define     tcon_delay                  0
+#define LVDS_BLANK_DATA_HI                         0x14ce
+#define LVDS_BLANK_DATA_LO                         0x14cf
+   /* 31:30 */
+   #define     LVDS_blank_data_reserved 30
+   /* 29:20 */
+   #define     LVDS_blank_data_r        20
+   /* 19:10 */
+   #define     LVDS_blank_data_g        10
+   /*  9:0 */
+   #define     LVDS_blank_data_b         0
+#define LVDS_PACK_CNTL_ADDR                        0x14d0
+   #define     LVDS_USE_TCON    7
+   #define     LVDS_DUAL        6
+   #define     PN_SWP           5
+   #define     LSB_FIRST        4
+   #define     LVDS_RESV        3
+   #define     ODD_EVEN_SWP     2
+   #define     LVDS_REPACK      0
+/* New from M3 :
+ * Bit 15:12 -- Enable OFFSET Double Generate(TOCN7-TCON4)
+ * Bit 11:0 -- de_hs(old tcon) second offset_hs (new tcon)
+ */
+#define DE_HS_ADDR                                 0x14d1
+/* New from M3 :
+ * Bit 15:12 -- Enable OFFSET Double Generate(TOCN3-TCON0)
+ */
+#define DE_HE_ADDR                                 0x14d2
+#define DE_VS_ADDR                                 0x14d3
+#define DE_VE_ADDR                                 0x14d4
+#define HSYNC_HS_ADDR                              0x14d5
+#define HSYNC_HE_ADDR                              0x14d6
+#define HSYNC_VS_ADDR                              0x14d7
+#define HSYNC_VE_ADDR                              0x14d8
+#define VSYNC_HS_ADDR                              0x14d9
+#define VSYNC_HE_ADDR                              0x14da
+#define VSYNC_VS_ADDR                              0x14db
+#define VSYNC_VE_ADDR                              0x14dc
+/* bit 8 -- vfifo_mcu_enable
+ * bit 7 -- halt_vs_de
+ * bit 6 -- R8G8B8_format
+ * bit 5 -- R6G6B6_format (round to 6 bits)
+ * bit 4 -- R5G6B5_format
+ * bit 3 -- dac_dith_sel
+ * bit 2 -- lcd_mcu_enable_de     -- ReadOnly
+ * bit 1 -- lcd_mcu_enable_vsync  -- ReadOnly
+ * bit 0 -- lcd_mcu_enable
+ */
+#define LCD_MCU_CTL                                0x14dd
+/* ReadOnly
+ *   R5G6B5 when R5G6B5_format
+ *   G8R8   when R8G8B8_format
+ *   G5R10  Other
+ */
+#define LCD_MCU_DATA_0                             0x14de
+/* ReadOnly
+ *   G8B8   when R8G8B8_format
+ *   G5B10  Other
+ */
+#define LCD_MCU_DATA_1                             0x14df
+/* LVDS */
+#define LVDS_GEN_CNTL                              0x14e0
+#define LVDS_PHY_CNTL0                             0x14e1
+#define LVDS_PHY_CNTL1                             0x14e2
+#define LVDS_PHY_CNTL2                             0x14e3
+#define LVDS_PHY_CNTL3                             0x14e4
+#define LVDS_PHY_CNTL4                             0x14e5
+#define LVDS_PHY_CNTL5                             0x14e6
+#define LVDS_SRG_TEST                              0x14e8
+#define LVDS_BIST_MUX0                             0x14e9
+#define LVDS_BIST_MUX1                             0x14ea
+#define LVDS_BIST_FIXED0                           0x14eb
+#define LVDS_BIST_FIXED1                           0x14ec
+#define LVDS_BIST_CNTL0                            0x14ed
+#define LVDS_CLKB_CLKA                             0x14ee
+#define LVDS_PHY_CLK_CNTL                          0x14ef
+#define LVDS_SER_EN                                0x14f0
+#define LVDS_PHY_CNTL6                             0x14f1
+#define LVDS_PHY_CNTL7                             0x14f2
+#define LVDS_PHY_CNTL8                             0x14f3
+#define MLVDS_CLK_CTL0_HI                          0x14f4
+#define MLVDS_CLK_CTL0_LO                          0x14f5
+   #define     mlvds_clk_pattern_reserved 31
+   /* Bit 2:0 */
+   #define     mpclk_dly                  28
+   /* Bit 1:0 (control phy clok divide 2,4,6,8) */
+   #define     mpclk_div                  26
+   #define     use_mpclk                  25
+   #define     mlvds_clk_half_delay       24
+   /* Bit 23:0 */
+   #define     mlvds_clk_pattern           0
+#define MLVDS_DUAL_GATE_WR_START                   0x14f6
+   /* Bit 12:0 */
+   #define     mlvds_dual_gate_wr_start    0
+#define MLVDS_DUAL_GATE_WR_END                     0x14f7
+   /* Bit 12:0 */
+   #define     mlvds_dual_gate_wr_end      0
+#define MLVDS_DUAL_GATE_RD_START                   0x14f8
+   /* Bit 12:0 */
+   #define     mlvds_dual_gate_rd_start    0
+#define MLVDS_DUAL_GATE_RD_END                     0x14f9
+   /* Bit 12:0 */
+   #define     mlvds_dual_gate_rd_end      0
+#define MLVDS_SECOND_RESET_CTL                     0x14fa
+   /* Bit 12:0 */
+   #define     mLVDS_2nd_reset_start       0
+#define MLVDS_DUAL_GATE_CTL_HI                     0x14fb
+#define MLVDS_DUAL_GATE_CTL_LO                     0x14fc
+   /* Bit 7:0 */
+   #define     mlvds_tcon_field_en        24
+   /* Bit 2:0 */
+   #define     mlvds_dual_gate_reserved   21
+   #define     mlvds_scan_mode_start_line_bit12 20
+   /* Bit 3:0 */
+   #define     mlvds_scan_mode_odd        16
+   /* Bit 3:0 */
+   #define     mlvds_scan_mode_even       12
+   /* Bit 11:0 */
+   #define     mlvds_scan_mode_start_line  0
+#define MLVDS_RESET_CONFIG_HI                      0x14fd
+#define MLVDS_RESET_CONFIG_LO                      0x14fe
+   #define     mLVDS_reset_range_enable   31
+   #define     mLVDS_reset_range_inv      30
+   #define     mLVDS_reset_config_res1    29
+   /* Bit 11:0 */
+   #define     mLVDS_reset_range_line_0   16
+   /* Bit 2:0 */
+   #define     mLVDS_reset_config_res3    13
+   /* Bit 11:0 */
+   #define     mLVDS_reset_range_line_1    0
+
+/* **************************************************************************
+ * Vbyone registers  (Note: no MinLVDS in G9tv, share the register)
+ */
+#define VBO_CTRL_L                                 0x1460
+#define VBO_CTRL_H                                 0x1461
+#define VBO_SOFT_RST                               0x1462
+#define VBO_LANES                                  0x1463
+#define VBO_VIN_CTRL                               0x1464
+#define VBO_ACT_VSIZE                              0x1465
+#define VBO_REGION_00                              0x1466
+#define VBO_REGION_01                              0x1467
+#define VBO_REGION_02                              0x1468
+#define VBO_REGION_03                              0x1469
+#define VBO_VBK_CTRL_0                             0x146a
+#define VBO_VBK_CTRL_1                             0x146b
+#define VBO_HBK_CTRL                               0x146c
+#define VBO_PXL_CTRL                               0x146d
+#define VBO_LANE_SKEW_L                            0x146e
+#define VBO_LANE_SKEW_H                            0x146f
+#define VBO_GCLK_LANE_L                            0x1470
+#define VBO_GCLK_LANE_H                            0x1471
+#define VBO_GCLK_MAIN                              0x1472
+#define VBO_STATUS_L                               0x1473
+#define VBO_STATUS_H                               0x1474
+#define VBO_LANE_OUTPUT                            0x1475
+#define LCD_PORT_SWAP                              0x1476
+#define VBO_TMCHK_THRD_L                           0x1478
+#define VBO_TMCHK_THRD_H                           0x1479
+#define VBO_FSM_HOLDER_L                           0x147a
+#define VBO_FSM_HOLDER_H                           0x147b
+#define VBO_INTR_STATE_CTRL                        0x147c
+#define VBO_INTR_UNMASK                            0x147d
+#define VBO_TMCHK_HSYNC_STATE_L                    0x147e
+#define VBO_TMCHK_HSYNC_STATE_H                    0x147f
+#define VBO_TMCHK_VSYNC_STATE_L                    0x14f4
+#define VBO_TMCHK_VSYNC_STATE_H                    0x14f5
+#define VBO_TMCHK_VDE_STATE_L                      0x14f6
+#define VBO_TMCHK_VDE_STATE_H                      0x14f7
+#define VBO_INTR_STATE                             0x14f8
+
+/* ********************************
+ * Video Interface:  VENC_VCBUS_BASE = 0x1b
+ */
+#define VENC_INTCTRL                               0x1b6e
+
+/* ********************************
+ * ENCL:  VCBUS_BASE = 0x1c
+ */
+/* ENCL */
+/* bit 15:8 -- vfifo2vd_vd_sel
+ * bit 7 -- vfifo2vd_drop
+ * bit 6:1 -- vfifo2vd_delay
+ * bit 0 -- vfifo2vd_en
+ */
+#define ENCL_VFIFO2VD_CTL                          0x1c90
+/* bit 12:0 -- vfifo2vd_pixel_start */
+#define ENCL_VFIFO2VD_PIXEL_START                  0x1c91
+/* bit 12:00 -- vfifo2vd_pixel_end */
+#define ENCL_VFIFO2VD_PIXEL_END                    0x1c92
+/* bit 10:0 -- vfifo2vd_line_top_start */
+#define ENCL_VFIFO2VD_LINE_TOP_START               0x1c93
+/* bit 10:00 -- vfifo2vd_line_top_end */
+#define ENCL_VFIFO2VD_LINE_TOP_END                 0x1c94
+/* bit 10:00 -- vfifo2vd_line_bot_start */
+#define ENCL_VFIFO2VD_LINE_BOT_START               0x1c95
+/* bit 10:00 -- vfifo2vd_line_bot_end */
+#define ENCL_VFIFO2VD_LINE_BOT_END                 0x1c96
+#define ENCL_VFIFO2VD_CTL2                         0x1c97
+#define ENCL_TST_EN                                0x1c98
+#define ENCL_TST_MDSEL                             0x1c99
+#define ENCL_TST_Y                                 0x1c9a
+#define ENCL_TST_CB                                0x1c9b
+#define ENCL_TST_CR                                0x1c9c
+#define ENCL_TST_CLRBAR_STRT                       0x1c9d
+#define ENCL_TST_CLRBAR_WIDTH                      0x1c9e
+#define ENCL_TST_VDCNT_STSET                       0x1c9f
+
+/* ENCL registers */
+#define ENCL_VIDEO_EN                              0x1ca0
+#define ENCL_VIDEO_Y_SCL                           0x1ca1
+#define ENCL_VIDEO_PB_SCL                          0x1ca2
+#define ENCL_VIDEO_PR_SCL                          0x1ca3
+#define ENCL_VIDEO_Y_OFFST                         0x1ca4
+#define ENCL_VIDEO_PB_OFFST                        0x1ca5
+#define ENCL_VIDEO_PR_OFFST                        0x1ca6
+/* ----- Video mode */
+#define ENCL_VIDEO_MODE                            0x1ca7
+#define ENCL_VIDEO_MODE_ADV                        0x1ca8
+/* --------------- Debug pins */
+#define ENCL_DBG_PX_RST                            0x1ca9
+#define ENCL_DBG_LN_RST                            0x1caa
+#define ENCL_DBG_PX_INT                            0x1cab
+#define ENCL_DBG_LN_INT                            0x1cac
+/* ----------- Video Advanced setting */
+#define ENCL_VIDEO_YFP1_HTIME                      0x1cad
+#define ENCL_VIDEO_YFP2_HTIME                      0x1cae
+#define ENCL_VIDEO_YC_DLY                          0x1caf
+#define ENCL_VIDEO_MAX_PXCNT                       0x1cb0
+#define ENCL_VIDEO_HAVON_END                       0x1cb1
+#define ENCL_VIDEO_HAVON_BEGIN                     0x1cb2
+#define ENCL_VIDEO_VAVON_ELINE                     0x1cb3
+#define ENCL_VIDEO_VAVON_BLINE                     0x1cb4
+#define ENCL_VIDEO_HSO_BEGIN                       0x1cb5
+#define ENCL_VIDEO_HSO_END                         0x1cb6
+#define ENCL_VIDEO_VSO_BEGIN                       0x1cb7
+#define ENCL_VIDEO_VSO_END                         0x1cb8
+#define ENCL_VIDEO_VSO_BLINE                       0x1cb9
+#define ENCL_VIDEO_VSO_ELINE                       0x1cba
+#define ENCL_VIDEO_MAX_LNCNT                       0x1cbb
+#define ENCL_VIDEO_BLANKY_VAL                      0x1cbc
+#define ENCL_VIDEO_BLANKPB_VAL                     0x1cbd
+#define ENCL_VIDEO_BLANKPR_VAL                     0x1cbe
+#define ENCL_VIDEO_HOFFST                          0x1cbf
+#define ENCL_VIDEO_VOFFST                          0x1cc0
+#define ENCL_VIDEO_RGB_CTRL                        0x1cc1
+#define ENCL_VIDEO_FILT_CTRL                       0x1cc2
+#define ENCL_VIDEO_OFLD_VPEQ_OFST                  0x1cc3
+#define ENCL_VIDEO_OFLD_VOAV_OFST                  0x1cc4
+#define ENCL_VIDEO_MATRIX_CB                       0x1cc5
+#define ENCL_VIDEO_MATRIX_CR                       0x1cc6
+#define ENCL_VIDEO_RGBIN_CTRL                      0x1cc7
+#define ENCL_MAX_LINE_SWITCH_POINT                 0x1cc8
+#define ENCL_DACSEL_0                              0x1cc9
+#define ENCL_DACSEL_1                              0x1cca
+
+/* ********************************
+ * ENCT:  VCBUS_BASE = 0x1c
+ */
+/* ENCT */
+/* bit 15:8 -- vfifo2vd_vd_sel
+ * bit 7 -- vfifo2vd_drop
+ * bit 6:1 -- vfifo2vd_delay
+ * bit 0 -- vfifo2vd_en
+ */
+#define ENCT_VFIFO2VD_CTL                          0x1c20
+/* bit 12:0 -- vfifo2vd_pixel_start */
+#define ENCT_VFIFO2VD_PIXEL_START                  0x1c21
+/* bit 12:00 -- vfifo2vd_pixel_end */
+#define ENCT_VFIFO2VD_PIXEL_END                    0x1c22
+/* bit 10:0 -- vfifo2vd_line_top_start */
+#define ENCT_VFIFO2VD_LINE_TOP_START               0x1c23
+/* bit 10:00 -- vfifo2vd_line_top_end */
+#define ENCT_VFIFO2VD_LINE_TOP_END                 0x1c24
+/* bit 10:00 -- vfifo2vd_line_bot_start */
+#define ENCT_VFIFO2VD_LINE_BOT_START               0x1c25
+/* bit 10:00 -- vfifo2vd_line_bot_end */
+#define ENCT_VFIFO2VD_LINE_BOT_END                 0x1c26
+#define ENCT_VFIFO2VD_CTL2                         0x1c27
+#define ENCT_TST_EN                                0x1c28
+#define ENCT_TST_MDSEL                             0x1c29
+#define ENCT_TST_Y                                 0x1c2a
+#define ENCT_TST_CB                                0x1c2b
+#define ENCT_TST_CR                                0x1c2c
+#define ENCT_TST_CLRBAR_STRT                       0x1c2d
+#define ENCT_TST_CLRBAR_WIDTH                      0x1c2e
+#define ENCT_TST_VDCNT_STSET                       0x1c2f
+
+/* ENCT registers */
+#define ENCT_VIDEO_EN                              0x1c60
+#define ENCT_VIDEO_Y_SCL                           0x1c61
+#define ENCT_VIDEO_PB_SCL                          0x1c62
+#define ENCT_VIDEO_PR_SCL                          0x1c63
+#define ENCT_VIDEO_Y_OFFST                         0x1c64
+#define ENCT_VIDEO_PB_OFFST                        0x1c65
+#define ENCT_VIDEO_PR_OFFST                        0x1c66
+/* ----- Video mode */
+#define ENCT_VIDEO_MODE                            0x1c67
+#define ENCT_VIDEO_MODE_ADV                        0x1c68
+/* --------------- Debug pins */
+#define ENCT_DBG_PX_RST                            0x1c69
+#define ENCT_DBG_LN_RST                            0x1c6a
+#define ENCT_DBG_PX_INT                            0x1c6b
+#define ENCT_DBG_LN_INT                            0x1c6c
+/* ----------- Video Advanced setting */
+#define ENCT_VIDEO_YFP1_HTIME                      0x1c6d
+#define ENCT_VIDEO_YFP2_HTIME                      0x1c6e
+#define ENCT_VIDEO_YC_DLY                          0x1c6f
+#define ENCT_VIDEO_MAX_PXCNT                       0x1c70
+#define ENCT_VIDEO_HAVON_END                       0x1c71
+#define ENCT_VIDEO_HAVON_BEGIN                     0x1c72
+#define ENCT_VIDEO_VAVON_ELINE                     0x1c73
+#define ENCT_VIDEO_VAVON_BLINE                     0x1c74
+#define ENCT_VIDEO_HSO_BEGIN                       0x1c75
+#define ENCT_VIDEO_HSO_END                         0x1c76
+#define ENCT_VIDEO_VSO_BEGIN                       0x1c77
+#define ENCT_VIDEO_VSO_END                         0x1c78
+#define ENCT_VIDEO_VSO_BLINE                       0x1c79
+#define ENCT_VIDEO_VSO_ELINE                       0x1c7a
+#define ENCT_VIDEO_MAX_LNCNT                       0x1c7b
+#define ENCT_VIDEO_BLANKY_VAL                      0x1c7c
+#define ENCT_VIDEO_BLANKPB_VAL                     0x1c7d
+#define ENCT_VIDEO_BLANKPR_VAL                     0x1c7e
+#define ENCT_VIDEO_HOFFST                          0x1c7f
+#define ENCT_VIDEO_VOFFST                          0x1c80
+#define ENCT_VIDEO_RGB_CTRL                        0x1c81
+#define ENCT_VIDEO_FILT_CTRL                       0x1c82
+#define ENCT_VIDEO_OFLD_VPEQ_OFST                  0x1c83
+#define ENCT_VIDEO_OFLD_VOAV_OFST                  0x1c84
+#define ENCT_VIDEO_MATRIX_CB                       0x1c85
+#define ENCT_VIDEO_MATRIX_CR                       0x1c86
+#define ENCT_VIDEO_RGBIN_CTRL                      0x1c87
+#define ENCT_MAX_LINE_SWITCH_POINT                 0x1c88
+#define ENCT_DACSEL_0                              0x1c89
+#define ENCT_DACSEL_1                              0x1c8a
+
+/* ********************************
+ * Video post-processing:  VPP_VCBUS_BASE = 0x1d
+ * Bit 31  vd1_bgosd_exchange_en for preblend
+ * Bit 30  vd1_bgosd_exchange_en for postblend
+ * bit 28   color management enable
+ * Bit 27,  reserved
+ * Bit 26:18, reserved
+ * Bit 17, osd2 enable for preblend
+ * Bit 16, osd1 enable for preblend
+ * Bit 15, reserved
+ * Bit 14, vd1 enable for preblend
+ * Bit 13, osd2 enable for postblend
+ * Bit 12, osd1 enable for postblend
+ * Bit 11, reserved
+ * Bit 10, vd1 enable for postblend
+ * Bit 9,  if true, osd1 is alpha premultipiled
+ * Bit 8,  if true, osd2 is alpha premultipiled
+ * Bit 7,  postblend module enable
+ * Bit 6,  preblend module enable
+ * Bit 5,  if true, osd2 foreground compared with osd1 in preblend
+ * Bit 4,  if true, osd2 foreground compared with osd1 in postblend
+ * Bit 3,
+ * Bit 2,  if true, disable resetting async fifo every vsync, otherwise every
+ *           vsync the aync fifo will be reseted.
+ * Bit 1,
+ * Bit 0    if true, the output result of VPP is saturated
+ */
+#define VPP2_MISC                                  0x1926
+/* Bit 31  vd1_bgosd_exchange_en for preblend
+ * Bit 30  vd1_bgosd_exchange_en for postblend
+ * Bit 28   color management enable
+ * Bit 27,  if true, vd2 use viu2 output as the input, otherwise use normal
+ *            vd2 from memory
+ * Bit 26:18, vd2 alpha
+ * Bit 17, osd2 enable for preblend
+ * Bit 16, osd1 enable for preblend
+ * Bit 15, vd2 enable for preblend
+ * Bit 14, vd1 enable for preblend
+ * Bit 13, osd2 enable for postblend
+ * Bit 12, osd1 enable for postblend
+ * Bit 11, vd2 enable for postblend
+ * Bit 10, vd1 enable for postblend
+ * Bit 9,  if true, osd1 is alpha premultipiled
+ * Bit 8,  if true, osd2 is alpha premultipiled
+ * Bit 7,  postblend module enable
+ * Bit 6,  preblend module enable
+ * Bit 5,  if true, osd2 foreground compared with osd1 in preblend
+ * Bit 4,  if true, osd2 foreground compared with osd1 in postblend
+ * Bit 3,
+ * Bit 2,  if true, disable resetting async fifo every vsync, otherwise every
+ *           vsync the aync fifo will be reseted.
+ * Bit 1,
+ * Bit 0       if true, the output result of VPP is saturated
+ */
+#define VPP_MISC                                   0x1d26
+
+#define VPP2_POSTBLEND_H_SIZE                      0x1921
+#define VPP_POSTBLEND_H_SIZE                       0x1d21
+/* Bit 3       minus black level enable for vadj2
+ * Bit 2       Video adjustment enable for vadj2
+ * Bit 1       minus black level enable for vadj1
+ * Bit 0       Video adjustment enable for vadj1
+ */
+#define VPP_VADJ_CTRL                              0x1d40
+/* Bit 16:8  brightness, signed value
+ * Bit 7:0     contrast, unsigned value,
+ * contrast from  0 <= contrast <2
+ */
+#define VPP_VADJ1_Y                                0x1d41
+/* cb' = cb*ma + cr*mb
+ * cr' = cb*mc + cr*md
+ * all are bit 9:0, signed value, -2 < ma/mb/mc/md < 2
+ */
+#define VPP_VADJ1_MA_MB                            0x1d42
+#define VPP_VADJ1_MC_MD                            0x1d43
+/* Bit 16:8  brightness, signed value
+ * Bit 7:0   contrast, unsigned value,
+ * contrast from  0 <= contrast <2
+ */
+#define VPP_VADJ2_Y                                0x1d44
+/* cb' = cb*ma + cr*mb
+ * cr' = cb*mc + cr*md
+ * all are bit 9:0, signed value, -2 < ma/mb/mc/md < 2
+ */
+#define VPP_VADJ2_MA_MB                            0x1d45
+#define VPP_VADJ2_MC_MD                            0x1d46
+
+#define VPP_MATRIX_CTRL                            0x1d5f
+/* Bit 28:16 coef00 */
+/* Bit 12:0  coef01 */
+#define VPP_MATRIX_COEF00_01                       0x1d60
+/* Bit 28:16 coef02 */
+/* Bit 12:0  coef10 */
+#define VPP_MATRIX_COEF02_10                       0x1d61
+/* Bit 28:16 coef11 */
+/* Bit 12:0  coef12 */
+#define VPP_MATRIX_COEF11_12                       0x1d62
+/* Bit 28:16 coef20 */
+/* Bit 12:0  coef21 */
+#define VPP_MATRIX_COEF20_21                       0x1d63
+#define VPP_MATRIX_COEF22                          0x1d64
+/* Bit 26:16 offset0 */
+/* Bit 10:0  offset1 */
+#define VPP_MATRIX_OFFSET0_1                       0x1d65
+/* Bit 10:0  offset2 */
+#define VPP_MATRIX_OFFSET2                         0x1d66
+/* Bit 26:16 pre_offset0 */
+/* Bit 10:0  pre_offset1 */
+#define VPP_MATRIX_PRE_OFFSET0_1                   0x1d67
+/* Bit 10:0  pre_offset2 */
+#define VPP_MATRIX_PRE_OFFSET2                     0x1d68
+
+/* ********************************
+ * VPU:  VPU_VCBUS_BASE = 0x27
+ * [31:11] Reserved.
+ * [10: 8] cntl_viu_vdin_sel_data. Select VIU to VDIN data path,
+ *   must clear it first before changing the path selection:
+ *          3'b000=Disable VIU to VDIN path;
+ *          3'b001=Enable VIU of ENC_I domain to VDIN;
+ *          3'b010=Enable VIU of ENC_P domain to VDIN;
+ *          3'b100=Enable VIU of ENC_T domain to VDIN;
+ * [ 6: 4] cntl_viu_vdin_sel_clk. Select which clock to VDIN path,
+ *   must clear it first before changing the clock:
+ *          3'b000=Disable VIU to VDIN clock;
+ *          3'b001=Select encI clock to VDIN;
+ *          3'b010=Select encP clock to VDIN;
+ *          3'b100=Select encT clock to VDIN;
+ * [ 3: 2] cntl_viu2_sel_venc. Select which one of the encI/P/T
+ *   that VIU2 connects to:
+ *         0=ENCL, 1=ENCI, 2=ENCP, 3=ENCT.
+ * [ 1: 0] cntl_viu1_sel_venc. Select which one of the encI/P/T
+ * that VIU1 connects to:
+ * 0=ENCL, 1=ENCI, 2=ENCP, 3=ENCT.
+ */
+#define VPU_VIU_VENC_MUX_CTRL                      0x271a
+
+/* Bit  6 RW, gclk_mpeg_vpu_misc
+ * Bit  5 RW, gclk_mpeg_venc_l_top
+ * Bit  4 RW, gclk_mpeg_vencl_int
+ * Bit  3 RW, gclk_mpeg_vencp_int
+ * Bit  2 RW, gclk_mpeg_vi2_top
+ * Bit  1 RW, gclk_mpeg_vi_top
+ * Bit  0 RW, gclk_mpeg_venc_p_top
+ */
+#define VPU_CLK_GATE                               0x2723
+#define VPU_MISC_CTRL                              0x2740
+
+#define VPU_VENCL_DITH_CTRL                        0x27e0
+
+#define VPU_VLOCK_CTRL                             0x3000
+#define VPU_VLOCK_ADJ_EN_SYNC_CTRL                 0x301d
+#define VPU_VLOCK_GCLK_EN                          0x301e
+/* ******************************** */
+
+/* ***********************************************
+ * DSI Host Controller register offset address define
+ * VCBUS_BASE = 0x2c(0x2c00 - 0x2cff)
+ */
+/* DWC IP registers */
+#define MIPI_DSI_DWC_VERSION_OS                    0x1800
+#define MIPI_DSI_DWC_PWR_UP_OS                     0x1801
+#define MIPI_DSI_DWC_CLKMGR_CFG_OS                 0x1802
+#define MIPI_DSI_DWC_DPI_VCID_OS                   0x1803
+#define MIPI_DSI_DWC_DPI_COLOR_CODING_OS           0x1804
+#define MIPI_DSI_DWC_DPI_CFG_POL_OS                0x1805
+#define MIPI_DSI_DWC_DPI_LP_CMD_TIM_OS             0x1806
+#define MIPI_DSI_DWC_PCKHDL_CFG_OS                 0x180b
+#define MIPI_DSI_DWC_GEN_VCID_OS                   0x180c
+#define MIPI_DSI_DWC_MODE_CFG_OS                   0x180d
+#define MIPI_DSI_DWC_VID_MODE_CFG_OS               0x180e
+#define MIPI_DSI_DWC_VID_PKT_SIZE_OS               0x180f
+#define MIPI_DSI_DWC_VID_NUM_CHUNKS_OS             0x1810
+#define MIPI_DSI_DWC_VID_NULL_SIZE_OS              0x1811
+#define MIPI_DSI_DWC_VID_HSA_TIME_OS               0x1812
+#define MIPI_DSI_DWC_VID_HBP_TIME_OS               0x1813
+#define MIPI_DSI_DWC_VID_HLINE_TIME_OS             0x1814
+#define MIPI_DSI_DWC_VID_VSA_LINES_OS              0x1815
+#define MIPI_DSI_DWC_VID_VBP_LINES_OS              0x1816
+#define MIPI_DSI_DWC_VID_VFP_LINES_OS              0x1817
+#define MIPI_DSI_DWC_VID_VACTIVE_LINES_OS          0x1818
+#define MIPI_DSI_DWC_EDPI_CMD_SIZE_OS              0x1819
+#define MIPI_DSI_DWC_CMD_MODE_CFG_OS               0x181a
+#define MIPI_DSI_DWC_GEN_HDR_OS                    0x181b
+#define MIPI_DSI_DWC_GEN_PLD_DATA_OS               0x181c
+#define MIPI_DSI_DWC_CMD_PKT_STATUS_OS             0x181d
+#define MIPI_DSI_DWC_TO_CNT_CFG_OS                 0x181e
+#define MIPI_DSI_DWC_HS_RD_TO_CNT_OS               0x181f
+#define MIPI_DSI_DWC_LP_RD_TO_CNT_OS               0x1820
+#define MIPI_DSI_DWC_HS_WR_TO_CNT_OS               0x1821
+#define MIPI_DSI_DWC_LP_WR_TO_CNT_OS               0x1822
+#define MIPI_DSI_DWC_BTA_TO_CNT_OS                 0x1823
+#define MIPI_DSI_DWC_SDF_3D_OS                     0x1824
+#define MIPI_DSI_DWC_LPCLK_CTRL_OS                 0x1825
+#define MIPI_DSI_DWC_PHY_TMR_LPCLK_CFG_OS          0x1826
+#define MIPI_DSI_DWC_PHY_TMR_CFG_OS                0x1827
+#define MIPI_DSI_DWC_PHY_RSTZ_OS                   0x1828
+#define MIPI_DSI_DWC_PHY_IF_CFG_OS                 0x1829
+#define MIPI_DSI_DWC_PHY_ULPS_CTRL_OS              0x182a
+#define MIPI_DSI_DWC_PHY_TX_TRIGGERS_OS            0x182b
+#define MIPI_DSI_DWC_PHY_STATUS_OS                 0x182c
+#define MIPI_DSI_DWC_PHY_TST_CTRL0_OS              0x182d
+#define MIPI_DSI_DWC_PHY_TST_CTRL1_OS              0x182e
+#define MIPI_DSI_DWC_INT_ST0_OS                    0x182f
+#define MIPI_DSI_DWC_INT_ST1_OS                    0x1830
+#define MIPI_DSI_DWC_INT_MSK0_OS                   0x1831
+#define MIPI_DSI_DWC_INT_MSK1_OS                   0x1832
+
+/* Top-level registers */
+/* [31: 3]    Reserved.     Default 0.
+ *     [2] RW dpi_rst_n: Default 1.
+ *             1=Assert SW reset on mipi_dsi_host_dpi block.   0=Release reset.
+ *     [1] RW intr_rst_n: Default 1.
+ *             1=Assert SW reset on mipi_dsi_host_intr block.  0=Release reset.
+ *     [0] RW dwc_rst_n:  Default 1.
+ *             1=Assert SW reset on IP core.   0=Release reset.
+ */
+#define MIPI_DSI_TOP_SW_RESET                      0x18f0
+/* [31: 5] Reserved.   Default 0.
+ *     [4] RW manual_edpihalt: Default 0.
+ *             1=Manual suspend VencL; 0=do not suspend VencL.
+ *     [3] RW auto_edpihalt_en: Default 0.
+ *             1=Enable IP's edpihalt signal to suspend VencL;
+ *             0=IP's edpihalt signal does not affect VencL.
+ *     [2] RW clock_freerun: Apply to auto-clock gate only. Default 0.
+ *             0=Default, use auto-clock gating to save power;
+ *             1=use free-run clock, disable auto-clock gating, for debug mode.
+ *     [1] RW enable_pixclk: A manual clock gate option, due to DWC IP does not
+ *             have auto-clock gating. 1=Enable pixclk.      Default 0.
+ *     [0] RW enable_sysclk: A manual clock gate option, due to DWC IP does not
+ *             have auto-clock gating. 1=Enable sysclk.      Default 0.
+ */
+#define MIPI_DSI_TOP_CLK_CNTL                      0x18f1
+/* [31:24]    Reserved. Default 0.
+ * [23:20] RW dpi_color_mode: Define DPI pixel format. Default 0.
+ *             0=16-bit RGB565 config 1;
+ *             1=16-bit RGB565 config 2;
+ *             2=16-bit RGB565 config 3;
+ *             3=18-bit RGB666 config 1;
+ *             4=18-bit RGB666 config 2;
+ *             5=24-bit RGB888;
+ *             6=20-bit YCbCr 4:2:2;
+ *             7=24-bit YCbCr 4:2:2;
+ *             8=16-bit YCbCr 4:2:2;
+ *             9=30-bit RGB;
+ *             10=36-bit RGB;
+ *             11=12-bit YCbCr 4:2:0.
+ *    [19] Reserved. Default 0.
+ * [18:16] RW in_color_mode:  Define VENC data width. Default 0.
+ *             0=30-bit pixel;
+ *             1=24-bit pixel;
+ *             2=18-bit pixel, RGB666;
+ *             3=16-bit pixel, RGB565.
+ * [15:14] RW chroma_subsample: Define method of chroma subsampling. Default 0.
+ *             Applicable to YUV422 or YUV420 only.
+ *             0=Use even pixel's chroma;
+ *             1=Use odd pixel's chroma;
+ *             2=Use averaged value between even and odd pair.
+ * [13:12] RW comp2_sel:  Select which component to be Cr or B: Default 2.
+ *             0=comp0; 1=comp1; 2=comp2.
+ * [11:10] RW comp1_sel:  Select which component to be Cb or G: Default 1.
+ *             0=comp0; 1=comp1; 2=comp2.
+ *  [9: 8] RW comp0_sel:  Select which component to be Y  or R: Default 0.
+ *             0=comp0; 1=comp1; 2=comp2.
+ *     [7]    Reserved. Default 0.
+ *     [6] RW de_pol:  Default 0.
+ *             If DE input is active low, set to 1 to invert to active high.
+ *     [5] RW hsync_pol: Default 0.
+ *             If HS input is active low, set to 1 to invert to active high.
+ *     [4] RW vsync_pol: Default 0.
+ *             If VS input is active low, set to 1 to invert to active high.
+ *     [3] RW dpicolorm: Signal to IP.   Default 0.
+ *     [2] RW dpishutdn: Signal to IP.   Default 0.
+ *     [1]    Reserved.  Default 0.
+ *     [0]    Reserved.  Default 0.
+ */
+#define MIPI_DSI_TOP_CNTL                          0x18f2
+#define MIPI_DSI_TOP_SUSPEND_CNTL                  0x18f3
+#define MIPI_DSI_TOP_SUSPEND_LINE                  0x18f4
+#define MIPI_DSI_TOP_SUSPEND_PIX                   0x18f5
+#define MIPI_DSI_TOP_MEAS_CNTL                     0x18f6
+/* [0] R  stat_edpihalt:  edpihalt signal from IP.    Default 0. */
+#define MIPI_DSI_TOP_STAT                          0x18f7
+#define MIPI_DSI_TOP_MEAS_STAT_TE0                 0x18f8
+#define MIPI_DSI_TOP_MEAS_STAT_TE1                 0x18f9
+#define MIPI_DSI_TOP_MEAS_STAT_VS0                 0x18fa
+#define MIPI_DSI_TOP_MEAS_STAT_VS1                 0x18fb
+/* [31:16] RW intr_stat/clr. Default 0.
+ *             For each bit, read as this interrupt level status,
+ *             write 1 to clear.
+ * [31:22] Reserved
+ * [   21] stat/clr of eof interrupt
+ * [   21] vde_fall interrupt
+ * [   19] stat/clr of de_rise interrupt
+ * [   18] stat/clr of vs_fall interrupt
+ * [   17] stat/clr of vs_rise interrupt
+ * [   16] stat/clr of dwc_edpite interrupt
+ * [15: 0] RW intr_enable. Default 0.
+ *             For each bit, 1=enable this interrupt, 0=disable.
+ *     [15: 6] Reserved
+ *     [    5] eof interrupt
+ *     [    4] de_fall interrupt
+ *     [    3] de_rise interrupt
+ *     [    2] vs_fall interrupt
+ *     [    1] vs_rise interrupt
+ *     [    0] dwc_edpite interrupt
+ */
+#define MIPI_DSI_TOP_INTR_CNTL_STAT                0x18fc
+// 31: 2    Reserved.   Default 0.
+//  1: 0 RW mem_pd.     Default 3.
+#define MIPI_DSI_TOP_MEM_PD                        0x18fd
+
+/* ***********************************************
+ * DSI PHY register offset address define
+ */
+#define MIPI_DSI_PHY_START      0xff640000
+#define MIPI_DSI_PHY_END        0xff641fff
+/* [31] soft reset for the phy.
+ *             1: reset. 0: dessert the reset.
+ * [30] clock lane soft reset.
+ * [29] data byte lane 3 soft reset.
+ * [28] data byte lane 2 soft reset.
+ * [27] data byte lane 1 soft reset.
+ * [26] data byte lane 0 soft reset.
+ * [25] mipi dsi pll clock selection.
+ *             1:  clock from fixed 850Mhz clock source. 0: from VID2 PLL.
+ * [12] mipi HSbyteclk enable.
+ * [11] mipi divider clk selection.
+ *             1: select the mipi DDRCLKHS from clock divider.
+ *             0: from PLL clock.
+ * [10] mipi clock divider control.
+ *             1: /4. 0: /2.
+ * [9]  mipi divider output enable.
+ * [8]  mipi divider counter enable.
+ * [7]  PLL clock enable.
+ * [5]  LPDT data endian.
+ *             1 = transfer the high bit first. 0 : transfer the low bit first.
+ * [4]  HS data endian.
+ * [3]  force data byte lane in stop mode.
+ * [2]  force data byte lane 0 in receiver mode.
+ * [1]  write 1 to sync the txclkesc input. the internal logic have to
+ *     use txclkesc to decide Txvalid and Txready.
+ * [0]  enalbe the MIPI DSI PHY TxDDRClk.
+ */
+#define MIPI_DSI_PHY_CTRL       0x0
+/* [31] clk lane tx_hs_en control selection.
+ *             1: from register. 0: use clk lane state machine.
+ * [30] register bit for clock lane tx_hs_en.
+ * [29] clk lane tx_lp_en contrl selection.
+ *             1: from register. 0: from clk lane state machine.
+ * [28] register bit for clock lane tx_lp_en.
+ * [27] chan0 tx_hs_en control selection.
+ *             1: from register. 0: from chan0 state machine.
+ * [26] register bit for chan0 tx_hs_en.
+ * [25] chan0 tx_lp_en control selection.
+ *             1: from register. 0: from chan0 state machine.
+ * [24] register bit from chan0 tx_lp_en.
+ * [23] chan0 rx_lp_en control selection.
+ *             1: from register. 0: from chan0 state machine.
+ * [22] register bit from chan0 rx_lp_en.
+ * [21] chan0 contention detection enable control selection.
+ *             1: from register. 0: from chan0 state machine.
+ * [20] register bit from chan0 contention dectection enable.
+ * [19] chan1 tx_hs_en control selection.
+ *             1: from register. 0: from chan0 state machine.
+ * [18] register bit for chan1 tx_hs_en.
+ * [17] chan1 tx_lp_en control selection.
+ *             1: from register. 0: from chan0 state machine.
+ * [16] register bit from chan1 tx_lp_en.
+ * [15] chan2 tx_hs_en control selection.
+ *             1: from register. 0: from chan0 state machine.
+ * [14] register bit for chan2 tx_hs_en.
+ * [13] chan2 tx_lp_en control selection.
+ *             1: from register. 0: from chan0 state machine.
+ * [12] register bit from chan2 tx_lp_en.
+ * [11] chan3 tx_hs_en control selection.
+ *             1: from register. 0: from chan0 state machine.
+ * [10] register bit for chan3 tx_hs_en.
+ * [9]  chan3 tx_lp_en control selection.
+ *             1: from register. 0: from chan0 state machine.
+ * [8]  register bit from chan3 tx_lp_en.
+ * [4]  clk chan power down. this bit is also used as the power down
+ *     of the whole MIPI_DSI_PHY.
+ * [3]  chan3 power down.
+ * [2]  chan2 power down.
+ * [1]  chan1 power down.
+ * [0]  chan0 power down.
+ */
+#define MIPI_DSI_CHAN_CTRL      0x1
+/* [24]   rx turn watch dog triggered.
+ * [23]   rx esc watchdog  triggered.
+ * [22]   mbias ready.
+ * [21]   txclkesc  synced and ready.
+ * [20:17] clk lane state. {mbias_ready, tx_stop, tx_ulps, tx_hs_active}
+ * [16:13] chan3 state{0, tx_stop, tx_ulps, tx_hs_active}
+ * [12:9]  chan2 state.{0, tx_stop, tx_ulps, tx_hs_active}
+ * [8:5]   chan1 state. {0, tx_stop, tx_ulps, tx_hs_active}
+ * [4:0]   chan0 state. {TX_STOP, tx_ULPS, hs_active, direction, rxulpsesc}
+ */
+#define MIPI_DSI_CHAN_STS       0x2
+/* [31:24] TCLK_PREPARE.
+ * [23:16] TCLK_ZERO.
+ * [15:8]  TCLK_POST.
+ * [7:0]   TCLK_TRAIL.
+ */
+#define MIPI_DSI_CLK_TIM        0x3
+/* [31:24] THS_PREPARE.
+ * [23:16] THS_ZERO.
+ * [15:8]  THS_TRAIL.
+ * [7:0]   THS_EXIT.
+ */
+#define MIPI_DSI_HS_TIM         0x4
+/* [31:24] tTA_GET.
+ * [23:16] tTA_GO.
+ * [15:8]  tTA_SURE.
+ * [7:0]   tLPX.
+ */
+#define MIPI_DSI_LP_TIM         0x5
+/* wait time to  MIPI DIS analog ready. */
+#define MIPI_DSI_ANA_UP_TIM     0x6
+/* TINIT. */
+#define MIPI_DSI_INIT_TIM       0x7
+/* TWAKEUP. */
+#define MIPI_DSI_WAKEUP_TIM     0x8
+/* when in RxULPS check state, after the the logic enable the analog,
+ *     how long we should wait to check the lP state .
+ */
+#define MIPI_DSI_LPOK_TIM       0x9
+/* Watchdog for RX low power state no finished. */
+#define MIPI_DSI_LP_WCHDOG      0xa
+/* tMBIAS,  after send power up signals to analog,
+ *     how long we should wait for analog powered up.
+ */
+#define MIPI_DSI_ANA_CTRL       0xb
+/* [31:8]  reserved for future.
+ * [7:0]   tCLK_PRE.
+ */
+#define MIPI_DSI_CLK_TIM1       0xc
+/* watchdog for turn around waiting time. */
+#define MIPI_DSI_TURN_WCHDOG    0xd
+/* When in RxULPS state, how frequency we should to check
+ *     if the TX side out of ULPS state.
+ */
+#define MIPI_DSI_ULPS_CHECK     0xe
+
+#define MIPI_DSI_TEST_CTRL0     0xf
+
+#define MIPI_DSI_TEST_CTRL1     0x10
+
+/* ***********************************************
+ * register access api
+ */
+
+extern int lcd_ioremap(void);
+extern unsigned int lcd_vcbus_read(unsigned int _reg);
+extern void lcd_vcbus_write(unsigned int _reg, unsigned int _value);
+extern void lcd_vcbus_setb(unsigned int reg, unsigned int value,
+               unsigned int _start, unsigned int _len);
+extern unsigned int lcd_vcbus_getb(unsigned int reg,
+               unsigned int _start, unsigned int _len);
+extern void lcd_vcbus_set_mask(unsigned int reg, unsigned int _mask);
+extern void lcd_vcbus_clr_mask(unsigned int reg, unsigned int _mask);
+
+extern unsigned int lcd_hiu_read(unsigned int _reg);
+extern void lcd_hiu_write(unsigned int _reg, unsigned int _value);
+extern void lcd_hiu_setb(unsigned int _reg, unsigned int _value,
+               unsigned int _start, unsigned int _len);
+extern unsigned int lcd_hiu_getb(unsigned int _reg,
+               unsigned int _start, unsigned int _len);
+extern void lcd_hiu_set_mask(unsigned int _reg, unsigned int _mask);
+extern void lcd_hiu_clr_mask(unsigned int _reg, unsigned int _mask);
+
+extern unsigned int lcd_cbus_read(unsigned int _reg);
+extern void lcd_cbus_write(unsigned int _reg, unsigned int _value);
+extern void lcd_cbus_setb(unsigned int _reg, unsigned int _value,
+               unsigned int _start, unsigned int _len);
+
+extern unsigned int lcd_periphs_read(unsigned int _reg);
+extern void lcd_periphs_write(unsigned int _reg, unsigned int _value);
+extern void lcd_pinmux_set_mask(unsigned int _reg, unsigned int _mask);
+extern void lcd_pinmux_clr_mask(unsigned int _reg, unsigned int _mask);
+
+extern unsigned int dsi_host_read(unsigned int _reg);
+extern void dsi_host_write(unsigned int _reg, unsigned int _value);
+extern void dsi_host_setb(unsigned int reg, unsigned int value,
+        unsigned int _start, unsigned int _len);
+extern unsigned int dsi_host_getb(unsigned int reg,
+        unsigned int _start, unsigned int _len);
+extern void dsi_host_set_mask(unsigned int reg, unsigned int _mask);
+extern void dsi_host_clr_mask(unsigned int reg, unsigned int _mask);
+extern unsigned int dsi_phy_read(unsigned int _reg);
+extern void dsi_phy_write(unsigned int _reg, unsigned int _value);
+extern void dsi_phy_setb(unsigned int reg, unsigned int value,
+        unsigned int _start, unsigned int _len);
+extern unsigned int dsi_phy_getb(unsigned int reg,
+        unsigned int _start, unsigned int _len);
+extern void dsi_phy_set_mask(unsigned int reg, unsigned int _mask);
+extern void dsi_phy_clr_mask(unsigned int reg, unsigned int _mask);
+
+#endif
+
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_tablet/Kconfig b/drivers/amlogic/media/vout/lcd/lcd_tablet/Kconfig
new file mode 100644 (file)
index 0000000..729a0fa
--- /dev/null
@@ -0,0 +1,6 @@
+
+config AMLOGIC_LCD_GAMMA_DEBUG
+       bool "LCD Gamma debug support"
+       default n
+       help
+               Amlogic LCD gamma debug
diff --git a/drivers/amlogic/media/vout/lcd/lcd_tablet/Makefile b/drivers/amlogic/media/vout/lcd/lcd_tablet/Makefile
new file mode 100644 (file)
index 0000000..9e2a579
--- /dev/null
@@ -0,0 +1 @@
+obj-y += lcd_tablet.o lcd_drv.o mipi_dsi_util.o
\ No newline at end of file
diff --git a/drivers/amlogic/media/vout/lcd/lcd_tablet/aml_lcd.dts b/drivers/amlogic/media/vout/lcd/lcd_tablet/aml_lcd.dts
new file mode 100644 (file)
index 0000000..4c88d06
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tablet/aml_lcd.dts
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+lcd{
+       compatible = "amlogic, lcd";
+       dev_name = "lcd";
+       mode = "tablet";
+       status = "okay";
+       resets = <&clock GCLK_IDX_VCLK2_ENCL &clock GCLK_IDX_VCLK2_VENCL>;
+       reset-names = "encl","vencl";
+       pinctrl-names = "ttl_6bit_hvsync_de_on","ttl_6bit_hvsync_on",
+               "ttl_6bit_de_on","ttl_8bit_hvsync_de_on",
+               "ttl_8bit_hvsync_on","ttl_8bit_de_on",
+               "ttl_6bit_hvsync_de_off","ttl_8bit_hvsync_de_off";
+       pinctrl-0 = <&lcd_ttl_rgb_6bit_pins_on &lcd_ttl_de_hvsync_on_pins>;
+       pinctrl-1 = <&lcd_ttl_rgb_6bit_pins_on &lcd_ttl_hvsync_on_pins>;
+       pinctrl-2 = <&lcd_ttl_rgb_6bit_pins_on &lcd_ttl_de_on_pins>;
+       pinctrl-3 = <&lcd_ttl_rgb_8bit_pins_on &lcd_ttl_de_hvsync_on_pins>;
+       pinctrl-4 = <&lcd_ttl_rgb_8bit_pins_on &lcd_ttl_hvsync_on_pins>;
+       pinctrl-5 = <&lcd_ttl_rgb_8bit_pins_on &lcd_ttl_de_on_pins>;
+       pinctrl-6 = <&lcd_ttl_rgb_6bit_pins_off &lcd_ttl_de_hvsync_off_pins>;
+       pinctrl-7 = <&lcd_ttl_rgb_8bit_pins_off &lcd_ttl_de_hvsync_off_pins>;
+
+       /* power type:(0=cpu_gpio, 1=pmu_gpio,
+        * 2=signal, 3=extern, 0xff=ending)
+        */
+       /* power index:(point gpios_index, or extern_index, 0xff=invalid) */
+       /* power value:(0=output low, 1=output high, 2=input) */
+       /* power delay:(unit in ms) */
+       lcd_cpu-gpios = <&gpio GPIOX_3 GPIO_ACTIVE_HIGH>;
+       lcd_cpu_gpio_names = "GPIOX_3";
+
+       lcd_0{
+               model_name = "LCD720P";
+               interface = "ttl"; /* lcd_interface(ttl, lvds, mipi, edp) */
+               basic_setting = <1280 720 1650 750 8 16 9>;
+                       /* h_active, v_active, h_period, v_period, lcd_bits,
+                        * screen_widht, screen_height
+                        */
+               lcd_timing = <40 220 1 5 20 1>;
+                       /* hs_width, hs_bp, hs_pol, vs_width, vs_bp, vs_pol */
+               clk_attr = <0 0 1 74250000>;
+                       /* fr_adj_type(0=clock, 1=htotal, 2=vtotal),
+                        * clk_ss_level, clk_auto_generate,
+                        * pixel_clk(unit in Hz)
+                        */
+               ttl_attr = <0 1 1 0 0>;
+                       /* clk_pol, de_valid, hvsync_valid,
+                        * rb_swap, bit_swap
+                        */
+               power_on_step = <0 0 1 50
+                               2 0 0 0
+                               0xff 0 0 0>; /* type, index, value, delay */
+               power_off_step = <2 0 0 50
+                               0 0 0 100
+                               0xff 0 0 0>; /* type, index, value, delay */
+               backlight_index = <0>;
+       };
+
+       lcd_1{
+               model_name = "HJ080IA";
+               interface = "lvds"; /* lcd_interface(ttl, lvds, mipi, edp) */
+               basic_setting = <1024 768 1344 806 8 162 122>;
+                       /* h_active, v_active, h_period, v_period, lcd_bits,
+                        *      screen_widht, screen_height
+                        */
+               lcd_timing = <10 60 0 2 18 0>;
+                       /* hs_width, hs_bp, hs_pol, vs_width, vs_bp, vs_pol */
+               clk_attr = <0 0 1 65000000>;
+                       /* fr_adj_type(0=clock, 1=htotal, 2=vtotal),
+                        * clk_ss_level, clk_auto_generate,
+                        * pixel_clk(unit in Hz)
+                        */
+               lvds_attr = <1 0 0 0>;
+                       /* lvds_repack, dual_port, pn_swap, port_swap */
+               power_on_step = <0 0 1 50
+                               2 0 0 0
+                               0xff 0 0 0>; /* type, index, value, delay */
+               power_off_step = <2 0 0 50
+                               0 0 0 100
+                               0xff 0 0 0>; /* type, index, value, delay */
+               backlight_index = <0>;
+       };
+
+       /* operation_mode:(bit[0] for init, bit[1] for display.
+        *0=video mode, 1=command mode)
+        */
+       /* lp_clk_continuous:(0=disable, 1=enable) */
+       /* transfer_switch:(0=auto, 1=standard, 2=slow) */
+       /* clk_factor:(special adjust between pixel_clk
+        * & lanebyte_clk, default 0)
+        */
+       /* mipi-dsi command:(data_type, num, data....).
+        *      data_type=0xff, num=0xff means ending
+        *                      num<0xff means delay num(unit: ms)
+        */
+       lcd_2{
+               model_name = "B080XAN01";
+               interface = "mipi"; /* lcd_interface(ttl, lvds, mipi, edp) */
+               basic_setting = <768 1024 948 1140 8 119 159>;
+                       /* h_active, v_active, h_period, v_period, lcd_bits,
+                        *      screen_widht, screen_height
+                        */
+               lcd_timing = <64 56 0 50 30 0>;
+                       /* hs_width, hs_bp, hs_pol, vs_width, vs_bp, vs_pol */
+               clk_attr = <0 0 1 64843200>;
+                       /* fr_adj_type(0=clock, 1=htotal, 2=vtotal),
+                        * clk_ss_level, clk_auto_generate,
+                        * pixel_clk(unit in Hz)
+                        */
+               mipi_attr = <4 0x1 1 0 1 1 550>;
+                       /* lane_count, operation_mode, lp_clk_continuous,
+                        * transfer_switch, factor_denominator,
+                        * factor_numerator, dsi_bit_rate_max (MHz)
+                        */
+               dsi_cmd_init_on = <0x05 1 0x11 /* sleep out */
+                               0xff 20     /* delay 20ms */
+                               0x05 1 0x29 /* display on */
+                               0xff 20     /* delay 20ms */
+                               0xff 0xff>; /* ending flag */
+               dsi_cmd_init_off = <0x05 1 0x28 /* display off */
+                               0xff 10     /* delay 10ms */
+                               0x05 1 0x10 /* sleep in */
+                               0xff 10     /* delay 10ms */
+                               0xff 0xff>; /* ending flag */
+               power_on_step = <0 0 1 50
+                               2 0 0 0
+                               0xff 0 0 0>; /* type, index, value, delay */
+               power_off_step = <2 0 0 50
+                               0 0 0 100
+                               0xff 0 0 0>; /* type, index, value, delay */
+               backlight_index = <0>;
+       };
+
+       /* link_rate:(0=1.62G, 1=2.7G, 0xff=auto setting) */
+       /* lane_count:(support 1,2,4, 0xff=auto setting) */
+       /* link_vswing:(support level 0,1,2,3, 0xff=adaptive) */
+       /* sync_clk_mode:(0=asyncronous clock,
+        * 1=synchronous clock. default 1)
+        */
+       /* edid_timing_used:(0=no use, 1=use) */
+       lcd_3{
+               model_name = "LP097QX1";
+               interface = "edp"; /* lcd_interface(ttl, lvds, mipi, edp) */
+               basic_setting = <2048 1536 2219 1560 8 197 147>;
+                       /* h_active, v_active, h_period, v_period, lcd_bits,
+                        *      screen_widht, screen_height
+                        */
+               lcd_timing = <5 115 0 1 21 0>;
+                       /* hs_width, hs_bp, hs_pol, vs_width, vs_bp, vs_pol */
+               clk_attr = <0 0 1 207700000>;
+                       /* fr_adj_type(0=clock, 1=htotal, 2=vtotal),
+                        * clk_ss_level, clk_auto_generate,
+                        * pixel_clk(unit in Hz)
+                        */
+               edp_attr = <4 1 2 0 1 0>;
+                       /* max_lane_count, link_rate, lane_count, link_vswing,
+                        *      sync_clk_mode, edid_timing_used
+                        */
+               power_on_step = <0 0 1 50
+                               2 0 0 0
+                               0xff 0 0 0>; /* type, index, value, delay */
+               power_off_step = <2 0 0 50
+                               0 0 0 100
+                               0xff 0 0 0>; /* type, index, value, delay */
+               backlight_index = <0>;
+       };
+};
diff --git a/drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_drv.c b/drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_drv.c
new file mode 100644 (file)
index 0000000..183dc1f
--- /dev/null
@@ -0,0 +1,1043 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_drv.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#ifdef CONFIG_AML_VPU
+#include <linux/amlogic/vpu.h>
+#endif
+#include <linux/amlogic/media/vout/vinfo.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include "lcd_tablet.h"
+#include "mipi_dsi_util.h"
+#include "../lcd_reg.h"
+#include "../lcd_common.h"
+
+static int lcd_type_supported(struct lcd_config_s *pconf)
+{
+       int lcd_type = pconf->lcd_basic.lcd_type;
+       int ret = -1;
+
+       switch (lcd_type) {
+       case LCD_TTL:
+       case LCD_LVDS:
+       case LCD_VBYONE:
+       case LCD_MIPI:
+               ret = 0;
+               break;
+       default:
+               LCDERR("invalid lcd type: %s(%d)\n",
+                       lcd_type_type_to_str(lcd_type), lcd_type);
+               break;
+       }
+       return ret;
+}
+
+static void lcd_lvds_phy_set(struct lcd_config_s *pconf, int status)
+{
+       unsigned int vswing, preem, clk_vswing, clk_preem, channel_on;
+       unsigned int data32;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s: %d\n", __func__, status);
+
+       if (status) {
+               vswing = pconf->lcd_control.lvds_config->phy_vswing;
+               preem = pconf->lcd_control.lvds_config->phy_preem;
+               clk_vswing = pconf->lcd_control.lvds_config->phy_clk_vswing;
+               clk_preem = pconf->lcd_control.lvds_config->phy_clk_preem;
+               if (vswing > 7) {
+                       LCDERR("%s: wrong vswing_level=0x%x, use default\n",
+                               __func__, vswing);
+                       vswing = LVDS_PHY_VSWING_DFT;
+               }
+               if (preem > 7) {
+                       LCDERR("%s: wrong preem_level=0x%x, use default\n",
+                               __func__, preem);
+                       preem = LVDS_PHY_PREEM_DFT;
+               }
+               if (clk_vswing > 7) {
+                       LCDERR("%s: wrong clk_vswing_level=0x%x, use default\n",
+                               __func__, clk_vswing);
+                       clk_vswing = LVDS_PHY_CLK_VSWING_DFT;
+               }
+               if (clk_preem > 7) {
+                       LCDERR("%s: wrong clk_preem_level=0x%x, use default\n",
+                               __func__, clk_preem);
+                       clk_preem = LVDS_PHY_CLK_PREEM_DFT;
+               }
+               channel_on = lcd_lvds_channel_on_value(pconf);
+
+               data32 = 0x606cca80 | (vswing << 26) | (preem << 0);
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, data32);
+               /*lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x6c6cca80);*/
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x0000006c);
+               data32 = (channel_on << 16) | 0x0800 | /* DIF_TX_CTL5 */
+                       (clk_vswing << 8) | (clk_preem << 5); /* DIF_TX_CTL4 */
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, data32);
+               /*lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x0fff0800);*/
+       } else {
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x0);
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x0);
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x0);
+       }
+}
+
+static void lcd_vbyone_phy_set(struct lcd_config_s *pconf, int status)
+{
+       unsigned int vswing, preem, ext_pullup;
+       unsigned int data32;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s: %d\n", __func__, status);
+
+       if (status) {
+               ext_pullup =
+                       (pconf->lcd_control.vbyone_config->phy_vswing >> 4) & 1;
+               vswing = pconf->lcd_control.vbyone_config->phy_vswing & 0xf;
+               preem = pconf->lcd_control.vbyone_config->phy_preem;
+               if (vswing > 7) {
+                       LCDERR("%s: wrong vswing_level=%d, use default\n",
+                               __func__, vswing);
+                       vswing = VX1_PHY_VSWING_DFT;
+               }
+               if (preem > 7) {
+                       LCDERR("%s: wrong preemphasis_level=%d, use default\n",
+                               __func__, preem);
+                       preem = VX1_PHY_PREEM_DFT;
+               }
+               data32 = 0x6e0ec900 | (vswing << 3) | (ext_pullup << 10);
+               if (ext_pullup)
+                       data32 &= ~(1 << 15);
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, data32);
+               data32 = 0x00000a7c | (preem << 20);
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, data32);
+               /*lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x6e0ec918);
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x00000a7c);*/
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x00ff0800);
+       } else {
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x0);
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x0);
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x0);
+       }
+}
+
+static void lcd_mipi_phy_set(struct lcd_config_s *pconf, int status)
+{
+       unsigned int phy_reg, phy_bit, phy_width;
+       unsigned int lane_cnt;
+
+       if (status) {
+               /* HHI_MIPI_CNTL0 */
+               /* DIF_REF_CTL1:31-16bit, DIF_REF_CTL0:15-0bit */
+               lcd_hiu_setb(HHI_MIPI_CNTL0, 0x1b8, 16, 10);
+               lcd_hiu_setb(HHI_MIPI_CNTL0, 1, 26, 1); /* bandgap */
+               lcd_hiu_setb(HHI_MIPI_CNTL0, 1, 29, 1); /* current */
+               lcd_hiu_setb(HHI_MIPI_CNTL0, 1, 31, 1);
+               lcd_hiu_setb(HHI_MIPI_CNTL0, 0x8, 0, 16);
+
+               /* HHI_MIPI_CNTL1 */
+               /* DIF_REF_CTL2:15-0bit */
+               lcd_hiu_write(HHI_MIPI_CNTL1, (0x001e << 0));
+
+               /* HHI_MIPI_CNTL2 */
+               /* DIF_TX_CTL1:31-16bit, DIF_TX_CTL0:15-0bit */
+               lcd_hiu_write(HHI_MIPI_CNTL2, (0x26e0 << 16) | (0xfc59 << 0));
+
+               phy_reg = HHI_MIPI_CNTL2;
+               phy_bit = BIT_PHY_LANE_AXG;
+               phy_width = PHY_LANE_WIDTH_AXG;
+               switch (pconf->lcd_control.mipi_config->lane_num) {
+               case 1:
+                       lane_cnt = DSI_LANE_COUNT_1;
+                       break;
+               case 2:
+                       lane_cnt = DSI_LANE_COUNT_2;
+                       break;
+               case 3:
+                       lane_cnt = DSI_LANE_COUNT_3;
+                       break;
+               case 4:
+                       lane_cnt = DSI_LANE_COUNT_4;
+                       break;
+               default:
+                       lane_cnt = 0;
+                       break;
+               }
+               lcd_hiu_setb(phy_reg, lane_cnt, phy_bit, phy_width);
+       } else {
+               lcd_hiu_setb(HHI_MIPI_CNTL0, 0, 16, 10);
+               lcd_hiu_setb(HHI_MIPI_CNTL0, 0, 31, 1);
+               lcd_hiu_setb(HHI_MIPI_CNTL0, 0, 0, 16);
+               lcd_hiu_write(HHI_MIPI_CNTL1, 0x6);
+               lcd_hiu_write(HHI_MIPI_CNTL2, 0x00200000);
+       }
+}
+
+static void lcd_tcon_set(struct lcd_config_s *pconf)
+{
+       struct lcd_timing_s *tcon_adr = &pconf->lcd_timing;
+
+       lcd_vcbus_write(L_RGB_BASE_ADDR, 0);
+       lcd_vcbus_write(L_RGB_COEFF_ADDR, 0x400);
+       aml_lcd_notifier_call_chain(LCD_EVENT_GAMMA_UPDATE, NULL);
+
+       switch (pconf->lcd_basic.lcd_bits) {
+       case 6:
+               lcd_vcbus_write(L_DITH_CNTL_ADDR,  0x600);
+               break;
+       case 8:
+               lcd_vcbus_write(L_DITH_CNTL_ADDR,  0x400);
+               break;
+       case 10:
+       default:
+               lcd_vcbus_write(L_DITH_CNTL_ADDR,  0x0);
+               break;
+       }
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_LVDS:
+               lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 0, 1);
+               if (pconf->lcd_timing.vsync_pol)
+                       lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 1, 1);
+               break;
+       case LCD_VBYONE:
+               if (pconf->lcd_timing.hsync_pol)
+                       lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 0, 1);
+               if (pconf->lcd_timing.vsync_pol)
+                       lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 1, 1);
+               break;
+       case LCD_MIPI:
+               /* lcd_vcbus_setb(L_POL_CNTL_ADDR, 3, 0, 2); */
+               break;
+       default:
+               break;
+       }
+
+       /* DE signal for TTL m8,m8m2 */
+       lcd_vcbus_write(L_OEH_HS_ADDR, tcon_adr->de_hs_addr);
+       lcd_vcbus_write(L_OEH_HE_ADDR, tcon_adr->de_he_addr);
+       lcd_vcbus_write(L_OEH_VS_ADDR, tcon_adr->de_vs_addr);
+       lcd_vcbus_write(L_OEH_VE_ADDR, tcon_adr->de_ve_addr);
+       /* DE signal for TTL m8b */
+       lcd_vcbus_write(L_OEV1_HS_ADDR,  tcon_adr->de_hs_addr);
+       lcd_vcbus_write(L_OEV1_HE_ADDR,  tcon_adr->de_he_addr);
+       lcd_vcbus_write(L_OEV1_VS_ADDR,  tcon_adr->de_vs_addr);
+       lcd_vcbus_write(L_OEV1_VE_ADDR,  tcon_adr->de_ve_addr);
+
+       /* Hsync signal for TTL m8,m8m2 */
+       if (tcon_adr->hsync_pol == 0) {
+               lcd_vcbus_write(L_STH1_HS_ADDR, tcon_adr->hs_he_addr);
+               lcd_vcbus_write(L_STH1_HE_ADDR, tcon_adr->hs_hs_addr);
+       } else {
+               lcd_vcbus_write(L_STH1_HS_ADDR, tcon_adr->hs_hs_addr);
+               lcd_vcbus_write(L_STH1_HE_ADDR, tcon_adr->hs_he_addr);
+       }
+       lcd_vcbus_write(L_STH1_VS_ADDR, tcon_adr->hs_vs_addr);
+       lcd_vcbus_write(L_STH1_VE_ADDR, tcon_adr->hs_ve_addr);
+
+       /* Vsync signal for TTL m8,m8m2 */
+       lcd_vcbus_write(L_STV1_HS_ADDR, tcon_adr->vs_hs_addr);
+       lcd_vcbus_write(L_STV1_HE_ADDR, tcon_adr->vs_he_addr);
+       if (tcon_adr->vsync_pol == 0) {
+               lcd_vcbus_write(L_STV1_VS_ADDR, tcon_adr->vs_ve_addr);
+               lcd_vcbus_write(L_STV1_VE_ADDR, tcon_adr->vs_vs_addr);
+       } else {
+               lcd_vcbus_write(L_STV1_VS_ADDR, tcon_adr->vs_vs_addr);
+               lcd_vcbus_write(L_STV1_VE_ADDR, tcon_adr->vs_ve_addr);
+       }
+
+       /* DE signal */
+       lcd_vcbus_write(L_DE_HS_ADDR,    tcon_adr->de_hs_addr);
+       lcd_vcbus_write(L_DE_HE_ADDR,    tcon_adr->de_he_addr);
+       lcd_vcbus_write(L_DE_VS_ADDR,    tcon_adr->de_vs_addr);
+       lcd_vcbus_write(L_DE_VE_ADDR,    tcon_adr->de_ve_addr);
+
+       /* Hsync signal */
+       lcd_vcbus_write(L_HSYNC_HS_ADDR,  tcon_adr->hs_hs_addr);
+       lcd_vcbus_write(L_HSYNC_HE_ADDR,  tcon_adr->hs_he_addr);
+       lcd_vcbus_write(L_HSYNC_VS_ADDR,  tcon_adr->hs_vs_addr);
+       lcd_vcbus_write(L_HSYNC_VE_ADDR,  tcon_adr->hs_ve_addr);
+
+       /* Vsync signal */
+       lcd_vcbus_write(L_VSYNC_HS_ADDR,  tcon_adr->vs_hs_addr);
+       lcd_vcbus_write(L_VSYNC_HE_ADDR,  tcon_adr->vs_he_addr);
+       lcd_vcbus_write(L_VSYNC_VS_ADDR,  tcon_adr->vs_vs_addr);
+       lcd_vcbus_write(L_VSYNC_VE_ADDR,  tcon_adr->vs_ve_addr);
+
+       lcd_vcbus_write(L_INV_CNT_ADDR, 0);
+       lcd_vcbus_write(L_TCON_MISC_SEL_ADDR,
+               ((1 << STV1_SEL) | (1 << STV2_SEL)));
+
+       if (lcd_vcbus_read(VPP_MISC) & VPP_OUT_SATURATE)
+               lcd_vcbus_write(VPP_MISC,
+                       lcd_vcbus_read(VPP_MISC) & ~(VPP_OUT_SATURATE));
+}
+
+static void lcd_ttl_control_set(struct lcd_config_s *pconf)
+{
+       unsigned int clk_pol, rb_swap, bit_swap;
+
+       clk_pol = pconf->lcd_control.ttl_config->clk_pol;
+       rb_swap = (pconf->lcd_control.ttl_config->swap_ctrl >> 1) & 1;
+       bit_swap = (pconf->lcd_control.ttl_config->swap_ctrl >> 0) & 1;
+
+       lcd_vcbus_setb(L_POL_CNTL_ADDR, clk_pol, 6, 1);
+       lcd_vcbus_setb(L_DUAL_PORT_CNTL_ADDR, rb_swap, 1, 1);
+       lcd_vcbus_setb(L_DUAL_PORT_CNTL_ADDR, bit_swap, 0, 1);
+}
+
+static void lcd_lvds_clk_util_set(struct lcd_config_s *pconf)
+{
+       unsigned int phy_div;
+
+       if (pconf->lcd_control.lvds_config->dual_port)
+               phy_div = 2;
+       else
+               phy_div = 1;
+
+       /* set fifo_clk_sel: div 7 */
+       lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL0, (1 << 6));
+       /* set cntl_ser_en:  8-channel to 1 */
+       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0xfff, 16, 12);
+
+       /* decoupling fifo enable, gated clock enable */
+       lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL1,
+               (1 << 30) | ((phy_div - 1) << 25) | (1 << 24));
+       /* decoupling fifo write enable after fifo enable */
+       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL1, 1, 31, 1);
+}
+
+static void lcd_lvds_control_set(struct lcd_config_s *pconf)
+{
+       unsigned int bit_num = 1;
+       unsigned int pn_swap, port_swap, lane_reverse;
+       unsigned int dual_port, fifo_mode;
+       unsigned int lvds_repack = 1;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       lcd_lvds_clk_util_set(pconf);
+
+       lvds_repack = (pconf->lcd_control.lvds_config->lvds_repack) & 0x3;
+       pn_swap   = (pconf->lcd_control.lvds_config->pn_swap) & 0x1;
+       dual_port = (pconf->lcd_control.lvds_config->dual_port) & 0x1;
+       port_swap = (pconf->lcd_control.lvds_config->port_swap) & 0x1;
+       lane_reverse = (pconf->lcd_control.lvds_config->lane_reverse) & 0x1;
+
+       switch (pconf->lcd_basic.lcd_bits) {
+       case 10:
+               bit_num = 0;
+               if (lvds_repack == 1)
+                       lvds_repack = 2;
+               break;
+       case 8:
+               bit_num = 1;
+               break;
+       case 6:
+               bit_num = 2;
+               break;
+       case 4:
+               bit_num = 3;
+               break;
+       default:
+               bit_num = 1;
+               break;
+       }
+       if (dual_port)
+               fifo_mode = 0x3;
+       else
+               fifo_mode = 0x1;
+
+       lcd_vcbus_write(LVDS_PACK_CNTL_ADDR,
+               (lvds_repack << 0) | /* repack //[1:0] */
+               (0 << 3) |      /* reserve */
+               (0 << 4) |      /* lsb first */
+               (pn_swap << 5) | /* pn swap */
+               (dual_port << 6) | /* dual port */
+               (0 << 7) |      /* use tcon control */
+               (bit_num << 8) | /* 0:10bits, 1:8bits, 2:6bits, 3:4bits */
+               (0 << 10) |     /* r_select  //0:R, 1:G, 2:B, 3:0 */
+               (1 << 12) |     /* g_select  //0:R, 1:G, 2:B, 3:0 */
+               (2 << 14));     /* b_select  //0:R, 1:G, 2:B, 3:0 */
+
+       lcd_vcbus_setb(LCD_PORT_SWAP, port_swap, 12, 1);
+
+       if (lane_reverse)
+               lcd_vcbus_setb(LVDS_GEN_CNTL, 0x03, 13, 2);
+
+       lcd_vcbus_write(LVDS_GEN_CNTL,
+               (lcd_vcbus_read(LVDS_GEN_CNTL) |
+               (1 << 4) | (fifo_mode << 0)));
+
+       lcd_vcbus_setb(LVDS_GEN_CNTL, 1, 3, 1);
+}
+
+static void lcd_lvds_disable(void)
+{
+       lcd_vcbus_setb(LVDS_GEN_CNTL, 0, 3, 1); /* disable lvds fifo */
+}
+
+static void lcd_venc_set(struct lcd_config_s *pconf)
+{
+       unsigned int h_active, v_active;
+       unsigned int video_on_pixel, video_on_line;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+       h_active = pconf->lcd_basic.h_active;
+       v_active = pconf->lcd_basic.v_active;
+       video_on_pixel = pconf->lcd_timing.video_on_pixel;
+       video_on_line = pconf->lcd_timing.video_on_line;
+
+       lcd_vcbus_write(ENCL_VIDEO_EN, 0);
+
+       lcd_vcbus_write(VPU_VIU_VENC_MUX_CTRL, ((0 << 0) | (0 << 2)));
+       lcd_vcbus_write(ENCL_VIDEO_MODE, 0);
+       lcd_vcbus_write(ENCL_VIDEO_MODE_ADV, 0x0418); /* Sampling rate: 1 */
+
+       lcd_vcbus_write(ENCL_VIDEO_FILT_CTRL, 0x1000); /* bypass filter */
+       lcd_vcbus_write(ENCL_VIDEO_MAX_PXCNT, pconf->lcd_basic.h_period - 1);
+       lcd_vcbus_write(ENCL_VIDEO_MAX_LNCNT, pconf->lcd_basic.v_period - 1);
+       lcd_vcbus_write(ENCL_VIDEO_HAVON_BEGIN, video_on_pixel);
+       lcd_vcbus_write(ENCL_VIDEO_HAVON_END,   h_active - 1 + video_on_pixel);
+       lcd_vcbus_write(ENCL_VIDEO_VAVON_BLINE, video_on_line);
+       lcd_vcbus_write(ENCL_VIDEO_VAVON_ELINE, v_active - 1  + video_on_line);
+
+       lcd_vcbus_write(ENCL_VIDEO_HSO_BEGIN, pconf->lcd_timing.hs_hs_addr);
+       lcd_vcbus_write(ENCL_VIDEO_HSO_END,   pconf->lcd_timing.hs_he_addr);
+       lcd_vcbus_write(ENCL_VIDEO_VSO_BEGIN, pconf->lcd_timing.vs_hs_addr);
+       lcd_vcbus_write(ENCL_VIDEO_VSO_END,   pconf->lcd_timing.vs_he_addr);
+       lcd_vcbus_write(ENCL_VIDEO_VSO_BLINE, pconf->lcd_timing.vs_vs_addr);
+       lcd_vcbus_write(ENCL_VIDEO_VSO_ELINE, pconf->lcd_timing.vs_ve_addr);
+       lcd_vcbus_write(ENCL_VIDEO_RGBIN_CTRL, 3);
+
+       lcd_vcbus_write(ENCL_VIDEO_EN, 1);
+
+       aml_lcd_notifier_call_chain(LCD_EVENT_BACKLIGHT_UPDATE, NULL);
+}
+
+static void lcd_vbyone_sync_pol(int hsync_pol, int vsync_pol)
+{
+       lcd_vcbus_setb(VBO_VIN_CTRL, hsync_pol, 4, 1);
+       lcd_vcbus_setb(VBO_VIN_CTRL, vsync_pol, 5, 1);
+
+       lcd_vcbus_setb(VBO_VIN_CTRL, hsync_pol, 6, 1);
+       lcd_vcbus_setb(VBO_VIN_CTRL, vsync_pol, 7, 1);
+}
+
+static void lcd_vbyone_clk_util_set(struct lcd_config_s *pconf)
+{
+       unsigned int lcd_bits;
+       unsigned int div_sel, phy_div;
+
+       phy_div = pconf->lcd_control.vbyone_config->phy_div;
+
+       lcd_bits = 10;
+       switch (lcd_bits) {
+       case 6:
+               div_sel = 0;
+               break;
+       case 8:
+               div_sel = 2;
+               break;
+       case 10:
+               div_sel = 3;
+               break;
+       default:
+               div_sel = 3;
+               break;
+       }
+       /* set fifo_clk_sel */
+       lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL0, (div_sel << 6));
+       /* set cntl_ser_en:  8-channel to 1 */
+       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0xfff, 16, 12);
+
+       /* decoupling fifo enable, gated clock enable */
+       lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL1,
+               (1 << 30) | ((phy_div - 1) << 25) | (1 << 24));
+       /* decoupling fifo write enable after fifo enable */
+       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL1, 1, 31, 1);
+}
+
+static int lcd_vbyone_lanes_set(int lane_num, int byte_mode, int region_num,
+               int hsize, int vsize)
+{
+       int sublane_num;
+       int region_size[4];
+       int tmp;
+
+       switch (lane_num) {
+       case 1:
+       case 2:
+       case 4:
+       case 8:
+               break;
+       default:
+               return -1;
+       }
+       switch (region_num) {
+       case 1:
+       case 2:
+       case 4:
+               break;
+       default:
+               return -1;
+       }
+       if (lane_num % region_num)
+               return -1;
+       switch (byte_mode) {
+       case 3:
+       case 4:
+               break;
+       default:
+               return -1;
+       }
+       if (lcd_debug_print_flag) {
+               LCDPR("byte_mode=%d, lane_num=%d, region_num=%d\n",
+                       byte_mode, lane_num, region_num);
+       }
+
+       sublane_num = lane_num / region_num; /* lane num in each region */
+       lcd_vcbus_setb(VBO_LANES, (lane_num - 1), 0, 3);
+       lcd_vcbus_setb(VBO_LANES, (region_num - 1), 4, 2);
+       lcd_vcbus_setb(VBO_LANES, (sublane_num - 1), 8, 3);
+       lcd_vcbus_setb(VBO_LANES, (byte_mode - 1), 11, 2);
+
+       if (region_num > 1) {
+               region_size[3] = (hsize / lane_num) * sublane_num;
+               tmp = (hsize % lane_num);
+               region_size[0] = region_size[3] + (((tmp / sublane_num) > 0) ?
+                       sublane_num : (tmp % sublane_num));
+               region_size[1] = region_size[3] + (((tmp / sublane_num) > 1) ?
+                       sublane_num : (tmp % sublane_num));
+               region_size[2] = region_size[3] + (((tmp / sublane_num) > 2) ?
+                       sublane_num : (tmp % sublane_num));
+               lcd_vcbus_write(VBO_REGION_00, region_size[0]);
+               lcd_vcbus_write(VBO_REGION_01, region_size[1]);
+               lcd_vcbus_write(VBO_REGION_02, region_size[2]);
+               lcd_vcbus_write(VBO_REGION_03, region_size[3]);
+       }
+       lcd_vcbus_write(VBO_ACT_VSIZE, vsize);
+       /* different from FBC code!!! */
+       /* lcd_vcbus_setb(VBO_CTRL_H,0x80,11,5); */
+       /* different from simulation code!!! */
+       lcd_vcbus_setb(VBO_CTRL_H, 0x0, 0, 4);
+       lcd_vcbus_setb(VBO_CTRL_H, 0x1, 9, 1);
+       /* lcd_vcbus_setb(VBO_CTRL_L,enable,0,1); */
+
+       return 0;
+}
+
+static void lcd_vbyone_sw_reset(void)
+{
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       /* force PHY to 0 */
+       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2);
+       lcd_vcbus_write(VBO_SOFT_RST, 0x1ff);
+       udelay(5);
+       /* realease PHY */
+       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2);
+       lcd_vcbus_write(VBO_SOFT_RST, 0);
+}
+
+static void lcd_vbyone_wait_timing_stable(void)
+{
+       unsigned int timing_state;
+       int i = 200;
+
+       timing_state = lcd_vcbus_read(VBO_INTR_STATE) & 0x1ff;
+       while ((timing_state) && (i > 0)) {
+               /* clear video timing error intr */
+               lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x7, 0, 3);
+               lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 3);
+               mdelay(2);
+               timing_state = lcd_vcbus_read(VBO_INTR_STATE) & 0x1ff;
+               i--;
+       };
+       if (lcd_debug_print_flag) {
+               LCDPR("vbyone timing state: 0x%03x, i=%d\n",
+                       timing_state, (200 - i));
+       }
+       mdelay(2);
+}
+
+static void lcd_vbyone_control_set(struct lcd_config_s *pconf)
+{
+       int lane_count, byte_mode, region_num, hsize, vsize, color_fmt;
+       int vin_color, vin_bpp;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       hsize = pconf->lcd_basic.h_active;
+       vsize = pconf->lcd_basic.v_active;
+       lane_count = pconf->lcd_control.vbyone_config->lane_count; /* 8 */
+       region_num = pconf->lcd_control.vbyone_config->region_num; /* 2 */
+       byte_mode = pconf->lcd_control.vbyone_config->byte_mode; /* 4 */
+       color_fmt = pconf->lcd_control.vbyone_config->color_fmt; /* 4 */
+
+       lcd_vbyone_clk_util_set(pconf);
+#if 0
+       switch (color_fmt) {
+       case 0:/* SDVT_VBYONE_18BPP_RGB */
+               vin_color = 4;
+               vin_bpp   = 2;
+               break;
+       case 1:/* SDVT_VBYONE_18BPP_YCBCR444 */
+               vin_color = 0;
+               vin_bpp   = 2;
+               break;
+       case 2:/* SDVT_VBYONE_24BPP_RGB */
+               vin_color = 4;
+               vin_bpp   = 1;
+               break;
+       case 3:/* SDVT_VBYONE_24BPP_YCBCR444 */
+               vin_color = 0;
+               vin_bpp   = 1;
+               break;
+       case 4:/* SDVT_VBYONE_30BPP_RGB */
+               vin_color = 4;
+               vin_bpp   = 0;
+               break;
+       case 5:/* SDVT_VBYONE_30BPP_YCBCR444 */
+               vin_color = 0;
+               vin_bpp   = 0;
+               break;
+       default:
+               LCDERR("vbyone COLOR_FORMAT unsupport\n");
+               return;
+       }
+#else
+       vin_color = 4; /* fixed RGB */
+       vin_bpp   = 0; /* fixed 30bbp 4:4:4 */
+#endif
+
+       /* set Vbyone vin color format */
+       lcd_vcbus_setb(VBO_VIN_CTRL, vin_color, 8, 3);
+       lcd_vcbus_setb(VBO_VIN_CTRL, vin_bpp, 11, 2);
+
+       lcd_vbyone_lanes_set(lane_count, byte_mode, region_num, hsize, vsize);
+       /*set hsync/vsync polarity to let the polarity is low active
+       inside the VbyOne */
+       lcd_vbyone_sync_pol(0, 0);
+
+       /* below line copy from simulation */
+       /* gate the input when vsync asserted */
+       lcd_vcbus_setb(VBO_VIN_CTRL, 1, 0, 2);
+       /* lcd_vcbus_write(VBO_VBK_CTRL_0,0x13);
+       //lcd_vcbus_write(VBO_VBK_CTRL_1,0x56);
+       //lcd_vcbus_write(VBO_HBK_CTRL,0x3478);
+       //lcd_vcbus_setb(VBO_PXL_CTRL,0x2,0,4);
+       //lcd_vcbus_setb(VBO_PXL_CTRL,0x3,VBO_PXL_CTR1_BIT,VBO_PXL_CTR1_WID);
+       //set_vbyone_ctlbits(1,0,0); */
+
+       /* PAD select: */
+       if ((lane_count == 1) || (lane_count == 2))
+               lcd_vcbus_setb(LCD_PORT_SWAP, 1, 9, 2);
+       else if (lane_count == 4)
+               lcd_vcbus_setb(LCD_PORT_SWAP, 2, 9, 2);
+       else
+               lcd_vcbus_setb(LCD_PORT_SWAP, 0, 9, 2);
+       /* lcd_vcbus_setb(LCD_PORT_SWAP, 1, 8, 1);//reverse lane output order */
+
+       /* Mux pads in combo-phy: 0 for dsi; 1 for lvds or vbyone; 2 for edp */
+       lcd_hiu_write(HHI_DSI_LVDS_EDP_CNTL0, 0x1);
+       lcd_vcbus_setb(VBO_CTRL_L, 1, 0, 1);
+
+       /*force vencl clk enable, otherwise, it might auto turn off by mipi DSI
+       //lcd_vcbus_setb(VPU_MISC_CTRL, 1, 0, 1); */
+
+       lcd_vbyone_wait_timing_stable();
+       lcd_vbyone_sw_reset();
+}
+
+static void lcd_vbyone_disable(void)
+{
+       lcd_vcbus_setb(VBO_CTRL_L, 0, 0, 1);
+}
+
+static void lcd_tablet_vbyone_wait_stable(void)
+{
+       int i = 5000;
+
+       while (((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) != 0x20) && (i > 0)) {
+               udelay(50);
+               i--;
+       }
+       LCDPR("%s status: 0x%x, i=%d\n",
+               __func__, lcd_vcbus_read(VBO_STATUS_L), (5000 - i));
+}
+
+static void lcd_vx1_wait_hpd(void)
+{
+       int i = 0;
+
+       if (lcd_debug_print_flag)
+               LCDPR("vx1 wait hpd to low ...\n");
+
+       while (lcd_vcbus_read(VBO_STATUS_L) & 0x40) {
+               if (i++ >= 10000)
+                       break;
+               udelay(50);
+       }
+       mdelay(10);
+       if (lcd_vcbus_read(VBO_STATUS_L) & 0x40)
+               LCDPR("%s: hpd=%d\n", __func__,
+                       ((lcd_vcbus_read(VBO_STATUS_L) >> 6) & 0x1));
+       else
+               LCDPR("%s: hpd=%d, i=%d\n", __func__,
+                       ((lcd_vcbus_read(VBO_STATUS_L) >> 6) & 0x1), i);
+}
+
+static unsigned int vbyone_lane_num[] = {
+       1,
+       2,
+       4,
+       8,
+       8,
+};
+
+#define VBYONE_BIT_RATE_MAX            3100 /* MHz */
+#define VBYONE_BIT_RATE_MIN            600
+static void lcd_vbyone_config_set(struct lcd_config_s *pconf)
+{
+       unsigned int band_width, bit_rate, pclk, phy_div;
+       unsigned int byte_mode, lane_count, minlane;
+       unsigned int lcd_bits;
+       unsigned int temp, i;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       /* auto calculate bandwidth, clock */
+       lane_count = pconf->lcd_control.vbyone_config->lane_count;
+       lcd_bits = 10; /* pconf->lcd_basic.lcd_bits */
+       byte_mode = (lcd_bits == 10) ? 4 : 3;
+       /* byte_mode * byte2bit * 8/10_encoding * pclk =
+          byte_mode * 8 * 10 / 8 * pclk */
+       pclk = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+       band_width = byte_mode * 10 * pclk;
+
+       temp = VBYONE_BIT_RATE_MAX * 1000;
+       temp = (band_width + temp - 1) / temp;
+       for (i = 0; i < 4; i++) {
+               if (temp <= vbyone_lane_num[i])
+                       break;
+       }
+       minlane = vbyone_lane_num[i];
+       if (lane_count < minlane) {
+               LCDERR("vbyone lane_num(%d) is less than min(%d)\n",
+                       lane_count, minlane);
+               lane_count = minlane;
+               pconf->lcd_control.vbyone_config->lane_count = lane_count;
+               LCDPR("change to min lane_num %d\n", minlane);
+       }
+
+       bit_rate = band_width / minlane;
+       phy_div = lane_count / minlane;
+       if (phy_div == 8) {
+               phy_div /= 2;
+               bit_rate /= 2;
+       }
+       if (bit_rate > (VBYONE_BIT_RATE_MAX * 1000)) {
+               LCDERR("vbyone bit rate(%dKHz) is out of max(%dKHz)\n",
+                       bit_rate, (VBYONE_BIT_RATE_MAX * 1000));
+       }
+       if (bit_rate < (VBYONE_BIT_RATE_MIN * 1000)) {
+               LCDERR("vbyone bit rate(%dKHz) is out of min(%dKHz)\n",
+                       bit_rate, (VBYONE_BIT_RATE_MIN * 1000));
+       }
+       bit_rate = bit_rate * 1000; /* Hz */
+
+       pconf->lcd_control.vbyone_config->phy_div = phy_div;
+       pconf->lcd_control.vbyone_config->bit_rate = bit_rate;
+
+       if (lcd_debug_print_flag) {
+               LCDPR("lane_count=%u, bit_rate = %uMHz, pclk=%u.%03uMhz\n",
+                       lane_count, (bit_rate / 1000000),
+                       (pclk / 1000), (pclk % 1000));
+       }
+}
+
+void lcd_tablet_clk_update(struct lcd_config_s *pconf)
+{
+#ifdef CONFIG_AML_VPU
+       request_vpu_clk_vmod(pconf->lcd_timing.lcd_clk, VPU_VENCL);
+#endif
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_VBYONE:
+               lcd_vbyone_config_set(pconf);
+               break;
+       case LCD_MIPI:
+               lcd_mipi_dsi_config_set(pconf);
+               break;
+       default:
+               break;
+       }
+
+       lcd_clk_generate_parameter(pconf);
+       lcd_clk_set(pconf);
+       if (pconf->lcd_basic.lcd_type == LCD_VBYONE)
+               lcd_tablet_vbyone_wait_stable();
+}
+
+void lcd_tablet_config_update(struct lcd_config_s *pconf)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct vinfo_s *info;
+
+       /* update lcd config sync_duration */
+       info = lcd_drv->lcd_info;
+       pconf->lcd_timing.sync_duration_num = info->sync_duration_num;
+       pconf->lcd_timing.sync_duration_den = info->sync_duration_den;
+
+       /* update clk & timing config */
+       lcd_vmode_change(pconf);
+       info->video_clk = pconf->lcd_timing.lcd_clk;
+       /* update interface timing */
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_VBYONE:
+               lcd_vbyone_config_set(pconf);
+               break;
+       case LCD_MIPI:
+               lcd_mipi_dsi_config_set(pconf);
+               break;
+       default:
+               break;
+       }
+}
+
+void lcd_tablet_config_post_update(struct lcd_config_s *pconf)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       /* update interface timing */
+       switch (lcd_drv->lcd_config->lcd_basic.lcd_type) {
+       case LCD_MIPI:
+               lcd_mipi_dsi_config_post(lcd_drv->lcd_config);
+               break;
+       default:
+               break;
+       }
+}
+
+void lcd_tablet_driver_init_pre(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       int ret;
+
+       pconf = lcd_drv->lcd_config;
+       LCDPR("tablet driver init(ver %s): %s\n", lcd_drv->version,
+               lcd_type_type_to_str(pconf->lcd_basic.lcd_type));
+       ret = lcd_type_supported(pconf);
+       if (ret)
+               return;
+
+       lcd_tablet_config_update(pconf);
+       lcd_tablet_config_post_update(pconf);
+#ifdef CONFIG_AML_VPU
+       request_vpu_clk_vmod(pconf->lcd_timing.lcd_clk, VPU_VENCL);
+       switch_vpu_mem_pd_vmod(VPU_VENCL, VPU_MEM_POWER_ON);
+#endif
+       lcd_clk_gate_switch(1);
+
+       lcd_clk_set(pconf);
+       lcd_venc_set(pconf);
+       lcd_tcon_set(pconf);
+       lcd_drv->lcd_test_pattern_restore();
+}
+
+int lcd_tablet_driver_init(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       int ret;
+
+       pconf = lcd_drv->lcd_config;
+       ret = lcd_type_supported(pconf);
+       if (ret)
+               return -1;
+
+       /* init driver */
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_TTL:
+               lcd_ttl_control_set(pconf);
+               lcd_ttl_pinmux_set(1);
+               break;
+       case LCD_LVDS:
+               lcd_lvds_control_set(pconf);
+               lcd_lvds_phy_set(pconf, 1);
+               break;
+       case LCD_VBYONE:
+               lcd_vbyone_pinmux_set(1);
+               lcd_vbyone_control_set(pconf);
+               lcd_vx1_wait_hpd();
+               lcd_vbyone_phy_set(pconf, 1);
+               lcd_tablet_vbyone_wait_stable();
+       case LCD_MIPI:
+               lcd_mipi_phy_set(pconf, 1);
+               lcd_mipi_control_set(pconf, 1);
+               break;
+       default:
+               break;
+       }
+
+       lcd_vcbus_write(VENC_INTCTRL, 0x200);
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s finished\n", __func__);
+
+       return 0;
+}
+
+void lcd_tablet_driver_disable(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       int ret;
+
+       LCDPR("disable driver\n");
+       pconf = lcd_drv->lcd_config;
+       ret = lcd_type_supported(pconf);
+       if (ret)
+               return;
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_TTL:
+               lcd_ttl_pinmux_set(0);
+               break;
+       case LCD_LVDS:
+               lcd_lvds_phy_set(pconf, 0);
+               lcd_lvds_disable();
+               break;
+       case LCD_VBYONE:
+               lcd_vbyone_phy_set(pconf, 0);
+               lcd_vbyone_pinmux_set(0);
+               lcd_vbyone_disable();
+       case LCD_MIPI:
+               mipi_dsi_link_off(pconf);
+               lcd_mipi_phy_set(pconf, 0);
+               lcd_mipi_control_set(pconf, 0);
+               break;
+       default:
+               break;
+       }
+
+       lcd_vcbus_write(ENCL_VIDEO_EN, 0); /* disable encl */
+
+       lcd_clk_disable();
+       lcd_clk_gate_switch(0);
+#ifdef CONFIG_AML_VPU
+       switch_vpu_mem_pd_vmod(VPU_VENCL, VPU_MEM_POWER_DOWN);
+       release_vpu_clk_vmod(VPU_VENCL);
+#endif
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s finished\n", __func__);
+}
+
+void lcd_tablet_driver_tiny_enable(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       int ret;
+
+       pconf = lcd_drv->lcd_config;
+       ret = lcd_type_supported(pconf);
+       if (ret)
+               return;
+
+       /* init driver */
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_TTL:
+               lcd_ttl_control_set(pconf);
+               lcd_ttl_pinmux_set(1);
+               break;
+       case LCD_LVDS:
+               lcd_lvds_control_set(pconf);
+               lcd_lvds_phy_set(pconf, 1);
+               break;
+       case LCD_VBYONE:
+               lcd_vbyone_pinmux_set(1);
+               lcd_vbyone_control_set(pconf);
+               lcd_vx1_wait_hpd();
+               lcd_vbyone_phy_set(pconf, 1);
+               lcd_tablet_vbyone_wait_stable();
+       case LCD_MIPI:
+               lcd_mipi_phy_set(pconf, 1);
+               lcd_mipi_control_set(pconf, 1);
+               break;
+       default:
+               break;
+       }
+
+       LCDPR("enable driver\n");
+}
+
+void lcd_tablet_driver_tiny_disable(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       int ret;
+
+       LCDPR("disable driver\n");
+       pconf = lcd_drv->lcd_config;
+       ret = lcd_type_supported(pconf);
+       if (ret)
+               return;
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_TTL:
+               lcd_ttl_pinmux_set(0);
+               break;
+       case LCD_LVDS:
+               lcd_lvds_phy_set(pconf, 0);
+               lcd_lvds_disable();
+               break;
+       case LCD_VBYONE:
+               lcd_vbyone_phy_set(pconf, 0);
+               lcd_vbyone_pinmux_set(0);
+               lcd_vbyone_disable();
+       case LCD_MIPI:
+               mipi_dsi_link_off(pconf);
+               lcd_mipi_phy_set(pconf, 0);
+               lcd_mipi_control_set(pconf, 0);
+               break;
+       default:
+               break;
+       }
+}
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_tablet.c b/drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_tablet.c
new file mode 100644 (file)
index 0000000..54e0c9d
--- /dev/null
@@ -0,0 +1,1210 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_tablet.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#ifdef CONFIG_AML_VPU
+#include <linux/amlogic/vpu.h>
+#endif
+#include <linux/amlogic/media/vout/vinfo.h>
+#include <linux/amlogic/media/vout/vout_notify.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include "lcd_tablet.h"
+#include "../lcd_reg.h"
+#include "../lcd_common.h"
+#include "mipi_dsi_util.h"
+
+#define PANEL_NAME    "panel"
+
+/* **************************************************
+ * vout server api
+ * **************************************************
+ */
+static enum vmode_e lcd_validate_vmode(char *mode)
+{
+       if (mode == NULL)
+               return VMODE_MAX;
+
+       if ((strncmp(mode, PANEL_NAME, strlen(PANEL_NAME))) == 0)
+               return VMODE_LCD;
+
+       return VMODE_MAX;
+}
+
+static struct vinfo_s *lcd_get_current_info(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       return lcd_drv->lcd_info;
+}
+
+static int lcd_set_current_vmode(enum vmode_e mode)
+{
+       int ret = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       mutex_lock(&lcd_vout_mutex);
+
+       if (!(mode & VMODE_INIT_BIT_MASK)) {
+               if (VMODE_LCD == (mode & VMODE_MODE_BIT_MASK)) {
+                       lcd_drv->driver_init_pre();
+                       ret = lcd_drv->driver_init();
+               } else {
+                       ret = -EINVAL;
+               }
+       }
+
+       lcd_vcbus_write(VPP_POSTBLEND_H_SIZE, lcd_drv->lcd_info->width);
+
+       mutex_unlock(&lcd_vout_mutex);
+       return ret;
+}
+
+static int lcd_vmode_is_supported(enum vmode_e mode)
+{
+       mode &= VMODE_MODE_BIT_MASK;
+       if (lcd_debug_print_flag)
+               LCDPR("%s vmode = %d\n", __func__, mode);
+
+       if (mode == VMODE_LCD)
+               return true;
+       return false;
+}
+
+static int lcd_vout_disable(enum vmode_e cur_vmod)
+{
+       aml_lcd_notifier_call_chain(LCD_EVENT_IF_POWER_OFF, NULL);
+       LCDPR("%s finished\n", __func__);
+       return 0;
+}
+
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+struct lcd_vframe_match_s {
+       int fps;
+       int frame_rate; /* *100 */
+       unsigned int duration_num;
+       unsigned int duration_den;
+};
+
+static struct lcd_vframe_match_s lcd_vframe_match_table_1[] = {
+       {5000, 5000, 50, 1},
+       {2500, 5000, 50, 1},
+       {6000, 6000, 60, 1},
+       {3000, 6000, 60, 1},
+       {2400, 6000, 60, 1},
+       {2397, 5994, 5994, 100},
+       {2997, 5994, 5994, 100},
+       {5994, 5994, 5994, 100},
+};
+
+static struct lcd_vframe_match_s lcd_vframe_match_table_2[] = {
+       {5000, 5000, 50, 1},
+       {2500, 5000, 50, 1},
+       {6000, 6000, 60, 1},
+       {3000, 6000, 60, 1},
+       {2400, 4800, 48, 1},
+       {2397, 5994, 5994, 100},
+       {2997, 5994, 5994, 100},
+       {5994, 5994, 5994, 100},
+};
+
+static int lcd_framerate_automation_set_mode(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       /* update lcd config sync_duration, for calculate */
+       lcd_drv->lcd_config->lcd_timing.sync_duration_num =
+               lcd_drv->lcd_info->sync_duration_num;
+       lcd_drv->lcd_config->lcd_timing.sync_duration_den =
+               lcd_drv->lcd_info->sync_duration_den;
+
+       /* update clk & timing config */
+       lcd_tablet_config_update(lcd_drv->lcd_config);
+       /* update interface timing if needed, current no need */
+#ifdef CONFIG_AML_VPU
+       request_vpu_clk_vmod(
+               lcd_drv->lcd_config->lcd_timing.lcd_clk, VPU_VENCL);
+#endif
+
+       /* change clk parameter */
+       switch (lcd_drv->lcd_config->lcd_timing.clk_change) {
+       case LCD_CLK_PLL_CHANGE:
+               lcd_clk_generate_parameter(lcd_drv->lcd_config);
+               lcd_clk_set(lcd_drv->lcd_config);
+               break;
+       case LCD_CLK_FRAC_UPDATE:
+               lcd_clk_update(lcd_drv->lcd_config);
+               break;
+       default:
+               break;
+       }
+       lcd_tablet_config_post_update(lcd_drv->lcd_config);
+       lcd_venc_change(lcd_drv->lcd_config);
+
+       vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE,
+               &lcd_drv->lcd_info->mode);
+
+       return 0;
+}
+#endif
+
+static int lcd_set_vframe_rate_hint(int duration)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct vinfo_s *info;
+       int fr_policy;
+       unsigned int frame_rate = 6000;
+       unsigned int duration_num = 60, duration_den = 1;
+       struct lcd_vframe_match_s *vtable = lcd_vframe_match_table_1;
+       int fps, i, n;
+
+       if (lcd_drv->lcd_status == 0) {
+               LCDPR("%s: lcd is disabled, exit\n", __func__);
+               return 0;
+       }
+
+       info = lcd_drv->lcd_info;
+
+       fr_policy = lcd_drv->fr_auto_policy;
+       switch (fr_policy) {
+       case 1:
+               vtable = lcd_vframe_match_table_1;
+               n = ARRAY_SIZE(lcd_vframe_match_table_1);
+               break;
+       case 2:
+               vtable = lcd_vframe_match_table_2;
+               n = ARRAY_SIZE(lcd_vframe_match_table_2);
+               break;
+       default:
+               LCDPR("%s: fr_auto_policy = %d, disabled\n",
+                       __func__, fr_policy);
+               return 0;
+       }
+       fps = get_vsource_fps(duration);
+       for (i = 0; i < n; i++) {
+               if (fps == vtable[i].fps) {
+                       frame_rate = vtable[i].frame_rate;
+                       duration_num = vtable[i].duration_num;
+                       duration_den = vtable[i].duration_den;
+               }
+       }
+       LCDPR("%s: policy = %d, duration = %d, fps = %d, frame_rate = %d\n",
+               __func__, fr_policy, duration, fps, frame_rate);
+
+       /* if the sync_duration is same as current */
+       if ((duration_num == info->sync_duration_num) &&
+               (duration_den == info->sync_duration_den)) {
+               LCDPR("%s: sync_duration is the same, exit\n", __func__);
+               return 0;
+       }
+
+       /* update vinfo */
+       info->sync_duration_num = duration_num;
+       info->sync_duration_den = duration_den;
+
+       lcd_framerate_automation_set_mode();
+#endif
+       return 0;
+}
+
+static int lcd_set_vframe_rate_end_hint(void)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct vinfo_s *info;
+
+       if (lcd_drv->lcd_status == 0) {
+               LCDPR("%s: lcd is disabled, exit\n", __func__);
+               return 0;
+       }
+
+       if (lcd_debug_print_flag)
+               LCDPR("fr_auto_policy = %d\n", lcd_drv->fr_auto_policy);
+       if (lcd_drv->fr_auto_policy) {
+               info = lcd_drv->lcd_info;
+               LCDPR("%s: return mode = %s, policy = %d\n", __func__,
+                       info->name, lcd_drv->fr_auto_policy);
+
+               /* update vinfo */
+               info->sync_duration_num = lcd_drv->std_duration.duration_num;
+               info->sync_duration_den = lcd_drv->std_duration.duration_den;
+
+               lcd_framerate_automation_set_mode();
+       }
+#endif
+       return 0;
+}
+
+static int lcd_set_vframe_rate_policy(int policy)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       lcd_drv->fr_auto_policy = policy;
+       LCDPR("%s: %d\n", __func__, lcd_drv->fr_auto_policy);
+#endif
+       return 0;
+}
+
+static int lcd_get_vframe_rate_policy(void)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       return lcd_drv->fr_auto_policy;
+#else
+       return 0;
+#endif
+}
+
+#ifdef CONFIG_PM
+static int lcd_suspend(void)
+{
+       mutex_lock(&lcd_power_mutex);
+       aml_lcd_notifier_call_chain(LCD_EVENT_POWER_OFF, NULL);
+       lcd_resume_flag = 0;
+       LCDPR("%s finished\n", __func__);
+       mutex_unlock(&lcd_power_mutex);
+       return 0;
+}
+static int lcd_resume(void)
+{
+       mutex_lock(&lcd_power_mutex);
+       if (lcd_resume_flag == 0) {
+               lcd_resume_flag = 1;
+               aml_lcd_notifier_call_chain(LCD_EVENT_POWER_ON, NULL);
+               LCDPR("%s finished\n", __func__);
+       }
+       mutex_unlock(&lcd_power_mutex);
+       return 0;
+}
+#endif
+
+static struct vout_server_s lcd_vout_server = {
+       .name = "lcd_vout_server",
+       .op = {
+               .get_vinfo = lcd_get_current_info,
+               .set_vmode = lcd_set_current_vmode,
+               .validate_vmode = lcd_validate_vmode,
+               .vmode_is_supported = lcd_vmode_is_supported,
+               .disable = lcd_vout_disable,
+               .set_vframe_rate_hint = lcd_set_vframe_rate_hint,
+               .set_vframe_rate_end_hint = lcd_set_vframe_rate_end_hint,
+               .set_vframe_rate_policy = lcd_set_vframe_rate_policy,
+               .get_vframe_rate_policy = lcd_get_vframe_rate_policy,
+#ifdef CONFIG_PM
+               .vout_suspend = lcd_suspend,
+               .vout_resume = lcd_resume,
+#endif
+       },
+};
+
+static void lcd_tablet_vinfo_update(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct vinfo_s *vinfo;
+       struct lcd_config_s *pconf;
+
+       vinfo = lcd_drv->lcd_info;
+       pconf = lcd_drv->lcd_config;
+       if (vinfo) {
+               vinfo->name = PANEL_NAME;
+               vinfo->mode = VMODE_LCD;
+               vinfo->width = pconf->lcd_basic.h_active;
+               vinfo->height = pconf->lcd_basic.v_active;
+               vinfo->field_height = pconf->lcd_basic.v_active;
+               vinfo->aspect_ratio_num = pconf->lcd_basic.screen_width;
+               vinfo->aspect_ratio_den = pconf->lcd_basic.screen_height;
+               vinfo->screen_real_width = pconf->lcd_basic.screen_width;
+               vinfo->screen_real_height = pconf->lcd_basic.screen_height;
+               vinfo->sync_duration_num = pconf->lcd_timing.sync_duration_num;
+               vinfo->sync_duration_den = pconf->lcd_timing.sync_duration_den;
+               vinfo->video_clk = pconf->lcd_timing.lcd_clk;
+               vinfo->viu_color_fmt = COLOR_FMT_RGB444;
+
+               lcd_hdr_vinfo_update();
+       }
+}
+
+static void lcd_tablet_vinfo_update_default(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct vinfo_s *vinfo;
+       unsigned int h_active, v_active;
+
+       if (lcd_drv->lcd_info == NULL) {
+               LCDERR("no lcd_info exist\n");
+               return;
+       }
+
+       h_active = lcd_vcbus_read(ENCL_VIDEO_HAVON_END)
+                       - lcd_vcbus_read(ENCL_VIDEO_HAVON_BEGIN) + 1;
+       v_active = lcd_vcbus_read(ENCL_VIDEO_VAVON_ELINE)
+                       - lcd_vcbus_read(ENCL_VIDEO_VAVON_BLINE) + 1;
+
+       vinfo = lcd_drv->lcd_info;
+       if (vinfo) {
+               vinfo->name = PANEL_NAME;
+               vinfo->mode = VMODE_LCD;
+               vinfo->width = h_active;
+               vinfo->height = v_active;
+               vinfo->field_height = v_active;
+               vinfo->aspect_ratio_num = h_active;
+               vinfo->aspect_ratio_den = v_active;
+               vinfo->screen_real_width = h_active;
+               vinfo->screen_real_height = v_active;
+               vinfo->sync_duration_num = 60;
+               vinfo->sync_duration_den = 1;
+               vinfo->video_clk = 0;
+               vinfo->viu_color_fmt = COLOR_FMT_RGB444;
+       }
+}
+
+void lcd_tablet_vout_server_init(void)
+{
+       lcd_tablet_vinfo_update_default();
+       vout_register_server(&lcd_vout_server);
+}
+
+/* **************************************************
+ * lcd tablet config
+ * **************************************************
+ */
+static void lcd_config_print(struct lcd_config_s *pconf)
+{
+       LCDPR("%s, %s, %dbit, %dx%d\n",
+               pconf->lcd_basic.model_name,
+               lcd_type_type_to_str(pconf->lcd_basic.lcd_type),
+               pconf->lcd_basic.lcd_bits,
+               pconf->lcd_basic.h_active, pconf->lcd_basic.v_active);
+
+       if (lcd_debug_print_flag == 0)
+               return;
+
+       LCDPR("h_period = %d\n", pconf->lcd_basic.h_period);
+       LCDPR("v_period = %d\n", pconf->lcd_basic.v_period);
+       LCDPR("screen_width = %d\n", pconf->lcd_basic.screen_width);
+       LCDPR("screen_height = %d\n", pconf->lcd_basic.screen_height);
+
+       LCDPR("h_period_min = %d\n", pconf->lcd_basic.h_period_min);
+       LCDPR("h_period_max = %d\n", pconf->lcd_basic.h_period_max);
+       LCDPR("v_period_min = %d\n", pconf->lcd_basic.v_period_min);
+       LCDPR("v_period_max = %d\n", pconf->lcd_basic.v_period_max);
+       LCDPR("pclk_min = %d\n", pconf->lcd_basic.lcd_clk_min);
+       LCDPR("pclk_max = %d\n", pconf->lcd_basic.lcd_clk_max);
+
+       LCDPR("hsync_width = %d\n", pconf->lcd_timing.hsync_width);
+       LCDPR("hsync_bp = %d\n", pconf->lcd_timing.hsync_bp);
+       LCDPR("hsync_pol = %d\n", pconf->lcd_timing.hsync_pol);
+       LCDPR("vsync_width = %d\n", pconf->lcd_timing.vsync_width);
+       LCDPR("vsync_bp = %d\n", pconf->lcd_timing.vsync_bp);
+       LCDPR("vsync_pol = %d\n", pconf->lcd_timing.vsync_pol);
+
+       LCDPR("fr_adjust_type = %d\n", pconf->lcd_timing.fr_adjust_type);
+       LCDPR("ss_level = %d\n", pconf->lcd_timing.ss_level);
+       LCDPR("clk_auto = %d\n", pconf->lcd_timing.clk_auto);
+
+       if (pconf->lcd_basic.lcd_type == LCD_TTL) {
+               LCDPR("clk_pol = %d\n",
+                       pconf->lcd_control.ttl_config->clk_pol);
+               LCDPR("sync_valid = %d\n",
+                       pconf->lcd_control.ttl_config->sync_valid);
+               LCDPR("swap_ctrl = %d\n",
+                       pconf->lcd_control.ttl_config->swap_ctrl);
+       } else if (pconf->lcd_basic.lcd_type == LCD_LVDS) {
+               LCDPR("lvds_repack = %d\n",
+                       pconf->lcd_control.lvds_config->lvds_repack);
+               LCDPR("pn_swap = %d\n",
+                       pconf->lcd_control.lvds_config->pn_swap);
+               LCDPR("dual_port = %d\n",
+                       pconf->lcd_control.lvds_config->dual_port);
+               LCDPR("port_swap = %d\n",
+                       pconf->lcd_control.lvds_config->port_swap);
+               LCDPR("lane_reverse = %d\n",
+                       pconf->lcd_control.lvds_config->lane_reverse);
+       } else if (pconf->lcd_basic.lcd_type == LCD_VBYONE) {
+               LCDPR("lane_count = %d\n",
+                       pconf->lcd_control.vbyone_config->lane_count);
+               LCDPR("byte_mode = %d\n",
+                       pconf->lcd_control.vbyone_config->byte_mode);
+               LCDPR("region_num = %d\n",
+                       pconf->lcd_control.vbyone_config->region_num);
+               LCDPR("color_fmt = %d\n",
+                       pconf->lcd_control.vbyone_config->color_fmt);
+       } else if (pconf->lcd_basic.lcd_type == LCD_MIPI) {
+               LCDPR("mipi_lane_num = %d\n",
+                       pconf->lcd_control.mipi_config->lane_num);
+               LCDPR("bit_rate_max = %d\n",
+                       pconf->lcd_control.mipi_config->bit_rate_max);
+               LCDPR("factor_numerator = %d\n",
+                       pconf->lcd_control.mipi_config->factor_numerator);
+               LCDPR("operation_mode_init = %d\n",
+                       pconf->lcd_control.mipi_config->operation_mode_init);
+               LCDPR("operation_mode_display = %d\n",
+                       pconf->lcd_control.mipi_config->operation_mode_display);
+               LCDPR("video_mode_type = %d\n",
+                       pconf->lcd_control.mipi_config->video_mode_type);
+               LCDPR("clk_lp_continuous = %d\n",
+                       pconf->lcd_control.mipi_config->clk_lp_continuous);
+               LCDPR("phy_stop_wait = %d\n",
+                       pconf->lcd_control.mipi_config->phy_stop_wait);
+               LCDPR("extern_init = %d\n",
+                       pconf->lcd_control.mipi_config->extern_init);
+       }
+}
+
+static int lcd_init_load_from_dts(struct lcd_config_s *pconf,
+               struct device *dev)
+{
+       int ret = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_TTL:
+               if (lcd_drv->lcd_status) /* lock pinmux if lcd in on */
+                       lcd_ttl_pinmux_set(1);
+               else
+                       lcd_ttl_pinmux_set(0);
+               break;
+       case LCD_VBYONE:
+               if (lcd_drv->lcd_status) /* lock pinmux if lcd in on */
+                       lcd_vbyone_pinmux_set(1);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+static int lcd_config_load_from_dts(struct lcd_config_s *pconf,
+               struct device *dev)
+{
+       int ret = 0;
+       const char *str;
+       unsigned int para[10];
+       struct device_node *child;
+       struct lvds_config_s *lvdsconf;
+
+       child = of_get_child_by_name(dev->of_node, pconf->lcd_propname);
+       if (child == NULL) {
+               LCDERR("failed to get %s\n", pconf->lcd_propname);
+               return -1;
+       }
+
+       ret = of_property_read_string(child, "model_name", &str);
+       if (ret) {
+               LCDERR("failed to get model_name\n");
+               strcpy(pconf->lcd_basic.model_name, pconf->lcd_propname);
+       } else {
+               strcpy(pconf->lcd_basic.model_name, str);
+       }
+
+       ret = of_property_read_string(child, "interface", &str);
+       if (ret) {
+               LCDERR("failed to get interface\n");
+               str = "invalid";
+       }
+       pconf->lcd_basic.lcd_type = lcd_type_str_to_type(str);
+
+       ret = of_property_read_u32_array(child, "basic_setting", &para[0], 7);
+       if (ret) {
+               LCDERR("failed to get basic_setting\n");
+       } else {
+               pconf->lcd_basic.h_active = para[0];
+               pconf->lcd_basic.v_active = para[1];
+               pconf->lcd_basic.h_period = para[2];
+               pconf->lcd_basic.v_period = para[3];
+               pconf->lcd_basic.lcd_bits = para[4];
+               pconf->lcd_basic.screen_width = para[5];
+               pconf->lcd_basic.screen_height = para[6];
+       }
+       ret = of_property_read_u32_array(child, "range_setting", &para[0], 6);
+       if (ret) {
+               LCDPR("failed to get range_setting\n");
+               pconf->lcd_basic.h_period_min = pconf->lcd_basic.h_period;
+               pconf->lcd_basic.h_period_max = pconf->lcd_basic.h_period;
+               pconf->lcd_basic.v_period_min = pconf->lcd_basic.v_period;
+               pconf->lcd_basic.v_period_max = pconf->lcd_basic.v_period;
+               pconf->lcd_basic.lcd_clk_min = 0;
+               pconf->lcd_basic.lcd_clk_max = 0;
+       } else {
+               pconf->lcd_basic.h_period_min = para[0];
+               pconf->lcd_basic.h_period_max = para[1];
+               pconf->lcd_basic.v_period_min = para[2];
+               pconf->lcd_basic.v_period_max = para[3];
+               pconf->lcd_basic.lcd_clk_min = para[4];
+               pconf->lcd_basic.lcd_clk_max = para[5];
+       }
+
+       ret = of_property_read_u32_array(child, "lcd_timing", &para[0], 6);
+       if (ret) {
+               LCDERR("failed to get lcd_timing\n");
+       } else {
+               pconf->lcd_timing.hsync_width = (unsigned short)(para[0]);
+               pconf->lcd_timing.hsync_bp = (unsigned short)(para[1]);
+               pconf->lcd_timing.hsync_pol = (unsigned short)(para[2]);
+               pconf->lcd_timing.vsync_width = (unsigned short)(para[3]);
+               pconf->lcd_timing.vsync_bp = (unsigned short)(para[4]);
+               pconf->lcd_timing.vsync_pol = (unsigned short)(para[5]);
+       }
+
+       ret = of_property_read_u32_array(child, "clk_attr", &para[0], 4);
+       if (ret) {
+               LCDERR("failed to get clk_attr\n");
+               pconf->lcd_timing.fr_adjust_type = 0;
+               pconf->lcd_timing.ss_level = 0;
+               pconf->lcd_timing.clk_auto = 1;
+               pconf->lcd_timing.lcd_clk = 60;
+       } else {
+               pconf->lcd_timing.fr_adjust_type = (unsigned char)(para[0]);
+               pconf->lcd_timing.ss_level = (unsigned char)(para[1]);
+               pconf->lcd_timing.clk_auto = (unsigned char)(para[2]);
+               if (para[3] > 0) {
+                       pconf->lcd_timing.lcd_clk = para[3];
+               } else { /* avoid 0 mistake */
+                       pconf->lcd_timing.lcd_clk = 60;
+                       LCDERR("lcd_clk is 0, default to 60Hz\n");
+               }
+       }
+       if (pconf->lcd_timing.clk_auto == 0) {
+               ret = of_property_read_u32_array(child, "clk_para",
+                       &para[0], 3);
+               if (ret) {
+                       LCDERR("failed to get clk_para\n");
+               } else {
+                       pconf->lcd_timing.pll_ctrl = para[0];
+                       pconf->lcd_timing.div_ctrl = para[1];
+                       pconf->lcd_timing.clk_ctrl = para[2];
+               }
+       }
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_TTL:
+               ret = of_property_read_u32_array(child, "ttl_attr",
+                       &para[0], 5);
+               if (ret) {
+                       LCDERR("failed to get ttl_attr\n");
+               } else {
+                       pconf->lcd_control.ttl_config->clk_pol = para[0];
+                       pconf->lcd_control.ttl_config->sync_valid =
+                               ((para[1] << 1) | (para[2] << 0));
+                       pconf->lcd_control.ttl_config->swap_ctrl =
+                               ((para[3] << 1) | (para[4] << 0));
+               }
+               break;
+       case LCD_LVDS:
+               lvdsconf = pconf->lcd_control.lvds_config;
+               ret = of_property_read_u32_array(child, "lvds_attr",
+                       &para[0], 5);
+               if (ret) {
+                       if (lcd_debug_print_flag)
+                               LCDERR("load 4 parameters in lvds_attr\n");
+                       ret = of_property_read_u32_array(child, "lvds_attr",
+                               &para[0], 4);
+                       if (ret) {
+                               if (lcd_debug_print_flag)
+                                       LCDPR("failed to get lvds_attr\n");
+                       } else {
+                               lvdsconf->lvds_repack = para[0];
+                               lvdsconf->dual_port = para[1];
+                               lvdsconf->pn_swap = para[2];
+                               lvdsconf->port_swap = para[3];
+                       }
+               } else {
+                               lvdsconf->lvds_repack = para[0];
+                               lvdsconf->dual_port = para[1];
+                               lvdsconf->pn_swap = para[2];
+                               lvdsconf->port_swap = para[3];
+                               lvdsconf->lane_reverse = para[4];
+               }
+               ret = of_property_read_u32_array(child, "phy_attr",
+                       &para[0], 4);
+               if (ret) {
+                       ret = of_property_read_u32_array(child, "phy_attr",
+                               &para[0], 2);
+                       if (ret) {
+                               if (lcd_debug_print_flag)
+                                       LCDPR("failed to get phy_attr\n");
+                       } else {
+                               lvdsconf->phy_vswing = para[0];
+                               lvdsconf->phy_preem = para[1];
+                               lvdsconf->phy_clk_vswing = 0;
+                               lvdsconf->phy_clk_preem = 0;
+                               LCDPR("set phy vswing=0x%x, preemphasis=0x%x\n",
+                                       lvdsconf->phy_vswing,
+                                       lvdsconf->phy_preem);
+                       }
+               } else {
+                       lvdsconf->phy_vswing = para[0];
+                       lvdsconf->phy_preem = para[1];
+                       lvdsconf->phy_clk_vswing = para[2];
+                       lvdsconf->phy_clk_preem = para[3];
+                       LCDPR("set phy vswing=0x%x, preemphasis=0x%x\n",
+                               lvdsconf->phy_vswing, lvdsconf->phy_preem);
+                       LCDPR("set phy_clk vswing=0x%x, preemphasis=0x%x\n",
+                               lvdsconf->phy_clk_vswing,
+                               lvdsconf->phy_clk_preem);
+               }
+               break;
+       case LCD_VBYONE:
+               ret = of_property_read_u32_array(child, "vbyone_attr",
+                       &para[0], 4);
+               if (ret) {
+                       LCDERR("failed to get vbyone_attr\n");
+               } else {
+                       pconf->lcd_control.vbyone_config->lane_count = para[0];
+                       pconf->lcd_control.vbyone_config->region_num = para[1];
+                       pconf->lcd_control.vbyone_config->byte_mode = para[2];
+                       pconf->lcd_control.vbyone_config->color_fmt = para[3];
+               }
+               ret = of_property_read_u32_array(child, "vbyone_intr_enable",
+                       &para[0], 2);
+               if (ret) {
+                       LCDERR("failed to get vbyone_intr_enable\n");
+               } else {
+                       pconf->lcd_control.vbyone_config->intr_en = para[0];
+                       pconf->lcd_control.vbyone_config->vsync_intr_en =
+                               para[1];
+               }
+               ret = of_property_read_u32_array(child, "phy_attr",
+                       &para[0], 2);
+               if (ret) {
+                       if (lcd_debug_print_flag)
+                               LCDPR("failed to get phy_attr\n");
+               } else {
+                       pconf->lcd_control.vbyone_config->phy_vswing = para[0];
+                       pconf->lcd_control.vbyone_config->phy_preem = para[1];
+                       if (lcd_debug_print_flag) {
+                               LCDPR("phy vswing=%d, preemphasis=%d\n",
+                               pconf->lcd_control.vbyone_config->phy_vswing,
+                               pconf->lcd_control.vbyone_config->phy_preem);
+                       }
+               }
+               break;
+       case LCD_MIPI:
+               ret = of_property_read_u32_array(child, "mipi_attr",
+                       &para[0], 8);
+               if (ret) {
+                       LCDERR("failed to get mipi_attr\n");
+               } else {
+                       pconf->lcd_control.mipi_config->lane_num = para[0];
+                       pconf->lcd_control.mipi_config->bit_rate_max
+                               = para[1];
+                       pconf->lcd_control.mipi_config->factor_numerator
+                               = para[2];
+                       pconf->lcd_control.mipi_config->factor_denominator
+                               = 100;
+                       pconf->lcd_control.mipi_config->operation_mode_init
+                               = para[3];
+                       pconf->lcd_control.mipi_config->operation_mode_display
+                               = para[4];
+                       pconf->lcd_control.mipi_config->video_mode_type
+                               = para[5];
+                       pconf->lcd_control.mipi_config->clk_lp_continuous
+                               = para[6];
+                       pconf->lcd_control.mipi_config->phy_stop_wait
+                               = para[7];
+               }
+
+               lcd_mipi_dsi_init_table_detect(child,
+                       pconf->lcd_control.mipi_config, 1);
+               lcd_mipi_dsi_init_table_detect(child,
+                       pconf->lcd_control.mipi_config, 0);
+
+               ret = of_property_read_u32_array(child, "extern_init",
+                       &para[0], 1);
+               if (ret)
+                       LCDPR("failed to get extern_init\n");
+               else
+                       pconf->lcd_control.mipi_config->extern_init = para[0];
+               break;
+       default:
+               LCDERR("invalid lcd type\n");
+               break;
+       }
+
+       ret = lcd_power_load_from_dts(pconf, child);
+
+       return ret;
+}
+
+static int lcd_config_load_from_unifykey(struct lcd_config_s *pconf)
+{
+       unsigned char *para;
+       int key_len, len;
+       unsigned char *p;
+       const char *str;
+       struct aml_lcd_unifykey_header_s lcd_header;
+       unsigned int temp;
+       int ret;
+
+       para = kmalloc((sizeof(unsigned char) * LCD_UKEY_LCD_SIZE), GFP_KERNEL);
+       if (!para) {
+               LCDERR("%s: Not enough memory\n", __func__);
+               return -1;
+       }
+       key_len = LCD_UKEY_LCD_SIZE;
+       memset(para, 0, (sizeof(unsigned char) * key_len));
+       ret = lcd_unifykey_get("lcd", para, &key_len);
+       if (ret < 0) {
+               kfree(para);
+               return -1;
+       }
+
+       /* step 1: check header */
+       len = LCD_UKEY_HEAD_SIZE;
+       ret = lcd_unifykey_len_check(key_len, len);
+       if (ret < 0) {
+               LCDERR("unifykey header length is incorrect\n");
+               kfree(para);
+               return -1;
+       }
+
+       lcd_unifykey_header_check(para, &lcd_header);
+       len = 10 + 36 + 18 + 31 + 20;
+       if (lcd_debug_print_flag) {
+               LCDPR("unifykey header:\n");
+               LCDPR("crc32             = 0x%08x\n", lcd_header.crc32);
+               LCDPR("data_len          = %d\n", lcd_header.data_len);
+               LCDPR("version           = 0x%04x\n", lcd_header.version);
+               LCDPR("reserved          = 0x%04x\n", lcd_header.reserved);
+       }
+
+       /* step 2: check lcd parameters */
+       ret = lcd_unifykey_len_check(key_len, len);
+       if (ret < 0) {
+               LCDERR("unifykey parameters length is incorrect\n");
+               kfree(para);
+               return -1;
+       }
+
+       /* basic: 36byte */
+       p = para + LCD_UKEY_HEAD_SIZE;
+       *(p + LCD_UKEY_MODEL_NAME - 1) = '\0'; /* ensure string ending */
+       str = (const char *)p;
+       strcpy(pconf->lcd_basic.model_name, str);
+       p += LCD_UKEY_MODEL_NAME;
+       pconf->lcd_basic.lcd_type = *p;
+       p += LCD_UKEY_INTERFACE;
+       pconf->lcd_basic.lcd_bits = *p;
+       p += LCD_UKEY_LCD_BITS;
+       pconf->lcd_basic.screen_width = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_SCREEN_WIDTH;
+       pconf->lcd_basic.screen_height = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_SCREEN_HEIGHT;
+
+       /* timing: 18byte */
+       pconf->lcd_basic.h_active = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_H_ACTIVE;
+       pconf->lcd_basic.v_active = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_V_ACTIVE;
+       pconf->lcd_basic.h_period = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_H_PERIOD;
+       pconf->lcd_basic.v_period = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_V_PERIOD;
+       pconf->lcd_timing.hsync_width = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_HS_WIDTH;
+       pconf->lcd_timing.hsync_bp = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_HS_BP;
+       pconf->lcd_timing.hsync_pol = *p;
+       p += LCD_UKEY_HS_POL;
+       pconf->lcd_timing.vsync_width = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_VS_WIDTH;
+       pconf->lcd_timing.vsync_bp = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_VS_BP;
+       pconf->lcd_timing.vsync_pol = *p;
+       p += LCD_UKEY_VS_POL;
+
+       /* customer: 31byte */
+       pconf->lcd_timing.fr_adjust_type = *p;
+       p += LCD_UKEY_FR_ADJ_TYPE;
+       pconf->lcd_timing.ss_level = *p;
+       p += LCD_UKEY_SS_LEVEL;
+       pconf->lcd_timing.clk_auto = *p;
+       p += LCD_UKEY_CLK_AUTO_GEN;
+       pconf->lcd_timing.lcd_clk = (*p | ((*(p + 1)) << 8) |
+               ((*(p + 2)) << 16) | ((*(p + 3)) << 24));
+       p += LCD_UKEY_PCLK;
+       pconf->lcd_basic.h_period_min = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_H_PERIOD_MIN;
+       pconf->lcd_basic.h_period_max = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_H_PERIOD_MAX;
+       pconf->lcd_basic.v_period_min = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_V_PERIOD_MIN;
+       pconf->lcd_basic.v_period_max = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_V_PERIOD_MAX;
+       pconf->lcd_basic.lcd_clk_min = (*p | ((*(p + 1)) << 8) |
+               ((*(p + 2)) << 16) | ((*(p + 3)) << 24));
+       p += LCD_UKEY_PCLK_MIN;
+       pconf->lcd_basic.lcd_clk_max = (*p | ((*(p + 1)) << 8) |
+               ((*(p + 2)) << 16) | ((*(p + 3)) << 24));
+       p += LCD_UKEY_PCLK_MAX;
+       /* dummy pointer */
+       p += LCD_UKEY_CUST_VAL_8;
+       p += LCD_UKEY_CUST_VAL_9;
+
+       /* interface: 20byte */
+       if (pconf->lcd_basic.lcd_type == LCD_LVDS) {
+               if (lcd_header.version == 1) {
+                       pconf->lcd_control.lvds_config->lvds_repack =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_0;
+                       pconf->lcd_control.lvds_config->dual_port =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_1;
+                       pconf->lcd_control.lvds_config->pn_swap  =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_2;
+                       pconf->lcd_control.lvds_config->port_swap  =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_3;
+                       pconf->lcd_control.lvds_config->phy_vswing =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_4;
+                       pconf->lcd_control.lvds_config->phy_preem =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_5;
+                       pconf->lcd_control.lvds_config->phy_clk_vswing =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_6;
+                       pconf->lcd_control.lvds_config->phy_clk_preem  =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_7;
+                       pconf->lcd_control.lvds_config->lane_reverse = 0;
+                       p += LCD_UKEY_IF_ATTR_8;
+
+                       /* dummy pointer */
+                       p += LCD_UKEY_IF_ATTR_9;
+                       }
+               else if (lcd_header.version == 2) {
+                       pconf->lcd_control.lvds_config->lvds_repack =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_0;
+                       pconf->lcd_control.lvds_config->dual_port =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_1;
+                       pconf->lcd_control.lvds_config->pn_swap  =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_2;
+                       pconf->lcd_control.lvds_config->port_swap  =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_3;
+                       pconf->lcd_control.lvds_config->lane_reverse  =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_4;
+                       pconf->lcd_control.lvds_config->phy_vswing =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_5;
+                       pconf->lcd_control.lvds_config->phy_preem =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_6;
+                       pconf->lcd_control.lvds_config->phy_clk_vswing =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_7;
+                       pconf->lcd_control.lvds_config->phy_clk_preem  =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_8;
+                       /* dummy pointer */
+                       p += LCD_UKEY_IF_ATTR_9;
+                       }
+       } else if (pconf->lcd_basic.lcd_type == LCD_TTL) {
+               pconf->lcd_control.ttl_config->clk_pol =
+                       (*p | ((*(p + 1)) << 8)) & 0x1;
+               p += LCD_UKEY_IF_ATTR_0;
+               temp = (*p | ((*(p + 1)) << 8)) & 0x1; /* de_valid */
+               pconf->lcd_control.ttl_config->sync_valid = (temp << 1);
+               p += LCD_UKEY_IF_ATTR_1;
+               temp = (*p | ((*(p + 1)) << 8)) & 0x1; /* hvsync_valid */
+               pconf->lcd_control.ttl_config->sync_valid |= (temp << 0);
+               p += LCD_UKEY_IF_ATTR_2;
+               temp = (*p | ((*(p + 1)) << 8)) & 0x1; /* rb_swap */
+               pconf->lcd_control.ttl_config->swap_ctrl = (temp << 1);
+               p += LCD_UKEY_IF_ATTR_3;
+               temp = (*p | ((*(p + 1)) << 8)) & 0x1; /* bit_swap */
+               pconf->lcd_control.ttl_config->swap_ctrl |= (temp << 0);
+               p += LCD_UKEY_IF_ATTR_4;
+               /* dummy pointer */
+               p += LCD_UKEY_IF_ATTR_5;
+               p += LCD_UKEY_IF_ATTR_6;
+               p += LCD_UKEY_IF_ATTR_7;
+               p += LCD_UKEY_IF_ATTR_8;
+               p += LCD_UKEY_IF_ATTR_9;
+       } else if (pconf->lcd_basic.lcd_type == LCD_VBYONE) {
+               pconf->lcd_control.vbyone_config->lane_count =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_0;
+               pconf->lcd_control.vbyone_config->region_num =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_1;
+               pconf->lcd_control.vbyone_config->byte_mode  =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_2;
+               pconf->lcd_control.vbyone_config->color_fmt  =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_3;
+               pconf->lcd_control.vbyone_config->phy_vswing =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_4;
+               pconf->lcd_control.vbyone_config->phy_preem =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_5;
+               pconf->lcd_control.vbyone_config->intr_en =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_6;
+               pconf->lcd_control.vbyone_config->vsync_intr_en =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_7;
+               /* dummy pointer */
+               p += LCD_UKEY_IF_ATTR_8;
+               p += LCD_UKEY_IF_ATTR_9;
+       } else if (pconf->lcd_basic.lcd_type == LCD_MIPI) {
+               pconf->lcd_control.mipi_config->lane_num =
+                       (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_0;
+               pconf->lcd_control.mipi_config->bit_rate_max =
+                       (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_1;
+               pconf->lcd_control.mipi_config->factor_numerator =
+                       (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_2;
+               pconf->lcd_control.mipi_config->factor_denominator =
+                       100;
+               pconf->lcd_control.mipi_config->operation_mode_init =
+                       (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_3;
+               pconf->lcd_control.mipi_config->operation_mode_display =
+                       (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_4;
+               pconf->lcd_control.mipi_config->video_mode_type =
+                       (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_5;
+               pconf->lcd_control.mipi_config->clk_lp_continuous =
+                       (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_6;
+               pconf->lcd_control.mipi_config->phy_stop_wait =
+                       (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_7;
+               pconf->lcd_control.mipi_config->extern_init =
+                       (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_8;
+
+               /* dummy pointer */
+               p += LCD_UKEY_IF_ATTR_9;
+
+       } else {
+               LCDERR("unsupport lcd_type: %d\n", pconf->lcd_basic.lcd_type);
+               p += LCD_UKEY_IF_ATTR_0;
+               p += LCD_UKEY_IF_ATTR_1;
+               p += LCD_UKEY_IF_ATTR_2;
+               p += LCD_UKEY_IF_ATTR_3;
+               p += LCD_UKEY_IF_ATTR_4;
+               p += LCD_UKEY_IF_ATTR_5;
+               p += LCD_UKEY_IF_ATTR_6;
+               p += LCD_UKEY_IF_ATTR_7;
+               p += LCD_UKEY_IF_ATTR_8;
+               p += LCD_UKEY_IF_ATTR_9;
+       }
+
+       /* step 3: check power sequence */
+       ret = lcd_power_load_from_unifykey(pconf, para, key_len, len);
+       if (ret < 0) {
+               kfree(para);
+               return -1;
+       }
+
+       kfree(para);
+       return 0;
+}
+
+static void lcd_config_init(struct lcd_config_s *pconf)
+{
+       struct lcd_clk_config_s *cconf = get_lcd_clk_config();
+       unsigned int ss_level;
+       unsigned int clk;
+       unsigned int sync_duration, h_period, v_period;
+
+       clk = pconf->lcd_timing.lcd_clk;
+       h_period = pconf->lcd_basic.h_period;
+       v_period = pconf->lcd_basic.v_period;
+       if (clk < 200) { /* regard as frame_rate */
+               sync_duration = clk * 100;
+               pconf->lcd_timing.lcd_clk = clk * h_period * v_period;
+       } else { /* regard as pixel clock */
+               sync_duration = ((clk / h_period) * 100) / v_period;
+       }
+       pconf->lcd_timing.sync_duration_num = sync_duration;
+       pconf->lcd_timing.sync_duration_den = 100;
+       pconf->lcd_timing.lcd_clk_dft = pconf->lcd_timing.lcd_clk;
+       pconf->lcd_timing.h_period_dft = pconf->lcd_basic.h_period;
+       pconf->lcd_timing.v_period_dft = pconf->lcd_basic.v_period;
+       lcd_tcon_config(pconf);
+
+       lcd_tablet_vinfo_update();
+
+       lcd_tablet_config_update(pconf);
+       lcd_clk_generate_parameter(pconf);
+       ss_level = pconf->lcd_timing.ss_level;
+       cconf->ss_level = (ss_level >= cconf->ss_level_max) ? 0 : ss_level;
+
+       lcd_tablet_config_post_update(pconf);
+}
+
+static int lcd_get_config(struct lcd_config_s *pconf, struct device *dev)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       int load_id = 0;
+       int ret;
+
+       if (dev->of_node == NULL) {
+               LCDERR("dev of_node is null\n");
+               return -1;
+       }
+       if (lcd_drv->lcd_key_valid) {
+               ret = lcd_unifykey_check("lcd");
+               if (ret < 0)
+                       load_id = 0;
+               else
+                       load_id = 1;
+       }
+       if (load_id) {
+               LCDPR("%s from unifykey\n", __func__);
+               lcd_drv->lcd_config_load = 1;
+               lcd_config_load_from_unifykey(pconf);
+       } else {
+               LCDPR("%s from dts\n", __func__);
+               lcd_drv->lcd_config_load = 0;
+               lcd_config_load_from_dts(pconf, dev);
+       }
+       lcd_init_load_from_dts(pconf, dev);
+       lcd_config_print(pconf);
+       lcd_config_init(pconf);
+
+       return 0;
+}
+
+/* **************************************************
+ * lcd notify
+ * **************************************************
+ */
+/* sync_duration is real_value * 100 */
+static void lcd_set_vinfo(unsigned int sync_duration)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       LCDPR("%s: sync_duration=%d\n", __func__, sync_duration);
+
+       /* update vinfo */
+       lcd_drv->lcd_info->sync_duration_num = sync_duration;
+       lcd_drv->lcd_info->sync_duration_den = 100;
+
+       /* update interface timing */
+       lcd_tablet_config_update(lcd_drv->lcd_config);
+#ifdef CONFIG_AML_VPU
+       request_vpu_clk_vmod(
+               lcd_drv->lcd_config->lcd_timing.lcd_clk, VPU_VENCL);
+#endif
+
+       /* change clk parameter */
+       switch (lcd_drv->lcd_config->lcd_timing.clk_change) {
+       case LCD_CLK_PLL_CHANGE:
+               lcd_clk_generate_parameter(lcd_drv->lcd_config);
+               lcd_clk_set(lcd_drv->lcd_config);
+               break;
+       case LCD_CLK_FRAC_UPDATE:
+               lcd_clk_update(lcd_drv->lcd_config);
+               break;
+       default:
+               break;
+       }
+       lcd_tablet_config_post_update(lcd_drv->lcd_config);
+       lcd_venc_change(lcd_drv->lcd_config);
+
+       vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE,
+               &lcd_drv->lcd_info->mode);
+}
+
+static int lcd_frame_rate_adjust_notifier(struct notifier_block *nb,
+               unsigned long event, void *data)
+{
+       unsigned int *sync_duration;
+
+       /* LCDPR("%s: 0x%lx\n", __func__, event); */
+       if ((event & LCD_EVENT_FRAME_RATE_ADJUST) == 0)
+               return NOTIFY_DONE;
+
+       sync_duration = (unsigned int *)data;
+       lcd_set_vinfo(*sync_duration);
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block lcd_frame_rate_adjust_nb = {
+       .notifier_call = lcd_frame_rate_adjust_notifier,
+};
+
+/* **************************************************
+ * lcd tablet
+ * **************************************************
+ */
+int lcd_tablet_probe(struct device *dev)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       int ret;
+
+       lcd_drv->version = LCD_DRV_VERSION;
+       lcd_drv->vout_server_init = lcd_tablet_vout_server_init;
+       lcd_drv->driver_init_pre = lcd_tablet_driver_init_pre;
+       lcd_drv->driver_init = lcd_tablet_driver_init;
+       lcd_drv->driver_disable = lcd_tablet_driver_disable;
+       lcd_drv->driver_tiny_enable = lcd_tablet_driver_tiny_enable;
+       lcd_drv->driver_tiny_disable = lcd_tablet_driver_tiny_disable;
+
+       lcd_get_config(lcd_drv->lcd_config, dev);
+
+       ret = aml_lcd_notifier_register(&lcd_frame_rate_adjust_nb);
+       if (ret)
+               LCDERR("register lcd_frame_rate_adjust_nb failed\n");
+
+       return 0;
+}
+
+int lcd_tablet_remove(struct device *dev)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       aml_lcd_notifier_unregister(&lcd_frame_rate_adjust_nb);
+       kfree(lcd_drv->lcd_info);
+       lcd_drv->lcd_info = NULL;
+       return 0;
+}
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_tablet.h b/drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_tablet.h
new file mode 100644 (file)
index 0000000..6c435dd
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_tablet.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __AML_LCD_TABLET_H__
+#define __AML_LCD_TABLET_H__
+
+extern void lcd_tablet_config_update(struct lcd_config_s *pconf);
+extern void lcd_tablet_config_post_update(struct lcd_config_s *pconf);
+extern void lcd_tablet_driver_init_pre(void);
+extern int lcd_tablet_driver_init(void);
+extern void lcd_tablet_driver_disable(void);
+extern void lcd_tablet_driver_tiny_enable(void);
+extern void lcd_tablet_driver_tiny_disable(void);
+
+#endif
diff --git a/drivers/amlogic/media/vout/lcd/lcd_tablet/mipi_dsi_util.c b/drivers/amlogic/media/vout/lcd/lcd_tablet/mipi_dsi_util.c
new file mode 100644 (file)
index 0000000..c1cad68
--- /dev/null
@@ -0,0 +1,1706 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tablet/mipi_dsi_util.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#endif
+#include "../lcd_reg.h"
+#include "../lcd_clk_config.h"
+#include "mipi_dsi_util.h"
+
+/* *************************************************************
+ * Define MIPI DSI Default config
+ */
+/* Range [0,3] */
+#define MIPI_DSI_VIRTUAL_CHAN_ID        0
+/* Define DSI command transfer type: high speed or low power */
+#define MIPI_DSI_CMD_TRANS_TYPE         DCS_TRANS_LP
+/* Define if DSI command need ack: req_ack or no_ack */
+#define MIPI_DSI_DCS_ACK_TYPE           MIPI_DSI_DCS_NO_ACK
+/* Applicable only to video mode. Define data transfer method:
+ *    non-burst sync pulse; non-burst sync event; or burst.
+ */
+
+#define MIPI_DSI_COLOR_18BIT            COLOR_18BIT_CFG_1
+#define MIPI_DSI_COLOR_24BIT            COLOR_24BIT
+#define MIPI_DSI_TEAR_SWITCH            MIPI_DCS_DISABLE_TEAR
+#define CMD_TIMEOUT_CNT                 3000
+/* ************************************************************* */
+
+static char *operation_mode_table[] = {
+       "VIDEO",
+       "COMMAND",
+};
+
+static char *video_mode_type_table[] = {
+       "SYNC_PULSE",
+       "SYNC_EVENT",
+       "BURST_MODE",
+};
+
+static char *video_data_type_table[] = {
+       "COLOR_16BIT_CFG_1",
+       "COLOR_16BIT_CFG_2",
+       "COLOR_16BIT_CFG_3",
+       "COLOR_18BIT_CFG_1",
+       "COLOR_18BIT_CFG_2(loosely)",
+       "COLOR_24BIT",
+       "COLOR_20BIT_LOOSE",
+       "COLOR_24_BIT_YCBCR",
+       "COLOR_16BIT_YCBCR",
+       "COLOR_30BIT",
+       "COLOR_36BIT",
+       "COLOR_12BIT",
+       "COLOR_RGB_111",
+       "COLOR_RGB_332",
+       "COLOR_RGB_444",
+       "un-support type",
+};
+
+static char *phy_stop_wait_table[] = {
+       "AUTO",
+       "STANDARD",
+       "SLOW",
+};
+
+static struct dsi_phy_s dsi_phy_config;
+static struct dsi_vid_s dsi_vconf;
+static unsigned char dsi_init_on_table_dft[] = {
+       0x05, 1, 0x11,
+       0xff, 50,
+       0x05, 1, 0x29,
+       0xff, 20,
+       0xff, 0xff,
+};
+static unsigned short dsi_rx_n;
+
+static void mipi_dsi_init_table_print(struct dsi_config_s *dconf, int on_off)
+{
+       int i, j, n;
+       int n_max;
+       unsigned char *dsi_table;
+       char str[200];
+       int len = 0;
+
+       if (on_off) {
+               if (dconf->dsi_init_on == NULL)
+                       return;
+               dsi_table = dconf->dsi_init_on;
+               n_max = DSI_INIT_ON_MAX;
+               pr_info("DSI INIT ON:\n");
+       } else {
+               if (dconf->dsi_init_off == NULL)
+                       return;
+               dsi_table = dconf->dsi_init_off;
+               n_max = DSI_INIT_OFF_MAX;
+               pr_info("DSI INIT OFF:\n");
+       }
+       i = 0;
+       n = 0;
+       while (i < n_max) {
+               if (dsi_table[i] == 0xff) {
+                       n = 2;
+                       if (dsi_table[i+1] == 0xff) {
+                               pr_info("  0x%02x,0x%02x,\n",
+                                       dsi_table[i], dsi_table[i+1]);
+                               break;
+                       } else {
+                               pr_info("  0x%02x,%d,\n",
+                                       dsi_table[i], dsi_table[i+1]);
+                       }
+               } else if ((dsi_table[i] & 0xf) == 0x0) {
+                       pr_info("dsi_init_%s wrong data_type: 0x%02x\n",
+                               on_off ? "on" : "off", dsi_table[i]);
+                       break;
+               } else {
+                       n = (DSI_CMD_INDEX + 1) + dsi_table[i+DSI_CMD_INDEX];
+                       len = 0;
+                       for (j = 0; j < n; j++) {
+                               if (j == DSI_CMD_INDEX) {
+                                       len += sprintf(str+len, "%d,",
+                                               dsi_table[i+j]);
+                               } else {
+                                       len += sprintf(str+len, "0x%02x,",
+                                               dsi_table[i+j]);
+                               }
+                       }
+                       if (len > 0)
+                               pr_info("  %s\n", str);
+               }
+               i += n;
+       }
+}
+
+static void mipi_dsi_print_dphy_info(struct dsi_config_s *dconf)
+{
+       unsigned int temp;
+
+       temp = ((1000000 * 100) / (dconf->bit_rate / 1000)) * 8;
+       pr_info("MIPI DSI DPHY timing (unit: ns)\n"
+       "  UI:                %d.%02d\n"
+       "  LP LPX:            %d\n"
+       "  LP TA_SURE:        %d\n"
+       "  LP TA_GO:          %d\n"
+       "  LP TA_GET:         %d\n"
+       "  HS EXIT:           %d\n"
+       "  HS TRAIL:          %d\n"
+       "  HS ZERO:           %d\n"
+       "  HS PREPARE:        %d\n"
+       "  CLK TRAIL:         %d\n"
+       "  CLK POST:          %d\n"
+       "  CLK ZERO:          %d\n"
+       "  CLK PREPARE:       %d\n"
+       "  CLK PRE:           %d\n"
+       "  INIT:              %d\n"
+       "  WAKEUP:            %d\n\n",
+       (temp / 8 / 100), ((temp / 8) % 100),
+       (temp * dsi_phy_config.lp_lpx / 100),
+       (temp * dsi_phy_config.lp_ta_sure / 100),
+       (temp * dsi_phy_config.lp_ta_go / 100),
+       (temp * dsi_phy_config.lp_ta_get / 100),
+       (temp * dsi_phy_config.hs_exit / 100),
+       (temp * dsi_phy_config.hs_trail / 100),
+       (temp * dsi_phy_config.hs_zero / 100),
+       (temp * dsi_phy_config.hs_prepare / 100),
+       (temp * dsi_phy_config.clk_trail / 100),
+       (temp * dsi_phy_config.clk_post / 100),
+       (temp * dsi_phy_config.clk_zero / 100),
+       (temp * dsi_phy_config.clk_prepare / 100),
+       (temp * dsi_phy_config.clk_pre / 100),
+       (temp * dsi_phy_config.init / 100),
+       (temp * dsi_phy_config.wakeup / 100));
+}
+
+void mipi_dsi_print_info(struct lcd_config_s *pconf)
+{
+       unsigned int esc_clk, factor;
+       struct dsi_config_s *dconf;
+
+       dconf = pconf->lcd_control.mipi_config;
+       esc_clk = dconf->bit_rate / 8 / dsi_phy_config.lp_tesc;
+       factor = dconf->factor_numerator;
+       factor = ((factor * 1000 / dconf->factor_denominator) + 5) / 10;
+
+       pr_info("MIPI DSI Config:\n"
+               "  lane num:              %d\n"
+               "  bit rate max:          %dMHz\n"
+               "  bit rate:              %d.%03dMHz\n"
+               "  pclk lanebyte factor:  %d(/100)\n"
+               "  operation mode:\n"
+               "      init:              %s(%d)\n"
+               "      display:           %s(%d)\n"
+               "  video mode type:       %s(%d)\n"
+               "  clk lp continuous:     %d\n"
+               "  phy stop wait:         %s(%d)\n"
+               "  data format:           %s\n"
+               "  lp escape clock:       %d.%03dMHz\n",
+               dconf->lane_num, dconf->bit_rate_max,
+               (dconf->bit_rate / 1000000), (dconf->bit_rate % 1000000) / 1000,
+               factor,
+               operation_mode_table[dconf->operation_mode_init],
+               dconf->operation_mode_init,
+               operation_mode_table[dconf->operation_mode_display],
+               dconf->operation_mode_display,
+               video_mode_type_table[dconf->video_mode_type],
+               dconf->video_mode_type,
+               dconf->clk_lp_continuous,
+               phy_stop_wait_table[dconf->phy_stop_wait],
+               dconf->phy_stop_wait,
+               video_data_type_table[dconf->dpi_data_format],
+               (esc_clk / 1000000), (esc_clk % 1000000) / 1000);
+
+       mipi_dsi_init_table_print(dconf, 1); /* dsi_init_on table */
+       mipi_dsi_init_table_print(dconf, 0); /* dsi_init_off table */
+
+       pr_info("extern init:             %d\n\n", dconf->extern_init);
+
+       mipi_dsi_print_dphy_info(dconf);
+}
+
+int lcd_mipi_dsi_init_table_detect(struct device_node *m_node,
+               struct dsi_config_s *dconf, int on_off)
+{
+       int ret = 0;
+       unsigned char *dsi_table;
+       unsigned char propname[15];
+       int i, j;
+       int n_max;
+       unsigned int *para; /* num 100 array */
+       unsigned int val;
+
+       if (on_off) {
+               if (dconf->dsi_init_on == NULL)
+                       return -1;
+               dsi_table = dconf->dsi_init_on;
+               n_max = DSI_INIT_ON_MAX;
+               sprintf(propname, "dsi_init_on");
+       } else {
+               if (dconf->dsi_init_off == NULL)
+                       return -1;
+               dsi_table = dconf->dsi_init_off;
+               n_max = DSI_INIT_OFF_MAX;
+               sprintf(propname, "dsi_init_off");
+       }
+
+       para = kmalloc(sizeof(unsigned int)*100, GFP_KERNEL);
+       if (para == NULL) {
+               LCDERR("%s: Not enough memory\n", __func__);
+               return -1;
+       }
+       ret = of_property_read_u32_index(m_node, propname, 0, &para[0]);
+       if (ret) {
+               LCDERR("faild to get %s\n", propname);
+               kfree(para);
+               return -1;
+       }
+       i = 0;
+       while (i < n_max) {
+               ret = of_property_read_u32_index(m_node, propname, i, &val);
+               if (val == 0xff) {
+                       ret = of_property_read_u32_index(m_node,
+                               propname, (i+1), &val);
+                       i += 2;
+                       if (val == 0xff)
+                               break;
+               } else if ((val & 0xf) == 0x0) {
+                       LCDERR("get %s wrong data_type: 0x%02x\n",
+                               propname, val);
+                       break;
+               } else {
+                       ret = of_property_read_u32_index(m_node,
+                               propname, (i + DSI_CMD_INDEX), &val);
+                       if (val > n_max)
+                               break;
+                       else
+                               i = i + (DSI_CMD_INDEX + 1) + (val & 0xff);
+               }
+       }
+       i = (i > n_max) ? n_max : i;
+       ret = of_property_read_u32_array(m_node, propname, &para[0], i);
+       if (ret) {
+               LCDERR("faild to get %s\n", propname);
+       } else {
+               for (j = 0; j < i; j++)
+                       dsi_table[j] = (unsigned char)(para[j] & 0xff);
+       }
+
+       if (lcd_debug_print_flag)
+               mipi_dsi_init_table_print(dconf, on_off);
+
+       kfree(para);
+       return ret;
+}
+
+#if 0
+static void dsi_meas_clk_set(void)
+{
+       lcd_hiu_setb(HHI_VDIN_MEAS_CLK_CNTL, 0, 21, 3);
+       lcd_hiu_setb(HHI_VDIN_MEAS_CLK_CNTL, 0, 12, 7);
+       lcd_hiu_setb(HHI_VDIN_MEAS_CLK_CNTL, 1, 20, 1);
+}
+#endif
+
+/* *************************************************************
+ * Function: mipi_dcs_set
+ * Configure relative registers in command mode
+ * Parameters:   int trans_type, // 0: high speed, 1: low power
+ *               int req_ack,    // 1: request ack, 0: do not need ack
+ *               int tear_en     // 1: enable tear ack, 0: disable tear ack
+ */
+static void mipi_dcs_set(int trans_type, int req_ack, int tear_en)
+{
+       dsi_host_write(MIPI_DSI_DWC_CMD_MODE_CFG_OS,
+               (trans_type << BIT_MAX_RD_PKT_SIZE) |
+               (trans_type << BIT_DCS_LW_TX)    |
+               (trans_type << BIT_DCS_SR_0P_TX) |
+               (trans_type << BIT_DCS_SW_1P_TX) |
+               (trans_type << BIT_DCS_SW_0P_TX) |
+               (trans_type << BIT_GEN_LW_TX)    |
+               (trans_type << BIT_GEN_SR_2P_TX) |
+               (trans_type << BIT_GEN_SR_1P_TX) |
+               (trans_type << BIT_GEN_SR_0P_TX) |
+               (trans_type << BIT_GEN_SW_2P_TX) |
+               (trans_type << BIT_GEN_SW_1P_TX) |
+               (trans_type << BIT_GEN_SW_0P_TX) |
+               (req_ack << BIT_ACK_RQST_EN)     |
+               (tear_en << BIT_TEAR_FX_EN));
+
+       if (tear_en == MIPI_DCS_ENABLE_TEAR) {
+               /* Enable Tear Interrupt if tear_en is valid */
+               lcd_vcbus_set_mask(MIPI_DSI_TOP_INTR_CNTL_STAT,
+                       (0x1 << BIT_EDPITE_INT_EN));
+               /* Enable Measure Vsync */
+               lcd_vcbus_set_mask(MIPI_DSI_TOP_MEAS_CNTL,
+                       (0x1 << BIT_VSYNC_MEAS_EN) | (0x1 << BIT_TE_MEAS_EN));
+       }
+
+       /* Packet header settings */
+       dsi_host_write(MIPI_DSI_DWC_PCKHDL_CFG_OS,
+               (1 << BIT_CRC_RX_EN)  |
+               (1 << BIT_ECC_RX_EN)  |
+               (0 << BIT_BTA_EN)     |
+               (0 << BIT_EOTP_RX_EN) |
+               (0 << BIT_EOTP_TX_EN));
+}
+
+
+/* *************************************************************
+ * Function: check_phy_st
+ * Check the status of the dphy: phylock and stopstateclklane,
+ *  to decide if the DPHY is ready
+ */
+static void check_phy_status(void)
+{
+       while (dsi_host_getb(MIPI_DSI_DWC_PHY_STATUS_OS, BIT_PHY_LOCK, 1) == 0)
+               udelay(6);
+       while (dsi_host_getb(MIPI_DSI_DWC_PHY_STATUS_OS,
+               BIT_PHY_STOPSTATECLKLANE, 1) == 0) {
+               LCDPR(" Waiting STOP STATE LANE\n");
+               udelay(6);
+       }
+}
+
+static void dsi_phy_init(struct dsi_phy_s *dphy, unsigned char lane_num)
+{
+       /* enable phy clock. */
+       dsi_phy_write(MIPI_DSI_PHY_CTRL,  0x1); /* enable DSI top clock. */
+       dsi_phy_write(MIPI_DSI_PHY_CTRL,
+               (1 << 0)  | /* enable the DSI PLL clock . */
+               (1 << 7)  |
+                       /* enable pll clock which connected to
+                        * DDR clock path
+                        */
+               (1 << 8)  | /* enable the clock divider counter */
+               (0 << 9)  | /* enable the divider clock out */
+               (0 << 10) | /* clock divider. 1: freq/4, 0: freq/2 */
+               (0 << 11) |
+                       /* 1: select the mipi DDRCLKHS from clock divider,
+                        * 0: from PLL clock
+                        */
+               (0 << 12)); /* enable the byte clock generateion. */
+       /* enable the divider clock out */
+       dsi_phy_setb(MIPI_DSI_PHY_CTRL,  1, 9, 1);
+       /* enable the byte clock generateion. */
+       dsi_phy_setb(MIPI_DSI_PHY_CTRL,  1, 12, 1);
+       dsi_phy_setb(MIPI_DSI_PHY_CTRL,  1, 31, 1);
+       dsi_phy_setb(MIPI_DSI_PHY_CTRL,  0, 31, 1);
+
+       /* 0x05210f08);//0x03211c08 */
+       dsi_phy_write(MIPI_DSI_CLK_TIM,
+               (dphy->clk_trail | (dphy->clk_post << 8) |
+               (dphy->clk_zero << 16) | (dphy->clk_prepare << 24)));
+       dsi_phy_write(MIPI_DSI_CLK_TIM1, dphy->clk_pre); /* ?? */
+       /* 0x050f090d */
+       dsi_phy_write(MIPI_DSI_HS_TIM,
+               (dphy->hs_exit | (dphy->hs_trail << 8) |
+               (dphy->hs_zero << 16) | (dphy->hs_prepare << 24)));
+       /* 0x4a370e0e */
+       dsi_phy_write(MIPI_DSI_LP_TIM,
+               (dphy->lp_lpx | (dphy->lp_ta_sure << 8) |
+               (dphy->lp_ta_go << 16) | (dphy->lp_ta_get << 24)));
+       /* ?? //some number to reduce sim time. */
+       dsi_phy_write(MIPI_DSI_ANA_UP_TIM, 0x0100);
+       /* 0xe20   //30d4 -> d4 to reduce sim time. */
+       dsi_phy_write(MIPI_DSI_INIT_TIM, dphy->init);
+       /* 0x8d40  //1E848-> 48 to reduct sim time. */
+       dsi_phy_write(MIPI_DSI_WAKEUP_TIM, dphy->wakeup);
+       /* wait for the LP analog ready. */
+       dsi_phy_write(MIPI_DSI_LPOK_TIM,  0x7C);
+       /* 1/3 of the tWAKEUP. */
+       dsi_phy_write(MIPI_DSI_ULPS_CHECK,  0x927C);
+       /* phy TURN watch dog. */
+       dsi_phy_write(MIPI_DSI_LP_WCHDOG,  0x1000);
+       /* phy ESC command watch dog. */
+       dsi_phy_write(MIPI_DSI_TURN_WCHDOG,  0x1000);
+
+       /* Powerup the analog circuit. */
+       switch (lane_num) {
+       case 1:
+               dsi_phy_write(MIPI_DSI_CHAN_CTRL, 0x0e);
+               break;
+       case 2:
+               dsi_phy_write(MIPI_DSI_CHAN_CTRL, 0x0c);
+               break;
+       case 3:
+               dsi_phy_write(MIPI_DSI_CHAN_CTRL, 0x08);
+               break;
+       case 4:
+       default:
+               dsi_phy_write(MIPI_DSI_CHAN_CTRL, 0);
+               break;
+       }
+}
+
+static void dsi_phy_config_set(struct lcd_config_s *pconf)
+{
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       /* Digital */
+       /* Power up DSI */
+       dsi_host_write(MIPI_DSI_DWC_PWR_UP_OS, 1);
+
+       /* Setup Parameters of DPHY */
+       dsi_host_write(MIPI_DSI_DWC_PHY_TST_CTRL1_OS, 0x00010044);/*testcode*/
+       dsi_host_write(MIPI_DSI_DWC_PHY_TST_CTRL0_OS, 0x2);
+       dsi_host_write(MIPI_DSI_DWC_PHY_TST_CTRL0_OS, 0x0);
+       dsi_host_write(MIPI_DSI_DWC_PHY_TST_CTRL1_OS, 0x00000074);/*testwrite*/
+       dsi_host_write(MIPI_DSI_DWC_PHY_TST_CTRL0_OS, 0x2);
+       dsi_host_write(MIPI_DSI_DWC_PHY_TST_CTRL0_OS, 0x0);
+
+       /* Power up D-PHY */
+       dsi_host_write(MIPI_DSI_DWC_PHY_RSTZ_OS, 0xf);
+
+       /* Analog */
+       dsi_phy_init(&dsi_phy_config, pconf->lcd_control.mipi_config->lane_num);
+
+       /* Check the phylock/stopstateclklane to decide if the DPHY is ready */
+       check_phy_status();
+
+       /* Trigger a sync active for esc_clk */
+       dsi_phy_set_mask(MIPI_DSI_PHY_CTRL, (1 << 1));
+
+       /* Startup transfer */
+       if (pconf->lcd_control.mipi_config->clk_lp_continuous) {
+               dsi_host_write(MIPI_DSI_DWC_LPCLK_CTRL_OS,
+                       (0x1 << BIT_TXREQUESTCLKHS));
+       } else {
+               dsi_host_write(MIPI_DSI_DWC_LPCLK_CTRL_OS,
+                       (0x1 << BIT_AUTOCLKLANE_CTRL) | (0x1 << BIT_TXREQUESTCLKHS));
+       }
+}
+
+static void startup_mipi_dsi_host(void)
+{
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       /* Enable dwc mipi_dsi_host's clock */
+       dsi_host_set_mask(MIPI_DSI_TOP_CNTL, ((1 << 4) | (1 << 5) | (0 << 6)));
+       /* mipi_dsi_host's reset */
+       dsi_host_set_mask(MIPI_DSI_TOP_SW_RESET, 0xf);
+       /* Release mipi_dsi_host's reset */
+       dsi_host_clr_mask(MIPI_DSI_TOP_SW_RESET, 0xf);
+       /* Enable dwc mipi_dsi_host's clock */
+       dsi_host_set_mask(MIPI_DSI_TOP_CLK_CNTL, 0x3);
+
+       dsi_host_write(MIPI_DSI_TOP_MEM_PD, 0);
+
+       mdelay(10);
+}
+
+/* *************************************************************
+ * Function: set_mipi_dsi_host
+ * Parameters: vcid, // virtual id
+ *             chroma_subsample, // chroma_subsample for YUV422 or YUV420 only
+ *             operation_mode,   // video mode/command mode
+ *             p,                //lcd config
+ */
+static void set_mipi_dsi_host(unsigned int vcid, unsigned int chroma_subsample,
+               unsigned int operation_mode, struct lcd_config_s *p)
+{
+       unsigned int dpi_data_format, venc_data_width;
+       unsigned int lane_num, vid_mode_type;
+       enum tv_enc_lcd_type_e  output_type;
+       unsigned int temp;
+       struct dsi_config_s *dconf;
+
+       dconf = p->lcd_control.mipi_config;
+       venc_data_width = dconf->venc_data_width;
+       dpi_data_format = dconf->dpi_data_format;
+       lane_num        = (unsigned int)(dconf->lane_num);
+       vid_mode_type   = (unsigned int)(dconf->video_mode_type);
+       output_type     = dconf->venc_fmt;
+
+       /* ----------------------------------------------------- */
+       /* Standard Configuration for Video Mode Operation */
+       /* ----------------------------------------------------- */
+       /* 1,    Configure Lane number and phy stop wait time */
+       if ((output_type != TV_ENC_LCD240x160_dsi) &&
+               (output_type != TV_ENC_LCD1920x1200p) &&
+               (output_type != TV_ENC_LCD2560x1600) &&
+               (output_type != TV_ENC_LCD768x1024p)) {
+               dsi_host_write(MIPI_DSI_DWC_PHY_IF_CFG_OS,
+                       (0x28 << BIT_PHY_STOP_WAIT_TIME) |
+                       ((lane_num-1) << BIT_N_LANES));
+       } else {
+               dsi_host_write(MIPI_DSI_DWC_PHY_IF_CFG_OS,
+                       (1 << BIT_PHY_STOP_WAIT_TIME) |
+                       ((lane_num-1) << BIT_N_LANES));
+       }
+
+       /* 2.1,  Configure Virtual channel settings */
+       dsi_host_write(MIPI_DSI_DWC_DPI_VCID_OS, vcid);
+       /* 2.2,  Configure Color format */
+       dsi_host_write(MIPI_DSI_DWC_DPI_COLOR_CODING_OS,
+               (((dpi_data_format == COLOR_18BIT_CFG_2) ?
+                       1 : 0) << BIT_LOOSELY18_EN) |
+               (dpi_data_format << BIT_DPI_COLOR_CODING));
+       /* 2.2.1 Configure Set color format for DPI register */
+       temp = (dsi_host_read(MIPI_DSI_TOP_CNTL) &
+               ~(0xf<<BIT_DPI_COLOR_MODE) &
+               ~(0x7<<BIT_IN_COLOR_MODE) &
+               ~(0x3<<BIT_CHROMA_SUBSAMPLE));
+       dsi_host_write(MIPI_DSI_TOP_CNTL,
+               (temp |
+               (dpi_data_format  << BIT_DPI_COLOR_MODE)  |
+               (venc_data_width  << BIT_IN_COLOR_MODE)   |
+               (chroma_subsample << BIT_CHROMA_SUBSAMPLE)));
+       /* 2.3   Configure Signal polarity */
+       dsi_host_write(MIPI_DSI_DWC_DPI_CFG_POL_OS,
+               (0x0 << BIT_COLORM_ACTIVE_LOW) |
+               (0x0 << BIT_SHUTD_ACTIVE_LOW)  |
+               (0 << BIT_HSYNC_ACTIVE_LOW)    |
+               (0 << BIT_VSYNC_ACTIVE_LOW)    |
+               (0x0 << BIT_DATAEN_ACTIVE_LOW));
+
+       if (operation_mode == OPERATION_VIDEO_MODE) {
+               /* 3.1   Configure Low power and video mode type settings */
+               dsi_host_write(MIPI_DSI_DWC_VID_MODE_CFG_OS,
+                       (1 << BIT_LP_HFP_EN)  |       /* enalbe lp */
+                       (1 << BIT_LP_HBP_EN)  |       /* enalbe lp */
+                       (1 << BIT_LP_VCAT_EN) |       /* enalbe lp */
+                       (1 << BIT_LP_VFP_EN)  |       /* enalbe lp */
+                       (1 << BIT_LP_VBP_EN)  |       /* enalbe lp */
+                       (1 << BIT_LP_VSA_EN)  |       /* enalbe lp */
+                       (1 << BIT_FRAME_BTA_ACK_EN) |
+                               /* enable BTA after one
+                                *      frame, TODO, need check
+                                */
+                       /* (1 << BIT_LP_CMD_EN) |  */
+                            /* enable the command*/
+                               /* transmission only in lp mode */
+                       (vid_mode_type << BIT_VID_MODE_TYPE));
+                                       /* burst non burst mode */
+               /* [23:16]outvact, [7:0]invact */
+               dsi_host_write(MIPI_DSI_DWC_DPI_LP_CMD_TIM_OS,
+                       (4 << 16) | (4 << 0));
+
+               /* 3.2   Configure video packet size settings */
+               if (vid_mode_type == BURST_MODE) {
+                       /* should be one line in pixels, such as 480/240... */
+                       dsi_host_write(MIPI_DSI_DWC_VID_PKT_SIZE_OS,
+                               p->lcd_basic.h_active);
+               } else {  /* non-burst mode */
+                       /* in unit of pixels,
+                        *   (pclk period/byte clk period)*num_of_lane
+                        *    should be integer
+                        */
+                       dsi_host_write(MIPI_DSI_DWC_VID_PKT_SIZE_OS,
+                               dsi_vconf.pixel_per_chunk);
+               }
+
+               /* 3.3  Configure number of chunks and null packet size
+                *  for one line
+                */
+               if (vid_mode_type == BURST_MODE) {
+                       dsi_host_write(MIPI_DSI_DWC_VID_NUM_CHUNKS_OS, 0);
+                       dsi_host_write(MIPI_DSI_DWC_VID_NULL_SIZE_OS, 0);
+               } else {  /* non burst mode */
+                       /* HACT/VID_PKT_SIZE */
+                       dsi_host_write(MIPI_DSI_DWC_VID_NUM_CHUNKS_OS,
+                               dsi_vconf.num_of_chunk);
+                       /* video null size */
+                       dsi_host_write(MIPI_DSI_DWC_VID_NULL_SIZE_OS,
+                               dsi_vconf.vid_null_size);
+               }
+
+               /* 4 Configure the video relative parameters according to
+                *         the output type
+                */
+               /* include horizontal timing and vertical line */
+               dsi_host_write(MIPI_DSI_DWC_VID_HLINE_TIME_OS, dsi_vconf.hline);
+               dsi_host_write(MIPI_DSI_DWC_VID_HSA_TIME_OS, dsi_vconf.hsa);
+               dsi_host_write(MIPI_DSI_DWC_VID_HBP_TIME_OS, dsi_vconf.hbp);
+               dsi_host_write(MIPI_DSI_DWC_VID_VSA_LINES_OS, dsi_vconf.vsa);
+               dsi_host_write(MIPI_DSI_DWC_VID_VBP_LINES_OS, dsi_vconf.vbp);
+               dsi_host_write(MIPI_DSI_DWC_VID_VFP_LINES_OS, dsi_vconf.vfp);
+               dsi_host_write(MIPI_DSI_DWC_VID_VACTIVE_LINES_OS,
+                       dsi_vconf.vact);
+       }  /* operation_mode == OPERATION_VIDEO_MODE */
+
+       /* ----------------------------------------------------- */
+       /* Finish Configuration */
+       /* ----------------------------------------------------- */
+
+       /* Inner clock divider settings */
+       dsi_host_write(MIPI_DSI_DWC_CLKMGR_CFG_OS,
+               (0x1 << BIT_TO_CLK_DIV) |
+               (dsi_phy_config.lp_tesc << BIT_TX_ESC_CLK_DIV));
+       /* Packet header settings  //move to mipi_dcs_set */
+       /* dsi_host_write( MIPI_DSI_DWC_PCKHDL_CFG_OS,
+        *      (1 << BIT_CRC_RX_EN) |
+        *      (1 << BIT_ECC_RX_EN) |
+        *      (0 << BIT_BTA_EN) |
+        *      (0 << BIT_EOTP_RX_EN) |
+        *      (0 << BIT_EOTP_TX_EN) );
+        */
+       /* operation mode setting: video/command mode */
+       dsi_host_write(MIPI_DSI_DWC_MODE_CFG_OS, operation_mode);
+
+       /* Phy Timer */
+       if ((output_type != TV_ENC_LCD240x160_dsi) &&
+               (output_type != TV_ENC_LCD1920x1200p) &&
+               (output_type != TV_ENC_LCD2560x1600) &&
+               (output_type != TV_ENC_LCD768x1024p)) {
+               dsi_host_write(MIPI_DSI_DWC_PHY_TMR_CFG_OS, 0x03320000);
+       } else {
+               dsi_host_write(MIPI_DSI_DWC_PHY_TMR_CFG_OS, 0x090f0000);
+       }
+
+       /* Configure DPHY Parameters */
+       if ((output_type != TV_ENC_LCD240x160_dsi) &&
+               (output_type != TV_ENC_LCD1920x1200p) &&
+               (output_type != TV_ENC_LCD2560x1600) &&
+               (output_type != TV_ENC_LCD768x1024p)) {
+               dsi_host_write(MIPI_DSI_DWC_PHY_TMR_LPCLK_CFG_OS, 0x870025);
+       } else {
+               dsi_host_write(MIPI_DSI_DWC_PHY_TMR_LPCLK_CFG_OS, 0x260017);
+       }
+}
+
+/* *************************************************************
+ * mipi dsi command support
+ */
+
+static inline void print_mipi_cmd_status(int cnt, unsigned int status)
+{
+       if (cnt == 0) {
+               LCDPR("cmd error: status=0x%04x, int0=0x%06x, int1=0x%06x\n",
+                       status,
+                       dsi_host_read(MIPI_DSI_DWC_INT_ST0_OS),
+                       dsi_host_read(MIPI_DSI_DWC_INT_ST1_OS));
+       }
+}
+
+#ifdef DSI_CMD_READ_VALID
+static void dsi_bta_control(int en)
+{
+       if (en) {
+               dsi_host_setb(MIPI_DSI_DWC_CMD_MODE_CFG_OS,
+                       MIPI_DSI_DCS_REQ_ACK, BIT_ACK_RQST_EN, 1);
+               dsi_host_setb(MIPI_DSI_DWC_PCKHDL_CFG_OS,
+                       MIPI_DSI_DCS_REQ_ACK, BIT_BTA_EN, 1);
+       } else {
+               dsi_host_setb(MIPI_DSI_DWC_PCKHDL_CFG_OS,
+                       MIPI_DSI_DCS_NO_ACK, BIT_BTA_EN, 1);
+               dsi_host_setb(MIPI_DSI_DWC_CMD_MODE_CFG_OS,
+                       MIPI_DSI_DCS_NO_ACK, BIT_ACK_RQST_EN, 1);
+       }
+}
+
+/* *************************************************************
+ * Function: generic_if_rd
+ * Generic interface read, address has to be MIPI_DSI_DWC_GEN_PLD_DATA_OS
+ */
+static unsigned int generic_if_rd(unsigned int address)
+{
+       unsigned int data_out;
+
+       if (address != MIPI_DSI_DWC_GEN_PLD_DATA_OS)
+               LCDERR(" Error Address : %x\n", address);
+
+       data_out = dsi_host_read(address);
+       return data_out;
+}
+#endif
+
+/* *************************************************************
+ * Function: generic_if_wr
+ * Generic interface write, address has to be
+ *                     MIPI_DSI_DWC_GEN_PLD_DATA_OS,
+ *                     MIPI_DSI_DWC_GEN_HDR_OS,
+ *                     MIPI_DSI_DWC_GEN_VCID_OS
+ */
+static unsigned int generic_if_wr(unsigned int address, unsigned int data_in)
+{
+       if ((address != MIPI_DSI_DWC_GEN_HDR_OS) &&
+               (address != MIPI_DSI_DWC_GEN_PLD_DATA_OS)) {
+               LCDERR(" Error Address : 0x%x\n", address);
+       }
+
+       if (lcd_debug_print_flag)
+               LCDPR("address 0x%x = 0x%08x\n", address, data_in);
+
+       dsi_host_write(address, data_in);
+
+       return 0;
+}
+
+/* *************************************************************
+ * Function: wait_bta_ack
+ * Poll to check if the BTA ack is finished
+ */
+static void wait_bta_ack(void)
+{
+       unsigned int phy_status, i;
+
+       /* Check if phydirection is RX */
+       i = CMD_TIMEOUT_CNT;
+       do {
+               udelay(10);
+               i--;
+               phy_status = dsi_host_read(MIPI_DSI_DWC_PHY_STATUS_OS);
+       } while ((((phy_status & 0x2) >> BIT_PHY_DIRECTION) == 0x0) && (i > 0));
+       if (i == 0)
+               LCDERR("phy direction error: RX\n");
+
+       /* Check if phydirection is return to TX */
+       i = CMD_TIMEOUT_CNT;
+       do {
+               udelay(10);
+               i--;
+               phy_status = dsi_host_read(MIPI_DSI_DWC_PHY_STATUS_OS);
+       } while (((phy_status & 0x2) >> BIT_PHY_DIRECTION) == 0x1);
+       if (i == 0)
+               LCDERR("phy direction error: TX\n");
+}
+
+/* *************************************************************
+ * Function: wait_cmd_fifo_empty
+ * Poll to check if the generic command fifo is empty
+ */
+static void wait_cmd_fifo_empty(void)
+{
+       unsigned int cmd_status;
+       int i = CMD_TIMEOUT_CNT;
+
+       do {
+               udelay(10);
+               i--;
+               cmd_status = dsi_host_read(MIPI_DSI_DWC_CMD_PKT_STATUS_OS);
+       } while ((((cmd_status >> BIT_GEN_CMD_EMPTY) & 0x1) != 0x1) && (i > 0));
+       print_mipi_cmd_status(i, cmd_status);
+}
+
+#if 0
+/* *************************************************************
+ * Function: wait_for_generic_read_response
+ * Wait for generic read response
+ */
+static unsigned int wait_for_generic_read_response(void)
+{
+       unsigned int timeout, phy_status, data_out;
+
+       phy_status = dsi_host_read(MIPI_DSI_DWC_PHY_STATUS_OS);
+       for (timeout = 0; timeout < 50; timeout++) {
+               if (((phy_status & 0x40) >> BIT_PHY_RXULPSESC0LANE) == 0x0)
+                       break;
+               phy_status = dsi_host_read(MIPI_DSI_DWC_PHY_STATUS_OS);
+               udelay(1);
+       }
+       phy_status = dsi_host_read(MIPI_DSI_DWC_PHY_STATUS_OS);
+       for (timeout = 0; timeout < 50; timeout++) {
+               if (((phy_status & 0x40) >> BIT_PHY_RXULPSESC0LANE) == 0x1)
+                       break;
+               phy_status = dsi_host_read(MIPI_DSI_DWC_PHY_STATUS_OS);
+               udelay(1);
+       }
+
+       data_out = dsi_host_read(MIPI_DSI_DWC_GEN_PLD_DATA_OS);
+       return data_out;
+}
+
+/* *************************************************************
+ * Function: generic_read_packet_0_para
+ * Generic Read Packet 0 Parameter with Generic Interface
+ * Supported DCS Command: DCS_SET_ADDRESS_MODE,
+ *                     DCS_SET_GAMMA_CURVE,
+ *                     DCS_SET_PIXEL_FORMAT,
+ *                     DCS_SET_TEAR_ON
+ */
+static unsigned int generic_read_packet_0_para(unsigned char data_type,
+               unsigned char vc_id, unsigned char dcs_command)
+{
+       unsigned int read_data;
+
+       /* lcd_print(" para is %x, dcs_command is %x\n", para, dcs_command); */
+       /* lcd_print(" vc_id %x, data_type is %x\n", vc_id, data_type); */
+       generic_if_wr(MIPI_DSI_DWC_GEN_HDR_OS,
+               ((0 << BIT_GEN_WC_MSBYTE)                           |
+               (((unsigned int)dcs_command) << BIT_GEN_WC_LSBYTE)  |
+               (((unsigned int)vc_id) << BIT_GEN_VC)               |
+               (((unsigned int)data_type) << BIT_GEN_DT)));
+
+       read_data = wait_for_generic_read_response();
+
+       return read_data;
+}
+#endif
+
+static void dsi_set_max_return_pkt_size(struct dsi_cmd_request_s *req)
+{
+       unsigned int d_para[2];
+
+       d_para[0] = (unsigned int)(req->payload[2] & 0xff);
+       d_para[1] = (unsigned int)(req->payload[3] & 0xff);
+       dsi_rx_n = (unsigned short)((d_para[1] << 8) | d_para[0]);
+       generic_if_wr(MIPI_DSI_DWC_GEN_HDR_OS,
+               ((d_para[1] << BIT_GEN_WC_MSBYTE)          |
+               (d_para[0] << BIT_GEN_WC_LSBYTE)           |
+               (((unsigned int)req->vc_id) << BIT_GEN_VC) |
+               (DT_SET_MAX_RET_PKT_SIZE << BIT_GEN_DT)));
+       if (req->req_ack == MIPI_DSI_DCS_REQ_ACK)
+               wait_bta_ack();
+       else if (req->req_ack == MIPI_DSI_DCS_NO_ACK)
+               wait_cmd_fifo_empty();
+}
+
+#ifdef DSI_CMD_READ_VALID
+static int dsi_generic_read_packet(struct dsi_cmd_request_s *req,
+               unsigned char *r_data)
+{
+       unsigned int d_para[2], read_data;
+       unsigned int i, j, done;
+
+       switch (req->data_type) {
+       case DT_GEN_RD_1:
+               d_para[0] = (req->pld_count == 0) ?
+                       0 : (((unsigned int)req->payload[2]) & 0xff);
+               d_para[1] = 0;
+               break;
+       case DT_GEN_RD_2:
+               d_para[0] = (req->pld_count == 0) ?
+                       0 : (((unsigned int)req->payload[2]) & 0xff);
+               d_para[1] = (req->pld_count < 2) ?
+                       0 : (((unsigned int)req->payload[3]) & 0xff);
+               break;
+       case DT_GEN_RD_0:
+       default:
+               d_para[0] = 0;
+               d_para[1] = 0;
+               break;
+       }
+
+       if (MIPI_DSI_DCS_ACK_TYPE == MIPI_DSI_DCS_NO_ACK)
+               dsi_bta_control(1);
+       generic_if_wr(MIPI_DSI_DWC_GEN_HDR_OS,
+               ((d_para[1] << BIT_GEN_WC_MSBYTE)          |
+               (d_para[0] << BIT_GEN_WC_LSBYTE)           |
+               (((unsigned int)req->vc_id) << BIT_GEN_VC) |
+               (((unsigned int)req->data_type) << BIT_GEN_DT)));
+       wait_bta_ack();
+       i = 0;
+       done = 0;
+       while (done == 0) {
+               read_data = generic_if_rd(MIPI_DSI_DWC_GEN_PLD_DATA_OS);
+               for (j = 0; j < 4; j++) {
+                       if (i < dsi_rx_n) {
+                               r_data[i] = (unsigned char)
+                                       ((read_data >> (j*8)) & 0xff);
+                               i++;
+                       } else {
+                               done = 1;
+                               break;
+                       }
+               }
+       }
+       if (MIPI_DSI_DCS_ACK_TYPE == MIPI_DSI_DCS_NO_ACK)
+               dsi_bta_control(0);
+
+       return dsi_rx_n;
+}
+
+static int dsi_dcs_read_packet(struct dsi_cmd_request_s *req,
+               unsigned char *r_data)
+{
+       unsigned int d_command, read_data;
+       unsigned int i, j, done;
+
+       d_command = ((unsigned int)req->payload[2]) & 0xff;
+
+       if (MIPI_DSI_DCS_ACK_TYPE == MIPI_DSI_DCS_NO_ACK)
+               dsi_bta_control(1);
+       generic_if_wr(MIPI_DSI_DWC_GEN_HDR_OS,
+               ((0 << BIT_GEN_WC_MSBYTE)                  |
+               (d_command << BIT_GEN_WC_LSBYTE)           |
+               (((unsigned int)req->vc_id) << BIT_GEN_VC) |
+               (((unsigned int)req->data_type) << BIT_GEN_DT)));
+       wait_bta_ack();
+       i = 0;
+       done = 0;
+       while (done == 0) {
+               read_data = generic_if_rd(MIPI_DSI_DWC_GEN_PLD_DATA_OS);
+               for (j = 0; j < 4; j++) {
+                       if (i < dsi_rx_n) {
+                               r_data[i] = (unsigned char)
+                                       ((read_data >> (j*8)) & 0xff);
+                               i++;
+                       } else {
+                               done = 1;
+                               break;
+                       }
+               }
+       }
+
+       if (MIPI_DSI_DCS_ACK_TYPE == MIPI_DSI_DCS_NO_ACK)
+               dsi_bta_control(0);
+
+       return dsi_rx_n;
+}
+#endif
+
+/* *************************************************************
+ * Function: generic_write_short_packet
+ * Generic Write Short Packet with Generic Interface
+ * Supported Data Type: DT_GEN_SHORT_WR_0,
+                       DT_GEN_SHORT_WR_1,
+                       DT_GEN_SHORT_WR_2,
+ */
+static void dsi_generic_write_short_packet(struct dsi_cmd_request_s *req)
+{
+       unsigned int d_para[2];
+
+       switch (req->data_type) {
+       case DT_GEN_SHORT_WR_1:
+               d_para[0] = (req->pld_count == 0) ?
+                       0 : (((unsigned int)req->payload[2]) & 0xff);
+               d_para[1] = 0;
+               break;
+       case DT_GEN_SHORT_WR_2:
+               d_para[0] = (req->pld_count == 0) ?
+                       0 : (((unsigned int)req->payload[2]) & 0xff);
+               d_para[1] = (req->pld_count < 2) ?
+                       0 : (((unsigned int)req->payload[3]) & 0xff);
+               break;
+       case DT_GEN_SHORT_WR_0:
+       default:
+               d_para[0] = 0;
+               d_para[1] = 0;
+               break;
+       }
+
+       generic_if_wr(MIPI_DSI_DWC_GEN_HDR_OS,
+               ((d_para[1] << BIT_GEN_WC_MSBYTE)          |
+               (d_para[0] << BIT_GEN_WC_LSBYTE)           |
+               (((unsigned int)req->vc_id) << BIT_GEN_VC) |
+               (((unsigned int)req->data_type) << BIT_GEN_DT)));
+       if (req->req_ack == MIPI_DSI_DCS_REQ_ACK)
+               wait_bta_ack();
+       else if (req->req_ack == MIPI_DSI_DCS_NO_ACK)
+               wait_cmd_fifo_empty();
+}
+
+/* *************************************************************
+ * Function: dcs_write_short_packet
+ * DCS Write Short Packet with Generic Interface
+ * Supported Data Type: DT_DCS_SHORT_WR_0, DT_DCS_SHORT_WR_1,
+ */
+static void dsi_dcs_write_short_packet(struct dsi_cmd_request_s *req)
+{
+       unsigned int d_command, d_para;
+
+       d_command = ((unsigned int)req->payload[2]) & 0xff;
+       d_para = (req->pld_count < 2) ?
+               0 : (((unsigned int)req->payload[3]) & 0xff);
+
+       generic_if_wr(MIPI_DSI_DWC_GEN_HDR_OS,
+               ((d_para << BIT_GEN_WC_MSBYTE)             |
+               (d_command << BIT_GEN_WC_LSBYTE)           |
+               (((unsigned int)req->vc_id) << BIT_GEN_VC) |
+               (((unsigned int)req->data_type) << BIT_GEN_DT)));
+       if (req->req_ack == MIPI_DSI_DCS_REQ_ACK)
+               wait_bta_ack();
+       else if (req->req_ack == MIPI_DSI_DCS_NO_ACK)
+               wait_cmd_fifo_empty();
+}
+
+/* *************************************************************
+ * Function: dsi_write_long_packet
+ * Write Long Packet with Generic Interface
+ * Supported Data Type: DT_GEN_LONG_WR, DT_DCS_LONG_WR
+ */
+static void dsi_write_long_packet(struct dsi_cmd_request_s *req)
+{
+       unsigned int d_command, payload_data, header_data;
+       unsigned int cmd_status;
+       unsigned int i, j, data_index, n, temp;
+
+       /* payload[2] start (payload[0]: data_type, payload[1]: data_cnt) */
+       data_index = DSI_CMD_INDEX + 1;
+       d_command = ((unsigned int)req->payload[data_index]) & 0xff;
+
+       /* Write Payload Register First */
+       n = (req->pld_count+3)/4;
+       for (i = 0; i < n; i++) {
+               payload_data = 0;
+               if (i < (req->pld_count/4))
+                       temp = 4;
+               else
+                       temp = req->pld_count % 4;
+               for (j = 0; j < temp; j++) {
+                       payload_data |= (((unsigned int)
+                               req->payload[data_index+(i*4)+j]) << (j*8));
+               }
+
+               /* Check the pld fifo status before write to it,
+                * do not need check every word
+                */
+               if ((i == (n/3)) || (i == (n/2))) {
+                       j = CMD_TIMEOUT_CNT;
+                       do {
+                               udelay(10);
+                               j--;
+                               cmd_status = dsi_host_read(
+                                               MIPI_DSI_DWC_CMD_PKT_STATUS_OS);
+                       } while ((((cmd_status >> BIT_GEN_PLD_W_FULL) & 0x1) ==
+                               0x1) && (j > 0));
+                       print_mipi_cmd_status(j, cmd_status);
+               }
+               /* Use direct memory write to save time when in
+                * WRITE_MEMORY_CONTINUE
+                */
+               if (d_command == DCS_WRITE_MEMORY_CONTINUE) {
+                       dsi_host_write(MIPI_DSI_DWC_GEN_PLD_DATA_OS,
+                               payload_data);
+               } else {
+                       generic_if_wr(MIPI_DSI_DWC_GEN_PLD_DATA_OS,
+                               payload_data);
+               }
+       }
+
+       /* Check cmd fifo status before write to it */
+       j = CMD_TIMEOUT_CNT;
+       do {
+               udelay(10);
+               j--;
+               cmd_status = dsi_host_read(MIPI_DSI_DWC_CMD_PKT_STATUS_OS);
+       } while ((((cmd_status >> BIT_GEN_CMD_FULL) & 0x1) == 0x1) && (j > 0));
+       print_mipi_cmd_status(j, cmd_status);
+       /* Write Header Register */
+       /* include command */
+       header_data = ((((unsigned int)req->pld_count) << BIT_GEN_WC_LSBYTE) |
+                       (((unsigned int)req->vc_id) << BIT_GEN_VC)           |
+                       (((unsigned int)req->data_type) << BIT_GEN_DT));
+       generic_if_wr(MIPI_DSI_DWC_GEN_HDR_OS, header_data);
+       if (req->req_ack == MIPI_DSI_DCS_REQ_ACK)
+               wait_bta_ack();
+       else if (req->req_ack == MIPI_DSI_DCS_NO_ACK)
+               wait_cmd_fifo_empty();
+}
+
+/* *************************************************************
+ * Function: dsi_write_cmd
+ * Supported Data Type: DT_GEN_SHORT_WR_0, DT_GEN_SHORT_WR_1, DT_GEN_SHORT_WR_2,
+ *                     DT_DCS_SHORT_WR_0, DT_DCS_SHORT_WR_1,
+ *                     DT_GEN_LONG_WR, DT_DCS_LONG_WR,
+ *                     DT_SET_MAX_RET_PKT_SIZE
+ *                     DT_GEN_RD_0, DT_GEN_RD_1, DT_GEN_RD_2,
+ *                     DT_DCS_RD_0
+ * Return:              command number
+ */
+int dsi_write_cmd(unsigned char *payload)
+{
+       int i = 0, j = 0, num = 0;
+#ifdef DSI_CMD_READ_VALID
+       int k = 0, n = 0;
+       unsigned char rd_data[100];
+#endif
+       struct dsi_cmd_request_s dsi_cmd_req;
+       unsigned char vc_id = MIPI_DSI_VIRTUAL_CHAN_ID;
+       unsigned int req_ack = MIPI_DSI_DCS_ACK_TYPE;
+
+       /* mipi command(payload) */
+       /* format:  data_type, num, data.... */
+       /* special: data_type=0xff,
+        *          num<0xff means delay ms, num=0xff means ending.
+        */
+       while (i < DSI_CMD_SIZE_MAX) {
+               if (payload[i] == 0xff) {
+                       j = 2;
+                       if (payload[i+1] == 0xff)
+                               break;
+                       else
+                               mdelay(payload[i+1]);
+               } else if ((payload[i] & 0xf) == 0x0) {
+                               LCDERR("data_type: 0x%02x\n", payload[i]);
+                               break;
+               } else {
+                       /* payload[i+DSI_CMD_INDEX] is data count */
+                       j = (DSI_CMD_INDEX + 1) + payload[i+DSI_CMD_INDEX];
+                       dsi_cmd_req.data_type = payload[i];
+                       dsi_cmd_req.vc_id = (vc_id & 0x3);
+                       dsi_cmd_req.payload = &payload[i];
+                       dsi_cmd_req.pld_count = payload[i+DSI_CMD_INDEX];
+                       dsi_cmd_req.req_ack = req_ack;
+                       switch (dsi_cmd_req.data_type) {/* analysis data_type */
+                       case DT_GEN_SHORT_WR_0:
+                       case DT_GEN_SHORT_WR_1:
+                       case DT_GEN_SHORT_WR_2:
+                               dsi_generic_write_short_packet(&dsi_cmd_req);
+                               break;
+                       case DT_DCS_SHORT_WR_0:
+                       case DT_DCS_SHORT_WR_1:
+                               dsi_dcs_write_short_packet(&dsi_cmd_req);
+                               break;
+                       case DT_DCS_LONG_WR:
+                       case DT_GEN_LONG_WR:
+                               dsi_write_long_packet(&dsi_cmd_req);
+                               break;
+                       case DT_TURN_ON:
+                               dsi_host_setb(MIPI_DSI_TOP_CNTL, 1, 2, 1);
+                               mdelay(20); /* wait for vsync trigger */
+                               dsi_host_setb(MIPI_DSI_TOP_CNTL, 0, 2, 1);
+                               mdelay(20); /* wait for vsync trigger */
+                               break;
+                       case DT_SHUT_DOWN:
+                               dsi_host_setb(MIPI_DSI_TOP_CNTL, 1, 2, 1);
+                               mdelay(20); /* wait for vsync trigger */
+                               break;
+                       case DT_SET_MAX_RET_PKT_SIZE:
+                               dsi_set_max_return_pkt_size(&dsi_cmd_req);
+                               break;
+#ifdef DSI_CMD_READ_VALID
+                       case DT_GEN_RD_0:
+                       case DT_GEN_RD_1:
+                       case DT_GEN_RD_2:
+                               /* need BTA ack */
+                               dsi_cmd_req.req_ack = MIPI_DSI_DCS_REQ_ACK;
+                               dsi_cmd_req.pld_count =
+                                       (dsi_cmd_req.pld_count > 2) ?
+                                       2 : dsi_cmd_req.pld_count;
+                               n = dsi_generic_read_packet(&dsi_cmd_req,
+                                               &rd_data[0]);
+                               LCDPR("generic read data");
+                               for (k = 0; k < dsi_cmd_req.pld_count; k++) {
+                                       pr_info(" 0x%02x",
+                                               dsi_cmd_req.payload[k+2]);
+                               }
+                               for (k = 0; k < n; k++)
+                                       pr_info("0x%02x ", rd_data[k]);
+                               pr_info("\n");
+                               break;
+                       case DT_DCS_RD_0:
+                               /* need BTA ack */
+                               dsi_cmd_req.req_ack = MIPI_DSI_DCS_REQ_ACK;
+                               n = dsi_dcs_read_packet(&dsi_cmd_req,
+                                       &rd_data[0]);
+                               pr_info("dcs read data 0x%02x:\n",
+                                       dsi_cmd_req.payload[2]);
+                               for (k = 0; k < n; k++)
+                                       pr_info("0x%02x ", rd_data[k]);
+                               pr_info("\n");
+                               break;
+#endif
+                       default:
+                               LCDPR("[warning]un-support data_type: 0x%02x\n",
+                                       dsi_cmd_req.data_type);
+
+                               break;
+                       }
+               }
+               i += j;
+               num++;
+       }
+
+       return num;
+}
+
+#ifdef DSI_CMD_READ_VALID
+/* *************************************************************
+ * Function: dsi_read_single
+ * Supported Data Type: DT_GEN_RD_0, DT_GEN_RD_1, DT_GEN_RD_2,
+ *             DT_DCS_RD_0
+ * Return:              data count
+ */
+int dsi_read_single(unsigned char *payload, unsigned char *rd_data,
+               unsigned int rd_byte_len)
+{
+       int num = 0;
+       unsigned char temp[4];
+       unsigned char vc_id = MIPI_DSI_VIRTUAL_CHAN_ID;
+       unsigned int req_ack;
+       struct dsi_cmd_request_s dsi_cmd_req;
+
+       req_ack = MIPI_DSI_DCS_ACK_TYPE;
+       dsi_cmd_req.data_type = DT_SET_MAX_RET_PKT_SIZE;
+       dsi_cmd_req.vc_id = (vc_id & 0x3);
+       temp[0] = dsi_cmd_req.data_type;
+       temp[1] = 2;
+       temp[2] = (unsigned char)((rd_byte_len >> 0) & 0xff);
+       temp[3] = (unsigned char)((rd_byte_len >> 8) & 0xff);
+       dsi_cmd_req.payload = &temp[0];
+       dsi_cmd_req.pld_count = 2;
+       dsi_cmd_req.req_ack = req_ack;
+       dsi_set_max_return_pkt_size(&dsi_cmd_req);
+
+       /* payload struct: */
+       /* data_type, data_cnt, command, parameters... */
+       req_ack = MIPI_DSI_DCS_REQ_ACK; /* need BTA ack */
+       dsi_cmd_req.data_type = payload[0];
+       dsi_cmd_req.vc_id = (vc_id & 0x3);
+       dsi_cmd_req.payload = &payload[0];
+       dsi_cmd_req.pld_count = payload[DSI_CMD_INDEX];
+       dsi_cmd_req.req_ack = req_ack;
+       switch (dsi_cmd_req.data_type) {/* analysis data_type */
+       case DT_GEN_RD_0:
+       case DT_GEN_RD_1:
+       case DT_GEN_RD_2:
+               num = dsi_generic_read_packet(&dsi_cmd_req, rd_data);
+               break;
+       case DT_DCS_RD_0:
+               num = dsi_dcs_read_packet(&dsi_cmd_req, rd_data);
+               break;
+       default:
+               LCDPR("read un-support data_type: 0x%02x\n",
+                       dsi_cmd_req.data_type);
+               break;
+       }
+
+       return num;
+}
+#else
+int dsi_read_single(unsigned char *payload, unsigned char *rd_data,
+               unsigned int rd_byte_len)
+{
+       LCDPR("Don't support mipi-dsi read command\n");
+       return 0;
+}
+#endif
+
+static void mipi_dsi_phy_config(struct dsi_phy_s *dphy, unsigned int dsi_ui)
+{
+       unsigned int temp, t_ui;
+
+       t_ui = (1000000 * 100) / (dsi_ui / 1000); /* 0.01ns*100 */
+       temp = t_ui * 8; /* lane_byte cycle time */
+
+       dphy->lp_tesc = ((DPHY_TIME_LP_TESC(t_ui) + temp - 1) / temp) & 0xff;
+       dphy->lp_lpx = ((DPHY_TIME_LP_LPX(t_ui) + temp - 1) / temp) & 0xff;
+       dphy->lp_ta_sure = ((DPHY_TIME_LP_TA_SURE(t_ui) + temp - 1) / temp) &
+                       0xff;
+       dphy->lp_ta_go = ((DPHY_TIME_LP_TA_GO(t_ui) + temp - 1) / temp) & 0xff;
+       dphy->lp_ta_get = ((DPHY_TIME_LP_TA_GETX(t_ui) + temp - 1) / temp) &
+                       0xff;
+       dphy->hs_exit = ((DPHY_TIME_HS_EXIT(t_ui) + temp - 1) / temp) & 0xff;
+       dphy->hs_trail = ((DPHY_TIME_HS_TRAIL(t_ui) + temp - 1) / temp) & 0xff;
+       dphy->hs_prepare = ((DPHY_TIME_HS_PREPARE(t_ui) + temp - 1) / temp) &
+                       0xff;
+       dphy->hs_zero = ((DPHY_TIME_HS_ZERO(t_ui) + temp - 1) / temp) & 0xff;
+       dphy->clk_trail = ((DPHY_TIME_CLK_TRAIL(t_ui) + temp - 1) / temp) &
+                       0xff;
+       dphy->clk_post = ((DPHY_TIME_CLK_POST(t_ui) + temp - 1) / temp) & 0xff;
+       dphy->clk_prepare = ((DPHY_TIME_CLK_PREPARE(t_ui) + temp - 1) / temp) &
+                       0xff;
+       dphy->clk_zero = ((DPHY_TIME_CLK_ZERO(t_ui) + temp - 1) / temp) & 0xff;
+       dphy->clk_pre = ((DPHY_TIME_CLK_PRE(t_ui) + temp - 1) / temp) & 0xff;
+       dphy->init = (DPHY_TIME_INIT(t_ui) + temp - 1) / temp;
+       dphy->wakeup = (DPHY_TIME_WAKEUP(t_ui) + temp - 1) / temp;
+
+       if (lcd_debug_print_flag) {
+               LCDPR("%s:\n"
+                       "lp_tesc     = 0x%02x\n"
+                       "lp_lpx      = 0x%02x\n"
+                       "lp_ta_sure  = 0x%02x\n"
+                       "lp_ta_go    = 0x%02x\n"
+                       "lp_ta_get   = 0x%02x\n"
+                       "hs_exit     = 0x%02x\n"
+                       "hs_trail    = 0x%02x\n"
+                       "hs_zero     = 0x%02x\n"
+                       "hs_prepare  = 0x%02x\n"
+                       "clk_trail   = 0x%02x\n"
+                       "clk_post    = 0x%02x\n"
+                       "clk_zero    = 0x%02x\n"
+                       "clk_prepare = 0x%02x\n"
+                       "clk_pre     = 0x%02x\n"
+                       "init        = 0x%02x\n"
+                       "wakeup      = 0x%02x\n",
+                       __func__,
+                       dphy->lp_tesc, dphy->lp_lpx, dphy->lp_ta_sure,
+                       dphy->lp_ta_go, dphy->lp_ta_get, dphy->hs_exit,
+                       dphy->hs_trail, dphy->hs_zero, dphy->hs_prepare,
+                       dphy->clk_trail, dphy->clk_post,
+                       dphy->clk_zero, dphy->clk_prepare, dphy->clk_pre,
+                       dphy->init, dphy->wakeup);
+       }
+}
+
+static void mipi_dsi_video_config(struct lcd_config_s *pconf)
+{
+       unsigned short h_period, hs_width, hs_bp;
+       unsigned int den, num;
+       unsigned short v_period, v_active, vs_width, vs_bp;
+
+       h_period = pconf->lcd_basic.h_period;
+       hs_width = pconf->lcd_timing.hsync_width;
+       hs_bp = pconf->lcd_timing.hsync_bp;
+       den = pconf->lcd_control.mipi_config->factor_denominator;
+       num = pconf->lcd_control.mipi_config->factor_numerator;
+
+       dsi_vconf.hline = (h_period * den + num - 1) / num;
+       dsi_vconf.hsa = (hs_width * den + num - 1) / num;
+       dsi_vconf.hbp = (hs_bp * den + num - 1) / num;
+
+       v_period = pconf->lcd_basic.v_period;
+       v_active = pconf->lcd_basic.v_active;
+       vs_width = pconf->lcd_timing.vsync_width;
+       vs_bp = pconf->lcd_timing.vsync_bp;
+       dsi_vconf.vsa = vs_width;
+       dsi_vconf.vbp = vs_bp;
+       dsi_vconf.vfp = v_period - v_active - vs_bp - vs_width;
+       dsi_vconf.vact = v_active;
+
+       if (lcd_debug_print_flag) {
+               LCDPR(" ============= VIDEO TIMING SETTING =============\n");
+               LCDPR(" HLINE        = %d\n", dsi_vconf.hline);
+               LCDPR(" HSA          = %d\n", dsi_vconf.hsa);
+               LCDPR(" HBP          = %d\n", dsi_vconf.hbp);
+               LCDPR(" VSA          = %d\n", dsi_vconf.vsa);
+               LCDPR(" VBP          = %d\n", dsi_vconf.vbp);
+               LCDPR(" VFP          = %d\n", dsi_vconf.vfp);
+               LCDPR(" VACT         = %d\n", dsi_vconf.vact);
+               LCDPR(" ================================================\n");
+       }
+}
+
+#define DSI_PACKET_HEADER_CRC      6 /* 4(header)+2(CRC) */
+static void mipi_dsi_non_burst_chunk_config(struct lcd_config_s *pconf)
+{
+       int pixel_per_chunk = 0, num_of_chunk = 0, vid_null_size = 0;
+       int byte_per_chunk = 0, total_bytes_per_chunk = 0, chunk_overhead = 0;
+       int bit_rate_pclk_factor;
+       int lane_num;
+       int i, done;
+
+       i = 1;
+       done = 0;
+       lane_num = (int)(pconf->lcd_control.mipi_config->lane_num);
+       bit_rate_pclk_factor = pconf->lcd_control.mipi_config->bit_rate /
+               pconf->lcd_timing.lcd_clk;
+       while ((i <= (pconf->lcd_basic.h_active/8)) && (done == 0)) {
+               pixel_per_chunk = i * 8;
+               if (pconf->lcd_control.mipi_config->dpi_data_format ==
+                       COLOR_18BIT_CFG_1) {
+                       /* 18bit (4*18/8=9byte) */
+                       byte_per_chunk = pixel_per_chunk * 9/4;
+               } else {
+                       /* 24bit or 18bit-loosely */
+                       byte_per_chunk = pixel_per_chunk * 3;
+               }
+               total_bytes_per_chunk =
+                       (lane_num * pixel_per_chunk * bit_rate_pclk_factor) / 8;
+               num_of_chunk = pconf->lcd_basic.h_active / pixel_per_chunk;
+               /* byte_per_chunk+6=valid_payload */
+               chunk_overhead = total_bytes_per_chunk -
+                       (byte_per_chunk + DSI_PACKET_HEADER_CRC);
+               if (chunk_overhead >= DSI_PACKET_HEADER_CRC) {
+                       /* if room for null_vid's head(4)+crc(2) */
+                       /* chunk_overhead-null_vid's head(4)+crc(2) =
+                        *   null_vid's payload
+                        */
+                       vid_null_size = chunk_overhead - DSI_PACKET_HEADER_CRC;
+                       done = 1;
+               } else if (chunk_overhead >= 0) {
+                       vid_null_size = 0;
+                       done = 1;
+               } else {
+                       vid_null_size = 0;
+               }
+               i++;
+       }
+       if (done == 0) {
+               LCDPR(" No room packet header & CRC, chunk_overhead is %d\n",
+                       chunk_overhead);
+       }
+
+       dsi_vconf.pixel_per_chunk = pixel_per_chunk;
+       dsi_vconf.num_of_chunk = num_of_chunk;
+       dsi_vconf.vid_null_size = vid_null_size;
+       if (lcd_debug_print_flag) {
+               LCDPR(" ============== NON_BURST SETTINGS =============\n");
+               LCDPR(" pixel_per_chunk       = %d\n", pixel_per_chunk);
+               LCDPR(" num_of_chunk          = %d\n", num_of_chunk);
+               LCDPR(" total_bytes_per_chunk = %d\n", total_bytes_per_chunk);
+               LCDPR(" byte_per_chunk        = %d\n", byte_per_chunk);
+               LCDPR(" chunk_overhead        = %d\n", chunk_overhead);
+               LCDPR(" vid_null_size         = %d\n", vid_null_size);
+               LCDPR(" ===============================================\n");
+       }
+}
+
+static void mipi_dsi_host_init(struct lcd_config_s *pconf)
+{
+       unsigned int op_mode_init;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       op_mode_init = pconf->lcd_control.mipi_config->operation_mode_init;
+       mipi_dcs_set(MIPI_DSI_CMD_TRANS_TYPE, /* 0: high speed, 1: low power */
+               MIPI_DSI_DCS_ACK_TYPE,        /* if need bta ack check */
+               MIPI_DSI_TEAR_SWITCH);        /* enable tear ack */
+
+       set_mipi_dsi_host(MIPI_DSI_VIRTUAL_CHAN_ID,   /* Virtual channel id */
+               0, /* Chroma sub sample, only for YUV 422 or 420, even or odd */
+               op_mode_init, /* DSI operation mode, video or command */
+               pconf);
+}
+
+static void mipi_dsi_link_on(struct lcd_config_s *pconf)
+{
+       unsigned int op_mode_init, op_mode_disp;
+       struct dsi_config_s *dconf;
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+       struct aml_lcd_extern_driver_s *lcd_ext;
+#endif
+       unsigned int temp = 0;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       dconf = pconf->lcd_control.mipi_config;
+       op_mode_init = dconf->operation_mode_init;
+       op_mode_disp = dconf->operation_mode_display;
+
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+       if (dconf->extern_init < LCD_EXTERN_INDEX_INVALID) {
+               lcd_ext = aml_lcd_extern_get_driver(dconf->extern_init);
+               if (lcd_ext == NULL) {
+                       LCDPR("no lcd_extern driver\n");
+               } else {
+                       if (lcd_ext->config.table_init_on) {
+                               temp += dsi_write_cmd(
+                                       lcd_ext->config.table_init_on);
+                               LCDPR("[extern]%s dsi init on\n",
+                                       lcd_ext->config.name);
+                       }
+               }
+       }
+#endif
+
+       if (dconf->dsi_init_on) {
+               temp += dsi_write_cmd(dconf->dsi_init_on);
+               LCDPR("dsi init on\n");
+       }
+
+       if (temp == 0) {
+               LCDPR("[warning]: no init command, use default\n");
+               dsi_write_cmd(dsi_init_on_table_dft);
+       }
+
+       if (op_mode_disp != op_mode_init) {
+               set_mipi_dsi_host(MIPI_DSI_VIRTUAL_CHAN_ID,
+                       0, /* Chroma sub sample, only for
+                           * YUV 422 or 420, even or odd
+                           */
+                       op_mode_disp, /* DSI operation mode, video or command */
+                       pconf);
+       }
+}
+
+void mipi_dsi_link_off(struct lcd_config_s *pconf)
+{
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+       struct aml_lcd_extern_driver_s *lcd_ext;
+       int ext_index;
+#endif
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       if (pconf->lcd_control.mipi_config->dsi_init_off) {
+               dsi_write_cmd(pconf->lcd_control.mipi_config->dsi_init_off);
+               LCDPR("dsi init off\n");
+       }
+
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+       ext_index = pconf->lcd_control.mipi_config->extern_init;
+       if (ext_index < LCD_EXTERN_INDEX_INVALID) {
+               lcd_ext = aml_lcd_extern_get_driver(ext_index);
+               if (lcd_ext == NULL) {
+                       LCDPR("no lcd_extern driver\n");
+               } else {
+                       if (lcd_ext->config.table_init_off) {
+                               dsi_write_cmd(lcd_ext->config.table_init_off);
+                               LCDPR("[extern]%s dsi init off\n", lcd_ext->config.name);
+                       }
+               }
+       }
+#endif
+}
+
+void lcd_mipi_dsi_config_set(struct lcd_config_s *pconf)
+{
+       unsigned int pclk, bit_rate, lcd_bits;
+       unsigned int bit_rate_max, bit_rate_min, pll_out_fmin;
+       struct dsi_config_s *dconf = pconf->lcd_control.mipi_config;
+       struct lcd_clk_config_s *cConf = get_lcd_clk_config();
+       int n;
+       unsigned int temp;
+
+       /* unit in kHz for calculation */
+       pll_out_fmin = cConf->pll_out_fmin;
+       pclk = pconf->lcd_timing.lcd_clk / 1000;
+
+       /* data format */
+       if (pconf->lcd_basic.lcd_bits == 6) {
+               dconf->venc_data_width = MIPI_DSI_VENC_COLOR_18B;
+               dconf->dpi_data_format = MIPI_DSI_COLOR_18BIT;
+               if (dconf->dpi_data_format == COLOR_18BIT_CFG_2)
+                       lcd_bits = 8;
+               else
+                       lcd_bits = 6;
+       } else {
+               dconf->venc_data_width = MIPI_DSI_VENC_COLOR_24B;
+               dconf->dpi_data_format  = MIPI_DSI_COLOR_24BIT;
+               lcd_bits = 8;
+       }
+
+       /* bit rate max */
+       if (dconf->bit_rate_max == 0) { /* auto calculate */
+               if ((dconf->operation_mode_display == OPERATION_VIDEO_MODE) &&
+                       (dconf->video_mode_type != BURST_MODE)) {
+                       temp = pclk * 4 * lcd_bits;
+                       bit_rate = temp / dconf->lane_num;
+               } else {
+                       temp = pclk * 3 * lcd_bits;
+                       bit_rate = temp / dconf->lane_num;
+               }
+               n = 0;
+               bit_rate_min = 0;
+               bit_rate_max = 0;
+               while ((bit_rate_min < pll_out_fmin) && (n < 100)) {
+                       bit_rate_max = bit_rate + (pclk / 2) + (n * pclk);
+                       bit_rate_min = bit_rate_max - pclk;
+                       n++;
+               }
+               dconf->bit_rate_max = bit_rate_max / 1000; /* unit: MHz*/
+               if (dconf->bit_rate_max > MIPI_PHY_CLK_MAX)
+                       dconf->bit_rate_max = MIPI_PHY_CLK_MAX;
+
+               LCDPR("mipi dsi bit_rate max=%dMHz\n", dconf->bit_rate_max);
+       } else { /* user define */
+               if (dconf->bit_rate_max < pll_out_fmin / 1000) {
+                       LCDERR("can't support bit_rate %dMHz (min=%dMHz)\n",
+                               dconf->bit_rate_max, (pll_out_fmin / 1000));
+               }
+               if (dconf->bit_rate_max > MIPI_PHY_CLK_MAX) {
+                       LCDPR("[warning]: bit_rate_max %dMHz is out of standard (%dMHz)\n",
+                               dconf->bit_rate_max, MIPI_PHY_CLK_MAX);
+               }
+       }
+
+       /* Venc resolution format */
+       switch (dconf->phy_stop_wait) {
+       case 1: /* standard */
+               dconf->venc_fmt = TV_ENC_LCD768x1024p;
+               break;
+       case 2: /* slow */
+               dconf->venc_fmt = TV_ENC_LCD1280x720;
+               break;
+       case 0: /* auto */
+       default:
+               if ((pconf->lcd_basic.h_active != 240) &&
+                       (pconf->lcd_basic.h_active != 768) &&
+                       (pconf->lcd_basic.h_active != 1920) &&
+                       (pconf->lcd_basic.h_active != 2560))
+                       dconf->venc_fmt = TV_ENC_LCD1280x720;
+               else
+                       dconf->venc_fmt = TV_ENC_LCD768x1024p;
+               break;
+       }
+}
+
+/* bit_rate is confirm by clk_genrate, so internal clk config must after that */
+void lcd_mipi_dsi_config_post(struct lcd_config_s *pconf)
+{
+       unsigned int pclk, lanebyteclk;
+       unsigned int den, num;
+       struct dsi_config_s *dconf = pconf->lcd_control.mipi_config;
+
+       pclk = pconf->lcd_timing.lcd_clk / 1000;
+
+       /* pclk lanebyteclk factor */
+       if (dconf->factor_numerator == 0) {
+               lanebyteclk = dconf->bit_rate / 8 / 1000;
+               LCDPR("pixel_clk = %d.%03dMHz, bit_rate = %d.%03dMHz\n",
+                       (pclk / 1000), (pclk % 1000),
+                       (dconf->bit_rate / 1000000),
+                       ((dconf->bit_rate / 1000) % 1000));
+               dconf->factor_numerator = pclk;
+               dconf->factor_denominator = lanebyteclk;
+       }
+       num = dconf->factor_numerator;
+       den = dconf->factor_denominator;
+       LCDPR("num=%d, den=%d, factor=%d.%02d\n",
+               num, den, (den / num), ((den % num) * 100 / num));
+
+       /* video config */
+       if (dconf->operation_mode_display == OPERATION_VIDEO_MODE) {
+               mipi_dsi_video_config(pconf);
+               if (dconf->video_mode_type != BURST_MODE)
+                       mipi_dsi_non_burst_chunk_config(pconf);
+       }
+
+       /* phy config */
+       mipi_dsi_phy_config(&dsi_phy_config, dconf->bit_rate);
+}
+
+static void mipi_dsi_host_on(struct lcd_config_s *pconf)
+{
+       if (lcd_debug_print_flag)
+               mipi_dsi_print_info(pconf);
+
+       startup_mipi_dsi_host();
+       mipi_dsi_host_init(pconf);
+       dsi_phy_config_set(pconf);
+
+       mipi_dsi_link_on(pconf);
+}
+
+static void mipi_dsi_host_off(void)
+{
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       /* Power down DSI */
+       dsi_host_write(MIPI_DSI_DWC_PWR_UP_OS, 0);
+
+       /* Power down D-PHY, do not have to close dphy */
+       /* dsi_host_write(MIPI_DSI_DWC_PHY_RSTZ_OS,
+        *      (dsi_host_read( MIPI_DSI_DWC_PHY_RSTZ_OS ) & 0xc));
+        */
+       /* dsi_host_write(MIPI_DSI_DWC_PHY_RSTZ_OS, 0xc); */
+
+       dsi_phy_write(MIPI_DSI_CHAN_CTRL, 0x1f);
+       //LCDPR("MIPI_DSI_PHY_CTRL=0x%x\n", dsi_phy_read(MIPI_DSI_PHY_CTRL));
+       dsi_phy_setb(MIPI_DSI_PHY_CTRL, 0, 7, 1);
+}
+
+void lcd_mipi_control_set(struct lcd_config_s *pconf, int status)
+{
+       if (lcd_debug_print_flag)
+               LCDPR("%s: %d\n", __func__, status);
+
+       if (pconf->lcd_control.mipi_config == NULL) {
+               LCDERR("%s: dsi config is NULL\n", __func__);
+               return;
+       }
+
+       if (status)
+               mipi_dsi_host_on(pconf);
+       else
+               mipi_dsi_host_off();
+}
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_tablet/mipi_dsi_util.h b/drivers/amlogic/media/vout/lcd/lcd_tablet/mipi_dsi_util.h
new file mode 100644 (file)
index 0000000..359945f
--- /dev/null
@@ -0,0 +1,539 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tablet/mipi_dsi_util.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef MIPI_DSI_UTIL_H
+#define MIPI_DSI_UTIL_H
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+
+
+/* ********************************************************
+ * MIPI DSI Data Type/ MIPI DCS Command Type Definitions
+ * Pheripheral to Host
+ */
+enum mipi_dsi_data_type_host_e {
+       DT_VSS                  = 0x01,
+       DT_VSE                  = 0x11,
+       DT_HSS                  = 0x21,
+       DT_HSE                  = 0x31,
+       DT_EOTP                 = 0x08,
+       DT_CMOFF                = 0x02,
+       DT_CMON                 = 0x12,
+       DT_SHUT_DOWN            = 0x22,
+       DT_TURN_ON              = 0x32,
+       DT_GEN_SHORT_WR_0       = 0x03,
+       DT_GEN_SHORT_WR_1       = 0x13,
+       DT_GEN_SHORT_WR_2       = 0x23,
+       DT_GEN_RD_0             = 0x04,
+       DT_GEN_RD_1             = 0x14,
+       DT_GEN_RD_2             = 0x24,
+       DT_DCS_SHORT_WR_0       = 0x05,
+       DT_DCS_SHORT_WR_1       = 0x15,
+       DT_DCS_RD_0             = 0x06,
+       DT_SET_MAX_RET_PKT_SIZE = 0x37,
+       DT_NULL_PKT             = 0x09,
+       DT_BLANK_PKT            = 0x19,
+       DT_GEN_LONG_WR          = 0x29,
+       DT_DCS_LONG_WR          = 0x39,
+       DT_20BIT_LOOSE_YCBCR    = 0x0c,
+       DT_24BIT_YCBCR          = 0x1c,
+       DT_16BIT_YCBCR          = 0x2c,
+       DT_30BIT_RGB_101010     = 0x0d,
+       DT_36BIT_RGB_121212     = 0x1d,
+       DT_12BIT_YCBCR          = 0x3d,
+       DT_16BIT_RGB_565        = 0x0e,
+       DT_18BIT_RGB_666        = 0x1e,
+       DT_18BIT_LOOSE_RGB_666  = 0x2e,
+       DT_24BIT_RGB_888        = 0x3e
+};
+
+/* DCS Command List */
+#define DCS_ENTER_IDLE_MODE          0x39
+#define DCS_ENTER_INVERT_MODE        0x21
+#define DCS_ENTER_NORMAL_MODE        0x13
+#define DCS_ENTER_PARTIAL_MODE       0x12
+#define DCS_ENTER_SLEEP_MODE         0x10
+#define DCS_EXIT_IDLE_MODE           0x38
+#define DCS_EXIT_INVERT_MODE         0x20
+#define DCS_EXIT_SLEEP_MODE          0x11
+#define DCS_GET_3D_CONTROL           0x3f
+#define DCS_GET_ADDRESS_MODE         0x0b
+#define DCS_GET_BLUE_CHANNEL         0x08
+#define DCS_GET_DIAGNOSTIC_RESULT    0x0f
+#define DCS_GET_DISPLAY_MODE         0x0d
+#define DCS_GET_GREEN_CHANNEL        0x07
+#define DCS_GET_PIXEL_FORMAT         0x0c
+#define DCS_GET_POWER_MODE           0x0a
+#define DCS_GET_RED_CHANNEL          0x06
+#define DCS_GET_SCANLINE             0x45
+#define DCS_GET_SIGNAL_MODE          0x0e
+#define DCS_NOP                      0x00
+#define DCS_READ_DDB_CONTINUE        0xa8
+#define DCS_READ_DDB_START           0xa1
+#define DCS_READ_MEMORY_CONTINUE     0x3e
+#define DCS_READ_MEMORY_START        0x2e
+#define DCS_SET_3D_CONTROL           0x3d
+#define DCS_SET_ADDRESS_MODE         0x36
+#define DCS_SET_COLUMN_ADDRESS       0x2a
+#define DCS_SET_DISPLAY_OFF          0x28
+#define DCS_SET_DISPLAY_ON           0x29
+#define DCS_SET_GAMMA_CURVE          0x26
+#define DCS_SET_PAGE_ADDRESS         0x2b
+#define DCS_SET_PARTIAL_COLUMNS      0x31
+#define DCS_SET_PARTIAL_ROWS         0x30
+#define DCS_SET_PIXEL_FORMAT         0x3a
+#define DCS_SET_SCROLL_AREA          0x33
+#define DCS_SET_SCROLL_START         0x37
+#define DCS_SET_TEAR_OFF             0x34
+#define DCS_SET_TEAR_ON              0x35
+#define DCS_SET_TEAR_SCANLINE        0x44
+#define DCS_SET_VSYNC_TIMING         0x40
+#define DCS_SOFT_RESET               0x01
+#define DCS_WRITE_LUT                0x2d
+#define DCS_WRITE_MEMORY_CONTINUE    0x3c
+#define DCS_WRITE_MEMORY_START       0x2c
+
+/* Pheripheral to Host
+ * normal: 0x87(LPDT), data_type, 0, 0, ecc.  (write or tearing-effect)
+ * error:  0x87(LPDT), 0x02, error_code[15:0], ecc.
+ * short read: 0x87, data_type, data0, data1, ecc
+ * long read:  0x87, data_type, word_cnt[15:0], ecc, data0, ... data(N-1),
+ *             checksum(or 0)[15:0].
+ */
+enum mipi_dsi_data_type_peripheral_e {
+       DT_RESP_TE             = 0xba,
+       DT_RESP_ACK            = 0x84,
+       DT_RESP_ACK_ERR        = 0x02,
+       DT_RESP_EOT            = 0x08,
+       DT_RESP_GEN_READ_1     = 0x11,
+       DT_RESP_GEN_READ_2     = 0x12,
+       DT_RESP_GEN_READ_LONG  = 0x1a,
+       DT_RESP_DCS_READ_LONG  = 0x1c,
+       DT_RESP_DCS_READ_1     = 0x21,
+       DT_RESP_DCS_READ_2     = 0x22,
+};
+
+struct dsi_cmd_request_s {
+       unsigned char data_type;
+       unsigned char vc_id;
+       unsigned char *payload;
+       unsigned short pld_count;
+       unsigned int req_ack;
+};
+
+/*  MIPI DCS Pixel-to-Byte Format */
+#define DCS_PF_RSVD                  0x0
+#define DCS_PF_3BIT                  0x1
+#define DCS_PF_8BIT                  0x2
+#define DCS_PF_12BIT                 0x3
+#define DCS_PF_16BIT                 0x5
+#define DCS_PF_18BIT                 0x6
+#define DCS_PF_24BIT                 0x7
+
+/*  MIPI DSI/VENC Color Format Definitions */
+#define MIPI_DSI_VENC_COLOR_30B   0x0
+#define MIPI_DSI_VENC_COLOR_24B   0x1
+#define MIPI_DSI_VENC_COLOR_18B   0x2
+#define MIPI_DSI_VENC_COLOR_16B   0x3
+
+#define COLOR_16BIT_CFG_1         0x0
+#define COLOR_16BIT_CFG_2         0x1
+#define COLOR_16BIT_CFG_3         0x2
+#define COLOR_18BIT_CFG_1         0x3
+#define COLOR_18BIT_CFG_2         0x4
+#define COLOR_24BIT               0x5
+#define COLOR_20BIT_LOOSE         0x6
+#define COLOR_24_BIT_YCBCR        0x7
+#define COLOR_16BIT_YCBCR         0x8
+#define COLOR_30BIT               0x9
+#define COLOR_36BIT               0xa
+#define COLOR_12BIT               0xb
+#define COLOR_RGB_111             0xc
+#define COLOR_RGB_332             0xd
+#define COLOR_RGB_444             0xe
+
+/*  MIPI DSI Relative REGISTERs Definitions */
+/* For MIPI_DSI_TOP_CNTL */
+#define BIT_DPI_COLOR_MODE        20
+#define BIT_IN_COLOR_MODE         16
+#define BIT_CHROMA_SUBSAMPLE      14
+#define BIT_COMP2_SEL             12
+#define BIT_COMP1_SEL             10
+#define BIT_COMP0_SEL              8
+#define BIT_DE_POL                 6
+#define BIT_HSYNC_POL              5
+#define BIT_VSYNC_POL              4
+#define BIT_DPICOLORM              3
+#define BIT_DPISHUTDN              2
+#define BIT_EDPITE_INTR_PULSE      1
+#define BIT_ERR_INTR_PULSE         0
+
+/* For MIPI_DSI_DWC_CLKMGR_CFG_OS */
+#define BIT_TO_CLK_DIV            8
+#define BIT_TX_ESC_CLK_DIV        0
+
+/* For MIPI_DSI_DWC_PCKHDL_CFG_OS */
+#define BIT_CRC_RX_EN             4
+#define BIT_ECC_RX_EN             3
+#define BIT_BTA_EN                2
+#define BIT_EOTP_RX_EN            1
+#define BIT_EOTP_TX_EN            0
+
+/* For MIPI_DSI_DWC_VID_MODE_CFG_OS */
+#define BIT_LP_CMD_EN            15
+#define BIT_FRAME_BTA_ACK_EN     14
+#define BIT_LP_HFP_EN            13
+#define BIT_LP_HBP_EN            12
+#define BIT_LP_VCAT_EN           11
+#define BIT_LP_VFP_EN            10
+#define BIT_LP_VBP_EN             9
+#define BIT_LP_VSA_EN             8
+#define BIT_VID_MODE_TYPE         0
+
+/* For MIPI_DSI_DWC_PHY_STATUS_OS */
+#define BIT_PHY_ULPSACTIVENOT3LANE 12
+#define BIT_PHY_STOPSTATE3LANE     11
+#define BIT_PHY_ULPSACTIVENOT2LANE 10
+#define BIT_PHY_STOPSTATE2LANE      9
+#define BIT_PHY_ULPSACTIVENOT1LANE  8
+#define BIT_PHY_STOPSTATE1LANE      7
+#define BIT_PHY_RXULPSESC0LANE      6
+#define BIT_PHY_ULPSACTIVENOT0LANE  5
+#define BIT_PHY_STOPSTATE0LANE      4
+#define BIT_PHY_ULPSACTIVENOTCLK    3
+#define BIT_PHY_STOPSTATECLKLANE    2
+#define BIT_PHY_DIRECTION           1
+#define BIT_PHY_LOCK                0
+
+/* For MIPI_DSI_DWC_PHY_IF_CFG_OS */
+#define BIT_PHY_STOP_WAIT_TIME      8
+#define BIT_N_LANES                 0
+
+/* For MIPI_DSI_DWC_DPI_COLOR_CODING_OS */
+#define BIT_LOOSELY18_EN            8
+#define BIT_DPI_COLOR_CODING        0
+
+/* For MIPI_DSI_DWC_GEN_HDR_OS */
+#define BIT_GEN_WC_MSBYTE          16
+#define BIT_GEN_WC_LSBYTE           8
+#define BIT_GEN_VC                  6
+#define BIT_GEN_DT                  0
+
+/* For MIPI_DSI_DWC_LPCLK_CTRL_OS */
+#define BIT_AUTOCLKLANE_CTRL        1
+#define BIT_TXREQUESTCLKHS          0
+
+/* For MIPI_DSI_DWC_DPI_CFG_POL_OS */
+#define BIT_COLORM_ACTIVE_LOW       4
+#define BIT_SHUTD_ACTIVE_LOW        3
+#define BIT_HSYNC_ACTIVE_LOW        2
+#define BIT_VSYNC_ACTIVE_LOW        1
+#define BIT_DATAEN_ACTIVE_LOW       0
+
+/* For MIPI_DSI_DWC_CMD_MODE_CFG_OS */
+#define BIT_MAX_RD_PKT_SIZE        24
+#define BIT_DCS_LW_TX              19
+#define BIT_DCS_SR_0P_TX           18
+#define BIT_DCS_SW_1P_TX           17
+#define BIT_DCS_SW_0P_TX           16
+#define BIT_GEN_LW_TX              14
+#define BIT_GEN_SR_2P_TX           13
+#define BIT_GEN_SR_1P_TX           12
+#define BIT_GEN_SR_0P_TX           11
+#define BIT_GEN_SW_2P_TX           10
+#define BIT_GEN_SW_1P_TX            9
+#define BIT_GEN_SW_0P_TX            8
+#define BIT_ACK_RQST_EN             1
+#define BIT_TEAR_FX_EN              0
+
+/* For MIPI_DSI_DWC_CMD_PKT_STATUS_OS */
+/* For DBI no use full */
+#define BIT_DBI_RD_CMD_BUSY        14
+#define BIT_DBI_PLD_R_FULL         13
+#define BIT_DBI_PLD_R_EMPTY        12
+#define BIT_DBI_PLD_W_FULL         11
+#define BIT_DBI_PLD_W_EMPTY        10
+#define BIT_DBI_CMD_FULL            9
+#define BIT_DBI_CMD_EMPTY           8
+/* For Generic interface */
+#define BIT_GEN_RD_CMD_BUSY         6
+#define BIT_GEN_PLD_R_FULL          5
+#define BIT_GEN_PLD_R_EMPTY         4
+#define BIT_GEN_PLD_W_FULL          3
+#define BIT_GEN_PLD_W_EMPTY         2
+#define BIT_GEN_CMD_FULL            1
+#define BIT_GEN_CMD_EMPTY           0
+
+/* For MIPI_DSI_TOP_MEAS_CNTL */
+/* measure vsync control */
+#define BIT_CNTL_MEAS_VSYNC        10
+/* tear measure enable */
+#define BIT_EDPITE_MEAS_EN          9
+/* not clear the counter */
+#define BIT_EDPITE_ACCUM_MEAS_EN    8
+#define BIT_EDPITE_VSYNC_SPAN       0
+
+/* For MIPI_DSI_TOP_STAT */
+/* signal from halt */
+#define BIT_STAT_EDPIHALT          31
+/* line number when edpite pulse */
+#define BIT_STAT_TE_LINE           16
+/* pixel number when edpite pulse */
+#define BIT_STAT_TE_PIXEL           0
+
+/* For MIPI_DSI_TOP_INTR_CNTL_STAT */
+/* State/Clear for pic_eof */
+#define BIT_STAT_CLR_DWC_PIC_EOF   21
+/* State/Clear for de_fall */
+#define BIT_STAT_CLR_DWC_DE_FALL   20
+/* State/Clear for de_rise */
+#define BIT_STAT_CLR_DWC_DE_RISE   19
+/* State/Clear for vs_fall */
+#define BIT_STAT_CLR_DWC_VS_FALL   18
+/* State/Clear for vs_rise */
+#define BIT_STAT_CLR_DWC_VS_RISE   17
+/* State/Clear for edpite */
+#define BIT_STAT_CLR_DWC_EDPITE    16
+/* end of picture */
+#define BIT_PIC_EOF                 5
+/* data enable fall */
+#define BIT_DE_FALL                 4
+/* data enable rise */
+#define BIT_DE_RISE                 3
+/* vsync fall */
+#define BIT_VS_FALL                 2
+/* vsync rise */
+#define BIT_VS_RISE                 1
+/* edpite int enable */
+#define BIT_EDPITE_INT_EN           0
+
+/* For MIPI_DSI_TOP_MEAS_CNTL */
+/* vsync measure enable */
+#define BIT_VSYNC_MEAS_EN          19
+/* vsync accumulate measure */
+#define BIT_VSYNC_ACCUM_MEAS_EN    18
+/* vsync span */
+#define BIT_VSYNC_SPAN             10
+/* tearing measure enable */
+#define BIT_TE_MEAS_EN              9
+/* tearing accumulate measure */
+#define BIT_TE_ACCUM_MEAS_EN        8
+/* tearing span */
+#define BIT_TE_SPAN                 0
+
+/* For MIPI_DSI_DWC_INT_ST0_OS */
+/* LP1 contention error from lane0 */
+#define BIT_DPHY_ERR_4             20
+/* LP0 contention error from lane0 */
+#define BIT_DPHY_ERR_3             19
+/* ErrControl error from lane0 */
+#define BIT_DPHY_ERR_2             18
+/* ErrSyncEsc error from lane0 */
+#define BIT_DPHY_ERR_1             17
+/* ErrEsc escape error lane0 */
+#define BIT_DPHY_ERR_0             16
+#define BIT_ACK_ERR_15             15
+#define BIT_ACK_ERR_14             14
+#define BIT_ACK_ERR_13             13
+#define BIT_ACK_ERR_12             12
+#define BIT_ACK_ERR_11             11
+#define BIT_ACK_ERR_10             10
+#define BIT_ACK_ERR_9               9
+#define BIT_ACK_ERR_8               8
+#define BIT_ACK_ERR_7               7
+#define BIT_ACK_ERR_6               6
+#define BIT_ACK_ERR_5               5
+#define BIT_ACK_ERR_4               4
+#define BIT_ACK_ERR_3               3
+#define BIT_ACK_ERR_2               2
+#define BIT_ACK_ERR_1               1
+#define BIT_ACK_ERR_0               0
+
+/* Command transfer type in command mode */
+#define DCS_TRANS_HS                0
+#define DCS_TRANS_LP                1
+
+#define MIPI_DSI_DCS_NO_ACK         0
+#define MIPI_DSI_DCS_REQ_ACK        1
+
+/* DSI Tear Defines */
+#define MIPI_DCS_SET_TEAR_ON_MODE_0         0
+#define MIPI_DCS_SET_TEAR_ON_MODE_1         1
+#define MIPI_DCS_ENABLE_TEAR                1
+#define MIPI_DCS_DISABLE_TEAR               0
+
+/* Pixel FIFO Depth */
+#define PIXEL_FIFO_DEPTH                    1440
+
+#define BYTE_PER_PIXEL_COLOR_16BIT_CFG_1    2
+#define BYTE_PER_PIXEL_COLOR_16BIT_CFG_2    2
+#define BYTE_PER_PIXEL_COLOR_16BIT_CFG_3    2
+#define BYTE_PER_PIXEL_COLOR_18BIT_CFG_1    3
+#define BYTE_PER_PIXEL_COLOR_18BIT_CFG_2    3
+#define BYTE_PER_PIXEL_COLOR_24BIT          3
+#define BYTE_PER_PIXEL_COLOR_20BIT_LOOSE    3
+#define BYTE_PER_PIXEL_COLOR_24_BIT_YCBCR   3
+#define BYTE_PER_PIXEL_COLOR_16BIT_YCBCR    2
+#define BYTE_PER_PIXEL_COLOR_30BIT          4
+#define BYTE_PER_PIXEL_COLOR_36BIT          5
+/* in fact it should be 1.5(12bit) */
+#define BYTE_PER_PIXEL_COLOR_12BIT          3
+
+/* Tearing Interrupt Bit */
+#define INT_TEARING                         6
+
+enum tv_enc_lcd_type_e {
+       TV_ENC_LCD480x234 = 0,
+       /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+       TV_ENC_LCD480x234_dsi36b = 1,
+       TV_ENC_LCD240x160 = 2,
+       /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+       TV_ENC_LCD240x160_dsi36b = 3,
+       TV_ENC_LCD720x480 = 4,
+       /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+       TV_ENC_LCD720x480_dsi36b = 5,
+       TV_ENC_LCD720x576 = 6,
+       /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+       TV_ENC_LCD720x576_dsi36b = 7,
+       TV_ENC_LCD1280x720 = 8,
+       /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+       TV_ENC_LCD1280x720_dsi36b = 9,
+       TV_ENC_LCD1920x1080 = 10,
+       /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+       TV_ENC_LCD1920x1080_dsi36b = 11,
+       TV_ENC_LCD1920x2205 = 12,
+       /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+       TV_ENC_LCD1920x2205_dsi36b = 13,
+       TV_ENC_LCD2560x1600 = 14,
+       /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+       TV_ENC_LCD2560x1600_dsi36b = 15,
+       TV_ENC_LCD3840x2440 = 16,
+       /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+       TV_ENC_LCD3840x2440_dsi36b = 17,
+       TV_ENC_LCD3840x2160p_vic03 = 18,
+       TV_ENC_LCD4096x2160p_vic04 = 19,
+       TV_ENC_LCD640x480 = 20,
+       TV_ENC_LCD1920x1200p = 21,
+       TV_ENC_LCD240x160_dsi   = 22,
+       TV_ENC_LCD240x160_slow   = 23,
+       TV_ENC_LCD3840x2160p_vic01 = 24,
+       TV_ENC_LCD2048x1536  = 25,
+       TV_ENC_LCD768x1024p = 26,
+       TV_ENC_LCD_TYPE_MAX
+};   /* tv encoder output format */
+
+/* DCS COMMAND LIST */
+#define DCS_CMD_CODE_ENTER_IDLE_MODE      0x0
+#define DCS_CMD_CODE_ENTER_INVERT_MODE    0x1
+#define DCS_CMD_CODE_ENTER_NORMAL_MODE    0x2
+#define DCS_CMD_CODE_ENTER_PARTIAL_MODE   0x3
+#define DCS_CMD_CODE_ENTER_SLEEP_MODE     0x4
+#define DCS_CMD_CODE_EXIT_IDLE_MODE       0x5
+#define DCS_CMD_CODE_EXIT_INVERT_MODE     0x6
+#define DCS_CMD_CODE_EXIT_SLEEP_MODE      0x7
+#define DCS_CMD_CODE_NOP                  0x8
+#define DCS_CMD_CODE_SET_DISPLAY_OFF      0x9
+#define DCS_CMD_CODE_SET_DISPLAY_ON       0xa
+#define DCS_CMD_CODE_SET_TEAR_OFF         0xb
+#define DCS_CMD_CODE_SOFT_RESET           0xc
+
+/* DPHY standard timing */
+/* unit: MHz */
+#define MIPI_PHY_CLK_MAX            1000
+
+/* **** DPHY timing parameter       Value (unit: 0.01ns) **** */
+/* >100ns (4M) */
+#define DPHY_TIME_LP_TESC(ui)       (250 * 100)
+/* >50ns */
+#define DPHY_TIME_LP_LPX(ui)        (100 * 100)
+/* (lpx, 2*lpx) */
+#define DPHY_TIME_LP_TA_SURE(ui)    DPHY_TIME_LP_LPX(ui)
+/* 4*lpx */
+#define DPHY_TIME_LP_TA_GO(ui)      (4 * DPHY_TIME_LP_LPX(ui))
+/* 5*lpx */
+#define DPHY_TIME_LP_TA_GETX(ui)    (5 * DPHY_TIME_LP_LPX(ui))
+/* >100ns */
+#define DPHY_TIME_HS_EXIT(ui)       (120 * 100)
+/* max(8*ui, 60+4*ui), (teot)<105+12*ui */
+#define DPHY_TIME_HS_TRAIL(ui)      ((ui > (60 * 100 / 4)) ? \
+                                       (8 * ui) : ((60 * 100) + 4 * ui))
+/* (40+4*ui, 85+6*ui) */
+#define DPHY_TIME_HS_PREPARE(ui)    (50 * 100 + 4 * t_ui)
+/* hs_prepare+hs_zero >145+10*ui */
+#define DPHY_TIME_HS_ZERO(ui)       (160 * 100 + 10 * ui - \
+                                       DPHY_TIME_HS_PREPARE(ui))
+/* >60ns, (teot)<105+12*ui */
+#define DPHY_TIME_CLK_TRAIL(ui)     (70 * 100)
+/* >60+52*ui */
+#define DPHY_TIME_CLK_POST(ui)      (70 * 100 + 52 * ui)
+/* (38, 95) */
+#define DPHY_TIME_CLK_PREPARE(ui)   (50 * 100)
+/* clk_prepare+clk_zero > 300 */
+#define DPHY_TIME_CLK_ZERO(ui)      (320 * 100 - DPHY_TIME_CLK_PREPARE(ui))
+/* >8*ui */
+#define DPHY_TIME_CLK_PRE(ui)       (10 * ui)
+/* >100us */
+#define DPHY_TIME_INIT(ui)          (110 * 1000 * 100)
+/* >1ms */
+#define DPHY_TIME_WAKEUP(ui)        (1020 * 1000 * 100)
+
+
+struct dsi_phy_s {
+       unsigned int lp_tesc;
+       unsigned int lp_lpx;
+       unsigned int lp_ta_sure;
+       unsigned int lp_ta_go;
+       unsigned int lp_ta_get;
+       unsigned int hs_exit;
+       unsigned int hs_trail;
+       unsigned int hs_zero;
+       unsigned int hs_prepare;
+       unsigned int clk_trail;
+       unsigned int clk_post;
+       unsigned int clk_zero;
+       unsigned int clk_prepare;
+       unsigned int clk_pre;
+       unsigned int init;
+       unsigned int wakeup;
+};
+
+struct dsi_vid_s {
+       unsigned int hline;
+       unsigned int hsa;
+       unsigned int hbp;
+       unsigned int vsa;
+       unsigned int vbp;
+       unsigned int vfp;
+       unsigned int vact;
+
+       /* for non-burst chunk overhead */
+       unsigned int pixel_per_chunk;
+       unsigned int num_of_chunk;
+       unsigned int vid_null_size;
+};
+
+#define DSI_CMD_SIZE_MAX               2000
+/* #define DSI_CMD_READ_VALID   // DPHY don't support for M8 */
+
+extern void mipi_dsi_print_info(struct lcd_config_s *pconf);
+extern int lcd_mipi_dsi_init_table_detect(struct device_node *m_node,
+               struct dsi_config_s *dconf, int on_off);
+extern void lcd_mipi_dsi_config_set(struct lcd_config_s *pconf);
+extern void lcd_mipi_dsi_config_post(struct lcd_config_s *pconf);
+extern void mipi_dsi_link_off(struct lcd_config_s *pconf);
+extern void lcd_mipi_control_set(struct lcd_config_s *pconf, int status);
+
+#endif
diff --git a/drivers/amlogic/media/vout/lcd/lcd_tv/Makefile b/drivers/amlogic/media/vout/lcd/lcd_tv/Makefile
new file mode 100644 (file)
index 0000000..3be366f
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_AMLOGIC_LCD_TV) += lcd_tv.o lcd_drv.o
diff --git a/drivers/amlogic/media/vout/lcd/lcd_tv/lcd_drv.c b/drivers/amlogic/media/vout/lcd/lcd_tv/lcd_drv.c
new file mode 100644 (file)
index 0000000..a2ec892
--- /dev/null
@@ -0,0 +1,1435 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tv/lcd_drv.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#ifdef CONFIG_AML_VPU
+#include <linux/amlogic/vpu.h>
+#endif
+#include <linux/amlogic/media/vout/vinfo.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include "lcd_tv.h"
+#include "../lcd_reg.h"
+#include "../lcd_common.h"
+
+/* interrupt source */
+#define INT_VIU_VSYNC    35
+#define INT_VENC_VX1     110
+
+static int vx1_fsm_acq_st;
+
+#define VX1_TRAINING_TIMEOUT    60  /* vsync cnt */
+static int vx1_training_wait_cnt;
+static int vx1_training_stable_cnt;
+static int vx1_timeout_reset_flag;
+static struct tasklet_struct  lcd_vx1_reset_tasklet;
+static int lcd_vx1_intr_request;
+
+#define VX1_HPLL_INTERVAL (HZ)
+static struct timer_list vx1_hpll_timer;
+
+static int lcd_type_supported(struct lcd_config_s *pconf)
+{
+       int lcd_type = pconf->lcd_basic.lcd_type;
+       int ret = -1;
+
+       switch (lcd_type) {
+       case LCD_LVDS:
+       case LCD_VBYONE:
+               ret = 0;
+               break;
+       default:
+               LCDERR("invalid lcd type: %s(%d)\n",
+                       lcd_type_type_to_str(lcd_type), lcd_type);
+               break;
+       }
+       return ret;
+}
+
+static void lcd_vbyone_phy_set(struct lcd_config_s *pconf, int status)
+{
+       unsigned int vswing, preem, ext_pullup;
+       unsigned int data32;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s: %d\n", __func__, status);
+
+       if (status) {
+               ext_pullup =
+                       (pconf->lcd_control.vbyone_config->phy_vswing >> 4) & 1;
+               vswing = pconf->lcd_control.vbyone_config->phy_vswing & 0xf;
+               preem = pconf->lcd_control.vbyone_config->phy_preem;
+               if (vswing > 7) {
+                       LCDERR("%s: wrong vswing_level=%d, use default\n",
+                               __func__, vswing);
+                       vswing = VX1_PHY_VSWING_DFT;
+               }
+               if (preem > 7) {
+                       LCDERR("%s: wrong preemphasis_level=%d, use default\n",
+                               __func__, preem);
+                       preem = VX1_PHY_PREEM_DFT;
+               }
+               data32 = 0x6e0ec900 | (vswing << 3) | (ext_pullup << 10);
+               if (ext_pullup)
+                       data32 &= ~(1 << 15);
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, data32);
+               data32 = 0x00000a7c | (preem << 20);
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, data32);
+               /*lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x6e0ec918);*/
+               /*lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x00000a7c);*/
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x00ff0800);
+       } else {
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x0);
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x0);
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x0);
+       }
+}
+
+static void lcd_lvds_phy_set(struct lcd_config_s *pconf, int status)
+{
+       unsigned int vswing, preem, clk_vswing, clk_preem, channel_on;
+       unsigned int data32;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s: %d\n", __func__, status);
+
+       if (status) {
+               vswing = pconf->lcd_control.lvds_config->phy_vswing;
+               preem = pconf->lcd_control.lvds_config->phy_preem;
+               clk_vswing = pconf->lcd_control.lvds_config->phy_clk_vswing;
+               clk_preem = pconf->lcd_control.lvds_config->phy_clk_preem;
+               if (vswing > 7) {
+                       LCDERR("%s: wrong vswing_level=%d, use default\n",
+                               __func__, vswing);
+                       vswing = LVDS_PHY_VSWING_DFT;
+               }
+               if (preem > 7) {
+                       LCDERR("%s: wrong preem_level=%d, use default\n",
+                               __func__, preem);
+                       preem = LVDS_PHY_PREEM_DFT;
+               }
+               if (clk_vswing > 3) {
+                       LCDERR("%s: wrong clk_vswing_level=%d, use default\n",
+                               __func__, clk_vswing);
+                       clk_vswing = LVDS_PHY_CLK_VSWING_DFT;
+               }
+               if (clk_preem > 7) {
+                       LCDERR("%s: wrong clk_preem_level=%d, use default\n",
+                               __func__, clk_preem);
+                       clk_preem = LVDS_PHY_CLK_PREEM_DFT;
+               }
+               channel_on = lcd_lvds_channel_on_value(pconf);
+
+               data32 = 0x606cca80 | (vswing << 26) | (preem << 0);
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, data32);
+               /*lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x6c6cca80);*/
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x0000006c);
+               data32 = (channel_on << 16) | 0x0800 | /* DIF_TX_CTL5 */
+                       (clk_vswing << 8) | (clk_preem << 5); /* DIF_TX_CTL4 */
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, data32);
+               /*lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x0fff0800);*/
+       } else {
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x0);
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x0);
+               lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x0);
+       }
+}
+
+static void lcd_tcon_set(struct lcd_config_s *pconf)
+{
+       lcd_vcbus_write(L_RGB_BASE_ADDR, 0);
+       lcd_vcbus_write(L_RGB_COEFF_ADDR, 0x400);
+       aml_lcd_notifier_call_chain(LCD_EVENT_GAMMA_UPDATE, NULL);
+
+       switch (pconf->lcd_basic.lcd_bits) {
+       case 8:
+               lcd_vcbus_write(L_DITH_CNTL_ADDR,  0x400);
+               break;
+       case 6:
+               lcd_vcbus_write(L_DITH_CNTL_ADDR,  0x600);
+               break;
+       case 10:
+       default:
+               lcd_vcbus_write(L_DITH_CNTL_ADDR,  0x0);
+               break;
+       }
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_LVDS:
+               lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 0, 1);
+               if (pconf->lcd_timing.vsync_pol)
+                       lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 1, 1);
+               break;
+       case LCD_VBYONE:
+               if (pconf->lcd_timing.hsync_pol)
+                       lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 0, 1);
+               if (pconf->lcd_timing.vsync_pol)
+                       lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 1, 1);
+               break;
+       default:
+               break;
+       }
+
+       if (lcd_vcbus_read(VPP_MISC) & VPP_OUT_SATURATE)
+               lcd_vcbus_write(VPP_MISC,
+                       lcd_vcbus_read(VPP_MISC) & ~(VPP_OUT_SATURATE));
+}
+
+static void lcd_lvds_clk_util_set(struct lcd_config_s *pconf)
+{
+       unsigned int phy_div;
+
+       if (pconf->lcd_control.lvds_config->dual_port)
+               phy_div = 2;
+       else
+               phy_div = 1;
+
+       /* set fifo_clk_sel: div 7 */
+       lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL0, (1 << 6));
+       /* set cntl_ser_en:  8-channel to 1 */
+       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0xfff, 16, 12);
+
+       /* decoupling fifo enable, gated clock enable */
+       lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL1,
+               (1 << 30) | ((phy_div - 1) << 25) | (1 << 24));
+       /* decoupling fifo write enable after fifo enable */
+       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL1, 1, 31, 1);
+}
+
+static void lcd_lvds_control_set(struct lcd_config_s *pconf)
+{
+       unsigned int bit_num = 1;
+       unsigned int pn_swap, port_swap, lane_reverse;
+       unsigned int dual_port, fifo_mode;
+       unsigned int lvds_repack = 1;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       lcd_lvds_clk_util_set(pconf);
+
+       lvds_repack = (pconf->lcd_control.lvds_config->lvds_repack) & 0x3;
+       pn_swap   = (pconf->lcd_control.lvds_config->pn_swap) & 0x1;
+       dual_port = (pconf->lcd_control.lvds_config->dual_port) & 0x1;
+       port_swap = (pconf->lcd_control.lvds_config->port_swap) & 0x1;
+       lane_reverse = (pconf->lcd_control.lvds_config->lane_reverse) & 0x1;
+
+       switch (pconf->lcd_basic.lcd_bits) {
+       case 10:
+               bit_num = 0;
+               if (lvds_repack == 1)
+                       lvds_repack = 2;
+               break;
+       case 8:
+               bit_num = 1;
+               break;
+       case 6:
+               bit_num = 2;
+               break;
+       case 4:
+               bit_num = 3;
+               break;
+       default:
+               bit_num = 1;
+               break;
+       }
+       if (dual_port)
+               fifo_mode = 0x3;
+       else
+               fifo_mode = 0x1;
+
+       lcd_vcbus_write(LVDS_PACK_CNTL_ADDR,
+               (lvds_repack << 0) | /* repack //[1:0] */
+               (0 << 3) |      /* reserve */
+               (0 << 4) |      /* lsb first */
+               (pn_swap << 5) | /* pn swap */
+               (dual_port << 6) | /* dual port */
+               (0 << 7) |      /* use tcon control */
+               (bit_num << 8) | /* 0:10bits, 1:8bits, 2:6bits, 3:4bits */
+               (0 << 10) |     /* r_select  //0:R, 1:G, 2:B, 3:0 */
+               (1 << 12) |     /* g_select  //0:R, 1:G, 2:B, 3:0 */
+               (2 << 14));     /* b_select  //0:R, 1:G, 2:B, 3:0 */
+
+       lcd_vcbus_setb(LCD_PORT_SWAP, port_swap, 12, 1);
+
+       if (lane_reverse)
+               lcd_vcbus_setb(LVDS_GEN_CNTL, 0x03, 13, 2);
+
+       lcd_vcbus_write(LVDS_GEN_CNTL,
+               (lcd_vcbus_read(LVDS_GEN_CNTL) | (1 << 4) | (fifo_mode << 0)));
+
+       lcd_vcbus_setb(LVDS_GEN_CNTL, 1, 3, 1);
+}
+
+static void lcd_lvds_disable(void)
+{
+       lcd_vcbus_setb(LVDS_GEN_CNTL, 0, 3, 1); /* disable lvds fifo */
+}
+
+#if 0
+static void lcd_vbyone_ctlbits(int p3d_en, int p3d_lr, int mode)
+{
+       if (mode == 0) { /* insert at the first pixel */
+               lcd_vcbus_setb(VBO_PXL_CTRL,
+                       (1 << p3d_en) | (p3d_lr & 0x1), 0, 4);
+       } else {
+               lcd_vcbus_setb(VBO_VBK_CTRL_0,
+                       (1 << p3d_en) | (p3d_lr & 0x1), 0, 2);
+       }
+}
+#endif
+
+static void lcd_vbyone_sync_pol(int hsync_pol, int vsync_pol)
+{
+       lcd_vcbus_setb(VBO_VIN_CTRL, hsync_pol, 4, 1);
+       lcd_vcbus_setb(VBO_VIN_CTRL, vsync_pol, 5, 1);
+
+       lcd_vcbus_setb(VBO_VIN_CTRL, hsync_pol, 6, 1);
+       lcd_vcbus_setb(VBO_VIN_CTRL, vsync_pol, 7, 1);
+}
+
+static void lcd_vbyone_clk_util_set(struct lcd_config_s *pconf)
+{
+       unsigned int lcd_bits;
+       unsigned int div_sel, phy_div;
+
+       phy_div = pconf->lcd_control.vbyone_config->phy_div;
+
+       lcd_bits = 10;
+       switch (lcd_bits) {
+       case 6:
+               div_sel = 0;
+               break;
+       case 8:
+               div_sel = 2;
+               break;
+       case 10:
+               div_sel = 3;
+               break;
+       default:
+               div_sel = 3;
+               break;
+       }
+       /* set fifo_clk_sel */
+       lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL0, (div_sel << 6));
+       /* set cntl_ser_en:  8-channel to 1 */
+       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0xfff, 16, 12);
+
+       /* decoupling fifo enable, gated clock enable */
+       lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL1,
+               (1 << 30) | ((phy_div - 1) << 25) | (1 << 24));
+       /* decoupling fifo write enable after fifo enable */
+       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL1, 1, 31, 1);
+}
+
+static int lcd_vbyone_lanes_set(int lane_num, int byte_mode, int region_num,
+               int hsize, int vsize)
+{
+       int sublane_num;
+       int region_size[4];
+       int tmp;
+
+       switch (lane_num) {
+       case 1:
+       case 2:
+       case 4:
+       case 8:
+               break;
+       default:
+               return -1;
+       }
+       switch (region_num) {
+       case 1:
+       case 2:
+       case 4:
+               break;
+       default:
+               return -1;
+       }
+       if (lane_num % region_num)
+               return -1;
+       switch (byte_mode) {
+       case 3:
+       case 4:
+               break;
+       default:
+               return -1;
+       }
+       if (lcd_debug_print_flag) {
+               LCDPR("byte_mode=%d, lane_num=%d, region_num=%d\n",
+                       byte_mode, lane_num, region_num);
+       }
+
+       sublane_num = lane_num / region_num; /* lane num in each region */
+       lcd_vcbus_setb(VBO_LANES, (lane_num - 1), 0, 3);
+       lcd_vcbus_setb(VBO_LANES, (region_num - 1), 4, 2);
+       lcd_vcbus_setb(VBO_LANES, (sublane_num - 1), 8, 3);
+       lcd_vcbus_setb(VBO_LANES, (byte_mode - 1), 11, 2);
+
+       if (region_num > 1) {
+               region_size[3] = (hsize / lane_num) * sublane_num;
+               tmp = (hsize % lane_num);
+               region_size[0] = region_size[3] + (((tmp / sublane_num) > 0) ?
+                       sublane_num : (tmp % sublane_num));
+               region_size[1] = region_size[3] + (((tmp / sublane_num) > 1) ?
+                       sublane_num : (tmp % sublane_num));
+               region_size[2] = region_size[3] + (((tmp / sublane_num) > 2) ?
+                       sublane_num : (tmp % sublane_num));
+               lcd_vcbus_write(VBO_REGION_00, region_size[0]);
+               lcd_vcbus_write(VBO_REGION_01, region_size[1]);
+               lcd_vcbus_write(VBO_REGION_02, region_size[2]);
+               lcd_vcbus_write(VBO_REGION_03, region_size[3]);
+       }
+       lcd_vcbus_write(VBO_ACT_VSIZE, vsize);
+       /* different from FBC code!!! */
+       /* lcd_vcbus_setb(VBO_CTRL_H,0x80,11,5); */
+       /* different from simulation code!!! */
+       lcd_vcbus_setb(VBO_CTRL_H, 0x0, 0, 4);
+       lcd_vcbus_setb(VBO_CTRL_H, 0x1, 9, 1);
+       /* lcd_vcbus_setb(VBO_CTRL_L,enable,0,1); */
+
+       return 0;
+}
+
+static void lcd_vbyone_sw_reset(void)
+{
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       /* force PHY to 0 */
+       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2);
+       lcd_vcbus_write(VBO_SOFT_RST, 0x1ff);
+       udelay(5);
+       /* realease PHY */
+       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2);
+       lcd_vcbus_write(VBO_SOFT_RST, 0);
+}
+
+static void lcd_vbyone_wait_timing_stable(void)
+{
+       unsigned int timing_state;
+       int i = 200;
+
+       timing_state = lcd_vcbus_read(VBO_INTR_STATE) & 0x1ff;
+       while ((timing_state) && (i > 0)) {
+               /* clear video timing error intr */
+               lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x7, 0, 3);
+               lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 3);
+               mdelay(2);
+               timing_state = lcd_vcbus_read(VBO_INTR_STATE) & 0x1ff;
+               i--;
+       };
+       if (lcd_debug_print_flag) {
+               LCDPR("vbyone timing state: 0x%03x, i=%d\n",
+                       timing_state, (200 - i));
+       }
+       mdelay(2);
+}
+
+static void lcd_vbyone_control_set(struct lcd_config_s *pconf)
+{
+       int lane_count, byte_mode, region_num, hsize, vsize, color_fmt;
+       int vin_color, vin_bpp;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       hsize = pconf->lcd_basic.h_active;
+       vsize = pconf->lcd_basic.v_active;
+       lane_count = pconf->lcd_control.vbyone_config->lane_count; /* 8 */
+       region_num = pconf->lcd_control.vbyone_config->region_num; /* 2 */
+       byte_mode = pconf->lcd_control.vbyone_config->byte_mode; /* 4 */
+       color_fmt = pconf->lcd_control.vbyone_config->color_fmt; /* 4 */
+
+       lcd_vbyone_clk_util_set(pconf);
+#if 0
+       switch (color_fmt) {
+       case 0:/* SDVT_VBYONE_18BPP_RGB */
+               vin_color = 4;
+               vin_bpp   = 2;
+               break;
+       case 1:/* SDVT_VBYONE_18BPP_YCBCR444 */
+               vin_color = 0;
+               vin_bpp   = 2;
+               break;
+       case 2:/* SDVT_VBYONE_24BPP_RGB */
+               vin_color = 4;
+               vin_bpp   = 1;
+               break;
+       case 3:/* SDVT_VBYONE_24BPP_YCBCR444 */
+               vin_color = 0;
+               vin_bpp   = 1;
+               break;
+       case 4:/* SDVT_VBYONE_30BPP_RGB */
+               vin_color = 4;
+               vin_bpp   = 0;
+               break;
+       case 5:/* SDVT_VBYONE_30BPP_YCBCR444 */
+               vin_color = 0;
+               vin_bpp   = 0;
+               break;
+       default:
+               LCDERR("vbyone COLOR_FORMAT unsupport\n");
+               return;
+       }
+#else
+       vin_color = 4; /* fixed RGB */
+       vin_bpp   = 0; /* fixed 30bbp 4:4:4 */
+#endif
+
+       /* set Vbyone vin color format */
+       lcd_vcbus_setb(VBO_VIN_CTRL, vin_color, 8, 3);
+       lcd_vcbus_setb(VBO_VIN_CTRL, vin_bpp, 11, 2);
+
+       lcd_vbyone_lanes_set(lane_count, byte_mode, region_num, hsize, vsize);
+       /*set hsync/vsync polarity to let the polarity is low active*/
+       /*inside the VbyOne */
+       lcd_vbyone_sync_pol(0, 0);
+
+       /* below line copy from simulation */
+       /* gate the input when vsync asserted */
+       lcd_vcbus_setb(VBO_VIN_CTRL, 1, 0, 2);
+       /* lcd_vcbus_write(VBO_VBK_CTRL_0,0x13);
+        * lcd_vcbus_write(VBO_VBK_CTRL_1,0x56);
+        * lcd_vcbus_write(VBO_HBK_CTRL,0x3478);
+        * lcd_vcbus_setb(VBO_PXL_CTRL,0x2,0,4);
+        * lcd_vcbus_setb(VBO_PXL_CTRL,0x3,VBO_PXL_CTR1_BIT,VBO_PXL_CTR1_WID);
+        * set_vbyone_ctlbits(1,0,0);
+        */
+
+       /* PAD select: */
+       if ((lane_count == 1) || (lane_count == 2))
+               lcd_vcbus_setb(LCD_PORT_SWAP, 1, 9, 2);
+       else if (lane_count == 4)
+               lcd_vcbus_setb(LCD_PORT_SWAP, 2, 9, 2);
+       else
+               lcd_vcbus_setb(LCD_PORT_SWAP, 0, 9, 2);
+       /* lcd_vcbus_setb(LCD_PORT_SWAP, 1, 8, 1);//reverse lane output order */
+
+       /* Mux pads in combo-phy: 0 for dsi; 1 for lvds or vbyone; 2 for edp */
+       lcd_hiu_write(HHI_DSI_LVDS_EDP_CNTL0, 0x1);
+       lcd_vcbus_setb(VBO_CTRL_L, 1, 0, 1);
+
+       /* force vencl clk enable, otherwise,
+        * it might auto turn off by mipi DSI
+        */
+       /* lcd_vcbus_setb(VPU_MISC_CTRL, 1, 0, 1); */
+
+       lcd_vbyone_wait_timing_stable();
+       lcd_vbyone_sw_reset();
+}
+
+static void lcd_vbyone_disable(void)
+{
+       lcd_vcbus_setb(VBO_CTRL_L, 0, 0, 1);
+}
+
+#define VBYONE_INTR_UNMASK   0x2bff /* 0x2a00 */
+                       /* enable htpdn_fail,lockn_fail,acq_hold */
+void lcd_vbyone_interrupt_enable(int flag)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct vbyone_config_s *vx1_conf;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s: %d\n", __func__, flag);
+
+       vx1_conf = lcd_drv->lcd_config->lcd_control.vbyone_config;
+       if (flag) {
+               if (vx1_conf->intr_en) {
+                       vx1_fsm_acq_st = 0;
+                       /* clear interrupt */
+                       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x01ff, 0, 9);
+                       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 9);
+
+                       /* set hold in FSM_ACQ */
+                       lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0xffff, 0, 16);
+                       /* enable interrupt */
+                       lcd_vcbus_setb(VBO_INTR_UNMASK,
+                               VBYONE_INTR_UNMASK, 0, 15);
+               } else {
+                       /* mask interrupt */
+                       lcd_vcbus_write(VBO_INTR_UNMASK, 0x0);
+                       if (vx1_conf->vsync_intr_en) {
+                               /* keep holder for vsync monitor enabled */
+                               /* set hold in FSM_ACQ */
+                               lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0xffff, 0, 16);
+                       } else {
+                               /* release holder for vsync monitor disabled */
+                               /* release hold in FSM_ACQ */
+                               lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0, 0, 16);
+                       }
+
+                       vx1_fsm_acq_st = 0;
+                       /* clear interrupt */
+                       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x01ff, 0, 9);
+                       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 9);
+               }
+       } else {
+               /* mask interrupt */
+               lcd_vcbus_write(VBO_INTR_UNMASK, 0x0);
+               /* release hold in FSM_ACQ */
+               lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0, 0, 16);
+               lcd_vx1_intr_request = 0;
+       }
+}
+
+static void lcd_vbyone_interrupt_init(void)
+{
+       /* set hold in FSM_ACQ */
+       lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0xffff, 0, 16);
+       /* set hold in FSM_CDR */
+       lcd_vcbus_setb(VBO_FSM_HOLDER_H, 0, 0, 16);
+       /* not wait lockn to 1 in FSM_ACQ */
+       lcd_vcbus_setb(VBO_CTRL_L, 1, 10, 1);
+       /* lcd_vcbus_setb(VBO_CTRL_L, 0, 9, 1);*/   /*use sw pll_lock */
+       /* reg_pll_lock = 1 to realease force to FSM_ACQ*/
+       /*lcd_vcbus_setb(VBO_CTRL_H, 1, 13, 1); */
+
+       /* vx1 interrupt setting */
+       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 12, 1);    /* intr pulse width */
+       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x01ff, 0, 9); /* clear interrupt */
+       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 9);
+
+       vx1_fsm_acq_st = 0;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+}
+
+void lcd_vbyone_wait_stable(void)
+{
+       int i = 5000;
+
+       while (((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) != 0x20) && (i > 0)) {
+               udelay(50);
+               i--;
+       }
+       LCDPR("%s status: 0x%x, i=%d\n",
+               __func__, lcd_vcbus_read(VBO_STATUS_L), (5000 - i));
+       vx1_training_wait_cnt = 0;
+       vx1_training_stable_cnt = 0;
+       vx1_fsm_acq_st = 0;
+       lcd_vbyone_interrupt_enable(1);
+}
+
+#define LCD_VX1_WAIT_STABLE_DELAY    300 /* ms */
+static void lcd_vx1_wait_stable_delayed(struct work_struct *work)
+{
+       if (lcd_vx1_intr_request == 0)
+               return;
+
+       lcd_vbyone_wait_stable();
+}
+
+static void lcd_vx1_wait_hpd(void)
+{
+       int i = 0;
+
+       if (lcd_debug_print_flag)
+               LCDPR("vx1 wait hpd to low ...\n");
+
+       while (lcd_vcbus_read(VBO_STATUS_L) & 0x40) {
+               if (i++ >= 10000)
+                       break;
+               udelay(50);
+       }
+       mdelay(10); /* add 10ms delay for compatibility */
+       if (lcd_vcbus_read(VBO_STATUS_L) & 0x40)
+               LCDPR("%s: hpd=%d\n", __func__,
+                       ((lcd_vcbus_read(VBO_STATUS_L) >> 6) & 0x1));
+       else
+               LCDPR("%s: hpd=%d, i=%d\n", __func__,
+                       ((lcd_vcbus_read(VBO_STATUS_L) >> 6) & 0x1), i);
+}
+
+#define LCD_PCLK_TOLERANCE          2000000  /* 2M */
+#define LCD_ENCL_CLK_ERR_CNT_MAX    3
+static unsigned char lcd_encl_clk_err_cnt;
+static void lcd_vx1_hpll_timer_handler(unsigned long arg)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       int encl_clk;
+#if 0
+       int pclk, pclk_min, pclk_max;
+#endif
+
+       if (lcd_drv->lcd_status == 0)
+               goto vx1_hpll_timer_end;
+
+#if 0
+       pclk = lcd_drv->lcd_config->lcd_timing.lcd_clk;
+       pclk_min = pclk - LCD_PCLK_TOLERANCE;
+       pclk_max = pclk + LCD_PCLK_TOLERANCE;
+       encl_clk = lcd_encl_clk_msr();
+       if ((encl_clk < pclk_min) || (encl_clk > pclk_max)) {
+               LCDPR("%s: pll frequency error: %d\n", __func__, encl_clk);
+               lcd_pll_reset();
+       }
+#else
+       encl_clk = lcd_encl_clk_msr();
+       if (encl_clk == 0)
+               lcd_encl_clk_err_cnt++;
+       else
+               lcd_encl_clk_err_cnt = 0;
+       if (lcd_encl_clk_err_cnt >= LCD_ENCL_CLK_ERR_CNT_MAX) {
+               LCDPR("%s: pll frequency error: %d\n", __func__, encl_clk);
+               lcd_pll_reset();
+               lcd_encl_clk_err_cnt = 0;
+       }
+#endif
+
+vx1_hpll_timer_end:
+       vx1_hpll_timer.expires = jiffies + VX1_HPLL_INTERVAL;
+       add_timer(&vx1_hpll_timer);
+}
+
+static void lcd_vx1_hold_reset(void)
+{
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       vx1_fsm_acq_st = 0;
+       lcd_vcbus_write(VBO_INTR_UNMASK, 0x0); /* mask interrupt */
+       /* clear interrupt */
+       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x01ff, 0, 9);
+       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 9);
+
+       /* clear FSM_continue */
+       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1);
+
+       /* force PHY to 0 */
+       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2);
+       lcd_vcbus_write(VBO_SOFT_RST, 0x1ff);
+       udelay(5);
+       /* clear lockn raising flag */
+       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 7, 1);
+       /* realease PHY */
+       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2);
+       /* clear lockn raising flag */
+       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 7, 1);
+       lcd_vcbus_write(VBO_SOFT_RST, 0);
+
+       /* enable interrupt */
+       lcd_vcbus_setb(VBO_INTR_UNMASK, VBYONE_INTR_UNMASK, 0, 15);
+}
+
+static void lcd_vx1_timeout_reset(unsigned long data)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       if (vx1_timeout_reset_flag == 0)
+               return;
+
+       LCDPR("%s\n", __func__);
+       if (vx1_timeout_reset_flag == 1)
+               lcd_drv->module_reset();
+       else
+               lcd_drv->module_tiny_reset();
+       if (lcd_drv->lcd_config->lcd_control.vbyone_config->intr_en)
+               lcd_vx1_hold_reset();
+       vx1_timeout_reset_flag = 0;
+}
+
+static irqreturn_t lcd_vbyone_vsync_isr(int irq, void *dev_id)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct vbyone_config_s *vx1_conf;
+
+       if (lcd_drv->lcd_status == 0)
+               return IRQ_HANDLED;
+       if (lcd_vcbus_read(VBO_STATUS_L) & 0x40) /* hpd detect */
+               return IRQ_HANDLED;
+
+       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 0, 1);
+       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 1);
+
+       vx1_conf = lcd_drv->lcd_config->lcd_control.vbyone_config;
+       if (vx1_conf->vsync_intr_en == 0) {
+               vx1_training_wait_cnt = 0;
+               return IRQ_HANDLED;
+       }
+
+#if 0
+       if (vx1_conf->intr_en == 0) {
+               if ((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) != 0x20)
+                       if (vx1_timeout_reset_flag == 0) {
+                               vx1_timeout_reset_flag = 1;
+                               tasklet_schedule(&lcd_vx1_reset_tasklet);
+                       }
+               }
+               return IRQ_HANDLED;
+       }
+#endif
+
+#if 1
+       if (vx1_training_wait_cnt >= VX1_TRAINING_TIMEOUT) {
+               if ((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) != 0x20) {
+                       if (vx1_timeout_reset_flag == 0) {
+                               vx1_timeout_reset_flag = 1;
+                               tasklet_schedule(&lcd_vx1_reset_tasklet);
+                       }
+               } else {
+                       vx1_training_stable_cnt++;
+                       if (vx1_training_stable_cnt >= 5) {
+                               vx1_training_wait_cnt = 0;
+                               vx1_training_stable_cnt = 0;
+                       }
+               }
+       } else {
+               vx1_training_wait_cnt++;
+       }
+#else
+       if ((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) != 0x20) {
+               if (vx1_training_wait_cnt >= VX1_TRAINING_TIMEOUT) {
+                       if (vx1_timeout_reset_flag == 0) {
+                               vx1_timeout_reset_flag = 1;
+                               tasklet_schedule(&lcd_vx1_reset_tasklet);
+                       }
+               } else {
+                       vx1_training_wait_cnt++;
+               }
+       } else {
+               vx1_training_stable_cnt++;
+               if (vx1_training_stable_cnt >= 5) {
+                       vx1_training_wait_cnt = 0;
+                       vx1_training_stable_cnt = 0;
+               }
+       }
+#endif
+
+       return IRQ_HANDLED;
+}
+
+#define VX1_LOCKN_WAIT_TIMEOUT    50
+static int vx1_lockn_wait_cnt;
+
+#define VX1_FSM_ACQ_NEXT_STEP_CONTINUE     0
+#define VX1_FSM_ACQ_NEXT_RELEASE_HOLDER    1
+#define VX1_FSM_ACQ_NEXT                   VX1_FSM_ACQ_NEXT_STEP_CONTINUE
+static irqreturn_t lcd_vbyone_interrupt_handler(int irq, void *dev_id)
+{
+       unsigned int data32, data32_1;
+       int encl_clk;
+
+       lcd_vcbus_write(VBO_INTR_UNMASK, 0x0);  /* mask interrupt */
+
+       encl_clk = lcd_encl_clk_msr();
+       data32 = (lcd_vcbus_read(VBO_INTR_STATE) & 0x7fff);
+#if 0
+       if (data32 & 0x1ff) { /* timing error */
+               LCDPR("vx1 timing err: VDE_CHK_LH=0x%04x,0x%04x\n",
+                       lcd_vcbus_read(VBO_TMCHK_VDE_STATE_L),
+                       lcd_vcbus_read(VBO_TMCHK_VDE_STATE_H));
+       }
+#endif
+       /* clear the interrupt */
+       data32_1 = ((data32 >> 9) << 3);
+       if (data32 & 0x1c0)
+               data32_1 |= (1 << 2);
+       if (data32 & 0x38)
+               data32_1 |= (1 << 1);
+       if (data32 & 0x7)
+               data32_1 |= (1 << 0);
+       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, data32_1, 0, 9);
+       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 9);
+       LCDPR("vx1 intr status = 0x%04x, encl_clkmsr = %d",
+               data32, encl_clk);
+
+       if (data32 & 0x200) {
+               LCDPR("vx1 htpdn fall occurred\n");
+               vx1_fsm_acq_st = 0;
+               lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1);
+       }
+#if 0
+       if (data32 & 0x400) {
+               LCDPR("vx1 htpdn raise occurred\n");
+               vx1_fsm_acq_st = 0;
+               lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1);
+       }
+#endif
+       if (data32 & 0x800) {
+               LCDPR("vx1 lockn fall occurred\n");
+               vx1_fsm_acq_st = 0;
+               lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1);
+               if (vx1_lockn_wait_cnt++ > VX1_LOCKN_WAIT_TIMEOUT) {
+#if 0
+                       LCDPR("vx1 sw reset for lockn timeout\n");
+                       /* force PHY to 0 */
+                       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2);
+                       lcd_vcbus_write(VBO_SOFT_RST, 0x1ff);
+                       udelay(5);
+                       /* clear lockn raising flag */
+                       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 7, 1);
+                       /* realease PHY */
+                       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2);
+                       /* clear lockn raising flag */
+                       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 7, 1);
+                       lcd_vcbus_write(VBO_SOFT_RST, 0);
+                       vx1_lockn_wait_cnt = 0;
+#else
+                       if (vx1_timeout_reset_flag == 0) {
+                               vx1_timeout_reset_flag = 1;
+                               tasklet_schedule(&lcd_vx1_reset_tasklet);
+                               vx1_lockn_wait_cnt = 0;
+                               return IRQ_HANDLED;
+                       }
+#endif
+               }
+       }
+#if 0
+       if (data32 & 0x1000) {
+               LCDPR("vx1 lockn raise occurred\n");
+               vx1_fsm_acq_st = 0;
+               lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1);
+       }
+#endif
+       if (data32 & 0x2000) {
+               /* LCDPR("vx1 fsm_acq wait end\n"); */
+               if (lcd_debug_print_flag) {
+                       LCDPR("vx1 status 0: 0x%x, fsm_acq_st: %d\n",
+                               lcd_vcbus_read(VBO_STATUS_L), vx1_fsm_acq_st);
+               }
+               if (vx1_fsm_acq_st == 0) {
+                       /* clear FSM_continue */
+                       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1);
+                       LCDPR("vx1 sw reset\n");
+                       /* force PHY to 0 */
+                       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2);
+                       lcd_vcbus_write(VBO_SOFT_RST, 0x1ff);
+                       udelay(5);
+                       /* clear lockn raising flag */
+                       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 7, 1);
+                       /* realease PHY */
+                       lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2);
+                       /* clear lockn raising flag */
+                       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 7, 1);
+                       lcd_vcbus_write(VBO_SOFT_RST, 0);
+                       vx1_fsm_acq_st = 1;
+               } else {
+                       vx1_fsm_acq_st = 2;
+                       /* set FSM_continue */
+#if (VX1_FSM_ACQ_NEXT == VX1_FSM_ACQ_NEXT_RELEASE_HOLDER)
+                       lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0, 0, 16);
+#else
+                       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1);
+                       lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 15, 1);
+#endif
+               }
+               LCDPR("vx1 status 1: 0x%x, fsm_acq_st: %d\n",
+                       lcd_vcbus_read(VBO_STATUS_L), vx1_fsm_acq_st);
+       }
+
+       if (data32 & 0x1ff) {
+               LCDPR("vx1 reset for timing err\n");
+               vx1_fsm_acq_st = 0;
+#if 1
+               /* force PHY to 0 */
+               lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2);
+               lcd_vcbus_write(VBO_SOFT_RST, 0x1ff);
+               udelay(5);
+               /* clear lockn raising flag */
+               lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 7, 1);
+               /* realease PHY */
+               lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2);
+               /* clear lockn raising flag */
+               lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 7, 1);
+               lcd_vcbus_write(VBO_SOFT_RST, 0);
+#else
+               if (vx1_timeout_reset_flag == 0) {
+                       vx1_timeout_reset_flag = 2;
+                       tasklet_schedule(&lcd_vx1_reset_tasklet);
+                       return IRQ_HANDLED;
+               }
+#endif
+       }
+
+       udelay(20);
+       if ((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) == 0x20) {
+               vx1_lockn_wait_cnt = 0;
+               /* vx1_training_wait_cnt = 0; */
+#if (VX1_FSM_ACQ_NEXT == VX1_FSM_ACQ_NEXT_RELEASE_HOLDER)
+               lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0xffff, 0, 16);
+#endif
+               LCDPR("vx1 fsm stable\n");
+       }
+
+       /* enable interrupt */
+       lcd_vcbus_setb(VBO_INTR_UNMASK, VBYONE_INTR_UNMASK, 0, 15);
+
+       return IRQ_HANDLED;
+}
+
+static void lcd_venc_set(struct lcd_config_s *pconf)
+{
+       unsigned int h_active, v_active;
+       unsigned int video_on_pixel, video_on_line;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       h_active = pconf->lcd_basic.h_active;
+       v_active = pconf->lcd_basic.v_active;
+       video_on_pixel = pconf->lcd_timing.video_on_pixel;
+       video_on_line = pconf->lcd_timing.video_on_line;
+
+       lcd_vcbus_write(ENCL_VIDEO_EN, 0);
+
+       lcd_vcbus_write(VPU_VIU_VENC_MUX_CTRL, (0 << 0) | (3 << 2));
+       /* Enable Hsync and equalization pulse switch in center;
+        * bit[14] cfg_de_v = 1
+        */
+       lcd_vcbus_write(ENCL_VIDEO_MODE,       40);
+       lcd_vcbus_write(ENCL_VIDEO_MODE_ADV,   0x18); /* Sampling rate: 1 */
+
+       /* bypass filter */
+       lcd_vcbus_write(ENCL_VIDEO_FILT_CTRL, 0x1000);
+       lcd_vcbus_write(ENCL_VIDEO_MAX_PXCNT, pconf->lcd_basic.h_period - 1);
+       lcd_vcbus_write(ENCL_VIDEO_MAX_LNCNT, pconf->lcd_basic.v_period - 1);
+
+       lcd_vcbus_write(ENCL_VIDEO_HAVON_BEGIN, video_on_pixel);
+       lcd_vcbus_write(ENCL_VIDEO_HAVON_END,   h_active - 1 + video_on_pixel);
+       lcd_vcbus_write(ENCL_VIDEO_VAVON_BLINE, video_on_line);
+       lcd_vcbus_write(ENCL_VIDEO_VAVON_ELINE, v_active - 1  + video_on_line);
+
+       lcd_vcbus_write(ENCL_VIDEO_HSO_BEGIN,   pconf->lcd_timing.hs_hs_addr);
+       lcd_vcbus_write(ENCL_VIDEO_HSO_END,     pconf->lcd_timing.hs_he_addr);
+       lcd_vcbus_write(ENCL_VIDEO_VSO_BEGIN,   pconf->lcd_timing.vs_hs_addr);
+       lcd_vcbus_write(ENCL_VIDEO_VSO_END,     pconf->lcd_timing.vs_he_addr);
+       lcd_vcbus_write(ENCL_VIDEO_VSO_BLINE,   pconf->lcd_timing.vs_vs_addr);
+       lcd_vcbus_write(ENCL_VIDEO_VSO_ELINE,   pconf->lcd_timing.vs_ve_addr);
+
+       lcd_vcbus_write(ENCL_VIDEO_RGBIN_CTRL, 3);
+
+       lcd_vcbus_write(ENCL_VIDEO_EN, 1);
+
+       aml_lcd_notifier_call_chain(LCD_EVENT_BACKLIGHT_UPDATE, NULL);
+}
+
+static unsigned int vbyone_lane_num[] = {
+       1,
+       2,
+       4,
+       8,
+       8,
+};
+
+#define VBYONE_BIT_RATE_MAX            3100 /* MHz */
+#define VBYONE_BIT_RATE_MIN            600
+static void lcd_vbyone_config_set(struct lcd_config_s *pconf)
+{
+       unsigned int band_width, bit_rate, pclk, phy_div;
+       unsigned int byte_mode, lane_count, minlane;
+       unsigned int lcd_bits;
+       unsigned int temp, i;
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       /* auto calculate bandwidth, clock */
+       lane_count = pconf->lcd_control.vbyone_config->lane_count;
+       lcd_bits = 10; /* pconf->lcd_basic.lcd_bits */
+       byte_mode = (lcd_bits == 10) ? 4 : 3;
+       /* byte_mode * byte2bit * 8/10_encoding * pclk =
+        * byte_mode * 8 * 10 / 8 * pclk
+        */
+       pclk = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+       band_width = byte_mode * 10 * pclk;
+
+       temp = VBYONE_BIT_RATE_MAX * 1000;
+       temp = (band_width + temp - 1) / temp;
+       for (i = 0; i < 4; i++) {
+               if (temp <= vbyone_lane_num[i])
+                       break;
+       }
+       minlane = vbyone_lane_num[i];
+       if (lane_count < minlane) {
+               LCDERR("vbyone lane_num(%d) is less than min(%d)\n",
+                       lane_count, minlane);
+               lane_count = minlane;
+               pconf->lcd_control.vbyone_config->lane_count = lane_count;
+               LCDPR("change to min lane_num %d\n", minlane);
+       }
+
+       bit_rate = band_width / minlane;
+       phy_div = lane_count / minlane;
+       if (phy_div == 8) {
+               phy_div /= 2;
+               bit_rate /= 2;
+       }
+       if (bit_rate > (VBYONE_BIT_RATE_MAX * 1000)) {
+               LCDERR("vbyone bit rate(%dKHz) is out of max(%dKHz)\n",
+                       bit_rate, (VBYONE_BIT_RATE_MAX * 1000));
+       }
+       if (bit_rate < (VBYONE_BIT_RATE_MIN * 1000)) {
+               LCDERR("vbyone bit rate(%dKHz) is out of min(%dKHz)\n",
+                       bit_rate, (VBYONE_BIT_RATE_MIN * 1000));
+       }
+       bit_rate = bit_rate * 1000; /* Hz */
+
+       pconf->lcd_control.vbyone_config->phy_div = phy_div;
+       pconf->lcd_control.vbyone_config->bit_rate = bit_rate;
+
+       if (lcd_debug_print_flag) {
+               LCDPR("lane_count=%u, bit_rate = %uMHz, pclk=%u.%03uMhz\n",
+                       lane_count, (bit_rate / 1000000),
+                       (pclk / 1000), (pclk % 1000));
+       }
+}
+
+void lcd_tv_clk_update(struct lcd_config_s *pconf)
+{
+#ifdef CONFIG_AML_VPU
+       request_vpu_clk_vmod(pconf->lcd_timing.lcd_clk, VPU_VENCL);
+#endif
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_VBYONE:
+               lcd_vbyone_config_set(pconf);
+               lcd_vbyone_interrupt_enable(0);
+               break;
+       default:
+               break;
+       }
+       lcd_clk_generate_parameter(pconf);
+       lcd_clk_set(pconf);
+       if (pconf->lcd_basic.lcd_type == LCD_VBYONE)
+               lcd_vbyone_wait_stable();
+}
+
+void lcd_tv_config_update(struct lcd_config_s *pconf)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct vinfo_s *info;
+
+       /* update lcd config sync_duration */
+       info = lcd_drv->lcd_info;
+       pconf->lcd_timing.sync_duration_num = info->sync_duration_num;
+       pconf->lcd_timing.sync_duration_den = info->sync_duration_den;
+
+       /* update clk & timing config */
+       lcd_vmode_change(pconf);
+       info->video_clk = pconf->lcd_timing.lcd_clk;
+       /* update interface timing */
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_VBYONE:
+               lcd_vbyone_config_set(pconf);
+               break;
+       default:
+               break;
+       }
+}
+
+void lcd_tv_driver_init_pre(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       int ret;
+
+       pconf = lcd_drv->lcd_config;
+       LCDPR("tv driver init(ver %s): %s\n", lcd_drv->version,
+               lcd_type_type_to_str(pconf->lcd_basic.lcd_type));
+       ret = lcd_type_supported(pconf);
+       if (ret)
+               return;
+
+#ifdef CONFIG_AML_VPU
+       request_vpu_clk_vmod(pconf->lcd_timing.lcd_clk, VPU_VENCL);
+       switch_vpu_mem_pd_vmod(VPU_VENCL, VPU_MEM_POWER_ON);
+#endif
+       lcd_clk_gate_switch(1);
+
+       /* init driver */
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_VBYONE:
+               lcd_vbyone_interrupt_enable(0);
+               break;
+       default:
+               break;
+       }
+
+       lcd_clk_set(pconf);
+       lcd_venc_set(pconf);
+       lcd_tcon_set(pconf);
+       lcd_drv->lcd_test_pattern_restore();
+}
+
+int lcd_tv_driver_init(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       int ret;
+
+       pconf = lcd_drv->lcd_config;
+       ret = lcd_type_supported(pconf);
+       if (ret)
+               return -1;
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_LVDS:
+               lcd_lvds_control_set(pconf);
+               lcd_lvds_phy_set(pconf, 1);
+               break;
+       case LCD_VBYONE:
+               lcd_vbyone_pinmux_set(1);
+               lcd_vbyone_control_set(pconf);
+               lcd_vx1_wait_hpd();
+               lcd_vbyone_phy_set(pconf, 1);
+               lcd_vx1_intr_request = 1;
+               queue_delayed_work(lcd_drv->workqueue,
+                               &lcd_drv->lcd_vx1_delayed_work,
+                               msecs_to_jiffies(LCD_VX1_WAIT_STABLE_DELAY));
+               break;
+       default:
+               break;
+       }
+
+       lcd_vcbus_write(VENC_INTCTRL, 0x200);
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s finished\n", __func__);
+       return 0;
+}
+
+void lcd_tv_driver_disable(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       int ret;
+
+       vx1_training_wait_cnt = 0;
+       vx1_timeout_reset_flag = 0;
+
+       LCDPR("disable driver\n");
+       pconf = lcd_drv->lcd_config;
+       ret = lcd_type_supported(pconf);
+       if (ret)
+               return;
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_LVDS:
+               lcd_lvds_phy_set(pconf, 0);
+               lcd_lvds_disable();
+               break;
+       case LCD_VBYONE:
+               lcd_vbyone_interrupt_enable(0);
+               lcd_vbyone_phy_set(pconf, 0);
+               lcd_vbyone_pinmux_set(0);
+               lcd_vbyone_disable();
+               break;
+       default:
+               break;
+       }
+
+       lcd_vcbus_write(ENCL_VIDEO_EN, 0); /* disable encl */
+
+       lcd_clk_disable();
+       lcd_clk_gate_switch(0);
+#ifdef CONFIG_AML_VPU
+       switch_vpu_mem_pd_vmod(VPU_VENCL, VPU_MEM_POWER_DOWN);
+       release_vpu_clk_vmod(VPU_VENCL);
+#endif
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s finished\n", __func__);
+}
+
+int lcd_tv_driver_change(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       int ret;
+
+       pconf = lcd_drv->lcd_config;
+       LCDPR("tv driver change(ver %s): %s\n", lcd_drv->version,
+               lcd_type_type_to_str(pconf->lcd_basic.lcd_type));
+       ret = lcd_type_supported(pconf);
+       if (ret)
+               return -1;
+
+       lcd_tv_config_update(pconf);
+#ifdef CONFIG_AML_VPU
+       request_vpu_clk_vmod(pconf->lcd_timing.lcd_clk, VPU_VENCL);
+#endif
+
+       if (lcd_drv->lcd_status == 0) {
+               /* only change parameters when panel is off */
+               switch (pconf->lcd_timing.clk_change) {
+               case LCD_CLK_PLL_CHANGE:
+                       lcd_clk_generate_parameter(pconf);
+                       break;
+               default:
+                       break;
+               }
+       } else {
+               if (pconf->lcd_basic.lcd_type == LCD_VBYONE)
+                       lcd_vbyone_interrupt_enable(0);
+
+               switch (pconf->lcd_timing.clk_change) {
+               case LCD_CLK_PLL_CHANGE:
+                       lcd_clk_generate_parameter(pconf);
+                       lcd_clk_set(pconf);
+                       break;
+               case LCD_CLK_FRAC_UPDATE:
+                       lcd_clk_update(pconf);
+                       break;
+               default:
+                       break;
+               }
+               lcd_venc_change(pconf);
+
+               if (pconf->lcd_basic.lcd_type == LCD_VBYONE)
+                       lcd_vbyone_wait_stable();
+       }
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s finished\n", __func__);
+       return 0;
+}
+
+void lcd_tv_driver_tiny_enable(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       int ret;
+
+       pconf = lcd_drv->lcd_config;
+       ret = lcd_type_supported(pconf);
+       if (ret)
+               return;
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_LVDS:
+               lcd_lvds_control_set(pconf);
+               lcd_lvds_phy_set(pconf, 1);
+               break;
+       case LCD_VBYONE:
+               lcd_vbyone_pinmux_set(1);
+               lcd_vbyone_control_set(pconf);
+               lcd_vx1_wait_hpd();
+               lcd_vbyone_phy_set(pconf, 1);
+               lcd_vbyone_wait_stable();
+               break;
+       default:
+               break;
+       }
+
+       LCDPR("enable driver\n");
+}
+
+void lcd_tv_driver_tiny_disable(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+       int ret;
+
+       LCDPR("disable driver\n");
+       pconf = lcd_drv->lcd_config;
+       ret = lcd_type_supported(pconf);
+       if (ret)
+               return;
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_LVDS:
+               lcd_lvds_phy_set(pconf, 0);
+               lcd_lvds_disable();
+               break;
+       case LCD_VBYONE:
+               lcd_vbyone_interrupt_enable(0);
+               lcd_vbyone_phy_set(pconf, 0);
+               lcd_vbyone_pinmux_set(0);
+               lcd_vbyone_disable();
+               break;
+       default:
+               break;
+       }
+}
+
+#define VBYONE_IRQF   IRQF_SHARED /* IRQF_DISABLED */ /* IRQF_SHARED */
+
+void lcd_vbyone_interrupt_up(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       lcd_vbyone_interrupt_init();
+       vx1_lockn_wait_cnt = 0;
+       vx1_training_wait_cnt = 0;
+       vx1_timeout_reset_flag = 0;
+       vx1_training_stable_cnt = 0;
+       lcd_vx1_intr_request = 0;
+       lcd_encl_clk_err_cnt = 0;
+
+       tasklet_init(&lcd_vx1_reset_tasklet, lcd_vx1_timeout_reset,
+               123);
+
+       INIT_DELAYED_WORK(&lcd_drv->lcd_vx1_delayed_work,
+               lcd_vx1_wait_stable_delayed);
+
+       if (request_irq(INT_VIU_VSYNC, &lcd_vbyone_vsync_isr,
+               IRQF_SHARED, "vbyone_vsync", (void *)"vbyone_vsync")) {
+               LCDERR("can't request vsync_irq for vbyone\n");
+       } else {
+               if (lcd_debug_print_flag)
+                       LCDPR("request vbyone vsync_irq successful\n");
+       }
+
+       if (request_irq(INT_VENC_VX1, &lcd_vbyone_interrupt_handler,
+               VBYONE_IRQF, "vbyone", (void *)"vbyone")) {
+               LCDERR("can't request irq for vbyone\n");
+       } else {
+               if (lcd_debug_print_flag)
+                       LCDPR("request vbyone irq successful\n");
+       }
+       lcd_vx1_intr_request = 1;
+       lcd_vbyone_interrupt_enable(1);
+
+       /* add timer to monitor hpll frequency */
+       init_timer(&vx1_hpll_timer);
+       /* vx1_hpll_timer.data = NULL; */
+       vx1_hpll_timer.function = lcd_vx1_hpll_timer_handler;
+       vx1_hpll_timer.expires = jiffies + VX1_HPLL_INTERVAL;
+       add_timer(&vx1_hpll_timer);
+       LCDPR("add vbyone hpll timer handler\n");
+}
+
+void lcd_vbyone_interrupt_down(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       del_timer_sync(&vx1_hpll_timer);
+
+       lcd_vbyone_interrupt_enable(0);
+       free_irq(INT_VENC_VX1, (void *)"vbyone");
+       free_irq(INT_VIU_VSYNC, (void *)"vbyone");
+       tasklet_kill(&lcd_vx1_reset_tasklet);
+       cancel_delayed_work(&lcd_drv->lcd_vx1_delayed_work);
+
+       if (lcd_debug_print_flag)
+               LCDPR("free vbyone irq\n");
+}
diff --git a/drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.c b/drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.c
new file mode 100644 (file)
index 0000000..0076c3f
--- /dev/null
@@ -0,0 +1,1270 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#ifdef CONFIG_AML_VPU
+#include <linux/amlogic/vpu.h>
+#endif
+#include <linux/amlogic/media/vout/vinfo.h>
+#include <linux/amlogic/media/vout/vout_notify.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include "lcd_tv.h"
+#include "../lcd_reg.h"
+#include "../lcd_common.h"
+
+static unsigned int lcd_output_vmode;
+static char lcd_output_name[30];
+
+/* lcd mode function */
+static unsigned int lcd_std_frame_rate[] = {
+       50,
+       60,
+       48,
+};
+
+struct lcd_vmode_info_s {
+       char *name;
+       enum vmode_e mode;
+       unsigned int width;
+       unsigned int height;
+       unsigned int frame_rate;
+};
+
+enum lcd_vmode_e {
+       LCD_VMODE_600P = 0,
+       LCD_VMODE_768P,
+       LCD_VMODE_1080P,
+       LCD_VMODE_2160P,
+       LCD_VMODE_MAX,
+};
+
+static struct lcd_vmode_info_s lcd_vmode_info[] = {
+       {
+               .name              = "600p",
+               .mode              = VMODE_LCD,
+               .width             = 1024,
+               .height            = 600,
+               .frame_rate        = 60,
+       },
+       {
+               .name              = "768p",
+               .mode              = VMODE_LCD,
+               .width             = 1366,
+               .height            = 768,
+               .frame_rate        = 60,
+       },
+       {
+               .name              = "1080p",
+               .mode              = VMODE_LCD,
+               .width             = 1920,
+               .height            = 1080,
+               .frame_rate        = 60,
+       },
+       {
+               .name              = "2160p",
+               .mode              = VMODE_LCD,
+               .width             = 3840,
+               .height            = 2160,
+               .frame_rate        = 60,
+       },
+       {
+               .name              = "invalid",
+               .mode              = VMODE_INIT_NULL,
+               .width             = 1920,
+               .height            = 1080,
+               .frame_rate        = 60,
+       },
+};
+
+static int lcd_vmode_is_mached(int index)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+
+       pconf = lcd_drv->lcd_config;
+       if ((pconf->lcd_basic.h_active == lcd_vmode_info[index].width) &&
+               (pconf->lcd_basic.v_active == lcd_vmode_info[index].height))
+               return 0;
+       else
+               return -1;
+}
+
+static int lcd_get_vmode(enum vmode_e mode)
+{
+       int lcd_vmode = LCD_VMODE_MAX;
+       int i, count = ARRAY_SIZE(lcd_vmode_info) - 1;
+       int ret;
+
+       for (i = 0; i < count; i++) {
+               if (mode == lcd_vmode_info[i].mode) {
+                       ret = lcd_vmode_is_mached(i);
+                       if (ret == 0) {
+                               lcd_vmode = i;
+                               break;
+                       }
+               }
+       }
+       return lcd_vmode;
+}
+
+static int lcd_outputmode_to_lcd_vmode(const char *mode)
+{
+       int lcd_vmode = LCD_VMODE_MAX;
+       int i, count = ARRAY_SIZE(lcd_vmode_info) - 1;
+       char temp[30], *p;
+       int n;
+
+       p = strchr(mode, 'p');
+       if (p == NULL)
+               return LCD_VMODE_MAX;
+       n = p - mode + 1;
+       strncpy(temp, mode, n);
+       temp[n] = '\0';
+       if (lcd_debug_print_flag)
+               LCDPR("outputmode=%s, lcd_vmode=%s\n", mode, temp);
+
+       for (i = 0; i < count; i++) {
+               if (strcmp(temp, lcd_vmode_info[i].name) == 0) {
+                       lcd_vmode = i;
+                       break;
+               }
+       }
+       return lcd_vmode;
+}
+
+static int lcd_outputmode_to_lcd_frame_rate(const char *mode)
+{
+       int frame_rate = 0;
+       char temp[30], *p;
+       int n, i, ret = 0;
+
+       p = strchr(mode, 'p');
+       if (p == NULL)
+               return 0;
+       n = p - mode + 1;
+       strncpy(temp, mode+n, (strlen(mode)-n));
+       p = strchr(temp, 'h');
+       if (p == NULL)
+               return 0;
+       *p = '\0';
+       ret = kstrtoint(temp, 10, &n);
+       if (lcd_debug_print_flag)
+               LCDPR("outputmode=%s, frame_rate=%d\n", mode, n);
+
+       for (i = 0; i < ARRAY_SIZE(lcd_std_frame_rate); i++) {
+               if (n == lcd_std_frame_rate[i]) {
+                       frame_rate = n;
+                       break;
+               }
+       }
+       return frame_rate;
+}
+
+static void lcd_vmode_vinfo_update(enum vmode_e mode)
+{
+       struct lcd_vmode_info_s *info;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct lcd_config_s *pconf;
+
+       lcd_output_vmode = lcd_get_vmode(mode);
+       info = &lcd_vmode_info[lcd_output_vmode];
+       sprintf(lcd_output_name, "%s%dhz", info->name, info->frame_rate);
+       if (lcd_debug_print_flag) {
+               LCDPR("%s vmode = %d, lcd_vmode = %d, outputmode = %s\n",
+                       __func__, mode, lcd_output_vmode, lcd_output_name);
+       }
+
+       /* store standard duration */
+       lcd_drv->std_duration.duration_num = info->frame_rate;
+       lcd_drv->std_duration.duration_den = 1;
+
+       /* update vinfo */
+       pconf = lcd_drv->lcd_config;
+       lcd_drv->lcd_info->name = lcd_output_name;
+       lcd_drv->lcd_info->mode = info->mode;
+       lcd_drv->lcd_info->width = info->width;
+       lcd_drv->lcd_info->height = info->height;
+       lcd_drv->lcd_info->field_height = info->height;
+       lcd_drv->lcd_info->aspect_ratio_num = pconf->lcd_basic.screen_width;
+       lcd_drv->lcd_info->aspect_ratio_den = pconf->lcd_basic.screen_height;
+       lcd_drv->lcd_info->screen_real_width = pconf->lcd_basic.screen_width;
+       lcd_drv->lcd_info->screen_real_height = pconf->lcd_basic.screen_height;
+       lcd_drv->lcd_info->sync_duration_num = info->frame_rate;
+       lcd_drv->lcd_info->sync_duration_den = 1;
+       lcd_drv->lcd_info->video_clk = pconf->lcd_timing.lcd_clk;
+
+       lcd_hdr_vinfo_update();
+}
+
+/*  vout server api */
+static enum vmode_e lcd_validate_vmode(char *mode)
+{
+       int lcd_vmode, frame_rate;
+       int ret;
+
+       if (lcd_vout_serve_bypass) {
+               LCDPR("vout_serve bypass\n");
+               return VMODE_MAX;
+       }
+       if (mode == NULL)
+               return VMODE_MAX;
+
+       lcd_vmode = lcd_outputmode_to_lcd_vmode(mode);
+       ret = lcd_vmode_is_mached(lcd_vmode);
+       if (ret) {
+               LCDERR("%s: outputmode is not support\n", __func__);
+               return VMODE_MAX;
+       }
+       frame_rate = lcd_outputmode_to_lcd_frame_rate(mode);
+       if (frame_rate == 0) {
+               LCDERR("%s: frame_rate is not support\n", __func__);
+       } else {
+               lcd_vmode_info[lcd_vmode].frame_rate = frame_rate;
+               if (lcd_vmode < LCD_VMODE_MAX)
+                       return lcd_vmode_info[lcd_vmode].mode;
+               else
+                       return VMODE_MAX;
+       }
+
+       return VMODE_MAX;
+}
+
+static struct vinfo_s *lcd_get_current_info(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       return lcd_drv->lcd_info;
+}
+
+static int lcd_set_current_vmode(enum vmode_e mode)
+{
+       int ret = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       if (lcd_vout_serve_bypass) {
+               LCDPR("vout_serve bypass\n");
+               return 0;
+       }
+       mutex_lock(&lcd_vout_mutex);
+
+       /* do not change mode value here, for bit mask is useful */
+       lcd_vmode_vinfo_update(mode & VMODE_MODE_BIT_MASK);
+
+       if (!(mode & VMODE_INIT_BIT_MASK)) {
+               switch (mode & VMODE_MODE_BIT_MASK) {
+               case VMODE_LCD:
+                       ret = lcd_drv->driver_change();
+                       break;
+               default:
+                       ret = -EINVAL;
+               }
+       }
+
+       lcd_vcbus_write(VPP_POSTBLEND_H_SIZE, lcd_drv->lcd_info->width);
+
+       mutex_unlock(&lcd_vout_mutex);
+       return ret;
+}
+
+static int lcd_vmode_is_supported(enum vmode_e mode)
+{
+       int lcd_vmode;
+
+       mode &= VMODE_MODE_BIT_MASK;
+       lcd_vmode = lcd_get_vmode(mode);
+       if (lcd_debug_print_flag) {
+               LCDPR("%s vmode = %d, lcd_vmode = %d(%s)\n",
+                       __func__, mode, lcd_vmode,
+                       lcd_vmode_info[lcd_vmode].name);
+       }
+
+       if (lcd_vmode < LCD_VMODE_MAX)
+               return true;
+       else
+               return false;
+}
+
+static int lcd_vout_disable(enum vmode_e cur_vmod)
+{
+       return 0;
+}
+
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+struct lcd_vframe_match_s {
+       int fps;
+       int frame_rate; /* *100 */
+       unsigned int duration_num;
+       unsigned int duration_den;
+};
+
+static struct lcd_vframe_match_s lcd_vframe_match_table_1[] = {
+       {5000, 5000, 50, 1},
+       {2500, 5000, 50, 1},
+       {6000, 6000, 60, 1},
+       {3000, 6000, 60, 1},
+       {2400, 6000, 60, 1},
+       {2397, 5994, 5994, 100},
+       {2997, 5994, 5994, 100},
+       {5994, 5994, 5994, 100},
+};
+
+static struct lcd_vframe_match_s lcd_vframe_match_table_2[] = {
+       {5000, 5000, 50, 1},
+       {2500, 5000, 50, 1},
+       {6000, 6000, 60, 1},
+       {3000, 6000, 60, 1},
+       {2400, 4800, 48, 1},
+       {2397, 5994, 5994, 100},
+       {2997, 5994, 5994, 100},
+       {5994, 5994, 5994, 100},
+};
+
+static int lcd_framerate_automation_set_mode(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       /* update interface timing */
+       lcd_tv_config_update(lcd_drv->lcd_config);
+#ifdef CONFIG_AML_VPU
+       request_vpu_clk_vmod(
+               lcd_drv->lcd_config->lcd_timing.lcd_clk, VPU_VENCL);
+#endif
+
+       if (lcd_drv->lcd_config->lcd_basic.lcd_type == LCD_VBYONE)
+               lcd_vbyone_interrupt_enable(0);
+       /* change clk parameter */
+       switch (lcd_drv->lcd_config->lcd_timing.clk_change) {
+       case LCD_CLK_PLL_CHANGE:
+               lcd_clk_generate_parameter(lcd_drv->lcd_config);
+               lcd_clk_set(lcd_drv->lcd_config);
+               break;
+       case LCD_CLK_FRAC_UPDATE:
+               lcd_clk_update(lcd_drv->lcd_config);
+               break;
+       default:
+               break;
+       }
+       lcd_venc_change(lcd_drv->lcd_config);
+       if (lcd_drv->lcd_config->lcd_basic.lcd_type == LCD_VBYONE)
+               lcd_vbyone_wait_stable();
+
+       vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE,
+               &lcd_drv->lcd_info->mode);
+
+       return 0;
+}
+#endif
+
+static int lcd_set_vframe_rate_hint(int duration)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct vinfo_s *info;
+       int fr_policy;
+       unsigned int frame_rate = 6000;
+       unsigned int duration_num = 60, duration_den = 1;
+       struct lcd_vframe_match_s *vtable = lcd_vframe_match_table_1;
+       int fps, i, n;
+
+       if (lcd_vout_serve_bypass) {
+               LCDPR("vout_serve bypass\n");
+               return 0;
+       }
+       if (lcd_drv->lcd_status == 0) {
+               LCDPR("%s: lcd is disabled, exit\n", __func__);
+               return 0;
+       }
+
+       info = lcd_drv->lcd_info;
+
+       fr_policy = lcd_drv->fr_auto_policy;
+       switch (fr_policy) {
+       case 1:
+               vtable = lcd_vframe_match_table_1;
+               n = ARRAY_SIZE(lcd_vframe_match_table_1);
+               break;
+       case 2:
+               vtable = lcd_vframe_match_table_2;
+               n = ARRAY_SIZE(lcd_vframe_match_table_2);
+               break;
+       default:
+               LCDPR("%s: fr_auto_policy = %d, disabled\n",
+                       __func__, fr_policy);
+               return 0;
+       }
+       fps = get_vsource_fps(duration);
+       for (i = 0; i < n; i++) {
+               if (fps == vtable[i].fps) {
+                       frame_rate = vtable[i].frame_rate;
+                       duration_num = vtable[i].duration_num;
+                       duration_den = vtable[i].duration_den;
+               }
+       }
+       LCDPR("%s: policy = %d, duration = %d, fps = %d, frame_rate = %d\n",
+               __func__, fr_policy, duration, fps, frame_rate);
+
+       /* if the sync_duration is same as current */
+       if ((duration_num == info->sync_duration_num) &&
+               (duration_den == info->sync_duration_den)) {
+               LCDPR("%s: sync_duration is the same, exit\n", __func__);
+               return 0;
+       }
+
+       /* update vinfo */
+       info->sync_duration_num = duration_num;
+       info->sync_duration_den = duration_den;
+
+       lcd_framerate_automation_set_mode();
+#endif
+       return 0;
+}
+
+static int lcd_set_vframe_rate_end_hint(void)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct vinfo_s *info;
+
+       if (lcd_vout_serve_bypass) {
+               LCDPR("vout_serve bypass\n");
+               return 0;
+       }
+       if (lcd_drv->lcd_status == 0) {
+               LCDPR("%s: lcd is disabled, exit\n", __func__);
+               return 0;
+       }
+
+       if (lcd_debug_print_flag)
+               LCDPR("fr_auto_policy = %d\n", lcd_drv->fr_auto_policy);
+       if (lcd_drv->fr_auto_policy) {
+               info = lcd_drv->lcd_info;
+               LCDPR("%s: return mode = %s, policy = %d\n", __func__,
+                       info->name, lcd_drv->fr_auto_policy);
+
+               /* update vinfo */
+               info->sync_duration_num = lcd_drv->std_duration.duration_num;
+               info->sync_duration_den = lcd_drv->std_duration.duration_den;
+
+               lcd_framerate_automation_set_mode();
+       }
+#endif
+       return 0;
+}
+
+static int lcd_set_vframe_rate_policy(int policy)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       if (lcd_vout_serve_bypass) {
+               LCDPR("vout_serve bypass\n");
+               return 0;
+       }
+       lcd_drv->fr_auto_policy = policy;
+       LCDPR("%s: %d\n", __func__, lcd_drv->fr_auto_policy);
+#endif
+       return 0;
+}
+
+static int lcd_get_vframe_rate_policy(void)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       return lcd_drv->fr_auto_policy;
+#else
+       return 0;
+#endif
+}
+
+#ifdef CONFIG_PM
+static int lcd_suspend(void)
+{
+       mutex_lock(&lcd_power_mutex);
+       aml_lcd_notifier_call_chain(LCD_EVENT_POWER_OFF, NULL);
+       lcd_resume_flag = 0;
+       LCDPR("%s finished\n", __func__);
+       mutex_unlock(&lcd_power_mutex);
+       return 0;
+}
+
+static int lcd_resume(void)
+{
+       mutex_lock(&lcd_power_mutex);
+       if (lcd_resume_flag == 0) {
+               lcd_resume_flag = 1;
+               aml_lcd_notifier_call_chain(LCD_EVENT_POWER_ON, NULL);
+               LCDPR("%s finished\n", __func__);
+       }
+       mutex_unlock(&lcd_power_mutex);
+       return 0;
+}
+
+#endif
+
+static struct vout_server_s lcd_vout_server = {
+       .name = "lcd_vout_server",
+       .op = {
+               .get_vinfo = lcd_get_current_info,
+               .set_vmode = lcd_set_current_vmode,
+               .validate_vmode = lcd_validate_vmode,
+               .vmode_is_supported = lcd_vmode_is_supported,
+               .disable = lcd_vout_disable,
+               .set_vframe_rate_hint = lcd_set_vframe_rate_hint,
+               .set_vframe_rate_end_hint = lcd_set_vframe_rate_end_hint,
+               .set_vframe_rate_policy = lcd_set_vframe_rate_policy,
+               .get_vframe_rate_policy = lcd_get_vframe_rate_policy,
+#ifdef CONFIG_PM
+               .vout_suspend = lcd_suspend,
+               .vout_resume = lcd_resume,
+#endif
+       },
+};
+
+static void lcd_vinfo_update_default(void)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       struct vinfo_s *vinfo;
+       unsigned int h_active, v_active;
+       char *mode;
+
+       if (lcd_drv->lcd_info == NULL) {
+               LCDERR("no lcd_info exist\n");
+               return;
+       }
+
+       mode = get_vout_mode_uboot();
+       h_active = lcd_vcbus_read(ENCL_VIDEO_HAVON_END)
+                       - lcd_vcbus_read(ENCL_VIDEO_HAVON_BEGIN) + 1;
+       v_active = lcd_vcbus_read(ENCL_VIDEO_VAVON_ELINE)
+                       - lcd_vcbus_read(ENCL_VIDEO_VAVON_BLINE) + 1;
+
+       vinfo = lcd_drv->lcd_info;
+       if (vinfo) {
+               vinfo->name = mode;
+               vinfo->mode = VMODE_LCD;
+               vinfo->width = h_active;
+               vinfo->height = v_active;
+               vinfo->field_height = v_active;
+               vinfo->aspect_ratio_num = h_active;
+               vinfo->aspect_ratio_den = v_active;
+               vinfo->screen_real_width = h_active;
+               vinfo->screen_real_height = v_active;
+               vinfo->sync_duration_num = 60;
+               vinfo->sync_duration_den = 1;
+               vinfo->video_clk = 0;
+               vinfo->viu_color_fmt = COLOR_FMT_RGB444;
+       }
+}
+
+void lcd_tv_vout_server_init(void)
+{
+       lcd_vinfo_update_default();
+       vout_register_server(&lcd_vout_server);
+}
+
+/* lcd tv config */
+static void lcd_config_print(struct lcd_config_s *pconf)
+{
+       LCDPR("%s, %s, %dbit, %dx%d\n",
+               pconf->lcd_basic.model_name,
+               lcd_type_type_to_str(pconf->lcd_basic.lcd_type),
+               pconf->lcd_basic.lcd_bits,
+               pconf->lcd_basic.h_active, pconf->lcd_basic.v_active);
+
+       if (lcd_debug_print_flag == 0)
+               return;
+
+       LCDPR("h_period = %d\n", pconf->lcd_basic.h_period);
+       LCDPR("v_period = %d\n", pconf->lcd_basic.v_period);
+       LCDPR("screen_width = %d\n", pconf->lcd_basic.screen_width);
+       LCDPR("screen_height = %d\n", pconf->lcd_basic.screen_height);
+
+       LCDPR("h_period_min = %d\n", pconf->lcd_basic.h_period_min);
+       LCDPR("h_period_max = %d\n", pconf->lcd_basic.h_period_max);
+       LCDPR("v_period_min = %d\n", pconf->lcd_basic.v_period_min);
+       LCDPR("v_period_max = %d\n", pconf->lcd_basic.v_period_max);
+       LCDPR("pclk_min = %d\n", pconf->lcd_basic.lcd_clk_min);
+       LCDPR("pclk_max = %d\n", pconf->lcd_basic.lcd_clk_max);
+
+       LCDPR("hsync_width = %d\n", pconf->lcd_timing.hsync_width);
+       LCDPR("hsync_bp = %d\n", pconf->lcd_timing.hsync_bp);
+       LCDPR("hsync_pol = %d\n", pconf->lcd_timing.hsync_pol);
+       LCDPR("vsync_width = %d\n", pconf->lcd_timing.vsync_width);
+       LCDPR("vsync_bp = %d\n", pconf->lcd_timing.vsync_bp);
+       LCDPR("vsync_pol = %d\n", pconf->lcd_timing.vsync_pol);
+
+       LCDPR("fr_adjust_type = %d\n", pconf->lcd_timing.fr_adjust_type);
+       LCDPR("ss_level = %d\n", pconf->lcd_timing.ss_level);
+       LCDPR("clk_auto = %d\n", pconf->lcd_timing.clk_auto);
+
+       if (pconf->lcd_basic.lcd_type == LCD_VBYONE) {
+               LCDPR("lane_count = %d\n",
+                       pconf->lcd_control.vbyone_config->lane_count);
+               LCDPR("byte_mode = %d\n",
+                       pconf->lcd_control.vbyone_config->byte_mode);
+               LCDPR("region_num = %d\n",
+                       pconf->lcd_control.vbyone_config->region_num);
+               LCDPR("color_fmt = %d\n",
+                       pconf->lcd_control.vbyone_config->color_fmt);
+       } else if (pconf->lcd_basic.lcd_type == LCD_LVDS) {
+               LCDPR("lvds_repack = %d\n",
+                       pconf->lcd_control.lvds_config->lvds_repack);
+               LCDPR("pn_swap = %d\n",
+                       pconf->lcd_control.lvds_config->pn_swap);
+               LCDPR("dual_port = %d\n",
+                       pconf->lcd_control.lvds_config->dual_port);
+               LCDPR("port_swap = %d\n",
+                       pconf->lcd_control.lvds_config->port_swap);
+               LCDPR("lane_reverse = %d\n",
+                       pconf->lcd_control.lvds_config->lane_reverse);
+       }
+}
+
+static int lcd_init_load_from_dts(struct lcd_config_s *pconf,
+               struct device *dev)
+{
+       int ret = 0;
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_VBYONE:
+               if (lcd_drv->lcd_status) /* lock pinmux if lcd in on */
+                       lcd_vbyone_pinmux_set(1);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+static int lcd_config_load_from_dts(struct lcd_config_s *pconf,
+               struct device *dev)
+{
+       int ret = 0;
+       const char *str;
+       unsigned int para[10];
+       struct device_node *child;
+       struct lvds_config_s *lvdsconf;
+
+       child = of_get_child_by_name(dev->of_node, pconf->lcd_propname);
+       if (child == NULL) {
+               LCDERR("failed to get %s\n", pconf->lcd_propname);
+               return -1;
+       }
+
+       ret = of_property_read_string(child, "model_name", &str);
+       if (ret) {
+               LCDERR("failed to get model_name\n");
+               strcpy(pconf->lcd_basic.model_name, pconf->lcd_propname);
+       } else {
+               strcpy(pconf->lcd_basic.model_name, str);
+       }
+
+       ret = of_property_read_string(child, "interface", &str);
+       if (ret) {
+               LCDERR("failed to get interface\n");
+               str = "invalid";
+       }
+       pconf->lcd_basic.lcd_type = lcd_type_str_to_type(str);
+
+       ret = of_property_read_u32_array(child, "basic_setting", &para[0], 7);
+       if (ret) {
+               LCDERR("failed to get basic_setting\n");
+       } else {
+               pconf->lcd_basic.h_active = para[0];
+               pconf->lcd_basic.v_active = para[1];
+               pconf->lcd_basic.h_period = para[2];
+               pconf->lcd_basic.v_period = para[3];
+               pconf->lcd_basic.lcd_bits = para[4];
+               pconf->lcd_basic.screen_width = para[5];
+               pconf->lcd_basic.screen_height = para[6];
+       }
+       ret = of_property_read_u32_array(child, "range_setting", &para[0], 6);
+       if (ret) {
+               LCDERR("failed to get range_setting\n");
+               pconf->lcd_basic.h_period_min = pconf->lcd_basic.h_period;
+               pconf->lcd_basic.h_period_max = pconf->lcd_basic.h_period;
+               pconf->lcd_basic.v_period_min = pconf->lcd_basic.v_period;
+               pconf->lcd_basic.v_period_max = pconf->lcd_basic.v_period;
+               pconf->lcd_basic.lcd_clk_min = 0;
+               pconf->lcd_basic.lcd_clk_max = 0;
+       } else {
+               pconf->lcd_basic.h_period_min = para[0];
+               pconf->lcd_basic.h_period_max = para[1];
+               pconf->lcd_basic.v_period_min = para[2];
+               pconf->lcd_basic.v_period_max = para[3];
+               pconf->lcd_basic.lcd_clk_min = para[4];
+               pconf->lcd_basic.lcd_clk_max = para[5];
+       }
+
+       ret = of_property_read_u32_array(child, "lcd_timing", &para[0], 6);
+       if (ret) {
+               LCDERR("failed to get lcd_timing\n");
+       } else {
+               pconf->lcd_timing.hsync_width = (unsigned short)(para[0]);
+               pconf->lcd_timing.hsync_bp = (unsigned short)(para[1]);
+               pconf->lcd_timing.hsync_pol = (unsigned short)(para[2]);
+               pconf->lcd_timing.vsync_width = (unsigned short)(para[3]);
+               pconf->lcd_timing.vsync_bp = (unsigned short)(para[4]);
+               pconf->lcd_timing.vsync_pol = (unsigned short)(para[5]);
+       }
+
+       ret = of_property_read_u32_array(child, "clk_attr", &para[0], 4);
+       if (ret) {
+               LCDERR("failed to get clk_attr\n");
+       } else {
+               pconf->lcd_timing.fr_adjust_type = (unsigned char)(para[0]);
+               pconf->lcd_timing.ss_level = (unsigned char)(para[1]);
+               pconf->lcd_timing.clk_auto = (unsigned char)(para[2]);
+       }
+
+       switch (pconf->lcd_basic.lcd_type) {
+       case LCD_LVDS:
+               lvdsconf = pconf->lcd_control.lvds_config;
+               ret = of_property_read_u32_array(child, "lvds_attr",
+                       &para[0], 5);
+               if (ret) {
+                       if (lcd_debug_print_flag)
+                               LCDERR("load 4 parameters in lvds_attr\n");
+                       ret = of_property_read_u32_array(child, "lvds_attr",
+                               &para[0], 4);
+                       if (ret) {
+                               if (lcd_debug_print_flag)
+                                       LCDPR("failed to get lvds_attr\n");
+                       } else {
+                               lvdsconf->lvds_repack = para[0];
+                               lvdsconf->dual_port = para[1];
+                               lvdsconf->pn_swap = para[2];
+                               lvdsconf->port_swap = para[3];
+                       }
+               } else {
+                               lvdsconf->lvds_repack = para[0];
+                               lvdsconf->dual_port = para[1];
+                               lvdsconf->pn_swap = para[2];
+                               lvdsconf->port_swap = para[3];
+                               lvdsconf->lane_reverse = para[4];
+               }
+               ret = of_property_read_u32_array(child, "phy_attr",
+                       &para[0], 4);
+               if (ret) { /* old parameters */
+                       if (lcd_debug_print_flag)
+                               LCDPR("load 2 parameters in phy_attr\n");
+                       ret = of_property_read_u32_array(child, "phy_attr",
+                               &para[0], 2);
+                       if (ret) {
+                               if (lcd_debug_print_flag)
+                                       LCDPR("failed to get phy_attr\n");
+                       } else {
+                               lvdsconf->phy_vswing = para[0];
+                               lvdsconf->phy_preem = para[1];
+                               lvdsconf->phy_clk_vswing = 0;
+                               lvdsconf->phy_clk_preem = 0;
+                               if (lcd_debug_print_flag) {
+                                       LCDPR("phy vswing=0x%x, preem=0x%x\n",
+                                       lvdsconf->phy_vswing,
+                                       lvdsconf->phy_preem);
+                               }
+                       }
+               } else {
+                       lvdsconf->phy_vswing = para[0];
+                       lvdsconf->phy_preem = para[1];
+                       lvdsconf->phy_clk_vswing = para[2];
+                       lvdsconf->phy_clk_preem = para[3];
+                       if (lcd_debug_print_flag) {
+                               LCDPR("phy vswing=0x%x, preemphasis=0x%x\n",
+                                       lvdsconf->phy_vswing,
+                                       lvdsconf->phy_preem);
+                               LCDPR("phy_clk vswing=0x%x, preemphasis=0x%x\n",
+                                       lvdsconf->phy_clk_vswing,
+                                       lvdsconf->phy_clk_preem);
+                       }
+               }
+               break;
+       case LCD_VBYONE:
+               ret = of_property_read_u32_array(child, "vbyone_attr",
+                       &para[0], 4);
+               if (ret) {
+                       LCDERR("failed to get vbyone_attr\n");
+               } else {
+                       pconf->lcd_control.vbyone_config->lane_count = para[0];
+                       pconf->lcd_control.vbyone_config->region_num = para[1];
+                       pconf->lcd_control.vbyone_config->byte_mode = para[2];
+                       pconf->lcd_control.vbyone_config->color_fmt = para[3];
+               }
+               ret = of_property_read_u32_array(child, "vbyone_intr_enable",
+                       &para[0], 2);
+               if (ret) {
+                       LCDERR("failed to get vbyone_intr_enable\n");
+               } else {
+                       pconf->lcd_control.vbyone_config->intr_en = para[0];
+                       pconf->lcd_control.vbyone_config->vsync_intr_en =
+                               para[1];
+               }
+               ret = of_property_read_u32_array(child, "phy_attr",
+                       &para[0], 2);
+               if (ret) {
+                       if (lcd_debug_print_flag)
+                               LCDPR("failed to get phy_attr\n");
+               } else {
+                       pconf->lcd_control.vbyone_config->phy_vswing = para[0];
+                       pconf->lcd_control.vbyone_config->phy_preem = para[1];
+                       if (lcd_debug_print_flag) {
+                               LCDPR("phy vswing=0x%x, preemphasis=0x%x\n",
+                               pconf->lcd_control.vbyone_config->phy_vswing,
+                               pconf->lcd_control.vbyone_config->phy_preem);
+                       }
+               }
+               break;
+       default:
+               LCDERR("invalid lcd type\n");
+               break;
+       }
+
+       ret = lcd_power_load_from_dts(pconf, child);
+
+       return ret;
+}
+
+static int lcd_config_load_from_unifykey(struct lcd_config_s *pconf)
+{
+       unsigned char *para;
+       int key_len, len;
+       unsigned char *p;
+       const char *str;
+       struct aml_lcd_unifykey_header_s lcd_header;
+       int ret;
+
+       para = kmalloc((sizeof(unsigned char) * LCD_UKEY_LCD_SIZE), GFP_KERNEL);
+       if (!para) {
+               LCDERR("%s: Not enough memory\n", __func__);
+               return -1;
+       }
+       key_len = LCD_UKEY_LCD_SIZE;
+       memset(para, 0, (sizeof(unsigned char) * key_len));
+       ret = lcd_unifykey_get("lcd", para, &key_len);
+       if (ret < 0) {
+               kfree(para);
+               return -1;
+       }
+
+       /* step 1: check header */
+       len = LCD_UKEY_HEAD_SIZE;
+       ret = lcd_unifykey_len_check(key_len, len);
+       if (ret < 0) {
+               LCDERR("unifykey header length is incorrect\n");
+               kfree(para);
+               return -1;
+       }
+
+       lcd_unifykey_header_check(para, &lcd_header);
+       len = 10 + 36 + 18 + 31 + 20;
+       if (lcd_debug_print_flag) {
+               LCDPR("unifykey header:\n");
+               LCDPR("crc32             = 0x%08x\n", lcd_header.crc32);
+               LCDPR("data_len          = %d\n", lcd_header.data_len);
+               LCDPR("version           = 0x%04x\n", lcd_header.version);
+               LCDPR("reserved          = 0x%04x\n", lcd_header.reserved);
+       }
+
+       /* step 2: check lcd parameters */
+       ret = lcd_unifykey_len_check(key_len, len);
+       if (ret < 0) {
+               LCDERR("unifykey parameters length is incorrect\n");
+               kfree(para);
+               return -1;
+       }
+
+       /* basic: 36byte */
+       p = para + LCD_UKEY_HEAD_SIZE;
+       *(p + LCD_UKEY_MODEL_NAME - 1) = '\0'; /* ensure string ending */
+       str = (const char *)p;
+       strcpy(pconf->lcd_basic.model_name, str);
+       p += LCD_UKEY_MODEL_NAME;
+       pconf->lcd_basic.lcd_type = *p;
+       p += LCD_UKEY_INTERFACE;
+       pconf->lcd_basic.lcd_bits = *p;
+       p += LCD_UKEY_LCD_BITS;
+       pconf->lcd_basic.screen_width = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_SCREEN_WIDTH;
+       pconf->lcd_basic.screen_height = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_SCREEN_HEIGHT;
+
+       /* timing: 18byte */
+       pconf->lcd_basic.h_active = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_H_ACTIVE;
+       pconf->lcd_basic.v_active = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_V_ACTIVE;
+       pconf->lcd_basic.h_period = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_H_PERIOD;
+       pconf->lcd_basic.v_period = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_V_PERIOD;
+       pconf->lcd_timing.hsync_width = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_HS_WIDTH;
+       pconf->lcd_timing.hsync_bp = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_HS_BP;
+       pconf->lcd_timing.hsync_pol = *p;
+       p += LCD_UKEY_HS_POL;
+       pconf->lcd_timing.vsync_width = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_VS_WIDTH;
+       pconf->lcd_timing.vsync_bp = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_VS_BP;
+       pconf->lcd_timing.vsync_pol = *p;
+       p += LCD_UKEY_VS_POL;
+
+       /* customer: 31byte */
+       pconf->lcd_timing.fr_adjust_type = *p;
+       p += LCD_UKEY_FR_ADJ_TYPE;
+       pconf->lcd_timing.ss_level = *p;
+       p += LCD_UKEY_SS_LEVEL;
+       pconf->lcd_timing.clk_auto = *p;
+       p += LCD_UKEY_CLK_AUTO_GEN;
+       /* dummy pointer */
+       p += LCD_UKEY_PCLK;
+       pconf->lcd_basic.h_period_min = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_H_PERIOD_MIN;
+       pconf->lcd_basic.h_period_max = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_H_PERIOD_MAX;
+       pconf->lcd_basic.v_period_min = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_V_PERIOD_MIN;
+       pconf->lcd_basic.v_period_max = (*p | ((*(p + 1)) << 8));
+       p += LCD_UKEY_V_PERIOD_MAX;
+       pconf->lcd_basic.lcd_clk_min = (*p | ((*(p + 1)) << 8) |
+               ((*(p + 2)) << 16) | ((*(p + 3)) << 24));
+       p += LCD_UKEY_PCLK_MIN;
+       pconf->lcd_basic.lcd_clk_max = (*p | ((*(p + 1)) << 8) |
+               ((*(p + 2)) << 16) | ((*(p + 3)) << 24));
+       p += LCD_UKEY_PCLK_MAX;
+       /* dummy pointer */
+       p += LCD_UKEY_CUST_VAL_8;
+       p += LCD_UKEY_CUST_VAL_9;
+
+       /* interface: 20byte */
+       if (pconf->lcd_basic.lcd_type == LCD_VBYONE) {
+               pconf->lcd_control.vbyone_config->lane_count =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_0;
+               pconf->lcd_control.vbyone_config->region_num =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_1;
+               pconf->lcd_control.vbyone_config->byte_mode  =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_2;
+               pconf->lcd_control.vbyone_config->color_fmt  =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_3;
+               pconf->lcd_control.vbyone_config->phy_vswing =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_4;
+               pconf->lcd_control.vbyone_config->phy_preem =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_5;
+               pconf->lcd_control.vbyone_config->intr_en =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_6;
+               pconf->lcd_control.vbyone_config->vsync_intr_en =
+                               (*p | ((*(p + 1)) << 8)) & 0xff;
+               p += LCD_UKEY_IF_ATTR_7;
+               /* dummy pointer */
+               p += LCD_UKEY_IF_ATTR_8;
+               p += LCD_UKEY_IF_ATTR_9;
+       } else if (pconf->lcd_basic.lcd_type == LCD_LVDS) {
+               if (lcd_header.version == 1) {
+                       pconf->lcd_control.lvds_config->lvds_repack =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_0;
+                       pconf->lcd_control.lvds_config->dual_port =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_1;
+                       pconf->lcd_control.lvds_config->pn_swap  =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_2;
+                       pconf->lcd_control.lvds_config->port_swap  =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_3;
+                       pconf->lcd_control.lvds_config->phy_vswing =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_4;
+                       pconf->lcd_control.lvds_config->phy_preem =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_5;
+                       pconf->lcd_control.lvds_config->phy_clk_vswing =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_6;
+                       pconf->lcd_control.lvds_config->phy_clk_preem  =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_7;
+                       pconf->lcd_control.lvds_config->lane_reverse = 0;
+                       p += LCD_UKEY_IF_ATTR_8;
+                       /* dummy pointer */
+                       p += LCD_UKEY_IF_ATTR_9;
+                       }
+               else if (lcd_header.version == 2) {
+                       pconf->lcd_control.lvds_config->lvds_repack =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_0;
+                       pconf->lcd_control.lvds_config->dual_port =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_1;
+                       pconf->lcd_control.lvds_config->pn_swap  =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_2;
+                       pconf->lcd_control.lvds_config->port_swap  =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_3;
+                       pconf->lcd_control.lvds_config->lane_reverse  =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_4;
+                       pconf->lcd_control.lvds_config->phy_vswing =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_5;
+                       pconf->lcd_control.lvds_config->phy_preem =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_6;
+                       pconf->lcd_control.lvds_config->phy_clk_vswing =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_7;
+                       pconf->lcd_control.lvds_config->phy_clk_preem  =
+                                       (*p | ((*(p + 1)) << 8)) & 0xff;
+                       p += LCD_UKEY_IF_ATTR_8;
+                       /* dummy pointer */
+                       p += LCD_UKEY_IF_ATTR_9;
+                       }
+       } else {
+               LCDERR("unsupport lcd_type: %d\n", pconf->lcd_basic.lcd_type);
+               p += LCD_UKEY_IF_ATTR_0;
+               p += LCD_UKEY_IF_ATTR_1;
+               p += LCD_UKEY_IF_ATTR_2;
+               p += LCD_UKEY_IF_ATTR_3;
+               p += LCD_UKEY_IF_ATTR_4;
+               p += LCD_UKEY_IF_ATTR_5;
+               p += LCD_UKEY_IF_ATTR_6;
+               p += LCD_UKEY_IF_ATTR_7;
+               p += LCD_UKEY_IF_ATTR_8;
+               p += LCD_UKEY_IF_ATTR_9;
+       }
+
+       /* step 3: check power sequence */
+       ret = lcd_power_load_from_unifykey(pconf, para, key_len, len);
+       if (ret < 0) {
+               kfree(para);
+               return -1;
+       }
+
+       kfree(para);
+       return 0;
+}
+
+static void lcd_vmode_init(struct lcd_config_s *pconf)
+{
+       char *mode;
+       enum vmode_e vmode;
+
+       mode = get_vout_mode_uboot();
+       LCDPR("%s mode: %s\n", __func__, mode);
+       vmode = lcd_validate_vmode(mode);
+       if (vmode >= VMODE_MAX) {
+               LCDERR("%s: invalid vout_init_mode: %s\n", __func__, mode);
+               vmode = VMODE_LCD;
+       }
+       lcd_vmode_vinfo_update(vmode & VMODE_MODE_BIT_MASK);
+       lcd_tv_config_update(pconf);
+}
+
+static void lcd_config_init(struct lcd_config_s *pconf)
+{
+       struct lcd_clk_config_s *cconf = get_lcd_clk_config();
+       unsigned int ss_level;
+       unsigned int clk;
+
+       clk = pconf->lcd_basic.h_period * pconf->lcd_basic.v_period * 60;
+       pconf->lcd_timing.lcd_clk = clk;
+       pconf->lcd_timing.lcd_clk_dft = pconf->lcd_timing.lcd_clk;
+       pconf->lcd_timing.h_period_dft = pconf->lcd_basic.h_period;
+       pconf->lcd_timing.v_period_dft = pconf->lcd_basic.v_period;
+       lcd_tcon_config(pconf); /* before vmode_init to avoid period changing */
+
+       lcd_vmode_init(pconf);
+
+       lcd_clk_generate_parameter(pconf);
+       ss_level = pconf->lcd_timing.ss_level;
+       cconf->ss_level = (ss_level >= cconf->ss_level_max) ? 0 : ss_level;
+}
+
+static int lcd_get_config(struct lcd_config_s *pconf, struct device *dev)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       int load_id = 0;
+       int ret;
+
+       if (dev->of_node == NULL) {
+               LCDERR("dev of_node is null\n");
+               return -1;
+       }
+       if (lcd_drv->lcd_key_valid) {
+               ret = lcd_unifykey_check("lcd");
+               if (ret < 0)
+                       load_id = 0;
+               else
+                       load_id = 1;
+       }
+       if (load_id) {
+               if (lcd_debug_print_flag)
+                       LCDPR("%s from unifykey\n", __func__);
+               lcd_drv->lcd_config_load = 1;
+               lcd_config_load_from_unifykey(pconf);
+       } else {
+               if (lcd_debug_print_flag)
+                       LCDPR("%s from dts\n", __func__);
+               lcd_drv->lcd_config_load = 0;
+               lcd_config_load_from_dts(pconf, dev);
+       }
+       lcd_init_load_from_dts(pconf, dev);
+       lcd_config_print(pconf);
+
+       lcd_config_init(pconf);
+
+       return 0;
+}
+
+/*  lcd notify */
+/* sync_duration is real_value * 100 */
+static void lcd_set_vinfo(unsigned int sync_duration)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       LCDPR("%s: sync_duration=%d\n", __func__, sync_duration);
+
+       /* update vinfo */
+       lcd_drv->lcd_info->sync_duration_num = sync_duration;
+       lcd_drv->lcd_info->sync_duration_den = 100;
+
+       /* update interface timing */
+       lcd_tv_config_update(lcd_drv->lcd_config);
+#ifdef CONFIG_AML_VPU
+       request_vpu_clk_vmod(
+               lcd_drv->lcd_config->lcd_timing.lcd_clk, VPU_VENCL);
+#endif
+
+       if (lcd_drv->lcd_config->lcd_basic.lcd_type == LCD_VBYONE)
+               lcd_vbyone_interrupt_enable(0);
+       /* change clk parameter */
+       switch (lcd_drv->lcd_config->lcd_timing.clk_change) {
+       case LCD_CLK_PLL_CHANGE:
+               lcd_clk_generate_parameter(lcd_drv->lcd_config);
+               lcd_clk_set(lcd_drv->lcd_config);
+               break;
+       case LCD_CLK_FRAC_UPDATE:
+               lcd_clk_update(lcd_drv->lcd_config);
+               break;
+       default:
+               break;
+       }
+       lcd_venc_change(lcd_drv->lcd_config);
+       if (lcd_drv->lcd_config->lcd_basic.lcd_type == LCD_VBYONE)
+               lcd_vbyone_wait_stable();
+
+       vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE,
+               &lcd_drv->lcd_info->mode);
+}
+
+static int lcd_frame_rate_adjust_notifier(struct notifier_block *nb,
+               unsigned long event, void *data)
+{
+       unsigned int *sync_duration;
+
+       /* LCDPR("%s: 0x%lx\n", __func__, event); */
+       if ((event & LCD_EVENT_FRAME_RATE_ADJUST) == 0)
+               return NOTIFY_DONE;
+
+       sync_duration = (unsigned int *)data;
+       lcd_set_vinfo(*sync_duration);
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block lcd_frame_rate_adjust_nb = {
+       .notifier_call = lcd_frame_rate_adjust_notifier,
+};
+
+/*  lcd tv */
+int lcd_tv_probe(struct device *dev)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+       int ret;
+
+       memset(lcd_output_name, 0, sizeof(lcd_output_name));
+       lcd_drv->version = LCD_DRV_VERSION;
+       lcd_drv->vout_server_init = lcd_tv_vout_server_init;
+       lcd_drv->driver_init_pre = lcd_tv_driver_init_pre;
+       lcd_drv->driver_init = lcd_tv_driver_init;
+       lcd_drv->driver_disable = lcd_tv_driver_disable;
+       lcd_drv->driver_change = lcd_tv_driver_change;
+       lcd_drv->driver_tiny_enable = lcd_tv_driver_tiny_enable;
+       lcd_drv->driver_tiny_disable = lcd_tv_driver_tiny_disable;
+
+       lcd_get_config(lcd_drv->lcd_config, dev);
+
+       switch (lcd_drv->lcd_config->lcd_basic.lcd_type) {
+       case LCD_VBYONE:
+               lcd_vbyone_interrupt_up();
+               break;
+       default:
+               break;
+       }
+
+       ret = aml_lcd_notifier_register(&lcd_frame_rate_adjust_nb);
+       if (ret)
+               LCDERR("register lcd_frame_rate_adjust_nb failed\n");
+
+       return 0;
+}
+
+int lcd_tv_remove(struct device *dev)
+{
+       struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+       aml_lcd_notifier_unregister(&lcd_frame_rate_adjust_nb);
+       switch (lcd_drv->lcd_config->lcd_basic.lcd_type) {
+       case LCD_VBYONE:
+               lcd_vbyone_interrupt_down();
+               break;
+       default:
+               break;
+       }
+
+       kfree(lcd_drv->lcd_info);
+       lcd_drv->lcd_info = NULL;
+       return 0;
+}
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.dts b/drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.dts
new file mode 100644 (file)
index 0000000..53ae949
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.dts
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+lcd{
+       compatible = "amlogic, lcd";
+       dev_name = "lcd";
+       mode = "tv";
+       status = "okay";
+       resets = <&clock GCLK_IDX_VCLK2_ENCL &clock GCLK_IDX_VCLK2_VENCL>;
+       reset-names = "encl","vencl";
+       interrupts = <0 78 1 0 3 1>;
+       interrupt-names = "vbyone","vbyone_vsync";
+       pinctrl-names = "vbyone";
+       pinctrl-0 = <&lcd_vbyone_pins>;
+
+       /* power type:(0=cpu, 1=pmu, 2=signal, 3=extern, 0xff=ending) */
+       /* power index:(point gpios_index, or extern_index, 0xff=invalid) */
+       /* power value:(0=output low, 1=output high, 2=input) */
+       /* power delay:(unit in ms) */
+       lcd_cpu-gpios = <&gpio GPIOX_3 GPIO_ACTIVE_HIGH>;
+       lcd_cpu_gpio_names = "GPIOX_3";
+
+       lvds_0{
+               model_name = "public";
+               interface = "lvds"; /* lcd_interface(lvds, vbyone) */
+               basic_setting = <1920 1080 2200 1125 8 16 9>;
+                       /* h_active, v_active, h_period, v_period, lcd_bits,
+                        *      screen_widht, screen_height
+                        */
+               lcd_timing = <44 148 0 5 30 0>;
+                       /* hs_width, hs_bp, hs_pol, vs_width, vs_bp, vs_pol */
+               clk_attr = <0 0 1>;
+                       /* fr_adj_type(0=clock, 1=htotal, 2=vtotal),
+                        *      clk_ss_level, clk_auto_generate
+                        */
+               lvds_attr = <1 1 0 0>;
+                       /** lvds_repack, dual_port, pn_swap, port_swap */
+               power_on_step = <0 0 1 50
+                               2 0 0 0
+                               0xff 0 0 0>; /* type, index, value, delay */
+               power_off_step = <2 0 0 50
+                               0 0 0 100
+                               0xff 0 0 0>; /* type, index, value, delay */
+               backlight_index = <0>;
+       };
+
+       vbyone_0{
+               model_name = "BOE_HV550QU2";
+               interface = "vbyone"; /* lcd_interface(lvds, vbyone) */
+               basic_setting = <3840 2160 4400 2250 10 16 9>;
+                       /* h_active, v_active, h_period, v_period, lcd_bits,
+                        *      screen_widht, screen_height
+                        */
+               lcd_timing = <33 477 0 6 65 0>;
+                       /* hs_width, hs_bp, hs_pol, vs_width, vs_bp, vs_pol */
+               clk_attr = <0 0 1>;
+                       /* fr_adj_type(0=clock, 1=htotal, 2=vtotal),
+                        *      clk_ss_level, clk_auto_generate
+                        */
+               vbyone_attr = <8 2 4 4>;
+                       /** lane_count, region_num, byte_mode, color_fmt */
+               power_on_step = <0 0 1 50
+                               2 0 0 0
+                               0xff 0 0 0>; /* type, index, value, delay */
+               power_off_step = <2 0 0 50
+                               0 0 0 100
+                               0xff 0 0 0>; /* type, index, value, delay */
+               backlight_index = <0>;
+       };
+
+       vbyone_1{
+               model_name = "LG_RDL550WY";
+               interface = "vbyone"; /* lcd_interface(lvds, vbyone) */
+               basic_setting = <3840 2160 4400 2250 10 16 9>;
+                       /* h_active, v_active, h_period, v_period, lcd_bits,
+                        *      screen_widht, screen_height
+                        */
+               lcd_timing = <33 477 0 6 65 0>;
+                       /* hs_width, hs_bp, hs_pol, vs_width, vs_bp, vs_pol */
+               clk_attr = <2 0 1>;
+                       /* fr_adj_type(0=clock, 1=htotal, 2=vtotal),
+                        *      clk_ss_level, clk_auto_generate
+                        */
+               vbyone_attr = <8 2 4 4>;
+                       /** lane_count, region_num, byte_mode, color_fmt */
+               power_on_step = <0 0 1 50
+                               2 0 0 0
+                               0xff 0 0 0>; /* type, index, value, delay */
+               power_off_step = <2 0 0 50
+                               0 0 0 100
+                               0xff 0 0 0>; /* type, index, value, delay */
+               backlight_index = <0>;
+       };
+
+       vbyone_2{
+               model_name = "INL_V580DJ2";
+               interface = "vbyone"; /* lcd_interface(lvds, vbyone) */
+               basic_setting = <3840 2160 4400 2250 10 16 9>;
+                       /* h_active, v_active, h_period, v_period, lcd_bits,
+                        *      screen_widht, screen_height
+                        */
+               lcd_timing = <33 477 0 6 65 0>;
+                       /* hs_width, hs_bp, hs_pol, vs_width, vs_bp, vs_pol */
+               clk_attr = <2 0 1>;
+                       /* fr_adj_type(0=clock, 1=htotal, 2=vtotal),
+                        *      clk_ss_level, clk_auto_generate
+                        */
+               vbyone_attr = <8 1 4 4>;
+                       /** lane_count, region_num, byte_mode, color_fmt */
+               power_on_step = <0 0 1 50
+                               2 0 0 0
+                               0xff 0 0 0>; /* type, index, value, delay */
+               power_off_step = <2 0 0 50
+                               0 0 0 100
+                               0xff 0 0 0>; /* type, index, value, delay */
+               backlight_index = <0>;
+       };
+};
diff --git a/drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.h b/drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.h
new file mode 100644 (file)
index 0000000..ad454e1
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __AML_LCD_TV_H__
+#define __AML_LCD_TV_H__
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+
+extern void lcd_tv_config_update(struct lcd_config_s *pconf);
+extern void lcd_tv_driver_init_pre(void);
+extern int lcd_tv_driver_init(void);
+extern void lcd_tv_driver_disable(void);
+extern int lcd_tv_driver_change(void);
+extern void lcd_tv_driver_tiny_enable(void);
+extern void lcd_tv_driver_tiny_disable(void);
+
+extern void lcd_vbyone_wait_stable(void);
+extern void lcd_vbyone_interrupt_up(void);
+extern void lcd_vbyone_interrupt_down(void);
+
+#endif
diff --git a/drivers/amlogic/media/vout/lcd/lcd_unifykey.c b/drivers/amlogic/media/vout/lcd/lcd_unifykey.c
new file mode 100644 (file)
index 0000000..99e923a
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_unifykey.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+
+#define LCDUKEY(fmt, args...)     pr_info("lcd ukey: "fmt"", ## args)
+#define LCDUKEYERR(fmt, args...)  pr_info("lcd ukey err: error: "fmt"", ## args)
+
+#ifdef CONFIG_KEY_MANAGE
+static unsigned int cal_crc32(unsigned int crc, const unsigned char *buf,
+               int buf_len) {
+       unsigned int s_crc32[16] = {
+               0, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
+               0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+               0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
+               0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
+       };
+       unsigned int crcu32 = crc;
+       unsigned char b;
+
+       if (buf_len <= 0)
+               return 0;
+       if (!buf)
+               return 0;
+
+       crcu32 = ~crcu32;
+       while (buf_len--) {
+               b = *buf++;
+               crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)];
+               crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)];
+       }
+
+       return ~crcu32;
+}
+
+int lcd_unifykey_len_check(int key_len, int len)
+{
+       if (key_len < len) {
+               LCDUKEYERR("invalid unifykey length %d, need %d\n",
+                       key_len, len);
+               return -1;
+       }
+       return 0;
+}
+
+int lcd_unifykey_header_check(unsigned char *buf,
+               struct aml_lcd_unifykey_header_s *header)
+{
+       header->crc32 = (buf[0] | (buf[1] << 8) |
+                       (buf[2] << 16) | (buf[3] << 24));
+       header->data_len = (buf[4] | (buf[5] << 8));
+       header->version = (buf[6] | (buf[7] << 8));
+       header->reserved = (buf[8] | (buf[9] << 8));
+
+       return 0;
+}
+
+int lcd_unifykey_check(char *key_name)
+{
+       unsigned int key_exist, keypermit, key_len;
+       unsigned char buf[550];
+       struct aml_lcd_unifykey_header_s key_header;
+       int retry_cnt = 0;
+       unsigned int key_crc32;
+       int ret;
+
+       key_exist = 0;
+       key_len = 0;
+       ret = key_unify_query(key_name, &key_exist, &keypermit);
+       if (ret < 0) {
+               if (lcd_debug_print_flag)
+                       LCDUKEYERR("%s query exist error\n", key_name);
+               return -1;
+       }
+       if (key_exist == 0) {
+               if (lcd_debug_print_flag)
+                       LCDUKEYERR("%s is not exist\n", key_name);
+               return -1;
+       }
+
+       ret = key_unify_size(key_name, &key_len);
+       if (ret < 0) {
+               LCDUKEYERR("%s query size error\n", key_name);
+               return -1;
+       }
+       if (key_len == 0) {
+               if (lcd_debug_print_flag)
+                       LCDUKEY("%s size is zero\n", key_name);
+               return -1;
+       }
+       if (lcd_debug_print_flag)
+               LCDUKEY("%s size: %d\n", key_name, key_len);
+
+lcd_unifykey_read:
+       ret = key_unify_read(key_name, buf, key_len, &key_len);
+       if (ret < 0) {
+               LCDUKEYERR("%s unify read error\n", key_name);
+               return -1;
+       }
+
+       /* check header */
+       if (key_len <= LCD_UKEY_HEAD_SIZE) {
+               LCDUKEYERR("%s unify key_len %d error\n", key_name, key_len);
+               return -1;
+       }
+       lcd_unifykey_header_check(buf, &key_header);
+       if (key_len != key_header.data_len) {  /* length check */
+               if (lcd_debug_print_flag) {
+                       LCDUKEYERR("data_len %d is not match key_len %d\n",
+                               key_header.data_len, key_len);
+               }
+               if (retry_cnt < LCD_UKEY_RETRY_CNT_MAX) {
+                       retry_cnt++;
+                       goto lcd_unifykey_read;
+               } else {
+                       LCDUKEYERR("%s: load unifykey failed\n", key_name);
+                       return -1;
+               }
+       }
+       key_crc32 = cal_crc32(0, &buf[4], (key_len - 4)); /* except crc32 */
+       if (lcd_debug_print_flag) {
+               LCDUKEY("crc32: 0x%08x, header_crc32: 0x%08x\n",
+                       key_crc32, key_header.crc32);
+       }
+       if (key_crc32 != key_header.crc32) {  /* crc32 check */
+               LCDUKEYERR("crc32 0x%08x is not match header_crc32 0x%08x\n",
+                       key_header.crc32, key_crc32);
+               if (retry_cnt < LCD_UKEY_RETRY_CNT_MAX) {
+                       retry_cnt++;
+                       goto lcd_unifykey_read;
+               } else {
+                       LCDUKEYERR("%s: load unifykey failed\n", key_name);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+int lcd_unifykey_get(char *key_name, unsigned char *buf, int *len)
+{
+       int key_len;
+       int ret;
+
+       key_len = 0;
+       ret = lcd_unifykey_check(key_name);
+       if (ret < 0)
+               return -1;
+       ret = key_unify_size(key_name, &key_len);
+       if (key_len > *len) {
+               LCDUKEYERR("%s size(%d) is bigger than buf_size(%d)\n",
+                       key_name, key_len, *len);
+               return -1;
+       }
+       *len = key_len;
+
+       ret = key_unify_read(key_name, buf, key_len, &key_len);
+       if (ret < 0) {
+               LCDUKEYERR("%s unify read error\n", key_name);
+               return -1;
+       }
+       return 0;
+}
+
+void lcd_unifykey_print(void)
+{
+       unsigned char buf[600];
+       char *key_name;
+       unsigned int key_len;
+       int i, j;
+       int ret;
+
+       key_name = "lcd";
+       key_len = LCD_UKEY_LCD_SIZE;
+       ret = lcd_unifykey_get(key_name, buf, &key_len);
+       if (ret < 0)
+               return;
+       LCDUKEY("%s: %s: %d\n", __func__, key_name, key_len);
+       i = 0;
+       while (1) {
+               pr_info("0x%08x: ", (i * 16));
+               for (j = 0; j < 16; j++) {
+                       if ((i*16+j) < key_len) {
+                               pr_info("0x%02x ", buf[i*16+j]);
+                       } else {
+                               pr_info("\n");
+                               goto exit_print_lcd;
+                       }
+               }
+               pr_info("\n");
+               i++;
+       }
+
+exit_print_lcd:
+       key_name = "lcd_extern";
+       key_len = LCD_UKEY_LCD_EXT_SIZE;
+       ret = lcd_unifykey_get(key_name, buf, &key_len);
+       if (ret < 0)
+               return;
+       LCDUKEY("%s: %s: %d\n", __func__, key_name, key_len);
+       i = 0;
+       while (1) {
+               pr_info("0x%08x: ", (i * 16));
+               for (j = 0; j < 16; j++) {
+                       if ((i*16+j) < key_len) {
+                               pr_info("0x%02x ", buf[i*16+j]);
+                       } else {
+                               pr_info("\n");
+                               goto exit_print_lcd_ext;
+                       }
+               }
+               pr_info("\n");
+               i++;
+       }
+
+exit_print_lcd_ext:
+       key_name = "backlight";
+       key_len = LCD_UKEY_BL_SIZE;
+       ret = lcd_unifykey_get(key_name, buf, &key_len);
+       if (ret < 0)
+               return;
+       LCDUKEY("%s: %s: %d\n", __func__, key_name, key_len);
+       i = 0;
+       while (1) {
+               pr_info("0x%08x: ", (i * 16));
+               for (j = 0; j < 16; j++) {
+                       if ((i*16+j) < key_len) {
+                               pr_info("0x%02x ", buf[i*16+j]);
+                       } else {
+                               pr_info("\n");
+                               goto exit_print_backlight;
+                       }
+               }
+               pr_info("\n");
+               i++;
+       }
+
+exit_print_backlight:
+       return;
+}
+
+#else
+/* dummy driver */
+int lcd_unifykey_len_check(int key_len, int len)
+{
+       LCDUKEYERR("Don't support unifykey\n");
+       return -1;
+}
+
+int lcd_unifykey_header_check(unsigned char *buf,
+               struct aml_lcd_unifykey_header_s *header)
+{
+       LCDUKEYERR("Don't support unifykey\n");
+       return -1;
+}
+
+int lcd_unifykey_check(char *key_name)
+{
+       LCDUKEYERR("Don't support unifykey\n");
+       return -1;
+}
+
+int lcd_unifykey_get(char *key_name, unsigned char *buf, int *len)
+{
+       LCDUKEYERR("Don't support unifykey\n");
+       return -1;
+}
+
+void lcd_unifykey_print(void)
+{
+       LCDUKEYERR("Don't support unifykey\n");
+}
+
+#endif
+
diff --git a/drivers/amlogic/media/vout/lcd/lcd_vout.c b/drivers/amlogic/media/vout/lcd/lcd_vout.c
new file mode 100644 (file)
index 0000000..758f460
--- /dev/null
@@ -0,0 +1,998 @@
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_vout.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/clk.h>
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#endif
+#include <linux/reset.h>
+#include <linux/amlogic/cpu_version.h>
+#include <linux/amlogic/media/vout/vinfo.h>
+#include <linux/amlogic/media/vout/vout_notify.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#endif
+#include "lcd_reg.h"
+#include "lcd_common.h"
+
+#define LCD_CDEV_NAME  "lcd"
+
+unsigned char lcd_debug_print_flag;
+unsigned char lcd_resume_flag;
+static struct aml_lcd_drv_s *lcd_driver;
+
+struct mutex lcd_power_mutex;
+struct mutex lcd_vout_mutex;
+int lcd_vout_serve_bypass;
+
+static char lcd_propname[20] = "lvds_0";
+
+struct lcd_cdev_s {
+       dev_t           devno;
+       struct cdev     cdev;
+       struct device   *dev;
+};
+
+static struct lcd_cdev_s *lcd_cdev;
+
+/* lcd config define */
+static struct ttl_config_s lcd_ttl_config = {
+       .clk_pol = 0,
+       .sync_valid = ((1 << 1) | (1 << 0)),
+       .swap_ctrl = ((0 << 1) | (0 << 0)),
+};
+
+static struct lvds_config_s lcd_lvds_config = {
+       .lvds_vswing = 1,
+       .lvds_repack = 1,
+       .dual_port = 0,
+       .pn_swap = 0,
+       .port_swap = 0,
+       .lane_reverse = 0,
+       .port_sel = 0,
+       .phy_vswing = LVDS_PHY_VSWING_DFT,
+       .phy_preem = LVDS_PHY_PREEM_DFT,
+};
+
+static struct vbyone_config_s lcd_vbyone_config = {
+       .lane_count = 8,
+       .region_num = 2,
+       .byte_mode = 4,
+       .color_fmt = 4,
+       .phy_div = 1,
+       .bit_rate = 0,
+       .phy_vswing = VX1_PHY_VSWING_DFT,
+       .phy_preem = VX1_PHY_PREEM_DFT,
+       .intr_en = 1,
+       .vsync_intr_en = 1,
+};
+
+static unsigned char dsi_init_on_table[DSI_INIT_ON_MAX] = {0xff, 0xff};
+static unsigned char dsi_init_off_table[DSI_INIT_OFF_MAX] = {0xff, 0xff};
+
+static struct dsi_config_s lcd_mipi_config = {
+       .lane_num = 4,
+       .bit_rate_max = 550, /* MHz */
+       .factor_numerator   = 0,
+       .factor_denominator = 100,
+       .operation_mode_init = 1,    /* 0=video mode, 1=command mode */
+       .operation_mode_display = 0, /* 0=video mode, 1=command mode */
+       .video_mode_type = 2, /* 0=sync_pulse, 1=sync_event, 2=burst */
+       .clk_lp_continuous = 1, /* 0=stop, 1=continue */
+       .phy_stop_wait = 0,   /* 0=auto, 1=standard, 2=slow */
+
+       .dsi_init_on  = &dsi_init_on_table[0],
+       .dsi_init_off = &dsi_init_off_table[0],
+       .extern_init = 0xff,
+               /* ext_index if needed, must match ext_config index;
+                *    0xff for invalid
+                */
+};
+
+static struct lcd_power_ctrl_s lcd_power_config = {
+       .cpu_gpio = {
+               {.flag = 0,},
+               {.flag = 0,},
+               {.flag = 0,},
+               {.flag = 0,},
+               {.flag = 0,},
+               {.flag = 0,},
+               {.flag = 0,},
+               {.flag = 0,},
+               {.flag = 0,},
+               {.flag = 0,},
+       },
+       .power_on_step = {
+               {
+                       .type = LCD_POWER_TYPE_MAX,
+               },
+       },
+       .power_off_step = {
+               {
+                       .type = LCD_POWER_TYPE_MAX,
+               },
+       },
+};
+
+static struct lcd_config_s lcd_config_dft = {
+       .lcd_propname = lcd_propname,
+       .lcd_basic = {
+               .lcd_type = LCD_TYPE_MAX,
+       },
+       .lcd_timing = {
+               .lcd_clk = 0,
+               .clk_auto = 1,
+               .ss_level = 0,
+               .fr_adjust_type = 0,
+       },
+       .hdr_info = {
+               .hdr_support = 0,
+               .features = 0,
+               .primaries_r_x = 0,
+               .primaries_r_y = 0,
+               .primaries_g_x = 0,
+               .primaries_g_y = 0,
+               .primaries_b_x = 0,
+               .primaries_b_y = 0,
+               .white_point_x = 0,
+               .white_point_y = 0,
+               .luma_max = 0,
+               .luma_min = 0,
+               .luma_avg = 0,
+       },
+       .lcd_control = {
+               .ttl_config = &lcd_ttl_config,
+               .lvds_config = &lcd_lvds_config,
+               .vbyone_config = &lcd_vbyone_config,
+               .mipi_config = &lcd_mipi_config,
+       },
+       .lcd_power = &lcd_power_config,
+};
+
+static struct vinfo_s lcd_vinfo = {
+       .name = "panel",
+       .mode = VMODE_LCD,
+       .viu_color_fmt = COLOR_FMT_RGB444,
+};
+
+struct aml_lcd_drv_s *aml_lcd_get_driver(void)
+{
+       return lcd_driver;
+}
+/* ********************************************************* */
+
+static void lcd_chip_detect(void)
+{
+       unsigned int cpu_type;
+
+       cpu_type = get_cpu_type();
+       switch (cpu_type) {
+       case MESON_CPU_MAJOR_ID_GXTVBB:
+               lcd_driver->chip_type = LCD_CHIP_GXTVBB;
+               break;
+       case MESON_CPU_MAJOR_ID_GXL:
+               lcd_driver->chip_type = LCD_CHIP_GXL;
+               break;
+       case MESON_CPU_MAJOR_ID_GXM:
+               lcd_driver->chip_type = LCD_CHIP_GXM;
+               break;
+       case MESON_CPU_MAJOR_ID_TXL:
+               lcd_driver->chip_type = LCD_CHIP_TXL;
+               break;
+       case MESON_CPU_MAJOR_ID_TXLX:
+               lcd_driver->chip_type = LCD_CHIP_TXLX;
+               break;
+       case MESON_CPU_MAJOR_ID_AXG:
+               lcd_driver->chip_type = LCD_CHIP_AXG;
+               break;
+       default:
+               lcd_driver->chip_type = LCD_CHIP_MAX;
+       }
+
+       if (lcd_debug_print_flag)
+               LCDPR("check chip: %d\n", lcd_driver->chip_type);
+}
+
+static void lcd_power_tiny_ctrl(int status)
+{
+       struct lcd_power_ctrl_s *lcd_power = lcd_driver->lcd_config->lcd_power;
+       struct lcd_power_step_s *power_step;
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+       struct aml_lcd_extern_driver_s *ext_drv;
+#endif
+       int i, index;
+
+       LCDPR("%s: %d\n", __func__, status);
+       i = 0;
+       while (i < LCD_PWR_STEP_MAX) {
+               if (status)
+                       power_step = &lcd_power->power_on_step[i];
+               else
+                       power_step = &lcd_power->power_off_step[i];
+
+               if (power_step->type >= LCD_POWER_TYPE_MAX)
+                       break;
+               if (lcd_debug_print_flag) {
+                       LCDPR("power_tiny_ctrl: %d, step %d\n", status, i);
+                       LCDPR("type=%d, index=%d, value=%d, delay=%d\n",
+                               power_step->type, power_step->index,
+                               power_step->value, power_step->delay);
+               }
+               switch (power_step->type) {
+               case LCD_POWER_TYPE_CPU:
+                       index = power_step->index;
+                       lcd_cpu_gpio_set(index, power_step->value);
+                       break;
+               case LCD_POWER_TYPE_PMU:
+                       LCDPR("to do\n");
+                       break;
+               case LCD_POWER_TYPE_SIGNAL:
+                       if (status)
+                               lcd_driver->driver_tiny_enable();
+                       else
+                               lcd_driver->driver_tiny_disable();
+                       break;
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+               case LCD_POWER_TYPE_EXTERN:
+                       index = power_step->index;
+                       ext_drv = aml_lcd_extern_get_driver(index);
+                       if (ext_drv) {
+                               if (status) {
+                                       if (ext_drv->power_on)
+                                               ext_drv->power_on();
+                                       else
+                                               LCDERR("no ext power on\n");
+                               } else {
+                                       if (ext_drv->power_off)
+                                               ext_drv->power_off();
+                                       else
+                                               LCDERR("no ext power off\n");
+                               }
+                       }
+                       break;
+#endif
+               default:
+                       break;
+               }
+               if (power_step->delay)
+                       mdelay(power_step->delay);
+               i++;
+       }
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s: %d finished\n", __func__, status);
+}
+
+static void lcd_power_ctrl(int status)
+{
+       struct lcd_power_ctrl_s *lcd_power = lcd_driver->lcd_config->lcd_power;
+       struct lcd_power_step_s *power_step;
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+       struct aml_lcd_extern_driver_s *ext_drv;
+#endif
+       int i, index;
+       int ret = 0;
+
+       LCDPR("%s: %d\n", __func__, status);
+       i = 0;
+       while (i < LCD_PWR_STEP_MAX) {
+               if (status)
+                       power_step = &lcd_power->power_on_step[i];
+               else
+                       power_step = &lcd_power->power_off_step[i];
+
+               if (power_step->type >= LCD_POWER_TYPE_MAX)
+                       break;
+               if (lcd_debug_print_flag) {
+                       LCDPR("power_ctrl: %d, step %d\n", status, i);
+                       LCDPR("type=%d, index=%d, value=%d, delay=%d\n",
+                               power_step->type, power_step->index,
+                               power_step->value, power_step->delay);
+               }
+               switch (power_step->type) {
+               case LCD_POWER_TYPE_CPU:
+                       index = power_step->index;
+                       lcd_cpu_gpio_set(index, power_step->value);
+                       break;
+               case LCD_POWER_TYPE_PMU:
+                       LCDPR("to do\n");
+                       break;
+               case LCD_POWER_TYPE_SIGNAL:
+                       if (status)
+                               ret = lcd_driver->driver_init();
+                       else
+                               lcd_driver->driver_disable();
+                       break;
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+               case LCD_POWER_TYPE_EXTERN:
+                       index = power_step->index;
+                       ext_drv = aml_lcd_extern_get_driver(index);
+                       if (ext_drv) {
+                               if (status) {
+                                       if (ext_drv->power_on)
+                                               ext_drv->power_on();
+                                       else
+                                               LCDERR("no ext power on\n");
+                               } else {
+                                       if (ext_drv->power_off)
+                                               ext_drv->power_off();
+                                       else
+                                               LCDERR("no ext power off\n");
+                               }
+                       }
+                       break;
+#endif
+               default:
+                       break;
+               }
+               if (power_step->delay)
+                       mdelay(power_step->delay);
+               i++;
+       }
+
+       if (lcd_debug_print_flag)
+               LCDPR("%s: %d finished\n", __func__, status);
+}
+
+static void lcd_module_enable(void)
+{
+       mutex_lock(&lcd_vout_mutex);
+
+       lcd_driver->driver_init_pre();
+       lcd_driver->power_ctrl(1);
+       lcd_driver->lcd_status = 1;
+
+       mutex_unlock(&lcd_vout_mutex);
+}
+
+static void lcd_module_disable(void)
+{
+       mutex_lock(&lcd_vout_mutex);
+
+       lcd_driver->lcd_status = 0;
+       lcd_driver->power_ctrl(0);
+
+       mutex_unlock(&lcd_vout_mutex);
+}
+
+static void lcd_module_reset(void)
+{
+       mutex_lock(&lcd_vout_mutex);
+
+       lcd_driver->lcd_status = 0;
+       lcd_driver->power_ctrl(0);
+
+       msleep(500);
+
+       lcd_driver->driver_init_pre();
+       lcd_driver->power_ctrl(1);
+       lcd_driver->lcd_status = 1;
+
+       mutex_unlock(&lcd_vout_mutex);
+}
+
+static void lcd_module_tiny_reset(void)
+{
+       mutex_lock(&lcd_vout_mutex);
+
+       lcd_driver->lcd_status = 0;
+       lcd_power_tiny_ctrl(0);
+       mdelay(500);
+       lcd_power_tiny_ctrl(1);
+       lcd_driver->lcd_status = 1;
+
+       mutex_unlock(&lcd_vout_mutex);
+}
+
+/* lcd notify */
+static int lcd_power_notifier(struct notifier_block *nb,
+               unsigned long event, void *data)
+{
+       if (lcd_debug_print_flag)
+               LCDPR("%s: 0x%lx\n", __func__, event);
+
+       if (event & LCD_EVENT_LCD_ON)
+               lcd_module_enable();
+       else if (event & LCD_EVENT_LCD_OFF)
+               lcd_module_disable();
+       else
+               return NOTIFY_DONE;
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block lcd_power_nb = {
+       .notifier_call = lcd_power_notifier,
+       .priority = LCD_PRIORITY_POWER_LCD,
+};
+
+static int lcd_interface_notifier(struct notifier_block *nb,
+               unsigned long event, void *data)
+{
+       if (lcd_debug_print_flag)
+               LCDPR("%s: 0x%lx\n", __func__, event);
+
+       if (event & LCD_EVENT_IF_ON)
+               lcd_driver->power_tiny_ctrl(1);
+       else if (event & LCD_EVENT_IF_OFF)
+               lcd_driver->power_tiny_ctrl(0);
+       else
+               return NOTIFY_DONE;
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block lcd_interface_nb = {
+       .notifier_call = lcd_interface_notifier,
+       .priority = LCD_PRIORITY_POWER_LCD,
+};
+
+static int lcd_bl_select_notifier(struct notifier_block *nb,
+               unsigned long event, void *data)
+{
+       unsigned int *index;
+
+       /* LCDPR("%s: 0x%lx\n", __func__, event); */
+       if ((event & LCD_EVENT_BACKLIGHT_SEL) == 0)
+               return NOTIFY_DONE;
+
+       index = (unsigned int *)data;
+       *index = lcd_driver->lcd_config->backlight_index;
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block lcd_bl_select_nb = {
+       .notifier_call = lcd_bl_select_notifier,
+};
+/* **************************************** */
+
+/* ************************************************************* */
+/* lcd ioctl                                                     */
+/* ************************************************************* */
+static int lcd_io_open(struct inode *inode, struct file *file)
+{
+       struct lcd_cdev_s *lcd_cdev;
+
+       LCDPR("%s\n", __func__);
+       lcd_cdev = container_of(inode->i_cdev, struct lcd_cdev_s, cdev);
+       file->private_data = lcd_cdev;
+       return 0;
+}
+
+static int lcd_io_release(struct inode *inode, struct file *file)
+{
+       LCDPR("%s\n", __func__);
+       file->private_data = NULL;
+       return 0;
+}
+
+static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       int ret = 0;
+       void __user *argp;
+       int mcd_nr;
+       struct lcd_hdr_info_s *hdr_info = &lcd_driver->lcd_config->hdr_info;
+
+       mcd_nr = _IOC_NR(cmd);
+       LCDPR("%s: cmd_dir = 0x%x, cmd_nr = 0x%x\n",
+               __func__, _IOC_DIR(cmd), mcd_nr);
+
+       argp = (void __user *)arg;
+       switch (mcd_nr) {
+       case LCD_IOC_NR_GET_HDR_INFO:
+               if (copy_to_user(argp, hdr_info, sizeof(struct lcd_hdr_info_s)))
+                       ret = -EFAULT;
+               break;
+       case LCD_IOC_NR_SET_HDR_INFO:
+               if (copy_from_user(hdr_info, argp,
+                       sizeof(struct lcd_hdr_info_s))) {
+                       ret = -EFAULT;
+               } else {
+                       lcd_hdr_vinfo_update();
+                       if (lcd_debug_print_flag) {
+                               LCDPR("set hdr_info:\n"
+                                       "hdr_support          %d\n"
+                                       "features             %d\n"
+                                       "primaries_r_x        %d\n"
+                                       "primaries_r_y        %d\n"
+                                       "primaries_g_x        %d\n"
+                                       "primaries_g_y        %d\n"
+                                       "primaries_b_x        %d\n"
+                                       "primaries_b_y        %d\n"
+                                       "white_point_x        %d\n"
+                                       "white_point_y        %d\n"
+                                       "luma_max             %d\n"
+                                       "luma_min             %d\n\n",
+                                       hdr_info->hdr_support,
+                                       hdr_info->features,
+                                       hdr_info->primaries_r_x,
+                                       hdr_info->primaries_r_y,
+                                       hdr_info->primaries_g_x,
+                                       hdr_info->primaries_g_y,
+                                       hdr_info->primaries_b_x,
+                                       hdr_info->primaries_b_y,
+                                       hdr_info->white_point_x,
+                                       hdr_info->white_point_y,
+                                       hdr_info->luma_max,
+                                       hdr_info->luma_min);
+                       }
+               }
+               break;
+       default:
+               LCDERR("not support ioctl cmd_nr: 0x%x\n", mcd_nr);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long lcd_compat_ioctl(struct file *file, unsigned int cmd,
+               unsigned long arg)
+{
+       unsigned long ret;
+
+       arg = (unsigned long)compat_ptr(arg);
+       ret = lcd_ioctl(file, cmd, arg);
+       return ret;
+}
+#endif
+
+static const struct file_operations lcd_fops = {
+       .owner          = THIS_MODULE,
+       .open           = lcd_io_open,
+       .release        = lcd_io_release,
+       .unlocked_ioctl = lcd_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = lcd_compat_ioctl,
+#endif
+};
+
+static int lcd_fops_create(void)
+{
+       int ret = 0;
+
+       lcd_cdev = kmalloc(sizeof(struct lcd_cdev_s), GFP_KERNEL);
+       if (!lcd_cdev) {
+               LCDERR("%s: failed to allocate lcd_cdev\n", __func__);
+               return -1;
+       }
+
+       ret = alloc_chrdev_region(&lcd_cdev->devno, 0, 1, LCD_CDEV_NAME);
+       if (ret < 0) {
+               LCDERR("%s: failed to alloc devno\n", __func__);
+               kfree(lcd_cdev);
+               lcd_cdev = NULL;
+               return -1;
+       }
+
+       cdev_init(&lcd_cdev->cdev, &lcd_fops);
+       lcd_cdev->cdev.owner = THIS_MODULE;
+       ret = cdev_add(&lcd_cdev->cdev, lcd_cdev->devno, 1);
+       if (ret) {
+               LCDERR("%s: failed to add cdev\n", __func__);
+               unregister_chrdev_region(lcd_cdev->devno, 1);
+               kfree(lcd_cdev);
+               lcd_cdev = NULL;
+               return -1;
+       }
+
+       lcd_cdev->dev = device_create(lcd_driver->lcd_debug_class, NULL,
+                       lcd_cdev->devno, NULL, LCD_CDEV_NAME);
+       if (IS_ERR(lcd_cdev->dev)) {
+               LCDERR("%s: failed to add device\n", __func__);
+               ret = PTR_ERR(lcd_cdev->dev);
+               cdev_del(&lcd_cdev->cdev);
+               unregister_chrdev_region(lcd_cdev->devno, 1);
+               kfree(lcd_cdev);
+               lcd_cdev = NULL;
+               return -1;
+       }
+
+       LCDPR("%s OK\n", __func__);
+       return 0;
+}
+
+static void lcd_fops_remove(void)
+{
+       cdev_del(&lcd_cdev->cdev);
+       unregister_chrdev_region(lcd_cdev->devno, 1);
+       kfree(lcd_cdev);
+       lcd_cdev = NULL;
+}
+/* ************************************************************* */
+
+static void lcd_init_vout(void)
+{
+       switch (lcd_driver->lcd_mode) {
+#ifdef CONFIG_AMLOGIC_LCD_TV
+       case LCD_MODE_TV:
+               lcd_tv_vout_server_init();
+               break;
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_TABLET
+       case LCD_MODE_TABLET:
+               lcd_tablet_vout_server_init();
+               break;
+#endif
+       default:
+               LCDERR("invalid lcd mode: %d\n", lcd_driver->lcd_mode);
+               break;
+       }
+}
+
+static int lcd_mode_probe(struct device *dev)
+{
+       int ret;
+
+       switch (lcd_driver->lcd_mode) {
+#ifdef CONFIG_AMLOGIC_LCD_TV
+       case LCD_MODE_TV:
+               lcd_tv_probe(dev);
+               break;
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_TABLET
+       case LCD_MODE_TABLET:
+               lcd_tablet_probe(dev);
+               break;
+#endif
+       default:
+               LCDERR("invalid lcd mode: %d\n", lcd_driver->lcd_mode);
+               break;
+       }
+
+       lcd_class_creat();
+       lcd_fops_create();
+
+       ret = aml_lcd_notifier_register(&lcd_interface_nb);
+       if (ret)
+               LCDERR("register aml_bl_select_notifier failed\n");
+       ret = aml_lcd_notifier_register(&lcd_bl_select_nb);
+       if (ret)
+               LCDERR("register aml_bl_select_notifier failed\n");
+       ret = aml_lcd_notifier_register(&lcd_power_nb);
+       if (ret)
+               LCDPR("register lcd_power_notifier failed\n");
+
+       /* add notifier for video sync_duration info refresh */
+       vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE,
+               &lcd_driver->lcd_info->mode);
+
+       return 0;
+}
+
+static int lcd_mode_remove(struct device *dev)
+{
+       switch (lcd_driver->lcd_mode) {
+#ifdef CONFIG_AMLOGIC_LCD_TV
+       case LCD_MODE_TV:
+               lcd_tv_remove(dev);
+               break;
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_TABLET
+       case LCD_MODE_TABLET:
+               lcd_tablet_remove(dev);
+               break;
+#endif
+       default:
+               LCDPR("invalid lcd mode\n");
+               break;
+       }
+       return 0;
+}
+
+static void lcd_config_probe_delayed(struct work_struct *work)
+{
+       int key_init_flag = 0;
+       int i = 0;
+       int ret;
+
+       if (lcd_driver->lcd_key_valid) {
+               key_init_flag = key_unify_get_init_flag();
+               while (key_init_flag == 0) {
+                       if (i++ >= LCD_UNIFYKEY_WAIT_TIMEOUT)
+                               break;
+                       msleep(20);
+                       key_init_flag = key_unify_get_init_flag();
+               }
+               LCDPR("key_init_flag=%d, i=%d\n", key_init_flag, i);
+       }
+       ret = lcd_mode_probe(lcd_driver->dev);
+       if (ret) {
+               kfree(lcd_driver);
+               lcd_driver = NULL;
+               LCDERR("probe exit\n");
+       }
+}
+
+static void lcd_config_default(void)
+{
+       struct lcd_config_s *pconf;
+
+       pconf = lcd_driver->lcd_config;
+       pconf->lcd_basic.h_active = lcd_vcbus_read(ENCL_VIDEO_HAVON_END)
+                       - lcd_vcbus_read(ENCL_VIDEO_HAVON_BEGIN) + 1;
+       pconf->lcd_basic.v_active = lcd_vcbus_read(ENCL_VIDEO_VAVON_ELINE)
+                       - lcd_vcbus_read(ENCL_VIDEO_VAVON_BLINE) + 1;
+       if (lcd_vcbus_read(ENCL_VIDEO_EN)) {
+               lcd_driver->lcd_status = 1;
+               lcd_resume_flag = 1;
+       } else {
+               lcd_driver->lcd_status = 0;
+               lcd_resume_flag = 0;
+       }
+       LCDPR("status: %d\n", lcd_driver->lcd_status);
+}
+
+static int lcd_config_probe(void)
+{
+       const char *str;
+       unsigned int val;
+       int ret = 0;
+
+       if (lcd_driver->dev->of_node == NULL) {
+               LCDERR("dev of_node is null\n");
+               lcd_driver->lcd_mode = LCD_MODE_MAX;
+               return -1;
+       }
+
+       /* lcd driver assign */
+       ret = of_property_read_string(lcd_driver->dev->of_node, "mode", &str);
+       if (ret) {
+               str = "none";
+               LCDERR("failed to get mode\n");
+               return -1;
+       }
+       lcd_driver->lcd_mode = lcd_mode_str_to_mode(str);
+       ret = of_property_read_u32(lcd_driver->dev->of_node,
+               "fr_auto_policy", &val);
+       if (ret) {
+               if (lcd_debug_print_flag)
+                       LCDPR("failed to get fr_auto_policy\n");
+               lcd_driver->fr_auto_policy = 0;
+       } else {
+               lcd_driver->fr_auto_policy = (unsigned char)val;
+       }
+       ret = of_property_read_u32(lcd_driver->dev->of_node, "key_valid", &val);
+       if (ret) {
+               if (lcd_debug_print_flag)
+                       LCDPR("failed to get key_valid\n");
+               lcd_driver->lcd_key_valid = 0;
+       } else {
+               lcd_driver->lcd_key_valid = (unsigned char)val;
+       }
+       LCDPR("detect mode: %s, fr_auto_policy: %d, key_valid: %d\n",
+               str, lcd_driver->fr_auto_policy, lcd_driver->lcd_key_valid);
+
+       lcd_clktree_probe();
+
+       lcd_driver->lcd_info = &lcd_vinfo;
+       lcd_driver->lcd_config = &lcd_config_dft;
+       lcd_driver->lcd_config->pinmux_flag = 0;
+       lcd_driver->lcd_test_flag = 0;
+       lcd_driver->power_ctrl = lcd_power_ctrl;
+       lcd_driver->module_reset = lcd_module_reset;
+       lcd_driver->power_tiny_ctrl = lcd_power_tiny_ctrl;
+       lcd_driver->module_tiny_reset = lcd_module_tiny_reset;
+       lcd_config_default();
+       lcd_init_vout();
+
+       if (lcd_driver->lcd_key_valid) {
+               if (lcd_driver->workqueue) {
+                       queue_delayed_work(lcd_driver->workqueue,
+                               &lcd_driver->lcd_probe_delayed_work,
+                               msecs_to_jiffies(2000));
+               } else {
+                       LCDPR("Warning: no lcd_probe_delayed workqueue\n");
+                       ret = lcd_mode_probe(lcd_driver->dev);
+                       if (ret) {
+                               kfree(lcd_driver);
+                               lcd_driver = NULL;
+                               LCDERR("probe exit\n");
+                       }
+               }
+       } else {
+               ret = lcd_mode_probe(lcd_driver->dev);
+               if (ret) {
+                       kfree(lcd_driver);
+                       lcd_driver = NULL;
+                       LCDERR("probe exit\n");
+               }
+       }
+
+       return 0;
+}
+
+static void lcd_resume_work(struct work_struct *p_work)
+{
+       mutex_lock(&lcd_power_mutex);
+       lcd_resume_flag = 1;
+       aml_lcd_notifier_call_chain(LCD_EVENT_POWER_ON, NULL);
+       LCDPR("%s finished\n", __func__);
+       mutex_unlock(&lcd_power_mutex);
+}
+
+static int lcd_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+
+#ifdef LCD_DEBUG_INFO
+       lcd_debug_print_flag = 1;
+#else
+       lcd_debug_print_flag = 0;
+#endif
+       lcd_driver = kmalloc(sizeof(struct aml_lcd_drv_s), GFP_KERNEL);
+       if (!lcd_driver) {
+               LCDERR("%s: lcd driver no enough memory\n", __func__);
+               return -ENOMEM;
+       }
+       lcd_driver->dev = &pdev->dev;
+
+       mutex_init(&lcd_vout_mutex);
+       mutex_init(&lcd_power_mutex);
+       lcd_vout_serve_bypass = 0;
+
+       /* init workqueue */
+       INIT_DELAYED_WORK(&lcd_driver->lcd_probe_delayed_work,
+               lcd_config_probe_delayed);
+       lcd_driver->workqueue = create_singlethread_workqueue("lcd_work_queue");
+       if (lcd_driver->workqueue == NULL)
+               LCDERR("can't create lcd workqueue\n");
+
+       INIT_WORK(&(lcd_driver->lcd_resume_work), lcd_resume_work);
+
+       lcd_chip_detect();
+       lcd_ioremap();
+       lcd_clk_config_probe();
+       ret = lcd_config_probe();
+
+       LCDPR("%s %s\n", __func__, (ret ? "failed" : "ok"));
+       return 0;
+}
+
+static int lcd_remove(struct platform_device *pdev)
+{
+       int ret;
+
+       ret = cancel_delayed_work(&lcd_driver->lcd_probe_delayed_work);
+       if (lcd_driver->workqueue)
+               destroy_workqueue(lcd_driver->workqueue);
+
+       if (lcd_driver) {
+               aml_lcd_notifier_unregister(&lcd_power_nb);
+               aml_lcd_notifier_unregister(&lcd_bl_select_nb);
+               aml_lcd_notifier_unregister(&lcd_interface_nb);
+
+               lcd_fops_remove();
+               lcd_class_remove();
+               if (!IS_ERR(lcd_driver->vencl_top))
+                       devm_clk_put(lcd_driver->dev, lcd_driver->vencl_top);
+               if (!IS_ERR(lcd_driver->vencl_int))
+                       devm_clk_put(lcd_driver->dev, lcd_driver->vencl_int);
+
+               lcd_mode_remove(lcd_driver->dev);
+               kfree(lcd_driver);
+               lcd_driver = NULL;
+       }
+
+       LCDPR("%s\n", __func__);
+       return 0;
+}
+
+static int lcd_resume(struct platform_device *pdev)
+{
+       queue_work(lcd_driver->workqueue, &(lcd_driver->lcd_resume_work));
+
+       return 0;
+}
+
+static int lcd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       mutex_lock(&lcd_power_mutex);
+       if (lcd_driver->lcd_status) {
+               aml_lcd_notifier_call_chain(LCD_EVENT_POWER_OFF, NULL);
+               lcd_resume_flag = 0;
+               LCDPR("%s finished\n", __func__);
+       }
+       mutex_unlock(&lcd_power_mutex);
+       return 0;
+}
+
+static void lcd_shutdown(struct platform_device *pdev)
+{
+       if (lcd_debug_print_flag)
+               LCDPR("%s\n", __func__);
+
+       if (lcd_driver->lcd_status)
+               aml_lcd_notifier_call_chain(LCD_EVENT_POWER_OFF, NULL);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id lcd_dt_match[] = {
+       {
+               .compatible = "amlogic, lcd",
+       },
+       {},
+};
+#endif
+
+static struct platform_driver lcd_platform_driver = {
+       .probe = lcd_probe,
+       .remove = lcd_remove,
+       .suspend = lcd_suspend,
+       .resume = lcd_resume,
+       .shutdown = lcd_shutdown,
+       .driver = {
+               .name = "mesonlcd",
+               .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+               .of_match_table = lcd_dt_match,
+#endif
+       },
+};
+
+static int __init lcd_init(void)
+{
+       if (platform_driver_register(&lcd_platform_driver)) {
+               LCDERR("failed to register lcd driver module\n");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void __exit lcd_exit(void)
+{
+       platform_driver_unregister(&lcd_platform_driver);
+}
+
+subsys_initcall(lcd_init);
+module_exit(lcd_exit);
+
+static int __init lcd_panel_type_para_setup(char *str)
+{
+       if (str != NULL)
+               sprintf(lcd_propname, "%s", str);
+
+       LCDPR("panel_type: %s\n", lcd_propname);
+       return 0;
+}
+__setup("panel_type=", lcd_panel_type_para_setup);
+
+MODULE_DESCRIPTION("Meson LCD Panel Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Amlogic, Inc.");
index d4c3243..1fc9151 100644 (file)
@@ -354,6 +354,7 @@ static const unsigned int tcon_stv1_pins[]  = { PIN(GPIODV_24, EE_OFF) };
 static const unsigned int tcon_sth1_pins[]     = { PIN(GPIODV_25, EE_OFF) };
 static const unsigned int tcon_cph_pins[]      = { PIN(GPIODV_26, EE_OFF) };
 static const unsigned int tcon_vcom_pins[]     = { PIN(GPIODV_27, EE_OFF) };
+static const unsigned int tcon_oeh_pins[]      = { PIN(GPIODV_27, EE_OFF) };
 
 /*i2c_a*/
 static const unsigned int i2c_sda_a_pins[]     = { PIN(GPIODV_24, EE_OFF) };
@@ -612,19 +613,23 @@ static struct meson_pmx_group meson_gxl_periphs_groups[] = {
        GROUP(tsout_clk, 1, 27),     /*dv15*/
        GROUP(tsout_d0, 1, 26),      /*dv16*/
        GROUP(tsout_d1_7, 1, 25),  /*dv17-23*/
-       GROUP(lcd_r0_1, 3, 10),    /*dv0-1*/
-       GROUP(lcd_r2_7, 3, 9),     /*dv2-7*/
-       GROUP(lcd_g0_1, 3, 8),     /*dv8-9*/
-       GROUP(lcd_g2_7, 3, 7),     /*dv10-15*/
-       GROUP(lcd_b0_1, 3, 6),     /*dv16-17*/
-       GROUP(lcd_b2_7, 3, 5),     /*dv18-23*/
+       GROUP(lcd_r0_1,  3, 10), /*dv0-1*/
+       GROUP(lcd_r2_7,  3, 9),  /*dv2-7*/
+       GROUP(lcd_g0_1,  3, 8),  /*dv8-9*/
+       GROUP(lcd_g2_7,  3, 7),  /*dv10-15*/
+       GROUP(lcd_b0_1,  3, 6),  /*dv16-17*/
+       GROUP(lcd_b2_7,  3, 5),  /*dv18-23*/
+       GROUP(lcd_vs,    3, 4),  /*dv24*/
+       GROUP(lcd_hs,    3, 3),  /*dv25*/
+       GROUP(tcon_stv1, 1, 22), /*dv24*/
+       GROUP(tcon_sth1, 1, 21), /*dv25*/
+       GROUP(tcon_cph,  1, 20), /*dv26*/
+       GROUP(tcon_vcom, 1, 19), /*dv27*/
+       GROUP(tcon_oeh,  1, 18), /*dv27*/
        GROUP(uart_tx_b,         2,     16),
        GROUP(uart_rx_b,         2,     15),
        GROUP(uart_cts_b, 2,    14),
        GROUP(uart_rts_b, 2,    13),
-       GROUP(lcd_vs,   3,      4),     /*dv24*/
-       GROUP(lcd_hs, 3, 3),    /*dv25*/
-       GROUP(tcon_stv1,        1,      22),
        GROUP(i2c_sda_a,        1,      15), /*dv24*/
        GROUP(i2c_scl_a,        1,      14), /*dv25*/
        GROUP(dmic_in_dv24, 2,  7),  /*dv24*/
@@ -969,11 +974,12 @@ static const char * const tsout_a_groups[] = {
        "tsout_d0", "tsout_d1_7",
 };
 
-static const char * const lcd_groups[] = {
+static const char * const lcd_ttl_groups[] = {
        "lcd_r0_1", "lcd_r2_7",
        "lcd_g0_1", "lcd_g2_7",
        "lcd_b0_1", "lcd_b2_7",
-       "lcd_vs",   "lcd_hs",
+       "tcon_stv1", "tcon_sth1",
+       "tcon_cph", "tcon_oeh",
 };
 
 static const char *const nor_groups[] = {
@@ -1022,7 +1028,7 @@ static struct meson_pmx_func meson_gxl_periphs_functions[] = {
        FUNCTION(tsin_a),
        FUNCTION(tsin_b),
        FUNCTION(tsout_a),
-       FUNCTION(lcd),
+       FUNCTION(lcd_ttl),
        FUNCTION(nor),
        FUNCTION(dvp),
        FUNCTION(dmic),
diff --git a/include/linux/amlogic/media/vout/lcd/aml_bl.h b/include/linux/amlogic/media/vout/lcd/aml_bl.h
new file mode 100644 (file)
index 0000000..3f304b3
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * include/linux/amlogic/media/vout/lcd/aml_bl.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __INC_AML_BL_H
+#define __INC_AML_BL_H
+#include <linux/workqueue.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/pinctrl/consumer.h>
+
+#define BLPR(fmt, args...)      pr_info("bl: "fmt"", ## args)
+#define BLERR(fmt, args...)      pr_err("bl error: "fmt"", ## args)
+#define AML_BL_NAME            "aml-bl"
+
+#define BL_LEVEL_MAX           255
+#define BL_LEVEL_MIN           10
+#define BL_LEVEL_OFF           1
+
+#define BL_LEVEL_MID           128
+#define BL_LEVEL_MID_MAPPED    BL_LEVEL_MID
+#define BL_LEVEL_DEFAULT       BL_LEVEL_MID
+
+#define XTAL_FREQ_HZ           (24*1000*1000) /* unit: Hz */
+#define XTAL_HALF_FREQ_HZ      (24*1000*500)  /* 24M/2 in HZ */
+
+#define BL_FREQ_DEFAULT                1000 /* unit: HZ */
+#define BL_FREQ_VS_DEFAULT     2    /* multiple 2 of vfreq */
+
+enum bl_chip_type_e {
+       BL_CHIP_GXTVBB,
+       BL_CHIP_GXL,
+       BL_CHIP_GXM,
+       BL_CHIP_TXL,
+       BL_CHIP_TXLX,
+       BL_CHIP_AXG,
+       BL_CHIP_MAX,
+};
+
+/* for lcd backlight power */
+enum bl_ctrl_method_e {
+       BL_CTRL_GPIO = 0,
+       BL_CTRL_PWM,
+       BL_CTRL_PWM_COMBO,
+       BL_CTRL_LOCAL_DIMING,
+       BL_CTRL_EXTERN,
+       BL_CTRL_MAX,
+};
+
+enum bl_pwm_method_e {
+       BL_PWM_NEGATIVE = 0,
+       BL_PWM_POSITIVE,
+       BL_PWM_METHOD_MAX,
+};
+
+enum bl_pwm_port_e {
+       BL_PWM_A = 0,
+       BL_PWM_B,
+       BL_PWM_C,
+       BL_PWM_D,
+       BL_PWM_E,
+       BL_PWM_F,
+       BL_PWM_VS,
+       BL_PWM_MAX,
+};
+
+enum bl_off_policy_e {
+       BL_OFF_POLICY_NONE = 0,
+       BL_OFF_POLICY_ALWAYS,
+       BL_OFF_POLICY_ONCE,
+       BL_OFF_POLICY_MAX,
+};
+
+#define BL_GPIO_OUTPUT_LOW      0
+#define BL_GPIO_OUTPUT_HIGH     1
+#define BL_GPIO_INPUT           2
+
+#define BL_GPIO_MAX             0xff
+#define BL_GPIO_NUM_MAX         5
+struct bl_gpio_s {
+       char name[15];
+       struct gpio_desc *gpio;
+       int flag;
+};
+
+struct bl_pwm_config_s {
+       unsigned int index;
+       struct pwm_device *bl_pwm_ch;
+       struct aml_pwm_chip *bl_pwm_chip;
+       enum bl_pwm_method_e pwm_method;
+       enum bl_pwm_port_e pwm_port;
+       unsigned int level_max;
+       unsigned int level_min;
+       unsigned int pwm_freq; /* pwm_vs: 1~4(vfreq), pwm: freq(unit: Hz) */
+       unsigned int pwm_duty; /* unit: % */
+       unsigned int pwm_duty_max; /* unit: % */
+       unsigned int pwm_duty_min; /* unit: % */
+       unsigned int pwm_cnt; /* internal used for pwm control */
+       unsigned int pwm_pre_div; /* internal used for pwm control */
+       unsigned int pwm_max; /* internal used for pwm control */
+       unsigned int pwm_min; /* internal used for pwm control */
+       unsigned int pwm_level; /* internal used for pwm control */
+};
+
+struct bl_config_s {
+       char name[30];
+       unsigned int level_default;
+       unsigned int level_min;
+       unsigned int level_max;
+       unsigned int level_mid;
+       unsigned int level_mid_mapping;
+
+       enum bl_ctrl_method_e method;
+       unsigned int en_gpio;
+       unsigned int en_gpio_on;
+       unsigned int en_gpio_off;
+       unsigned int power_on_delay;
+       unsigned int power_off_delay;
+       unsigned int dim_max;
+       unsigned int dim_min;
+
+       struct bl_pwm_config_s *bl_pwm;
+       struct bl_pwm_config_s *bl_pwm_combo0;
+       struct bl_pwm_config_s *bl_pwm_combo1;
+       unsigned int pwm_on_delay;
+       unsigned int pwm_off_delay;
+
+       struct bl_gpio_s bl_gpio[BL_GPIO_NUM_MAX];
+       struct pinctrl *pin;
+       unsigned int pinmux_flag;
+};
+
+#define BL_INDEX_DEFAULT     0
+
+/* backlight_properties: state */
+/* Flags used to signal drivers of state changes */
+/* Upper 4 bits in bl props are reserved for driver internal use */
+#define BL_STATE_LCD_ON               (1 << 3)
+#define BL_STATE_BL_POWER_ON          (1 << 1)
+#define BL_STATE_BL_ON                (1 << 0)
+struct aml_bl_drv_s {
+       unsigned int index;
+       unsigned int level;
+       unsigned int state;
+       enum bl_chip_type_e chip_type;
+       struct device             *dev;
+       struct bl_config_s        *bconf;
+       struct backlight_device   *bldev;
+       struct workqueue_struct   *workqueue;
+       struct delayed_work       bl_delayed_work;
+};
+
+extern enum bl_chip_type_e aml_bl_check_chip(void);
+extern struct aml_bl_drv_s *aml_bl_get_driver(void);
+extern void bl_pwm_config_init(struct bl_pwm_config_s *bl_pwm);
+extern enum bl_pwm_port_e bl_pwm_str_to_pwm(const char *str);
+
+#define BL_GPIO_OUTPUT_LOW             0
+#define BL_GPIO_OUTPUT_HIGH            1
+#define BL_GPIO_INPUT                  2
+
+#endif
+
diff --git a/include/linux/amlogic/media/vout/lcd/aml_bl_extern.h b/include/linux/amlogic/media/vout/lcd/aml_bl_extern.h
new file mode 100644 (file)
index 0000000..81a4e3f
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * include/linux/amlogic/media/vout/lcd/aml_bl_extern.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __AMLOGIC_BL_EXTERN_H_
+#define __AMLOGIC_BL_EXTERN_H_
+
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/pinctrl/consumer.h>
+
+enum bl_extern_type_e {
+       BL_EXTERN_I2C = 0,
+       BL_EXTERN_SPI,
+       BL_EXTERN_OTHER,
+       BL_EXTERN_MAX,
+};
+
+struct bl_extern_config_s {
+       const char *name;
+       enum bl_extern_type_e type;
+       struct gpio_desc *gpio;
+       unsigned char gpio_on;
+       unsigned char gpio_off;
+
+       int i2c_addr;
+       int i2c_bus;
+       struct gpio_desc *spi_cs;
+       struct gpio_desc *spi_clk;
+       struct gpio_desc *spi_data;
+       unsigned int dim_min;
+       unsigned int dim_max;
+       /* unsigned int level_min; */
+       /* unsigned int level_max; */
+};
+
+/*******global API******/
+struct aml_bl_extern_driver_t {
+       const char *name;
+       enum bl_extern_type_e type;
+       int (*power_on)(void);
+       int (*power_off)(void);
+       int (*set_level)(unsigned int level);
+};
+
+#define BL_EXTERN_DRIVER               "bl_extern"
+
+
+#define bl_extern_gpio_free(gpio)      gpiod_free(gpio, BL_EXTERN_DRIVER)
+#define bl_extern_gpio_input(gpio)        gpiod_direction_input(gpio)
+#define bl_extern_gpio_output(gpio, val)  gpiod_direction_output(gpio, val)
+#define bl_extern_gpio_get_value(gpio)              gpiod_get_value(gpio)
+#define bl_extern_gpio_set_value(gpio, val)         gpiod_set_value(gpio, val)
+
+
+extern struct aml_bl_extern_driver_t *aml_bl_extern_get_driver(void);
+extern int bl_extern_driver_check(void);
+extern int get_bl_extern_dt_data(struct device dev,
+                                       struct bl_extern_config_s *pdata);
+
+extern void get_bl_ext_level(struct bl_extern_config_s *bl_ext_cfg);
+
+#endif
+
diff --git a/include/linux/amlogic/media/vout/lcd/aml_ldim.h b/include/linux/amlogic/media/vout/lcd/aml_ldim.h
new file mode 100644 (file)
index 0000000..29d4368
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * include/linux/amlogic/media/vout/lcd/aml_ldim.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _INC_AML_LDIM_H_
+#define _INC_AML_LDIM_H_
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/amlogic/media/vout/aml_bl.h>
+#include <linux/spi/spi.h>
+
+enum ldim_dev_type_e {
+       LDIM_DEV_TYPE_NORMAL = 0,
+       LDIM_DEV_TYPE_SPI,
+       LDIM_DEV_TYPE_I2C,
+       LDIM_DEV_TYPE_MAX,
+};
+
+#define LDIM_SPI_INIT_ON_SIZE     300
+#define LDIM_SPI_INIT_OFF_SIZE    20
+struct ldim_dev_config_s {
+       char name[20];
+       char pinmux_name[20];
+       unsigned char type;
+       int cs_hold_delay;
+       int cs_clk_delay;
+       int en_gpio;
+       int en_gpio_on;
+       int en_gpio_off;
+       int lamp_err_gpio;
+       unsigned char fault_check;
+       unsigned char write_check;
+
+       unsigned int dim_min;
+       unsigned int dim_max;
+       unsigned char cmd_size;
+       unsigned char *init_on;
+       unsigned char *init_off;
+
+       struct bl_pwm_config_s pwm_config;
+};
+
+/*******global API******/
+struct aml_ldim_driver_s {
+       int valid_flag;
+       int dev_index;
+       int static_pic_flag;
+       struct ldim_dev_config_s *ldev_conf;
+       unsigned short *ldim_matrix_buf;
+       int (*init)(void);
+       int (*power_on)(void);
+       int (*power_off)(void);
+       int (*set_level)(unsigned int level);
+       int (*pinmux_ctrl)(char *pin_str, int status);
+       int (*pwm_vs_update)(void);
+       int (*device_power_on)(void);
+       int (*device_power_off)(void);
+       int (*device_bri_update)(unsigned short *buf, unsigned char len);
+       int (*device_bri_check)(void);
+       void (*config_print)(void);
+       void (*test_ctrl)(int flag);
+       struct pinctrl *pin;
+       struct device *dev;
+       struct spi_device *spi;
+       struct spi_board_info *spi_dev;
+};
+
+extern struct aml_ldim_driver_s *aml_ldim_get_driver(void);
+extern int aml_ldim_probe(struct platform_device *pdev);
+extern int aml_ldim_remove(void);
+
+#endif
+
diff --git a/include/linux/amlogic/media/vout/lcd/lcd_extern.h b/include/linux/amlogic/media/vout/lcd/lcd_extern.h
new file mode 100644 (file)
index 0000000..6c18502
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * include/linux/amlogic/media/vout/lcd/lcd_extern.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _INC_AML_LCD_EXTERN_H_
+#define _INC_AML_LCD_EXTERN_H_
+
+enum lcd_extern_type_e {
+       LCD_EXTERN_I2C = 0,
+       LCD_EXTERN_SPI,
+       LCD_EXTERN_MIPI,
+       LCD_EXTERN_MAX,
+};
+
+enum lcd_extern_i2c_bus_e {
+       LCD_EXTERN_I2C_BUS_AO = 0,
+       LCD_EXTERN_I2C_BUS_A,
+       LCD_EXTERN_I2C_BUS_B,
+       LCD_EXTERN_I2C_BUS_C,
+       LCD_EXTERN_I2C_BUS_D,
+       LCD_EXTERN_I2C_BUS_MAX,
+};
+#define LCD_EXTERN_I2C_BUS_INVALID   0xff
+
+#define LCD_EXTERN_SPI_CLK_FREQ_DFT  10000 /* default 10k */
+
+#define LCD_EXTERN_INIT_TABLE_MAX    500
+
+#define LCD_EXTERN_INIT_CMD          0x00
+#define LCD_EXTERN_INIT_CMD2         0x01  /* only for special i2c device */
+#define LCD_EXTERN_INIT_GPIO         0x10
+#define LCD_EXTERN_INIT_NONE         0xf0
+#define LCD_EXTERN_INIT_END          0xff
+
+
+#define LCD_EXTERN_DYNAMIC_LEN         0xff
+
+
+#define LCD_EXTERN_GPIO_NUM_MAX      6
+#define LCD_EXTERN_INDEX_INVALID     0xff
+#define LCD_EXTERN_NAME_LEN_MAX      30
+struct lcd_extern_config_s {
+       unsigned char index;
+       char name[LCD_EXTERN_NAME_LEN_MAX];
+       enum lcd_extern_type_e type;
+       unsigned char status;
+       unsigned char i2c_addr;
+       unsigned char i2c_addr2;
+       unsigned char i2c_bus;
+       unsigned char spi_gpio_cs;
+       unsigned char spi_gpio_clk;
+       unsigned char spi_gpio_data;
+       unsigned int spi_clk_freq;
+       unsigned char spi_clk_pol;
+       unsigned char cmd_size;
+       unsigned char table_init_loaded; /* internal use */
+       unsigned char *table_init_on;
+       unsigned char *table_init_off;
+};
+
+/* global API */
+#define LCD_EXT_DRIVER_MAX    10
+struct aml_lcd_extern_driver_s {
+       struct lcd_extern_config_s config;
+       int (*reg_read)(unsigned char reg, unsigned char *buf);
+       int (*reg_write)(unsigned char reg, unsigned char value);
+       int (*power_on)(void);
+       int (*power_off)(void);
+};
+
+extern struct aml_lcd_extern_driver_s *aml_lcd_extern_get_driver(int index);
+
+#endif
+
diff --git a/include/linux/amlogic/media/vout/lcd/lcd_mipi.h b/include/linux/amlogic/media/vout/lcd/lcd_mipi.h
new file mode 100644 (file)
index 0000000..99f09ec
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * include/linux/amlogic/media/vout/lcd/lcd_mipi.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _INC_LCD_MIPI_H
+#define _INC_LCD_MIPI_H
+
+/* **********************************
+ * mipi-dsi read/write api
+ */
+
+/* mipi command(payload) */
+/* format:  data_type, num, data.... */
+/* special: data_type=0xff,
+ *         num<0xff means delay ms, num=0xff means ending.
+ */
+
+/* *************************************************************
+ * Function: dsi_write_cmd
+ * Supported Data Type: DT_GEN_SHORT_WR_0, DT_GEN_SHORT_WR_1, DT_GEN_SHORT_WR_2,
+                       DT_DCS_SHORT_WR_0, DT_DCS_SHORT_WR_1,
+                       DT_GEN_LONG_WR, DT_DCS_LONG_WR,
+                       DT_SET_MAX_RET_PKT_SIZE
+                       DT_GEN_RD_0, DT_GEN_RD_1, DT_GEN_RD_2,
+                       DT_DCS_RD_0
+ * Return:              command number
+ */
+extern int dsi_write_cmd(unsigned char *payload);
+
+/* *************************************************************
+ * Function: dsi_read_single
+ * Supported Data Type: DT_GEN_RD_0, DT_GEN_RD_1, DT_GEN_RD_2,
+                       DT_DCS_RD_0
+ * Return:              data count
+                       0 for not support
+ */
+extern int dsi_read_single(unsigned char *payload, unsigned char *rd_data,
+               unsigned int rd_byte_len);
+#endif
diff --git a/include/linux/amlogic/media/vout/lcd/lcd_notify.h b/include/linux/amlogic/media/vout/lcd/lcd_notify.h
new file mode 100644 (file)
index 0000000..1dd69a7
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * include/linux/amlogic/media/vout/lcd/lcd_notify.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _INC_LCD_NOTIFY_H_
+#define _INC_LCD_NOTIFY_H_
+
+#include <linux/notifier.h>
+
+#define LCD_PRIORITY_INIT_CONFIG       4
+#define LCD_PRIORITY_INIT_VOUT         3
+
+#define LCD_PRIORITY_BLACK_SCREEN      3
+#define LCD_PRIORITY_POWER_BL_OFF      2
+#define LCD_PRIORITY_POWER_LCD         1
+#define LCD_PRIORITY_POWER_BL_ON       0
+
+#define LCD_EVENT_BL_ON             (1 << 0)
+#define LCD_EVENT_LCD_ON            (1 << 1)
+#define LCD_EVENT_IF_ON             (1 << 2)
+#define LCD_EVENT_POWER_ON          (LCD_EVENT_BL_ON | LCD_EVENT_LCD_ON)
+#define LCD_EVENT_IF_POWER_ON       (LCD_EVENT_BL_ON | LCD_EVENT_IF_ON)
+#define LCD_EVENT_BLACK_SCREEN      (1 << 3)
+#define LCD_EVENT_BL_OFF            (1 << 4)
+#define LCD_EVENT_LCD_OFF           (1 << 5)
+#define LCD_EVENT_IF_OFF            (1 << 6)
+#define LCD_EVENT_POWER_OFF         (LCD_EVENT_BL_OFF | LCD_EVENT_LCD_OFF |\
+                                       LCD_EVENT_BLACK_SCREEN)
+#define LCD_EVENT_IF_POWER_OFF      (LCD_EVENT_BL_OFF | LCD_EVENT_IF_OFF |\
+                                       LCD_EVENT_BLACK_SCREEN)
+
+/* lcd backlight index select */
+#define LCD_EVENT_BACKLIGHT_SEL     (1 << 8)
+/* lcd backlight pwm_vs vfreq change occurred */
+#define LCD_EVENT_BACKLIGHT_UPDATE  (1 << 9)
+
+#define LCD_EVENT_GAMMA_UPDATE      (1 << 10)
+
+/* lcd frame rate change occurred */
+#define LCD_EVENT_FRAME_RATE_ADJUST (1 << 12)
+/* lcd config change occurred */
+#define LCD_EVENT_CONFIG_UPDATE     (1 << 13)
+/* lcd bist pattern test occurred */
+#define LCD_EVENT_TEST_PATTERN      (1 << 14)
+
+extern int aml_lcd_notifier_register(struct notifier_block *nb);
+extern int aml_lcd_notifier_unregister(struct notifier_block *nb);
+extern int aml_lcd_notifier_call_chain(unsigned long event, void *v);
+
+#endif /* _INC_LCD_NOTIFY_H_ */
diff --git a/include/linux/amlogic/media/vout/lcd/lcd_unifykey.h b/include/linux/amlogic/media/vout/lcd/lcd_unifykey.h
new file mode 100644 (file)
index 0000000..6413024
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * include/linux/amlogic/media/vout/lcd/lcd_unifykey.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _INC_AML_LCD_UNIFYKEY_H__
+#define _INC_AML_LCD_UNIFYKEY_H__
+
+#define LCD_UNIFYKEY_WAIT_TIMEOUT    300
+
+/* declare external unifykey function */
+extern int key_unify_read(char *keyname, unsigned char *keydata,
+       unsigned int datalen, unsigned int *reallen);
+extern int key_unify_size(char *keyname, unsigned int *reallen);
+extern int key_unify_query(char *keyname, unsigned int *keystate,
+       unsigned int *keypermit);
+extern int key_unify_get_init_flag(void);
+
+
+#define LCD_UKEY_RETRY_CNT_MAX   5
+/*
+ *  lcd unifykey data struct: little-endian, for example:
+ *    4byte: d[0]=0x01, d[1]=0x02, d[2] = 0x03, d[3]= 0x04,
+ *        data = 0x04030201
+ */
+
+/* define lcd unifykey length */
+
+#define LCD_UKEY_HEAD_SIZE        10
+#define LCD_UKEY_HEAD_CRC32       4
+#define LCD_UKEY_HEAD_DATA_LEN    2
+#define LCD_UKEY_HEAD_VERSION     2
+#define LCD_UKEY_HEAD_RESERVED    2
+
+struct aml_lcd_unifykey_header_s {
+       unsigned int crc32;
+       unsigned short data_len;
+       unsigned short version;
+       unsigned short reserved;
+};
+
+/*  lcd */
+#define LCD_UKEY_LCD_SIZE          265
+
+/* header (10Byte) */
+/* LCD_UKEY_HEAD_SIZE */
+/* basic (36Byte) */
+#define LCD_UKEY_MODEL_NAME        30
+#define LCD_UKEY_INTERFACE         1
+#define LCD_UKEY_LCD_BITS          1
+#define LCD_UKEY_SCREEN_WIDTH      2
+#define LCD_UKEY_SCREEN_HEIGHT     2
+/* timing (18Byte) */
+#define LCD_UKEY_H_ACTIVE          2
+#define LCD_UKEY_V_ACTIVE          2
+#define LCD_UKEY_H_PERIOD          2
+#define LCD_UKEY_V_PERIOD          2
+#define LCD_UKEY_HS_WIDTH          2
+#define LCD_UKEY_HS_BP             2
+#define LCD_UKEY_HS_POL            1
+#define LCD_UKEY_VS_WIDTH          2
+#define LCD_UKEY_VS_BP             2
+#define LCD_UKEY_VS_POL            1
+/* customer (31Byte) */
+#define LCD_UKEY_FR_ADJ_TYPE       1
+#define LCD_UKEY_SS_LEVEL          1
+#define LCD_UKEY_CLK_AUTO_GEN      1
+#define LCD_UKEY_PCLK              4
+#define LCD_UKEY_H_PERIOD_MIN      2
+#define LCD_UKEY_H_PERIOD_MAX      2
+#define LCD_UKEY_V_PERIOD_MIN      2
+#define LCD_UKEY_V_PERIOD_MAX      2
+#define LCD_UKEY_PCLK_MIN          4
+#define LCD_UKEY_PCLK_MAX          4
+#define LCD_UKEY_CUST_VAL_8        4
+#define LCD_UKEY_CUST_VAL_9        4
+/* interface (20Byte) */
+#define LCD_UKEY_IF_ATTR_0         2
+#define LCD_UKEY_IF_ATTR_1         2
+#define LCD_UKEY_IF_ATTR_2         2
+#define LCD_UKEY_IF_ATTR_3         2
+#define LCD_UKEY_IF_ATTR_4         2
+#define LCD_UKEY_IF_ATTR_5         2
+#define LCD_UKEY_IF_ATTR_6         2
+#define LCD_UKEY_IF_ATTR_7         2
+#define LCD_UKEY_IF_ATTR_8         2
+#define LCD_UKEY_IF_ATTR_9         2
+/* power (5Byte * n) */
+#define LCD_UKEY_PWR_TYPE          1
+#define LCD_UKEY_PWR_INDEX         1
+#define LCD_UKEY_PWR_VAL           1
+#define LCD_UKEY_PWR_DELAY         2
+
+/* lcd extern */
+#define LCD_UKEY_LCD_EXT_SIZE       550
+
+/* header (10Byte) */
+/* LCD_UKEY_HEAD_SIZE */
+/* basic (33Byte) */
+#define LCD_UKEY_EXT_NAME           30
+#define LCD_UKEY_EXT_INDEX          1
+#define LCD_UKEY_EXT_TYPE           1
+#define LCD_UKEY_EXT_STATUS         1
+/* type (10Byte) */
+#define LCD_UKEY_EXT_TYPE_VAL_0     1
+#define LCD_UKEY_EXT_TYPE_VAL_1     1
+#define LCD_UKEY_EXT_TYPE_VAL_2     1
+#define LCD_UKEY_EXT_TYPE_VAL_3     1
+#define LCD_UKEY_EXT_TYPE_VAL_4     1
+#define LCD_UKEY_EXT_TYPE_VAL_5     1
+#define LCD_UKEY_EXT_TYPE_VAL_6     1
+#define LCD_UKEY_EXT_TYPE_VAL_7     1
+#define LCD_UKEY_EXT_TYPE_VAL_8     1
+#define LCD_UKEY_EXT_TYPE_VAL_9     1
+/* init (cmd_size) */
+#define LCD_UKEY_EXT_INIT_TYPE       1
+/*#define LCD_UKEY_EXT_INIT_VAL        1   //not defined */
+#define LCD_UKEY_EXT_INIT_DELAY      1
+
+/* backlight */
+#define LCD_UKEY_BL_SIZE            92
+
+/* header (10Byte) */
+/* LCD_UKEY_HEAD_SIZE */
+/* basic (30Byte) */
+#define LCD_UKEY_BL_NAME            30
+/* level (12Byte) */
+#define LCD_UKEY_BL_LEVEL_UBOOT     2
+#define LCD_UKEY_BL_LEVEL_KERNEL    2
+#define LCD_UKEY_BL_LEVEL_MAX       2
+#define LCD_UKEY_BL_LEVEL_MIN       2
+#define LCD_UKEY_BL_LEVEL_MID       2
+#define LCD_UKEY_BL_LEVEL_MID_MAP   2
+/* method (8Byte) */
+#define LCD_UKEY_BL_METHOD          1
+#define LCD_UKEY_BL_EN_GPIO         1
+#define LCD_UKEY_BL_EN_GPIO_ON      1
+#define LCD_UKEY_BL_EN_GPIO_OFF     1
+#define LCD_UKEY_BL_ON_DELAY        2
+#define LCD_UKEY_BL_OFF_DELAY       2
+/* pwm (32Byte) */
+#define LCD_UKEY_BL_PWM_ON_DELAY    2
+#define LCD_UKEY_BL_PWM_OFF_DELAY   2
+#define LCD_UKEY_BL_PWM_METHOD      1
+#define LCD_UKEY_BL_PWM_PORT        1
+#define LCD_UKEY_BL_PWM_FREQ        4
+#define LCD_UKEY_BL_PWM_DUTY_MAX    1
+#define LCD_UKEY_BL_PWM_DUTY_MIN    1
+#define LCD_UKEY_BL_PWM_GPIO        1
+#define LCD_UKEY_BL_PWM_GPIO_OFF    1
+#define LCD_UKEY_BL_PWM2_METHOD     1
+#define LCD_UKEY_BL_PWM2_PORT       1
+#define LCD_UKEY_BL_PWM2_FREQ       4
+#define LCD_UKEY_BL_PWM2_DUTY_MAX   1
+#define LCD_UKEY_BL_PWM2_DUTY_MIN   1
+#define LCD_UKEY_BL_PWM2_GPIO       1
+#define LCD_UKEY_BL_PWM2_GPIO_OFF   1
+#define LCD_UKEY_BL_PWM_LEVEL_MAX   2
+#define LCD_UKEY_BL_PWM_LEVEL_MIN   2
+#define LCD_UKEY_BL_PWM2_LEVEL_MAX  2
+#define LCD_UKEY_BL_PWM2_LEVEL_MIN  2
+
+
+/* API */
+extern int lcd_unifykey_len_check(int key_len, int len);
+extern int lcd_unifykey_check(char *key_name);
+extern int lcd_unifykey_header_check(unsigned char *buf,
+               struct aml_lcd_unifykey_header_s *header);
+extern int lcd_unifykey_get(char *key_name,
+               unsigned char *buf, int *len);
+extern void lcd_unifykey_print(void);
+
+#endif
diff --git a/include/linux/amlogic/media/vout/lcd/lcd_vout.h b/include/linux/amlogic/media/vout/lcd/lcd_vout.h
new file mode 100644 (file)
index 0000000..4d5f52b
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * include/linux/amlogic/media/vout/lcd/lcd_vout.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _INC_LCD_VOUT_H
+#define _INC_LCD_VOUT_H
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/amlogic/media/vout/vout_notify.h>
+
+/* debug print define */
+/* #define LCD_DEBUG_INFO */
+extern unsigned char lcd_debug_print_flag;
+#define LCDPR(fmt, args...)     pr_info("lcd: "fmt"", ## args)
+#define LCDERR(fmt, args...)    pr_err("lcd: error: "fmt"", ## args)
+
+/* clk parameter bit define */
+/* pll_ctrl, div_ctrl, clk_ctrl */
+
+/* ******** pll_ctrl ******** */
+#define PLL_CTRL_OD3                20 /* [21:20] */
+#define PLL_CTRL_OD2                18 /* [19:18] */
+#define PLL_CTRL_OD1                16 /* [17:16] */
+#define PLL_CTRL_N                  9  /* [13:9] */
+#define PLL_CTRL_M                  0  /* [8:0] */
+
+/* ******** div_ctrl ******** */
+#define DIV_CTRL_EDP_DIV1           24 /* [26:24] */
+#define DIV_CTRL_EDP_DIV0           20 /* [23:20] */
+#define DIV_CTRL_DIV_POST           12 /* [14:12] */
+#define DIV_CTRL_DIV_PRE            8 /* [10:8] */
+#define DIV_CTRL_DIV_SEL            8 /* [15:8] */
+#define DIV_CTRL_XD                 0 /* [7:0] */
+
+/* ******** clk_ctrl ******** */
+#define CLK_CTRL_LEVEL              12 /* [14:12] */
+#define CLK_CTRL_FRAC               0  /* [11:0] */
+
+/* VENC to TCON sync delay */
+#define TTL_DELAY                   13
+
+/* ******** AXG ******** */
+/* bit[15:11] */
+#define BIT_PHY_LANE_AXG        11
+#define PHY_LANE_WIDTH_AXG       5
+
+/* MIPI-DSI */
+#define DSI_LANE_0              (1 << 4)
+#define DSI_LANE_1              (1 << 3)
+#define DSI_LANE_CLK            (1 << 2)
+#define DSI_LANE_2              (1 << 1)
+#define DSI_LANE_3              (1 << 0)
+#define DSI_LANE_COUNT_1        (DSI_LANE_CLK | DSI_LANE_0)
+#define DSI_LANE_COUNT_2        (DSI_LANE_CLK | DSI_LANE_0 | DSI_LANE_1)
+#define DSI_LANE_COUNT_3        (DSI_LANE_CLK | DSI_LANE_0 |\
+                                       DSI_LANE_1 | DSI_LANE_2)
+#define DSI_LANE_COUNT_4        (DSI_LANE_CLK | DSI_LANE_0 |\
+                                       DSI_LANE_1 | DSI_LANE_2 | DSI_LANE_3)
+
+
+/* global control define */
+enum lcd_mode_e {
+       LCD_MODE_TV = 0,
+       LCD_MODE_TABLET,
+       LCD_MODE_MAX,
+};
+
+
+enum lcd_chip_e {
+       LCD_CHIP_GXTVBB = 0,
+       LCD_CHIP_GXL,   /* 1 */
+       LCD_CHIP_GXM,   /* 2 */
+       LCD_CHIP_TXL,   /* 3 */
+       LCD_CHIP_TXLX,  /* 4 */
+       LCD_CHIP_AXG,   /* 5 */
+       LCD_CHIP_MAX,
+};
+
+
+enum lcd_type_e {
+       LCD_TTL = 0,
+       LCD_LVDS,
+       LCD_VBYONE,
+       LCD_MIPI,
+       LCD_TYPE_MAX,
+};
+
+#define MOD_LEN_MAX        30
+struct lcd_basic_s {
+       char model_name[MOD_LEN_MAX];
+       enum lcd_type_e lcd_type;
+       unsigned short lcd_bits;
+
+       unsigned short h_active;    /* Horizontal display area */
+       unsigned short v_active;    /* Vertical display area */
+       unsigned short h_period;    /* Horizontal total period time */
+       unsigned short v_period;    /* Vertical total period time */
+       unsigned short h_period_min;
+       unsigned short h_period_max;
+       unsigned short v_period_min;
+       unsigned short v_period_max;
+       unsigned int lcd_clk_min;
+       unsigned int lcd_clk_max;
+
+       unsigned int screen_width;  /* screen physical width(unit: mm) */
+       unsigned int screen_height; /* screen physical height(unit: mm) */
+};
+
+#define LCD_CLK_FRAC_UPDATE     (1 << 0)
+#define LCD_CLK_PLL_CHANGE      (1 << 1)
+struct lcd_timing_s {
+       unsigned char clk_auto; /* clk parameters auto generation */
+       unsigned int lcd_clk;   /* pixel clock(unit: Hz) */
+       unsigned int lcd_clk_dft; /* internal used */
+       unsigned int h_period_dft; /* internal used */
+       unsigned int v_period_dft; /* internal used */
+       unsigned char clk_change; /* internal used */
+       unsigned int pll_ctrl;  /* pll settings */
+       unsigned int div_ctrl;  /* divider settings */
+       unsigned int clk_ctrl;  /* clock settings */
+
+       unsigned char fr_adjust_type; /* 0=clock, 1=htotal, 2=vtotal */
+       unsigned char ss_level;
+
+       unsigned int sync_duration_num;
+       unsigned int sync_duration_den;
+
+       unsigned short video_on_pixel;
+       unsigned short video_on_line;
+
+       unsigned short hsync_width;
+       unsigned short hsync_bp;
+       unsigned short hsync_pol;
+       unsigned short vsync_width;
+       unsigned short vsync_bp;
+       unsigned short vsync_pol;
+       /* unsigned int vsync_h_phase; // [31]sign, [15:0]value */
+       unsigned int h_offset;
+       unsigned int v_offset;
+
+       unsigned short de_hs_addr;
+       unsigned short de_he_addr;
+       unsigned short de_vs_addr;
+       unsigned short de_ve_addr;
+
+       unsigned short hs_hs_addr;
+       unsigned short hs_he_addr;
+       unsigned short hs_vs_addr;
+       unsigned short hs_ve_addr;
+
+       unsigned short vs_hs_addr;
+       unsigned short vs_he_addr;
+       unsigned short vs_vs_addr;
+       unsigned short vs_ve_addr;
+};
+
+/*  HDR info define */
+struct lcd_hdr_info_s {
+       unsigned int hdr_support;
+       unsigned int features;
+       unsigned int primaries_r_x;
+       unsigned int primaries_r_y;
+       unsigned int primaries_g_x;
+       unsigned int primaries_g_y;
+       unsigned int primaries_b_x;
+       unsigned int primaries_b_y;
+       unsigned int white_point_x;
+       unsigned int white_point_y;
+       unsigned int luma_max;
+       unsigned int luma_min;
+       unsigned int luma_avg;
+};
+
+struct ttl_config_s {
+       unsigned int clk_pol;
+       unsigned int sync_valid; /* [1]DE, [0]hvsync */
+       unsigned int swap_ctrl; /* [1]rb swap, [0]bit swap */
+};
+
+#define LVDS_PHY_VSWING_DFT        3
+#define LVDS_PHY_PREEM_DFT         0
+#define LVDS_PHY_CLK_VSWING_DFT    0
+#define LVDS_PHY_CLK_PREEM_DFT     0
+struct lvds_config_s {
+       unsigned int lvds_vswing;
+       unsigned int lvds_repack;
+       unsigned int dual_port;
+       unsigned int pn_swap;
+       unsigned int port_swap;
+       unsigned int lane_reverse;
+       unsigned int port_sel;
+       unsigned int phy_vswing;
+       unsigned int phy_preem;
+       unsigned int phy_clk_vswing;
+       unsigned int phy_clk_preem;
+};
+
+#define VX1_PHY_VSWING_DFT    3
+#define VX1_PHY_PREEM_DFT     0
+struct vbyone_config_s {
+       unsigned int lane_count;
+       unsigned int region_num;
+       unsigned int byte_mode;
+       unsigned int color_fmt;
+       unsigned int phy_div;
+       unsigned int bit_rate;
+       unsigned int phy_vswing; /*[4]:ext_pullup, [3:0]vswing*/
+       unsigned int phy_preem;
+       unsigned int intr_en;
+       unsigned int vsync_intr_en;
+};
+
+/* mipi-dsi config */
+/* Operation mode parameters */
+#define OPERATION_VIDEO_MODE     0
+#define OPERATION_COMMAND_MODE   1
+
+#define SYNC_PULSE               0x0
+#define SYNC_EVENT               0x1
+#define BURST_MODE               0x2
+
+/* command config */
+#define DSI_CMD_INDEX            1  /* byte[1] */
+
+#define DSI_INIT_ON_MAX          100
+#define DSI_INIT_OFF_MAX         30
+
+struct dsi_config_s {
+       unsigned char lane_num;
+       unsigned int bit_rate_max; /* MHz */
+       unsigned int bit_rate_min; /* MHz*/
+       unsigned int bit_rate; /* Hz */
+       unsigned int factor_numerator;
+       unsigned int factor_denominator; /* 100 */
+       unsigned char operation_mode_init; /* 0=video mode, 1=command mode */
+       unsigned char operation_mode_display; /* 0=video mode, 1=command mode */
+       unsigned char video_mode_type; /* 0=sync_pulse, 1=sync_event, 2=burst */
+       unsigned char clk_lp_continuous; /* 0=stop, 1=continue */
+       unsigned char phy_stop_wait; /* 0=auto, 1=standard, 2=slow */
+
+       unsigned int venc_data_width;
+       unsigned int dpi_data_format;
+       unsigned int venc_fmt;
+
+       unsigned char *dsi_init_on;
+       unsigned char *dsi_init_off;
+       unsigned char extern_init;
+};
+
+struct lcd_control_config_s {
+       struct ttl_config_s *ttl_config;
+       struct lvds_config_s *lvds_config;
+       struct vbyone_config_s *vbyone_config;
+       struct dsi_config_s *mipi_config;
+};
+
+/* power control define */
+enum lcd_power_type_e {
+       LCD_POWER_TYPE_CPU = 0,
+       LCD_POWER_TYPE_PMU,
+       LCD_POWER_TYPE_SIGNAL,
+       LCD_POWER_TYPE_EXTERN,
+       LCD_POWER_TYPE_MAX,
+};
+
+enum lcd_pmu_gpio_e {
+       LCD_PMU_GPIO0 = 0,
+       LCD_PMU_GPIO1,
+       LCD_PMU_GPIO2,
+       LCD_PMU_GPIO3,
+       LCD_PMU_GPIO4,
+       LCD_PMU_GPIO_MAX,
+};
+
+#define LCD_GPIO_MAX                    0xff
+#define LCD_GPIO_OUTPUT_LOW             0
+#define LCD_GPIO_OUTPUT_HIGH            1
+#define LCD_GPIO_INPUT                  2
+
+/* Power Control */
+#define LCD_CPU_GPIO_NUM_MAX         10
+struct lcd_cpu_gpio_s {
+       char name[15];
+       struct gpio_desc *gpio;
+       int flag;
+};
+
+#define LCD_PMU_GPIO_NUM_MAX         3
+struct lcd_pmu_gpio_s {
+       char name[15];
+       int gpio;
+};
+
+#define LCD_PWR_STEP_MAX         15
+struct lcd_power_step_s {
+       unsigned char type;
+       unsigned int index; /* point to lcd_cpu/pmu_gpio_s or lcd_extern */
+       unsigned int value;
+       unsigned int delay;
+};
+
+struct lcd_power_ctrl_s {
+       struct lcd_cpu_gpio_s cpu_gpio[LCD_CPU_GPIO_NUM_MAX];
+       struct lcd_pmu_gpio_s pmu_gpio[LCD_PMU_GPIO_NUM_MAX];
+       struct lcd_power_step_s power_on_step[LCD_PWR_STEP_MAX];
+       struct lcd_power_step_s power_off_step[LCD_PWR_STEP_MAX];
+       int power_on_step_max; /*  internal use for debug */
+       int power_off_step_max; /* internal use for debug */
+};
+
+struct lcd_clk_gate_ctrl_s {
+       struct reset_control *encl;
+       struct reset_control *vencl;
+};
+
+struct lcd_config_s {
+       char *lcd_propname;
+       unsigned int backlight_index;
+       struct lcd_basic_s lcd_basic;
+       struct lcd_timing_s lcd_timing;
+       struct lcd_hdr_info_s hdr_info;
+       struct lcd_control_config_s lcd_control;
+       struct lcd_power_ctrl_s *lcd_power;
+       struct pinctrl *pin;
+       unsigned char pinmux_flag;
+       struct lcd_clk_gate_ctrl_s rstc;
+};
+
+struct lcd_duration_s {
+       unsigned int duration_num;
+       unsigned int duration_den;
+};
+
+struct aml_lcd_drv_s {
+       char *version;
+       enum lcd_chip_e chip_type;
+       unsigned char lcd_mode;
+       unsigned char lcd_status;
+       unsigned char lcd_key_valid;
+       unsigned char lcd_config_load;
+       unsigned char lcd_test_flag;
+       unsigned char lcd_mute;
+
+       struct clk *vencl_top;
+       struct clk *vencl_int;
+       struct clk *dsi_host;
+       struct clk *dsi_phy;
+       struct clk *dsi_meas;
+
+       struct device *dev;
+       struct lcd_config_s *lcd_config;
+       struct vinfo_s *lcd_info;
+       struct class *lcd_debug_class;
+       int fr_auto_policy;
+       struct lcd_duration_s std_duration;
+
+       void (*vout_server_init)(void);
+       void (*driver_init_pre)(void);
+       int (*driver_init)(void);
+       void (*driver_disable)(void);
+       int (*driver_change)(void);
+       void (*module_reset)(void);
+       void (*power_tiny_ctrl)(int status);
+       void (*driver_tiny_enable)(void);
+       void (*driver_tiny_disable)(void);
+       void (*module_tiny_reset)(void);
+       void (*lcd_test_pattern_restore)(void);
+       void (*power_ctrl)(int status);
+
+       struct workqueue_struct *workqueue;
+       struct delayed_work lcd_probe_delayed_work;
+       struct delayed_work lcd_vx1_delayed_work;
+       struct work_struct  lcd_resume_work;
+};
+
+extern struct aml_lcd_drv_s *aml_lcd_get_driver(void);
+
+/*  IOCTL define  */
+#define LCD_IOC_TYPE               'C'
+#define LCD_IOC_NR_GET_HDR_INFO    0x0
+#define LCD_IOC_NR_SET_HDR_INFO    0x1
+
+#define LCD_IOC_CMD_GET_HDR_INFO   \
+       _IOR(LCD_IOC_TYPE, LCD_IOC_NR_GET_HDR_INFO, struct lcd_hdr_info_s)
+#define LCD_IOC_CMD_SET_HDR_INFO   \
+       _IOW(LCD_IOC_TYPE, LCD_IOC_NR_SET_HDR_INFO, struct lcd_hdr_info_s)
+
+#endif
index 9275929..202881f 100644 (file)
@@ -19,6 +19,7 @@
 #define _VINFO_H_
 #include <linux/amlogic/media/vfm/video_common.h>
 
+
 /* the MSB is represent vmode set by vmode_init */
 #define        VMODE_INIT_BIT_MASK     0x8000
 #define        VMODE_MODE_BIT_MASK     0xff
@@ -57,6 +58,7 @@ struct hdr_info {
        u32 lumi_max; /* RX EDID Lumi Max value */
        u32 lumi_avg; /* RX EDID Lumi Avg value */
        u32 lumi_min; /* RX EDID Lumi Min value */
+       u8 sink_flag; /*0 = hdmi, 1 = panel*/
 };
 enum eotf_type {
        EOTF_T_NULL = 0,