From: Evoke Zhang Date: Fri, 9 Jun 2017 02:46:09 +0000 (+0800) Subject: lcd: add lcd driver for axg X-Git-Tag: khadas-vims-v0.9.6-release~3067 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c966c5d0b7630b2fb962135edfcce55ba1cf3d7c;p=platform%2Fkernel%2Flinux-amlogic.git lcd: add lcd driver for axg PD#142470: lcd: add lcd driver for axg Change-Id: I58e2a2af474bafbbe3718f51493c9b46cb07cf31 Signed-off-by: Evoke Zhang --- diff --git a/MAINTAINERS b/MAINTAINERS index c8e71c6..bd8c7ff 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13948,3 +13948,11 @@ F: include/linux/amlogic/unifykey/* AMLOGIC AXG ADD OSD DRIVER M: Pengcheng Chen F: drivers/amlogic/media/osd/osd_io.c + +AMLOGIC LCD driver +M: Weiming Liu +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 diff --git a/arch/arm64/boot/dts/amlogic/axg_s400.dts b/arch/arm64/boot/dts/amlogic/axg_s400.dts index 083b862..0bff4a9c 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s400.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s400.dts @@ -18,7 +18,7 @@ /dts-v1/; #include "mesonaxg.dtsi" - +#include "mesonaxg_s400-panel.dtsi" / { model = "Amlogic"; compatible = "amlogic, axg"; @@ -399,6 +399,12 @@ /* 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"; @@ -975,6 +981,13 @@ 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 index 0000000..9e48913 --- /dev/null +++ b/arch/arm64/boot/dts/amlogic/mesonaxg_s400-panel.dtsi @@ -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 */ + diff --git a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi index 024c59d..abf72ba 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi @@ -1051,6 +1051,50 @@ }; }; + 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 index 0000000..c0a888f --- /dev/null +++ b/arch/arm64/boot/dts/amlogic/mesongxm_q200-panel.dtsi @@ -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 */ + diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index 968802e..7daed21 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -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 diff --git a/drivers/amlogic/clk/axg/axg_clk-pll.c b/drivers/amlogic/clk/axg/axg_clk-pll.c index 625b129..e5f4663 100644 --- a/drivers/amlogic/clk/axg/axg_clk-pll.c +++ b/drivers/amlogic/clk/axg/axg_clk-pll.c @@ -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)); diff --git a/drivers/amlogic/media/enhancement/amvecm/amvecm.c b/drivers/amlogic/media/enhancement/amvecm/amvecm.c index 23ea3a2..2c09cef 100644 --- a/drivers/amlogic/media/enhancement/amvecm/amvecm.c +++ b/drivers/amlogic/media/enhancement/amvecm/amvecm.c @@ -39,8 +39,8 @@ #include #include -#ifdef CONFIG_AML_LCD -#include +#ifdef CONFIG_AMLOGIC_LCD +#include #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; diff --git a/drivers/amlogic/media/vin/tvin/vdin/vdin_ctl.c b/drivers/amlogic/media/vin/tvin/vdin/vdin_ctl.c index c6eba2a..c4be691 100644 --- a/drivers/amlogic/media/vin/tvin/vdin/vdin_ctl.c +++ b/drivers/amlogic/media/vin/tvin/vdin/vdin_ctl.c @@ -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, diff --git a/drivers/amlogic/media/vin/tvin/vdin/vdin_ctl.h b/drivers/amlogic/media/vin/tvin/vdin/vdin_ctl.h index a0785b6..6c808e7 100644 --- a/drivers/amlogic/media/vin/tvin/vdin/vdin_ctl.h +++ b/drivers/amlogic/media/vin/tvin/vdin/vdin_ctl.h @@ -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; diff --git a/drivers/amlogic/media/vin/tvin/vdin/vdin_debug.c b/drivers/amlogic/media/vin/tvin/vdin/vdin_debug.c index ddc0e6d..6393d15 100644 --- a/drivers/amlogic/media/vin/tvin/vdin/vdin_debug.c +++ b/drivers/amlogic/media/vin/tvin/vdin/vdin_debug.c @@ -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; diff --git a/drivers/amlogic/media/vin/tvin/vdin/vdin_drv.c b/drivers/amlogic/media/vin/tvin/vdin/vdin_drv.c index 42a6316..807cba2 100644 --- a/drivers/amlogic/media/vin/tvin/vdin/vdin_drv.c +++ b/drivers/amlogic/media/vin/tvin/vdin/vdin_drv.c @@ -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) && diff --git a/drivers/amlogic/media/vout/Kconfig b/drivers/amlogic/media/vout/Kconfig index e1f107c..c610744 100644 --- a/drivers/amlogic/media/vout/Kconfig +++ b/drivers/amlogic/media/vout/Kconfig @@ -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 diff --git a/drivers/amlogic/media/vout/Makefile b/drivers/amlogic/media/vout/Makefile index 5eea3dc..ec7ca51 100644 --- a/drivers/amlogic/media/vout/Makefile +++ b/drivers/amlogic/media/vout/Makefile @@ -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 index 0000000..dd4debe --- /dev/null +++ b/drivers/amlogic/media/vout/backlight/Kconfig @@ -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 index 0000000..27b6297 --- /dev/null +++ b/drivers/amlogic/media/vout/backlight/Makefile @@ -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 index 0000000..7278b4a --- /dev/null +++ b/drivers/amlogic/media/vout/backlight/aml_bl.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_AMLOGIC_LCD +#include +#include +#endif +#ifdef CONFIG_AMLOGIC_BL_EXTERN +#include +#endif +#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING +#include +#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 > pwm ; set pwm frequency(unit in Hz for pwm, vfreq multiple for pwm_vs)\n" +" echo duty > pwm ; set pwm duty cycle(unit: %)\n" +" echo pol > pwm ; set pwm polarity(unit: %)\n" +" echo max > pwm ; set pwm duty_max(unit: %)\n" +" echo 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 index 0000000..125d3fd --- /dev/null +++ b/drivers/amlogic/media/vout/backlight/aml_bl.dts @@ -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 index 0000000..8e38f8c --- /dev/null +++ b/drivers/amlogic/media/vout/backlight/aml_bl_reg.h @@ -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 + +/* 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 index 0000000..f8548e2 --- /dev/null +++ b/drivers/amlogic/media/vout/backlight/aml_ldim/Kconfig @@ -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 index 0000000..6873fe6 --- /dev/null +++ b/drivers/amlogic/media/vout/backlight/bl_extern/Kconfig @@ -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 index 0000000..8c260b5 --- /dev/null +++ b/drivers/amlogic/media/vout/backlight/bl_extern/Makefile @@ -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 index 0000000..1e55875 --- /dev/null +++ b/drivers/amlogic/media/vout/backlight/bl_extern/aml_bl_extern.dts @@ -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 index 0000000..1626e25 --- /dev/null +++ b/drivers/amlogic/media/vout/backlight/bl_extern/bl_extern.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..472eaf0 --- /dev/null +++ b/drivers/amlogic/media/vout/backlight/bl_extern/i2c_lp8556.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000..d47ed5c --- /dev/null +++ b/drivers/amlogic/media/vout/backlight/bl_extern/mipi_LT070ME05.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..efe4994 --- /dev/null +++ b/drivers/amlogic/media/vout/backlight/bl_extern/pmu_aml1218.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU +#include +#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 index 0000000..ae77c24 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/Kconfig @@ -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 index 0000000..8018bfd --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/Makefile @@ -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 index 0000000..b35f057 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_clk_config.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..1db5e45 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_clk_config.h @@ -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 +#include + +/* 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 index 0000000..e614300 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_common.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_AML_VPU +#include +#endif +#include +#include +#include +#include + +#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", ¶[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", ¶[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", ¶[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 index 0000000..08ae6af --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_common.h @@ -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 +#include +#include +#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 index 0000000..99a9c76 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_debug.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 > frame_rate ; set lcd frame rate adjust type\n" +" echo set > frame_rate ; set lcd frame rate(unit in 1/100Hz)\n" +" cat frame_rate ; read current lcd frame rate\n" +"\n" +" echo > test ; show lcd bist pattern(1~7), 0=disable bist\n" +"\n" +" echo w > reg ; write data to vcbus|hiu|cbus|periphs reg\n" +" echo r > reg ; read vcbus|hiu|cbus|periphs reg\n" +" echo d > 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 ... > debug ; lcd common debug, use 'cat debug' for help\n" +" cat debug ; print help information for debug command\n" +"\n" +" echo > 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 > debug ; set lcd pixel clock, unit in Hz\n" +" echo bit > debug ; set lcd bits\n" +" echo basic > debug ; set lcd basic config\n" +" echo sync > debug ; set lcd sync timing\n" +"data format:\n" +" : 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 > 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", ®32, &data32); + bus = 0; + } else if (buf[1] == 'h') { + ret = sscanf(buf, "wh %x %x", ®32, &data32); + bus = 1; + } else if (buf[1] == 'c') { + ret = sscanf(buf, "wc %x %x", ®32, &data32); + bus = 2; + } else if (buf[1] == 'p') { + ret = sscanf(buf, "wp %x %x", ®32, &data32); + bus = 3; + } else if (buf[1] == 'm') { + if (buf[2] == 'h') { /* mipi host */ + ret = sscanf(buf, "wmh %x %x", ®32, &data32); + bus = 4; + } else if (buf[2] == 'p') { /* mipi phy */ + ret = sscanf(buf, "wmp %x %x", ®32, &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", ®32); + bus = 0; + } else if (buf[1] == 'h') { + ret = sscanf(buf, "rh %x", ®32); + bus = 1; + } else if (buf[1] == 'c') { + ret = sscanf(buf, "rc %x", ®32); + bus = 2; + } else if (buf[1] == 'p') { + ret = sscanf(buf, "rp %x", ®32); + bus = 3; + } else if (buf[1] == 'm') { + if (buf[2] == 'h') { /* mipi host */ + ret = sscanf(buf, "rmh %x", ®32); + bus = 4; + } else if (buf[2] == 'p') { /* mipi phy */ + ret = sscanf(buf, "rmp %x", ®32); + 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", ®32, &data32); + bus = 0; + } else if (buf[1] == 'h') { + ret = sscanf(buf, "dh %x %d", ®32, &data32); + bus = 1; + } else if (buf[1] == 'c') { + ret = sscanf(buf, "dc %x %d", ®32, &data32); + bus = 2; + } else if (buf[1] == 'p') { + ret = sscanf(buf, "dp %x %d", ®32, &data32); + bus = 3; + } else if (buf[1] == 'm') { + if (buf[2] == 'h') { /* mipi host */ + ret = sscanf(buf, "dmh %x %x", ®32, &data32); + bus = 4; + } else if (buf[2] == 'p') { /* mipi phy */ + ret = sscanf(buf, "dmp %x %x", ®32, &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 > ttl ; set ttl config\n" +"data format:\n" +" : 0=negative, 1=positive\n" +" : for DE, 0=invalid, 1=valid\n" +" : for hvsync, 0=invalid, 1=valid\n" +" : for R/B port, 0=normal, 1=swap\n" +" : for RGB MSB/LSB, 0=normal, 1=swap\n" +"\n" +}; + +static const char *lcd_lvds_debug_usage_str = { +"Usage:\n" +" echo > lvds ; set lvds config\n" +"data format:\n" +" : 0=JEIDA mode, 1=VESA mode\n" +" : 0=single port, 1=dual port\n" +" : 0=normal, 1=swap p/n channels\n" +" : 0=normal, 1=swap A/B port\n" +" : 0=normal, 1=swap A0-A4/B0-B4\n" +"\n" +" echo > phy ; set vbyone phy config\n" +"data format:\n" +" : vswing level, support 0~7\n" +" : preemphasis level, support 0~7\n" +"\n" +}; + +static const char *lcd_vbyone_debug_usage_str = { +"Usage:\n" +" echo > vbyone ; set vbyone config\n" +"data format:\n" +" : 4/8/16\n" +" : 1/2\n" +" : 3/4/5\n" +"\n" +" echo > phy ; set vbyone phy config\n" +"data format:\n" +" : vswing level, support 0~7\n" +" : preemphasis level, support 0~7\n" +" : 3/4/5\n" +"\n" +" echo intr > vbyone; enable or disable vbyone interrupt\n" +"data format:\n" +" : 0=temp no use intr, 1=temp use intr. keep effect until reset lcd driver\n" +" : 0=disable intr, 1=enable intr\n" +"\n" +" echo vintr > vbyone; enable or disable vbyone interrupt\n" +"data format:\n" +" : 0=disable vsync monitor intr, 1=enable vsync monitor intr\n" +"\n" +}; + +static const char *lcd_mipi_debug_usage_str = { +"Usage:\n" +" echo > mipi ; set mpi config\n" +"data format:\n" +" : 1/2/3/4\n" +" : unit in MHz\n" +" : : special adjust, 0 for default\n" +" : operation mode for init (0=video mode, 1=command mode)\n" +" : operation mode for display (0=video mode, 1=command mode)\n" +" : video mode type (0=sync_pulse, 1=sync_event, 2=burst)\n" +" : 0=stop, 1=continue\n" +" : 0=auto, 1=standard, 2=slow\n" +"\n" +}; + +static const char *lcd_edp_debug_usage_str = { +"Usage:\n" +" echo > edp ; set edp config\n" +"data format:\n" +" : 0=1.62G, 1=2.7G\n" +" : 1/2/4\n" +" : 0=no use, 1=use, default=0\n" +" : 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", + ¶[0], ¶[1], ¶[2], ¶[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 index 0000000..d357b03 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_extern/Kconfig @@ -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 index 0000000..79facc6 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_extern/Makefile @@ -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 index 0000000..7c74074 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_extern/ext_default.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..d666df1 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_extern/i2c_DLPC3439.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..66d337b --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_extern/i2c_T5800Q.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 <> ... <> > 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 index 0000000..31280a6 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_extern/i2c_anx6345.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..462ba4b --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_extern/i2c_anx6345.h @@ -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 index 0000000..bbeb459 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_extern/i2c_tc101.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..6615564 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 > 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 index 0000000..209b51a --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.dts @@ -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 index 0000000..e0960b0 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.h @@ -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 +#include +#include +#include +#include + +#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 index 0000000..80b15c2 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_extern/mipi_KD080D13.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..1e7111a --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_extern/mipi_N070ICN.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..7c2678c --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_extern/spi_LD070WS2.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..23d7f05 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_notify.c @@ -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 +#include +#include + +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 index 0000000..dc87e56 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_reg.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..f7c4f09 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_reg.h @@ -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 + +/* 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 index 0000000..729a0fa --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_tablet/Kconfig @@ -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 index 0000000..9e2a579 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_tablet/Makefile @@ -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 index 0000000..4c88d06 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_tablet/aml_lcd.dts @@ -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 index 0000000..183dc1f --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_drv.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_AML_VPU +#include +#endif +#include +#include +#include +#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 index 0000000..54e0c9d --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_tablet.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_AML_VPU +#include +#endif +#include +#include +#include +#include +#include +#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", ¶[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", ¶[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", ¶[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", ¶[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", + ¶[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", + ¶[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", + ¶[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", + ¶[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", + ¶[0], 4); + if (ret) { + ret = of_property_read_u32_array(child, "phy_attr", + ¶[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", + ¶[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", + ¶[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", + ¶[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", + ¶[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", + ¶[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 index 0000000..6c435dd --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_tablet.h @@ -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 index 0000000..c1cad68 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_tablet/mipi_dsi_util.c @@ -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 +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_AMLOGIC_LCD_EXTERN +#include +#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, ¶[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, ¶[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<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 index 0000000..359945f --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_tablet/mipi_dsi_util.h @@ -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 + + +/* ******************************************************** + * 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 index 0000000..3be366f --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_tv/Makefile @@ -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 index 0000000..a2ec892 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_tv/lcd_drv.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_AML_VPU +#include +#endif +#include +#include +#include +#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 index 0000000..0076c3f --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_AML_VPU +#include +#endif +#include +#include +#include +#include +#include +#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", ¶[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", ¶[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", ¶[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", ¶[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", + ¶[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", + ¶[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", + ¶[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", + ¶[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", + ¶[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", + ¶[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", + ¶[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 index 0000000..53ae949 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.dts @@ -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 index 0000000..ad454e1 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.h @@ -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 + +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 index 0000000..99e923a --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_unifykey.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..758f460 --- /dev/null +++ b/drivers/amlogic/media/vout/lcd/lcd_vout.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_AMLOGIC_LCD_EXTERN +#include +#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."); diff --git a/drivers/amlogic/pinctrl/pinctrl_gxl.c b/drivers/amlogic/pinctrl/pinctrl_gxl.c index d4c3243..1fc91516 100644 --- a/drivers/amlogic/pinctrl/pinctrl_gxl.c +++ b/drivers/amlogic/pinctrl/pinctrl_gxl.c @@ -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 index 0000000..3f304b3 --- /dev/null +++ b/include/linux/amlogic/media/vout/lcd/aml_bl.h @@ -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 +#include +#include + +#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 index 0000000..81a4e3f --- /dev/null +++ b/include/linux/amlogic/media/vout/lcd/aml_bl_extern.h @@ -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 +#include + +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 index 0000000..29d4368 --- /dev/null +++ b/include/linux/amlogic/media/vout/lcd/aml_ldim.h @@ -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 +#include +#include +#include +#include +#include + +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 index 0000000..6c18502 --- /dev/null +++ b/include/linux/amlogic/media/vout/lcd/lcd_extern.h @@ -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 index 0000000..99f09ec --- /dev/null +++ b/include/linux/amlogic/media/vout/lcd/lcd_mipi.h @@ -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 index 0000000..1dd69a7 --- /dev/null +++ b/include/linux/amlogic/media/vout/lcd/lcd_notify.h @@ -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 + +#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 index 0000000..6413024 --- /dev/null +++ b/include/linux/amlogic/media/vout/lcd/lcd_unifykey.h @@ -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 index 0000000..4d5f52b --- /dev/null +++ b/include/linux/amlogic/media/vout/lcd/lcd_vout.h @@ -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 +#include +#include +#include +#include + +/* 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 diff --git a/include/linux/amlogic/media/vout/vinfo.h b/include/linux/amlogic/media/vout/vinfo.h index 9275929..202881f 100644 --- a/include/linux/amlogic/media/vout/vinfo.h +++ b/include/linux/amlogic/media/vout/vinfo.h @@ -19,6 +19,7 @@ #define _VINFO_H_ #include + /* 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,