AMLOGIC AXG ADD OSD DRIVER
M: Pengcheng Chen <pengcheng.chen@amlogic.com>
F: drivers/amlogic/media/osd/osd_io.c
+
+AMLOGIC LCD driver
+M: Weiming Liu <weiming.liu@amlogic.com>
+F: drivers/amlogic/media/vout/backlight/*
+F: drivers/amlogic/media/vout/lcd/*
+F: include/linux/amlogic/media/vout/lcd/*
+F: arch/arm64/boot/dts/amlogic/mesongxm_q200-panel.dtsi
+F: arch/arm64/boot/dts/amlogic/mesonaxg_s400-panel.dtsi
/dts-v1/;
#include "mesonaxg.dtsi"
-
+#include "mesonaxg_s400-panel.dtsi"
/ {
model = "Amlogic";
compatible = "amlogic, axg";
/* 0: 100.0M 1: 166.7M 2: 200.0M 3: 250.0M */
};
+ vout {
+ compatible = "amlogic, vout";
+ dev_name = "vout";
+ status = "okay";
+ };
+
/* Sound iomap */
aml_snd_iomap {
compatible = "amlogic, snd_iomap";
function = "pdm";
};
};
+
+ bl_pwm_on_pins: bl_pwm_on_pin {
+ mux {
+ groups = "GPIOZ_4";
+ function = "pwm_b";
+ };
+ };
}; /* end of pinctrl_periphs */
/* Audio Related End */
--- /dev/null
+/*
+ * 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 */
+
};
};
+ 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 {
--- /dev/null
+/*
+ * 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 */
+
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
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
#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
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));
#include <linux/amlogic/media/vout/vout_notify.h>
#include <linux/io.h>
-#ifdef CONFIG_AML_LCD
-#include <linux/amlogic/media/vout/lcd_notify.h>
+#ifdef CONFIG_AMLOGIC_LCD
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
#endif
#include "arch/vpp_regs.h"
/* 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)
{
}
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");
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;
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)
{
{
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 */
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,
/* 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,
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;
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)
} 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;
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)
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) &&
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
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/
+
--- /dev/null
+
+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
--- /dev/null
+obj-$(CONFIG_AMLOGIC_BACKLIGHT) += aml_bl.o
+obj-$(CONFIG_AMLOGIC_BL_EXTERN) += bl_extern/
\ No newline at end of file
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/backlight/aml_bl.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/backlight.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/pwm.h>
+#include <linux/amlogic/pwm_meson.h>
+#include <linux/amlogic/cpu_version.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/amlogic/media/vout/lcd/aml_bl.h>
+#ifdef CONFIG_AMLOGIC_LCD
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#endif
+#ifdef CONFIG_AMLOGIC_BL_EXTERN
+#include <linux/amlogic/media/vout/lcd/aml_bl_extern.h>
+#endif
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+#include <linux/amlogic/media/vout/lcd/aml_ldim.h>
+#endif
+
+#include "aml_bl_reg.h"
+
+/* #define AML_BACKLIGHT_DEBUG */
+static unsigned int bl_debug_print_flag;
+
+static enum bl_chip_type_e bl_chip_type = BL_CHIP_MAX;
+static struct aml_bl_drv_s *bl_drv;
+
+static unsigned int bl_key_valid;
+static unsigned char bl_config_load;
+
+/* bl_off_policy support */
+static int aml_bl_off_policy_cnt;
+
+static unsigned int bl_off_policy;
+module_param(bl_off_policy, uint, 0664);
+MODULE_PARM_DESC(bl_off_policy, "bl_off_policy");
+
+static unsigned int bl_level_uboot;
+static unsigned int brightness_bypass;
+module_param(brightness_bypass, uint, 0664);
+MODULE_PARM_DESC(brightness_bypass, "bl_brightness_bypass");
+
+static unsigned char bl_pwm_bypass; /* debug flag */
+static unsigned char bl_pwm_duty_free; /* debug flag */
+static unsigned char bl_on_request; /* for lcd power sequence */
+static unsigned int bl_on_level;
+
+static DEFINE_MUTEX(bl_power_mutex);
+static DEFINE_MUTEX(bl_level_mutex);
+
+static struct bl_config_s bl_config = {
+ .level_default = 128,
+ .level_mid = 128,
+ .level_mid_mapping = 128,
+ .level_min = 10,
+ .level_max = 255,
+ .power_on_delay = 100,
+ .power_off_delay = 30,
+ .method = BL_CTRL_MAX,
+
+ .bl_pwm = NULL,
+ .bl_pwm_combo0 = NULL,
+ .bl_pwm_combo1 = NULL,
+ .pwm_on_delay = 0,
+ .pwm_off_delay = 0,
+
+ .bl_gpio = {
+ {.flag = 0,},
+ {.flag = 0,},
+ {.flag = 0,},
+ {.flag = 0,},
+ {.flag = 0,},
+ },
+};
+
+const char *bl_chip_table[] = {
+ "GXTVBB",
+ "GXM",
+ "GXL",
+ "TXL",
+ "TXLX",
+ "AXG",
+ "invalid",
+};
+
+struct bl_method_match_s {
+ char *name;
+ enum bl_ctrl_method_e type;
+};
+
+static struct bl_method_match_s bl_method_match_table[] = {
+ {"gpio", BL_CTRL_GPIO},
+ {"pwm", BL_CTRL_PWM},
+ {"pwm_combo", BL_CTRL_PWM_COMBO},
+ {"local_diming", BL_CTRL_LOCAL_DIMING},
+ {"extern", BL_CTRL_EXTERN},
+ {"invalid", BL_CTRL_MAX},
+};
+
+static char *bl_method_type_to_str(int type)
+{
+ int i;
+ char *str = bl_method_match_table[BL_CTRL_MAX].name;
+
+ for (i = 0; i < BL_CTRL_MAX; i++) {
+ if (type == bl_method_match_table[i].type) {
+ str = bl_method_match_table[i].name;
+ break;
+ }
+ }
+ return str;
+}
+
+static unsigned int *pwm_reg;
+static unsigned int pwm_reg_gxtvbb[6] = {
+ PWM_PWM_A,
+ PWM_PWM_B,
+ PWM_PWM_C,
+ PWM_PWM_D,
+ PWM_PWM_E,
+ PWM_PWM_F,
+};
+
+static unsigned int pwm_reg_txlx[6] = {
+ PWM_PWM_A_TXLX,
+ PWM_PWM_B_TXLX,
+ PWM_PWM_C_TXLX,
+ PWM_PWM_D_TXLX,
+ PWM_PWM_E_TXLX,
+ PWM_PWM_F_TXLX,
+};
+
+enum bl_chip_type_e aml_bl_check_chip(void)
+{
+ unsigned int cpu_type;
+ enum bl_chip_type_e bl_chip = BL_CHIP_MAX;
+
+ cpu_type = get_cpu_type();
+ switch (cpu_type) {
+ case MESON_CPU_MAJOR_ID_GXTVBB:
+ bl_chip = BL_CHIP_GXTVBB;
+ pwm_reg = pwm_reg_gxtvbb;
+ break;
+ case MESON_CPU_MAJOR_ID_GXM:
+ bl_chip = BL_CHIP_GXM;
+ pwm_reg = pwm_reg_gxtvbb;
+ break;
+ case MESON_CPU_MAJOR_ID_GXL:
+ bl_chip = BL_CHIP_GXL;
+ pwm_reg = pwm_reg_gxtvbb;
+ break;
+ case MESON_CPU_MAJOR_ID_TXL:
+ bl_chip = BL_CHIP_TXL;
+ pwm_reg = pwm_reg_gxtvbb;
+ break;
+ case MESON_CPU_MAJOR_ID_TXLX:
+ bl_chip = BL_CHIP_TXLX;
+ pwm_reg = pwm_reg_txlx;
+ break;
+ case MESON_CPU_MAJOR_ID_AXG:
+ bl_chip = BL_CHIP_AXG;
+ pwm_reg = pwm_reg_txlx;
+ break;
+ default:
+ bl_chip = BL_CHIP_MAX;
+ pwm_reg = pwm_reg_gxtvbb;
+ }
+
+ if (bl_debug_print_flag)
+ BLPR("BL driver check chip : %s\n", bl_chip_table[bl_chip]);
+ return bl_chip;
+}
+
+static int aml_bl_check_driver(void)
+{
+ int ret = 0;
+
+ if (bl_drv == NULL) {
+ BLERR("no bl driver\n");
+ return -1;
+ }
+ switch (bl_drv->bconf->method) {
+ case BL_CTRL_PWM:
+ if (bl_drv->bconf->bl_pwm == NULL) {
+ ret = -1;
+ BLERR("no bl_pwm struct\n");
+ }
+ break;
+ case BL_CTRL_PWM_COMBO:
+ if (bl_drv->bconf->bl_pwm_combo0 == NULL) {
+ ret = -1;
+ BLERR("no bl_pwm_combo_0 struct\n");
+ }
+ if (bl_drv->bconf->bl_pwm_combo1 == NULL) {
+ ret = -1;
+ BLERR("no bl_pwm_combo_1 struct\n");
+ }
+ break;
+ case BL_CTRL_MAX:
+ ret = -1;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+struct aml_bl_drv_s *aml_bl_get_driver(void)
+{
+ if (bl_drv == NULL)
+ BLERR("no bl driver");
+
+ return bl_drv;
+}
+
+static void bl_gpio_register(int index)
+{
+ struct bl_gpio_s *bl_gpio;
+ const char *str;
+ int ret;
+
+ if (index >= BL_GPIO_NUM_MAX) {
+ BLERR("gpio index %d, exit\n", index);
+ return;
+ }
+ bl_gpio = &bl_drv->bconf->bl_gpio[index];
+ if (bl_gpio->flag) {
+ if (bl_debug_print_flag) {
+ BLPR("gpio %s[%d] is already registered\n",
+ bl_gpio->name, index);
+ }
+ return;
+ }
+
+ /* get gpio name */
+ ret = of_property_read_string_index(bl_drv->dev->of_node,
+ "bl_gpio_names", index, &str);
+ if (ret) {
+ BLERR("failed to get bl_gpio_names: %d\n", index);
+ str = "unknown";
+ }
+ strcpy(bl_gpio->name, str);
+
+ /* request gpio */
+ bl_gpio->gpio = devm_gpiod_get_index(
+ bl_drv->dev, "bl", index, GPIOD_OUT_HIGH);
+ if (IS_ERR(bl_gpio->gpio)) {
+ BLERR("register gpio %s[%d]: %p, err: %d\n",
+ bl_gpio->name, index, bl_gpio->gpio,
+ IS_ERR(bl_gpio->gpio));
+ } else {
+ bl_gpio->flag = 1;
+ if (bl_debug_print_flag) {
+ BLPR("register gpio %s[%d]: %p\n",
+ bl_gpio->name, index, bl_gpio->gpio);
+ }
+ }
+}
+
+static void bl_gpio_set(int index, int value)
+{
+ struct bl_gpio_s *bl_gpio;
+
+ if (index >= BL_GPIO_NUM_MAX) {
+ BLERR("gpio index %d, exit\n", index);
+ return;
+ }
+ bl_gpio = &bl_drv->bconf->bl_gpio[index];
+ if (bl_gpio->flag == 0) {
+ BLERR("gpio [%d] is not registered\n", index);
+ return;
+ }
+ if (IS_ERR(bl_gpio->gpio)) {
+ BLERR("gpio %s[%d]: %p, err: %ld\n",
+ bl_gpio->name, index, bl_gpio->gpio,
+ PTR_ERR(bl_gpio->gpio));
+ return;
+ }
+
+ switch (value) {
+ case BL_GPIO_OUTPUT_LOW:
+ case BL_GPIO_OUTPUT_HIGH:
+ gpiod_direction_output(bl_gpio->gpio, value);
+ break;
+ case BL_GPIO_INPUT:
+ default:
+ gpiod_direction_input(bl_gpio->gpio);
+ break;
+ }
+ if (bl_debug_print_flag) {
+ BLPR("set gpio %s[%d] value: %d\n",
+ bl_gpio->name, index, value);
+ }
+}
+
+/* ****************************************************** */
+static char *bl_pinmux_str[] = {
+ "pwm_on", /* 0 */
+ "pwm_vs_on", /* 1 */
+ "pwm_combo_0_1_on", /* 2 */
+ "pwm_combo_0_vs_1_on", /* 3 */
+ "pwm_combo_0_1_vs_on", /* 4 */
+};
+
+static void bl_pwm_pinmux_set(struct bl_config_s *bconf)
+{
+ int index = 0xff;
+
+ if (bl_debug_print_flag)
+ BLPR("%s\n", __func__);
+
+ switch (bconf->method) {
+ case BL_CTRL_PWM:
+ if (bconf->bl_pwm->pwm_port == BL_PWM_VS)
+ index = 1;
+ else
+ index = 0;
+ break;
+ case BL_CTRL_PWM_COMBO:
+ if (bconf->bl_pwm_combo0->pwm_port == BL_PWM_VS)
+ index = 3;
+ else {
+ if (bconf->bl_pwm_combo1->pwm_port == BL_PWM_VS)
+ index = 4;
+ else
+ index = 2;
+ }
+ break;
+ default:
+ BLERR("%s: wrong ctrl_mothod=%d\n", __func__, bconf->method);
+ break;
+ }
+
+ /* request pwm pinmux */
+ bconf->pin = devm_pinctrl_get_select(bl_drv->dev, bl_pinmux_str[index]);
+ if (IS_ERR(bconf->pin)) {
+ BLERR("set %s pinmux error\n", bl_pinmux_str[index]);
+ } else {
+ if (bl_debug_print_flag) {
+ BLPR("request %s pinmux: %p\n",
+ bl_pinmux_str[index], bconf->pin);
+ }
+ }
+}
+
+static void bl_pwm_request(struct bl_pwm_config_s *bl_pwm)
+{
+ unsigned int pwm_num = bl_pwm->pwm_port;
+
+ switch (bl_pwm->pwm_port) {
+ case BL_PWM_A:
+ case BL_PWM_B:
+ case BL_PWM_C:
+ case BL_PWM_D:
+ case BL_PWM_E:
+ case BL_PWM_F:
+ bl_pwm->bl_pwm_ch = pwm_request(pwm_num, NULL);
+ if (IS_ERR(bl_pwm->bl_pwm_ch)) {
+ BLERR("request pwm %d failed\n", bl_pwm->pwm_port);
+ return;
+ }
+
+ bl_pwm->bl_pwm_chip = to_aml_pwm_chip(bl_pwm->bl_pwm_ch->chip);
+ pwm_enable(bl_pwm->bl_pwm_ch);
+ break;
+ default:
+ break;
+ }
+}
+/* ****************************************************** */
+
+static int bl_pwm_out_level_check(struct bl_pwm_config_s *bl_pwm)
+{
+ int out_level = 0xff;
+
+ switch (bl_pwm->pwm_method) {
+ case BL_PWM_POSITIVE:
+ if (bl_pwm->pwm_duty == 0)
+ out_level = 0;
+ else if (bl_pwm->pwm_duty == 100)
+ out_level = 1;
+ else
+ out_level = 0xff;
+ break;
+ case BL_PWM_NEGATIVE:
+ if (bl_pwm->pwm_duty == 0)
+ out_level = 1;
+ else if (bl_pwm->pwm_duty == 100)
+ out_level = 0;
+ else
+ out_level = 0xff;
+ break;
+ default:
+ BLERR("%s: port %d: invalid pwm_method %d\n",
+ __func__, bl_pwm->pwm_port, bl_pwm->pwm_method);
+ break;
+ }
+
+ return out_level;
+}
+
+static void bl_set_pwm_vs(struct bl_pwm_config_s *bl_pwm, int out_level)
+{
+ unsigned int pwm_hi, n, sw;
+ unsigned int vs[4], ve[4];
+ unsigned int pol = 0;
+ int i;
+
+ if (bl_debug_print_flag) {
+ BLPR("%s: pwm_duty=%d, out_level=%d\n",
+ __func__, bl_pwm->pwm_duty, out_level);
+ }
+
+ if (out_level == 0) {
+ for (i = 0; i < 4; i++) {
+ vs[i] = 0x1fff;
+ ve[i] = 0;
+ }
+ } else if (out_level == 1) {
+ for (i = 0; i < 4; i++) {
+ vs[i] = 0;
+ ve[i] = 0x1fff;
+ }
+ } else {
+ if (bl_pwm->pwm_method == BL_PWM_NEGATIVE)
+ pol = 1;
+ pwm_hi = bl_pwm->pwm_level;
+ n = bl_pwm->pwm_freq;
+ sw = (bl_pwm->pwm_cnt * 10 / n + 5) / 10;
+ pwm_hi = (pwm_hi * 10 / n + 5) / 10;
+ pwm_hi = (pwm_hi > 1) ? pwm_hi : 1;
+ if (bl_debug_print_flag)
+ BLPR("n=%d, sw=%d, pwm_high=%d\n", n, sw, pwm_hi);
+ for (i = 0; i < n; i++) {
+ vs[i] = 1 + (sw * i);
+ ve[i] = vs[i] + pwm_hi - 1;
+ }
+ for (i = n; i < 4; i++) {
+ vs[i] = 0xffff;
+ ve[i] = 0xffff;
+ }
+ }
+ if (bl_debug_print_flag) {
+ for (i = 0; i < 4; i++) {
+ BLPR("vs[%d]=%d, ve[%d]=%d\n",
+ i, vs[i], i, ve[i]);
+ }
+ }
+
+ bl_vcbus_write(VPU_VPU_PWM_V0, (pol << 31) |
+ (2 << 14) | /* vsync latch */
+ (ve[0] << 16) | (vs[0]));
+ bl_vcbus_write(VPU_VPU_PWM_V1, (ve[1] << 16) | (vs[1]));
+ bl_vcbus_write(VPU_VPU_PWM_V2, (ve[2] << 16) | (vs[2]));
+ bl_vcbus_write(VPU_VPU_PWM_V3, (ve[3] << 16) | (vs[3]));
+
+ if (bl_debug_print_flag) {
+ BLPR("VPU_VPU_PWM_V0=0x%08x\n", bl_vcbus_read(VPU_VPU_PWM_V0));
+ BLPR("VPU_VPU_PWM_V1=0x%08x\n", bl_vcbus_read(VPU_VPU_PWM_V1));
+ BLPR("VPU_VPU_PWM_V2=0x%08x\n", bl_vcbus_read(VPU_VPU_PWM_V2));
+ BLPR("VPU_VPU_PWM_V3=0x%08x\n", bl_vcbus_read(VPU_VPU_PWM_V3));
+ }
+}
+
+void bl_pwm_ctrl(struct bl_pwm_config_s *bl_pwm, int status)
+{
+ int out_level;
+
+ if (status) {
+ /* enable pwm */
+ switch (bl_pwm->pwm_port) {
+ case BL_PWM_A:
+ case BL_PWM_B:
+ case BL_PWM_C:
+ case BL_PWM_D:
+ case BL_PWM_E:
+ case BL_PWM_F:
+ if (!IS_ERR(bl_pwm->bl_pwm_ch))
+ pwm_enable(bl_pwm->bl_pwm_ch);
+ else
+ BLERR("%s: invalid bl_pwm_ch\n", __func__);
+ break;
+ case BL_PWM_VS:
+ out_level = bl_pwm_out_level_check(bl_pwm);
+ bl_set_pwm_vs(bl_pwm, out_level);
+ break;
+ default:
+ break;
+ }
+ } else {
+ /* disable pwm */
+ switch (bl_pwm->pwm_port) {
+ case BL_PWM_A:
+ case BL_PWM_B:
+ case BL_PWM_C:
+ case BL_PWM_D:
+ case BL_PWM_E:
+ case BL_PWM_F:
+ if (!IS_ERR(bl_pwm->bl_pwm_ch))
+ pwm_disable(bl_pwm->bl_pwm_ch);
+ else
+ BLERR("%s: invalid bl_pwm_ch\n", __func__);
+ break;
+ case BL_PWM_VS:
+ bl_set_pwm_vs(bl_pwm, 0);
+ default:
+ break;
+ }
+ }
+}
+
+static void bl_power_en_ctrl(struct bl_config_s *bconf, int status)
+{
+ if (status) {
+ if (bconf->en_gpio < BL_GPIO_NUM_MAX)
+ bl_gpio_set(bconf->en_gpio, bconf->en_gpio_on);
+ } else {
+ if (bconf->en_gpio < BL_GPIO_NUM_MAX)
+ bl_gpio_set(bconf->en_gpio, bconf->en_gpio_off);
+ }
+}
+
+static void bl_power_on(void)
+{
+ struct bl_config_s *bconf = bl_drv->bconf;
+#ifdef CONFIG_AMLOGIC_BL_EXTERN
+ struct aml_bl_extern_driver_s *bl_ext;
+#endif
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+ struct aml_ldim_driver_s *ldim_drv;
+#endif
+ int ret;
+
+ if (aml_bl_check_driver())
+ return;
+
+ /* bl_off_policy */
+ if (bl_off_policy != BL_OFF_POLICY_NONE) {
+ BLPR("bl_off_policy=%d for bl_off\n", bl_off_policy);
+ return;
+ }
+
+ mutex_lock(&bl_power_mutex);
+
+ if (brightness_bypass == 0) {
+ if ((bl_drv->level == 0) ||
+ (bl_drv->state & BL_STATE_BL_ON)) {
+ goto exit_power_on_bl;
+ }
+ }
+
+ ret = 0;
+ switch (bconf->method) {
+ case BL_CTRL_GPIO:
+ bl_power_en_ctrl(bconf, 1);
+ break;
+ case BL_CTRL_PWM:
+ /* step 1: power on pwm */
+ bl_pwm_ctrl(bconf->bl_pwm, 1);
+ if (bconf->pwm_on_delay > 0)
+ mdelay(bconf->pwm_on_delay);
+ /* step 2: power on enable */
+ bl_power_en_ctrl(bconf, 1);
+ break;
+ case BL_CTRL_PWM_COMBO:
+ /* step 1: power on pwm_combo */
+ bl_pwm_ctrl(bconf->bl_pwm_combo0, 1);
+ bl_pwm_ctrl(bconf->bl_pwm_combo1, 1);
+ if (bconf->pwm_on_delay > 0)
+ mdelay(bconf->pwm_on_delay);
+ /* step 2: power on enable */
+ bl_power_en_ctrl(bconf, 1);
+ break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+ case BL_CTRL_LOCAL_DIMING:
+ /* step 1: power on enable */
+ bl_power_en_ctrl(bconf, 1);
+ /* step 2: power on ldim */
+ ldim_drv = aml_ldim_get_driver();
+ if (ldim_drv == NULL) {
+ BLERR("no ldim driver\n");
+ } else {
+ if (ldim_drv->power_on) {
+ ret = ldim_drv->power_on();
+ if (ret) {
+ BLERR("ldim: power on error\n");
+ goto exit_power_on_bl;
+ }
+ } else {
+ BLPR("ldim: power on is null\n");
+ }
+ }
+ break;
+#endif
+#ifdef CONFIG_AMLOGIC_BL_EXTERN
+ case BL_CTRL_EXTERN:
+ /* step 1: power on enable */
+ bl_power_en_ctrl(bconf, 1);
+ /* step 2: power on bl_extern */
+ bl_ext = aml_bl_extern_get_driver();
+ if (bl_ext == NULL) {
+ BLERR("no bl_extern driver\n");
+ } else {
+ if (bl_ext->power_on) {
+ ret = bl_ext->power_on();
+ if (ret) {
+ BLERR("bl_extern: power on error\n");
+ goto exit_power_on_bl;
+ }
+ } else {
+ BLERR("bl_extern: power on is null\n");
+ }
+ }
+ break;
+#endif
+ default:
+ BLPR("invalid backlight control method\n");
+ goto exit_power_on_bl;
+ }
+ bl_drv->state |= BL_STATE_BL_ON;
+ BLPR("backlight power on\n");
+
+exit_power_on_bl:
+ mutex_unlock(&bl_power_mutex);
+}
+
+static void bl_power_off(void)
+{
+ struct bl_config_s *bconf = bl_drv->bconf;
+#ifdef CONFIG_AMLOGIC_BL_EXTERN
+ struct aml_bl_extern_driver_s *bl_ext;
+#endif
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+ struct aml_ldim_driver_s *ldim_drv;
+#endif
+ int ret;
+
+ if (aml_bl_check_driver())
+ return;
+ mutex_lock(&bl_power_mutex);
+
+ if ((bl_drv->state & BL_STATE_BL_ON) == 0) {
+ mutex_unlock(&bl_power_mutex);
+ return;
+ }
+
+ ret = 0;
+ switch (bconf->method) {
+ case BL_CTRL_GPIO:
+ bl_power_en_ctrl(bconf, 0);
+ break;
+ case BL_CTRL_PWM:
+ /* step 1: power off enable */
+ bl_power_en_ctrl(bconf, 0);
+ /* step 2: power off pwm */
+ if (bconf->pwm_off_delay > 0)
+ mdelay(bconf->pwm_off_delay);
+ bl_pwm_ctrl(bconf->bl_pwm, 0);
+ break;
+ case BL_CTRL_PWM_COMBO:
+ /* step 1: power off enable */
+ bl_power_en_ctrl(bconf, 0);
+ /* step 2: power off pwm_combo */
+ if (bconf->pwm_off_delay > 0)
+ mdelay(bconf->pwm_off_delay);
+ bl_pwm_ctrl(bconf->bl_pwm_combo0, 0);
+ bl_pwm_ctrl(bconf->bl_pwm_combo1, 0);
+ break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+ case BL_CTRL_LOCAL_DIMING:
+ /* step 1: power off ldim */
+ ldim_drv = aml_ldim_get_driver();
+ if (ldim_drv == NULL) {
+ BLERR("no ldim driver\n");
+ } else {
+ if (ldim_drv->power_off) {
+ ret = ldim_drv->power_off();
+ if (ret)
+ BLERR("ldim: power off error\n");
+ } else {
+ BLERR("ldim: power off is null\n");
+ }
+ }
+ /* step 2: power off enable */
+ bl_power_en_ctrl(bconf, 0);
+ break;
+#endif
+#ifdef CONFIG_AMLOGIC_BL_EXTERN
+ case BL_CTRL_EXTERN:
+ /* step 1: power off bl_extern */
+ bl_ext = aml_bl_extern_get_driver();
+ if (bl_ext == NULL) {
+ BLERR("no bl_extern driver\n");
+ } else {
+ if (bl_ext->power_off) {
+ ret = bl_ext->power_off();
+ if (ret)
+ BLERR("bl_extern: power off error\n");
+ } else {
+ BLERR("bl_extern: power off is null\n");
+ }
+ }
+ /* step 2: power off enable */
+ bl_power_en_ctrl(bconf, 0);
+ break;
+#endif
+ default:
+ BLPR("invalid backlight control method\n");
+ break;
+ }
+ if (bconf->power_off_delay > 0)
+ mdelay(bconf->power_off_delay);
+
+ bl_drv->state &= ~BL_STATE_BL_ON;
+ BLPR("backlight power off\n");
+ mutex_unlock(&bl_power_mutex);
+}
+
+static unsigned int bl_level_mapping(unsigned int level)
+{
+ unsigned int mid = bl_drv->bconf->level_mid;
+ unsigned int mid_map = bl_drv->bconf->level_mid_mapping;
+ unsigned int max = bl_drv->bconf->level_max;
+ unsigned int min = bl_drv->bconf->level_min;
+
+ if (mid == mid_map)
+ return level;
+
+ level = level > max ? max : level;
+ if ((level >= mid) && (level <= max)) {
+ level = (((level - mid) * (max - mid_map)) / (max - mid)) +
+ mid_map;
+ } else if ((level >= min) && (level < mid)) {
+ level = (((level - min) * (mid_map - min)) / (mid - min)) + min;
+ } else {
+ level = 0;
+ }
+ return level;
+}
+
+static void bl_set_pwm(struct bl_pwm_config_s *bl_pwm)
+{
+ unsigned int port = bl_pwm->pwm_port;
+ unsigned int pol = 0;
+ unsigned int pwm_period, pwm_duty, out_level = 0xff;
+
+ out_level = bl_pwm_out_level_check(bl_pwm);
+ if (bl_pwm->pwm_method == BL_PWM_NEGATIVE)
+ pol = 1;
+ if (bl_debug_print_flag) {
+ BLPR("port %d: pwm_duty=%d, out_level=%d, pol=%s\n",
+ port, bl_pwm->pwm_duty, out_level,
+ (pol ? "negative":"positive"));
+ }
+
+ switch (port) {
+ case BL_PWM_A:
+ case BL_PWM_B:
+ case BL_PWM_C:
+ case BL_PWM_D:
+ case BL_PWM_E:
+ case BL_PWM_F:
+ pwm_period = 1000000000 / bl_pwm->pwm_freq;
+ pwm_duty = (pwm_period * bl_pwm->pwm_duty) / 100;
+ if (!IS_ERR(bl_pwm->bl_pwm_ch)) {
+ pwm_set_period(bl_pwm->bl_pwm_ch, pwm_period);
+ pwm_config(bl_pwm->bl_pwm_ch, pwm_duty, pwm_period);
+ pwm_set_polarity(bl_pwm->bl_pwm_ch, pol);
+ if (out_level == 0xff) {
+ pwm_constant_disable(bl_pwm->bl_pwm_chip,
+ bl_pwm->pwm_port);
+ } else {
+ /* pwm duty 100% or 0% special control */
+ pwm_constant_enable(bl_pwm->bl_pwm_chip,
+ bl_pwm->pwm_port);
+ }
+ } else {
+ BLERR("%s: invalid bl_pwm_ch\n", __func__);
+ }
+ break;
+ case BL_PWM_VS:
+ bl_set_pwm_vs(bl_pwm, out_level);
+ break;
+ default:
+ break;
+ }
+}
+
+static void bl_set_duty_pwm(struct bl_pwm_config_s *bl_pwm)
+{
+ if (bl_pwm_bypass)
+ return;
+
+ if (bl_pwm_duty_free) {
+ if (bl_pwm->pwm_duty > 100) {
+ BLERR("pwm_duty %d%% is bigger than 100%%\n",
+ bl_pwm->pwm_duty);
+ bl_pwm->pwm_duty = 100;
+ BLPR("reset to 100%%\n");
+ }
+ } else {
+ if (bl_pwm->pwm_duty > bl_pwm->pwm_duty_max) {
+ BLERR("pwm_duty %d%% is bigger than duty_max %d%%\n",
+ bl_pwm->pwm_duty, bl_pwm->pwm_duty_max);
+ bl_pwm->pwm_duty = bl_pwm->pwm_duty_max;
+ BLPR("reset to duty_max\n");
+ } else if (bl_pwm->pwm_duty < bl_pwm->pwm_duty_min) {
+ BLERR("pwm_duty %d%% is smaller than duty_min %d%%\n",
+ bl_pwm->pwm_duty, bl_pwm->pwm_duty_min);
+ bl_pwm->pwm_duty = bl_pwm->pwm_duty_min;
+ BLPR("reset to duty_min\n");
+ }
+ }
+
+ bl_pwm->pwm_level =
+ (((bl_pwm->pwm_cnt * bl_pwm->pwm_duty / 10) + 5) / 10);
+ if (bl_debug_print_flag) {
+ BLPR("pwm port %d: duty=%d%%, duty_max=%d, duty_min=%d\n",
+ bl_pwm->pwm_port, bl_pwm->pwm_duty,
+ bl_pwm->pwm_duty_max, bl_pwm->pwm_duty_min);
+ }
+ bl_set_pwm(bl_pwm);
+}
+
+static void bl_set_level_pwm(struct bl_pwm_config_s *bl_pwm, unsigned int level)
+{
+ unsigned int min = bl_pwm->level_min;
+ unsigned int max = bl_pwm->level_max;
+ unsigned int pwm_max = bl_pwm->pwm_max;
+ unsigned int pwm_min = bl_pwm->pwm_min;
+
+ if (bl_pwm_bypass)
+ return;
+
+ level = bl_level_mapping(level);
+ max = bl_level_mapping(max);
+ min = bl_level_mapping(min);
+ if ((max <= min) || (level < min)) {
+ bl_pwm->pwm_level = pwm_min;
+ } else {
+ bl_pwm->pwm_level = ((pwm_max - pwm_min) * (level - min) /
+ (max - min)) + pwm_min;
+ }
+
+ bl_pwm->pwm_duty =
+ (((bl_pwm->pwm_level * 1000 / bl_pwm->pwm_cnt) + 5) / 10);
+ if (bl_debug_print_flag) {
+ BLPR("port %d mapping: level=%d, level_max=%d, level_min=%d\n",
+ bl_pwm->pwm_port, level, max, min);
+ BLPR("port %d: pwm_max=%d, pwm_min=%d, pwm_level=%d\n",
+ bl_pwm->pwm_port, pwm_max, pwm_min, bl_pwm->pwm_level);
+ BLPR("port %d: duty=%d%%\n",
+ bl_pwm->pwm_port, bl_pwm->pwm_duty);
+ }
+
+ bl_set_pwm(bl_pwm);
+}
+
+#ifdef CONFIG_AMLOGIC_BL_EXTERN
+static void bl_set_level_extern(unsigned int level)
+{
+ struct aml_bl_extern_driver_s *bl_ext;
+ int ret;
+
+ bl_ext = aml_bl_extern_get_driver();
+ if (bl_ext == NULL) {
+ BLERR("no bl_extern driver\n");
+ } else {
+ if (bl_ext->set_level) {
+ ret = bl_ext->set_level(level);
+ if (ret)
+ BLERR("bl_extern: set_level error\n");
+ } else {
+ BLERR("bl_extern: set_level is null\n");
+ }
+ }
+}
+#endif
+
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+static void bl_set_level_ldim(unsigned int level)
+{
+ struct aml_ldim_driver_s *ldim_drv;
+ int ret = 0;
+
+ ldim_drv = aml_ldim_get_driver();
+ if (ldim_drv == NULL) {
+ BLERR("no ldim driver\n");
+ } else {
+ if (ldim_drv->set_level) {
+ ret = ldim_drv->set_level(level);
+ if (ret)
+ BLERR("ldim: set_level error\n");
+ } else {
+ BLERR("ldim: set_level is null\n");
+ }
+ }
+}
+#endif
+
+static void aml_bl_set_level(unsigned int level)
+{
+ unsigned int temp;
+ struct bl_pwm_config_s *pwm0, *pwm1;
+
+ if (aml_bl_check_driver())
+ return;
+
+ if (bl_debug_print_flag) {
+ BLPR("aml_bl_set_level=%u, last_level=%u, state=0x%x\n",
+ level, bl_drv->level, bl_drv->state);
+ }
+
+ /* level range check */
+ if (level > bl_drv->bconf->level_max)
+ level = bl_drv->bconf->level_max;
+ if (level < bl_drv->bconf->level_min) {
+ if (level < BL_LEVEL_OFF)
+ level = 0;
+ else
+ level = bl_drv->bconf->level_min;
+ }
+ bl_drv->level = level;
+
+ if (level == 0)
+ return;
+
+ switch (bl_drv->bconf->method) {
+ case BL_CTRL_GPIO:
+ break;
+ case BL_CTRL_PWM:
+ bl_set_level_pwm(bl_drv->bconf->bl_pwm, level);
+ break;
+ case BL_CTRL_PWM_COMBO:
+ pwm0 = bl_drv->bconf->bl_pwm_combo0;
+ pwm1 = bl_drv->bconf->bl_pwm_combo1;
+ if ((level >= pwm0->level_min) &&
+ (level <= pwm0->level_max)) {
+ temp = (pwm0->level_min > pwm1->level_min) ?
+ pwm1->level_max : pwm1->level_min;
+ if (bl_debug_print_flag) {
+ BLPR("pwm0 region, level=%u, pwm1_level=%u\n",
+ level, temp);
+ }
+ bl_set_level_pwm(pwm0, level);
+ bl_set_level_pwm(pwm1, temp);
+ } else if ((level >= pwm1->level_min) &&
+ (level <= pwm1->level_max)) {
+ temp = (pwm1->level_min > pwm0->level_min) ?
+ pwm0->level_max : pwm0->level_min;
+ if (bl_debug_print_flag) {
+ BLPR("pwm1 region, level=%u, pwm0_level=%u\n",
+ level, temp);
+ }
+ bl_set_level_pwm(pwm0, temp);
+ bl_set_level_pwm(pwm1, level);
+ }
+ break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+ case BL_CTRL_LOCAL_DIMING:
+ bl_set_level_ldim(level);
+ break;
+#endif
+#ifdef CONFIG_AML_BL_TABLET_EXTERN
+ case BL_CTRL_EXTERN:
+ bl_set_level_extern(level);
+ break;
+#endif
+ default:
+ if (bl_debug_print_flag)
+ BLPR("invalid backlight control method\n");
+ break;
+ }
+}
+
+static unsigned int aml_bl_get_level(void)
+{
+ if (aml_bl_check_driver())
+ return 0;
+
+ BLPR("aml bl state: 0x%x\n", bl_drv->state);
+ return bl_drv->level;
+}
+
+static int aml_bl_update_status(struct backlight_device *bd)
+{
+ int brightness = bd->props.brightness;
+
+ if (brightness_bypass)
+ return 0;
+
+ mutex_lock(&bl_level_mutex);
+ if (brightness < 0)
+ brightness = 0;
+ else if (brightness > 255)
+ brightness = 255;
+
+ if (((bl_drv->state & BL_STATE_LCD_ON) == 0) ||
+ ((bl_drv->state & BL_STATE_BL_POWER_ON) == 0))
+ brightness = 0;
+
+ if (bl_debug_print_flag) {
+ BLPR("%s: %u, real brightness: %u, state: 0x%x\n",
+ __func__, bd->props.brightness,
+ brightness, bl_drv->state);
+ }
+ if (brightness == 0) {
+ if (bl_drv->state & BL_STATE_BL_ON)
+ bl_power_off();
+ } else {
+ aml_bl_set_level(brightness);
+ if ((bl_drv->state & BL_STATE_BL_ON) == 0)
+ bl_power_on();
+ }
+ mutex_unlock(&bl_level_mutex);
+ return 0;
+}
+
+static int aml_bl_get_brightness(struct backlight_device *bd)
+{
+ return aml_bl_get_level();
+}
+
+static const struct backlight_ops aml_bl_ops = {
+ .get_brightness = aml_bl_get_brightness,
+ .update_status = aml_bl_update_status,
+};
+
+#ifdef CONFIG_OF
+static char *bl_pwm_name[] = {
+ "PWM_A",
+ "PWM_B",
+ "PWM_C",
+ "PWM_D",
+ "PWM_E",
+ "PWM_F",
+ "PWM_VS",
+};
+
+enum bl_pwm_port_e bl_pwm_str_to_pwm(const char *str)
+{
+ enum bl_pwm_port_e pwm_port = BL_PWM_MAX;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bl_pwm_name); i++) {
+ if (strcmp(str, bl_pwm_name[i]) == 0) {
+ pwm_port = i;
+ break;
+ }
+ }
+
+ return pwm_port;
+}
+
+void bl_pwm_config_init(struct bl_pwm_config_s *bl_pwm)
+{
+ unsigned int freq, cnt, pre_div;
+ int i;
+
+ if (bl_debug_print_flag) {
+ BLPR("%s pwm_port %d: freq = %u\n",
+ __func__, bl_pwm->pwm_port, bl_pwm->pwm_freq);
+ }
+ freq = bl_pwm->pwm_freq;
+ switch (bl_pwm->pwm_port) {
+ case BL_PWM_VS:
+ cnt = bl_vcbus_read(ENCL_VIDEO_MAX_LNCNT) + 1;
+ bl_pwm->pwm_cnt = cnt;
+ bl_pwm->pwm_pre_div = 0;
+ if (bl_debug_print_flag)
+ BLPR("pwm_cnt = %u\n", bl_pwm->pwm_cnt);
+ break;
+ default:
+ for (i = 0; i < 0x7f; i++) {
+ pre_div = i;
+ cnt = XTAL_FREQ_HZ / (freq * (pre_div + 1)) - 2;
+ if (cnt <= 0xffff)
+ break;
+ }
+ bl_pwm->pwm_cnt = cnt;
+ bl_pwm->pwm_pre_div = pre_div;
+ if (bl_debug_print_flag)
+ BLPR("pwm_cnt = %u, pwm_pre_div = %u\n", cnt, pre_div);
+ break;
+ }
+ bl_pwm->pwm_max = (bl_pwm->pwm_cnt * bl_pwm->pwm_duty_max / 100);
+ bl_pwm->pwm_min = (bl_pwm->pwm_cnt * bl_pwm->pwm_duty_min / 100);
+
+ if (bl_debug_print_flag) {
+ BLPR("pwm_max = %u, pwm_min = %u\n",
+ bl_pwm->pwm_max, bl_pwm->pwm_min);
+ }
+}
+
+static void aml_bl_config_print(struct bl_config_s *bconf)
+{
+ struct bl_pwm_config_s *bl_pwm;
+
+ if (bconf->method == BL_CTRL_MAX) {
+ BLPR("no backlight exist\n");
+ return;
+ }
+
+ BLPR("name = %s\n", bconf->name);
+ BLPR("method = %s(%d)\n",
+ bl_method_type_to_str(bconf->method), bconf->method);
+
+ if (bl_debug_print_flag == 0)
+ return;
+
+ BLPR("level_default = %d\n", bconf->level_default);
+ BLPR("level_min = %d\n", bconf->level_min);
+ BLPR("level_max = %d\n", bconf->level_max);
+ BLPR("level_mid = %d\n", bconf->level_mid);
+ BLPR("level_mid_mapping = %d\n", bconf->level_mid_mapping);
+
+ BLPR("en_gpio = %d\n", bconf->en_gpio);
+ BLPR("en_gpio_on = %d\n", bconf->en_gpio_on);
+ BLPR("en_gpio_off = %d\n", bconf->en_gpio_off);
+ BLPR("power_on_delay = %dms\n", bconf->power_on_delay);
+ BLPR("power_off_delay = %dms\n\n", bconf->power_off_delay);
+
+ switch (bconf->method) {
+ case BL_CTRL_PWM:
+ BLPR("pwm_on_delay = %dms\n", bconf->pwm_on_delay);
+ BLPR("pwm_off_delay = %dms\n", bconf->pwm_off_delay);
+ if (bconf->bl_pwm) {
+ bl_pwm = bconf->bl_pwm;
+ BLPR("pwm_index = %d\n", bl_pwm->index);
+ BLPR("pwm_method = %d\n", bl_pwm->pwm_method);
+ BLPR("pwm_port = %d\n", bl_pwm->pwm_port);
+ if (bl_pwm->pwm_port == BL_PWM_VS) {
+ BLPR("pwm_freq = %d x vfreq\n",
+ bl_pwm->pwm_freq);
+ BLPR("pwm_cnt = %u\n", bl_pwm->pwm_cnt);
+ } else {
+ BLPR("pwm_freq = %uHz\n",
+ bl_pwm->pwm_freq);
+ BLPR("pwm_cnt = %u\n", bl_pwm->pwm_cnt);
+ BLPR("pwm_pre_div = %u\n",
+ bl_pwm->pwm_pre_div);
+ }
+ BLPR("pwm_level_max = %u\n", bl_pwm->level_max);
+ BLPR("pwm_level_min = %u\n", bl_pwm->level_min);
+ BLPR("pwm_duty_max = %d%%\n", bl_pwm->pwm_duty_max);
+ BLPR("pwm_duty_min = %d%%\n", bl_pwm->pwm_duty_min);
+ BLPR("pwm_max = %u\n", bl_pwm->pwm_max);
+ BLPR("pwm_min = %u\n", bl_pwm->pwm_min);
+ }
+ break;
+ case BL_CTRL_PWM_COMBO:
+ BLPR("pwm_on_delay = %dms\n", bconf->pwm_on_delay);
+ BLPR("pwm_off_delay = %dms\n", bconf->pwm_off_delay);
+ /* pwm_combo_0 */
+ if (bconf->bl_pwm_combo0) {
+ bl_pwm = bconf->bl_pwm_combo0;
+ BLPR("pwm_combo0_index = %d\n", bl_pwm->index);
+ BLPR("pwm_combo0_method = %d\n", bl_pwm->pwm_method);
+ BLPR("pwm_combo0_port = %d\n", bl_pwm->pwm_port);
+ if (bl_pwm->pwm_port == BL_PWM_VS) {
+ BLPR("pwm_combo0_freq = %d x vfreq\n",
+ bl_pwm->pwm_freq);
+ BLPR("pwm_combo0_cnt = %u\n",
+ bl_pwm->pwm_cnt);
+ } else {
+ BLPR("pwm_combo0_freq = %uHz\n",
+ bl_pwm->pwm_freq);
+ BLPR("pwm_combo0_cnt = %u\n",
+ bl_pwm->pwm_cnt);
+ BLPR("pwm_combo0_pre_div = %u\n",
+ bl_pwm->pwm_pre_div);
+ }
+ BLPR("pwm_combo0_level_max = %u\n", bl_pwm->level_max);
+ BLPR("pwm_combo0_level_min = %u\n", bl_pwm->level_min);
+ BLPR("pwm_combo0_duty_max = %d\n",
+ bl_pwm->pwm_duty_max);
+ BLPR("pwm_combo0_duty_min = %d\n",
+ bl_pwm->pwm_duty_min);
+ BLPR("pwm_combo0_max = %u\n", bl_pwm->pwm_max);
+ BLPR("pwm_combo0_min = %u\n", bl_pwm->pwm_min);
+ }
+ /* pwm_combo_1 */
+ if (bconf->bl_pwm_combo1) {
+ bl_pwm = bconf->bl_pwm_combo1;
+ BLPR("pwm_combo1_index = %d\n", bl_pwm->index);
+ BLPR("pwm_combo1_method = %d\n", bl_pwm->pwm_method);
+ BLPR("pwm_combo1_port = %d\n", bl_pwm->pwm_port);
+ if (bl_pwm->pwm_port == BL_PWM_VS) {
+ BLPR("pwm_combo1_freq = %d x vfreq\n",
+ bl_pwm->pwm_freq);
+ BLPR("pwm_combo1_cnt = %u\n",
+ bl_pwm->pwm_cnt);
+ } else {
+ BLPR("pwm_combo1_freq = %uHz\n",
+ bl_pwm->pwm_freq);
+ BLPR("pwm_combo1_cnt = %u\n",
+ bl_pwm->pwm_cnt);
+ BLPR("pwm_combo1_pre_div = %u\n",
+ bl_pwm->pwm_pre_div);
+ }
+ BLPR("pwm_combo1_level_max = %u\n", bl_pwm->level_max);
+ BLPR("pwm_combo1_level_min = %u\n", bl_pwm->level_min);
+ BLPR("pwm_combo1_duty_max = %d\n",
+ bl_pwm->pwm_duty_max);
+ BLPR("pwm_combo1_duty_min = %d\n",
+ bl_pwm->pwm_duty_min);
+ BLPR("pwm_combo1_max = %u\n", bl_pwm->pwm_max);
+ BLPR("pwm_combo1_min = %u\n", bl_pwm->pwm_min);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int aml_bl_config_load_from_dts(struct bl_config_s *bconf,
+ struct platform_device *pdev)
+{
+ int ret = 0;
+ int val;
+ const char *str;
+ unsigned int bl_para[10];
+ char bl_propname[20];
+ int index = BL_INDEX_DEFAULT;
+ struct device_node *child;
+ struct bl_pwm_config_s *bl_pwm;
+ struct bl_pwm_config_s *pwm_combo0, *pwm_combo1;
+
+ /* select backlight by index */
+#ifdef CONFIG_AMLOGIC_LCD
+ aml_lcd_notifier_call_chain(LCD_EVENT_BACKLIGHT_SEL, &index);
+#endif
+ bl_drv->index = index;
+ if (bl_drv->index == 0xff) {
+ bconf->method = BL_CTRL_MAX;
+ return -1;
+ }
+ sprintf(bl_propname, "backlight_%d", index);
+ BLPR("load: %s\n", bl_propname);
+ child = of_get_child_by_name(pdev->dev.of_node, bl_propname);
+ if (child == NULL) {
+ BLERR("failed to get %s\n", bl_propname);
+ return -1;
+ }
+
+ ret = of_property_read_string(child, "bl_name", &str);
+ if (ret) {
+ BLERR("failed to get bl_name\n");
+ str = "backlight";
+ }
+ strcpy(bconf->name, str);
+
+ ret = of_property_read_u32_array(child, "bl_level_default_uboot_kernel",
+ &bl_para[0], 2);
+ if (ret) {
+ BLERR("failed to get bl_level_default_uboot_kernel\n");
+ bl_level_uboot = BL_LEVEL_DEFAULT;
+ bconf->level_default = BL_LEVEL_DEFAULT;
+ } else {
+ bl_level_uboot = bl_para[0];
+ bconf->level_default = bl_para[1];
+ }
+ ret = of_property_read_u32_array(child, "bl_level_attr",
+ &bl_para[0], 4);
+ if (ret) {
+ BLERR("failed to get bl_level_attr\n");
+ bconf->level_min = BL_LEVEL_MIN;
+ bconf->level_max = BL_LEVEL_MAX;
+ bconf->level_mid = BL_LEVEL_MID;
+ bconf->level_mid_mapping = BL_LEVEL_MID_MAPPED;
+ } else {
+ bconf->level_max = bl_para[0];
+ bconf->level_min = bl_para[1];
+ bconf->level_mid = bl_para[2];
+ bconf->level_mid_mapping = bl_para[3];
+ }
+ /* adjust brightness_bypass by level_default */
+ if (bconf->level_default > bconf->level_max) {
+ brightness_bypass = 1;
+ BLPR("level_default > level_max, enable brightness_bypass\n");
+ }
+
+ ret = of_property_read_u32(child, "bl_ctrl_method", &val);
+ if (ret) {
+ BLERR("failed to get bl_ctrl_method\n");
+ bconf->method = BL_CTRL_MAX;
+ } else {
+ bconf->method = (val >= BL_CTRL_MAX) ? BL_CTRL_MAX : val;
+ }
+ ret = of_property_read_u32_array(child, "bl_power_attr",
+ &bl_para[0], 5);
+ if (ret) {
+ BLERR("failed to get bl_power_attr\n");
+ bconf->en_gpio = BL_GPIO_MAX;
+ bconf->en_gpio_on = BL_GPIO_OUTPUT_HIGH;
+ bconf->en_gpio_off = BL_GPIO_OUTPUT_LOW;
+ bconf->power_on_delay = 100;
+ bconf->power_off_delay = 30;
+ } else {
+ if (bl_para[0] >= BL_GPIO_NUM_MAX) {
+ bconf->en_gpio = BL_GPIO_MAX;
+ } else {
+ bconf->en_gpio = bl_para[0];
+ bl_gpio_register(bconf->en_gpio);
+ }
+ bconf->en_gpio_on = bl_para[1];
+ bconf->en_gpio_off = bl_para[2];
+ bconf->power_on_delay = bl_para[3];
+ bconf->power_off_delay = bl_para[4];
+ }
+
+ switch (bconf->method) {
+ case BL_CTRL_PWM:
+ bconf->bl_pwm = kzalloc(sizeof(struct bl_pwm_config_s),
+ GFP_KERNEL);
+ if (bconf->bl_pwm == NULL) {
+ BLERR("bl_pwm struct malloc error\n");
+ return -1;
+ }
+ bl_pwm = bconf->bl_pwm;
+ bl_pwm->index = 0;
+
+ bl_pwm->level_max = bconf->level_max;
+ bl_pwm->level_min = bconf->level_min;
+
+ ret = of_property_read_string(child, "bl_pwm_port", &str);
+ if (ret) {
+ BLERR("failed to get bl_pwm_port\n");
+ bl_pwm->pwm_port = BL_PWM_MAX;
+ } else {
+ bl_pwm->pwm_port = bl_pwm_str_to_pwm(str);
+ BLPR("bl pwm_port: %s(%u)\n", str, bl_pwm->pwm_port);
+ }
+ ret = of_property_read_u32_array(child, "bl_pwm_attr",
+ &bl_para[0], 4);
+ if (ret) {
+ BLERR("failed to get bl_pwm_attr\n");
+ bl_pwm->pwm_method = BL_PWM_POSITIVE;
+ if (bl_pwm->pwm_port == BL_PWM_VS)
+ bl_pwm->pwm_freq = BL_FREQ_VS_DEFAULT;
+ else
+ bl_pwm->pwm_freq = BL_FREQ_DEFAULT;
+ bl_pwm->pwm_duty_max = 80;
+ bl_pwm->pwm_duty_min = 20;
+ } else {
+ bl_pwm->pwm_method = bl_para[0];
+ bl_pwm->pwm_freq = bl_para[1];
+ bl_pwm->pwm_duty_max = bl_para[2];
+ bl_pwm->pwm_duty_min = bl_para[3];
+ }
+ if (bl_pwm->pwm_port == BL_PWM_VS) {
+ if (bl_pwm->pwm_freq > 4) {
+ BLERR("bl_pwm_vs wrong freq %d\n",
+ bl_pwm->pwm_freq);
+ bl_pwm->pwm_freq = BL_FREQ_VS_DEFAULT;
+ }
+ } else {
+ if (bl_pwm->pwm_freq > XTAL_HALF_FREQ_HZ)
+ bl_pwm->pwm_freq = XTAL_HALF_FREQ_HZ;
+ if (bl_pwm->pwm_freq < 50)
+ bl_pwm->pwm_freq = 50;
+ }
+ ret = of_property_read_u32_array(child, "bl_pwm_power",
+ &bl_para[0], 4);
+ if (ret) {
+ BLERR("failed to get bl_pwm_power\n");
+ bconf->pwm_on_delay = 0;
+ bconf->pwm_off_delay = 0;
+ } else {
+ bconf->pwm_on_delay = bl_para[2];
+ bconf->pwm_off_delay = bl_para[3];
+ }
+
+ bl_pwm->pwm_duty = bl_pwm->pwm_duty_min;
+ /* init pwm config */
+ bl_pwm_config_init(bl_pwm);
+ break;
+ case BL_CTRL_PWM_COMBO:
+ bconf->bl_pwm_combo0 = kzalloc(sizeof(struct bl_pwm_config_s),
+ GFP_KERNEL);
+ if (bconf->bl_pwm_combo0 == NULL) {
+ BLERR("bl_pwm_combo0 struct malloc error\n");
+ return -1;
+ }
+ bconf->bl_pwm_combo1 = kzalloc(sizeof(struct bl_pwm_config_s),
+ GFP_KERNEL);
+ if (bconf->bl_pwm_combo1 == NULL) {
+ BLERR("bl_pwm_combo1 struct malloc error\n");
+ return -1;
+ }
+ pwm_combo0 = bconf->bl_pwm_combo0;
+ pwm_combo1 = bconf->bl_pwm_combo1;
+
+ pwm_combo0->index = 0;
+ pwm_combo1->index = 1;
+
+ ret = of_property_read_string_index(child, "bl_pwm_combo_port",
+ 0, &str);
+ if (ret) {
+ BLERR("failed to get bl_pwm_combo_port\n");
+ pwm_combo0->pwm_port = BL_PWM_MAX;
+ } else {
+ pwm_combo0->pwm_port = bl_pwm_str_to_pwm(str);
+ }
+ ret = of_property_read_string_index(child, "bl_pwm_combo_port",
+ 1, &str);
+ if (ret) {
+ BLERR("failed to get bl_pwm_combo_port\n");
+ pwm_combo1->pwm_port = BL_PWM_MAX;
+ } else {
+ pwm_combo1->pwm_port = bl_pwm_str_to_pwm(str);
+ }
+ BLPR("pwm_combo_port: %s(%u), %s(%u)\n",
+ bl_pwm_name[pwm_combo0->pwm_port], pwm_combo0->pwm_port,
+ bl_pwm_name[pwm_combo1->pwm_port],
+ pwm_combo1->pwm_port);
+ ret = of_property_read_u32_array(child,
+ "bl_pwm_combo_level_mapping", &bl_para[0], 4);
+ if (ret) {
+ BLERR("failed to get bl_pwm_combo_level_mapping\n");
+ pwm_combo0->level_max = BL_LEVEL_MAX;
+ pwm_combo0->level_min = BL_LEVEL_MID;
+ pwm_combo1->level_max = BL_LEVEL_MID;
+ pwm_combo1->level_min = BL_LEVEL_MIN;
+ } else {
+ pwm_combo0->level_max = bl_para[0];
+ pwm_combo0->level_min = bl_para[1];
+ pwm_combo1->level_max = bl_para[2];
+ pwm_combo1->level_min = bl_para[3];
+ }
+ ret = of_property_read_u32_array(child, "bl_pwm_combo_attr",
+ &bl_para[0], 8);
+ if (ret) {
+ BLERR("failed to get bl_pwm_combo_attr\n");
+ pwm_combo0->pwm_method = BL_PWM_POSITIVE;
+ if (pwm_combo0->pwm_port == BL_PWM_VS)
+ pwm_combo0->pwm_freq = BL_FREQ_VS_DEFAULT;
+ else
+ pwm_combo0->pwm_freq = BL_FREQ_DEFAULT;
+ pwm_combo0->pwm_duty_max = 80;
+ pwm_combo0->pwm_duty_min = 20;
+ pwm_combo1->pwm_method = BL_PWM_NEGATIVE;
+ if (pwm_combo1->pwm_port == BL_PWM_VS)
+ pwm_combo1->pwm_freq = BL_FREQ_VS_DEFAULT;
+ else
+ pwm_combo1->pwm_freq = BL_FREQ_DEFAULT;
+ pwm_combo1->pwm_duty_max = 80;
+ pwm_combo1->pwm_duty_min = 20;
+ } else {
+ pwm_combo0->pwm_method = bl_para[0];
+ pwm_combo0->pwm_freq = bl_para[1];
+ pwm_combo0->pwm_duty_max = bl_para[2];
+ pwm_combo0->pwm_duty_min = bl_para[3];
+ pwm_combo1->pwm_method = bl_para[4];
+ pwm_combo1->pwm_freq = bl_para[5];
+ pwm_combo1->pwm_duty_max = bl_para[6];
+ pwm_combo1->pwm_duty_min = bl_para[7];
+ }
+ if (pwm_combo0->pwm_port == BL_PWM_VS) {
+ if (pwm_combo0->pwm_freq > 4) {
+ BLERR("bl_pwm_0_vs wrong freq %d\n",
+ pwm_combo0->pwm_freq);
+ pwm_combo0->pwm_freq = BL_FREQ_VS_DEFAULT;
+ }
+ } else {
+ if (pwm_combo0->pwm_freq > XTAL_HALF_FREQ_HZ)
+ pwm_combo0->pwm_freq = XTAL_HALF_FREQ_HZ;
+ if (pwm_combo0->pwm_freq < 50)
+ pwm_combo0->pwm_freq = 50;
+ }
+ if (pwm_combo1->pwm_port == BL_PWM_VS) {
+ if (pwm_combo1->pwm_freq > 4) {
+ BLERR("bl_pwm_1_vs wrong freq %d\n",
+ pwm_combo1->pwm_freq);
+ pwm_combo1->pwm_freq = BL_FREQ_VS_DEFAULT;
+ }
+ } else {
+ if (pwm_combo1->pwm_freq > XTAL_HALF_FREQ_HZ)
+ pwm_combo1->pwm_freq = XTAL_HALF_FREQ_HZ;
+ if (pwm_combo1->pwm_freq < 50)
+ pwm_combo1->pwm_freq = 50;
+ }
+ ret = of_property_read_u32_array(child, "bl_pwm_combo_power",
+ &bl_para[0], 6);
+ if (ret) {
+ BLERR("failed to get bl_pwm_combo_power\n");
+ bconf->pwm_on_delay = 0;
+ bconf->pwm_off_delay = 0;
+ } else {
+ bconf->pwm_on_delay = bl_para[4];
+ bconf->pwm_off_delay = bl_para[5];
+ }
+
+ pwm_combo0->pwm_duty = pwm_combo0->pwm_duty_min;
+ pwm_combo1->pwm_duty = pwm_combo1->pwm_duty_min;
+ /* init pwm config */
+ bl_pwm_config_init(pwm_combo0);
+ bl_pwm_config_init(pwm_combo1);
+ break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+ case BL_CTRL_LOCAL_DIMING:
+ break;
+#endif
+ case BL_CTRL_EXTERN:
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+#endif
+
+static int aml_bl_config_load_from_unifykey(struct bl_config_s *bconf)
+{
+ unsigned char *para;
+ int key_len, len;
+ unsigned char *p;
+ const char *str;
+ unsigned char temp;
+ struct aml_lcd_unifykey_header_s bl_header;
+ struct bl_pwm_config_s *bl_pwm;
+ struct bl_pwm_config_s *pwm_combo0, *pwm_combo1;
+ int ret;
+
+ para = kmalloc((sizeof(unsigned char) * LCD_UKEY_BL_SIZE), GFP_KERNEL);
+ if (!para) {
+ BLERR("%s: Not enough memory\n", __func__);
+ return -1;
+ }
+
+ key_len = LCD_UKEY_BL_SIZE;
+ memset(para, 0, (sizeof(unsigned char) * key_len));
+ ret = lcd_unifykey_get("backlight", para, &key_len);
+ if (ret < 0) {
+ kfree(para);
+ return -1;
+ }
+
+ /* check backlight unifykey length */
+ len = 10 + 30 + 12 + 8 + 32;
+ ret = lcd_unifykey_len_check(key_len, len);
+ if (ret < 0) {
+ BLERR("unifykey length is not correct\n");
+ kfree(para);
+ return -1;
+ }
+
+ /* header: 10byte */
+ lcd_unifykey_header_check(para, &bl_header);
+ if (bl_debug_print_flag) {
+ BLPR("unifykey header:\n");
+ BLPR("crc32 = 0x%08x\n", bl_header.crc32);
+ BLPR("data_len = %d\n", bl_header.data_len);
+ BLPR("version = 0x%04x\n", bl_header.version);
+ BLPR("reserved = 0x%04x\n", bl_header.reserved);
+ }
+
+ /* basic: 30byte */
+ p = para + LCD_UKEY_HEAD_SIZE;
+ *(p + LCD_UKEY_BL_NAME - 1) = '\0'; /* ensure string ending */
+ str = (const char *)p;
+ strcpy(bconf->name, str);
+ p += LCD_UKEY_BL_NAME;
+
+ /* level: 6byte */
+ bl_level_uboot = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_LEVEL_UBOOT;
+ bconf->level_default = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_LEVEL_KERNEL; /* dummy pointer */
+ bconf->level_max = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_LEVEL_MAX;
+ bconf->level_min = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_LEVEL_MIN;
+ bconf->level_mid = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_LEVEL_MID;
+ bconf->level_mid_mapping = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_LEVEL_MID_MAP;
+
+ /* adjust brightness_bypass by level_default */
+ if (bconf->level_default > bconf->level_max) {
+ brightness_bypass = 1;
+ BLPR("level_default > level_max, enable brightness_bypass\n");
+ }
+
+ /* method: 8byte */
+ temp = *p;
+ bconf->method = (temp >= BL_CTRL_MAX) ? BL_CTRL_MAX : temp;
+ p += LCD_UKEY_BL_METHOD;
+
+ temp = *p;
+ if (temp >= BL_GPIO_NUM_MAX) {
+ bconf->en_gpio = BL_GPIO_MAX;
+ } else {
+ bconf->en_gpio = temp;
+ bl_gpio_register(bconf->en_gpio);
+ }
+ p += LCD_UKEY_BL_EN_GPIO;
+ bconf->en_gpio_on = *p;
+ p += LCD_UKEY_BL_EN_GPIO_ON;
+ bconf->en_gpio_off = *p;
+ p += LCD_UKEY_BL_EN_GPIO_OFF;
+ bconf->power_on_delay = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_ON_DELAY;
+ bconf->power_off_delay = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_OFF_DELAY;
+
+ /* pwm: 24byte */
+ switch (bconf->method) {
+ case BL_CTRL_PWM:
+ bconf->bl_pwm = kzalloc(sizeof(struct bl_pwm_config_s),
+ GFP_KERNEL);
+ if (bconf->bl_pwm == NULL) {
+ BLERR("bl_pwm struct malloc error\n");
+ kfree(para);
+ return -1;
+ }
+ bl_pwm = bconf->bl_pwm;
+ bl_pwm->index = 0;
+
+ bl_pwm->level_max = bconf->level_max;
+ bl_pwm->level_min = bconf->level_min;
+
+ bconf->pwm_on_delay = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_PWM_ON_DELAY;
+ bconf->pwm_off_delay = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_PWM_OFF_DELAY;
+ bl_pwm->pwm_method = *p;
+ p += LCD_UKEY_BL_PWM_METHOD;
+ bl_pwm->pwm_port = *p;
+ p += LCD_UKEY_BL_PWM_PORT;
+ bl_pwm->pwm_freq = (*p | ((*(p + 1)) << 8) |
+ ((*(p + 2)) << 8) | ((*(p + 3)) << 8));
+ if (bl_pwm->pwm_port == BL_PWM_VS) {
+ if (bl_pwm->pwm_freq > 4) {
+ BLERR("bl_pwm_vs wrong freq %d\n",
+ bl_pwm->pwm_freq);
+ bl_pwm->pwm_freq = BL_FREQ_VS_DEFAULT;
+ }
+ } else {
+ if (bl_pwm->pwm_freq > XTAL_HALF_FREQ_HZ)
+ bl_pwm->pwm_freq = XTAL_HALF_FREQ_HZ;
+ if (bl_pwm->pwm_freq < 50)
+ bl_pwm->pwm_freq = 50;
+ }
+ p += LCD_UKEY_BL_PWM_FREQ;
+ bl_pwm->pwm_duty_max = *p;
+ p += LCD_UKEY_BL_PWM_DUTY_MAX;
+ bl_pwm->pwm_duty_min = *p;
+ p += LCD_UKEY_BL_PWM_DUTY_MIN;
+
+ p += LCD_UKEY_BL_PWM_GPIO;
+ p += LCD_UKEY_BL_PWM_GPIO_OFF;
+
+ bl_pwm->pwm_duty = bl_pwm->pwm_duty_min;
+ bl_pwm_config_init(bl_pwm);
+ break;
+ case BL_CTRL_PWM_COMBO:
+ bconf->bl_pwm_combo0 = kzalloc(sizeof(struct bl_pwm_config_s),
+ GFP_KERNEL);
+ if (bconf->bl_pwm_combo0 == NULL) {
+ BLERR("bl_pwm_combo0 struct malloc error\n");
+ kfree(para);
+ return -1;
+ }
+ bconf->bl_pwm_combo1 = kzalloc(sizeof(struct bl_pwm_config_s),
+ GFP_KERNEL);
+ if (bconf->bl_pwm_combo1 == NULL) {
+ BLERR("bl_pwm_combo1 struct malloc error\n");
+ kfree(para);
+ return -1;
+ }
+ pwm_combo0 = bconf->bl_pwm_combo0;
+ pwm_combo1 = bconf->bl_pwm_combo1;
+ pwm_combo0->index = 0;
+ pwm_combo1->index = 1;
+
+ bconf->pwm_on_delay = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_PWM_ON_DELAY;
+ bconf->pwm_off_delay = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_PWM_OFF_DELAY;
+
+ pwm_combo0->pwm_method = *p;
+ p += LCD_UKEY_BL_PWM_METHOD;
+ pwm_combo0->pwm_port = *p;
+ p += LCD_UKEY_BL_PWM_PORT;
+ pwm_combo0->pwm_freq = (*p | ((*(p + 1)) << 8) |
+ ((*(p + 2)) << 8) | ((*(p + 3)) << 8));
+ if (pwm_combo0->pwm_port == BL_PWM_VS) {
+ if (pwm_combo0->pwm_freq > 4) {
+ BLERR("bl_pwm_0_vs wrong freq %d\n",
+ pwm_combo0->pwm_freq);
+ pwm_combo0->pwm_freq = BL_FREQ_VS_DEFAULT;
+ }
+ } else {
+ if (pwm_combo0->pwm_freq > XTAL_HALF_FREQ_HZ)
+ pwm_combo0->pwm_freq = XTAL_HALF_FREQ_HZ;
+ if (pwm_combo0->pwm_freq < 50)
+ pwm_combo0->pwm_freq = 50;
+ }
+ p += LCD_UKEY_BL_PWM_FREQ;
+ pwm_combo0->pwm_duty_max = *p;
+ p += LCD_UKEY_BL_PWM_DUTY_MAX;
+ pwm_combo0->pwm_duty_min = *p;
+ p += LCD_UKEY_BL_PWM_DUTY_MIN;
+
+ p += LCD_UKEY_BL_PWM_GPIO;
+ p += LCD_UKEY_BL_PWM_GPIO_OFF;
+
+ pwm_combo1->pwm_method = *p;
+ p += LCD_UKEY_BL_PWM2_METHOD;
+ pwm_combo1->pwm_port = *p;
+ p += LCD_UKEY_BL_PWM2_PORT;
+ pwm_combo1->pwm_freq = (*p | ((*(p + 1)) << 8) |
+ ((*(p + 2)) << 8) | ((*(p + 3)) << 8));
+ if (pwm_combo1->pwm_port == BL_PWM_VS) {
+ if (pwm_combo1->pwm_freq > 4) {
+ BLERR("bl_pwm_1_vs wrong freq %d\n",
+ pwm_combo1->pwm_freq);
+ pwm_combo1->pwm_freq = BL_FREQ_VS_DEFAULT;
+ }
+ } else {
+ if (pwm_combo1->pwm_freq > XTAL_HALF_FREQ_HZ)
+ pwm_combo1->pwm_freq = XTAL_HALF_FREQ_HZ;
+ if (pwm_combo1->pwm_freq < 50)
+ pwm_combo1->pwm_freq = 50;
+ }
+ p += LCD_UKEY_BL_PWM2_FREQ;
+ pwm_combo1->pwm_duty_max = *p;
+ p += LCD_UKEY_BL_PWM2_DUTY_MAX;
+ pwm_combo1->pwm_duty_min = *p;
+ p += LCD_UKEY_BL_PWM2_DUTY_MIN;
+
+ p += LCD_UKEY_BL_PWM2_GPIO;
+ p += LCD_UKEY_BL_PWM2_GPIO_OFF;
+
+ pwm_combo0->level_max = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_PWM_LEVEL_MAX;
+ pwm_combo0->level_min = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_PWM_LEVEL_MIN;
+ pwm_combo1->level_max = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_PWM2_LEVEL_MAX;
+ pwm_combo1->level_min = (*p | ((*(p + 1)) << 8));
+ p += LCD_UKEY_BL_PWM2_LEVEL_MIN;
+
+ pwm_combo0->pwm_duty = pwm_combo0->pwm_duty_min;
+ pwm_combo1->pwm_duty = pwm_combo1->pwm_duty_min;
+ bl_pwm_config_init(pwm_combo0);
+ bl_pwm_config_init(pwm_combo1);
+ break;
+ default:
+ break;
+ }
+
+ kfree(para);
+ return 0;
+}
+
+static int aml_bl_config_load(struct bl_config_s *bconf,
+ struct platform_device *pdev)
+{
+ int load_id = 0;
+ int ret = 0;
+
+ if (pdev->dev.of_node == NULL) {
+ BLERR("no backlight of_node exist\n");
+ return -1;
+ }
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "key_valid", &bl_key_valid);
+ if (ret) {
+ if (bl_debug_print_flag)
+ BLPR("failed to get key_valid\n");
+ bl_key_valid = 0;
+ }
+ BLPR("key_valid: %d\n", bl_key_valid);
+
+ if (bl_key_valid) {
+ ret = lcd_unifykey_check("backlight");
+ if (ret < 0)
+ load_id = 0;
+ else
+ load_id = 1;
+ }
+ if (load_id) {
+ BLPR("%s from unifykey\n", __func__);
+ bl_config_load = 1;
+ ret = aml_bl_config_load_from_unifykey(bconf);
+ } else {
+#ifdef CONFIG_OF
+ BLPR("%s from dts\n", __func__);
+ bl_config_load = 0;
+ ret = aml_bl_config_load_from_dts(bconf, pdev);
+#endif
+ }
+
+ aml_bl_config_print(bconf);
+
+ switch (bconf->method) {
+ case BL_CTRL_PWM:
+ bl_pwm_request(bconf->bl_pwm);
+ bl_pwm_pinmux_set(bconf);
+ break;
+ case BL_CTRL_PWM_COMBO:
+ bl_pwm_request(bconf->bl_pwm_combo0);
+ bl_pwm_request(bconf->bl_pwm_combo1);
+ bl_pwm_pinmux_set(bconf);
+ break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+ case BL_CTRL_LOCAL_DIMING:
+ aml_ldim_probe(pdev);
+ break;
+#endif
+ default:
+ break;
+ }
+ return ret;
+}
+
+/* lcd notify */
+static void aml_bl_on_function(void)
+{
+ /* lcd power on backlight flag */
+ bl_drv->state |= (BL_STATE_LCD_ON | BL_STATE_BL_POWER_ON);
+ BLPR("%s: bl_level=%u, state=0x%x\n",
+ __func__, bl_drv->level, bl_drv->state);
+ if (brightness_bypass) {
+ if ((bl_drv->state & BL_STATE_BL_ON) == 0)
+ bl_power_on();
+ } else
+ aml_bl_update_status(bl_drv->bldev);
+}
+
+static void aml_bl_delayd_on(struct work_struct *work)
+{
+ if (aml_bl_check_driver())
+ return;
+
+ if (bl_on_request == 0)
+ return;
+
+ aml_bl_on_function();
+}
+
+static int aml_bl_on_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct bl_config_s *bconf;
+
+ if ((event & LCD_EVENT_BL_ON) == 0)
+ return NOTIFY_DONE;
+ if (bl_debug_print_flag)
+ BLPR("%s: 0x%lx\n", __func__, event);
+
+ if (aml_bl_check_driver())
+ return NOTIFY_DONE;
+
+ bconf = bl_drv->bconf;
+ bl_on_request = 1;
+ /* lcd power on sequence control */
+ if (bconf->method < BL_CTRL_MAX) {
+ if (bl_drv->workqueue) {
+ queue_delayed_work(bl_drv->workqueue,
+ &bl_drv->bl_delayed_work,
+ msecs_to_jiffies(bconf->power_on_delay));
+ } else {
+ BLPR("Warning: no bl workqueue\n");
+ if (bconf->power_on_delay)
+ msleep(bconf->power_on_delay);
+ aml_bl_on_function();
+ }
+ } else {
+ BLERR("wrong backlight control method\n");
+ }
+
+ return NOTIFY_OK;
+}
+
+static int aml_bl_off_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ if ((event & LCD_EVENT_BL_OFF) == 0)
+ return NOTIFY_DONE;
+ if (bl_debug_print_flag)
+ BLPR("%s: 0x%lx\n", __func__, event);
+
+ if (aml_bl_check_driver())
+ return NOTIFY_DONE;
+
+ bl_on_request = 0;
+ bl_drv->state &= ~(BL_STATE_LCD_ON | BL_STATE_BL_POWER_ON);
+ if (brightness_bypass) {
+ if (bl_drv->state & BL_STATE_BL_ON)
+ bl_power_off();
+ } else {
+ aml_bl_update_status(bl_drv->bldev);
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block aml_bl_on_nb = {
+ .notifier_call = aml_bl_on_notifier,
+ .priority = LCD_PRIORITY_POWER_BL_ON,
+};
+
+static struct notifier_block aml_bl_off_nb = {
+ .notifier_call = aml_bl_off_notifier,
+ .priority = LCD_PRIORITY_POWER_BL_OFF,
+};
+
+static int aml_bl_lcd_update_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct bl_pwm_config_s *bl_pwm = NULL;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+ struct aml_ldim_driver_s *ldim_drv = aml_ldim_get_driver();
+#endif
+
+ /* If we aren't interested in this event, skip it immediately */
+ if (event != LCD_EVENT_BACKLIGHT_UPDATE)
+ return NOTIFY_DONE;
+
+ if (aml_bl_check_driver())
+ return NOTIFY_DONE;
+ if (bl_debug_print_flag)
+ BLPR("bl_lcd_update_notifier for pwm_vs\n");
+ switch (bl_drv->bconf->method) {
+ case BL_CTRL_PWM:
+ if (bl_drv->bconf->bl_pwm->pwm_port == BL_PWM_VS) {
+ bl_pwm = bl_drv->bconf->bl_pwm;
+ if (bl_pwm) {
+ bl_pwm_config_init(bl_pwm);
+ if (brightness_bypass)
+ bl_set_duty_pwm(bl_pwm);
+ else
+ aml_bl_update_status(bl_drv->bldev);
+ }
+ }
+ break;
+ case BL_CTRL_PWM_COMBO:
+ if (bl_drv->bconf->bl_pwm_combo0->pwm_port == BL_PWM_VS)
+ bl_pwm = bl_drv->bconf->bl_pwm_combo0;
+ else if (bl_drv->bconf->bl_pwm_combo1->pwm_port == BL_PWM_VS)
+ bl_pwm = bl_drv->bconf->bl_pwm_combo1;
+ if (bl_pwm) {
+ bl_pwm_config_init(bl_pwm);
+ if (brightness_bypass)
+ bl_set_duty_pwm(bl_pwm);
+ else
+ aml_bl_update_status(bl_drv->bldev);
+ }
+ break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+ case BL_CTRL_LOCAL_DIMING:
+ if (ldim_drv->pwm_vs_update)
+ ldim_drv->pwm_vs_update();
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block aml_bl_lcd_update_nb = {
+ .notifier_call = aml_bl_lcd_update_notifier,
+};
+
+static int aml_bl_lcd_test_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ int *flag;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+ struct aml_ldim_driver_s *ldim_drv = aml_ldim_get_driver();
+#endif
+
+ /* If we aren't interested in this event, skip it immediately */
+ if (event != LCD_EVENT_TEST_PATTERN)
+ return NOTIFY_DONE;
+
+ if (aml_bl_check_driver())
+ return NOTIFY_DONE;
+ if (bl_debug_print_flag)
+ BLPR("bl_lcd_test_notifier for lcd test_pattern\n");
+
+ flag = (int *)data;
+ switch (bl_drv->bconf->method) {
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+ case BL_CTRL_LOCAL_DIMING:
+ if (ldim_drv->test_ctrl)
+ ldim_drv->test_ctrl(*flag);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block aml_bl_lcd_test_nb = {
+ .notifier_call = aml_bl_lcd_test_notifier,
+};
+
+/* bl debug calss */
+struct class *bl_debug_class;
+
+static const char *bl_debug_usage_str = {
+"Usage:\n"
+" cat status ; dump backlight config\n"
+"\n"
+" echo freq <index> <pwm_freq> > pwm ; set pwm frequency(unit in Hz for pwm, vfreq multiple for pwm_vs)\n"
+" echo duty <index> <pwm_duty> > pwm ; set pwm duty cycle(unit: %)\n"
+" echo pol <index> <pwm_pol> > pwm ; set pwm polarity(unit: %)\n"
+" echo max <index> <duty_max> > pwm ; set pwm duty_max(unit: %)\n"
+" echo min <index> <duty_min> > pwm ; set pwm duty_min(unit: %)\n"
+" cat pwm ; dump pwm state\n"
+" echo free <0|1> > pwm ; set bl_pwm_duty_free enable or disable\n"
+"\n"
+" echo <0|1> > power ; backlight power ctrl\n"
+" cat power ; print backlight power state\n"
+"\n"
+" echo <0|1> > print ; 0=disable debug print; 1=enable debug print\n"
+" cat print ; read current debug print flag\n"
+};
+
+static ssize_t bl_debug_help(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", bl_debug_usage_str);
+}
+
+static ssize_t bl_status_read(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ struct bl_config_s *bconf = bl_drv->bconf;
+ struct bl_pwm_config_s *bl_pwm;
+ struct bl_pwm_config_s *pwm_combo0, *pwm_combo1;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+ struct aml_ldim_driver_s *ldim_drv = aml_ldim_get_driver();
+#endif
+ ssize_t len = 0;
+
+ len = sprintf(buf, "read backlight status:\n"
+ "key_valid: %d\n"
+ "config_load: %d\n"
+ "index: %d\n"
+ "name: %s\n"
+ "state: 0x%x\n"
+ "level: %d\n"
+ "level_uboot: %d\n"
+ "brightness_bypass: %d\n\n"
+ "level_max: %d\n"
+ "level_min: %d\n"
+ "level_mid: %d\n"
+ "level_mid_mapping: %d\n\n"
+ "method: %s\n"
+ "en_gpio: %s(%d)\n"
+ "en_gpio_on: %d\n"
+ "en_gpio_off: %d\n"
+ "power_on_delay: %d\n"
+ "power_off_delay: %d\n\n",
+ bl_key_valid, bl_config_load,
+ bl_drv->index, bconf->name, bl_drv->state,
+ bl_drv->level, bl_level_uboot, brightness_bypass,
+ bconf->level_max, bconf->level_min,
+ bconf->level_mid, bconf->level_mid_mapping,
+ bl_method_type_to_str(bconf->method),
+ bconf->bl_gpio[bconf->en_gpio].name,
+ bconf->en_gpio, bconf->en_gpio_on, bconf->en_gpio_off,
+ bconf->power_on_delay, bconf->power_off_delay);
+ switch (bconf->method) {
+ case BL_CTRL_GPIO:
+ len += sprintf(buf+len, "to do\n");
+ break;
+ case BL_CTRL_PWM:
+ bl_pwm = bconf->bl_pwm;
+ len += sprintf(buf+len,
+ "pwm_method: %d\n"
+ "pwm_port: %d\n"
+ "pwm_freq: %d\n"
+ "pwm_duty_max: %d\n"
+ "pwm_duty_min: %d\n"
+ "pwm_on_delay: %d\n"
+ "pwm_off_delay: %d\n\n",
+ bl_pwm->pwm_method, bl_pwm->pwm_port, bl_pwm->pwm_freq,
+ bl_pwm->pwm_duty_max, bl_pwm->pwm_duty_min,
+ bconf->pwm_on_delay, bconf->pwm_off_delay);
+ break;
+ case BL_CTRL_PWM_COMBO:
+ pwm_combo0 = bconf->bl_pwm_combo0;
+ pwm_combo1 = bconf->bl_pwm_combo1;
+ len += sprintf(buf+len,
+ "pwm_0_level_max: %d\n"
+ "pwm_0_level_min: %d\n"
+ "pwm_0_method: %d\n"
+ "pwm_0_port: %d\n"
+ "pwm_0_freq: %d\n"
+ "pwm_0_duty_max: %d\n"
+ "pwm_0_duty_min: %d\n"
+ "pwm_1_level_max: %d\n"
+ "pwm_1_level_min: %d\n"
+ "pwm_1_method: %d\n"
+ "pwm_1_port: %d\n"
+ "pwm_1_freq: %d\n"
+ "pwm_1_duty_max: %d\n"
+ "pwm_1_duty_min: %d\n"
+ "pwm_on_delay: %d\n"
+ "pwm_off_delay: %d\n\n",
+ pwm_combo0->level_max, pwm_combo0->level_min,
+ pwm_combo0->pwm_method, pwm_combo0->pwm_port,
+ pwm_combo0->pwm_freq,
+ pwm_combo0->pwm_duty_max, pwm_combo0->pwm_duty_min,
+ pwm_combo1->level_max, pwm_combo1->level_min,
+ pwm_combo1->pwm_method, pwm_combo1->pwm_port,
+ pwm_combo1->pwm_freq,
+ pwm_combo1->pwm_duty_max, pwm_combo1->pwm_duty_min,
+ bconf->pwm_on_delay, bconf->pwm_off_delay);
+ break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+ case BL_CTRL_LOCAL_DIMING:
+ if (ldim_drv->config_print)
+ ldim_drv->config_print();
+ break;
+#endif
+ case BL_CTRL_EXTERN:
+ break;
+ default:
+ len += sprintf(buf+len, "wrong backlight control method\n");
+ break;
+ }
+ return len;
+}
+
+static ssize_t bl_debug_pwm_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ struct bl_config_s *bconf = bl_drv->bconf;
+ struct bl_pwm_config_s *bl_pwm;
+ unsigned int value;
+ ssize_t len = 0;
+
+ len = sprintf(buf, "read backlight pwm state:\n");
+ switch (bconf->method) {
+ case BL_CTRL_PWM:
+ len += sprintf(buf+len,
+ "bl_pwm_bypass: %d\n"
+ "bl_pwm_duty_free: %d\n",
+ bl_pwm_bypass, bl_pwm_duty_free);
+ if (bconf->bl_pwm) {
+ bl_pwm = bconf->bl_pwm;
+ len += sprintf(buf+len,
+ "pwm_index: %d\n"
+ "pwm_method: %d\n"
+ "pwm_port: %d\n"
+ "pwm_freq: %d\n"
+ "pwm_duty_max: %d\n"
+ "pwm_duty_min: %d\n"
+ "pwm_cnt: %d\n"
+ "pwm_duty: %d%%\n",
+ bl_pwm->index, bl_pwm->pwm_method,
+ bl_pwm->pwm_port, bl_pwm->pwm_freq,
+ bl_pwm->pwm_duty_max, bl_pwm->pwm_duty_min,
+ bl_pwm->pwm_cnt, bl_pwm->pwm_duty);
+ switch (bl_pwm->pwm_port) {
+ case BL_PWM_A:
+ case BL_PWM_B:
+ case BL_PWM_C:
+ case BL_PWM_D:
+ case BL_PWM_E:
+ case BL_PWM_F:
+ value = bl_cbus_read(pwm_reg[bl_pwm->pwm_port]);
+ len += sprintf(buf+len,
+ "pwm_reg: 0x%08x\n",
+ value);
+ break;
+ case BL_PWM_VS:
+ len += sprintf(buf+len,
+ "pwm_reg0: 0x%08x\n"
+ "pwm_reg1: 0x%08x\n"
+ "pwm_reg2: 0x%08x\n"
+ "pwm_reg3: 0x%08x\n",
+ bl_vcbus_read(VPU_VPU_PWM_V0),
+ bl_vcbus_read(VPU_VPU_PWM_V1),
+ bl_vcbus_read(VPU_VPU_PWM_V2),
+ bl_vcbus_read(VPU_VPU_PWM_V3));
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case BL_CTRL_PWM_COMBO:
+ len += sprintf(buf+len,
+ "bl_pwm_bypass: %d\n"
+ "bl_pwm_duty_free: %d\n",
+ bl_pwm_bypass, bl_pwm_duty_free);
+ if (bconf->bl_pwm_combo0) {
+ bl_pwm = bconf->bl_pwm_combo0;
+ len += sprintf(buf+len,
+ "pwm_0_index: %d\n"
+ "pwm_0_method: %d\n"
+ "pwm_0_port: %d\n"
+ "pwm_0_freq: %d\n"
+ "pwm_0_duty_max: %d\n"
+ "pwm_0_duty_min: %d\n"
+ "pwm_0_cnt: %d\n"
+ "pwm_0_duty: %d%%\n",
+ bl_pwm->index, bl_pwm->pwm_method,
+ bl_pwm->pwm_port, bl_pwm->pwm_freq,
+ bl_pwm->pwm_duty_max, bl_pwm->pwm_duty_min,
+ bl_pwm->pwm_cnt, bl_pwm->pwm_duty);
+ switch (bl_pwm->pwm_port) {
+ case BL_PWM_A:
+ case BL_PWM_B:
+ case BL_PWM_C:
+ case BL_PWM_D:
+ case BL_PWM_E:
+ case BL_PWM_F:
+ value = bl_cbus_read(pwm_reg[bl_pwm->pwm_port]);
+ len += sprintf(buf+len,
+ "pwm_0_reg: 0x%08x\n",
+ value);
+ break;
+ case BL_PWM_VS:
+ len += sprintf(buf+len,
+ "pwm_0_reg0: 0x%08x\n"
+ "pwm_0_reg1: 0x%08x\n"
+ "pwm_0_reg2: 0x%08x\n"
+ "pwm_0_reg3: 0x%08x\n",
+ bl_vcbus_read(VPU_VPU_PWM_V0),
+ bl_vcbus_read(VPU_VPU_PWM_V1),
+ bl_vcbus_read(VPU_VPU_PWM_V2),
+ bl_vcbus_read(VPU_VPU_PWM_V3));
+ break;
+ default:
+ break;
+ }
+ }
+ if (bconf->bl_pwm_combo1) {
+ bl_pwm = bconf->bl_pwm_combo1;
+ len += sprintf(buf+len,
+ "pwm_1_index: %d\n"
+ "pwm_1_method: %d\n"
+ "pwm_1_port: %d\n"
+ "pwm_1_freq: %d\n"
+ "pwm_1_duty_max: %d\n"
+ "pwm_1_duty_min: %d\n"
+ "pwm_1_cnt: %d\n"
+ "pwm_1_duty: %d%%\n",
+ bl_pwm->index, bl_pwm->pwm_method,
+ bl_pwm->pwm_port, bl_pwm->pwm_freq,
+ bl_pwm->pwm_duty_max, bl_pwm->pwm_duty_min,
+ bl_pwm->pwm_cnt, bl_pwm->pwm_duty);
+ switch (bl_pwm->pwm_port) {
+ case BL_PWM_A:
+ case BL_PWM_B:
+ case BL_PWM_C:
+ case BL_PWM_D:
+ case BL_PWM_E:
+ case BL_PWM_F:
+ value = bl_cbus_read(pwm_reg[bl_pwm->pwm_port]);
+ len += sprintf(buf+len,
+ "pwm_1_reg: 0x%08x\n",
+ value);
+ break;
+ case BL_PWM_VS:
+ len += sprintf(buf+len,
+ "pwm_1_reg0: 0x%08x\n"
+ "pwm_1_reg1: 0x%08x\n"
+ "pwm_1_reg2: 0x%08x\n"
+ "pwm_1_reg3: 0x%08x\n",
+ bl_vcbus_read(VPU_VPU_PWM_V0),
+ bl_vcbus_read(VPU_VPU_PWM_V1),
+ bl_vcbus_read(VPU_VPU_PWM_V2),
+ bl_vcbus_read(VPU_VPU_PWM_V3));
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ len += sprintf(buf+len, "not pwm control method\n");
+ break;
+ }
+ return len;
+}
+
+#define BL_DEBUG_PWM_FREQ 0
+#define BL_DEBUG_PWM_DUTY 1
+#define BL_DEBUG_PWM_POL 2
+#define BL_DEBUG_PWM_DUTY_MAX 3
+#define BL_DEBUG_PWM_DUTY_MIN 4
+static void bl_debug_pwm_set(unsigned int index, unsigned int value, int state)
+{
+ struct bl_config_s *bconf = bl_drv->bconf;
+ struct bl_pwm_config_s *bl_pwm = NULL;
+
+ if (aml_bl_check_driver())
+ return;
+
+ switch (bconf->method) {
+ case BL_CTRL_PWM:
+ bl_pwm = bconf->bl_pwm;
+ break;
+ case BL_CTRL_PWM_COMBO:
+ if (index == 0)
+ bl_pwm = bconf->bl_pwm_combo0;
+ else
+ bl_pwm = bconf->bl_pwm_combo1;
+ break;
+ default:
+ BLERR("not pwm control method\n");
+ break;
+ }
+ if (bl_pwm) {
+ switch (state) {
+ case BL_DEBUG_PWM_FREQ:
+ bl_pwm->pwm_freq = value;
+ if (bl_pwm->pwm_freq < 50)
+ bl_pwm->pwm_freq = 50;
+ bl_pwm_config_init(bl_pwm);
+ bl_set_duty_pwm(bl_pwm);
+ if (bl_debug_print_flag) {
+ BLPR("set index(%d) pwm_port(%d) freq: %dHz\n",
+ index, bl_pwm->pwm_port, bl_pwm->pwm_freq);
+ }
+ break;
+ case BL_DEBUG_PWM_DUTY:
+ bl_pwm->pwm_duty = value;
+ bl_set_duty_pwm(bl_pwm);
+ if (bl_debug_print_flag) {
+ BLPR("set index(%d) pwm_port(%d) duty: %d%%\n",
+ index, bl_pwm->pwm_port, bl_pwm->pwm_duty);
+ }
+ break;
+ case BL_DEBUG_PWM_POL:
+ bl_pwm->pwm_method = value;
+ bl_pwm_config_init(bl_pwm);
+ bl_set_duty_pwm(bl_pwm);
+ if (bl_debug_print_flag) {
+ BLPR("set index(%d) pwm_port(%d) method: %d\n",
+ index, bl_pwm->pwm_port, bl_pwm->pwm_method);
+ }
+ break;
+ case BL_DEBUG_PWM_DUTY_MAX:
+ bl_pwm->pwm_duty_max = value;
+ bl_set_duty_pwm(bl_pwm);
+ if (bl_debug_print_flag) {
+ BLPR("set index(%d) pwm_port(%d) duty: %d%%\n",
+ index, bl_pwm->pwm_port, bl_pwm->pwm_duty_max);
+ }
+ break;
+ case BL_DEBUG_PWM_DUTY_MIN:
+ bl_pwm->pwm_duty_min = value;
+ bl_set_duty_pwm(bl_pwm);
+ if (bl_debug_print_flag) {
+ BLPR("set index(%d) pwm_port(%d) duty: %d%%\n",
+ index, bl_pwm->pwm_port, bl_pwm->pwm_duty_min);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static ssize_t bl_debug_pwm_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int ret;
+ unsigned int index = 0, val = 0;
+
+ switch (buf[0]) {
+ case 'f':
+ if (buf[3] == 'q') { /* frequency */
+ ret = sscanf(buf, "freq %d %d", &index, &val);
+ bl_debug_pwm_set(index, val, BL_DEBUG_PWM_FREQ);
+ } else if (buf[3] == 'e') { /* duty free */
+ ret = sscanf(buf, "free %d", &val);
+ bl_pwm_duty_free = (unsigned char)val;
+ BLPR("set bl_pwm_duty_free: %d\n", bl_pwm_duty_free);
+ }
+ break;
+ case 'd': /* duty */
+ ret = sscanf(buf, "duty %d %d", &index, &val);
+ bl_debug_pwm_set(index, val, BL_DEBUG_PWM_DUTY);
+ break;
+ case 'p': /* polarity */
+ ret = sscanf(buf, "pol %d %d", &index, &val);
+ bl_debug_pwm_set(index, val, BL_DEBUG_PWM_POL);
+ break;
+ case 'b': /* bypass */
+ ret = sscanf(buf, "bypass %d", &val);
+ bl_pwm_bypass = (unsigned char)val;
+ BLPR("set bl_pwm_bypass: %d\n", bl_pwm_bypass);
+ break;
+ case 'm':
+ if (buf[1] == 'a') { /* max */
+ ret = sscanf(buf, "max %d %d", &index, &val);
+ bl_debug_pwm_set(index, val, BL_DEBUG_PWM_DUTY_MAX);
+ } else if (buf[1] == 'i') { /* min */
+ ret = sscanf(buf, "min %d %d", &index, &val);
+ bl_debug_pwm_set(index, val, BL_DEBUG_PWM_DUTY_MIN);
+ }
+ break;
+ default:
+ BLERR("wrong command\n");
+ break;
+ }
+
+ return count;
+}
+
+static ssize_t bl_debug_power_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ int state;
+
+ if ((bl_drv->state & BL_STATE_LCD_ON) == 0) {
+ state = 0;
+ } else {
+ if (bl_drv->state & BL_STATE_BL_POWER_ON)
+ state = 1;
+ else
+ state = 0;
+ }
+ return sprintf(buf, "backlight power state: %d\n", state);
+}
+
+static ssize_t bl_debug_power_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int ret;
+ unsigned int temp = 0;
+
+ ret = kstrtouint(buf, 10, &temp);
+ if (ret != 0)
+ return -EINVAL;
+
+ BLPR("power control: %u\n", temp);
+ if ((bl_drv->state & BL_STATE_LCD_ON) == 0) {
+ temp = 0;
+ BLPR("backlight force off for lcd is off\n");
+ }
+ if (temp == 0) {
+ bl_drv->state &= ~BL_STATE_BL_POWER_ON;
+ if (bl_drv->state & BL_STATE_BL_ON)
+ bl_power_off();
+ } else {
+ bl_drv->state |= BL_STATE_BL_POWER_ON;
+ if ((bl_drv->state & BL_STATE_BL_ON) == 0)
+ bl_power_on();
+ }
+
+ return count;
+}
+
+static ssize_t bl_debug_delay_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ struct bl_config_s *bconf = bl_drv->bconf;
+
+ return sprintf(buf, "bl power delay: on=%dms, off=%dms\n",
+ bconf->power_on_delay, bconf->power_off_delay);
+}
+
+static ssize_t bl_debug_delay_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int ret;
+ unsigned int val[2];
+ struct bl_config_s *bconf = bl_drv->bconf;
+
+ ret = sscanf(buf, "%d %d", &val[0], &val[1]);
+ if (ret == 2) {
+ bconf->power_on_delay = val[0];
+ bconf->power_off_delay = val[1];
+ pr_info("set bl power_delay: on=%dms, off=%dms\n",
+ val[0], val[1]);
+ } else {
+ pr_info("invalid data\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t bl_debug_key_valid_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", bl_key_valid);
+}
+
+static ssize_t bl_debug_config_load_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", bl_config_load);
+}
+
+static ssize_t bl_debug_print_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "show bl_debug_print_flag: %d\n",
+ bl_debug_print_flag);
+}
+
+static ssize_t bl_debug_print_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int ret;
+ unsigned int temp = 0;
+
+ ret = kstrtouint(buf, 10, &temp);
+ if (ret == 0) {
+ bl_debug_print_flag = temp;
+ BLPR("set bl_debug_print_flag: %u\n", bl_debug_print_flag);
+ } else {
+ pr_info("invalid data\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static struct class_attribute bl_debug_class_attrs[] = {
+ __ATTR(help, 0644, bl_debug_help, NULL),
+ __ATTR(status, 0644, bl_status_read, NULL),
+ __ATTR(pwm, 0644, bl_debug_pwm_show,
+ bl_debug_pwm_store),
+ __ATTR(power, 0644, bl_debug_power_show,
+ bl_debug_power_store),
+ __ATTR(delay, 0644, bl_debug_delay_show,
+ bl_debug_delay_store),
+ __ATTR(key_valid, 0644, bl_debug_key_valid_show, NULL),
+ __ATTR(config_load, 0644,
+ bl_debug_config_load_show, NULL),
+ __ATTR(print, 0644, bl_debug_print_show,
+ bl_debug_print_store),
+};
+
+static int aml_bl_creat_class(void)
+{
+ int i;
+
+ bl_debug_class = class_create(THIS_MODULE, "aml_bl");
+ if (IS_ERR(bl_debug_class)) {
+ BLERR("create aml_bl debug class fail\n");
+ return -1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bl_debug_class_attrs); i++) {
+ if (class_create_file(bl_debug_class,
+ &bl_debug_class_attrs[i])) {
+ BLERR("create aml_bl debug attribute %s fail\n",
+ bl_debug_class_attrs[i].attr.name);
+ }
+ }
+ return 0;
+}
+
+static int aml_bl_remove_class(void)
+{
+ int i;
+
+ if (bl_debug_class == NULL)
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE(bl_debug_class_attrs); i++)
+ class_remove_file(bl_debug_class, &bl_debug_class_attrs[i]);
+ class_destroy(bl_debug_class);
+ bl_debug_class = NULL;
+ return 0;
+}
+/* **************************************** */
+
+#ifdef CONFIG_PM
+static int aml_bl_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ switch (bl_off_policy) {
+ case BL_OFF_POLICY_ONCE:
+ aml_bl_off_policy_cnt += 1;
+ break;
+ default:
+ break;
+ }
+
+ if (aml_bl_off_policy_cnt == 2) {
+ aml_bl_off_policy_cnt = 0;
+ bl_off_policy = BL_OFF_POLICY_NONE;
+ BLPR("change bl_off_policy: %d\n", bl_off_policy);
+ }
+
+ BLPR("aml_bl_suspend: bl_off_policy=%d, aml_bl_off_policy_cnt=%d\n",
+ bl_off_policy, aml_bl_off_policy_cnt);
+ return 0;
+}
+
+static int aml_bl_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
+
+static int aml_bl_probe(struct platform_device *pdev)
+{
+ struct backlight_properties props;
+ struct backlight_device *bldev;
+ struct bl_config_s *bconf;
+ int ret;
+
+#ifdef AML_BACKLIGHT_DEBUG
+ bl_debug_print_flag = 1;
+#else
+ bl_debug_print_flag = 0;
+#endif
+
+ aml_bl_off_policy_cnt = 0;
+
+ /* init backlight parameters */
+ brightness_bypass = 0;
+ bl_pwm_bypass = 0;
+ bl_pwm_duty_free = 0;
+
+ bl_chip_type = aml_bl_check_chip();
+ bl_drv = kzalloc(sizeof(struct aml_bl_drv_s), GFP_KERNEL);
+ if (!bl_drv) {
+ BLERR("driver malloc error\n");
+ return -ENOMEM;
+ }
+
+ bconf = &bl_config;
+ bl_drv->dev = &pdev->dev;
+ bl_drv->bconf = bconf;
+ ret = aml_bl_config_load(bconf, pdev);
+ if (ret)
+ goto err;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.power = FB_BLANK_UNBLANK; /* full on */
+ props.max_brightness = (bconf->level_max > 0 ?
+ bconf->level_max : BL_LEVEL_MAX);
+ props.brightness = (bconf->level_default > 0 ?
+ bconf->level_default : BL_LEVEL_DEFAULT);
+
+ bldev = backlight_device_register(AML_BL_NAME, &pdev->dev,
+ bl_drv, &aml_bl_ops, &props);
+ if (IS_ERR(bldev)) {
+ BLERR("failed to register backlight\n");
+ ret = PTR_ERR(bldev);
+ goto err;
+ }
+ bl_drv->bldev = bldev;
+ /* platform_set_drvdata(pdev, bl_drv); */
+
+ /* init workqueue */
+ INIT_DELAYED_WORK(&bl_drv->bl_delayed_work, aml_bl_delayd_on);
+ bl_drv->workqueue = create_workqueue("bl_power_on_queue");
+ if (bl_drv->workqueue == NULL)
+ BLERR("can't create bl work queue\n");
+
+#ifdef CONFIG_AMLOGIC_LCD
+ ret = aml_lcd_notifier_register(&aml_bl_on_nb);
+ if (ret)
+ BLERR("register aml_bl_on_notifier failed\n");
+ ret = aml_lcd_notifier_register(&aml_bl_off_nb);
+ if (ret)
+ BLERR("register aml_bl_off_notifier failed\n");
+ ret = aml_lcd_notifier_register(&aml_bl_lcd_update_nb);
+ if (ret)
+ BLERR("register aml_bl_lcd_update_nb failed\n");
+ ret = aml_lcd_notifier_register(&aml_bl_lcd_test_nb);
+ if (ret)
+ BLERR("register aml_bl_lcd_test_nb failed\n");
+#endif
+ aml_bl_creat_class();
+
+ /* update bl status */
+ bl_drv->state = (BL_STATE_LCD_ON |
+ BL_STATE_BL_POWER_ON | BL_STATE_BL_ON);
+ bl_on_request = 1;
+
+ if (brightness_bypass) {
+ aml_bl_set_level(bl_level_uboot);
+ bl_on_level = bl_level_uboot;
+ } else {
+ aml_bl_update_status(bl_drv->bldev);
+ bl_on_level = bconf->level_default;
+ }
+
+ BLPR("probe OK\n");
+ return 0;
+err:
+ kfree(bl_drv);
+ bl_drv = NULL;
+ return ret;
+}
+
+static int __exit aml_bl_remove(struct platform_device *pdev)
+{
+ int ret;
+ /*struct aml_bl *bl_drv = platform_get_drvdata(pdev);*/
+
+ aml_bl_remove_class();
+
+ ret = cancel_delayed_work_sync(&bl_drv->bl_delayed_work);
+ if (bl_drv->workqueue)
+ destroy_workqueue(bl_drv->workqueue);
+
+ backlight_device_unregister(bl_drv->bldev);
+#ifdef CONFIG_AMLOGIC_LCD
+ aml_lcd_notifier_unregister(&aml_bl_lcd_update_nb);
+ aml_lcd_notifier_unregister(&aml_bl_on_nb);
+ aml_lcd_notifier_unregister(&aml_bl_off_nb);
+#endif
+ /* platform_set_drvdata(pdev, NULL); */
+ switch (bl_drv->bconf->method) {
+ case BL_CTRL_PWM:
+ kfree(bl_drv->bconf->bl_pwm);
+ break;
+ case BL_CTRL_PWM_COMBO:
+ kfree(bl_drv->bconf->bl_pwm_combo0);
+ kfree(bl_drv->bconf->bl_pwm_combo1);
+ break;
+#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
+ case BL_CTRL_LOCAL_DIMING:
+ aml_ldim_remove();
+ break;
+#endif
+ default:
+ break;
+ }
+ kfree(bl_drv);
+ bl_drv = NULL;
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id aml_bl_dt_match[] = {
+ {
+ .compatible = "amlogic, backlight",
+ },
+ {},
+};
+#endif
+
+static struct platform_driver aml_bl_driver = {
+ .driver = {
+ .name = AML_BL_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = aml_bl_dt_match,
+#endif
+ },
+ .probe = aml_bl_probe,
+ .remove = __exit_p(aml_bl_remove),
+#ifdef CONFIG_PM
+ .suspend = aml_bl_suspend,
+ .resume = aml_bl_resume,
+#endif
+};
+
+static int __init aml_bl_init(void)
+{
+ if (platform_driver_register(&aml_bl_driver)) {
+ BLPR("failed to register bl driver module\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void __exit aml_bl_exit(void)
+{
+ platform_driver_unregister(&aml_bl_driver);
+}
+
+late_initcall(aml_bl_init);
+module_exit(aml_bl_exit);
+
+static int __init aml_bl_boot_para_setup(char *s)
+{
+ char bl_off_policy_str[10] = "none";
+
+ bl_off_policy = BL_OFF_POLICY_NONE;
+ aml_bl_off_policy_cnt = 0;
+ if (s != NULL)
+ sprintf(bl_off_policy_str, "%s", s);
+
+ if (strncmp(bl_off_policy_str, "none", 2) == 0)
+ bl_off_policy = BL_OFF_POLICY_NONE;
+ else if (strncmp(bl_off_policy_str, "always", 2) == 0)
+ bl_off_policy = BL_OFF_POLICY_ALWAYS;
+ else if (strncmp(bl_off_policy_str, "once", 2) == 0)
+ bl_off_policy = BL_OFF_POLICY_ONCE;
+ BLPR("bl_off_policy: %s\n", bl_off_policy_str);
+
+ return 0;
+}
+__setup("bl_off=", aml_bl_boot_para_setup);
+
+MODULE_DESCRIPTION("AML Backlight Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Amlogic, Inc.");
--- /dev/null
+/*
+ * 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
+ */
+ };
+};
+
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/backlight/aml_bl_reg.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __AML_BL_REG_H__
+#define __AML_BL_REG_H__
+#include <linux/amlogic/iomap.h>
+
+/* normal pwm reg: cbus */
+#define PWM_PWM_A 0x2154
+#define PWM_PWM_B 0x2155
+#define PWM_MISC_REG_AB 0x2156
+#define PWM_PWM_C 0x2190
+#define PWM_PWM_D 0x2191
+#define PWM_MISC_REG_CD 0x2192
+#define PWM_PWM_E 0x21b0
+#define PWM_PWM_F 0x21b1
+#define PWM_MISC_REG_EF 0x21b2
+/* normal pwm reg: cbus, txlx */
+#define PWM_PWM_A_TXLX 0x6c00
+#define PWM_PWM_B_TXLX 0x6c01
+#define PWM_MISC_REG_AB_TXLX 0x6c02
+#define PWM_PWM_C_TXLX 0x6800
+#define PWM_PWM_D_TXLX 0x6801
+#define PWM_MISC_REG_CD_TXLX 0x6802
+#define PWM_PWM_E_TXLX 0x6400
+#define PWM_PWM_F_TXLX 0x6401
+#define PWM_MISC_REG_EF_TXLX 0x6402
+
+/* pwm_vs reg: vcbus */
+#define VPU_VPU_PWM_V0 0x2730
+#define VPU_VPU_PWM_V1 0x2731
+#define VPU_VPU_PWM_V2 0x2732
+#define VPU_VPU_PWM_V3 0x2733
+
+#define ENCL_VIDEO_MAX_LNCNT 0x1cbb
+
+
+static inline unsigned int bl_cbus_read(unsigned int reg)
+{
+ return aml_read_cbus(reg);
+};
+
+static inline void bl_cbus_write(unsigned int reg, unsigned int value)
+{
+ aml_write_cbus(reg, value);
+};
+
+static inline void bl_cbus_setb(unsigned int reg, unsigned int value,
+ unsigned int _start, unsigned int _len)
+{
+ bl_cbus_write(reg, ((bl_cbus_read(reg) &
+ (~(((1L << _len)-1) << _start))) |
+ ((value & ((1L << _len)-1)) << _start)));
+
+}
+
+static inline unsigned int bl_vcbus_read(unsigned int reg)
+{
+ return aml_read_vcbus(reg);
+};
+
+static inline void bl_vcbus_write(unsigned int reg, unsigned int value)
+{
+ aml_write_vcbus(reg, value);
+};
+
+static inline void bl_vcbus_setb(unsigned int reg, unsigned int value,
+ unsigned int _start, unsigned int _len)
+{
+ bl_vcbus_write(reg, ((bl_vcbus_read(reg) &
+ (~(((1L << _len)-1) << _start))) |
+ ((value & ((1L << _len)-1)) << _start)));
+
+}
+
+#endif
+
--- /dev/null
+#
+# 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,
+
--- /dev/null
+
+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
--- /dev/null
+
+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
+
--- /dev/null
+/*
+ * 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>;
+};
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/backlight/bl_extern/bl_extern.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/aml_bl_extern.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+
+#ifdef BL_EXT_DEBUG_INFO
+#define DBG_PRINT(...) pr_info(__VA_ARGS__)
+#else
+#define DBG_PRINT(...)
+#endif
+
+static struct aml_bl_extern_driver_t bl_ext_driver = {
+ .type = BL_EXTERN_MAX,
+ .name = NULL,
+ .power_on = NULL,
+ .power_off = NULL,
+ .set_level = NULL,
+
+};
+
+struct aml_bl_extern_driver_t *aml_bl_extern_get_driver(void)
+{
+ return &bl_ext_driver;
+}
+
+int bl_extern_driver_check(void)
+{
+ struct aml_bl_extern_driver_t *bl_ext;
+
+ bl_ext = aml_bl_extern_get_driver();
+ if (bl_ext) {
+ if (bl_ext->type < BL_EXTERN_MAX) {
+ pr_err("[warning]: bl_extern has already exist (%s)\n",
+ bl_ext->name);
+ return -1;
+ }
+ } else {
+ pr_err("get bl_extern_driver failed\n");
+ }
+ return 0;
+}
+
+int get_bl_extern_dt_data(struct device dev, struct bl_extern_config_t *pdata)
+{
+ int ret;
+ struct device_node *of_node = dev.of_node;
+ u32 bl_para[2];
+ const char *str;
+ struct gpio_desc *bl_extern_gpio;
+
+ ret = of_property_read_string(of_node, "dev_name",
+ (const char **)&pdata->name);
+ if (ret) {
+ pdata->name = "aml_bl_extern";
+ pr_err("warning: get dev_name failed\n");
+ }
+
+ ret = of_property_read_u32(of_node, "type", &pdata->type);
+ if (ret) {
+ pdata->type = BL_EXTERN_MAX;
+ pr_err("%s warning: get type failed, exit\n", pdata->name);
+ return -1;
+ }
+ pdata->gpio_used = 0;
+ ret = of_property_read_string_index(of_node,
+ "gpio_enable_on_off", 0, &str);
+ if (ret) {
+ pr_warn("%s warning: get gpio_enable failed\n", pdata->name);
+ } else {
+ if (strncmp(str, "G", 1) == 0) {
+ pdata->gpio_used = 1;
+ bl_extern_gpio = gpiod_get(&dev, str);
+ if (bl_extern_gpio) {
+ pr_warn("%s warning:failed to alloc gpio (%s)\n",
+ pdata->name, str);
+ }
+ pdata->gpio = bl_extern_gpio;
+ }
+ DBG_PRINT("%s: gpio_enable %s\n",
+ pdata->name, ((pdata->gpio_used) ? str:"none"));
+ }
+ ret = of_property_read_string_index(of_node,
+ "gpio_enable_on_off", 1, &str);
+ if (ret) {
+ pr_warn("%s warning: get gpio_enable_on failed\n", pdata->name);
+ } else {
+ if (strncmp(str, "2", 1) == 0)
+ pdata->gpio_on = LCD_POWER_GPIO_INPUT;
+ else if (strncmp(str, "0", 1) == 0)
+ pdata->gpio_on = LCD_POWER_GPIO_OUTPUT_LOW;
+ else
+ pdata->gpio_on = LCD_POWER_GPIO_OUTPUT_HIGH;
+ }
+ ret = of_property_read_string_index(of_node,
+ "gpio_enable_on_off", 2, &str);
+ if (ret) {
+ pr_warn("%s warning:get gpio_enable_off failed\n", pdata->name);
+ } else {
+ if (strncmp(str, "2", 1) == 0)
+ pdata->gpio_off = LCD_POWER_GPIO_INPUT;
+ else if (strncmp(str, "1", 1) == 0)
+ pdata->gpio_off = LCD_POWER_GPIO_OUTPUT_HIGH;
+ else
+ pdata->gpio_off = LCD_POWER_GPIO_OUTPUT_LOW;
+ }
+ DBG_PRINT("%s: gpio_on = %d,", pdata->name, pdata->gpio_on);
+ DBG_PRINT("gpio_off = %d\n", pdata->gpio_off);
+ switch (pdata->type) {
+ case BL_EXTERN_I2C:
+ ret = of_property_read_u32(of_node, "i2c_address",
+ &pdata->i2c_addr);
+ if (ret) {
+ pr_warn("%s warning: get i2c_address failed\n",
+ pdata->name);
+ pdata->i2c_addr = 0;
+ }
+ DBG_PRINT("%s: i2c_address=", pdata->name);
+ DBG_PRINT("0x%02x\n", pdata->i2c_addr);
+ ret = of_property_read_string(of_node, "i2c_bus", &str);
+ if (ret) {
+ pr_warn("%s warning: get i2c_bus failed,", pdata->name);
+ pr_warn("use default i2c bus\n");
+ pdata->i2c_bus = AML_I2C_MASTER_A;
+ } else {
+ if (strncmp(str, "i2c_bus_a", 9) == 0)
+ pdata->i2c_bus = AML_I2C_MASTER_A;
+ else if (strncmp(str, "i2c_bus_b", 9) == 0)
+ pdata->i2c_bus = AML_I2C_MASTER_B;
+ else if (strncmp(str, "i2c_bus_c", 9) == 0)
+ pdata->i2c_bus = AML_I2C_MASTER_C;
+ else if (strncmp(str, "i2c_bus_d", 9) == 0)
+ pdata->i2c_bus = AML_I2C_MASTER_D;
+ else if (strncmp(str, "i2c_bus_ao", 10) == 0)
+ pdata->i2c_bus = AML_I2C_MASTER_AO;
+ else
+ pdata->i2c_bus = AML_I2C_MASTER_A;
+ }
+ DBG_PRINT("%s: i2c_bus=%s[%d]\n", pdata->name,
+ str, pdata->i2c_bus);
+ break;
+ case BL_EXTERN_SPI:
+ ret = of_property_read_string(of_node, "gpio_spi_cs", &str);
+ if (ret) {
+ pr_warn("%s warning: get spi gpio_spi_cs failed\n",
+ pdata->name);
+ pdata->spi_cs = NULL;
+ } else {
+ bl_extern_gpio = gpiod_get(&dev, str);
+ if (bl_extern_gpio) {
+ pdata->spi_cs = bl_extern_gpio;
+ DBG_PRINT("spi_cs gpio = %s\n", str);
+ } else {
+ pr_warn("%s warning:failed to alloc gpio (%s)\n",
+ pdata->name, str);
+ pdata->spi_cs = NULL;
+ }
+ }
+ ret = of_property_read_string(of_node, "gpio_spi_clk", &str);
+ if (ret) {
+ pr_warn("%s warning: get spi gpio_spi_clk failed\n",
+ pdata->name);
+ pdata->spi_clk = NULL;
+ } else {
+ bl_extern_gpio = gpiod_get(&dev, str);
+ if (bl_extern_gpio) {
+ pdata->spi_clk = bl_extern_gpio;
+ DBG_PRINT("spi_cs gpio = %s\n", str);
+ } else {
+ pdata->spi_clk = NULL;
+ pr_warn("%s warning:failed to alloc gpio (%s)\n",
+ pdata->name, str);
+ }
+ }
+ ret = of_property_read_string(of_node, "gpio_spi_data", &str);
+ if (ret) {
+ pr_warn("%s warning: get spi gpio_spi_data failed\n",
+ pdata->name);
+ pdata->spi_data = NULL;
+ } else {
+ bl_extern_gpio = gpiod_get(&dev, str);
+ if (bl_extern_gpio) {
+ pdata->spi_data = bl_extern_gpio;
+ DBG_PRINT("spi_cs gpio = %s\n", str);
+ } else {
+ pdata->spi_data = NULL;
+ pr_warn("%s warning:failed to alloc gpio (%s)\n",
+ pdata->name, str);
+ }
+ }
+ break;
+ case BL_EXTERN_OTHER:
+ break;
+ default:
+ break;
+ }
+ ret = of_property_read_u32_array(of_node, "dim_max_min",
+ &bl_para[0], 2);
+ if (ret) {
+ pr_warn("%s warning: get dim_max_min failed\n", pdata->name);
+ pdata->dim_max = 0;
+ pdata->dim_min = 0;
+ } else {
+ pdata->dim_max = bl_para[0];
+ pdata->dim_min = bl_para[1];
+ }
+ DBG_PRINT("%s dim_min = %d, dim_max = %d\n", pdata->name,
+ pdata->dim_min, pdata->dim_max);
+ return 0;
+}
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/backlight/bl_extern/i2c_lp8556.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/sysctl.h>
+#include <linux/uaccess.h>
+#include <linux/amlogic/media/vout/lcd/aml_bl_extern.h>
+
+static struct bl_extern_config_t *bl_ext_config;
+
+static struct i2c_client *aml_lp8556_i2c_client;
+
+
+#ifdef BL_EXT_DEBUG_INFO
+#define DBG_PRINT(...) pr_info(__VA_ARGS__)
+#else
+#define DBG_PRINT(...)
+#endif
+
+#define BL_EXTERN_NAME "bl_i2c_lp8556"
+static unsigned int bl_status = 1;
+static unsigned int bl_level;
+
+static unsigned char i2c_init_table[][2] = {
+ {0xa1, 0x76}, /* hight bit(8~11)(0~0X66e set backlight) */
+ {0xa0, 0x66}, /* low bit(0~7) 20mA */
+ {0x16, 0x1F}, /* 5channel LED enable 0x1F */
+ {0xa9, 0xA0}, /* VBOOST_MAX 25V */
+ {0x9e, 0x12},
+ {0xa2, 0x23},
+ {0x01, 0x05}, /* 0x03 pwm+I2c set brightness,0x5 I2c set brightness */
+ {0xff, 0xff}, /* ending flag */
+};
+
+static int aml_i2c_write(struct i2c_client *i2client,
+ unsigned char *buff, unsigned int len)
+{
+ int res = 0;
+ struct i2c_msg msg[] = {
+ {
+ .addr = i2client->addr,
+ .flags = 0,
+ .len = len,
+ .buf = buff,
+ }
+ };
+
+ res = i2c_transfer(i2client->adapter, msg, 1);
+ if (res < 0)
+ pr_err("%s: i2c transfer failed [addr 0x%02x]\n",
+ __func__, i2client->addr);
+
+ return res;
+}
+#if 0
+static int aml_i2c_read(struct i2c_client *i2client,
+ unsigned char *buff, unsigned int len)
+{
+ int res = 0;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = buff,
+ },
+ {
+ .addr = i2client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buff,
+ }
+ };
+ res = i2c_transfer(i2client->adapter, msgs, 2);
+ if (res < 0)
+ pr_err("%s: i2c transfer failed [addr 0x%02x]\n",
+ __func__, i2client->addr);
+ return res;
+}
+#endif
+
+static int bl_extern_set_level(unsigned int level)
+{
+ unsigned char tData[3];
+ int ret = 0;
+ struct aml_bl_drv_s *bl_drv = aml_bl_get_driver();
+ unsigned int level_max, level_min;
+ unsigned int dim_max, dim_min;
+
+ bl_level = level;
+
+ if (bl_ext_config == NULL) {
+ pr_err("no %s driver\n", BL_EXTERN_NAME);
+ return -1;
+ }
+
+ if (bl_drv == NULL)
+ return -1;
+ level_max = bl_drv->bconf->level_max;
+ level_min = bl_drv->bconf->level_min;
+ dim_max = bl_ext_config->dim_max;
+ dim_min = bl_ext_config->dim_min;
+ level = dim_min - ((level - level_min) * (dim_min - dim_max)) /
+ (level_max - level_min);
+ level &= 0xff;
+
+ if (bl_status) {
+ tData[0] = 0x0;
+ tData[1] = level;
+ ret = aml_i2c_write(aml_lp8556_i2c_client, tData, 2);
+ }
+ return ret;
+}
+
+static int bl_extern_power_on(void)
+{
+ unsigned char tData[3];
+ int i = 0, ending_flag = 0;
+ int ret = 0;
+
+ if (bl_ext_config->gpio) {
+ if (bl_ext_config->gpio_on == 2) {
+ bl_gpio_input(bl_ext_config->gpio);
+ } else {
+ bl_gpio_output(bl_ext_config->gpio,
+ bl_ext_config->gpio_on);
+ }
+ }
+
+ while (ending_flag == 0) {
+ if (i2c_init_table[i][0] == 0xff) {
+ if (i2c_init_table[i][1] == 0xff)
+ ending_flag = 1;
+ else
+ mdelay(i2c_init_table[i][1]);
+ } else {
+ tData[0] = i2c_init_table[i][0];
+ tData[1] = i2c_init_table[i][1];
+ ret = aml_i2c_write(aml_lp8556_i2c_client, tData, 2);
+ }
+ i++;
+ }
+ bl_status = 1;
+ bl_extern_set_level(bl_level);
+ BLPR("%s\n", __func__);
+ return ret;
+}
+
+static int bl_extern_power_off(void)
+{
+ bl_status = 0;
+ if (bl_ext_config->gpio) {
+ if (bl_ext_config->gpio_off == 2) {
+ bl_gpio_input(bl_ext_config->gpio);
+ } else {
+ bl_gpio_output(bl_ext_config->gpio,
+ bl_ext_config->gpio_off);
+ }
+ }
+ BLPR("%s\n", __func__);
+ return 0;
+}
+
+static int get_bl_extern_config(struct device dev,
+ struct bl_extern_config_t *bl_ext_cfg)
+{
+ int ret = 0;
+ struct aml_bl_extern_driver_t *bl_ext;
+
+ ret = get_bl_extern_dt_data(dev, bl_ext_cfg);
+ if (ret) {
+ BLPR("error %s: failed to get dt data\n", BL_EXTERN_NAME);
+ return ret;
+ }
+
+ if (bl_ext_cfg->dim_min > 0xff)
+ bl_ext_cfg->dim_min = 0xff;
+ if (bl_ext_cfg->dim_max > 0xff)
+ bl_ext_cfg->dim_max = 0xff;
+
+ bl_ext = aml_bl_extern_get_driver();
+ if (bl_ext) {
+ bl_ext->type = bl_ext_cfg->type;
+ bl_ext->name = bl_ext_cfg->name;
+ bl_ext->power_on = bl_extern_power_on;
+ bl_ext->power_off = bl_extern_power_off;
+ bl_ext->set_level = bl_extern_set_level;
+ } else {
+ BLPR("error %s get bl_extern_driver failed\n",
+ bl_ext_cfg->name);
+ ret = -1;
+ }
+ return ret;
+}
+
+static int aml_lp8556_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ BLPR("error %s: functionality check failed\n",
+ __func__);
+ else
+ aml_lp8556_i2c_client = client;
+ BLPR("%s OK\n", __func__);
+
+ return 0;
+}
+
+static int aml_lp8556_i2c_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id aml_lp8556_i2c_id[] = {
+ {BL_EXTERN_NAME, 0},
+ { }
+};
+
+static struct i2c_driver aml_lp8556_i2c_driver = {
+ .probe = aml_lp8556_i2c_probe,
+ .remove = aml_lp8556_i2c_remove,
+ .id_table = aml_lp8556_i2c_id,
+ .driver = {
+ .name = BL_EXTERN_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int aml_lp8556_probe(struct platform_device *pdev)
+{
+ struct i2c_board_info i2c_info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *i2c_client;
+ int ret = 0;
+
+ if (bl_extern_driver_check())
+ return -1;
+ if (bl_ext_config == NULL)
+ bl_ext_config = kzalloc(sizeof(*bl_ext_config), GFP_KERNEL);
+ if (bl_ext_config == NULL) {
+ BLPR("error %s probe: failed to alloc data\n", BL_EXTERN_NAME);
+ return -1;
+ }
+
+ pdev->dev.platform_data = bl_ext_config;
+ ret = get_bl_extern_config(pdev->dev, bl_ext_config);
+ if (ret)
+ goto bl_extern_probe_failed;
+
+ memset(&i2c_info, 0, sizeof(i2c_info));
+ adapter = i2c_get_adapter(bl_ext_config->i2c_bus);
+ if (!adapter) {
+ BLPR("error %s£ºfailed get i2c adapter\n", BL_EXTERN_NAME);
+ goto bl_extern_probe_failed;
+ }
+
+ strncpy(i2c_info.type, bl_ext_config->name, I2C_NAME_SIZE);
+ i2c_info.addr = bl_ext_config->i2c_addr;
+ i2c_info.platform_data = bl_ext_config;
+ i2c_info.flags = 0;
+ if (i2c_info.addr > 0x7f)
+ i2c_info.flags = 0x10;
+ i2c_client = i2c_new_device(adapter, &i2c_info);
+ if (!i2c_client) {
+ BLPR("error %s :failed new i2c device\n", BL_EXTERN_NAME);
+ goto bl_extern_probe_failed;
+ } else{
+ DBG_PRINT("error %s: new i2c device succeed\n",
+ ((struct bl_extern_data_t *)(bl_ext_config))->name);
+ }
+
+ if (!aml_lp8556_i2c_client) {
+ ret = i2c_add_driver(&aml_lp8556_i2c_driver);
+ if (ret) {
+ BLPR("error %s probe: add i2c_driver failed\n",
+ BL_EXTERN_NAME);
+ goto bl_extern_probe_failed;
+ }
+ }
+ BLPR("%s ok\n", __func__);
+ return ret;
+
+bl_extern_probe_failed:
+ kfree(bl_ext_config);
+ bl_ext_config = NULL;
+
+ return -1;
+}
+
+static int aml_lp8556_remove(struct platform_device *pdev)
+{
+ kfree(pdev->dev.platform_data);
+ return 0;
+}
+
+#ifdef CONFIG_USE_OF
+static const struct of_device_id aml_lp8556_dt_match[] = {
+ {
+ .compatible = "amlogic, bl_i2c_lp8556",
+ },
+ {},
+};
+#else
+#define aml_lp8556_dt_match NULL
+#endif
+
+static struct platform_driver aml_lp8556_driver = {
+ .probe = aml_lp8556_probe,
+ .remove = aml_lp8556_remove,
+ .driver = {
+ .name = BL_EXTERN_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_USE_OF
+ .of_match_table = aml_lp8556_dt_match,
+#endif
+ },
+};
+
+static int __init aml_lp8556_init(void)
+{
+ int ret;
+
+ BLPR("%s\n", __func__);
+ ret = platform_driver_register(&aml_lp8556_driver);
+ if (ret) {
+ BLPR("error %s failed to register bl extern driver\n",
+ __func__);
+ return -ENODEV;
+ }
+ return ret;
+}
+
+static void __exit aml_lp8556_exit(void)
+{
+ platform_driver_unregister(&aml_lp8556_driver);
+}
+
+module_init(aml_lp8556_init);
+module_exit(aml_lp8556_exit);
+
+MODULE_AUTHOR("AMLOGIC");
+MODULE_DESCRIPTION("BL Extern driver for LP8556");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/backlight/bl_extern/mipi_LT070ME05.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/sysctl.h>
+#include <linux/uaccess.h>
+#include <linux/amlogic/media/vout/lcd/aml_bl_extern.h>
+
+#ifdef CONFIG_LCD_IF_MIPI_VALID
+static struct bl_extern_config_t *bl_ext_config;
+
+#ifdef BL_EXT_DEBUG_INFO
+#define DBG_PRINT(...) pr_info(__VA_ARGS__)
+#else
+#define DBG_PRINT(...)
+#endif
+
+#define BL_EXTERN_NAME "bl_mipi_LT070ME05"
+static unsigned int bl_status = 1;
+static unsigned int bl_level;
+
+/******************** mipi command ********************
+ * format: data_type, num, data....
+ * special: data_type=0xff, num<0xff means delay ms, num=0xff means ending.
+ */
+static int bl_extern_set_level(unsigned int level)
+{
+ unsigned char payload[] = {0x15, 2, 0x51, 0xe6, 0xff, 0xff};
+
+ bl_level = level;
+
+ if (bl_ext_config == NULL) {
+ pr_err("no %s driver\n", BL_EXTERN_NAME);
+ return -1;
+ }
+ get_bl_ext_level(bl_ext_config);
+ level = bl_ext_config->dim_min -
+ ((level - bl_ext_config->level_min) *
+ (bl_ext_config->dim_min - bl_ext_config->dim_max)) /
+ (bl_ext_config->level_max - bl_ext_config->level_min);
+ level &= 0xff;
+
+ if (bl_status) {
+ payload[3] = level;
+ dsi_write_cmd(payload);
+ }
+ return 0;
+}
+
+static int bl_extern_power_on(void)
+{
+ if (bl_ext_config->gpio_used > 0) {
+ if (bl_ext_config->gpio_on == 2) {
+ bl_extern_gpio_input(bl_ext_config->gpio);
+ } else {
+ bl_extern_gpio_output(bl_ext_config->gpio,
+ bl_ext_config->gpio_on);
+ }
+ }
+ bl_status = 1;
+ bl_extern_set_level(bl_level);
+ pr_info("%s\n", __func__);
+ return 0;
+}
+
+static int bl_extern_power_off(void)
+{
+ if (bl_ext_config->gpio_used > 0) {
+ if (bl_ext_config->gpio_off == 2) {
+ bl_extern_gpio_input(bl_ext_config->gpio);
+ } else {
+ bl_extern_gpio_output(bl_ext_config->gpio,
+ bl_ext_config->gpio_off);
+ }
+ }
+ pr_info("%s\n", __func__);
+ return 0;
+}
+
+static int get_bl_extern_config(struct device dev,
+ struct bl_extern_config_t *bl_ext_cfg)
+{
+ int ret = 0;
+ struct aml_bl_extern_driver_t *bl_ext;
+
+ ret = get_bl_extern_dt_data(dev, bl_ext_cfg);
+ if (ret) {
+ pr_err("[error] %s: failed to get dt data\n", BL_EXTERN_NAME);
+ return ret;
+ }
+
+ if (bl_ext_cfg->dim_min > 0xff)
+ bl_ext_cfg->dim_min = 0xff;
+ if (bl_ext_cfg->dim_max > 0xff)
+ bl_ext_cfg->dim_max = 0xff;
+
+ bl_ext = aml_bl_extern_get_driver();
+ if (bl_ext) {
+ bl_ext->type = bl_ext_cfg->type;
+ bl_ext->name = bl_ext_cfg->name;
+ bl_ext->power_on = bl_extern_power_on;
+ bl_ext->power_off = bl_extern_power_off;
+ bl_ext->set_level = bl_extern_set_level;
+ } else {
+ pr_err("[error] %s get bl_extern_driver failed\n",
+ bl_ext_cfg->name);
+ ret = -1;
+ }
+ return ret;
+}
+
+static int aml_LT070ME05_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ if (bl_extern_driver_check())
+ return -1;
+ if (bl_ext_config == NULL)
+ bl_ext_config = kzalloc(sizeof(*bl_ext_config), GFP_KERNEL);
+ if (bl_ext_config == NULL) {
+ pr_err("[error] %s probe: failed to alloc data\n",
+ BL_EXTERN_NAME);
+ return -1;
+ }
+
+ pdev->dev.platform_data = bl_ext_config;
+ ret = get_bl_extern_config(pdev->dev, bl_ext_config);
+ if (ret)
+ goto bl_extern_probe_failed;
+
+ pr_info("%s ok\n", __func__);
+ return ret;
+
+bl_extern_probe_failed:
+ kfree(bl_ext_config);
+ bl_ext_config = NULL;
+ return -1;
+}
+
+static int aml_LT070ME05_remove(struct platform_device *pdev)
+{
+ kfree(pdev->dev.platform_data);
+ return 0;
+}
+
+#ifdef CONFIG_USE_OF
+static const struct of_device_id aml_LT070ME05_dt_match[] = {
+ {
+ .compatible = "amlogic, bl_mipi_LT070ME05",
+ },
+ {},
+};
+#else
+#define aml_LT070ME05_dt_match NULL
+#endif
+
+static struct platform_driver aml_LT070ME05_driver = {
+ .probe = aml_LT070ME05_probe,
+ .remove = aml_LT070ME05_remove,
+ .driver = {
+ .name = BL_EXTERN_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_USE_OF
+ .of_match_table = aml_LT070ME05_dt_match,
+#endif
+ },
+};
+
+static int __init aml_LT070ME05_init(void)
+{
+ int ret;
+
+ DBG_PRINT("%s\n", __func__);
+
+ ret = platform_driver_register(&aml_LT070ME05_driver);
+ if (ret) {
+ pr_err("[error] %s failed to register bl extern driver\n",
+ __func__);
+ return -ENODEV;
+ }
+ return ret;
+}
+
+static void __exit aml_LT070ME05_exit(void)
+{
+ platform_driver_unregister(&aml_LT070ME05_driver);
+}
+
+rootfs_initcall(aml_LT070ME05_init);
+module_exit(aml_LT070ME05_exit);
+
+MODULE_AUTHOR("AMLOGIC");
+MODULE_DESCRIPTION("BL Extern driver for LT070ME05");
+MODULE_LICENSE("GPL");
+
+#endif
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/backlight/bl_extern/pmu_aml1218.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/sysctl.h>
+#include <linux/uaccess.h>
+#include <linux/amlogic/media/vout/lcd/aml_bl_extern.h>
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+#include <linux/amlogic/aml_pmu_common.h>
+#endif
+
+static struct bl_extern_config_t *bl_ext_config;
+
+
+#ifdef BL_EXT_DEBUG_INFO
+#define DBG_PRINT(...) pr_info(__VA_ARGS__)
+#else
+#define DBG_PRINT(...)
+#endif
+
+#define BL_EXTERN_NAME "bl_pmu_aml1218"
+
+static int bl_extern_set_level(unsigned int level)
+{
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+ struct aml_pmu_driver *pmu_driver;
+ unsigned char temp;
+#endif
+ int ret = 0;
+
+ if (bl_ext_config == NULL) {
+ pr_err("no %s driver\n", BL_EXTERN_NAME);
+ return -1;
+ }
+ get_bl_ext_level(bl_ext_config);
+ level = bl_ext_config->dim_min -
+ ((level - bl_ext_config->level_min) *
+ (bl_ext_config->dim_min - bl_ext_config->dim_max)) /
+ (bl_ext_config->level_max - bl_ext_config->level_min);
+ level &= 0x1f;
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+ pmu_driver = aml_pmu_get_driver();
+ if (pmu_driver == NULL) {
+ pr_err("no pmu driver\n");
+ } else {
+ if ((pmu_driver->pmu_reg_write) && (pmu_driver->pmu_reg_read)) {
+ ret = pmu_driver->pmu_reg_read(0x005f, &temp);
+ temp &= ~(0x3f << 2);
+ temp |= (level << 2);
+ ret = pmu_driver->pmu_reg_write(0x005f, temp);
+ } else {
+ pr_err("no pmu_reg_read/write\n");
+ return -1;
+ }
+ }
+#endif
+ return ret;
+}
+
+static int bl_extern_power_on(void)
+{
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+ struct aml_pmu_driver *pmu_driver;
+ unsigned char temp;
+#endif
+ int ret = 0;
+
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+ pmu_driver = aml_pmu_get_driver();
+ if (pmu_driver == NULL) {
+ pr_err("no pmu driver\n");
+ } else {
+ if ((pmu_driver->pmu_reg_write) && (pmu_driver->pmu_reg_read)) {
+ ret = pmu_driver->pmu_reg_read(0x005e, &temp);
+ temp |= (1 << 7);
+ ret = pmu_driver->pmu_reg_write(0x005e, temp);
+ } else {
+ pr_err("no pmu_reg_read/write\n");
+ return -1;
+ }
+ }
+#endif
+ if (bl_ext_config->gpio_used > 0) {
+ if (bl_ext_config->gpio_on == 2)
+ bl_extern_gpio_input(bl_ext_config->gpio);
+ else
+ bl_extern_gpio_output(bl_ext_config->gpio,
+ bl_ext_config->gpio_on);
+ }
+ pr_info("%s\n", __func__);
+ return ret;
+}
+
+static int bl_extern_power_off(void)
+{
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+ struct aml_pmu_driver *pmu_driver;
+ unsigned char temp;
+#endif
+ int ret = 0;
+
+ if (bl_ext_config->gpio_used > 0) {
+ if (bl_ext_config->gpio_off == 2)
+ bl_extern_gpio_input(bl_ext_config->gpio);
+ else
+ bl_extern_gpio_output(bl_ext_config->gpio,
+ bl_ext_config->gpio_off);
+ }
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+ pmu_driver = aml_pmu_get_driver();
+ if (pmu_driver == NULL) {
+ pr_err("no pmu driver\n");
+ } else {
+ if ((pmu_driver->pmu_reg_write) && (pmu_driver->pmu_reg_read)) {
+ ret = pmu_driver->pmu_reg_read(0x005e, &temp);
+ temp &= ~(1 << 7);
+ ret = pmu_driver->pmu_reg_write(0x005e, temp);
+ } else {
+ pr_err("no pmu_reg_read/write\n");
+ return -1;
+ }
+ }
+#endif
+ pr_info("%s\n", __func__);
+ return ret;
+}
+
+static ssize_t bl_extern_debug(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+ struct aml_pmu_driver *pmu_driver;
+ unsigned char temp;
+ unsigned int t[2];
+#endif
+ int ret = 0;
+
+#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
+ pmu_driver = aml_pmu_get_driver();
+ if (pmu_driver == NULL) {
+ pr_err("no pmu driver\n");
+ return -EINVAL;
+ }
+
+ switch (buf[0]) {
+ case 'r':
+ ret = sscanf(buf, "r %x", &t[0]);
+ if ((pmu_driver->pmu_reg_write) && (pmu_driver->pmu_reg_read)) {
+ ret = pmu_driver->pmu_reg_read(t[0], &temp);
+ pr_info("read pmu reg: 0x%x=0x%x\n", t[0], temp);
+ }
+ break;
+ case 'w':
+ ret = sscanf(buf, "w %x %x", &t[0], &t[1]);
+ if ((pmu_driver->pmu_reg_write) && (pmu_driver->pmu_reg_read)) {
+ ret = pmu_driver->pmu_reg_write(t[0], t[1]);
+ ret = pmu_driver->pmu_reg_read(t[0], &temp);
+ pr_info("write pmu reg 0x%x: 0x%x, readback: 0x%x\n",
+ t[0], t[1], temp);
+ }
+ break;
+ default:
+ pr_err("wrong format of command.\n");
+ break;
+ }
+#endif
+ if (ret != 1 || ret != 2)
+ return -EINVAL;
+ return count;
+}
+
+static struct class_attribute bl_extern_debug_class_attrs[] = {
+ __ATTR(debug, 0644, NULL, bl_extern_debug),
+ __ATTR_NULL
+};
+
+static struct class bl_extern_debug_class = {
+ .name = "bl_ext",
+ .class_attrs = bl_extern_debug_class_attrs,
+};
+
+static int get_bl_extern_config(struct device dev,
+ struct bl_extern_config_t *bl_ext_cfg)
+{
+ int ret = 0;
+ struct aml_bl_extern_driver_t *bl_ext;
+
+ ret = get_bl_extern_dt_data(dev, bl_ext_cfg);
+ if (ret) {
+ pr_err("[error] %s: failed to get dt data\n", BL_EXTERN_NAME);
+ return ret;
+ }
+
+ if (bl_ext_cfg->dim_min > 0x1f)
+ bl_ext_cfg->dim_min = 0x1f;
+ if (bl_ext_cfg->dim_max > 0x1f)
+ bl_ext_cfg->dim_max = 0x1f;
+
+ bl_ext = aml_bl_extern_get_driver();
+ if (bl_ext) {
+ bl_ext->type = bl_ext_cfg->type;
+ bl_ext->name = bl_ext_cfg->name;
+ bl_ext->power_on = bl_extern_power_on;
+ bl_ext->power_off = bl_extern_power_off;
+ bl_ext->set_level = bl_extern_set_level;
+ } else {
+ pr_err("[error] %s get bl_extern_driver failed\n",
+ bl_ext_cfg->name);
+ ret = -1;
+ }
+ return ret;
+}
+
+static int aml_aml1218_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ if (bl_extern_driver_check())
+ return -1;
+ if (bl_ext_config == NULL)
+ bl_ext_config = kzalloc(sizeof(*bl_ext_config), GFP_KERNEL);
+ if (bl_ext_config == NULL) {
+ pr_err("[error] %s probe: failed to alloc data\n",
+ BL_EXTERN_NAME);
+ return -1;
+ }
+
+ pdev->dev.platform_data = bl_ext_config;
+
+ ret = get_bl_extern_config(pdev->dev, bl_ext_config);
+ if (ret)
+ goto bl_extern_probe_failed;
+
+ ret = class_register(&bl_extern_debug_class);
+ if (ret)
+ pr_err("class register bl_extern_debug_class fail!\n");
+
+ pr_err("%s ok\n", __func__);
+ return ret;
+
+bl_extern_probe_failed:
+ kfree(bl_ext_config);
+ bl_ext_config = NULL;
+ return -1;
+}
+
+static int aml_aml1218_remove(struct platform_device *pdev)
+{
+ kfree(pdev->dev.platform_data);
+ return 0;
+}
+
+#ifdef CONFIG_USE_OF
+static const struct of_device_id aml_aml1218_dt_match[] = {
+ {
+ .compatible = "amlogic, bl_pmu_aml1218",
+ },
+ {},
+};
+#else
+#define aml_aml1218_dt_match NULL
+#endif
+
+static struct platform_driver aml_aml1218_driver = {
+ .probe = aml_aml1218_probe,
+ .remove = aml_aml1218_remove,
+ .driver = {
+ .name = BL_EXTERN_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_USE_OF
+ .of_match_table = aml_aml1218_dt_match,
+#endif
+ },
+};
+
+static int __init aml_aml1218_init(void)
+{
+ int ret;
+
+ DBG_PRINT("%s\n", __func__);
+
+ ret = platform_driver_register(&aml_aml1218_driver);
+ if (ret) {
+ pr_err("[error] %s failed to register bl extern driver\n",
+ __func__);
+ return -ENODEV;
+ }
+ return ret;
+}
+
+static void __exit aml_aml1218_exit(void)
+{
+ platform_driver_unregister(&aml_aml1218_driver);
+}
+
+rootfs_initcall(aml_aml1218_init);
+module_exit(aml_aml1218_exit);
+
+MODULE_AUTHOR("AMLOGIC");
+MODULE_DESCRIPTION("BL Extern driver for aml1218");
+MODULE_LICENSE("GPL");
--- /dev/null
+#
+# 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
--- /dev/null
+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/
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_clk_config.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include "lcd_reg.h"
+#include "lcd_clk_config.h"
+
+static spinlock_t lcd_clk_lock;
+
+static const unsigned int od_fb_table[2] = {1, 2};
+
+static const unsigned int od_table[4] = {
+ 1, 2, 4, 8
+};
+
+static const unsigned int div_pre_table[6] = {
+ 1, 2, 3, 4, 5, 6
+};
+
+static char *lcd_clk_div_sel_table[] = {
+ "1",
+ "2",
+ "3",
+ "3.5",
+ "3.75",
+ "4",
+ "5",
+ "6",
+ "6.25",
+ "7",
+ "7.5",
+ "12",
+ "14",
+ "15",
+ "2.5",
+ "invalid",
+};
+
+static char *lcd_pll_ss_table_gxtvbb[] = {
+ "0, disable",
+ "1, +/-0.3%",
+ "2, +/-0.5%",
+ "3, +/-0.9%",
+ "4, +/-1.2%",
+};
+
+static char *lcd_pll_ss_table_txl[] = {
+ "0, disable",
+ "1, +/-0.3%",
+ "2, +/-0.4%",
+ "3, +/-0.9%",
+ "4, +/-1.2%",
+};
+
+static char *lcd_pll_ss_table_txlx[] = {
+ "0, disable",
+ "1, +/-0.3%",
+ "2, +/-0.5%",
+ "3, +/-1.0%",
+ "4, +/-1.6%",
+ "5, +/-3.0%",
+};
+
+static struct lcd_clk_config_s clk_conf = { /* unit: kHz */
+ /* IN-OUT parameters */
+ .fin = FIN_FREQ,
+ .fout = 0,
+
+ /* pll parameters */
+ .od_fb = 0,
+ .pll_m = 0,
+ .pll_n = 0,
+ .pll_od1_sel = 0,
+ .pll_od2_sel = 0,
+ .pll_od3_sel = 0,
+ .pll_level = 0,
+ .ss_level = 0,
+ .edp_div0 = 0,
+ .edp_div1 = 0,
+ .div_pre = 0, /* m6, m8, m8b */
+ .div_post = 0, /* m6, m8, m8b */
+ .div_sel = 0, /* g9tv, g9bb, gxtvbb */
+ .xd = 0,
+ .pll_fout = 0,
+
+ /* clk path node parameters */
+ .pll_m_max = 0,
+ .pll_m_min = 0,
+ .pll_n_max = 0,
+ .pll_n_min = 0,
+ .pll_frac_range = 0,
+ .pll_od_sel_max = 0,
+ .ss_level_max = 0,
+ .div_pre_sel_max = 0, /* m6, m8, m8b */
+ .div_post_sel_max = 0, /* m6, m8, m8b */
+ .div_sel_max = 0, /* g9tv, g9bb, gxtvbb */
+ .xd_max = 0,
+ .pll_ref_fmax = 0,
+ .pll_ref_fmin = 0,
+ .pll_vco_fmax = 0,
+ .pll_vco_fmin = 0,
+ .pll_out_fmax = 0,
+ .pll_out_fmin = 0,
+ .div_post_out_fmax = 0, /* m6, m8, m8b */
+ .div_in_fmax = 0, /* g9tv, g9bb, gxtvbb */
+ .div_out_fmax = 0, /* g9tv, g9bb, gxtvbb */
+ .xd_out_fmax = 0,
+};
+
+struct lcd_clk_config_s *get_lcd_clk_config(void)
+{
+ return &clk_conf;
+}
+
+static void lcd_clk_config_init_print(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ switch (lcd_drv->chip_type) {
+ case LCD_CHIP_AXG:
+ LCDPR("lcd clk config init:\n"
+ "pll_m_max: %d\n"
+ "pll_m_min: %d\n"
+ "pll_n_max: %d\n"
+ "pll_n_min: %d\n"
+ "pll_frac_range: %d\n"
+ "pll_od_sel_max: %d\n"
+ "ss_level_max: %d\n"
+ "pll_ref_fmax: %d\n"
+ "pll_ref_fmin: %d\n"
+ "pll_vco_fmax: %d\n"
+ "pll_vco_fmin: %d\n"
+ "pll_out_fmax: %d\n"
+ "pll_out_fmin: %d\n"
+ "xd_out_fmax: %d\n\n",
+ clk_conf.pll_m_max, clk_conf.pll_m_min,
+ clk_conf.pll_n_max, clk_conf.pll_n_min,
+ clk_conf.pll_frac_range,
+ clk_conf.pll_od_sel_max, clk_conf.ss_level_max,
+ clk_conf.pll_ref_fmax, clk_conf.pll_ref_fmin,
+ clk_conf.pll_vco_fmax, clk_conf.pll_vco_fmin,
+ clk_conf.pll_out_fmax, clk_conf.pll_out_fmin,
+ clk_conf.xd_out_fmax);
+ break;
+ default:
+ LCDPR("lcd clk config:\n"
+ "pll_m_max: %d\n"
+ "pll_m_min: %d\n"
+ "pll_n_max: %d\n"
+ "pll_n_min: %d\n"
+ "pll_frac_range: %d\n"
+ "pll_od_sel_max: %d\n"
+ "ss_level_max: %d\n"
+ "pll_ref_fmax: %d\n"
+ "pll_ref_fmin: %d\n"
+ "pll_vco_fmax: %d\n"
+ "pll_vco_fmin: %d\n"
+ "pll_out_fmax: %d\n"
+ "pll_out_fmin: %d\n"
+ "div_in_fmax: %d\n"
+ "div_out_fmax: %d\n"
+ "xd_out_fmax: %d\n\n",
+ clk_conf.pll_m_max, clk_conf.pll_m_min,
+ clk_conf.pll_n_max, clk_conf.pll_n_min,
+ clk_conf.pll_frac_range,
+ clk_conf.pll_od_sel_max, clk_conf.ss_level_max,
+ clk_conf.pll_ref_fmax, clk_conf.pll_ref_fmin,
+ clk_conf.pll_vco_fmax, clk_conf.pll_vco_fmin,
+ clk_conf.pll_out_fmax, clk_conf.pll_out_fmin,
+ clk_conf.div_in_fmax, clk_conf.div_out_fmax,
+ clk_conf.xd_out_fmax);
+ break;
+ }
+}
+
+void lcd_clk_config_print(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ switch (lcd_drv->chip_type) {
+ case LCD_CHIP_AXG:
+ LCDPR("lcd clk config:\n"
+ "pll_m: %d\n"
+ "pll_n: %d\n"
+ "pll_frac: 0x%03x\n"
+ "pll_fvco: %dkHz\n"
+ "pll_od: %d\n"
+ "pll_out: %dkHz\n"
+ "xd: %d\n"
+ "fout: %dkHz\n"
+ "ss_level: %d\n\n",
+ clk_conf.pll_m, clk_conf.pll_n,
+ clk_conf.pll_frac, clk_conf.pll_fvco,
+ clk_conf.pll_od1_sel, clk_conf.pll_fout,
+ clk_conf.xd, clk_conf.fout, clk_conf.ss_level);
+ break;
+ default:
+ LCDPR("lcd clk config:\n"
+ "pll_m: %d\n"
+ "pll_n: %d\n"
+ "pll_frac: 0x%03x\n"
+ "pll_fvco: %dkHz\n"
+ "pll_od1: %d\n"
+ "pll_od2: %d\n"
+ "pll_od3: %d\n"
+ "pll_out: %dkHz\n"
+ "div_sel: %s(index %d)\n"
+ "xd: %d\n"
+ "fout: %dkHz\n"
+ "ss_level: %d\n\n",
+ clk_conf.pll_m, clk_conf.pll_n,
+ clk_conf.pll_frac, clk_conf.pll_fvco,
+ clk_conf.pll_od1_sel, clk_conf.pll_od2_sel,
+ clk_conf.pll_od3_sel, clk_conf.pll_fout,
+ lcd_clk_div_sel_table[clk_conf.div_sel],
+ clk_conf.div_sel, clk_conf.xd,
+ clk_conf.fout, clk_conf.ss_level);
+ break;
+ }
+}
+
+static void lcd_clk_config_chip_init(void)
+{
+ struct lcd_clk_config_s *cConf;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ cConf = get_lcd_clk_config();
+ switch (lcd_drv->chip_type) {
+ case LCD_CHIP_GXTVBB:
+ cConf->od_fb = PLL_FRAC_OD_FB_GXTVBB;
+ cConf->ss_level_max = SS_LEVEL_MAX_GXTVBB;
+ cConf->pll_m_max = PLL_M_MAX_GXTVBB;
+ cConf->pll_m_min = PLL_M_MIN_GXTVBB;
+ cConf->pll_n_max = PLL_N_MAX_GXTVBB;
+ cConf->pll_n_min = PLL_N_MIN_GXTVBB;
+ cConf->pll_frac_range = PLL_FRAC_RANGE_GXTVBB;
+ cConf->pll_od_sel_max = PLL_OD_SEL_MAX_GXTVBB;
+ cConf->pll_ref_fmax = PLL_FREF_MAX_GXTVBB;
+ cConf->pll_ref_fmin = PLL_FREF_MIN_GXTVBB;
+ cConf->pll_vco_fmax = PLL_VCO_MAX_GXTVBB;
+ cConf->pll_vco_fmin = PLL_VCO_MIN_GXTVBB;
+ cConf->pll_out_fmax = CLK_DIV_IN_MAX_GXTVBB;
+ cConf->pll_out_fmin = cConf->pll_vco_fmin / 16;
+ cConf->div_in_fmax = CLK_DIV_IN_MAX_GXTVBB;
+ cConf->div_out_fmax = CRT_VID_CLK_IN_MAX_GXTVBB;
+ cConf->xd_out_fmax = ENCL_CLK_IN_MAX_GXTVBB;
+ break;
+ case LCD_CHIP_GXL:
+ cConf->od_fb = PLL_FRAC_OD_FB_GXL;
+ cConf->ss_level_max = SS_LEVEL_MAX_GXL;
+ cConf->pll_m_max = PLL_M_MAX_GXL;
+ cConf->pll_m_min = PLL_M_MIN_GXL;
+ cConf->pll_n_max = PLL_N_MAX_GXL;
+ cConf->pll_n_min = PLL_N_MIN_GXL;
+ cConf->pll_frac_range = PLL_FRAC_RANGE_GXL;
+ cConf->pll_od_sel_max = PLL_OD_SEL_MAX_GXL;
+ cConf->pll_ref_fmax = PLL_FREF_MAX_GXL;
+ cConf->pll_ref_fmin = PLL_FREF_MIN_GXL;
+ cConf->pll_vco_fmax = PLL_VCO_MAX_GXL;
+ cConf->pll_vco_fmin = PLL_VCO_MIN_GXL;
+ cConf->pll_out_fmax = CLK_DIV_IN_MAX_GXL;
+ cConf->pll_out_fmin = cConf->pll_vco_fmin / 16;
+ cConf->div_in_fmax = CLK_DIV_IN_MAX_GXL;
+ cConf->div_out_fmax = CRT_VID_CLK_IN_MAX_GXL;
+ cConf->xd_out_fmax = ENCL_CLK_IN_MAX_GXL;
+ break;
+ case LCD_CHIP_GXM:
+ cConf->od_fb = PLL_FRAC_OD_FB_GXM;
+ cConf->ss_level_max = SS_LEVEL_MAX_GXM;
+ cConf->pll_m_max = PLL_M_MAX_GXM;
+ cConf->pll_m_min = PLL_M_MIN_GXM;
+ cConf->pll_n_max = PLL_N_MAX_GXM;
+ cConf->pll_n_min = PLL_N_MIN_GXM;
+ cConf->pll_frac_range = PLL_FRAC_RANGE_GXM;
+ cConf->pll_od_sel_max = PLL_OD_SEL_MAX_GXM;
+ cConf->pll_ref_fmax = PLL_FREF_MAX_GXM;
+ cConf->pll_ref_fmin = PLL_FREF_MIN_GXM;
+ cConf->pll_vco_fmax = PLL_VCO_MAX_GXM;
+ cConf->pll_vco_fmin = PLL_VCO_MIN_GXM;
+ cConf->pll_out_fmax = CLK_DIV_IN_MAX_GXM;
+ cConf->pll_out_fmin = cConf->pll_vco_fmin / 16;
+ cConf->div_in_fmax = CLK_DIV_IN_MAX_GXM;
+ cConf->div_out_fmax = CRT_VID_CLK_IN_MAX_GXM;
+ cConf->xd_out_fmax = ENCL_CLK_IN_MAX_GXM;
+ break;
+ case LCD_CHIP_TXL:
+ cConf->od_fb = PLL_FRAC_OD_FB_TXL;
+ cConf->ss_level_max = SS_LEVEL_MAX_TXL;
+ cConf->pll_m_max = PLL_M_MAX_TXL;
+ cConf->pll_m_min = PLL_M_MIN_TXL;
+ cConf->pll_n_max = PLL_N_MAX_TXL;
+ cConf->pll_n_min = PLL_N_MIN_TXL;
+ cConf->pll_frac_range = PLL_FRAC_RANGE_TXL;
+ cConf->pll_od_sel_max = PLL_OD_SEL_MAX_TXL;
+ cConf->pll_ref_fmax = PLL_FREF_MAX_TXL;
+ cConf->pll_ref_fmin = PLL_FREF_MIN_TXL;
+ cConf->pll_vco_fmax = PLL_VCO_MAX_TXL;
+ cConf->pll_vco_fmin = PLL_VCO_MIN_TXL;
+ cConf->pll_out_fmax = CLK_DIV_IN_MAX_TXL;
+ cConf->pll_out_fmin = cConf->pll_vco_fmin / 16;
+ cConf->div_in_fmax = CLK_DIV_IN_MAX_TXL;
+ cConf->div_out_fmax = CRT_VID_CLK_IN_MAX_TXL;
+ cConf->xd_out_fmax = ENCL_CLK_IN_MAX_TXL;
+ break;
+ case LCD_CHIP_TXLX:
+ cConf->od_fb = PLL_FRAC_OD_FB_TXLX;
+ cConf->ss_level_max = SS_LEVEL_MAX_TXLX;
+ cConf->pll_m_max = PLL_M_MAX_TXLX;
+ cConf->pll_m_min = PLL_M_MIN_TXLX;
+ cConf->pll_n_max = PLL_N_MAX_TXLX;
+ cConf->pll_n_min = PLL_N_MIN_TXLX;
+ cConf->pll_frac_range = PLL_FRAC_RANGE_TXLX;
+ cConf->pll_od_sel_max = PLL_OD_SEL_MAX_TXLX;
+ cConf->pll_ref_fmax = PLL_FREF_MAX_TXLX;
+ cConf->pll_ref_fmin = PLL_FREF_MIN_TXLX;
+ cConf->pll_vco_fmax = PLL_VCO_MAX_TXLX;
+ cConf->pll_vco_fmin = PLL_VCO_MIN_TXLX;
+ cConf->pll_out_fmax = CLK_DIV_IN_MAX_TXLX;
+ cConf->pll_out_fmin = cConf->pll_vco_fmin / 16;
+ cConf->div_in_fmax = CLK_DIV_IN_MAX_TXLX;
+ cConf->div_out_fmax = CRT_VID_CLK_IN_MAX_TXLX;
+ cConf->xd_out_fmax = ENCL_CLK_IN_MAX_TXLX;
+ break;
+ case LCD_CHIP_AXG:
+ cConf->od_fb = PLL_FRAC_OD_FB_AXG;
+ cConf->ss_level_max = SS_LEVEL_MAX_AXG;
+ cConf->pll_m_max = PLL_M_MAX_AXG;
+ cConf->pll_m_min = PLL_M_MIN_AXG;
+ cConf->pll_n_max = PLL_N_MAX_AXG;
+ cConf->pll_n_min = PLL_N_MIN_AXG;
+ cConf->pll_frac_range = PLL_FRAC_RANGE_AXG;
+ cConf->pll_od_sel_max = PLL_OD_SEL_MAX_AXG;
+ cConf->pll_ref_fmax = PLL_FREF_MAX_AXG;
+ cConf->pll_ref_fmin = PLL_FREF_MIN_AXG;
+ cConf->pll_vco_fmax = PLL_VCO_MAX_AXG;
+ cConf->pll_vco_fmin = PLL_VCO_MIN_AXG;
+ cConf->pll_out_fmax = CRT_VID_CLK_IN_MAX_AXG;
+ cConf->pll_out_fmin = cConf->pll_vco_fmin /
+ od_table[cConf->pll_od_sel_max - 1];
+ cConf->div_post_out_fmax = CRT_VID_CLK_IN_MAX_AXG;
+ cConf->xd_out_fmax = ENCL_CLK_IN_MAX_AXG;
+ break;
+ default:
+ LCDPR("%s invalid chip type\n", __func__);
+ break;
+ }
+ if (lcd_debug_print_flag > 0)
+ lcd_clk_config_init_print();
+}
+
+/* lcd controller operation */
+static int lcd_pll_wait_lock(unsigned int reg, unsigned int lock_bit)
+{
+ unsigned int pll_lock;
+ int wait_loop = PLL_WAIT_LOCK_CNT; /* 200 */
+ int ret = 0;
+
+ do {
+ udelay(50);
+ pll_lock = lcd_hiu_getb(reg, lock_bit, 1);
+ wait_loop--;
+ } while ((pll_lock == 0) && (wait_loop > 0));
+ if (pll_lock == 0)
+ ret = -1;
+ LCDPR("%s: pll_lock=%d, wait_loop=%d\n",
+ __func__, pll_lock, (PLL_WAIT_LOCK_CNT - wait_loop));
+ return ret;
+}
+
+static void lcd_set_pll_ss_gxtvbb(struct lcd_clk_config_s *cConf)
+{
+ if ((cConf->pll_fvco >= 5500000) && (cConf->pll_fvco <= 6000000)) {
+ switch (cConf->ss_level) {
+ case 1: /* +/- 0.3% */
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x12dc5080);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xb01da72c);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+ break;
+ case 2: /* +/- 0.5% */
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x12dc5080);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xa85da72c);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+ break;
+ case 3: /* +/- 0.9% */
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x12dc5080);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xb09da72c);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+ break;
+ case 4: /* +/- 1.2% */
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x12dc5080);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xb0dda72c);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+ break;
+ default: /* disable */
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x12dc5081);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0x801da72c);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x71486980);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00002a55);
+ break;
+ }
+ } else {
+ switch (cConf->ss_level) {
+ case 1: /* +/- 0.3% */
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x0d1c5090);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xb01da72c);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+ break;
+ case 2: /* +/- 0.5% */
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x0d1c5090);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xa85da72c);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+ break;
+ case 3: /* +/- 0.9% */
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x0d1c5090);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xb09da72c);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+ break;
+ case 4: /* +/- 1.2% */
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x0d1c5090);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0xb0dda72c);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x51486980);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00082a55);
+ break;
+ default: /* disable */
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0x801da72c);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x71486980);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00002a55);
+ break;
+ }
+ }
+ LCDPR("set pll spread spectrum: %s\n",
+ lcd_pll_ss_table_gxtvbb[cConf->ss_level]);
+}
+
+static void lcd_pll_reset_gxtvbb(void)
+{
+ lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 1, LCD_PLL_RST_GXTVBB, 1);
+ udelay(10);
+ lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 0, LCD_PLL_RST_GXTVBB, 1);
+}
+
+static void lcd_set_pll_gxtvbb(struct lcd_clk_config_s *cConf)
+{
+ unsigned int pll_ctrl, pll_ctrl2;
+ int ret;
+
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s\n", __func__);
+ pll_ctrl = ((1 << LCD_PLL_EN_GXTVBB) |
+ (1 << 27) | /* DPLL_BGP_EN */
+ (cConf->pll_n << LCD_PLL_N_GXTVBB) |
+ (cConf->pll_m << LCD_PLL_M_GXTVBB));
+
+ pll_ctrl2 = ((cConf->pll_od3_sel << LCD_PLL_OD3_GXTVBB) |
+ (cConf->pll_od2_sel << LCD_PLL_OD2_GXTVBB) |
+ (cConf->pll_od1_sel << LCD_PLL_OD1_GXTVBB));
+ pll_ctrl2 |= ((1 << 14) | (cConf->pll_frac << 0));
+
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL, pll_ctrl | (1 << LCD_PLL_RST_GXTVBB));
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL2, pll_ctrl2);
+ if ((cConf->pll_fvco >= 5500000) && (cConf->pll_fvco <= 6000000)) {
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x12dc5081);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0x801da72c);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x71486980);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00002a55);
+ } else {
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0x801da72c);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x71486980);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x00002a55);
+ }
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL, pll_ctrl);
+
+ ret = lcd_pll_wait_lock(HHI_HDMI_PLL_CNTL, LCD_PLL_LOCK_GXTVBB);
+ if (ret)
+ LCDERR("hpll lock failed\n");
+
+ if (cConf->ss_level > 0)
+ lcd_set_pll_ss_gxtvbb(cConf);
+}
+
+static void lcd_update_pll_frac_gxtvbb(struct lcd_clk_config_s *cConf)
+{
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s\n", __func__);
+
+ lcd_hiu_setb(HHI_HDMI_PLL_CNTL2, cConf->pll_frac, 0, 12);
+}
+
+static void lcd_set_pll_ss_txl(struct lcd_clk_config_s *cConf)
+{
+ unsigned int pll_ctrl3, pll_ctrl4;
+
+ pll_ctrl3 = lcd_hiu_read(HHI_HDMI_PLL_CNTL3);
+ pll_ctrl4 = lcd_hiu_read(HHI_HDMI_PLL_CNTL4);
+
+ switch (cConf->ss_level) {
+ case 1: /* +/- 0.3% */
+ pll_ctrl3 &= ~(0xf << 10);
+ pll_ctrl3 |= ((1 << 14) | (0xc << 10));
+ pll_ctrl4 &= ~(0x3 << 2);
+ break;
+ case 2: /* +/- 0.4% */
+ pll_ctrl3 &= ~(0xf << 10);
+ pll_ctrl3 |= ((1 << 14) | (0x8 << 10));
+ pll_ctrl4 &= ~(0x3 << 2);
+ pll_ctrl4 |= (0x1 << 2);
+ break;
+ case 3: /* +/- 0.9% */
+ pll_ctrl3 &= ~(0xf << 10);
+ pll_ctrl3 |= ((1 << 14) | (0xc << 10));
+ pll_ctrl4 &= ~(0x3 << 2);
+ pll_ctrl4 |= (0x2 << 2);
+ break;
+ case 4: /* +/- 1.2% */
+ pll_ctrl3 &= ~(0xf << 10);
+ pll_ctrl3 |= ((1 << 14) | (0xc << 10));
+ pll_ctrl4 &= ~(0x3 << 2);
+ pll_ctrl4 |= (0x3 << 2);
+ break;
+ default: /* disable */
+ pll_ctrl3 &= ~((0xf << 10) | (1 << 14));
+ pll_ctrl4 &= ~(0x3 << 2);
+ break;
+ }
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, pll_ctrl3);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, pll_ctrl4);
+
+ LCDPR("set pll spread spectrum: %s\n",
+ lcd_pll_ss_table_txl[cConf->ss_level]);
+}
+
+static void lcd_pll_reset_txl(void)
+{
+ lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 1, LCD_PLL_RST_TXL, 1);
+ udelay(10);
+ lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 0, LCD_PLL_RST_TXL, 1);
+}
+
+static void lcd_set_pll_txl(struct lcd_clk_config_s *cConf)
+{
+ unsigned int pll_ctrl, pll_ctrl2, pll_ctrl3;
+ int ret;
+
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s\n", __func__);
+ pll_ctrl = ((1 << LCD_PLL_EN_TXL) |
+ (cConf->pll_n << LCD_PLL_N_TXL) |
+ (cConf->pll_m << LCD_PLL_M_TXL));
+ pll_ctrl2 = 0x800ca000;
+ pll_ctrl2 |= ((1 << 12) | (cConf->pll_frac << 0));
+ pll_ctrl3 = 0x860330c4 | (cConf->od_fb << 30);
+ pll_ctrl3 |= ((cConf->pll_od3_sel << LCD_PLL_OD3_TXL) |
+ (cConf->pll_od2_sel << LCD_PLL_OD2_TXL) |
+ (cConf->pll_od1_sel << LCD_PLL_OD1_TXL));
+
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL, pll_ctrl);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL2, pll_ctrl2);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, pll_ctrl3);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x001fa729);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x01a31500);
+ lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 1, LCD_PLL_RST_TXL, 1);
+ lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 0, LCD_PLL_RST_TXL, 1);
+
+ ret = lcd_pll_wait_lock(HHI_HDMI_PLL_CNTL, LCD_PLL_LOCK_TXL);
+ if (ret)
+ LCDERR("hpll lock failed\n");
+
+ if (cConf->ss_level > 0)
+ lcd_set_pll_ss_txl(cConf);
+}
+
+static void lcd_update_pll_frac_txl(struct lcd_clk_config_s *cConf)
+{
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s\n", __func__);
+
+ lcd_hiu_setb(HHI_HDMI_PLL_CNTL2, cConf->pll_frac, 0, 12);
+}
+
+static void lcd_set_pll_ss_txlx(struct lcd_clk_config_s *cConf)
+{
+ unsigned int pll_ctrl3, pll_ctrl4, pll_ctrl5;
+
+ pll_ctrl3 = lcd_hiu_read(HHI_HDMI_PLL_CNTL3);
+ pll_ctrl4 = lcd_hiu_read(HHI_HDMI_PLL_CNTL4);
+ pll_ctrl5 = lcd_hiu_read(HHI_HDMI_PLL_CNTL5);
+
+ switch (cConf->ss_level) {
+ case 1: /* +/- 0.3% */
+ pll_ctrl3 &= ~(0xf << 10);
+ pll_ctrl3 |= ((1 << 14) | (0x6 << 10));
+ pll_ctrl4 &= ~(0x3 << 2);
+ pll_ctrl4 |= (0x1 << 2);
+ pll_ctrl5 &= ~(0x3 << 30);
+ break;
+ case 2: /* +/- 0.5% */
+ pll_ctrl3 &= ~(0xf << 10);
+ pll_ctrl3 |= ((1 << 14) | (0xa << 10));
+ pll_ctrl4 &= ~(0x3 << 2);
+ pll_ctrl4 |= (0x1 << 2);
+ pll_ctrl5 &= ~(0x3 << 30);
+ break;
+ case 3: /* +/- 1.0% */
+ pll_ctrl3 &= ~(0xf << 10);
+ pll_ctrl3 |= ((1 << 14) | (0xa << 10));
+ pll_ctrl4 &= ~(0x3 << 2);
+ pll_ctrl4 |= (0x3 << 2);
+ pll_ctrl5 &= ~(0x3 << 30);
+ break;
+ case 4: /* +/- 1.6% */
+ pll_ctrl3 &= ~(0xf << 10);
+ pll_ctrl3 |= ((1 << 14) | (0x8 << 10));
+ pll_ctrl4 &= ~(0x3 << 2);
+ pll_ctrl4 |= (0x3 << 2);
+ pll_ctrl5 &= ~(0x3 << 30);
+ pll_ctrl5 |= (0x1 << 30);
+ break;
+ case 5: /* +/- 3.0% */
+ pll_ctrl3 &= ~(0xf << 10);
+ pll_ctrl3 |= ((1 << 14) | (0xa << 10));
+ pll_ctrl4 &= ~(0x3 << 2);
+ pll_ctrl4 |= (0x3 << 2);
+ pll_ctrl5 &= ~(0x3 << 30);
+ pll_ctrl5 |= (0x2 << 30);
+ break;
+ default: /* disable */
+ pll_ctrl3 &= ~((0xf << 10) | (1 << 14));
+ pll_ctrl4 &= ~(0x3 << 2);
+ pll_ctrl5 &= ~(0x3 << 30);
+ break;
+ }
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, pll_ctrl3);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, pll_ctrl4);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL5, pll_ctrl5);
+
+ LCDPR("set pll spread spectrum: %s\n",
+ lcd_pll_ss_table_txlx[cConf->ss_level]);
+}
+
+static void lcd_set_pll_txlx(struct lcd_clk_config_s *cConf)
+{
+ unsigned int pll_ctrl, pll_ctrl2, pll_ctrl3;
+ int ret;
+
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s\n", __func__);
+ pll_ctrl = ((1 << LCD_PLL_EN_TXL) |
+ (cConf->pll_n << LCD_PLL_N_TXL) |
+ (cConf->pll_m << LCD_PLL_M_TXL));
+ pll_ctrl2 = 0x800ca000;
+ pll_ctrl2 |= ((1 << 12) | (cConf->pll_frac << 0));
+ pll_ctrl3 = 0x860030c4 | (cConf->od_fb << 30);
+ pll_ctrl3 |= ((cConf->pll_od3_sel << LCD_PLL_OD3_TXL) |
+ (cConf->pll_od2_sel << LCD_PLL_OD2_TXL) |
+ (cConf->pll_od1_sel << LCD_PLL_OD1_TXL));
+
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL, pll_ctrl);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL2, pll_ctrl2);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL3, pll_ctrl3);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL5, 0x001fa729);
+ lcd_hiu_write(HHI_HDMI_PLL_CNTL6, 0x01a31500);
+ lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 1, LCD_PLL_RST_TXL, 1);
+ lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 0, LCD_PLL_RST_TXL, 1);
+
+ ret = lcd_pll_wait_lock(HHI_HDMI_PLL_CNTL, LCD_PLL_LOCK_TXL);
+ if (ret)
+ LCDERR("hpll lock failed\n");
+
+ if (cConf->ss_level > 0)
+ lcd_set_pll_ss_txlx(cConf);
+}
+
+static unsigned int lcd_clk_div_g9_gxtvbb[][3] = {
+ /* divider, shift_val, shift_sel */
+ {CLK_DIV_SEL_1, 0xffff, 0,},
+ {CLK_DIV_SEL_2, 0x0aaa, 0,},
+ {CLK_DIV_SEL_3, 0x0db6, 0,},
+ {CLK_DIV_SEL_3p5, 0x36cc, 1,},
+ {CLK_DIV_SEL_3p75, 0x6666, 2,},
+ {CLK_DIV_SEL_4, 0x0ccc, 0,},
+ {CLK_DIV_SEL_5, 0x739c, 2,},
+ {CLK_DIV_SEL_6, 0x0e38, 0,},
+ {CLK_DIV_SEL_6p25, 0x0000, 3,},
+ {CLK_DIV_SEL_7, 0x3c78, 1,},
+ {CLK_DIV_SEL_7p5, 0x78f0, 2,},
+ {CLK_DIV_SEL_12, 0x0fc0, 0,},
+ {CLK_DIV_SEL_14, 0x3f80, 1,},
+ {CLK_DIV_SEL_15, 0x7f80, 2,},
+ {CLK_DIV_SEL_2p5, 0x5294, 2,},
+ {CLK_DIV_SEL_MAX, 0xffff, 0,},
+};
+
+static void lcd_set_clk_div_g9_gxtvbb(struct lcd_clk_config_s *cConf)
+{
+ unsigned int shift_val, shift_sel;
+ int i;
+
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s\n", __func__);
+
+ lcd_hiu_setb(HHI_VIID_CLK_CNTL, 0, VCLK2_EN, 1);
+ udelay(5);
+
+ /* Disable the div output clock */
+ lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 0, 19, 1);
+ lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 0, 15, 1);
+
+ i = 0;
+ while (lcd_clk_div_g9_gxtvbb[i][0] != CLK_DIV_SEL_MAX) {
+ if (cConf->div_sel == lcd_clk_div_g9_gxtvbb[i][0])
+ break;
+ i++;
+ }
+ if (lcd_clk_div_g9_gxtvbb[i][0] == CLK_DIV_SEL_MAX)
+ LCDERR("invalid clk divider\n");
+ shift_val = lcd_clk_div_g9_gxtvbb[i][1];
+ shift_sel = lcd_clk_div_g9_gxtvbb[i][2];
+
+ if (shift_val == 0xffff) { /* if divide by 1 */
+ lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 1, 18, 1);
+ } else {
+ lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 0, 18, 1);
+ lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 0, 16, 2);
+ lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 0, 15, 1);
+ lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 0, 0, 14);
+
+ lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, shift_sel, 16, 2);
+ lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 1, 15, 1);
+ lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, shift_val, 0, 14);
+ lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 0, 15, 1);
+ }
+ /* Enable the final output clock */
+ lcd_hiu_setb(HHI_VID_PLL_CLK_DIV, 1, 19, 1);
+}
+
+static void lcd_set_vclk_crt(int lcd_type, struct lcd_clk_config_s *cConf)
+{
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s\n", __func__);
+
+ /* setup the XD divider value */
+ lcd_hiu_setb(HHI_VIID_CLK_DIV, (cConf->xd-1), VCLK2_XD, 8);
+ udelay(5);
+
+ /* select vid_pll_clk */
+ lcd_hiu_setb(HHI_VIID_CLK_CNTL, 0, VCLK2_CLK_IN_SEL, 3);
+ lcd_hiu_setb(HHI_VIID_CLK_CNTL, 1, VCLK2_EN, 1);
+ udelay(2);
+
+ /* [15:12] encl_clk_sel, select vclk2_div1 */
+ lcd_hiu_setb(HHI_VIID_CLK_DIV, 8, ENCL_CLK_SEL, 4);
+ /* release vclk2_div_reset and enable vclk2_div */
+ lcd_hiu_setb(HHI_VIID_CLK_DIV, 1, VCLK2_XD_EN, 2);
+ udelay(5);
+
+ lcd_hiu_setb(HHI_VIID_CLK_CNTL, 1, VCLK2_DIV1_EN, 1);
+ lcd_hiu_setb(HHI_VIID_CLK_CNTL, 1, VCLK2_SOFT_RST, 1);
+ udelay(10);
+ lcd_hiu_setb(HHI_VIID_CLK_CNTL, 0, VCLK2_SOFT_RST, 1);
+ udelay(5);
+
+ /* enable CTS_ENCL clk gate */
+ lcd_hiu_setb(HHI_VID_CLK_CNTL2, 1, ENCL_GATE_VCLK, 1);
+}
+
+static unsigned int clk_div_calc_g9_gxtvbb(unsigned int clk,
+ unsigned int div_sel, int dir)
+{
+ unsigned int clk_ret;
+
+ switch (div_sel) {
+ case CLK_DIV_SEL_1:
+ clk_ret = clk;
+ break;
+ case CLK_DIV_SEL_2:
+ if (dir == CLK_DIV_I2O)
+ clk_ret = clk / 2;
+ else
+ clk_ret = clk * 2;
+ break;
+ case CLK_DIV_SEL_3:
+ if (dir == CLK_DIV_I2O)
+ clk_ret = clk / 3;
+ else
+ clk_ret = clk * 3;
+ break;
+ case CLK_DIV_SEL_3p5:
+ if (dir == CLK_DIV_I2O)
+ clk_ret = clk * 2 / 7;
+ else
+ clk_ret = clk * 7 / 2;
+ break;
+ case CLK_DIV_SEL_3p75:
+ if (dir == CLK_DIV_I2O)
+ clk_ret = clk * 4 / 15;
+ else
+ clk_ret = clk * 15 / 4;
+ break;
+ case CLK_DIV_SEL_4:
+ if (dir == CLK_DIV_I2O)
+ clk_ret = clk / 4;
+ else
+ clk_ret = clk * 4;
+ break;
+ case CLK_DIV_SEL_5:
+ if (dir == CLK_DIV_I2O)
+ clk_ret = clk / 5;
+ else
+ clk_ret = clk * 5;
+ break;
+ case CLK_DIV_SEL_6:
+ if (dir == CLK_DIV_I2O)
+ clk_ret = clk / 6;
+ else
+ clk_ret = clk * 6;
+ break;
+ case CLK_DIV_SEL_6p25:
+ if (dir == CLK_DIV_I2O)
+ clk_ret = clk * 4 / 25;
+ else
+ clk_ret = clk * 25 / 4;
+ break;
+ case CLK_DIV_SEL_7:
+ if (dir == CLK_DIV_I2O)
+ clk_ret = clk / 7;
+ else
+ clk_ret = clk * 7;
+ break;
+ case CLK_DIV_SEL_7p5:
+ if (dir == CLK_DIV_I2O)
+ clk_ret = clk * 2 / 15;
+ else
+ clk_ret = clk * 15 / 2;
+ break;
+ case CLK_DIV_SEL_12:
+ if (dir == CLK_DIV_I2O)
+ clk_ret = clk / 12;
+ else
+ clk_ret = clk * 12;
+ break;
+ case CLK_DIV_SEL_14:
+ if (dir == CLK_DIV_I2O)
+ clk_ret = clk / 14;
+ else
+ clk_ret = clk * 14;
+ break;
+ case CLK_DIV_SEL_15:
+ if (dir == CLK_DIV_I2O)
+ clk_ret = clk / 15;
+ else
+ clk_ret = clk * 15;
+ break;
+ case CLK_DIV_SEL_2p5:
+ if (dir == CLK_DIV_I2O)
+ clk_ret = clk * 2 / 5;
+ else
+ clk_ret = clk * 5 / 2;
+ break;
+ default:
+ clk_ret = clk;
+ LCDERR("clk_div_sel: Invalid parameter\n");
+ break;
+ }
+
+ return clk_ret;
+}
+
+static unsigned int clk_div_get_g9_gxtvbb(unsigned int clk_div)
+{
+ unsigned int div_sel;
+
+ /* div * 100 */
+ switch (clk_div) {
+ case 375:
+ div_sel = CLK_DIV_SEL_3p75;
+ break;
+ case 750:
+ div_sel = CLK_DIV_SEL_7p5;
+ break;
+ case 1500:
+ div_sel = CLK_DIV_SEL_15;
+ break;
+ case 500:
+ div_sel = CLK_DIV_SEL_5;
+ break;
+ default:
+ div_sel = CLK_DIV_SEL_MAX;
+ break;
+ }
+ return div_sel;
+}
+
+static int check_pll_g9_gxtvbb(struct lcd_clk_config_s *cConf,
+ unsigned int pll_fout)
+{
+ unsigned int m, n;
+ unsigned int od1_sel, od2_sel, od3_sel, od1, od2, od3;
+ unsigned int pll_fod2_in, pll_fod3_in, pll_fvco;
+ unsigned int od_fb = 0, pll_frac;
+ int done;
+
+ done = 0;
+ if ((pll_fout > cConf->pll_out_fmax) ||
+ (pll_fout < cConf->pll_out_fmin)) {
+ return done;
+ }
+ for (od3_sel = cConf->pll_od_sel_max; od3_sel > 0; od3_sel--) {
+ od3 = od_table[od3_sel - 1];
+ pll_fod3_in = pll_fout * od3;
+ for (od2_sel = od3_sel; od2_sel > 0; od2_sel--) {
+ od2 = od_table[od2_sel - 1];
+ pll_fod2_in = pll_fod3_in * od2;
+ for (od1_sel = od2_sel; od1_sel > 0; od1_sel--) {
+ od1 = od_table[od1_sel - 1];
+ pll_fvco = pll_fod2_in * od1;
+ if ((pll_fvco < cConf->pll_vco_fmin) ||
+ (pll_fvco > cConf->pll_vco_fmax)) {
+ continue;
+ }
+ cConf->pll_od1_sel = od1_sel - 1;
+ cConf->pll_od2_sel = od2_sel - 1;
+ cConf->pll_od3_sel = od3_sel - 1;
+ cConf->pll_fout = pll_fout;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("od1=%d, od2=%d, od3=%d\n",
+ (od1_sel - 1), (od2_sel - 1),
+ (od3_sel - 1));
+ LCDPR("pll_fvco=%d\n", pll_fvco);
+ }
+ cConf->pll_fvco = pll_fvco;
+ n = 1;
+ od_fb = cConf->od_fb; /* pll default */
+ pll_fvco = pll_fvco / od_fb_table[od_fb + 1];
+ m = pll_fvco / cConf->fin;
+ pll_frac = (pll_fvco % cConf->fin) *
+ cConf->pll_frac_range / cConf->fin;
+ cConf->pll_m = m;
+ cConf->pll_n = n;
+ cConf->pll_frac = pll_frac;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("m=%d, n=%d, frac=%d\n",
+ m, n, pll_frac);
+ }
+ done = 1;
+ break;
+ }
+ }
+ }
+ return done;
+}
+
+static void lcd_clk_generate_g9_gxtvbb(struct lcd_config_s *pconf)
+{
+ unsigned int pll_fout;
+ unsigned int clk_div_in, clk_div_out;
+ unsigned int clk_div_sel, xd;
+ struct lcd_clk_config_s *cConf;
+ int done;
+
+ done = 0;
+ cConf = get_lcd_clk_config();
+ cConf->fout = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+ cConf->err_fmin = MAX_ERROR;
+
+ if (cConf->fout > cConf->xd_out_fmax) {
+ LCDERR("%s: wrong lcd_clk value %dkHz\n",
+ __func__, cConf->fout);
+ goto generate_clk_done_g9_gxtvbb;
+ }
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_TTL:
+ clk_div_sel = CLK_DIV_SEL_1;
+ cConf->xd_max = CRT_VID_DIV_MAX;
+ for (xd = 1; xd <= cConf->xd_max; xd++) {
+ clk_div_out = cConf->fout * xd;
+ if (clk_div_out > cConf->div_out_fmax)
+ continue;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("fout=%d, xd=%d, clk_div_out=%d\n",
+ cConf->fout, xd, clk_div_out);
+ }
+ clk_div_in = clk_div_calc_g9_gxtvbb(clk_div_out,
+ clk_div_sel, CLK_DIV_O2I);
+ if (clk_div_in > cConf->div_in_fmax)
+ continue;
+ cConf->xd = xd;
+ cConf->div_sel = clk_div_sel;
+ pll_fout = clk_div_in;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("clk_div_sel=%s(index %d), pll_fout=%d\n",
+ lcd_clk_div_sel_table[clk_div_sel],
+ clk_div_sel, pll_fout);
+ }
+ done = check_pll_g9_gxtvbb(cConf, pll_fout);
+ if (done)
+ goto generate_clk_done_g9_gxtvbb;
+ }
+ break;
+ case LCD_LVDS:
+ clk_div_sel = CLK_DIV_SEL_7;
+ xd = 1;
+ clk_div_out = cConf->fout * xd;
+ if (clk_div_out > cConf->div_out_fmax)
+ goto generate_clk_done_g9_gxtvbb;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("fout=%d, xd=%d, clk_div_out=%d\n",
+ cConf->fout, xd, clk_div_out);
+ }
+ clk_div_in = clk_div_calc_g9_gxtvbb(clk_div_out,
+ clk_div_sel, CLK_DIV_O2I);
+ if (clk_div_in > cConf->div_in_fmax)
+ goto generate_clk_done_g9_gxtvbb;
+ cConf->xd = xd;
+ cConf->div_sel = clk_div_sel;
+ pll_fout = clk_div_in;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("clk_div_sel=%s(index %d), pll_fout=%d\n",
+ lcd_clk_div_sel_table[clk_div_sel],
+ clk_div_sel, pll_fout);
+ }
+ done = check_pll_g9_gxtvbb(cConf, pll_fout);
+ if (done)
+ goto generate_clk_done_g9_gxtvbb;
+ break;
+ case LCD_VBYONE:
+ cConf->div_sel_max = CLK_DIV_SEL_MAX;
+ cConf->xd_max = CRT_VID_DIV_MAX;
+ pll_fout = pconf->lcd_control.vbyone_config->bit_rate / 1000;
+ clk_div_in = pll_fout;
+ if (clk_div_in > cConf->div_in_fmax)
+ goto generate_clk_done_g9_gxtvbb;
+ if (lcd_debug_print_flag == 2)
+ LCDPR("pll_fout=%d\n", pll_fout);
+ if ((clk_div_in / cConf->fout) > 15)
+ cConf->xd = 4;
+ else
+ cConf->xd = 1;
+ clk_div_out = cConf->fout * cConf->xd;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("clk_div_in=%d, fout=%d, xd=%d, clk_div_out=%d\n",
+ clk_div_in, cConf->fout,
+ clk_div_out, cConf->xd);
+ }
+ if (clk_div_out > cConf->div_out_fmax)
+ goto generate_clk_done_g9_gxtvbb;
+ clk_div_sel = clk_div_get_g9_gxtvbb(
+ clk_div_in * 100 / clk_div_out);
+ cConf->div_sel = clk_div_sel;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("clk_div_sel=%s(index %d)\n",
+ lcd_clk_div_sel_table[clk_div_sel],
+ cConf->div_sel);
+ }
+ done = check_pll_g9_gxtvbb(cConf, pll_fout);
+ break;
+ default:
+ break;
+ }
+
+generate_clk_done_g9_gxtvbb:
+ if (done) {
+ pconf->lcd_timing.pll_ctrl =
+ (cConf->pll_od1_sel << PLL_CTRL_OD1) |
+ (cConf->pll_od2_sel << PLL_CTRL_OD2) |
+ (cConf->pll_od3_sel << PLL_CTRL_OD3) |
+ (cConf->pll_n << PLL_CTRL_N) |
+ (cConf->pll_m << PLL_CTRL_M);
+ pconf->lcd_timing.div_ctrl =
+ (cConf->div_sel << DIV_CTRL_DIV_SEL) |
+ (cConf->xd << DIV_CTRL_XD);
+ pconf->lcd_timing.clk_ctrl =
+ (cConf->pll_frac << CLK_CTRL_FRAC);
+ } else {
+ pconf->lcd_timing.pll_ctrl =
+ (1 << PLL_CTRL_OD1) |
+ (1 << PLL_CTRL_OD2) |
+ (1 << PLL_CTRL_OD3) |
+ (1 << PLL_CTRL_N) |
+ (50 << PLL_CTRL_M);
+ pconf->lcd_timing.div_ctrl =
+ (CLK_DIV_SEL_1 << DIV_CTRL_DIV_SEL) |
+ (7 << DIV_CTRL_XD);
+ pconf->lcd_timing.clk_ctrl = (0 << CLK_CTRL_FRAC);
+ LCDERR("Out of clock range, reset to default setting\n");
+ }
+}
+
+static void lcd_pll_frac_generate_g9_gxtvbb(struct lcd_config_s *pconf)
+{
+ unsigned int pll_fout;
+ unsigned int clk_div_in, clk_div_out, clk_div_sel;
+ unsigned int od1, od2, od3, pll_fvco;
+ unsigned int m, n, od_fb, frac, offset, temp;
+ struct lcd_clk_config_s *cConf;
+
+ cConf = get_lcd_clk_config();
+ cConf->fout = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+ clk_div_sel = cConf->div_sel;
+ od1 = od_table[cConf->pll_od1_sel];
+ od2 = od_table[cConf->pll_od2_sel];
+ od3 = od_table[cConf->pll_od3_sel];
+ m = cConf->pll_m;
+ n = cConf->pll_n;
+
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("m=%d, n=%d, od1=%d, od2=%d, od3=%d\n",
+ m, n, cConf->pll_od1_sel, cConf->pll_od2_sel,
+ cConf->pll_od3_sel);
+ LCDPR("clk_div_sel=%s(index %d), xd=%d\n",
+ lcd_clk_div_sel_table[clk_div_sel],
+ clk_div_sel, cConf->xd);
+ }
+ if (cConf->fout > cConf->xd_out_fmax) {
+ LCDERR("%s: wrong lcd_clk value %dkHz\n",
+ __func__, cConf->fout);
+ return;
+ }
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s pclk=%d\n", __func__, cConf->fout);
+
+ clk_div_out = cConf->fout * cConf->xd;
+ if (clk_div_out > cConf->div_out_fmax) {
+ LCDERR("%s: wrong clk_div_out value %dkHz\n",
+ __func__, clk_div_out);
+ return;
+ }
+
+ clk_div_in =
+ clk_div_calc_g9_gxtvbb(clk_div_out, clk_div_sel, CLK_DIV_O2I);
+ if (clk_div_in > cConf->div_in_fmax) {
+ LCDERR("%s: wrong clk_div_in value %dkHz\n",
+ __func__, clk_div_in);
+ return;
+ }
+
+ pll_fout = clk_div_in;
+ if ((pll_fout > cConf->pll_out_fmax) ||
+ (pll_fout < cConf->pll_out_fmin)) {
+ LCDERR("%s: wrong pll_fout value %dkHz\n", __func__, pll_fout);
+ return;
+ }
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s pll_fout=%d\n", __func__, pll_fout);
+
+ pll_fvco = pll_fout * od1 * od2 * od3;
+ if ((pll_fvco < cConf->pll_vco_fmin) ||
+ (pll_fvco > cConf->pll_vco_fmax)) {
+ LCDERR("%s: wrong pll_fvco value %dkHz\n", __func__, pll_fvco);
+ return;
+ }
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s pll_fvco=%d\n", __func__, pll_fvco);
+
+ cConf->pll_fvco = pll_fvco;
+ od_fb = cConf->od_fb; /* pll default */
+ pll_fvco = pll_fvco / od_fb_table[od_fb + 1];
+ temp = cConf->fin * m / n;
+ if (pll_fvco >= temp) {
+ temp = pll_fvco - temp;
+ offset = 0;
+ } else {
+ temp = temp - pll_fvco;
+ offset = 1;
+ }
+ if (temp >= (2 * cConf->fin)) {
+ LCDERR("%s: pll changing %dkHz is too much\n",
+ __func__, temp);
+ return;
+ }
+ frac = temp * cConf->pll_frac_range * n / cConf->fin;
+ cConf->pll_frac = frac | (offset << 11);
+ if (lcd_debug_print_flag)
+ LCDPR("lcd_pll_frac_generate frac=%d\n", frac);
+}
+
+static int check_pll_txl(struct lcd_clk_config_s *cConf,
+ unsigned int pll_fout)
+{
+ unsigned int m, n;
+ unsigned int od1_sel, od2_sel, od3_sel, od1, od2, od3;
+ unsigned int pll_fod2_in, pll_fod3_in, pll_fvco;
+ unsigned int od_fb = 0, pll_frac;
+ int done;
+
+ done = 0;
+ if ((pll_fout > cConf->pll_out_fmax) ||
+ (pll_fout < cConf->pll_out_fmin)) {
+ return done;
+ }
+ for (od3_sel = cConf->pll_od_sel_max; od3_sel > 0; od3_sel--) {
+ od3 = od_table[od3_sel - 1];
+ pll_fod3_in = pll_fout * od3;
+ for (od2_sel = od3_sel; od2_sel > 0; od2_sel--) {
+ od2 = od_table[od2_sel - 1];
+ pll_fod2_in = pll_fod3_in * od2;
+ for (od1_sel = od2_sel; od1_sel > 0; od1_sel--) {
+ od1 = od_table[od1_sel - 1];
+ pll_fvco = pll_fod2_in * od1;
+ if ((pll_fvco < cConf->pll_vco_fmin) ||
+ (pll_fvco > cConf->pll_vco_fmax)) {
+ continue;
+ }
+ cConf->pll_od1_sel = od1_sel - 1;
+ cConf->pll_od2_sel = od2_sel - 1;
+ cConf->pll_od3_sel = od3_sel - 1;
+ cConf->pll_fout = pll_fout;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("od1=%d, od2=%d, od3=%d\n",
+ (od1_sel - 1), (od2_sel - 1),
+ (od3_sel - 1));
+ LCDPR("pll_fvco=%d\n", pll_fvco);
+ }
+ cConf->pll_fvco = pll_fvco;
+ n = 1;
+ /* update od_fb to 1 for ss width */
+ od_fb = cConf->od_fb; /* pll default */
+ pll_fvco = pll_fvco / od_fb_table[od_fb];
+ m = pll_fvco / cConf->fin;
+ pll_frac = (pll_fvco % cConf->fin) *
+ cConf->pll_frac_range / cConf->fin;
+ cConf->pll_m = m;
+ cConf->pll_n = n;
+ cConf->pll_frac = pll_frac;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("m=%d, n=%d, frac=%d\n",
+ m, n, pll_frac);
+ }
+ done = 1;
+ break;
+ }
+ }
+ }
+ return done;
+}
+
+static void lcd_clk_generate_txl(struct lcd_config_s *pconf)
+{
+ unsigned int pll_fout;
+ unsigned int clk_div_in, clk_div_out;
+ unsigned int clk_div_sel, xd;
+ struct lcd_clk_config_s *cConf;
+ int done;
+
+ done = 0;
+ cConf = get_lcd_clk_config();
+ cConf->fout = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+ cConf->err_fmin = MAX_ERROR;
+
+ if (cConf->fout > cConf->xd_out_fmax) {
+ LCDERR("%s: wrong lcd_clk value %dkHz\n",
+ __func__, cConf->fout);
+ goto generate_clk_done_txl;
+ }
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_TTL:
+ clk_div_sel = CLK_DIV_SEL_1;
+ cConf->xd_max = CRT_VID_DIV_MAX;
+ for (xd = 1; xd <= cConf->xd_max; xd++) {
+ clk_div_out = cConf->fout * xd;
+ if (clk_div_out > cConf->div_out_fmax)
+ continue;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("fout=%d, xd=%d, clk_div_out=%d\n",
+ cConf->fout, xd, clk_div_out);
+ }
+ clk_div_in = clk_div_calc_g9_gxtvbb(clk_div_out,
+ clk_div_sel, CLK_DIV_O2I);
+ if (clk_div_in > cConf->div_in_fmax)
+ continue;
+ cConf->xd = xd;
+ cConf->div_sel = clk_div_sel;
+ pll_fout = clk_div_in;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("clk_div_sel=%s(index %d), pll_fout=%d\n",
+ lcd_clk_div_sel_table[clk_div_sel],
+ clk_div_sel, pll_fout);
+ }
+ done = check_pll_txl(cConf, pll_fout);
+ if (done)
+ goto generate_clk_done_txl;
+ }
+ break;
+ case LCD_LVDS:
+ clk_div_sel = CLK_DIV_SEL_7;
+ xd = 1;
+ clk_div_out = cConf->fout * xd;
+ if (clk_div_out > cConf->div_out_fmax)
+ goto generate_clk_done_txl;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("fout=%d, xd=%d, clk_div_out=%d\n",
+ cConf->fout, xd, clk_div_out);
+ }
+ clk_div_in = clk_div_calc_g9_gxtvbb(clk_div_out,
+ clk_div_sel, CLK_DIV_O2I);
+ if (clk_div_in > cConf->div_in_fmax)
+ goto generate_clk_done_txl;
+ cConf->xd = xd;
+ cConf->div_sel = clk_div_sel;
+ pll_fout = clk_div_in;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("clk_div_sel=%s(index %d), pll_fout=%d\n",
+ lcd_clk_div_sel_table[clk_div_sel],
+ clk_div_sel, pll_fout);
+ }
+ done = check_pll_txl(cConf, pll_fout);
+ if (done)
+ goto generate_clk_done_txl;
+ break;
+ case LCD_VBYONE:
+ cConf->div_sel_max = CLK_DIV_SEL_MAX;
+ cConf->xd_max = CRT_VID_DIV_MAX;
+ pll_fout = pconf->lcd_control.vbyone_config->bit_rate / 1000;
+ clk_div_in = pll_fout;
+ if (clk_div_in > cConf->div_in_fmax)
+ goto generate_clk_done_txl;
+ if (lcd_debug_print_flag == 2)
+ LCDPR("pll_fout=%d\n", pll_fout);
+ if ((clk_div_in / cConf->fout) > 15)
+ cConf->xd = 4;
+ else
+ cConf->xd = 1;
+ clk_div_out = cConf->fout * cConf->xd;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("clk_div_in=%d, fout=%d, xd=%d, clk_div_out=%d\n",
+ clk_div_in, cConf->fout,
+ clk_div_out, cConf->xd);
+ }
+ if (clk_div_out > cConf->div_out_fmax)
+ goto generate_clk_done_txl;
+ clk_div_sel = clk_div_get_g9_gxtvbb(
+ clk_div_in * 100 / clk_div_out);
+ cConf->div_sel = clk_div_sel;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("clk_div_sel=%s(index %d)\n",
+ lcd_clk_div_sel_table[clk_div_sel],
+ cConf->div_sel);
+ }
+ done = check_pll_txl(cConf, pll_fout);
+ break;
+ default:
+ break;
+ }
+
+generate_clk_done_txl:
+ if (done) {
+ pconf->lcd_timing.pll_ctrl =
+ (cConf->pll_od1_sel << PLL_CTRL_OD1) |
+ (cConf->pll_od2_sel << PLL_CTRL_OD2) |
+ (cConf->pll_od3_sel << PLL_CTRL_OD3) |
+ (cConf->pll_n << PLL_CTRL_N) |
+ (cConf->pll_m << PLL_CTRL_M);
+ pconf->lcd_timing.div_ctrl =
+ (cConf->div_sel << DIV_CTRL_DIV_SEL) |
+ (cConf->xd << DIV_CTRL_XD);
+ pconf->lcd_timing.clk_ctrl =
+ (cConf->pll_frac << CLK_CTRL_FRAC);
+ } else {
+ pconf->lcd_timing.pll_ctrl =
+ (1 << PLL_CTRL_OD1) |
+ (1 << PLL_CTRL_OD2) |
+ (1 << PLL_CTRL_OD3) |
+ (1 << PLL_CTRL_N) |
+ (50 << PLL_CTRL_M);
+ pconf->lcd_timing.div_ctrl =
+ (CLK_DIV_SEL_1 << DIV_CTRL_DIV_SEL) |
+ (7 << DIV_CTRL_XD);
+ pconf->lcd_timing.clk_ctrl = (0 << CLK_CTRL_FRAC);
+ LCDERR("Out of clock range, reset to default setting\n");
+ }
+}
+
+static void lcd_pll_frac_generate_txl(struct lcd_config_s *pconf)
+{
+ unsigned int pll_fout;
+ unsigned int clk_div_in, clk_div_out, clk_div_sel;
+ unsigned int od1, od2, od3, pll_fvco;
+ unsigned int m, n, od_fb, frac, offset, temp;
+ struct lcd_clk_config_s *cConf;
+
+ cConf = get_lcd_clk_config();
+ cConf->fout = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+ clk_div_sel = cConf->div_sel;
+ od1 = od_table[cConf->pll_od1_sel];
+ od2 = od_table[cConf->pll_od2_sel];
+ od3 = od_table[cConf->pll_od3_sel];
+ m = cConf->pll_m;
+ n = cConf->pll_n;
+
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("m=%d, n=%d, od1=%d, od2=%d, od3=%d\n",
+ m, n, cConf->pll_od1_sel, cConf->pll_od2_sel,
+ cConf->pll_od3_sel);
+ LCDPR("clk_div_sel=%s(index %d), xd=%d\n",
+ lcd_clk_div_sel_table[clk_div_sel],
+ clk_div_sel, cConf->xd);
+ }
+ if (cConf->fout > cConf->xd_out_fmax) {
+ LCDERR("%s: wrong lcd_clk value %dkHz\n",
+ __func__, cConf->fout);
+ return;
+ }
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s pclk=%d\n", __func__, cConf->fout);
+
+ clk_div_out = cConf->fout * cConf->xd;
+ if (clk_div_out > cConf->div_out_fmax) {
+ LCDERR("%s: wrong clk_div_out value %dkHz\n",
+ __func__, clk_div_out);
+ return;
+ }
+
+ clk_div_in =
+ clk_div_calc_g9_gxtvbb(clk_div_out, clk_div_sel, CLK_DIV_O2I);
+ if (clk_div_in > cConf->div_in_fmax) {
+ LCDERR("%s: wrong clk_div_in value %dkHz\n",
+ __func__, clk_div_in);
+ return;
+ }
+
+ pll_fout = clk_div_in;
+ if ((pll_fout > cConf->pll_out_fmax) ||
+ (pll_fout < cConf->pll_out_fmin)) {
+ LCDERR("%s: wrong pll_fout value %dkHz\n", __func__, pll_fout);
+ return;
+ }
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s pll_fout=%d\n", __func__, pll_fout);
+
+ pll_fvco = pll_fout * od1 * od2 * od3;
+ if ((pll_fvco < cConf->pll_vco_fmin) ||
+ (pll_fvco > cConf->pll_vco_fmax)) {
+ LCDERR("%s: wrong pll_fvco value %dkHz\n", __func__, pll_fvco);
+ return;
+ }
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s pll_fvco=%d\n", __func__, pll_fvco);
+
+ cConf->pll_fvco = pll_fvco;
+ od_fb = cConf->od_fb; /* pll default */
+ pll_fvco = pll_fvco / od_fb_table[od_fb];
+ temp = cConf->fin * m / n;
+ if (pll_fvco >= temp) {
+ temp = pll_fvco - temp;
+ offset = 0;
+ } else {
+ temp = temp - pll_fvco;
+ offset = 1;
+ }
+ if (temp >= (2 * cConf->fin)) {
+ LCDERR("%s: pll changing %dkHz is too much\n",
+ __func__, temp);
+ return;
+ }
+ frac = temp * cConf->pll_frac_range * n / cConf->fin;
+ cConf->pll_frac = frac | (offset << 11);
+ if (lcd_debug_print_flag)
+ LCDPR("lcd_pll_frac_generate: frac=0x%x\n", frac);
+}
+
+static int check_pll_axg(struct lcd_clk_config_s *cConf,
+ unsigned int pll_fout)
+{
+ unsigned int m, n, od_sel, od;
+ unsigned int pll_fvco;
+ unsigned int od_fb = 0, pll_frac;
+ int done = 0;
+
+ if ((pll_fout > cConf->pll_out_fmax) ||
+ (pll_fout < cConf->pll_out_fmin)) {
+ return done;
+ }
+ for (od_sel = cConf->pll_od_sel_max; od_sel > 0; od_sel--) {
+ od = od_table[od_sel - 1];
+ pll_fvco = pll_fout * od;
+ if ((pll_fvco < cConf->pll_vco_fmin) ||
+ (pll_fvco > cConf->pll_vco_fmax)) {
+ continue;
+ }
+ cConf->pll_od1_sel = od_sel - 1;
+ cConf->pll_fout = pll_fout;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("od_sel=%d, pll_fvco=%d\n",
+ (od_sel - 1), pll_fvco);
+ }
+
+ cConf->pll_fvco = pll_fvco;
+ n = 1;
+ od_fb = cConf->od_fb;
+ pll_fvco = pll_fvco / od_fb_table[od_fb];
+ m = pll_fvco / cConf->fin;
+ pll_frac = (pll_fvco % cConf->fin) *
+ cConf->pll_frac_range / cConf->fin;
+ cConf->pll_m = m;
+ cConf->pll_n = n;
+ cConf->pll_frac = pll_frac;
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("pll_m=%d, pll_n=%d\n", m, n);
+ LCDPR("pll_frac=0x%03x\n",
+ pll_frac);
+ }
+ done = 1;
+ break;
+ }
+ return done;
+}
+
+static void lcd_clk_generate_axg(struct lcd_config_s *pconf)
+{
+ unsigned int pll_fout;
+ unsigned int xd;
+ unsigned int dsi_bit_rate_max = 0, dsi_bit_rate_min = 0;
+ unsigned int tmp;
+ struct lcd_clk_config_s *cConf;
+ int done;
+
+ done = 0;
+ cConf = get_lcd_clk_config();
+ cConf->fout = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+ cConf->err_fmin = MAX_ERROR;
+
+ if (cConf->fout > cConf->xd_out_fmax) {
+ LCDERR("%s: wrong lcd_clk value %dkHz\n",
+ __func__, cConf->fout);
+ goto generate_clk_done_axg;
+ }
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_MIPI:
+ cConf->xd_max = CRT_VID_DIV_MAX;
+ tmp = pconf->lcd_control.mipi_config->bit_rate_max;
+ dsi_bit_rate_max = tmp * 1000; /* change to kHz */
+ dsi_bit_rate_min = dsi_bit_rate_max - cConf->fout;
+
+ for (xd = 1; xd <= cConf->xd_max; xd++) {
+ pll_fout = cConf->fout * xd;
+ if ((pll_fout > dsi_bit_rate_max) ||
+ (pll_fout < dsi_bit_rate_min)) {
+ continue;
+ }
+ if (lcd_debug_print_flag == 2)
+ LCDPR("fout=%d, xd=%d\n", cConf->fout, xd);
+
+ pconf->lcd_control.mipi_config->bit_rate = pll_fout * 1000;
+ cConf->xd = xd;
+ done = check_pll_axg(cConf, pll_fout);
+ if (done)
+ goto generate_clk_done_axg;
+ }
+ break;
+ default:
+ break;
+ }
+
+generate_clk_done_axg:
+ if (done) {
+ pconf->lcd_timing.pll_ctrl =
+ (cConf->pll_od1_sel << PLL_CTRL_OD1) |
+ (cConf->pll_n << PLL_CTRL_N) |
+ (cConf->pll_m << PLL_CTRL_M);
+ pconf->lcd_timing.div_ctrl =
+ (cConf->edp_div1 << DIV_CTRL_EDP_DIV1) |
+ (cConf->edp_div0 << DIV_CTRL_EDP_DIV0) |
+ (cConf->div_post << DIV_CTRL_DIV_POST) |
+ (cConf->div_pre << DIV_CTRL_DIV_PRE) |
+ (cConf->xd << DIV_CTRL_XD);
+ tmp = (pconf->lcd_timing.clk_ctrl &
+ ~((0x7 << CLK_CTRL_LEVEL) | (0xfff << CLK_CTRL_FRAC)));
+ pconf->lcd_timing.clk_ctrl = (tmp |
+ (cConf->pll_level << CLK_CTRL_LEVEL) |
+ (cConf->pll_frac << CLK_CTRL_FRAC));
+ } else {
+ pconf->lcd_timing.pll_ctrl = (1 << PLL_CTRL_OD1) |
+ (1 << PLL_CTRL_N) | (50 << PLL_CTRL_M);
+ pconf->lcd_timing.div_ctrl =
+ (0 << DIV_CTRL_EDP_DIV1) | (0 << DIV_CTRL_EDP_DIV0) |
+ (1 << DIV_CTRL_DIV_PRE) | (1 << DIV_CTRL_DIV_PRE) |
+ (7 << DIV_CTRL_XD);
+ tmp = (pconf->lcd_timing.clk_ctrl &
+ ~((0x7 << CLK_CTRL_LEVEL) | (0xfff << CLK_CTRL_FRAC)));
+ pconf->lcd_timing.clk_ctrl |= (1 << CLK_CTRL_LEVEL);
+ LCDERR("Out of clock range, reset to default setting!\n");
+ }
+}
+
+static void lcd_pll_reset_axg(void)
+{
+ lcd_hiu_setb(HHI_GP0_PLL_CNTL, 1, LCD_PLL_RST_AXG, 1);
+ udelay(10);
+ lcd_hiu_setb(HHI_GP0_PLL_CNTL, 0, LCD_PLL_RST_AXG, 1);
+}
+
+static void lcd_set_pll_axg(struct lcd_clk_config_s *cConf)
+{
+ unsigned int pll_ctrl, pll_ctrl1, pll_ctrl2;
+ int ret;
+
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s\n", __func__);
+
+ pll_ctrl = ((1 << LCD_PLL_EN_AXG) |
+ (cConf->pll_n << LCD_PLL_N_AXG) |
+ (cConf->pll_m << LCD_PLL_M_AXG) |
+ (cConf->pll_od1_sel << LCD_PLL_OD_AXG));
+ pll_ctrl1 = 0xc084a000;
+ pll_ctrl1 |= ((1 << 12) | (cConf->pll_frac << 0));
+ pll_ctrl2 = 0xb75020be | (cConf->od_fb << 19);
+
+ lcd_hiu_write(HHI_GP0_PLL_CNTL, pll_ctrl);
+ lcd_hiu_write(HHI_GP0_PLL_CNTL1, pll_ctrl1);
+ lcd_hiu_write(HHI_GP0_PLL_CNTL2, pll_ctrl2);
+ lcd_hiu_write(HHI_GP0_PLL_CNTL3, 0x0a59a288);
+ lcd_hiu_write(HHI_GP0_PLL_CNTL4, 0xc000004d);
+ lcd_hiu_write(HHI_GP0_PLL_CNTL5, 0x00058000);
+ lcd_hiu_setb(HHI_GP0_PLL_CNTL, 1, LCD_PLL_RST_AXG, 1);
+ lcd_hiu_setb(HHI_GP0_PLL_CNTL, 0, LCD_PLL_RST_AXG, 1);
+
+ ret = lcd_pll_wait_lock(HHI_GP0_PLL_CNTL, LCD_PLL_LOCK_AXG);
+ if (ret)
+ LCDERR("gp0_pll lock failed\n");
+
+}
+
+static void lcd_pll_frac_generate_axg(struct lcd_config_s *pconf)
+{
+ unsigned int pll_fout;
+ unsigned int od, pll_fvco;
+ unsigned int m, n, od_fb, frac, offset, temp;
+ struct lcd_clk_config_s *cConf;
+
+ cConf = get_lcd_clk_config();
+ cConf->fout = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+ od = od_table[cConf->pll_od1_sel];
+ m = cConf->pll_m;
+ n = cConf->pll_n;
+
+ if (lcd_debug_print_flag == 2) {
+ LCDPR("m=%d, n=%d, od=%d, xd=%d\n",
+ m, n, cConf->pll_od1_sel, cConf->xd);
+ }
+ if (cConf->fout > cConf->xd_out_fmax) {
+ LCDERR("%s: wrong lcd_clk value %dkHz\n",
+ __func__, cConf->fout);
+ return;
+ }
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s pclk=%d\n", __func__, cConf->fout);
+
+ pll_fout = cConf->fout * cConf->xd;
+ if ((pll_fout > cConf->pll_out_fmax) ||
+ (pll_fout < cConf->pll_out_fmin)) {
+ LCDERR("%s: wrong pll_fout value %dkHz\n", __func__, pll_fout);
+ return;
+ }
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s pll_fout=%d\n", __func__, pll_fout);
+
+ pll_fvco = pll_fout * od;
+ if ((pll_fvco < cConf->pll_vco_fmin) ||
+ (pll_fvco > cConf->pll_vco_fmax)) {
+ LCDERR("%s: wrong pll_fvco value %dkHz\n", __func__, pll_fvco);
+ return;
+ }
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s pll_fvco=%d\n", __func__, pll_fvco);
+
+ cConf->pll_fvco = pll_fvco;
+ od_fb = cConf->od_fb; /* pll default */
+ pll_fvco = pll_fvco / od_fb_table[od_fb];
+ temp = cConf->fin * m / n;
+ if (pll_fvco >= temp) {
+ temp = pll_fvco - temp;
+ offset = 0;
+ } else {
+ temp = temp - pll_fvco;
+ offset = 1;
+ }
+ if (temp >= (2 * cConf->fin)) {
+ LCDERR("%s: pll changing %dkHz is too much\n",
+ __func__, temp);
+ return;
+ }
+ frac = temp * cConf->pll_frac_range * n / cConf->fin;
+ cConf->pll_frac = frac | (offset << 11);
+ if (lcd_debug_print_flag)
+ LCDPR("lcd_pll_frac_generate: frac=0x%x\n", frac);
+}
+
+static void lcd_update_pll_frac_axg(struct lcd_clk_config_s *cConf)
+{
+ if (lcd_debug_print_flag == 2)
+ LCDPR("%s\n", __func__);
+
+ lcd_hiu_setb(HHI_GP0_PLL_CNTL1, cConf->pll_frac, 0, 12);
+}
+
+void lcd_clk_generate_parameter(struct lcd_config_s *pconf)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ switch (lcd_drv->chip_type) {
+ case LCD_CHIP_GXTVBB:
+ lcd_clk_generate_g9_gxtvbb(pconf);
+ break;
+ case LCD_CHIP_GXL:
+ case LCD_CHIP_GXM:
+ case LCD_CHIP_TXL:
+ case LCD_CHIP_TXLX:
+ lcd_clk_generate_txl(pconf);
+ break;
+ case LCD_CHIP_AXG:
+ lcd_clk_generate_axg(pconf);
+ break;
+ default:
+ break;
+ }
+}
+
+char *lcd_get_spread_spectrum(void)
+{
+ char *ss_str;
+ int ss_level;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ ss_level = lcd_drv->lcd_config->lcd_timing.ss_level;
+ ss_level = (ss_level >= clk_conf.ss_level_max) ? 0 : ss_level;
+ switch (lcd_drv->chip_type) {
+ case LCD_CHIP_GXTVBB:
+ ss_str = lcd_pll_ss_table_gxtvbb[ss_level];
+ break;
+ case LCD_CHIP_TXL:
+ ss_str = lcd_pll_ss_table_txl[ss_level];
+ break;
+ case LCD_CHIP_TXLX:
+ ss_str = lcd_pll_ss_table_txlx[ss_level];
+ break;
+ default:
+ ss_str = "unknown";
+ break;
+ }
+
+ return ss_str;
+}
+
+void lcd_set_spread_spectrum(void)
+{
+ unsigned long flags = 0;
+ int ss_level;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ spin_lock_irqsave(&lcd_clk_lock, flags);
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ ss_level = lcd_drv->lcd_config->lcd_timing.ss_level;
+ clk_conf.ss_level = (ss_level >= clk_conf.ss_level_max) ? 0 : ss_level;
+ switch (lcd_drv->chip_type) {
+ case LCD_CHIP_GXTVBB:
+ lcd_set_pll_ss_gxtvbb(&clk_conf);
+ break;
+ case LCD_CHIP_TXL:
+ lcd_set_pll_ss_txl(&clk_conf);
+ break;
+ case LCD_CHIP_TXLX:
+ lcd_set_pll_ss_txlx(&clk_conf);
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&lcd_clk_lock, flags);
+}
+
+int lcd_encl_clk_msr(void)
+{
+ unsigned int clk_mux;
+ int encl_clk = 0;
+
+ clk_mux = 9;
+ encl_clk = meson_clk_measure(clk_mux);
+
+ return encl_clk;
+}
+
+void lcd_pll_reset(void)
+{
+ unsigned long flags = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ spin_lock_irqsave(&lcd_clk_lock, flags);
+ LCDPR("%s\n", __func__);
+
+ switch (lcd_drv->chip_type) {
+ case LCD_CHIP_GXTVBB:
+ lcd_pll_reset_gxtvbb();
+ break;
+ case LCD_CHIP_GXL:
+ case LCD_CHIP_GXM:
+ case LCD_CHIP_TXL:
+ case LCD_CHIP_TXLX:
+ lcd_pll_reset_txl();
+ break;
+ case LCD_CHIP_AXG:
+ lcd_pll_reset_axg();
+ break;
+ default:
+ break;
+ }
+
+ spin_unlock_irqrestore(&lcd_clk_lock, flags);
+}
+
+/* for frame rate change */
+void lcd_clk_update(struct lcd_config_s *pconf)
+{
+ unsigned long flags = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ spin_lock_irqsave(&lcd_clk_lock, flags);
+ LCDPR("%s\n", __func__);
+
+ switch (lcd_drv->chip_type) {
+ case LCD_CHIP_GXTVBB:
+ lcd_pll_frac_generate_g9_gxtvbb(pconf);
+ lcd_update_pll_frac_gxtvbb(&clk_conf);
+ break;
+ case LCD_CHIP_GXL:
+ case LCD_CHIP_GXM:
+ case LCD_CHIP_TXL:
+ case LCD_CHIP_TXLX:
+ lcd_pll_frac_generate_txl(pconf);
+ lcd_update_pll_frac_txl(&clk_conf);
+ break;
+ case LCD_CHIP_AXG:
+ lcd_pll_frac_generate_axg(pconf);
+ lcd_update_pll_frac_axg(&clk_conf);
+ break;
+ default:
+ break;
+ }
+
+ spin_unlock_irqrestore(&lcd_clk_lock, flags);
+}
+
+/* for timing change */
+void lcd_clk_set(struct lcd_config_s *pconf)
+{
+ unsigned long flags = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ spin_lock_irqsave(&lcd_clk_lock, flags);
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ switch (lcd_drv->chip_type) {
+ case LCD_CHIP_GXTVBB:
+ lcd_set_pll_gxtvbb(&clk_conf);
+ lcd_set_clk_div_g9_gxtvbb(&clk_conf);
+ break;
+ case LCD_CHIP_GXL:
+ case LCD_CHIP_GXM:
+ case LCD_CHIP_TXL:
+ lcd_set_pll_txl(&clk_conf);
+ lcd_set_clk_div_g9_gxtvbb(&clk_conf);
+ break;
+ case LCD_CHIP_TXLX:
+ lcd_set_pll_txlx(&clk_conf);
+ lcd_set_clk_div_g9_gxtvbb(&clk_conf);
+ break;
+ case LCD_CHIP_AXG:
+ lcd_set_pll_axg(&clk_conf);
+ break;
+ default:
+ break;
+ }
+ lcd_set_vclk_crt(pconf->lcd_basic.lcd_type, &clk_conf);
+ mdelay(10);
+ spin_unlock_irqrestore(&lcd_clk_lock, flags);
+}
+
+void lcd_clk_disable(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ /* disable CTS_ENCL clk gate, new added in m8m2 */
+ switch (lcd_drv->chip_type) {
+ case LCD_CHIP_GXTVBB:
+ case LCD_CHIP_GXL:
+ case LCD_CHIP_GXM:
+ case LCD_CHIP_TXL:
+ case LCD_CHIP_TXLX:
+ case LCD_CHIP_AXG:
+ lcd_hiu_setb(HHI_VID_CLK_CNTL2, 0, ENCL_GATE_VCLK, 1);
+ break;
+ default:
+ break;
+ }
+
+ /* close vclk2_div gate: 0x104b[4:0] */
+ lcd_hiu_setb(HHI_VIID_CLK_CNTL, 0, 0, 5);
+ lcd_hiu_setb(HHI_VIID_CLK_CNTL, 0, VCLK2_EN, 1);
+
+ /* close vid2_pll gate: 0x104c[16] */
+ lcd_hiu_setb(HHI_VIID_DIVIDER_CNTL, 0, DIV_CLK_IN_EN, 1);
+
+ /* disable pll */
+ switch (lcd_drv->chip_type) {
+ case LCD_CHIP_GXTVBB:
+ /* disable hdmi_pll: 0x10c8[30] */
+ lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 0, LCD_PLL_EN_GXTVBB, 1);
+ lcd_hiu_setb(HHI_HDMI_PLL_CNTL5, 0, 30, 1); /* bandgap */
+ break;
+ case LCD_CHIP_GXL:
+ case LCD_CHIP_GXM:
+ case LCD_CHIP_TXL:
+ case LCD_CHIP_TXLX:
+ /* disable hdmi_pll: 0x10c8[30] */
+ lcd_hiu_setb(HHI_HDMI_PLL_CNTL, 0, LCD_PLL_EN_TXL, 1);
+ break;
+ case LCD_CHIP_AXG:
+ /* disable hdmi_pll: 0x10c8[30] */
+ lcd_hiu_setb(HHI_GP0_PLL_CNTL, 0, LCD_PLL_EN_AXG, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+void lcd_clk_config_probe(void)
+{
+ spin_lock_init(&lcd_clk_lock);
+ lcd_clk_config_chip_init();
+}
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_clk_config.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _LCD_CLK_CONFIG_H
+#define _LCD_CLK_CONFIG_H
+
+#include <linux/types.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+
+/* clk config */
+struct lcd_clk_config_s { /* unit: kHz */
+ /* IN-OUT parameters */
+ unsigned int fin;
+ unsigned int fout;
+
+ /* pll parameters */
+ unsigned int od_fb;
+ unsigned int pll_m;
+ unsigned int pll_n;
+ unsigned int pll_fvco;
+ unsigned int pll_od1_sel;
+ unsigned int pll_od2_sel;
+ unsigned int pll_od3_sel;
+ unsigned int pll_level;
+ unsigned int pll_frac;
+ unsigned int pll_fout;
+ unsigned int ss_level;
+ unsigned int edp_div0;
+ unsigned int edp_div1;
+ unsigned int div_pre; /* m6, m8, m8b */
+ unsigned int div_post; /* m6, m8, m8b */
+ unsigned int div_sel; /* g9tv, g9bb, gxbb */
+ unsigned int xd;
+
+ /* clk path node parameters */
+ unsigned int ss_level_max;
+ unsigned int pll_m_max;
+ unsigned int pll_m_min;
+ unsigned int pll_n_max;
+ unsigned int pll_n_min;
+ unsigned int pll_frac_range;
+ unsigned int pll_od_sel_max;
+ unsigned int div_pre_sel_max; /* m6, m8, m8b */
+ unsigned int div_post_sel_max; /* m6, m8, m8b */
+ unsigned int div_sel_max; /* g9tv, g9bb, gxbb */
+ unsigned int xd_max;
+ unsigned int pll_ref_fmax;
+ unsigned int pll_ref_fmin;
+ unsigned int pll_vco_fmax;
+ unsigned int pll_vco_fmin;
+ unsigned int pll_out_fmax;
+ unsigned int pll_out_fmin;
+ unsigned int div_post_out_fmax; /* m6, m8, m8b */
+ unsigned int div_in_fmax; /* g9tv, g9bb, gxbb */
+ unsigned int div_out_fmax; /* g9tv, g9bb, gxbb */
+ unsigned int xd_out_fmax;
+ unsigned int err_fmin;
+};
+
+/* pll & clk parameter */
+/* ******** clk calculation ******** */
+#define PLL_WAIT_LOCK_CNT 200
+ /* frequency unit: kHz */
+#define FIN_FREQ (24 * 1000)
+/* clk max error */
+#define MAX_ERROR (2 * 1000)
+
+/* ******** register bit ******** */
+/* divider */
+#define CRT_VID_DIV_MAX 255
+
+#define DIV_PRE_SEL_MAX 6
+#define EDP_DIV0_SEL_MAX 15
+#define EDP_DIV1_SEL_MAX 8
+
+/* g9tv, g9bb, gxbb divider */
+#define CLK_DIV_I2O 0
+#define CLK_DIV_O2I 1
+enum div_sel_e {
+ CLK_DIV_SEL_1 = 0,
+ CLK_DIV_SEL_2, /* 1 */
+ CLK_DIV_SEL_3, /* 2 */
+ CLK_DIV_SEL_3p5, /* 3 */
+ CLK_DIV_SEL_3p75, /* 4 */
+ CLK_DIV_SEL_4, /* 5 */
+ CLK_DIV_SEL_5, /* 6 */
+ CLK_DIV_SEL_6, /* 7 */
+ CLK_DIV_SEL_6p25, /* 8 */
+ CLK_DIV_SEL_7, /* 9 */
+ CLK_DIV_SEL_7p5, /* 10 */
+ CLK_DIV_SEL_12, /* 11 */
+ CLK_DIV_SEL_14, /* 12 */
+ CLK_DIV_SEL_15, /* 13 */
+ CLK_DIV_SEL_2p5, /* 14 */
+ CLK_DIV_SEL_MAX,
+};
+
+
+/* GXTVBB */
+/* ******** register bit ******** */
+/* PLL_CNTL 0x10c8 */
+#define LCD_PLL_LOCK_GXTVBB 31
+#define LCD_PLL_EN_GXTVBB 30
+#define LCD_PLL_RST_GXTVBB 28
+#define LCD_PLL_N_GXTVBB 9
+#define LCD_PLL_M_GXTVBB 0
+
+#define LCD_PLL_OD3_GXTVBB 18
+#define LCD_PLL_OD2_GXTVBB 22
+#define LCD_PLL_OD1_GXTVBB 16
+
+/* ******** frequency limit (unit: kHz) ******** */
+/* pll */
+#define PLL_FRAC_OD_FB_GXTVBB 0
+#define SS_LEVEL_MAX_GXTVBB 5
+#define PLL_M_MIN_GXTVBB 2
+#define PLL_M_MAX_GXTVBB 511
+#define PLL_N_MIN_GXTVBB 1
+#define PLL_N_MAX_GXTVBB 1
+#define PLL_FRAC_RANGE_GXTVBB (1 << 10)
+#define PLL_OD_SEL_MAX_GXTVBB 3
+#define PLL_FREF_MIN_GXTVBB (5 * 1000)
+#define PLL_FREF_MAX_GXTVBB (25 * 1000)
+#define PLL_VCO_MIN_GXTVBB (3000 * 1000)
+#define PLL_VCO_MAX_GXTVBB (6000 * 1000)
+
+/* video */
+#define CLK_DIV_IN_MAX_GXTVBB (3100 * 1000)
+#define CRT_VID_CLK_IN_MAX_GXTVBB (3100 * 1000)
+#define ENCL_CLK_IN_MAX_GXTVBB (620 * 1000)
+
+/* GXL */
+/* ******** register bit ******** */
+/* PLL_CNTL 0x10c8 */
+#define LCD_PLL_LOCK_GXL 31
+#define LCD_PLL_EN_GXL 30
+#define LCD_PLL_RST_GXL 28
+#define LCD_PLL_N_GXL 9
+#define LCD_PLL_M_GXL 0
+
+#define LCD_PLL_OD3_GXL 19
+#define LCD_PLL_OD2_GXL 23
+#define LCD_PLL_OD1_GXL 21
+
+/* ******** frequency limit (unit: kHz) ******** */
+/* pll */
+#define PLL_FRAC_OD_FB_GXL 1
+#define SS_LEVEL_MAX_GXL 5
+#define PLL_M_MIN_GXL 2
+#define PLL_M_MAX_GXL 511
+#define PLL_N_MIN_GXL 1
+#define PLL_N_MAX_GXL 1
+#define PLL_FRAC_RANGE_GXL (1 << 10)
+#define PLL_OD_SEL_MAX_GXL 3
+#define PLL_FREF_MIN_GXL (5 * 1000)
+#define PLL_FREF_MAX_GXL (25 * 1000)
+#define PLL_VCO_MIN_GXL (3000 * 1000)
+#define PLL_VCO_MAX_GXL (6000 * 1000)
+
+/* video */
+#define CLK_DIV_IN_MAX_GXL (3100 * 1000)
+#define CRT_VID_CLK_IN_MAX_GXL (3100 * 1000)
+#define ENCL_CLK_IN_MAX_GXL (620 * 1000)
+
+/* GXM */
+/* ******** register bit ******** */
+/* PLL_CNTL 0x10c8 */
+#define LCD_PLL_LOCK_GXM 31
+#define LCD_PLL_EN_GXM 30
+#define LCD_PLL_RST_GXM 28
+#define LCD_PLL_N_GXM 9
+#define LCD_PLL_M_GXM 0
+
+#define LCD_PLL_OD3_GXM 19
+#define LCD_PLL_OD2_GXM 23
+#define LCD_PLL_OD1_GXM 21
+
+/* ******** frequency limit (unit: kHz) ******** */
+/* pll */
+#define PLL_FRAC_OD_FB_GXM 1
+#define SS_LEVEL_MAX_GXM 5
+#define PLL_M_MIN_GXM 2
+#define PLL_M_MAX_GXM 511
+#define PLL_N_MIN_GXM 1
+#define PLL_N_MAX_GXM 1
+#define PLL_FRAC_RANGE_GXM (1 << 10)
+#define PLL_OD_SEL_MAX_GXM 3
+#define PLL_FREF_MIN_GXM (5 * 1000)
+#define PLL_FREF_MAX_GXM (25 * 1000)
+#define PLL_VCO_MIN_GXM (3000 * 1000)
+#define PLL_VCO_MAX_GXM (6000 * 1000)
+
+/* video */
+#define CLK_DIV_IN_MAX_GXM (3100 * 1000)
+#define CRT_VID_CLK_IN_MAX_GXM (3100 * 1000)
+#define ENCL_CLK_IN_MAX_GXM (620 * 1000)
+
+/* TXL */
+/* ******** register bit ******** */
+/* PLL_CNTL 0x10c8 */
+#define LCD_PLL_LOCK_TXL 31
+#define LCD_PLL_EN_TXL 30
+#define LCD_PLL_RST_TXL 28
+#define LCD_PLL_N_TXL 9
+#define LCD_PLL_M_TXL 0
+
+#define LCD_PLL_OD3_TXL 19
+#define LCD_PLL_OD2_TXL 23
+#define LCD_PLL_OD1_TXL 21
+
+/* ******** frequency limit (unit: kHz) ******** */
+/* pll */
+#define PLL_FRAC_OD_FB_TXL 1
+#define SS_LEVEL_MAX_TXL 5
+#define PLL_M_MIN_TXL 2
+#define PLL_M_MAX_TXL 511
+#define PLL_N_MIN_TXL 1
+#define PLL_N_MAX_TXL 1
+#define PLL_FRAC_RANGE_TXL (1 << 10)
+#define PLL_OD_SEL_MAX_TXL 3
+#define PLL_FREF_MIN_TXL (5 * 1000)
+#define PLL_FREF_MAX_TXL (25 * 1000)
+#define PLL_VCO_MIN_TXL (3000 * 1000)
+#define PLL_VCO_MAX_TXL (6000 * 1000)
+
+/* video */
+#define CLK_DIV_IN_MAX_TXL (3100 * 1000)
+#define CRT_VID_CLK_IN_MAX_TXL (3100 * 1000)
+#define ENCL_CLK_IN_MAX_TXL (620 * 1000)
+
+/* TXLX */
+/* ******** register bit ******** */
+/* PLL_CNTL 0x10c8 */
+#define LCD_PLL_LOCK_TXLX 31
+#define LCD_PLL_EN_TXLX 30
+#define LCD_PLL_RST_TXLX 28
+#define LCD_PLL_N_TXLX 9
+#define LCD_PLL_M_TXLX 0
+
+#define LCD_PLL_OD3_TXLX 19
+#define LCD_PLL_OD2_TXLX 23
+#define LCD_PLL_OD1_TXLX 21
+
+/* ******** frequency limit (unit: kHz) ******** */
+/* pll */
+#define PLL_FRAC_OD_FB_TXLX 0
+#define SS_LEVEL_MAX_TXLX 6
+#define PLL_M_MIN_TXLX 2
+#define PLL_M_MAX_TXLX 511
+#define PLL_N_MIN_TXLX 1
+#define PLL_N_MAX_TXLX 1
+#define PLL_FRAC_RANGE_TXLX (1 << 10)
+#define PLL_OD_SEL_MAX_TXLX 3
+#define PLL_FREF_MIN_TXLX (5 * 1000)
+#define PLL_FREF_MAX_TXLX (25 * 1000)
+#define PLL_VCO_MIN_TXLX (3000 * 1000)
+#define PLL_VCO_MAX_TXLX (6000 * 1000)
+
+/* video */
+#define CLK_DIV_IN_MAX_TXLX (3100 * 1000)
+#define CRT_VID_CLK_IN_MAX_TXLX (3100 * 1000)
+#define ENCL_CLK_IN_MAX_TXLX (620 * 1000)
+
+/* AXG */
+/* ******** register bit ******** */
+/* PLL_CNTL */
+#define LCD_PLL_LOCK_AXG 31
+#define LCD_PLL_EN_AXG 30
+#define LCD_PLL_RST_AXG 29
+#define LCD_PLL_OD_AXG 16
+#define LCD_PLL_N_AXG 9
+#define LCD_PLL_M_AXG 0
+
+/* ******** frequency limit (unit: kHz) ******** */
+/* pll */
+#define PLL_FRAC_OD_FB_AXG 0
+#define SS_LEVEL_MAX_AXG 5
+#define PLL_M_MIN_AXG 2
+#define PLL_M_MAX_AXG 511
+#define PLL_N_MIN_AXG 1
+#define PLL_N_MAX_AXG 1
+#define PLL_FRAC_RANGE_AXG (1 << 10)
+#define PLL_OD_SEL_MAX_AXG 3
+#define PLL_FREF_MIN_AXG (5 * 1000)
+#define PLL_FREF_MAX_AXG (25 * 1000)
+#define PLL_VCO_MIN_AXG (1500 * 1000)
+#define PLL_VCO_MAX_AXG (3000 * 1000)
+
+/* video */
+#define CRT_VID_CLK_IN_MAX_AXG (3000 * 1000)
+#define ENCL_CLK_IN_MAX_AXG (200 * 1000)
+
+
+extern int meson_clk_measure(unsigned int clk_mux);
+extern struct lcd_clk_config_s *get_lcd_clk_config(void);
+extern void lcd_clk_config_print(void);
+extern int lcd_encl_clk_msr(void);
+extern void lcd_pll_reset(void);
+extern char *lcd_get_spread_spectrum(void);
+extern void lcd_set_spread_spectrum(void);
+extern void lcd_clk_update(struct lcd_config_s *pconf);
+extern void lcd_clk_set(struct lcd_config_s *pconf);
+extern void lcd_clk_disable(void);
+extern void lcd_clk_generate_parameter(struct lcd_config_s *pconf);
+extern void lcd_clk_config_probe(void);
+
+#endif
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_common.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#include <linux/clk.h>
+#ifdef CONFIG_AML_VPU
+#include <linux/amlogic/vpu.h>
+#endif
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#include <linux/amlogic/media/vout/vinfo.h>
+
+#include "lcd_common.h"
+#include "lcd_reg.h"
+
+/* lcd type */
+struct lcd_type_match_s {
+ char *name;
+ enum lcd_type_e type;
+};
+
+static struct lcd_type_match_s lcd_type_match_table[] = {
+ {"ttl", LCD_TTL},
+ {"lvds", LCD_LVDS},
+ {"vbyone", LCD_VBYONE},
+ {"mipi", LCD_MIPI},
+ {"invalid", LCD_TYPE_MAX},
+};
+
+int lcd_type_str_to_type(const char *str)
+{
+ int i;
+ int type = LCD_TYPE_MAX;
+
+ for (i = 0; i < LCD_TYPE_MAX; i++) {
+ if (!strcmp(str, lcd_type_match_table[i].name)) {
+ type = lcd_type_match_table[i].type;
+ break;
+ }
+ }
+ return type;
+}
+
+char *lcd_type_type_to_str(int type)
+{
+ int i;
+ char *str = lcd_type_match_table[LCD_TYPE_MAX].name;
+
+ for (i = 0; i < LCD_TYPE_MAX; i++) {
+ if (type == lcd_type_match_table[i].type) {
+ str = lcd_type_match_table[i].name;
+ break;
+ }
+ }
+ return str;
+}
+
+static char *lcd_mode_table[] = {
+ "tv",
+ "tablet",
+ "invalid",
+};
+
+unsigned char lcd_mode_str_to_mode(const char *str)
+{
+ unsigned char mode;
+
+ for (mode = 0; mode < ARRAY_SIZE(lcd_mode_table); mode++) {
+ if (!strcmp(str, lcd_mode_table[mode]))
+ break;
+ }
+ return mode;
+}
+
+char *lcd_mode_mode_to_str(int mode)
+{
+ return lcd_mode_table[mode];
+}
+
+/* lcd gpio */
+#if 0
+#define lcd_gpio_request(dev, str) gpiod_get(dev, str)
+#define lcd_gpio_free(gdesc) gpiod_put(gdesc)
+#define lcd_gpio_input(gdesc) gpiod_direction_input(gdesc)
+#define lcd_gpio_output(gdesc, val) gpiod_direction_output(gdesc, val)
+#define lcd_gpio_get_value(gdesc) gpiod_get_value(gdesc)
+#define lcd_gpio_set_value(gdesc, val) gpiod_set_value(gdesc, val)
+#endif
+
+void lcd_cpu_gpio_register(unsigned int index)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_cpu_gpio_s *cpu_gpio;
+ const char *str;
+ int ret;
+
+ if (index >= LCD_CPU_GPIO_NUM_MAX) {
+ LCDERR("gpio index %d, exit\n", index);
+ return;
+ }
+ cpu_gpio = &lcd_drv->lcd_config->lcd_power->cpu_gpio[index];
+ if (cpu_gpio->flag) {
+ if (lcd_debug_print_flag) {
+ LCDPR("gpio %s[%d] is already registered\n",
+ cpu_gpio->name, index);
+ }
+ return;
+ }
+
+ /* get gpio name */
+ ret = of_property_read_string_index(lcd_drv->dev->of_node,
+ "lcd_cpu_gpio_names", index, &str);
+ if (ret) {
+ LCDERR("failed to get lcd_cpu_gpio_names: %d\n", index);
+ str = "unknown";
+ }
+ strcpy(cpu_gpio->name, str);
+
+ /* request gpio */
+ cpu_gpio->gpio = devm_gpiod_get_index(lcd_drv->dev,
+ "lcd_cpu", index, GPIOD_OUT_HIGH);
+ if (IS_ERR(cpu_gpio->gpio)) {
+ LCDERR("register gpio %s[%d]: %p, err: %d\n",
+ cpu_gpio->name, index, cpu_gpio->gpio,
+ IS_ERR(cpu_gpio->gpio));
+ } else {
+ cpu_gpio->flag = 1;
+ if (lcd_debug_print_flag) {
+ LCDPR("register gpio %s[%d]: %p\n",
+ cpu_gpio->name, index, cpu_gpio->gpio);
+ }
+ }
+}
+
+void lcd_cpu_gpio_set(unsigned int index, int value)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_cpu_gpio_s *cpu_gpio;
+
+ if (index >= LCD_CPU_GPIO_NUM_MAX) {
+ LCDERR("gpio index %d, exit\n", index);
+ return;
+ }
+ cpu_gpio = &lcd_drv->lcd_config->lcd_power->cpu_gpio[index];
+ if (cpu_gpio->flag == 0) {
+ LCDERR("gpio [%d] is not registered\n", index);
+ return;
+ }
+ if (IS_ERR(cpu_gpio->gpio)) {
+ LCDERR("gpio %s[%d]: %p, err: %ld\n",
+ cpu_gpio->name, index, cpu_gpio->gpio,
+ PTR_ERR(cpu_gpio->gpio));
+ return;
+ }
+
+ switch (value) {
+ case LCD_GPIO_OUTPUT_LOW:
+ case LCD_GPIO_OUTPUT_HIGH:
+ gpiod_direction_output(cpu_gpio->gpio, value);
+ break;
+ case LCD_GPIO_INPUT:
+ default:
+ gpiod_direction_input(cpu_gpio->gpio);
+ break;
+ }
+ if (lcd_debug_print_flag) {
+ LCDPR("set gpio %s[%d] value: %d\n",
+ cpu_gpio->name, index, value);
+ }
+}
+
+unsigned int lcd_cpu_gpio_get(unsigned int index)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_cpu_gpio_s *cpu_gpio;
+
+ cpu_gpio = &lcd_drv->lcd_config->lcd_power->cpu_gpio[index];
+ if (cpu_gpio->flag == 0) {
+ LCDERR("gpio[%d] is not registered\n", index);
+ return -1;
+ }
+ if (IS_ERR(cpu_gpio->gpio)) {
+ LCDERR("gpio[%d]: %p, err: %ld\n",
+ index, cpu_gpio->gpio, PTR_ERR(cpu_gpio->gpio));
+ return -1;
+ }
+
+ return gpiod_get_value(cpu_gpio->gpio);
+}
+
+const char *lcd_ttl_pinmux_str[] = {
+ "ttl_6bit_hvsync_on", /* 0 */
+ "ttl_6bit_de_on", /* 1 */
+ "ttl_6bit_hvsync_de_on", /* 2 */
+ "ttl_6bit_hvsync_de_off", /* 3 */
+ "ttl_8bit_hvsync_on", /* 4 */
+ "ttl_8bit_de_on", /* 5 */
+ "ttl_8bit_hvsync_de_on", /* 6 */
+ "ttl_8bit_hvsync_de_off", /* 7 */
+};
+
+void lcd_ttl_pinmux_set(int status)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+ unsigned int index, num;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s: %d\n", __func__, status);
+
+ pconf = lcd_drv->lcd_config;
+ if (pconf->lcd_basic.lcd_bits == 6)
+ index = 0;
+ else
+ index = 4;
+
+ if (status) {
+ if (pconf->pinmux_flag == 0) {
+ pconf->pinmux_flag = 1;
+ switch (pconf->lcd_control.ttl_config->sync_valid) {
+ case 1: /* hvsync */
+ num = index + 0;
+ break;
+ case 2: /* DE */
+ num = index + 1;
+ break;
+ default:
+ case 3: /* DE + hvsync */
+ num = index + 2;
+ break;
+ }
+ } else {
+ LCDPR("ttl pinmux is already selected\n");
+ return;
+ }
+ } else {
+ if (pconf->pinmux_flag) {
+ pconf->pinmux_flag = 0;
+ num = index + 3;
+ } else {
+ LCDPR("ttl pinmux is already released\n");
+ return;
+ }
+ }
+
+ /* request pinmux */
+ pconf->pin = devm_pinctrl_get_select(lcd_drv->dev,
+ lcd_ttl_pinmux_str[num]);
+ if (IS_ERR(pconf->pin))
+ LCDERR("set ttl pinmux error\n");
+}
+
+/* set VX1_LOCKN && VX1_HTPDN */
+void lcd_vbyone_pinmux_set(int status)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s: %d\n", __func__, status);
+
+#if 1
+ pconf = lcd_drv->lcd_config;
+ if (status) {
+ if (pconf->pinmux_flag == 0) {
+ pconf->pinmux_flag = 1;
+ /* request pinmux */
+ pconf->pin = devm_pinctrl_get_select(lcd_drv->dev,
+ "vbyone");
+ if (IS_ERR(pconf->pin))
+ LCDERR("set vbyone pinmux error\n");
+ } else {
+ LCDPR("vbyone pinmux is already selected\n");
+ }
+ } else {
+ if (pconf->pinmux_flag) {
+ pconf->pinmux_flag = 0;
+ /* release pinmux */
+ devm_pinctrl_put(pconf->pin);
+ } else {
+ LCDPR("vbyone pinmux is already released\n");
+ }
+ }
+#else
+ if (status) {
+ lcd_pinmux_clr_mask(7,
+ ((1 << 1) | (1 << 2) | (1 << 9) | (1 << 10)));
+ lcd_pinmux_set_mask(7, ((1 << 11) | (1 << 12)));
+ } else {
+ lcd_pinmux_clr_mask(7, ((1 << 11) | (1 << 12)));
+ }
+#endif
+}
+
+unsigned int lcd_lvds_channel_on_value(struct lcd_config_s *pconf)
+{
+ unsigned int channel_on = 0;
+
+ if (pconf->lcd_control.lvds_config->dual_port == 0) {
+ if (pconf->lcd_control.lvds_config->lane_reverse == 0) {
+ switch (pconf->lcd_basic.lcd_bits) {
+ case 6:
+ channel_on = 0xf;
+ break;
+ case 8:
+ channel_on = 0x1f;
+ break;
+ case 10:
+ default:
+ channel_on = 0x3f;
+ break;
+ }
+ } else {
+ switch (pconf->lcd_basic.lcd_bits) {
+ case 6:
+ channel_on = 0x3c;
+ break;
+ case 8:
+ channel_on = 0x3e;
+ break;
+ case 10:
+ default:
+ channel_on = 0x3f;
+ break;
+ }
+ }
+ if (pconf->lcd_control.lvds_config->port_swap == 1)
+ channel_on = (channel_on << 6); /* use channel B */
+ } else {
+ if (pconf->lcd_control.lvds_config->lane_reverse == 0) {
+ switch (pconf->lcd_basic.lcd_bits) {
+ case 6:
+ channel_on = 0x3cf;
+ break;
+ case 8:
+ channel_on = 0x7df;
+ break;
+ case 10:
+ default:
+ channel_on = 0xfff;
+ break;
+ }
+ } else {
+ switch (pconf->lcd_basic.lcd_bits) {
+ case 6:
+ channel_on = 0xf3c;
+ break;
+ case 8:
+ channel_on = 0xfbe;
+ break;
+ case 10:
+ default:
+ channel_on = 0xfff;
+ break;
+ }
+ }
+ }
+ return channel_on;
+}
+
+int lcd_power_load_from_dts(struct lcd_config_s *pconf,
+ struct device_node *child)
+{
+ int ret = 0;
+ unsigned int para[5];
+ unsigned int val;
+ struct lcd_power_ctrl_s *lcd_power = pconf->lcd_power;
+ int i, j;
+ unsigned int index;
+
+ if (child == NULL) {
+ LCDPR("error: failed to get %s\n", pconf->lcd_propname);
+ return -1;
+ }
+
+ ret = of_property_read_u32_array(child, "power_on_step", ¶[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__);
+}
+
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_common.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __AML_LCD_COMMON_H__
+#define __AML_LCD_COMMON_H__
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include "lcd_clk_config.h"
+
+/* 20170505: add a113 support to linux4.9 */
+#define LCD_DRV_VERSION "20170505"
+
+#define VPP_OUT_SATURATE (1 << 0)
+
+extern struct mutex lcd_power_mutex;
+extern struct mutex lcd_vout_mutex;
+extern unsigned char lcd_resume_flag;
+extern int lcd_vout_serve_bypass;
+
+/* lcd common */
+extern int lcd_type_str_to_type(const char *str);
+extern char *lcd_type_type_to_str(int type);
+extern unsigned char lcd_mode_str_to_mode(const char *str);
+extern char *lcd_mode_mode_to_str(int mode);
+
+extern void lcd_cpu_gpio_register(unsigned int index);
+extern void lcd_cpu_gpio_set(unsigned int index, int value);
+extern unsigned int lcd_cpu_gpio_get(unsigned int index);
+extern void lcd_ttl_pinmux_set(int status);
+extern void lcd_vbyone_pinmux_set(int status);
+extern unsigned int lcd_lvds_channel_on_value(struct lcd_config_s *pconf);
+extern int lcd_power_load_from_dts(struct lcd_config_s *pconf,
+ struct device_node *child);
+extern int lcd_power_load_from_unifykey(struct lcd_config_s *pconf,
+ unsigned char *buf, int key_len, int len);
+
+extern void lcd_hdr_vinfo_update(void);
+extern void lcd_tcon_config(struct lcd_config_s *pconf);
+extern int lcd_vmode_change(struct lcd_config_s *pconf);
+extern void lcd_venc_change(struct lcd_config_s *pconf);
+extern void lcd_clk_gate_switch(int status);
+extern void lcd_clktree_probe(void);
+
+/* lcd debug */
+extern int lcd_class_creat(void);
+extern int lcd_class_remove(void);
+
+/* lcd driver */
+#ifdef CONFIG_AMLOGIC_LCD_TV
+extern void lcd_vbyone_interrupt_enable(int flag);
+extern void lcd_tv_clk_update(struct lcd_config_s *pconf);
+extern void lcd_tv_vout_server_init(void);
+extern int lcd_tv_probe(struct device *dev);
+extern int lcd_tv_remove(struct device *dev);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_TABLET
+extern void lcd_tablet_clk_update(struct lcd_config_s *pconf);
+extern void lcd_tablet_vout_server_init(void);
+extern int lcd_tablet_probe(struct device *dev);
+extern int lcd_tablet_remove(struct device *dev);
+#endif
+
+#endif
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_debug.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#include "lcd_reg.h"
+#include "lcd_common.h"
+#ifdef CONFIG_AMLOGIC_LCD_TABLET
+#include "lcd_tablet/mipi_dsi_util.h"
+#endif
+
+
+static const char *lcd_common_usage_str = {
+"Usage:\n"
+" echo <0|1> > enable ; 0=disable lcd; 1=enable lcd\n"
+"\n"
+" echo type <adj_type> > frame_rate ; set lcd frame rate adjust type\n"
+" echo set <frame_rate> > frame_rate ; set lcd frame rate(unit in 1/100Hz)\n"
+" cat frame_rate ; read current lcd frame rate\n"
+"\n"
+" echo <num> > test ; show lcd bist pattern(1~7), 0=disable bist\n"
+"\n"
+" echo w<v|h|c|p> <reg> <data> > reg ; write data to vcbus|hiu|cbus|periphs reg\n"
+" echo r<v|h|c|p> <reg> > reg ; read vcbus|hiu|cbus|periphs reg\n"
+" echo d<v|h|c|p> <reg> <num> > reg ; dump vcbus|hiu|cbus|periphs regs\n"
+"\n"
+" echo <0|1> > print ; 0=disable debug print; 1=enable debug print\n"
+" cat print ; read current debug print flag\n"
+"\n"
+" echo <cmd> ... > debug ; lcd common debug, use 'cat debug' for help\n"
+" cat debug ; print help information for debug command\n"
+"\n"
+" echo <on|off> <step_num> <delay> > power ; set power on/off step delay(unit: ms)\n"
+" cat power ; print lcd power on/off step\n"
+"\n"
+" cat key_valid ; print lcd_key_valid setting\n"
+" cat config_load ; print lcd_config load_id(0=dts, 1=unifykey)\n"
+};
+
+static const char *lcd_debug_usage_str = {
+"Usage:\n"
+" echo clk <freq> > debug ; set lcd pixel clock, unit in Hz\n"
+" echo bit <lcd_bits> > debug ; set lcd bits\n"
+" echo basic <h_active> <v_active> <h_period> <v_period> <lcd_bits> > debug ; set lcd basic config\n"
+" echo sync <hs_width> <hs_bp> <hs_pol> <vs_width> <vs_bp> <vs_pol> > debug ; set lcd sync timing\n"
+"data format:\n"
+" <xx_pol> : 0=negative, 1=positive\n"
+"\n"
+" echo info > debug ; show lcd information\n"
+" echo reg > debug ; show lcd registers\n"
+" echo dump > debug ; show lcd information & registers\n"
+" echo dith <dither_en> <rounding_en> <dither_md> > debug ; set vpu_vencl_dith_ctrl\n"
+" echo key > debug ; show lcd_key_valid config, and lcd unifykey raw data\n"
+"\n"
+" echo reset > debug; reset lcd driver\n"
+" echo power <0|1> > debug ; lcd power control: 0=power off, 1=power on\n"
+};
+
+static ssize_t lcd_debug_common_help(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", lcd_common_usage_str);
+}
+
+static ssize_t lcd_debug_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", lcd_debug_usage_str);
+}
+
+static void lcd_cpu_gpio_register_print(struct lcd_config_s *pconf)
+{
+ int i;
+ struct lcd_cpu_gpio_s *cpu_gpio;
+
+ pr_info("cpu_gpio register:\n");
+
+ i = 0;
+ while (i < LCD_CPU_GPIO_NUM_MAX) {
+ cpu_gpio = &pconf->lcd_power->cpu_gpio[i];
+ if (cpu_gpio->flag) {
+ pr_info("%d: name=%s, gpio=%p\n",
+ i, cpu_gpio->name, cpu_gpio->gpio);
+ }
+ i++;
+ }
+}
+
+static void lcd_power_info_print(struct lcd_config_s *pconf, int status)
+{
+ int i;
+ struct lcd_power_step_s *power_step;
+
+ if (status)
+ pr_info("power on step:\n");
+ else
+ pr_info("power off step:\n");
+
+ i = 0;
+ while (i < LCD_PWR_STEP_MAX) {
+ if (status)
+ power_step = &pconf->lcd_power->power_on_step[i];
+ else
+ power_step = &pconf->lcd_power->power_off_step[i];
+
+ if (power_step->type >= LCD_POWER_TYPE_MAX)
+ break;
+ switch (power_step->type) {
+ case LCD_POWER_TYPE_CPU:
+ case LCD_POWER_TYPE_PMU:
+ pr_info("%d: type=%d, index=%d, value=%d, delay=%d\n",
+ i, power_step->type, power_step->index,
+ power_step->value, power_step->delay);
+ break;
+ case LCD_POWER_TYPE_EXTERN:
+ pr_info("%d: type=%d, index=%d, delay=%d\n",
+ i, power_step->type, power_step->index,
+ power_step->delay);
+ break;
+ case LCD_POWER_TYPE_SIGNAL:
+ pr_info("%d: type=%d, delay=%d\n",
+ i, power_step->type, power_step->delay);
+ break;
+ default:
+ break;
+ }
+ i++;
+ }
+}
+
+static void lcd_info_print(void)
+{
+ unsigned int lcd_clk, sync_duration;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+
+ pconf = lcd_drv->lcd_config;
+ lcd_clk = (pconf->lcd_timing.lcd_clk / 1000);
+ sync_duration = pconf->lcd_timing.sync_duration_num * 100;
+ sync_duration = sync_duration / pconf->lcd_timing.sync_duration_den;
+
+ LCDPR("panel_type: %s\n", pconf->lcd_propname);
+ LCDPR("key_valid: %d, config_load: %d\n",
+ lcd_drv->lcd_key_valid, lcd_drv->lcd_config_load);
+ LCDPR("chip: %d, mode : %s, status: %d, fr_auto_policy: %d\n",
+ lcd_drv->chip_type, lcd_mode_mode_to_str(lcd_drv->lcd_mode),
+ lcd_drv->lcd_status, lcd_drv->fr_auto_policy);
+
+ LCDPR("%s, %s %ubit, %ux%u@%u.%02uHz\n",
+ pconf->lcd_basic.model_name,
+ lcd_type_type_to_str(pconf->lcd_basic.lcd_type),
+ pconf->lcd_basic.lcd_bits,
+ pconf->lcd_basic.h_active, pconf->lcd_basic.v_active,
+ (sync_duration / 100), (sync_duration % 100));
+
+ pr_info("lcd_clk %u.%03uMHz\n"
+ "ss_level %d\n"
+ "clk_auto %d\n"
+ "fr_adj_type %d\n\n",
+ (lcd_clk / 1000), (lcd_clk % 1000),
+ pconf->lcd_timing.ss_level, pconf->lcd_timing.clk_auto,
+ pconf->lcd_timing.fr_adjust_type);
+
+ pr_info("h_period %d\n"
+ "v_period %d\n"
+ "hs_width %d\n"
+ "hs_backporch %d\n"
+ "hs_pol %d\n"
+ "vs_width %d\n"
+ "vs_backporch %d\n"
+ "vs_pol %d\n\n",
+ pconf->lcd_basic.h_period, pconf->lcd_basic.v_period,
+ pconf->lcd_timing.hsync_width, pconf->lcd_timing.hsync_bp,
+ pconf->lcd_timing.hsync_pol,
+ pconf->lcd_timing.vsync_width, pconf->lcd_timing.vsync_bp,
+ pconf->lcd_timing.vsync_pol);
+ pr_info("h_period_min %d\n"
+ "h_period_max %d\n"
+ "v_period_min %d\n"
+ "v_period_max %d\n"
+ "pclk_min %d\n"
+ "pclk_max %d\n\n",
+ pconf->lcd_basic.h_period_min, pconf->lcd_basic.h_period_max,
+ pconf->lcd_basic.v_period_min, pconf->lcd_basic.v_period_max,
+ pconf->lcd_basic.lcd_clk_min, pconf->lcd_basic.lcd_clk_max);
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_TTL:
+ pr_info("clk_pol %u\n"
+ "hvsync_valid %u\n"
+ "de_valid %u\n"
+ "rb_swap %u\n"
+ "bit_swap %u\n\n",
+ pconf->lcd_control.ttl_config->clk_pol,
+ ((pconf->lcd_control.ttl_config->sync_valid >> 0) & 1),
+ ((pconf->lcd_control.ttl_config->sync_valid >> 1) & 1),
+ ((pconf->lcd_control.ttl_config->swap_ctrl >> 1) & 1),
+ ((pconf->lcd_control.ttl_config->swap_ctrl >> 0) & 1));
+ break;
+ case LCD_LVDS:
+ pr_info("lvds_repack %u\n"
+ "dual_port %u\n"
+ "pn_swap %u\n"
+ "port_swap %u\n"
+ "lane_reverse %u\n"
+ "phy_vswing 0x%x\n"
+ "phy_preem 0x%x\n"
+ "phy_clk_vswing 0x%x\n"
+ "phy_clk_preem 0x%x\n\n",
+ pconf->lcd_control.lvds_config->lvds_repack,
+ pconf->lcd_control.lvds_config->dual_port,
+ pconf->lcd_control.lvds_config->pn_swap,
+ pconf->lcd_control.lvds_config->port_swap,
+ pconf->lcd_control.lvds_config->lane_reverse,
+ pconf->lcd_control.lvds_config->phy_vswing,
+ pconf->lcd_control.lvds_config->phy_preem,
+ pconf->lcd_control.lvds_config->phy_clk_vswing,
+ pconf->lcd_control.lvds_config->phy_clk_preem);
+ break;
+ case LCD_VBYONE:
+ pr_info("lane_count %u\n"
+ "region_num %u\n"
+ "byte_mode %u\n"
+ "color_fmt %u\n"
+ "bit_rate %u\n"
+ "phy_vswing 0x%x\n"
+ "phy_preemphasis 0x%x\n"
+ "intr_en %u\n"
+ "vsync_intr_en %u\n\n",
+ pconf->lcd_control.vbyone_config->lane_count,
+ pconf->lcd_control.vbyone_config->region_num,
+ pconf->lcd_control.vbyone_config->byte_mode,
+ pconf->lcd_control.vbyone_config->color_fmt,
+ pconf->lcd_control.vbyone_config->bit_rate,
+ pconf->lcd_control.vbyone_config->phy_vswing,
+ pconf->lcd_control.vbyone_config->phy_preem,
+ pconf->lcd_control.vbyone_config->intr_en,
+ pconf->lcd_control.vbyone_config->vsync_intr_en);
+ break;
+ case LCD_MIPI:
+#ifdef CONFIG_AMLOGIC_LCD_TABLET
+ mipi_dsi_print_info(pconf);
+#endif
+ break;
+ default:
+ break;
+ }
+
+ pr_info("pll_ctrl 0x%08x\n"
+ "div_ctrl 0x%08x\n"
+ "clk_ctrl 0x%08x\n"
+ "video_on_pixel %d\n"
+ "video_on_line %d\n\n",
+ pconf->lcd_timing.pll_ctrl, pconf->lcd_timing.div_ctrl,
+ pconf->lcd_timing.clk_ctrl,
+ pconf->lcd_timing.video_on_pixel,
+ pconf->lcd_timing.video_on_line);
+
+ lcd_power_info_print(pconf, 1);
+ lcd_power_info_print(pconf, 0);
+ lcd_cpu_gpio_register_print(pconf);
+}
+
+static unsigned int lcd_reg_dump_clk[] = {
+ HHI_HDMI_PLL_CNTL,
+ HHI_HDMI_PLL_CNTL2,
+ HHI_HDMI_PLL_CNTL3,
+ HHI_HDMI_PLL_CNTL4,
+ HHI_HDMI_PLL_CNTL5,
+ HHI_HDMI_PLL_CNTL6,
+ HHI_VID_PLL_CLK_DIV,
+ HHI_VIID_CLK_DIV,
+ HHI_VIID_CLK_CNTL,
+ HHI_VID_CLK_CNTL2,
+};
+
+static unsigned int lcd_reg_dump_clk_axg[] = {
+ HHI_GP0_PLL_CNTL,
+ HHI_GP0_PLL_CNTL2,
+ HHI_GP0_PLL_CNTL3,
+ HHI_GP0_PLL_CNTL4,
+ HHI_GP0_PLL_CNTL5,
+ HHI_GP0_PLL_CNTL1,
+ HHI_VID_PLL_CLK_DIV,
+ HHI_VIID_CLK_DIV,
+ HHI_VIID_CLK_CNTL,
+ HHI_VID_CLK_CNTL2,
+};
+
+static unsigned int lcd_reg_dump_encl[] = {
+ VPU_VIU_VENC_MUX_CTRL,
+ ENCL_VIDEO_EN,
+ ENCL_VIDEO_MODE,
+ ENCL_VIDEO_MODE_ADV,
+ ENCL_VIDEO_MAX_PXCNT,
+ ENCL_VIDEO_MAX_LNCNT,
+ ENCL_VIDEO_HAVON_BEGIN,
+ ENCL_VIDEO_HAVON_END,
+ ENCL_VIDEO_VAVON_BLINE,
+ ENCL_VIDEO_VAVON_ELINE,
+ ENCL_VIDEO_HSO_BEGIN,
+ ENCL_VIDEO_HSO_END,
+ ENCL_VIDEO_VSO_BEGIN,
+ ENCL_VIDEO_VSO_END,
+ ENCL_VIDEO_VSO_BLINE,
+ ENCL_VIDEO_VSO_ELINE,
+ ENCL_VIDEO_RGBIN_CTRL,
+ L_GAMMA_CNTL_PORT,
+ L_RGB_BASE_ADDR,
+ L_RGB_COEFF_ADDR,
+ L_POL_CNTL_ADDR,
+ L_DITH_CNTL_ADDR,
+};
+
+static void lcd_ttl_reg_print(void)
+{
+ unsigned int reg;
+
+ pr_info("\nttl registers:\n");
+ reg = L_DUAL_PORT_CNTL_ADDR;
+ pr_info("PORT_CNTL [0x%04x] = 0x%08x\n",
+ reg, lcd_vcbus_read(reg));
+ reg = L_STH1_HS_ADDR;
+ pr_info("STH1_HS_ADDR [0x%04x] = 0x%08x\n",
+ reg, lcd_vcbus_read(reg));
+ reg = L_STH1_HE_ADDR;
+ pr_info("STH1_HE_ADDR [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ reg = L_STH1_VS_ADDR;
+ pr_info("STH1_VS_ADDR [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ reg = L_STH1_VE_ADDR;
+ pr_info("STH1_VE_ADDR [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ reg = L_STV1_HS_ADDR;
+ pr_info("STV1_HS_ADDR [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ reg = L_STV1_HE_ADDR;
+ pr_info("STV1_HE_ADDR [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ reg = L_STV1_VS_ADDR;
+ pr_info("STV1_VS_ADDR [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ reg = L_STV1_VE_ADDR;
+ pr_info("STV1_VE_ADDR [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ reg = L_OEH_HS_ADDR;
+ pr_info("OEH_HS_ADDR [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ reg = L_OEH_HE_ADDR;
+ pr_info("OEH_HE_ADDR [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ reg = L_OEH_VS_ADDR;
+ pr_info("OEH_VS_ADDR [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ reg = L_OEH_VE_ADDR;
+ pr_info("OEH_VE_ADDR [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+}
+
+static void lcd_lvds_reg_print(void)
+{
+ unsigned int reg;
+
+ pr_info("\nlvds registers:\n");
+ reg = LVDS_PACK_CNTL_ADDR;
+ pr_info("LVDS_PACK_CNTL [0x%04x] = 0x%08x\n",
+ reg, lcd_vcbus_read(reg));
+ reg = LVDS_GEN_CNTL;
+ pr_info("LVDS_GEN_CNTL [0x%04x] = 0x%08x\n",
+ reg, lcd_vcbus_read(reg));
+ reg = LCD_PORT_SWAP;
+ pr_info("LCD_PORT_SWAP [0x%04x] = 0x%08x\n",
+ reg, lcd_vcbus_read(reg));
+ reg = HHI_LVDS_TX_PHY_CNTL0;
+ pr_info("LVDS_PHY_CNTL0 [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ reg = HHI_LVDS_TX_PHY_CNTL1;
+ pr_info("LVDS_PHY_CNTL1 [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+}
+
+static void lcd_vbyone_reg_print(void)
+{
+ unsigned int reg;
+
+ pr_info("\nvbyone registers:\n");
+ reg = PERIPHS_PIN_MUX_7;
+ pr_info("VX1_PINMUX [0x%04x] = 0x%08x\n",
+ reg, lcd_periphs_read(reg));
+ reg = VBO_STATUS_L;
+ pr_info("VX1_STATUS [0x%04x] = 0x%08x\n",
+ reg, lcd_vcbus_read(reg));
+ reg = VBO_FSM_HOLDER_L;
+ pr_info("VX1_FSM_HOLDER_L [0x%04x] = 0x%08x\n",
+ reg, lcd_vcbus_read(reg));
+ reg = VBO_FSM_HOLDER_H;
+ pr_info("VX1_FSM_HOLDER_H [0x%04x] = 0x%08x\n",
+ reg, lcd_vcbus_read(reg));
+ reg = VBO_INTR_STATE_CTRL;
+ pr_info("VX1_INTR_STATE_CTRL [0x%04x] = 0x%08x\n",
+ reg, lcd_vcbus_read(reg));
+ reg = VBO_INTR_UNMASK;
+ pr_info("VX1_INTR_UNMASK [0x%04x] = 0x%08x\n",
+ reg, lcd_vcbus_read(reg));
+ reg = VBO_INTR_STATE;
+ pr_info("VX1_INTR_STATE [0x%04x] = 0x%08x\n",
+ reg, lcd_vcbus_read(reg));
+ reg = HHI_LVDS_TX_PHY_CNTL0;
+ pr_info("VX1_PHY_CNTL0 [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+}
+
+static void lcd_mipi_reg_print(void)
+{
+ unsigned int reg;
+
+ pr_info("\nmipi_dsi_host registers:\n");
+ reg = MIPI_DSI_DWC_PWR_UP_OS;
+ pr_info("MIPI_DSI_DWC_PWR_UP_OS [0x%04x] = 0x%08x\n",
+ reg, dsi_host_read(reg));
+ reg = MIPI_DSI_TOP_SW_RESET;
+ pr_info("MIPI_DSI_TOP_SW_RESET [0x%04x] = 0x%08x\n",
+ reg, dsi_host_read(reg));
+ reg = MIPI_DSI_TOP_CLK_CNTL;
+ pr_info("MIPI_DSI_TOP_CLK_CNTL [0x%04x] = 0x%08x\n",
+ reg, dsi_host_read(reg));
+ reg = MIPI_DSI_TOP_CNTL;
+ pr_info("MIPI_DSI_TOP_CNTL [0x%04x] = 0x%08x\n",
+ reg, dsi_host_read(reg));
+ reg = MIPI_DSI_TOP_STAT;
+ pr_info("MIPI_DSI_TOP_STAT [0x%04x] = 0x%08x\n",
+ reg, dsi_host_read(reg));
+ reg = MIPI_DSI_TOP_INTR_CNTL_STAT;
+ pr_info("MIPI_DSI_TOP_INTR_CNTL_STAT [0x%04x] = 0x%08x\n",
+ reg, dsi_host_read(reg));
+ reg = MIPI_DSI_TOP_MEM_PD;
+ pr_info("MIPI_DSI_TOP_MEM_PD [0x%04x] = 0x%08x\n",
+ reg, dsi_host_read(reg));
+ //reg = HHI_LVDS_TX_PHY_CNTL0;
+ //pr_info("VX1_PHY_CNTL0 [0x%04x] = 0x%08x\n",
+ // reg, lcd_hiu_read(reg));
+}
+
+static void lcd_phy_analog_reg_print(void)
+{
+ unsigned int reg;
+
+ pr_info("\nphy analog registers:\n");
+ reg = HHI_DIF_CSI_PHY_CNTL1;
+ pr_info("PHY_CNTL1 [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ reg = HHI_DIF_CSI_PHY_CNTL2;
+ pr_info("PHY_CNTL2 [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ reg = HHI_DIF_CSI_PHY_CNTL3;
+ pr_info("PHY_CNTL3 [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+}
+
+static void lcd_mipi_phy_analog_reg_print(void)
+{
+ unsigned int reg;
+
+ pr_info("\nmipi_dsi_phy analog registers:\n");
+ reg = HHI_MIPI_CNTL0;
+ pr_info("HHI_MIPI_CNTL0 [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ reg = HHI_MIPI_CNTL1;
+ pr_info("HHI_MIPI_CNTL1 [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ reg = HHI_MIPI_CNTL2;
+ pr_info("HHI_MIPI_CNTL2 [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+}
+
+static void lcd_reg_print(void)
+{
+ int i;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+
+ pconf = lcd_drv->lcd_config;
+ pr_info("clk registers:\n");
+ switch (lcd_drv->chip_type) {
+ case LCD_CHIP_AXG:
+ for (i = 0; i < ARRAY_SIZE(lcd_reg_dump_clk_axg); i++) {
+ pr_info("hiu [0x%04x] = 0x%08x\n",
+ lcd_reg_dump_clk_axg[i],
+ lcd_hiu_read(lcd_reg_dump_clk_axg[i]));
+ }
+ break;
+ default:
+ for (i = 0; i < ARRAY_SIZE(lcd_reg_dump_clk); i++) {
+ pr_info("hiu [0x%04x] = 0x%08x\n",
+ lcd_reg_dump_clk[i],
+ lcd_hiu_read(lcd_reg_dump_clk[i]));
+ }
+ break;
+ }
+
+ pr_info("\nencl registers:\n");
+ for (i = 0; i < ARRAY_SIZE(lcd_reg_dump_encl); i++) {
+ pr_info("vcbus [0x%04x] = 0x%08x\n",
+ lcd_reg_dump_encl[i],
+ lcd_vcbus_read(lcd_reg_dump_encl[i]));
+ }
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_TTL:
+ lcd_ttl_reg_print();
+ break;
+ case LCD_LVDS:
+ lcd_lvds_reg_print();
+ lcd_phy_analog_reg_print();
+ break;
+ case LCD_VBYONE:
+ lcd_vbyone_reg_print();
+ lcd_phy_analog_reg_print();
+ break;
+ case LCD_MIPI:
+ lcd_mipi_reg_print();
+ lcd_mipi_phy_analog_reg_print();
+ break;
+ default:
+ break;
+ }
+}
+
+static void lcd_hdr_info_print(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+
+ pconf = lcd_drv->lcd_config;
+ pr_info("lcd hdr info:\n"
+ "hdr_support %d\n"
+ "features %d\n"
+ "primaries_r_x %d\n"
+ "primaries_r_y %d\n"
+ "primaries_g_x %d\n"
+ "primaries_g_y %d\n"
+ "primaries_b_x %d\n"
+ "primaries_b_y %d\n"
+ "white_point_x %d\n"
+ "white_point_y %d\n"
+ "luma_max %d\n"
+ "luma_min %d\n\n",
+ pconf->hdr_info.hdr_support, pconf->hdr_info.features,
+ pconf->hdr_info.primaries_r_x, pconf->hdr_info.primaries_r_y,
+ pconf->hdr_info.primaries_g_x, pconf->hdr_info.primaries_g_y,
+ pconf->hdr_info.primaries_b_x, pconf->hdr_info.primaries_b_y,
+ pconf->hdr_info.white_point_x, pconf->hdr_info.white_point_y,
+ pconf->hdr_info.luma_max, pconf->hdr_info.luma_min);
+}
+
+#define LCD_ENC_TST_NUM_MAX 9
+static char *lcd_enc_tst_str[] = {
+ "0-None", /* 0 */
+ "1-Color Bar", /* 1 */
+ "2-Thin Line", /* 2 */
+ "3-Dot Grid", /* 3 */
+ "4-Gray", /* 4 */
+ "5-Red", /* 5 */
+ "6-Green", /* 6 */
+ "7-Blue", /* 7 */
+ "8-Black", /* 8 */
+};
+
+static unsigned int lcd_enc_tst[][8] = {
+/*tst_mode, Y, Cb, Cr, tst_en, vfifo_en rgbin*/
+ {0, 0x200, 0x200, 0x200, 0, 1, 3}, /* 0 */
+ {1, 0x200, 0x200, 0x200, 1, 0, 1}, /* 1 */
+ {2, 0x200, 0x200, 0x200, 1, 0, 1}, /* 2 */
+ {3, 0x200, 0x200, 0x200, 1, 0, 1}, /* 3 */
+ {0, 0x1ff, 0x1ff, 0x1ff, 1, 0, 3}, /* 4 */
+ {0, 0x3ff, 0x0, 0x0, 1, 0, 3}, /* 5 */
+ {0, 0x0, 0x3ff, 0x0, 1, 0, 3}, /* 6 */
+ {0, 0x0, 0x0, 0x3ff, 1, 0, 3}, /* 7 */
+ {0, 0x0, 0x0, 0x0, 1, 0, 3}, /* 8 */
+};
+
+static void lcd_debug_test(unsigned int num)
+{
+ unsigned int h_active, video_on_pixel;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ int flag;
+
+ num = (num >= LCD_ENC_TST_NUM_MAX) ? 0 : num;
+ flag = (num > 0) ? 1 : 0;
+ aml_lcd_notifier_call_chain(LCD_EVENT_TEST_PATTERN, &flag);
+
+ h_active = lcd_drv->lcd_config->lcd_basic.h_active;
+ video_on_pixel = lcd_drv->lcd_config->lcd_timing.video_on_pixel;
+ if (num >= 0) {
+ lcd_vcbus_write(ENCL_VIDEO_RGBIN_CTRL, lcd_enc_tst[num][6]);
+ lcd_vcbus_write(ENCL_TST_MDSEL, lcd_enc_tst[num][0]);
+ lcd_vcbus_write(ENCL_TST_Y, lcd_enc_tst[num][1]);
+ lcd_vcbus_write(ENCL_TST_CB, lcd_enc_tst[num][2]);
+ lcd_vcbus_write(ENCL_TST_CR, lcd_enc_tst[num][3]);
+ lcd_vcbus_write(ENCL_TST_CLRBAR_STRT, video_on_pixel);
+ lcd_vcbus_write(ENCL_TST_CLRBAR_WIDTH, (h_active / 9));
+ lcd_vcbus_write(ENCL_TST_EN, lcd_enc_tst[num][4]);
+ lcd_vcbus_setb(ENCL_VIDEO_MODE_ADV, lcd_enc_tst[num][5], 3, 1);
+ LCDPR("show test pattern: %s\n", lcd_enc_tst_str[num]);
+ } else {
+ LCDPR("disable test pattern\n");
+ }
+}
+
+static void lcd_mute_setting(unsigned char flag)
+{
+ LCDPR("set lcd mute: %d\n", flag);
+ if (flag) {
+ lcd_vcbus_write(ENCL_VIDEO_RGBIN_CTRL, 3);
+ lcd_vcbus_write(ENCL_TST_MDSEL, 0);
+ lcd_vcbus_write(ENCL_TST_Y, 0);
+ lcd_vcbus_write(ENCL_TST_CB, 0);
+ lcd_vcbus_write(ENCL_TST_CR, 0);
+ lcd_vcbus_write(ENCL_TST_EN, 1);
+ lcd_vcbus_setb(ENCL_VIDEO_MODE_ADV, 0, 3, 1);
+ } else {
+ lcd_vcbus_setb(ENCL_VIDEO_MODE_ADV, 1, 3, 1);
+ lcd_vcbus_write(ENCL_TST_EN, 0);
+ }
+}
+
+static void lcd_test_check(void)
+{
+ unsigned int h_active, video_on_pixel;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ unsigned int num;
+ int flag;
+
+ lcd_drv->lcd_mute = 0;
+
+ num = lcd_drv->lcd_test_flag;
+ num = (num >= LCD_ENC_TST_NUM_MAX) ? 0 : num;
+ flag = (num > 0) ? 1 : 0;
+ aml_lcd_notifier_call_chain(LCD_EVENT_TEST_PATTERN, &flag);
+
+ h_active = lcd_drv->lcd_config->lcd_basic.h_active;
+ video_on_pixel = lcd_drv->lcd_config->lcd_timing.video_on_pixel;
+ if (num >= 0) {
+ lcd_vcbus_write(ENCL_VIDEO_RGBIN_CTRL, lcd_enc_tst[num][6]);
+ lcd_vcbus_write(ENCL_TST_MDSEL, lcd_enc_tst[num][0]);
+ lcd_vcbus_write(ENCL_TST_Y, lcd_enc_tst[num][1]);
+ lcd_vcbus_write(ENCL_TST_CB, lcd_enc_tst[num][2]);
+ lcd_vcbus_write(ENCL_TST_CR, lcd_enc_tst[num][3]);
+ lcd_vcbus_write(ENCL_TST_CLRBAR_STRT, video_on_pixel);
+ lcd_vcbus_write(ENCL_TST_CLRBAR_WIDTH, (h_active / 9));
+ lcd_vcbus_write(ENCL_TST_EN, lcd_enc_tst[num][4]);
+ lcd_vcbus_setb(ENCL_VIDEO_MODE_ADV, lcd_enc_tst[num][5], 3, 1);
+ if (num > 0)
+ LCDPR("show test pattern: %s\n", lcd_enc_tst_str[num]);
+ }
+}
+
+static void lcd_debug_config_update(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ lcd_drv->module_reset();
+}
+
+static void lcd_debug_clk_change(unsigned int pclk)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+ unsigned int sync_duration;
+
+ pconf = lcd_drv->lcd_config;
+ sync_duration = pclk / pconf->lcd_basic.h_period;
+ sync_duration = sync_duration * 100 / pconf->lcd_basic.v_period;
+ pconf->lcd_timing.lcd_clk = pclk;
+ pconf->lcd_timing.sync_duration_num = sync_duration;
+ pconf->lcd_timing.sync_duration_den = 100;
+
+ /* update vinfo */
+ lcd_drv->lcd_info->sync_duration_num = sync_duration;
+ lcd_drv->lcd_info->sync_duration_den = 100;
+ lcd_drv->lcd_info->video_clk = pclk;
+
+ switch (lcd_drv->lcd_mode) {
+#ifdef CONFIG_AMLOGIC_LCD_TV
+ case LCD_MODE_TV:
+ lcd_tv_clk_update(pconf);
+ break;
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_TABLET
+ case LCD_MODE_TABLET:
+ lcd_tablet_clk_update(pconf);
+ break;
+#endif
+ default:
+ LCDPR("invalid lcd mode\n");
+ break;
+ }
+
+ vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE,
+ &lcd_drv->lcd_info->mode);
+}
+
+static ssize_t lcd_debug_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ unsigned int temp, val[6];
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+
+ pconf = lcd_drv->lcd_config;
+ switch (buf[0]) {
+ case 'c': /* clk */
+ ret = sscanf(buf, "clk %d", &temp);
+ if (ret == 1) {
+ if (temp > 200) {
+ pr_info("set clk: %dHz\n", temp);
+ } else {
+ pr_info("set frame_rate: %dHz\n", temp);
+ temp = pconf->lcd_basic.h_period *
+ pconf->lcd_basic.v_period * temp;
+ pr_info("set clk: %dHz\n", temp);
+ }
+ lcd_debug_clk_change(temp);
+ } else {
+ LCDERR("invalid data\n");
+ return -EINVAL;
+ }
+ break;
+ case 'b':
+ if (buf[1] == 'a') { /* basic */
+ ret = sscanf(buf, "basic %d %d %d %d %d",
+ &val[0], &val[1], &val[2], &val[3], &val[4]);
+ if (ret == 4) {
+ pconf->lcd_basic.h_active = val[0];
+ pconf->lcd_basic.v_active = val[1];
+ pconf->lcd_basic.h_period = val[2];
+ pconf->lcd_basic.v_period = val[3];
+ pr_info("set h_active=%d, v_active=%d\n",
+ val[0], val[1]);
+ pr_info("set h_period=%d, v_period=%d\n",
+ val[2], val[3]);
+ lcd_tcon_config(pconf);
+ lcd_debug_config_update();
+ } else if (ret == 5) {
+ pconf->lcd_basic.h_active = val[0];
+ pconf->lcd_basic.v_active = val[1];
+ pconf->lcd_basic.h_period = val[2];
+ pconf->lcd_basic.v_period = val[3];
+ pconf->lcd_basic.lcd_bits = val[4];
+ pr_info("set h_active=%d, v_active=%d\n",
+ val[0], val[1]);
+ pr_info("set h_period=%d, v_period=%d\n",
+ val[2], val[3]);
+ pr_info("set lcd_bits=%d\n", val[4]);
+ lcd_tcon_config(pconf);
+ lcd_debug_config_update();
+ } else {
+ LCDERR("invalid data\n");
+ return -EINVAL;
+ }
+ } else if (buf[1] == 'i') { /* bit */
+ ret = sscanf(buf, "bit %d", &val[0]);
+ if (ret == 1) {
+ pconf->lcd_basic.lcd_bits = val[4];
+ pr_info("set lcd_bits=%d\n", val[4]);
+ lcd_debug_config_update();
+ } else {
+ LCDERR("invalid data\n");
+ return -EINVAL;
+ }
+ }
+ break;
+ case 's': /* sync */
+ ret = sscanf(buf, "sync %d %d %d %d %d %d",
+ &val[0], &val[1], &val[2], &val[3], &val[4], &val[5]);
+ if (ret == 6) {
+ pconf->lcd_timing.hsync_width = val[0];
+ pconf->lcd_timing.hsync_bp = val[1];
+ pconf->lcd_timing.hsync_pol = val[2];
+ pconf->lcd_timing.vsync_width = val[3];
+ pconf->lcd_timing.vsync_bp = val[4];
+ pconf->lcd_timing.vsync_pol = val[5];
+ pr_info("set hsync width=%d, bp=%d, pol=%d\n",
+ val[0], val[1], val[2]);
+ pr_info("set vsync width=%d, bp=%d, pol=%d\n",
+ val[3], val[4], val[5]);
+ lcd_tcon_config(pconf);
+ lcd_debug_config_update();
+ } else {
+ LCDERR("invalid data\n");
+ return -EINVAL;
+ }
+ break;
+ case 't': /* test */
+ ret = sscanf(buf, "test %d", &temp);
+ if (ret == 1) {
+ lcd_drv->lcd_test_flag = (unsigned char)temp;
+ lcd_debug_test(temp);
+ } else {
+ LCDERR("invalid data\n");
+ return -EINVAL;
+ }
+ break;
+ case 'i': /* info */
+ LCDPR("driver version: %s\n", lcd_drv->version);
+ lcd_info_print();
+ break;
+ case 'r':
+ if (buf[2] == 'g') { /* reg */
+ LCDPR("driver version: %s\n", lcd_drv->version);
+ lcd_reg_print();
+ } else if (buf[2] == 's') { /* reset */
+ lcd_drv->module_reset();
+ } else if (buf[2] == 'n') { /* range */
+ ret = sscanf(buf, "range %d %d %d %d %d %d",
+ &val[0], &val[1], &val[2], &val[3],
+ &val[4], &val[5]);
+ if (ret == 6) {
+ pconf->lcd_basic.h_period_min = val[0];
+ pconf->lcd_basic.h_period_max = val[1];
+ pconf->lcd_basic.h_period_min = val[2];
+ pconf->lcd_basic.v_period_max = val[3];
+ pconf->lcd_basic.lcd_clk_min = val[4];
+ pconf->lcd_basic.lcd_clk_max = val[5];
+ pr_info("set h_period min=%d, max=%d\n",
+ pconf->lcd_basic.h_period_min,
+ pconf->lcd_basic.h_period_max);
+ pr_info("set v_period min=%d, max=%d\n",
+ pconf->lcd_basic.v_period_min,
+ pconf->lcd_basic.v_period_max);
+ pr_info("set pclk min=%d, max=%d\n",
+ pconf->lcd_basic.lcd_clk_min,
+ pconf->lcd_basic.lcd_clk_max);
+ } else {
+ LCDERR("invalid data\n");
+ return -EINVAL;
+ }
+ }
+ break;
+ case 'd': /* dump */
+ LCDPR("driver version: %s\n", lcd_drv->version);
+ lcd_info_print();
+ pr_info("\n");
+ lcd_reg_print();
+ pr_info("\n");
+ lcd_hdr_info_print();
+ break;
+ case 'k': /* key */
+ LCDPR("key_valid: %d, config_load: %d\n",
+ lcd_drv->lcd_key_valid, lcd_drv->lcd_config_load);
+ if (lcd_drv->lcd_key_valid)
+ lcd_unifykey_print();
+ break;
+ case 'h': /* hdr */
+ lcd_hdr_info_print();
+ break;
+ case 'p': /* power */
+ ret = sscanf(buf, "power %d", &temp);
+ if (ret == 1) {
+ mutex_lock(&lcd_power_mutex);
+ LCDPR("power: %d\n", temp);
+ if (temp) {
+ aml_lcd_notifier_call_chain(
+ LCD_EVENT_IF_POWER_ON, NULL);
+ } else {
+ aml_lcd_notifier_call_chain(
+ LCD_EVENT_IF_POWER_OFF, NULL);
+ }
+ mutex_unlock(&lcd_power_mutex);
+ } else {
+ LCDERR("invalid data\n");
+ return -EINVAL;
+ }
+ break;
+ case 'v':
+ ret = sscanf(buf, "vout %d", &temp);
+ if (ret == 1) {
+ LCDPR("vout_serve bypass: %d\n", temp);
+ lcd_vout_serve_bypass = temp;
+ } else {
+ LCDERR("invalid data\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ LCDERR("wrong command\n");
+ break;
+ }
+
+ return count;
+}
+
+static ssize_t lcd_debug_enable_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ unsigned int temp = 1;
+
+ ret = kstrtouint(buf, 10, &temp);
+ if (temp) {
+ mutex_lock(&lcd_power_mutex);
+ aml_lcd_notifier_call_chain(LCD_EVENT_POWER_ON, NULL);
+ mutex_unlock(&lcd_power_mutex);
+ } else {
+ mutex_lock(&lcd_power_mutex);
+ aml_lcd_notifier_call_chain(LCD_EVENT_POWER_OFF, NULL);
+ mutex_unlock(&lcd_power_mutex);
+ }
+
+ return count;
+}
+
+static ssize_t lcd_debug_power_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ lcd_power_info_print(lcd_drv->lcd_config, 1);
+ lcd_power_info_print(lcd_drv->lcd_config, 0);
+
+ return sprintf(buf, "\n");
+}
+
+static ssize_t lcd_debug_power_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ unsigned int i, delay;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_power_ctrl_s *lcd_power;
+
+ lcd_power = lcd_drv->lcd_config->lcd_power;
+ switch (buf[1]) {
+ case 'n': /* on */
+ ret = sscanf(buf, "on %d %d", &i, &delay);
+ if (ret == 2) {
+ if (i >= lcd_power->power_on_step_max) {
+ pr_info("invalid power_on step: %d\n", i);
+ return -EINVAL;
+ }
+ lcd_power->power_on_step[i].delay = delay;
+ pr_info("set power_on step %d delay: %dms\n",
+ i, delay);
+ } else {
+ pr_info("invalid data\n");
+ return -EINVAL;
+ }
+ break;
+ case 'f': /* off */
+ ret = sscanf(buf, "off %d %d", &i, &delay);
+ if (ret == 1) {
+ if (i >= lcd_power->power_off_step_max) {
+ pr_info("invalid power_off step: %d\n", i);
+ return -EINVAL;
+ }
+ lcd_power->power_off_step[i].delay = delay;
+ pr_info("set power_off step %d delay: %dms\n",
+ i, delay);
+ } else {
+ pr_info("invalid data\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ pr_info("wrong command\n");
+ break;
+ }
+
+ return count;
+}
+
+static ssize_t lcd_debug_frame_rate_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ unsigned int sync_duration;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+
+ pconf = lcd_drv->lcd_config;
+ sync_duration = pconf->lcd_timing.sync_duration_num * 100;
+ sync_duration = sync_duration / pconf->lcd_timing.sync_duration_den;
+
+ return sprintf(buf, "get frame_rate: %u.%02uHz, fr_adjust_type: %d\n",
+ (sync_duration / 100), (sync_duration % 100),
+ pconf->lcd_timing.fr_adjust_type);
+}
+
+static ssize_t lcd_debug_frame_rate_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ unsigned int temp = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ switch (buf[0]) {
+ case 't':
+ ret = sscanf(buf, "type %d", &temp);
+ if (ret == 1) {
+ lcd_drv->lcd_config->lcd_timing.fr_adjust_type = temp;
+ pr_info("set fr_adjust_type: %d\n", temp);
+ } else {
+ pr_info("invalid data\n");
+ return -EINVAL;
+ }
+ break;
+ case 's':
+ ret = sscanf(buf, "set %d", &temp);
+ if (ret == 1) {
+ pr_info("set frame rate(*100): %d\n", temp);
+ aml_lcd_notifier_call_chain(
+ LCD_EVENT_FRAME_RATE_ADJUST, &temp);
+ } else {
+ pr_info("invalid data\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ pr_info("wrong command\n");
+ break;
+ }
+
+ return count;
+}
+
+static ssize_t lcd_debug_fr_policy_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ return sprintf(buf, "fr_auto_policy: %d\n", lcd_drv->fr_auto_policy);
+}
+
+static ssize_t lcd_debug_fr_policy_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ unsigned int temp = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ ret = kstrtouint(buf, 10, &temp);
+ lcd_drv->fr_auto_policy = temp;
+ pr_info("set fr_auto_policy: %d\n", temp);
+
+ return count;
+}
+
+static ssize_t lcd_debug_ss_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "get lcd pll spread spectrum: %s\n",
+ lcd_get_spread_spectrum());
+}
+
+static ssize_t lcd_debug_ss_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ unsigned int temp = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ ret = kstrtouint(buf, 10, &temp);
+ lcd_drv->lcd_config->lcd_timing.ss_level = temp;
+ lcd_set_spread_spectrum();
+
+ return count;
+}
+
+static ssize_t lcd_debug_clk_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ lcd_clk_config_print();
+ return sprintf(buf, "\n");
+}
+
+static ssize_t lcd_debug_test_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ unsigned int temp = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ ret = kstrtouint(buf, 10, &temp);
+ lcd_drv->lcd_test_flag = (unsigned char)temp;
+ lcd_debug_test(temp);
+
+ return count;
+}
+
+static ssize_t lcd_debug_mute_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ return sprintf(buf, "get lcd mute state: %d\n", lcd_drv->lcd_mute);
+}
+
+static ssize_t lcd_debug_mute_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ unsigned int temp = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ ret = kstrtouint(buf, 10, &temp);
+ lcd_drv->lcd_mute = (unsigned char)temp;
+ lcd_mute_setting(lcd_drv->lcd_mute);
+
+ return count;
+}
+
+static void lcd_debug_reg_write(unsigned int reg, unsigned int data,
+ unsigned int type)
+{
+ switch (type) {
+ case 0: /* vcbus */
+ lcd_vcbus_write(reg, data);
+ pr_info("write vcbus [0x%04x] = 0x%08x, readback 0x%08x\n",
+ reg, data, lcd_vcbus_read(reg));
+ break;
+ case 1: /* hiu */
+ lcd_hiu_write(reg, data);
+ pr_info("write hiu [0x%04x] = 0x%08x, readback 0x%08x\n",
+ reg, data, lcd_hiu_read(reg));
+ break;
+ case 2: /* cbus */
+ lcd_cbus_write(reg, data);
+ pr_info("write cbus [0x%04x] = 0x%08x, readback 0x%08x\n",
+ reg, data, lcd_cbus_read(reg));
+ break;
+ case 3: /* periphs */
+ lcd_periphs_write(reg, data);
+ pr_info("write periphs [0x%04x] = 0x%08x, readback 0x%08x\n",
+ reg, data, lcd_periphs_read(reg));
+ break;
+ case 4: /* mipi_dsi_host */
+ dsi_host_write(reg, data);
+ pr_info("write mipi_dsi_host [0x%04x] = 0x%08x, readback 0x%08x\n",
+ reg, data, dsi_host_read(reg));
+ break;
+ case 5: /* mipi_dsi_phy */
+ dsi_phy_write(reg, data);
+ pr_info("write mipi_dsi_phy [0x%04x] = 0x%08x, readback 0x%08x\n",
+ reg, data, dsi_phy_read(reg));
+ break;
+ default:
+ break;
+ }
+}
+
+static void lcd_debug_reg_read(unsigned int reg, unsigned int bus)
+{
+ switch (bus) {
+ case 0: /* vcbus */
+ pr_info("read vcbus [0x%04x] = 0x%08x\n",
+ reg, lcd_vcbus_read(reg));
+ break;
+ case 1: /* hiu */
+ pr_info("read hiu [0x%04x] = 0x%08x\n",
+ reg, lcd_hiu_read(reg));
+ break;
+ case 2: /* cbus */
+ pr_info("read cbus [0x%04x] = 0x%08x\n",
+ reg, lcd_cbus_read(reg));
+ break;
+ case 3: /* periphs */
+ pr_info("read periphs [0x%04x] = 0x%08x\n",
+ reg, lcd_periphs_read(reg));
+ break;
+ case 4: /* mipi_dsi_host */
+ pr_info("read mipi_dsi_host [0x%04x] = 0x%08x\n",
+ reg, dsi_host_read(reg));
+ break;
+ case 5: /* mipi_dsi_phy */
+ pr_info("read mipi_dsi_phy [0x%04x] = 0x%08x\n",
+ reg, dsi_phy_read(reg));
+ break;
+ default:
+ break;
+ }
+}
+
+static void lcd_debug_reg_dump(unsigned int reg, unsigned int num,
+ unsigned int bus)
+{
+ int i;
+
+ switch (bus) {
+ case 0: /* vcbus */
+ pr_info("dump vcbus regs:\n");
+ for (i = 0; i < num; i++) {
+ pr_info("[0x%04x] = 0x%08x\n",
+ (reg + i), lcd_vcbus_read(reg + i));
+ }
+ break;
+ case 1: /* hiu */
+ pr_info("dump hiu-bus regs:\n");
+ for (i = 0; i < num; i++) {
+ pr_info("[0x%04x] = 0x%08x\n",
+ (reg + i), lcd_hiu_read(reg + i));
+ }
+ break;
+ case 2: /* cbus */
+ pr_info("dump cbus regs:\n");
+ for (i = 0; i < num; i++) {
+ pr_info("[0x%04x] = 0x%08x\n",
+ (reg + i), lcd_cbus_read(reg + i));
+ }
+ break;
+ case 3: /* periphs */
+ pr_info("dump periphs-bus regs:\n");
+ for (i = 0; i < num; i++) {
+ pr_info("[0x%04x] = 0x%08x\n",
+ (reg + i), lcd_periphs_read(reg + i));
+ }
+ break;
+ case 4: /* mipi_dsi_host */
+ pr_info("dump mipi_dsi_host regs:\n");
+ for (i = 0; i < num; i++) {
+ pr_info("[0x%04x] = 0x%08x\n",
+ (reg + i), dsi_host_read(reg + i));
+ }
+ break;
+ case 5: /* mipi_dsi_phy */
+ pr_info("dump mipi_dsi_phy regs:\n");
+ for (i = 0; i < num; i++) {
+ pr_info("[0x%04x] = 0x%08x\n",
+ (reg + i), dsi_phy_read(reg + i));
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static ssize_t lcd_debug_reg_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ unsigned int bus = 0;
+ unsigned int reg32 = 0, data32 = 0;
+
+ switch (buf[0]) {
+ case 'w':
+ if (buf[1] == 'v') {
+ ret = sscanf(buf, "wv %x %x", ®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 <clk_pol> <de_valid> <hvsync_valid> <rb_swpa> <bit_swap> > ttl ; set ttl config\n"
+"data format:\n"
+" <clk_pol> : 0=negative, 1=positive\n"
+" <de_valid> : for DE, 0=invalid, 1=valid\n"
+" <hvsync_valid> : for hvsync, 0=invalid, 1=valid\n"
+" <rb_swpa> : for R/B port, 0=normal, 1=swap\n"
+" <bit_swap> : for RGB MSB/LSB, 0=normal, 1=swap\n"
+"\n"
+};
+
+static const char *lcd_lvds_debug_usage_str = {
+"Usage:\n"
+" echo <repack> <dual_port> <pn_swap> <port_swap> <lane_reverse> > lvds ; set lvds config\n"
+"data format:\n"
+" <repack> : 0=JEIDA mode, 1=VESA mode\n"
+" <dual_port> : 0=single port, 1=dual port\n"
+" <pn_swap> : 0=normal, 1=swap p/n channels\n"
+" <port_swap> : 0=normal, 1=swap A/B port\n"
+" <lane_reverse> : 0=normal, 1=swap A0-A4/B0-B4\n"
+"\n"
+" echo <vswing> <preem> > phy ; set vbyone phy config\n"
+"data format:\n"
+" <vswing> : vswing level, support 0~7\n"
+" <preem> : preemphasis level, support 0~7\n"
+"\n"
+};
+
+static const char *lcd_vbyone_debug_usage_str = {
+"Usage:\n"
+" echo <lane_count> <region_num> <byte_mode> > vbyone ; set vbyone config\n"
+"data format:\n"
+" <lane_count> : 4/8/16\n"
+" <region_num> : 1/2\n"
+" <byte_mode> : 3/4/5\n"
+"\n"
+" echo <vswing> <preem> > phy ; set vbyone phy config\n"
+"data format:\n"
+" <vswing> : vswing level, support 0~7\n"
+" <preem> : preemphasis level, support 0~7\n"
+" <byte_mode> : 3/4/5\n"
+"\n"
+" echo intr <state> <en> > vbyone; enable or disable vbyone interrupt\n"
+"data format:\n"
+" <state> : 0=temp no use intr, 1=temp use intr. keep effect until reset lcd driver\n"
+" <en> : 0=disable intr, 1=enable intr\n"
+"\n"
+" echo vintr <en> > vbyone; enable or disable vbyone interrupt\n"
+"data format:\n"
+" <en> : 0=disable vsync monitor intr, 1=enable vsync monitor intr\n"
+"\n"
+};
+
+static const char *lcd_mipi_debug_usage_str = {
+"Usage:\n"
+" echo <lane_num> <bit_rate_max> <factor> <op_mode_init> <op_mode_disp> <vid_mode_type> <clk_lp_continuous> <phy_stop_wait> > mipi ; set mpi config\n"
+"data format:\n"
+" <lane_num> : 1/2/3/4\n"
+" <bit_rate_max> : unit in MHz\n"
+" <factor>: : special adjust, 0 for default\n"
+" <op_mode_init> : operation mode for init (0=video mode, 1=command mode)\n"
+" <op_mode_disp> : operation mode for display (0=video mode, 1=command mode)\n"
+" <vid_mode_type> : video mode type (0=sync_pulse, 1=sync_event, 2=burst)\n"
+" <clk_lp_continuous> : 0=stop, 1=continue\n"
+" <phy_stop_wait> : 0=auto, 1=standard, 2=slow\n"
+"\n"
+};
+
+static const char *lcd_edp_debug_usage_str = {
+"Usage:\n"
+" echo <link_rate> <lane_count> <edid_timing_used> <sync_clock_mode> > edp ; set edp config\n"
+"data format:\n"
+" <link_rate> : 0=1.62G, 1=2.7G\n"
+" <lane_count> : 1/2/4\n"
+" <edid_timing_used> : 0=no use, 1=use, default=0\n"
+" <sync_clock_mode> : 0=asyncronous, 1=synchronous, default=1\n"
+"\n"
+};
+
+static ssize_t lcd_ttl_debug_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", lcd_ttl_debug_usage_str);
+}
+
+static ssize_t lcd_lvds_debug_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", lcd_lvds_debug_usage_str);
+}
+
+static ssize_t lcd_vx1_debug_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", lcd_vbyone_debug_usage_str);
+}
+
+static ssize_t lcd_mipi_debug_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", lcd_mipi_debug_usage_str);
+}
+
+static ssize_t lcd_edp_debug_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", lcd_edp_debug_usage_str);
+}
+
+static ssize_t lcd_ttl_debug_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct ttl_config_s *ttl_conf;
+ unsigned int temp[5];
+
+ ttl_conf = lcd_drv->lcd_config->lcd_control.ttl_config;
+ ret = sscanf(buf, "%d %d %d %d %d",
+ &temp[0], &temp[1], &temp[2], &temp[3], &temp[4]);
+ if (ret == 5) {
+ pr_info("set ttl config:\n"
+ "clk_pol=%d, de_valid=%d, de_valid=%d\n"
+ "rb_swap=%d, bit_swap=%d\n",
+ temp[0], temp[1], temp[2], temp[3], temp[4]);
+ ttl_conf->clk_pol = temp[0];
+ ttl_conf->sync_valid = ((temp[1] << 1) | temp[2]);
+ ttl_conf->swap_ctrl = ((temp[3] << 1) | temp[4]);
+ lcd_debug_config_update();
+ } else {
+ pr_info("invalid data\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t lcd_lvds_debug_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lvds_config_s *lvds_conf;
+
+ lvds_conf = lcd_drv->lcd_config->lcd_control.lvds_config;
+ ret = sscanf(buf, "%d %d %d %d %d",
+ &lvds_conf->lvds_repack, &lvds_conf->dual_port,
+ &lvds_conf->pn_swap, &lvds_conf->port_swap,
+ &lvds_conf->lane_reverse);
+ if (ret == 5 || ret == 4) {
+ pr_info("set lvds config:\n"
+ "repack=%d, dual_port=%d, pn_swap=%d, port_swap=%d, lane_reverse=%d\n",
+ lvds_conf->lvds_repack, lvds_conf->dual_port,
+ lvds_conf->pn_swap, lvds_conf->port_swap,
+ lvds_conf->lane_reverse);
+ lcd_debug_config_update();
+ } else {
+ pr_info("invalid data\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static int vx1_intr_state = 1;
+static ssize_t lcd_vx1_debug_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct vbyone_config_s *vx1_conf;
+ int val[2];
+
+ vx1_conf = lcd_drv->lcd_config->lcd_control.vbyone_config;
+ if (buf[0] == 'i') { /* intr */
+ ret = sscanf(buf, "intr %d %d", &val[0], &val[1]);
+ if (ret == 1) {
+ pr_info("set vbyone interrupt enable: %d\n", val[0]);
+ vx1_intr_state = val[0];
+ lcd_vbyone_interrupt_enable(vx1_intr_state);
+ } else if (ret == 2) {
+ pr_info("set vbyone interrupt enable: %d %d\n",
+ val[0], val[1]);
+ vx1_intr_state = val[0];
+ vx1_conf->intr_en = val[1];
+ lcd_vbyone_interrupt_enable(vx1_intr_state);
+ } else {
+ pr_info("vx1_intr_enable: %d %d\n",
+ vx1_intr_state, vx1_conf->intr_en);
+ return -EINVAL;
+ }
+ } else if (buf[0] == 'v') { /* vintr */
+ ret = sscanf(buf, "vintr %d", &val[0]);
+ if (ret == 1) {
+ pr_info("set vbyone vsync interrupt enable: %d\n",
+ val[0]);
+ vx1_conf->vsync_intr_en = val[0];
+ lcd_vbyone_interrupt_enable(vx1_intr_state);
+ } else {
+ pr_info("vx1_vsync_intr_enable: %d\n",
+ vx1_conf->vsync_intr_en);
+ return -EINVAL;
+ }
+ } else {
+ ret = sscanf(buf, "%d %d %d", &vx1_conf->lane_count,
+ &vx1_conf->region_num, &vx1_conf->byte_mode);
+ if (ret == 3) {
+ pr_info("set vbyone config:\n"
+ "lane_count=%d, region_num=%d, byte_mode=%d\n",
+ vx1_conf->lane_count, vx1_conf->region_num,
+ vx1_conf->byte_mode);
+ lcd_debug_config_update();
+ } else {
+ pr_info("invalid data\n");
+ return -EINVAL;
+ }
+ }
+
+ return count;
+}
+
+static ssize_t lcd_mipi_debug_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct dsi_config_s *dsi_conf;
+ int val[8];
+
+ dsi_conf = lcd_drv->lcd_config->lcd_control.mipi_config;
+ ret = sscanf(buf, "%d %d %d %d %d %d %d %d",
+ &val[0], &val[1], &val[2], &val[3],
+ &val[4], &val[5], &val[6], &val[7]);
+ if (ret >= 2) {
+ dsi_conf->lane_num = (unsigned char)val[0];
+ dsi_conf->bit_rate_max = val[1];
+ dsi_conf->factor_numerator = val[2];
+ dsi_conf->operation_mode_init = (unsigned char)val[3];
+ dsi_conf->operation_mode_display = (unsigned char)val[4];
+ dsi_conf->video_mode_type = (unsigned char)val[5];
+ dsi_conf->clk_lp_continuous = (unsigned char)val[6];
+ dsi_conf->phy_stop_wait = (unsigned char)val[7];
+ pr_info("set mipi_dsi config:\n"
+ "lane_num=%d, bit_rate_max=%dMhz, factor_numerator=%d\n"
+ "operation_mode_init=%d, operation_mode_display=%d\n"
+ "video_mode_type=%d\n"
+ "clk_lp_continuous=%d, phy_stop_wait=%d\n\n",
+ dsi_conf->lane_num,
+ dsi_conf->bit_rate_max,
+ dsi_conf->factor_numerator,
+ dsi_conf->operation_mode_init,
+ dsi_conf->operation_mode_display,
+ dsi_conf->video_mode_type,
+ dsi_conf->clk_lp_continuous,
+ dsi_conf->phy_stop_wait);
+ lcd_debug_config_update();
+ } else {
+ pr_info("invalid data\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t lcd_edp_debug_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ pr_info("to do\n");
+
+ return count;
+}
+
+static void lcd_phy_config_update(unsigned int *para, int cnt)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+ struct lvds_config_s *lvdsconf;
+ int type;
+ unsigned int data32, vswing, preem, ext_pullup;
+
+ pconf = lcd_drv->lcd_config;
+ type = pconf->lcd_basic.lcd_type;
+ switch (type) {
+ case LCD_LVDS:
+ lvdsconf = pconf->lcd_control.lvds_config;
+ if (cnt == 4) {
+ if ((para[0] > 7) || (para[1] > 7) ||
+ (para[2] > 3) || (para[3] > 7)) {
+ LCDERR("%s: wrong value:\n", __func__);
+ pr_info("vswing=0x%x, preemphasis=0x%x\n",
+ para[0], para[1]);
+ pr_info("clk_vswing=0x%x, clk_preem=0x%x\n",
+ para[2], para[3]);
+ return;
+ }
+
+ lvdsconf->phy_vswing = para[0];
+ lvdsconf->phy_preem = para[1];
+ lvdsconf->phy_clk_vswing = para[2];
+ lvdsconf->phy_clk_preem = para[3];
+
+ data32 = lcd_hiu_read(HHI_DIF_CSI_PHY_CNTL1);
+ data32 &= ~((0x7 << 26) | (0x7 << 0));
+ data32 |= ((para[0] << 26) | (para[1] << 0));
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, data32);
+ data32 = lcd_hiu_read(HHI_DIF_CSI_PHY_CNTL3);
+ data32 &= ~((0x3 << 8) | (0x7 << 5));
+ data32 |= ((para[2] << 8) | (para[3] << 5));
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, data32);
+
+ LCDPR("%s:\n", __func__);
+ pr_info("vswing=0x%x, preemphasis=0x%x\n",
+ para[0], para[1]);
+ pr_info("clk_vswing=0x%x, clk_preemphasis=0x%x\n",
+ para[2], para[3]);
+ } else if (cnt == 2) {
+ if ((para[0] > 7) || (para[1] > 7)) {
+ LCDERR("%s: wrong value:\n", __func__);
+ pr_info("vswing=0x%x, preemphasis=0x%x\n",
+ para[0], para[1]);
+ return;
+ }
+
+ lvdsconf->phy_vswing = para[0];
+ lvdsconf->phy_preem = para[1];
+
+ data32 = lcd_hiu_read(HHI_DIF_CSI_PHY_CNTL1);
+ data32 &= ~((0x7 << 26) | (0x7 << 0));
+ data32 |= ((para[0] << 26) | (para[1] << 0));
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, data32);
+
+ LCDPR("%s: vswing=0x%x, preemphasis=0x%x\n",
+ __func__, para[0], para[1]);
+ } else {
+ LCDERR("%s: invalid parameters cnt: %d\n",
+ __func__, cnt);
+ }
+ break;
+ case LCD_VBYONE:
+ if (cnt >= 2) {
+ ext_pullup = (para[0] >> 4) & 1;
+ vswing = para[0] & 0xf;
+ preem = para[1];
+ if ((vswing > 7) || (preem > 7)) {
+ LCDERR("%s: wrong value:\n", __func__);
+ pr_info("vswing=0x%x, preemphasis=0x%x\n",
+ vswing, preem);
+ return;
+ }
+
+ pconf->lcd_control.vbyone_config->phy_vswing = para[0];
+ pconf->lcd_control.vbyone_config->phy_preem = para[1];
+
+ data32 = lcd_hiu_read(HHI_DIF_CSI_PHY_CNTL1);
+ data32 &= ~((0x7 << 3) | (1 << 10));
+ data32 |= ((vswing << 3) | (ext_pullup << 10));
+ if (ext_pullup)
+ data32 &= ~(1 << 15);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, data32);
+ data32 = lcd_hiu_read(HHI_DIF_CSI_PHY_CNTL2);
+ data32 &= ~(0x7 << 20);
+ data32 |= (preem << 20);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, data32);
+
+ LCDPR("%s: vswing=0x%x, preemphasis=0x%x\n",
+ __func__, para[0], para[1]);
+ } else {
+ LCDERR("%s: invalid parameters cnt: %d\n",
+ __func__, cnt);
+ }
+ break;
+ default:
+ LCDERR("%s: not support lcd_type: %s\n",
+ __func__, lcd_type_type_to_str(type));
+ break;
+ }
+}
+
+static ssize_t lcd_phy_debug_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+ unsigned int vswing = 0xff, preem = 0xff;
+ unsigned int clk_vswing = 0xff, clk_preem = 0xff;
+ ssize_t len = 0;
+
+ pconf = lcd_drv->lcd_config;
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_LVDS:
+ vswing = pconf->lcd_control.lvds_config->phy_vswing;
+ preem = pconf->lcd_control.lvds_config->phy_preem;
+ clk_vswing = pconf->lcd_control.lvds_config->phy_clk_vswing;
+ clk_preem = pconf->lcd_control.lvds_config->phy_clk_preem;
+ len += sprintf(buf+len, "%s:\n", __func__);
+ len += sprintf(buf+len, "vswing=0x%x, preemphasis=0x%x\n",
+ vswing, preem);
+ len += sprintf(buf+len,
+ "clk_vswing=0x%x, clk_preemphasis=0x%x\n",
+ clk_vswing, clk_preem);
+ break;
+ case LCD_VBYONE:
+ vswing = pconf->lcd_control.vbyone_config->phy_vswing;
+ preem = pconf->lcd_control.vbyone_config->phy_preem;
+ len += sprintf(buf+len, "%s:\n", __func__);
+ len += sprintf(buf+len, "vswing=0x%x, preemphasis=0x%x\n",
+ vswing, preem);
+ break;
+ default:
+ len = sprintf(buf, "%s: invalid lcd_type: %d\n",
+ __func__, pconf->lcd_basic.lcd_type);
+ break;
+ }
+ return len;
+}
+
+static ssize_t lcd_phy_debug_store(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ int ret = 0;
+ unsigned int para[4];
+
+ ret = sscanf(buf, "%x %x %x %x",
+ ¶[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;
+}
+
--- /dev/null
+
+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
+
--- /dev/null
+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
+
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/ext_default.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+
+#define LCD_EXTERN_INDEX 0
+#define LCD_EXTERN_NAME "ext_default"
+
+#define LCD_EXTERN_TYPE LCD_EXTERN_I2C
+
+#define LCD_EXTERN_I2C_ADDR (0x1c) /* 7bit address */
+#define LCD_EXTERN_I2C_ADDR2 (0xff) /* 7bit address */
+#define LCD_EXTERN_I2C_BUS AML_I2C_MASTER_A
+
+#define SPI_GPIO_CS 0 /* index */
+#define SPI_GPIO_CLK 1 /* index */
+#define SPI_GPIO_DATA 2 /* index */
+#define SPI_CLK_FREQ 10000 /* Hz */
+#define SPI_CLK_POL 1
+
+static struct i2c_client *aml_default_i2c_client;
+static struct i2c_client *aml_default_i2c2_client;
+static struct lcd_extern_config_s *ext_config;
+
+#define LCD_EXTERN_CMD_SIZE 9
+static unsigned char init_on_table[] = {
+ 0x00, 0x20, 0x01, 0x02, 0x00, 0x40, 0xFF, 0x00, 0x00,
+ 0x00, 0x80, 0x02, 0x00, 0x40, 0x62, 0x51, 0x73, 0x00,
+ 0x00, 0x61, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xC1, 0x05, 0x0F, 0x00, 0x08, 0x70, 0x00, 0x00,
+ 0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3D, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xED, 0x0D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x23, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, /* delay 10ms */
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ending */
+};
+
+static unsigned char init_off_table[] = {
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ending */
+};
+
+static int lcd_extern_i2c_write(struct i2c_client *i2client,
+ unsigned char *buff, unsigned int len)
+{
+ int ret = 0;
+ struct i2c_msg msg[] = {
+ {
+ .addr = i2client->addr,
+ .flags = 0,
+ .len = len,
+ .buf = buff,
+ }
+ };
+
+ ret = i2c_transfer(i2client->adapter, msg, 1);
+ if (ret < 0)
+ EXTERR("i2c write failed [addr 0x%02x]\n", i2client->addr);
+
+ return ret;
+}
+
+static int lcd_extern_reg_read(unsigned char reg, unsigned char *buf)
+{
+ int ret = 0;
+
+ return ret;
+}
+
+static int lcd_extern_reg_write(unsigned char reg, unsigned char value)
+{
+ int ret = 0;
+
+ return ret;
+}
+
+static int lcd_extern_spi_write(unsigned char *buf, int len)
+{
+ EXTPR("to do\n");
+ return 0;
+}
+
+static int lcd_extern_power_cmd(unsigned char *init_table)
+{
+ int i = 0, len;
+ int ret = 0;
+
+ len = ext_config->cmd_size;
+ if (len < 1) {
+ EXTERR("%s: cmd_size %d is invalid\n", __func__, len);
+ return -1;
+ }
+ if (len == LCD_EXTERN_DYNAMIC_LEN) {
+ switch (ext_config->type) {
+ case LCD_EXTERN_I2C:
+ while (i <= LCD_EXTERN_INIT_TABLE_MAX) {
+ if (init_table[i] == LCD_EXTERN_INIT_END) {
+ break;
+ } else if (init_table[i] ==
+ LCD_EXTERN_INIT_NONE) {
+ /* do nothing, only for delay */
+ if (init_table[i+1] > 0)
+ mdelay(init_table[i+1]);
+ i += 2;
+ } else if (init_table[i] ==
+ LCD_EXTERN_INIT_GPIO) {
+ if (init_table[i+1] < LCD_GPIO_MAX) {
+ lcd_extern_gpio_set(
+ init_table[i+1],
+ init_table[i+2]);
+ }
+ if (init_table[i+3] > 0)
+ mdelay(init_table[i+3]);
+ i += 4;
+ } else if (init_table[i] ==
+ LCD_EXTERN_INIT_CMD) {
+ ret = lcd_extern_i2c_write(
+ aml_default_i2c_client,
+ &init_table[i+2],
+ init_table[i+1]-1);
+ if (init_table[i+init_table[i+1]+1] > 0)
+ mdelay(init_table[i+
+ init_table[i+1]+1]);
+ i += (init_table[i+1] + 2);
+ } else if (init_table[i] ==
+ LCD_EXTERN_INIT_CMD2) {
+ ret = lcd_extern_i2c_write(
+ aml_default_i2c2_client,
+ &init_table[i+2],
+ init_table[i+1]-1);
+ if (init_table[i+init_table[i+1]+1] > 0)
+ mdelay(init_table[i+
+ init_table[i+1]+1]);
+ i += (init_table[i+1] + 2);
+ } else {
+ EXTERR("%s(%d: %s): type %d invalid\n",
+ __func__, ext_config->index,
+ ext_config->name,
+ ext_config->type);
+ }
+ }
+ break;
+ case LCD_EXTERN_SPI:
+ while (i <= LCD_EXTERN_INIT_TABLE_MAX) {
+ if (init_table[i] == LCD_EXTERN_INIT_END) {
+ break;
+ } else if (init_table[i] ==
+ LCD_EXTERN_INIT_NONE) {
+ /* do nothing, only for delay */
+ if (init_table[i+1] > 0)
+ mdelay(init_table[i+1]);
+ i += 2;
+ } else if (init_table[i] ==
+ LCD_EXTERN_INIT_GPIO) {
+ if (init_table[i+1] < LCD_GPIO_MAX) {
+ lcd_extern_gpio_set(
+ init_table[i+1],
+ init_table[i+2]);
+ }
+ if (init_table[i+3] > 0)
+ mdelay(init_table[i+3]);
+ i += 4;
+ } else if (init_table[i] ==
+ LCD_EXTERN_INIT_CMD) {
+ ret = lcd_extern_spi_write(
+ &init_table[i+2],
+ init_table[i+1]-1);
+ if (init_table[i+init_table[i+1]+1] > 0)
+ mdelay(init_table[i+
+ init_table[i+1]+1]);
+ i += (init_table[i+1] + 2);
+ } else {
+ EXTERR("%s(%d: %s): type %d invalid\n",
+ __func__, ext_config->index,
+ ext_config->name,
+ ext_config->type);
+ }
+ }
+ break;
+ default:
+ EXTERR("%s(%d: %s): extern_type %d is not support\n",
+ __func__, ext_config->index,
+ ext_config->name, ext_config->type);
+ break;
+ }
+ } else {
+ switch (ext_config->type) {
+ case LCD_EXTERN_I2C:
+ while (i <= LCD_EXTERN_INIT_TABLE_MAX) {
+ if (init_table[i] == LCD_EXTERN_INIT_END) {
+ break;
+ } else if (init_table[i] ==
+ LCD_EXTERN_INIT_NONE) {
+ /* do nothing, only for delay */
+ } else if (init_table[i] ==
+ LCD_EXTERN_INIT_GPIO) {
+ if (init_table[i+1] < LCD_GPIO_MAX) {
+ lcd_extern_gpio_set(
+ init_table[i+1],
+ init_table[i+2]);
+ }
+ } else if (init_table[i] ==
+ LCD_EXTERN_INIT_CMD) {
+ ret = lcd_extern_i2c_write(
+ aml_default_i2c_client,
+ &init_table[i+1], (len-2));
+ } else if (init_table[i] ==
+ LCD_EXTERN_INIT_CMD2) {
+ ret = lcd_extern_i2c_write(
+ aml_default_i2c2_client,
+ &init_table[i+1], (len-2));
+ } else {
+ EXTERR("%s(%d: %s): type %d invalid\n",
+ __func__, ext_config->index,
+ ext_config->name,
+ ext_config->type);
+ }
+ if (init_table[i+len-1] > 0)
+ mdelay(init_table[i+len-1]);
+ i += len;
+ }
+ break;
+ case LCD_EXTERN_SPI:
+ while (i <= LCD_EXTERN_INIT_TABLE_MAX) {
+ if (init_table[i] == LCD_EXTERN_INIT_END) {
+ break;
+ } else if (init_table[i] ==
+ LCD_EXTERN_INIT_NONE) {
+ /* do nothing, only for delay */
+ } else if (init_table[i] ==
+ LCD_EXTERN_INIT_GPIO) {
+ if (init_table[i+1] < LCD_GPIO_MAX) {
+ lcd_extern_gpio_set(
+ init_table[i+1],
+ init_table[i+2]);
+ }
+ } else if (init_table[i] ==
+ LCD_EXTERN_INIT_CMD) {
+ ret = lcd_extern_spi_write(
+ &init_table[i+1], (len-1));
+ } else {
+ EXTERR("%s(%d: %s): type %d invalid\n",
+ __func__, ext_config->index,
+ ext_config->name,
+ ext_config->type);
+ }
+ if (init_table[i+len-1] > 0)
+ mdelay(init_table[i+len-1]);
+ i += len;
+ }
+ break;
+ default:
+ EXTERR("%s(%d: %s): extern_type %d is not support\n",
+ __func__, ext_config->index,
+ ext_config->name, ext_config->type);
+ break;
+ }
+ }
+ return ret;
+}
+
+static int lcd_extern_power_ctrl(int flag)
+{
+ int ret = 0;
+
+ if (flag)
+ ret = lcd_extern_power_cmd(ext_config->table_init_on);
+ else
+ ret = lcd_extern_power_cmd(ext_config->table_init_off);
+
+ EXTPR("%s(%d: %s): %d\n",
+ __func__, ext_config->index, ext_config->name, flag);
+ return ret;
+}
+
+static int lcd_extern_power_on(void)
+{
+ int ret;
+
+ ret = lcd_extern_power_ctrl(1);
+ return ret;
+}
+
+static int lcd_extern_power_off(void)
+{
+ int ret;
+
+ ret = lcd_extern_power_ctrl(0);
+ return ret;
+}
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ if (ext_drv == NULL) {
+ EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+ return -1;
+ }
+
+ if (ext_drv->config.type == LCD_EXTERN_MAX) { /* default for no dts */
+ ext_drv->config.index = LCD_EXTERN_INDEX;
+ ext_drv->config.type = LCD_EXTERN_TYPE;
+ strcpy(ext_drv->config.name, LCD_EXTERN_NAME);
+ ext_drv->config.cmd_size = LCD_EXTERN_CMD_SIZE;
+ switch (ext_drv->config.type) {
+ case LCD_EXTERN_I2C:
+ ext_drv->config.i2c_addr = LCD_EXTERN_I2C_ADDR;
+ ext_drv->config.i2c_addr2 = LCD_EXTERN_I2C_ADDR2;
+ ext_drv->config.i2c_bus = LCD_EXTERN_I2C_BUS;
+ break;
+ case LCD_EXTERN_SPI:
+ ext_drv->config.spi_gpio_cs = SPI_GPIO_CS;
+ ext_drv->config.spi_gpio_clk = SPI_GPIO_CLK;
+ ext_drv->config.spi_gpio_data = SPI_GPIO_DATA;
+ ext_drv->config.spi_clk_freq = SPI_CLK_FREQ;
+ ext_drv->config.spi_clk_pol = SPI_CLK_POL;
+ break;
+ default:
+ break;
+ }
+ }
+ if (ext_drv->config.table_init_loaded == 0) {
+ ext_drv->config.table_init_on = init_on_table;
+ ext_drv->config.table_init_off = init_off_table;
+ }
+ ext_drv->reg_read = lcd_extern_reg_read;
+ ext_drv->reg_write = lcd_extern_reg_write;
+ ext_drv->power_on = lcd_extern_power_on;
+ ext_drv->power_off = lcd_extern_power_off;
+
+ return 0;
+}
+
+static int aml_default_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ EXTERR("%s: functionality check failed\n", __func__);
+ else
+ aml_default_i2c_client = client;
+
+ EXTPR("%s OK\n", __func__);
+ return 0;
+}
+
+static int aml_default_i2c_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id aml_default_i2c_id[] = {
+ {LCD_EXTERN_NAME, 0},
+ { }
+};
+/* MODULE_DEVICE_TABLE(i2c, aml_T5800Q_id); */
+
+static struct i2c_driver aml_default_i2c_driver = {
+ .probe = aml_default_i2c_probe,
+ .remove = aml_default_i2c_remove,
+ .id_table = aml_default_i2c_id,
+ .driver = {
+ .name = LCD_EXTERN_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+int aml_lcd_extern_default_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ struct i2c_board_info i2c_info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *i2c_client;
+ int ret = 0;
+
+ ext_config = &ext_drv->config;
+
+ switch (ext_drv->config.type) {
+ case LCD_EXTERN_I2C:
+ aml_default_i2c_client = NULL;
+ aml_default_i2c2_client = NULL;
+ memset(&i2c_info, 0, sizeof(i2c_info));
+ adapter = i2c_get_adapter(ext_drv->config.i2c_bus);
+ if (!adapter) {
+ EXTERR("%s failed to get i2c adapter\n",
+ ext_drv->config.name);
+ return -1;
+ }
+
+ strncpy(i2c_info.type, ext_drv->config.name, I2C_NAME_SIZE);
+ i2c_info.addr = ext_drv->config.i2c_addr;
+ i2c_info.platform_data = &ext_drv->config;
+ i2c_info.flags = 0;
+ if (i2c_info.addr > 0x7f) {
+ EXTERR("%s invalid i2c address: 0x%02x\n",
+ ext_drv->config.name, ext_drv->config.i2c_addr);
+ return -1;
+ }
+ i2c_client = i2c_new_device(adapter, &i2c_info);
+ if (!i2c_client) {
+ EXTERR("%s failed to new i2c device\n",
+ ext_drv->config.name);
+ } else {
+ if (lcd_debug_print_flag) {
+ EXTPR("%s new i2c device succeed\n",
+ ext_drv->config.name);
+ }
+ }
+
+ if (!aml_default_i2c_client) {
+ ret = i2c_add_driver(&aml_default_i2c_driver);
+ if (ret) {
+ EXTERR("%s add i2c_driver failed\n",
+ ext_drv->config.name);
+ return -1;
+ }
+ }
+ break;
+ case LCD_EXTERN_SPI:
+ break;
+ default:
+ break;
+ }
+
+ ret = lcd_extern_driver_update(ext_drv);
+
+ if (lcd_debug_print_flag)
+ EXTPR("%s: %d\n", __func__, ret);
+ return ret;
+}
+
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/i2c_DLPC3439.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+
+#define LCD_EXTERN_NAME "i2c_DLPC3439"
+
+static struct i2c_client *aml_DLPC3439_i2c_client;
+static struct lcd_extern_config_s *ext_config;
+
+
+ /* Write: ImageCrop: 1920x1080
+ * W 36 10 00 00 00 00 80 07 38 04
+ */
+static unsigned char data_1[] = {0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x07, 0x38, 0x04};
+ /* Write: DisplaySize: 1920x1080
+ * W 36 12 80 07 38 04
+ */
+static unsigned char data_2[] = {0x12, 0x80, 0x07, 0x38, 0x04};
+ /* Write: InputImageSize: 1920x1080
+ * W 36 2e 80 07 38 04
+ */
+static unsigned char data_3[] = {0x2e, 0x80, 0x07, 0x38, 0x04};
+ /* Write: InputSourceSelect; 0 = External Video Port
+ * W 36 05 00
+ */
+static unsigned char data_4[] = {0x05, 0x00};
+ /* Write: VideoSourceFormatSelect: 0x43=RGB888
+ * W 36 07 43
+ */
+static unsigned char data_5[] = {0x07, 0x43};
+
+static int lcd_extern_i2c_write(struct i2c_client *i2client,
+ unsigned char *buff, unsigned int len)
+{
+ int ret = 0;
+ struct i2c_msg msg[] = {
+ {
+ .addr = i2client->addr,
+ .flags = 0,
+ .len = len,
+ .buf = buff,
+ }
+ };
+
+ ret = i2c_transfer(i2client->adapter, msg, 1);
+ if (ret < 0)
+ EXTERR("i2c write failed [addr 0x%02x]\n", i2client->addr);
+
+ return ret;
+}
+
+static int lcd_extern_power_on(void)
+{
+ int ret = 0;
+
+ lcd_extern_i2c_write(aml_DLPC3439_i2c_client, data_1, 9);
+ lcd_extern_i2c_write(aml_DLPC3439_i2c_client, data_2, 5);
+ lcd_extern_i2c_write(aml_DLPC3439_i2c_client, data_3, 5);
+ lcd_extern_i2c_write(aml_DLPC3439_i2c_client, data_4, 2);
+ lcd_extern_i2c_write(aml_DLPC3439_i2c_client, data_5, 2);
+
+ EXTPR("%s\n", __func__);
+ return ret;
+}
+
+static int lcd_extern_power_off(void)
+{
+ int ret = 0;
+
+ return ret;
+}
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ int ret = 0;
+
+ if (ext_drv) {
+ ext_drv->power_on = lcd_extern_power_on;
+ ext_drv->power_off = lcd_extern_power_off;
+ } else {
+ EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int aml_DLPC3439_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ EXTERR("%s: functionality check failed\n", __func__);
+ else
+ aml_DLPC3439_i2c_client = client;
+
+ EXTPR("%s OK\n", __func__);
+ return 0;
+}
+
+static int aml_DLPC3439_i2c_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id aml_DLPC3439_i2c_id[] = {
+ {LCD_EXTERN_NAME, 0},
+ { }
+};
+/* MODULE_DEVICE_TABLE(i2c, aml_DLPC3439_id); */
+
+static struct i2c_driver aml_DLPC3439_i2c_driver = {
+ .probe = aml_DLPC3439_i2c_probe,
+ .remove = aml_DLPC3439_i2c_remove,
+ .id_table = aml_DLPC3439_i2c_id,
+ .driver = {
+ .name = LCD_EXTERN_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+int aml_lcd_extern_i2c_DLPC3439_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ struct i2c_board_info i2c_info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *i2c_client;
+ int ret = 0;
+
+ ext_config = &ext_drv->config;
+ memset(&i2c_info, 0, sizeof(i2c_info));
+
+ adapter = i2c_get_adapter(ext_drv->config.i2c_bus);
+ if (!adapter) {
+ EXTERR("%s failed to get i2c adapter\n", ext_drv->config.name);
+ return -1;
+ }
+
+ strncpy(i2c_info.type, ext_drv->config.name, I2C_NAME_SIZE);
+ i2c_info.addr = ext_drv->config.i2c_addr;
+ i2c_info.platform_data = &ext_drv->config;
+ i2c_info.flags = 0;
+ if (i2c_info.addr > 0x7f) {
+ EXTERR("%s invalid i2c address: 0x%02x\n",
+ ext_drv->config.name, ext_drv->config.i2c_addr);
+ return -1;
+ }
+ i2c_client = i2c_new_device(adapter, &i2c_info);
+ if (!i2c_client) {
+ EXTERR("%s failed to new i2c device\n", ext_drv->config.name);
+ } else {
+ if (lcd_debug_print_flag) {
+ EXTPR("%s new i2c device succeed\n",
+ ext_drv->config.name);
+ }
+ }
+
+ if (!aml_DLPC3439_i2c_client) {
+ ret = i2c_add_driver(&aml_DLPC3439_i2c_driver);
+ if (ret) {
+ EXTERR("%s add i2c_driver failed\n",
+ ext_drv->config.name);
+ return -1;
+ }
+ }
+
+ ret = lcd_extern_driver_update(ext_drv);
+
+ if (lcd_debug_print_flag)
+ EXTPR("%s: %d\n", __func__, ret);
+ return ret;
+}
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/i2c_T5800Q.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+
+#define LCD_EXTERN_NAME "i2c_T5800Q"
+
+static struct i2c_client *aml_T5800Q_i2c_client;
+static struct lcd_extern_config_s *ext_config;
+
+#define LCD_EXTERN_CMD_SIZE 9
+static unsigned char init_on_table[] = {
+ 0x00, 0x20, 0x01, 0x02, 0x00, 0x40, 0xFF, 0x00, 0x00,
+ 0x00, 0x80, 0x02, 0x00, 0x40, 0x62, 0x51, 0x73, 0x00,
+ 0x00, 0x61, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xC1, 0x05, 0x0F, 0x00, 0x08, 0x70, 0x00, 0x00,
+ 0x00, 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3D, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xED, 0x0D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x23, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, /* delay 10ms */
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ending */
+};
+
+static unsigned char init_off_table[] = {
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ending */
+};
+
+static int lcd_extern_i2c_write(struct i2c_client *i2client,
+ unsigned char *buff, unsigned int len)
+{
+ int ret = 0;
+ struct i2c_msg msg[] = {
+ {
+ .addr = i2client->addr,
+ .flags = 0,
+ .len = len,
+ .buf = buff,
+ }
+ };
+
+ ret = i2c_transfer(i2client->adapter, msg, 1);
+ if (ret < 0)
+ EXTERR("i2c write failed [addr 0x%02x]\n", i2client->addr);
+
+ return ret;
+}
+#if 0
+static int lcd_extern_i2c_read(struct i2c_client *i2client,
+ unsigned char *buff, unsigned int len)
+{
+ int ret = 0;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = buff,
+ },
+ {
+ .addr = i2client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buff,
+ }
+ };
+
+ ret = i2c_transfer(i2client->adapter, msgs, 2);
+ if (ret < 0)
+ EXTERR("i2c read failed [addr 0x%02x]\n", i2client->addr);
+
+ return ret;
+}
+#endif
+
+static int lcd_extern_reg_read(unsigned char reg, unsigned char *buf)
+{
+ int ret = 0;
+
+ return ret;
+}
+
+static int lcd_extern_reg_write(unsigned char reg, unsigned char value)
+{
+ int ret = 0;
+
+ return ret;
+}
+
+static int lcd_extern_power_cmd(unsigned char *init_table)
+{
+ int i = 0, len;
+ int ret = 0;
+
+ len = ext_config->cmd_size;
+ if (len < 1) {
+ EXTERR("%s: cmd_size %d is invalid\n", __func__, len);
+ return -1;
+ }
+
+ while (i <= LCD_EXTERN_INIT_TABLE_MAX) {
+ if (init_table[i] == LCD_EXTERN_INIT_END) {
+ break;
+ } else if (init_table[i] == LCD_EXTERN_INIT_NONE) {
+ /* do nothing, only for delay */
+ } else if (init_table[i] == LCD_EXTERN_INIT_GPIO) {
+ if (init_table[i+1] < LCD_GPIO_MAX) {
+ lcd_extern_gpio_set(init_table[i+1],
+ init_table[i+2]);
+ }
+ } else if (init_table[i] == LCD_EXTERN_INIT_CMD) {
+ ret = lcd_extern_i2c_write(aml_T5800Q_i2c_client,
+ &init_table[i+1], (len-2));
+ } else {
+ EXTERR("%s(%d: %s): power_type %d is invalid\n",
+ __func__, ext_config->index,
+ ext_config->name, ext_config->type);
+ }
+ if (init_table[i+len-1] > 0)
+ mdelay(init_table[i+len-1]);
+ i += len;
+ }
+
+ return ret;
+}
+
+static int lcd_extern_power_ctrl(int flag)
+{
+ int ret = 0;
+
+ if (flag)
+ ret = lcd_extern_power_cmd(ext_config->table_init_on);
+ else
+ ret = lcd_extern_power_cmd(ext_config->table_init_off);
+
+ EXTPR("%s(%d: %s): %d\n",
+ __func__, ext_config->index, ext_config->name, flag);
+ return ret;
+}
+
+static int lcd_extern_power_on(void)
+{
+ int ret;
+
+ ret = lcd_extern_power_ctrl(1);
+ return ret;
+}
+
+static int lcd_extern_power_off(void)
+{
+ int ret;
+
+ ret = lcd_extern_power_ctrl(0);
+ return ret;
+}
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ if (ext_drv == NULL) {
+ EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+ return -1;
+ }
+
+ if (ext_drv->config.table_init_loaded == 0) {
+ ext_drv->config.table_init_on = init_on_table;
+ ext_drv->config.table_init_off = init_off_table;
+ }
+ ext_drv->reg_read = lcd_extern_reg_read;
+ ext_drv->reg_write = lcd_extern_reg_write;
+ ext_drv->power_on = lcd_extern_power_on;
+ ext_drv->power_off = lcd_extern_power_off;
+
+ return 0;
+}
+
+/* debug function */
+static const char *lcd_extern_debug_usage_str = {
+"Usage:\n"
+" echo <reg> <> ... <> > write ; T5800Q i2c command write, 7 parameters without address\n"
+};
+
+static ssize_t lcd_extern_debug_help(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", lcd_extern_debug_usage_str);
+}
+
+static ssize_t lcd_extern_debug_write(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int ret;
+ unsigned int temp[8];
+ unsigned char data[8];
+ int i;
+
+ memset(temp, 0, (sizeof(unsigned int) * 8));
+ ret = sscanf(buf, "%x %x %x %x %x %x %x",
+ &temp[0], &temp[1], &temp[2], &temp[3],
+ &temp[4], &temp[5], &temp[6]);
+ EXTPR("T5800Q i2c write:\n");
+ for (i = 0; i < 7; i++) {
+ data[i] = (unsigned char)temp[i];
+ pr_info("0x%02x ", data[i]);
+ }
+ pr_info("\n");
+ lcd_extern_i2c_write(aml_T5800Q_i2c_client, data, 7);
+
+ if (ret != 1 || ret != 2)
+ return -EINVAL;
+
+ return count;
+}
+
+static struct class_attribute lcd_extern_class_attrs[] = {
+ __ATTR(write, 0644,
+ lcd_extern_debug_help, lcd_extern_debug_write),
+};
+
+static struct class *debug_class;
+static int creat_lcd_extern_class(void)
+{
+ int i;
+
+ debug_class = class_create(THIS_MODULE, LCD_EXTERN_NAME);
+ if (IS_ERR(debug_class)) {
+ EXTERR("create debug class failed\n");
+ return -1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lcd_extern_class_attrs); i++) {
+ if (class_create_file(debug_class,
+ &lcd_extern_class_attrs[i])) {
+ EXTERR("create debug attribute %s failed\n",
+ lcd_extern_class_attrs[i].attr.name);
+ }
+ }
+
+ return 0;
+}
+
+#if 0
+static int remove_lcd_extern_class(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lcd_extern_class_attrs); i++)
+ class_remove_file(debug_class, &lcd_extern_class_attrs[i]);
+
+ class_destroy(debug_class);
+ debug_class = NULL;
+
+ return 0;
+}
+#endif
+/* ********************************************************* */
+
+static int aml_T5800Q_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ EXTERR("%s: functionality check failed\n", __func__);
+ else
+ aml_T5800Q_i2c_client = client;
+
+ EXTPR("%s OK\n", __func__);
+ return 0;
+}
+
+static int aml_T5800Q_i2c_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id aml_T5800Q_i2c_id[] = {
+ {LCD_EXTERN_NAME, 0},
+ { }
+};
+/* MODULE_DEVICE_TABLE(i2c, aml_T5800Q_id); */
+
+static struct i2c_driver aml_T5800Q_i2c_driver = {
+ .probe = aml_T5800Q_i2c_probe,
+ .remove = aml_T5800Q_i2c_remove,
+ .id_table = aml_T5800Q_i2c_id,
+ .driver = {
+ .name = LCD_EXTERN_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+int aml_lcd_extern_i2c_T5800Q_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ struct i2c_board_info i2c_info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *i2c_client;
+ int ret = 0;
+
+ ext_config = &ext_drv->config;
+ memset(&i2c_info, 0, sizeof(i2c_info));
+
+ adapter = i2c_get_adapter(ext_drv->config.i2c_bus);
+ if (!adapter) {
+ EXTERR("%s failed to get i2c adapter\n", ext_drv->config.name);
+ return -1;
+ }
+
+ strncpy(i2c_info.type, ext_drv->config.name, I2C_NAME_SIZE);
+ i2c_info.addr = ext_drv->config.i2c_addr;
+ i2c_info.platform_data = &ext_drv->config;
+ i2c_info.flags = 0;
+ if (i2c_info.addr > 0x7f) {
+ EXTERR("%s invalid i2c address: 0x%02x\n",
+ ext_drv->config.name, ext_drv->config.i2c_addr);
+ return -1;
+ }
+ i2c_client = i2c_new_device(adapter, &i2c_info);
+ if (!i2c_client) {
+ EXTERR("%s failed to new i2c device\n", ext_drv->config.name);
+ } else {
+ if (lcd_debug_print_flag) {
+ EXTPR("%s new i2c device succeed\n",
+ ext_drv->config.name);
+ }
+ }
+
+ if (!aml_T5800Q_i2c_client) {
+ ret = i2c_add_driver(&aml_T5800Q_i2c_driver);
+ if (ret) {
+ EXTERR("%s add i2c_driver failed\n",
+ ext_drv->config.name);
+ return -1;
+ }
+ }
+
+ ret = lcd_extern_driver_update(ext_drv);
+ creat_lcd_extern_class();
+
+ if (lcd_debug_print_flag)
+ EXTPR("%s: %d\n", __func__, ret);
+ return ret;
+}
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/i2c_anx6345.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+#include "i2c_anx6345.h"
+
+static struct lcd_extern_config_s *ext_config;
+static struct i2c_client *aml_anx6345_70_client;
+static struct i2c_client *aml_anx6345_72_client;
+
+const char *anx_addr_name[2] = {"anx6345_70", "anx6345_72"};
+static unsigned int edp_tx_lane = 1;
+
+#define LCD_EXTERN_NAME "lcd_anx6345"
+
+static int aml_i2c_write(struct i2c_client *i2client, unsigned char *buff,
+ unsigned int len)
+{
+ int ret = 0;
+ struct i2c_msg msg[] = {
+ {
+ .addr = i2client->addr,
+ .flags = 0,
+ .len = len,
+ .buf = buff,
+ },
+ };
+
+ ret = i2c_transfer(i2client->adapter, msg, 1);
+ if (ret < 0) {
+ EXTERR("%s: i2c transfer failed [addr 0x%02x]\n",
+ __func__, i2client->addr);
+ }
+
+ return ret;
+}
+
+static int aml_i2c_read(struct i2c_client *i2client, unsigned char *buff,
+ unsigned int len)
+{
+ int ret = 0;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = buff,
+ },
+ {
+ .addr = i2client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buff,
+ },
+ };
+
+ ret = i2c_transfer(i2client->adapter, msgs, 2);
+ if (ret < 0) {
+ EXTERR("%s: i2c transfer failed [addr 0x%02x]\n",
+ __func__, i2client->addr);
+ }
+
+ return ret;
+}
+
+#if 0
+static int i2c_reg_read(unsigned char reg, unsigned char *buf)
+{
+ int ret = 0;
+
+ return ret;
+}
+
+static int i2c_reg_write(unsigned char reg, unsigned char value)
+{
+ int ret = 0;
+
+ return ret;
+}
+#endif
+
+static int SP_TX_Write_Reg(unsigned char addr, unsigned char reg,
+ unsigned char data)
+{
+ struct i2c_client *client = aml_anx6345_70_client;
+ unsigned char buff[2];
+ int ret;
+
+ buff[0] = reg;
+ buff[1] = data;
+ if (addr == 0x70)
+ client = aml_anx6345_70_client;
+ else if (addr == 0x72)
+ client = aml_anx6345_72_client;
+
+ ret = aml_i2c_write(client, buff, 1);
+ return ret;
+}
+
+static int SP_TX_Read_Reg(unsigned char addr, unsigned char reg,
+ unsigned char *data)
+{
+ struct i2c_client *client = aml_anx6345_70_client;
+ int ret;
+
+ *data = reg;
+ if (addr == 0x70)
+ client = aml_anx6345_70_client;
+ else if (addr == 0x72)
+ client = aml_anx6345_72_client;
+
+ ret = aml_i2c_read(client, data, 1);
+ return ret;
+}
+
+static int SP_TX_Wait_AUX_Finished(void)
+{
+ unsigned char c;
+ unsigned char cCnt;
+
+ cCnt = 0;
+ SP_TX_Read_Reg(0x70, SP_TX_AUX_STATUS, &c);
+ while (c & 0x10) {/* aux busy */
+ cCnt++;
+ SP_TX_Read_Reg(0x70, SP_TX_AUX_STATUS, &c);
+ if (cCnt > 100)
+ return -1; /* aux fail */
+ }
+
+ return 0; /* aux ok */
+}
+
+static int SP_TX_AUX_DPCDRead_Bytes(unsigned char addrh, unsigned char addrm,
+ unsigned char addrl, unsigned char cCount, unsigned char *pBuf)
+{
+ unsigned char c, i;
+
+ /* clr buffer */
+ SP_TX_Write_Reg(0x70, SP_TX_BUF_DATA_COUNT_REG, 0x80);
+
+ /* set read cmd and count */
+ SP_TX_Write_Reg(0x70, SP_TX_AUX_CTRL_REG, (((cCount-1) << 4) | 0x09));
+
+ /* set aux address15:0 */
+ SP_TX_Write_Reg(0x70, SP_TX_AUX_ADDR_7_0_REG, addrl);
+ SP_TX_Write_Reg(0x70, SP_TX_AUX_ADDR_15_8_REG, addrm);
+
+ /* set address19:16 and enable aux */
+ SP_TX_Read_Reg(0x70, SP_TX_AUX_ADDR_19_16_REG, &c);
+ SP_TX_Write_Reg(0x70, SP_TX_AUX_ADDR_19_16_REG, ((c & 0xf0) | addrh));
+
+ /* Enable Aux */
+ SP_TX_Read_Reg(0x70, SP_TX_AUX_CTRL_REG2, &c);
+ SP_TX_Write_Reg(0x70, SP_TX_AUX_CTRL_REG2, (c | 0x01));
+
+ mdelay(5);
+ if (SP_TX_Wait_AUX_Finished())
+ return -1;
+
+ for (i = 0; i < cCount; i++) {
+ SP_TX_Read_Reg(0x70, SP_TX_BUF_DATA_0_REG+i, &c);
+ *(pBuf+i) = c;
+ if (i >= MAX_BUF_CNT)
+ break;
+ }
+
+ return 0; /* aux ok */
+}
+
+static int lcd_extern_power_on(void)
+{
+ unsigned int lane_num;
+ unsigned int link_rate;
+ unsigned char bits;
+ unsigned char device_id;
+ unsigned char temp;
+ unsigned char temp1;
+ unsigned int count = 0;
+ unsigned int count1 = 0;
+
+ lane_num = edp_tx_lane; /* 1 lane */
+ link_rate = VAL_EDP_TX_LINK_BW_SET_270; /* 2.7G */
+ bits = 0; /* 0x00: 6bit; 0x10:8bit */
+ SP_TX_Write_Reg(0x72, 0x05, 0x00);
+
+ SP_TX_Read_Reg(0x72, 0x01, &device_id);
+ if (device_id == 0xaa) {
+ EXTPR("ANX6345 Chip found\n\n");
+ } else {
+ EXTERR("ANX6345 Chip not found\n\n");
+ return -1;
+ }
+ temp = device_id;
+ /* if aux read fail, do h/w reset */
+ while (SP_TX_AUX_DPCDRead_Bytes(0x00, 0x00, 0x00, 1, &temp1) &&
+ (count < 200)) {
+ /* read fail, h/w reset */
+ SP_TX_Write_Reg(0x72, 0x06, 0x01);
+ SP_TX_Write_Reg(0x72, 0x06, 0x00);
+ SP_TX_Write_Reg(0x72, 0x05, 0x00);
+ mdelay(10);
+ count++;
+ }
+
+ /* software reset */
+ SP_TX_Read_Reg(0x72, SP_TX_RST_CTRL_REG, &temp);
+ SP_TX_Write_Reg(0x72, SP_TX_RST_CTRL_REG, (temp | SP_TX_RST_SW_RST));
+ SP_TX_Write_Reg(0x72, SP_TX_RST_CTRL_REG, (temp & ~SP_TX_RST_SW_RST));
+
+ /* EDID address for AUX access */
+ SP_TX_Write_Reg(0x70, SP_TX_EXTRA_ADDR_REG, 0x50);
+ /* disable HDCP polling mode. */
+ SP_TX_Write_Reg(0x70, SP_TX_HDCP_CTRL, 0x00);
+ /* Enable HDCP polling mode. */
+ /* SP_TX_Write_Reg(0x70, SP_TX_HDCP_CTRL, 0x02); */
+ /* enable M value read out */
+ SP_TX_Write_Reg(0x70, SP_TX_LINK_DEBUG_REG, 0x30);
+
+ /* SP_TX_Read_Reg(0x70, SP_TX_DEBUG_REG1, &temp); */
+ SP_TX_Write_Reg(0x70, SP_TX_DEBUG_REG1, 0x00); /*disable polling HPD */
+
+ SP_TX_Read_Reg(0x70, SP_TX_HDCP_CONTROL_0_REG, &temp);
+ /* set KSV valid */
+ SP_TX_Write_Reg(0x70, SP_TX_HDCP_CONTROL_0_REG, (temp | 0x03));
+
+ SP_TX_Read_Reg(0x70, SP_TX_AUX_CTRL_REG2, &temp);
+ /* set double AUX output */
+ SP_TX_Write_Reg(0x70, SP_TX_AUX_CTRL_REG2, (temp | 0x08));
+
+ /* unmask pll change int */
+ SP_TX_Write_Reg(0x72, SP_COMMON_INT_MASK1, 0xbf);
+ SP_TX_Write_Reg(0x72, SP_COMMON_INT_MASK2, 0xff);/* mask all int */
+ SP_TX_Write_Reg(0x72, SP_COMMON_INT_MASK3, 0xff);/* mask all int */
+ SP_TX_Write_Reg(0x72, SP_COMMON_INT_MASK4, 0xff);/* mask all int */
+
+ /* reset AUX */
+ SP_TX_Read_Reg(0x72, SP_TX_RST_CTRL2_REG, &temp);
+ SP_TX_Write_Reg(0x72, SP_TX_RST_CTRL2_REG, temp | SP_TX_AUX_RST);
+ SP_TX_Write_Reg(0x72, SP_TX_RST_CTRL2_REG, temp & (~SP_TX_AUX_RST));
+
+ /* Chip initialization */
+ SP_TX_Write_Reg(0x70, SP_TX_SYS_CTRL1_REG, 0x00);
+ mdelay(10);
+
+ SP_TX_Write_Reg(0x72, SP_TX_VID_CTRL2_REG, bits);
+
+ /* ANX6345 chip analog setting */
+ SP_TX_Write_Reg(0x70, SP_TX_PLL_CTRL_REG, 0x00);/* UPDATE: 0x07->0x00 */
+
+ /* ANX chip analog setting */
+ /* UPDATE: 0xF0->0X70 */
+ /* SP_TX_Write_Reg(0x72, ANALOG_DEBUG_REG1, 0x70); */
+ SP_TX_Write_Reg(0x70, SP_TX_LINK_DEBUG_REG, 0x30);
+
+ /* force HPD */
+ SP_TX_Write_Reg(0x70, SP_TX_SYS_CTRL3_REG, 0x30);
+
+ /* enable ssc function */
+ SP_TX_Write_Reg(0x70, 0xA7, 0x00); /* disable SSC first */
+ SP_TX_Write_Reg(0x70, 0xD0, 0x5f); /* ssc d 0.4%, f0/4 mode */
+ SP_TX_Write_Reg(0x70, 0xD1, 0x00);
+ SP_TX_Write_Reg(0x70, 0xD2, 0x75); /* ctrl_th */
+ SP_TX_Read_Reg(0x70, 0xA7, &temp);
+ SP_TX_Write_Reg(0x70, 0xA7, (temp | 0x10)); /* enable SSC */
+ SP_TX_Read_Reg(0x72, 0x07, &temp); /* reset SSC */
+ SP_TX_Write_Reg(0x72, 0x07, (temp | 0x80));
+ SP_TX_Write_Reg(0x72, 0x07, (temp & (~0x80)));
+
+ /* Select 2.7G */
+ /* 2.7g:0x0a; 1.62g:0x06 */
+ SP_TX_Write_Reg(0x70, SP_TX_LINK_BW_SET_REG, link_rate);
+ /* Select 2 lanes */
+ SP_TX_Write_Reg(0x70, 0xa1, lane_num);
+
+ SP_TX_Write_Reg(0x70, SP_TX_LINK_TRAINING_CTRL_REG,
+ SP_TX_LINK_TRAINING_CTRL_EN);
+ mdelay(5);
+ SP_TX_Read_Reg(0x70, SP_TX_LINK_TRAINING_CTRL_REG, &temp);
+ /* UPDATE: FROM 0X01 TO 0X80 */
+ while ((temp & 0x80) != 0) {
+ /* debug_puts("Waiting...\n"); */
+ mdelay(5);
+ count1++;
+ if (count1 > 100) {
+ EXTERR("ANX6345 Link training fail\n");
+ break;
+ }
+ SP_TX_Read_Reg(0x70, SP_TX_LINK_TRAINING_CTRL_REG, &temp);
+ }
+
+ SP_TX_Write_Reg(0x72, 0x12, 0x2c);
+ SP_TX_Write_Reg(0x72, 0x13, 0x06);
+ SP_TX_Write_Reg(0x72, 0x14, 0x00);
+ SP_TX_Write_Reg(0x72, 0x15, 0x06);
+ SP_TX_Write_Reg(0x72, 0x16, 0x02);
+ SP_TX_Write_Reg(0x72, 0x17, 0x04);
+ SP_TX_Write_Reg(0x72, 0x18, 0x26);
+ SP_TX_Write_Reg(0x72, 0x19, 0x50);
+ SP_TX_Write_Reg(0x72, 0x1a, 0x04);
+ SP_TX_Write_Reg(0x72, 0x1b, 0x00);
+ SP_TX_Write_Reg(0x72, 0x1c, 0x04);
+ SP_TX_Write_Reg(0x72, 0x1d, 0x18);
+ SP_TX_Write_Reg(0x72, 0x1e, 0x00);
+ SP_TX_Write_Reg(0x72, 0x1f, 0x10);
+ SP_TX_Write_Reg(0x72, 0x20, 0x00);
+ SP_TX_Write_Reg(0x72, 0x21, 0x28);
+
+ SP_TX_Write_Reg(0x72, 0x11, 0x03);
+ /* enable BIST. In normal mode, don't need to config this reg
+ * if want to open BIST, must setting
+ * right dat 0x11-0x21 base lcd timing.
+ */
+ /* SP_TX_Write_Reg(0x72, 0x0b, 0x09); //colorbar:08,graystep:09 */
+ SP_TX_Write_Reg(0x72, 0x08, 0x81); /* SDR:0x81;DDR:0x8f */
+
+ /* force HPD and stream valid */
+ SP_TX_Write_Reg(0x70, 0x82, 0x33);
+ EXTPR("%s\n", __func__);
+ return 0;
+}
+
+static int lcd_extern_power_off(void)
+{
+ int ret = 0;
+
+ return ret;
+}
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ int ret = 0;
+
+ if (ext_drv) {
+ ext_drv->power_on = lcd_extern_power_on;
+ ext_drv->power_off = lcd_extern_power_off;
+ } else {
+ EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int aml_anx6345_70_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ EXTERR("%s: functionality check failed\n", __func__);
+ else
+ aml_anx6345_70_client = client;
+
+ EXTPR("%s OK\n", __func__);
+ return 0;
+}
+
+static int aml_anx6345_72_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ EXTERR("%s: functionality check failed\n", __func__);
+ else
+ aml_anx6345_72_client = client;
+
+ EXTPR("%s OK\n", __func__);
+ return 0;
+}
+
+static int aml_anx6345_70_remove(struct i2c_client *client)
+{
+ return 0;
+}
+static int aml_anx6345_72_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id aml_anx6345_70_id[] = {
+ {"anx6345_70", 0},
+ { }
+};
+static const struct i2c_device_id aml_anx6345_72_id[] = {
+ {"anx6345_72", 0},
+ { }
+};
+
+/* MODULE_DEVICE_TABLE(i2c, aml_tc101_id); */
+
+static struct i2c_driver aml_anx6345_70_driver = {
+ .probe = aml_anx6345_70_probe,
+ .remove = aml_anx6345_70_remove,
+ .id_table = aml_anx6345_70_id,
+ .driver = {
+ .name = "anx6345_70",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct i2c_driver aml_anx6345_72_driver = {
+ .probe = aml_anx6345_72_probe,
+ .remove = aml_anx6345_72_remove,
+ .id_table = aml_anx6345_72_id,
+ .driver = {
+ .name = "anx6345_72",
+ .owner = THIS_MODULE,
+ },
+};
+
+int aml_lcd_extern_i2c_anx6345_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ struct i2c_board_info i2c_info[2];
+ struct i2c_adapter *adapter;
+ struct i2c_client *i2c_client;
+ int i = 0;
+ int ret = 0;
+
+ ext_config = &ext_drv->config;
+ for (i = 0; i < 2; i++)
+ memset(&i2c_info[i], 0, sizeof(i2c_info[i]));
+
+ adapter = i2c_get_adapter(ext_config->i2c_bus);
+ if (!adapter) {
+ EXTERR("%s failed to get i2c adapter\n", ext_drv->config.name);
+ return -1;
+ }
+
+ i2c_info[0].addr = 0x38; /* 0x70 >> 1; */
+ i2c_info[1].addr = 0x39; /* 0x72 >> 1; */
+ for (i = 0; i < 2; i++) {
+ strncpy(i2c_info[i].type, anx_addr_name[i], I2C_NAME_SIZE);
+ /* i2c_info[i].platform_data = &ext_drv->config; */
+ i2c_info[i].flags = 0;
+ if (i2c_info[i].addr > 0x7f) {
+ EXTERR("%s invalid i2c address: 0x%02x\n",
+ ext_drv->config.name, i2c_info[i].addr);
+ return -1;
+ }
+ i2c_client = i2c_new_device(adapter, &i2c_info[i]);
+ if (!i2c_client) {
+ EXTERR("%s failed to new i2c device\n",
+ ext_drv->config.name);
+ } else {
+ if (lcd_debug_print_flag) {
+ EXTPR("%s new i2c device succeed\n",
+ ext_drv->config.name);
+ }
+ }
+ }
+
+ if (!aml_anx6345_70_client) {
+ ret = i2c_add_driver(&aml_anx6345_70_driver);
+ if (ret) {
+ EXTERR("%s add i2c_driver failed\n",
+ ext_drv->config.name);
+ return -1;
+ }
+ }
+ if (!aml_anx6345_72_client) {
+ ret = i2c_add_driver(&aml_anx6345_72_driver);
+ if (ret) {
+ EXTERR("%s add i2c_driver failed\n",
+ ext_drv->config.name);
+ return -1;
+ }
+ }
+
+ ret = lcd_extern_driver_update(ext_drv);
+
+ if (lcd_debug_print_flag)
+ EXTPR("%s: %d\n", __func__, ret);
+ return ret;
+}
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/i2c_tc101.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+
+static struct lcd_extern_config_s *ext_config;
+static struct i2c_client *aml_tc101_i2c_client;
+
+#define LCD_EXTERN_NAME "lcd_i2c_tc101"
+
+static unsigned char i2c_init_table[][3] = {
+ /* {0xff, 0xff, 20},//delay mark(20ms) */
+ {0xf8, 0x30, 0xb2},
+ {0xf8, 0x33, 0xc2},
+ {0xf8, 0x31, 0xf0},
+ {0xf8, 0x40, 0x80},
+ {0xf8, 0x81, 0xec},
+ {0xff, 0xff, 0xff},/* end mark */
+};
+
+static int aml_i2c_write(struct i2c_client *i2client, unsigned char *buff,
+ unsigned int len)
+{
+ int ret = 0;
+ struct i2c_msg msg[] = {
+ {
+ .addr = i2client->addr,
+ .flags = 0,
+ .len = len,
+ .buf = buff,
+ },
+ };
+
+ ret = i2c_transfer(i2client->adapter, msg, 1);
+ if (ret < 0) {
+ EXTERR("%s: i2c transfer failed [addr 0x%02x]\n",
+ __func__, i2client->addr);
+ }
+
+ return ret;
+}
+#if 0
+static int aml_i2c_read(struct i2c_client *i2client, unsigned char *buff,
+ unsigned int len)
+{
+ int ret = 0;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = buff,
+ },
+ {
+ .addr = i2client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buff,
+ },
+ };
+
+ ret = i2c_transfer(i2client->adapter, msgs, 2);
+ if (ret < 0) {
+ EXTERR("%s: i2c transfer failed [addr 0x%02x]\n",
+ __func__, i2client->addr);
+ }
+
+ return ret;
+}
+
+static int i2c_reg_read(unsigned char reg, unsigned char *buf)
+{
+ int ret = 0;
+
+ return ret;
+}
+
+static int i2c_reg_write(unsigned char reg, unsigned char value)
+{
+ int ret = 0;
+
+ return ret;
+}
+#endif
+
+static int lcd_extern_power_on(void)
+{
+ unsigned char tData[4];
+ int i = 0, ending_flag = 0;
+ int ret = 0;
+
+ while (ending_flag == 0) {
+ if ((i2c_init_table[i][0] == 0xff) &&
+ (i2c_init_table[i][1] == 0xff)) { /* special mark */
+ if (i2c_init_table[i][2] == 0xff) /* ending flag */
+ ending_flag = 1;
+ else /* delay flag */
+ mdelay(i2c_init_table[i][2]);
+ } else {
+ tData[0] = i2c_init_table[i][0];
+ tData[1] = i2c_init_table[i][1];
+ tData[2] = i2c_init_table[i][2];
+ aml_i2c_write(aml_tc101_i2c_client, tData, 3);
+ }
+ i++;
+ }
+ EXTPR("%s\n", __func__);
+ return ret;
+}
+
+static int lcd_extern_power_off(void)
+{
+ int ret = 0;
+
+ return ret;
+}
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ int ret = 0;
+
+ if (ext_drv) {
+ ext_drv->power_on = lcd_extern_power_on;
+ ext_drv->power_off = lcd_extern_power_off;
+ } else {
+ EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int aml_tc101_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ EXTERR("%s: functionality check failed\n", __func__);
+ else
+ aml_tc101_i2c_client = client;
+
+ EXTPR("%s OK\n", __func__);
+ return 0;
+}
+
+static int aml_tc101_i2c_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id aml_tc101_i2c_id[] = {
+ {LCD_EXTERN_NAME, 0},
+ { }
+};
+/* MODULE_DEVICE_TABLE(i2c, aml_tc101_id); */
+
+static struct i2c_driver aml_tc101_i2c_driver = {
+ .probe = aml_tc101_i2c_probe,
+ .remove = aml_tc101_i2c_remove,
+ .id_table = aml_tc101_i2c_id,
+ .driver = {
+ .name = LCD_EXTERN_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+int aml_lcd_extern_i2c_tc101_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ struct i2c_board_info i2c_info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *i2c_client;
+ int ret = 0;
+
+ ext_config = &ext_drv->config;
+ memset(&i2c_info, 0, sizeof(i2c_info));
+
+ adapter = i2c_get_adapter(ext_drv->config.i2c_bus);
+ if (!adapter) {
+ EXTERR("%s failed to get i2c adapter\n", ext_drv->config.name);
+ return -1;
+ }
+
+ strncpy(i2c_info.type, ext_drv->config.name, I2C_NAME_SIZE);
+ i2c_info.addr = ext_drv->config.i2c_addr;
+ i2c_info.platform_data = &ext_drv->config;
+ i2c_info.flags = 0;
+ if (i2c_info.addr > 0x7f) {
+ EXTERR("%s invalid i2c address: 0x%02x\n",
+ ext_drv->config.name, ext_drv->config.i2c_addr);
+ return -1;
+ }
+ i2c_client = i2c_new_device(adapter, &i2c_info);
+ if (!i2c_client) {
+ EXTERR("%s failed to new i2c device\n", ext_drv->config.name);
+ } else {
+ if (lcd_debug_print_flag) {
+ EXTPR("%s new i2c device succeed\n",
+ ext_drv->config.name);
+ }
+ }
+
+ if (!aml_tc101_i2c_client) {
+ ret = i2c_add_driver(&aml_tc101_i2c_driver);
+ if (ret) {
+ EXTERR("%s add i2c_driver failed\n",
+ ext_drv->config.name);
+ return -1;
+ }
+ }
+
+ ret = lcd_extern_driver_update(ext_drv);
+
+ if (lcd_debug_print_flag)
+ EXTPR("%s: %d\n", __func__, ret);
+ return ret;
+}
+
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#include "lcd_extern.h"
+
+static struct device *lcd_extern_dev;
+static int lcd_ext_driver_num;
+static struct aml_lcd_extern_driver_s *lcd_ext_driver[LCD_EXT_DRIVER_MAX];
+static int lcd_extern_add_driver(struct lcd_extern_config_s *extconf);
+
+static unsigned int lcd_ext_key_valid;
+static unsigned char lcd_ext_config_load;
+
+static unsigned char lcd_extern_init_on_table[LCD_EXTERN_INIT_TABLE_MAX] = {
+ 0xff,
+};
+
+static unsigned char lcd_extern_init_off_table[LCD_EXTERN_INIT_TABLE_MAX] = {
+ 0xff,
+};
+
+struct lcd_ext_gpio_s {
+ char name[15];
+ struct gpio_desc *gpio;
+ int flag;
+};
+
+static struct lcd_ext_gpio_s lcd_extern_gpio[LCD_EXTERN_GPIO_NUM_MAX] = {
+ {.flag = 0,},
+ {.flag = 0,},
+ {.flag = 0,},
+ {.flag = 0,},
+ {.flag = 0,},
+ {.flag = 0,},
+};
+
+struct aml_lcd_extern_driver_s *aml_lcd_extern_get_driver(int index)
+{
+ int i;
+ struct aml_lcd_extern_driver_s *ext_driver = NULL;
+
+ if (index >= LCD_EXTERN_INDEX_INVALID) {
+ EXTERR("invalid driver index: %d\n", index);
+ return NULL;
+ }
+ for (i = 0; i < lcd_ext_driver_num; i++) {
+ if (lcd_ext_driver[i]->config.index == index) {
+ ext_driver = lcd_ext_driver[i];
+ break;
+ }
+ }
+ if (ext_driver == NULL)
+ EXTERR("invalid driver index: %d\n", index);
+ return ext_driver;
+}
+
+#if 0
+static struct aml_lcd_extern_driver_s
+ *aml_lcd_extern_get_driver_by_name(char *name)
+{
+ int i;
+ struct aml_lcd_extern_driver_s *ext_driver = NULL;
+
+ for (i = 0; i < lcd_ext_driver_num; i++) {
+ if (strcmp(lcd_ext_driver[i]->config.name, name) == 0) {
+ ext_driver = lcd_ext_driver[i];
+ break;
+ }
+ }
+ if (ext_driver == NULL)
+ EXTERR("invalid driver name: %s\n", name);
+ return ext_driver;
+}
+#endif
+
+#ifdef CONFIG_OF
+void lcd_extern_gpio_register(unsigned char index)
+{
+ struct lcd_ext_gpio_s *ext_gpio;
+ const char *str;
+ int ret;
+
+ if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
+ EXTERR("gpio index %d, exit\n", index);
+ return;
+ }
+ ext_gpio = &lcd_extern_gpio[index];
+ if (ext_gpio->flag) {
+ if (lcd_debug_print_flag) {
+ EXTPR("gpio %s[%d] is already registered\n",
+ ext_gpio->name, index);
+ }
+ return;
+ }
+
+ /* get gpio name */
+ ret = of_property_read_string_index(lcd_extern_dev->of_node,
+ "extern_gpio_names", index, &str);
+ if (ret) {
+ EXTERR("failed to get extern_gpio_names: %d\n", index);
+ str = "unknown";
+ }
+ strcpy(ext_gpio->name, str);
+
+ /* request gpio */
+ ext_gpio->gpio = devm_gpiod_get_index(
+ lcd_extern_dev, "extern", index, GPIOD_OUT_HIGH);
+ if (IS_ERR(ext_gpio->gpio)) {
+ EXTERR("register gpio %s[%d]: %p, err: %d\n",
+ ext_gpio->name, index, ext_gpio->gpio,
+ IS_ERR(ext_gpio->gpio));
+ ext_gpio->gpio = NULL;
+ } else {
+ if (lcd_debug_print_flag) {
+ EXTPR("register gpio %s[%d]: %p\n",
+ ext_gpio->name, index, ext_gpio->gpio);
+ }
+ }
+}
+#endif
+
+void lcd_extern_gpio_set(unsigned char index, int value)
+{
+ struct lcd_ext_gpio_s *ext_gpio;
+
+ if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
+ EXTERR("gpio index %d, exit\n", index);
+ return;
+ }
+ ext_gpio = &lcd_extern_gpio[index];
+ if (ext_gpio->flag == 0) {
+ EXTERR("gpio [%d] is not registered\n", index);
+ return;
+ }
+ if (IS_ERR(ext_gpio->gpio)) {
+ EXTERR("gpio %s[%d]: %p, err: %ld\n",
+ ext_gpio->name, index, ext_gpio->gpio,
+ PTR_ERR(ext_gpio->gpio));
+ return;
+ }
+
+ switch (value) {
+ case LCD_GPIO_OUTPUT_LOW:
+ case LCD_GPIO_OUTPUT_HIGH:
+ gpiod_direction_output(ext_gpio->gpio, value);
+ break;
+ case LCD_GPIO_INPUT:
+ default:
+ gpiod_direction_input(ext_gpio->gpio);
+ break;
+ }
+ if (lcd_debug_print_flag) {
+ EXTPR("set gpio %s[%d] value: %d\n",
+ ext_gpio->name, index, value);
+ }
+}
+
+unsigned int lcd_extern_gpio_get(unsigned char index)
+{
+ struct lcd_ext_gpio_s *ext_gpio;
+
+ if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
+ EXTERR("gpio index %d, exit\n", index);
+ return -1;
+ }
+ ext_gpio = &lcd_extern_gpio[index];
+ if (ext_gpio->flag == 0) {
+ EXTERR("gpio [%d] is not registered\n", index);
+ return -1;
+ }
+ if (IS_ERR(ext_gpio->gpio)) {
+ EXTERR("gpio %s[%d]: %p, err: %ld\n",
+ ext_gpio->name, index, ext_gpio->gpio,
+ PTR_ERR(ext_gpio->gpio));
+ return -1;
+ }
+
+ return gpiod_get_value(ext_gpio->gpio);
+}
+
+#ifdef CONFIG_OF
+static unsigned char lcd_extern_get_i2c_bus_str(const char *str)
+{
+ unsigned char i2c_bus;
+
+ if (strncmp(str, "i2c_bus_ao", 10) == 0)
+ i2c_bus = AML_I2C_MASTER_AO;
+ else if (strncmp(str, "i2c_bus_a", 9) == 0)
+ i2c_bus = AML_I2C_MASTER_A;
+ else if (strncmp(str, "i2c_bus_b", 9) == 0)
+ i2c_bus = AML_I2C_MASTER_B;
+ else if (strncmp(str, "i2c_bus_c", 9) == 0)
+ i2c_bus = AML_I2C_MASTER_C;
+ else if (strncmp(str, "i2c_bus_d", 9) == 0)
+ i2c_bus = AML_I2C_MASTER_D;
+ else {
+ i2c_bus = AML_I2C_MASTER_A;
+ EXTERR("invalid i2c_bus: %s\n", str);
+ }
+
+ return i2c_bus;
+}
+
+struct device_node *aml_lcd_extern_get_dts_child(int index)
+{
+ char propname[30];
+ struct device_node *child;
+
+ sprintf(propname, "extern_%d", index);
+ child = of_get_child_by_name(lcd_extern_dev->of_node, propname);
+ return child;
+}
+
+static int lcd_extern_get_config_dts(struct device_node *of_node,
+ struct lcd_extern_config_s *extconf)
+{
+ int ret;
+ int val;
+ const char *str;
+ unsigned char cmd_size, index;
+ int i, j;
+
+ extconf->index = LCD_EXTERN_INDEX_INVALID;
+ extconf->type = LCD_EXTERN_MAX;
+ extconf->table_init_loaded = 0;
+ extconf->table_init_on = lcd_extern_init_on_table;
+ extconf->table_init_off = lcd_extern_init_off_table;
+
+ ret = of_property_read_u32(of_node, "index", &val);
+ if (ret)
+ EXTERR("get index failed, exit\n");
+ else
+ extconf->index = (unsigned char)val;
+
+ ret = of_property_read_string(of_node, "status", &str);
+ if (ret) {
+ EXTERR("get index %d status failed\n", extconf->index);
+ } else {
+ if (lcd_debug_print_flag)
+ EXTPR("index %d status = %s\n", extconf->index, str);
+ if (strncmp(str, "okay", 2) == 0)
+ extconf->status = 1;
+ else
+ return -1;
+ }
+
+ ret = of_property_read_string(of_node, "extern_name", &str);
+ if (ret) {
+ str = "none";
+ EXTERR("get extern_name failed\n");
+ }
+ strcpy(extconf->name, str);
+ EXTPR("load config: %s[%d]\n", extconf->name, extconf->index);
+
+ ret = of_property_read_u32(of_node, "type", &extconf->type);
+ if (ret) {
+ extconf->type = LCD_EXTERN_MAX;
+ EXTERR("get type failed, exit\n");
+ return -1;
+ }
+
+ switch (extconf->type) {
+ case LCD_EXTERN_I2C:
+ ret = of_property_read_u32(of_node, "i2c_address", &val);
+ if (ret) {
+ EXTERR("get %s i2c_address failed, exit\n",
+ extconf->name);
+ extconf->i2c_addr = 0xff;
+ } else {
+ extconf->i2c_addr = (unsigned char)val;
+ }
+ if (lcd_debug_print_flag) {
+ EXTPR("%s i2c_address=0x%02x\n",
+ extconf->name, extconf->i2c_addr);
+ }
+ ret = of_property_read_u32(of_node, "i2c_second_address", &val);
+ if (ret) {
+ EXTERR("get %s i2c_second_address failed, exit\n",
+ extconf->name);
+ extconf->i2c_addr2 = 0xff;
+ } else {
+ extconf->i2c_addr = (unsigned char)val;
+ }
+ if (lcd_debug_print_flag) {
+ EXTPR("%s i2c_second_address=0x%02x\n",
+ extconf->name, extconf->i2c_addr2);
+ }
+
+ ret = of_property_read_string(of_node, "i2c_bus", &str);
+ if (ret) {
+ EXTERR("get %s i2c_bus failed, exit\n", extconf->name);
+ extconf->i2c_bus = AML_I2C_MASTER_A;
+ } else {
+ extconf->i2c_bus = lcd_extern_get_i2c_bus_str(str);
+ }
+ if (lcd_debug_print_flag) {
+ EXTPR("%s i2c_bus=%s[%d]\n",
+ extconf->name, str, extconf->i2c_bus);
+ }
+
+ ret = of_property_read_u32(of_node, "cmd_size", &val);
+ if (ret) {
+ EXTERR("get %s cmd_size failed\n", extconf->name);
+ extconf->cmd_size = 0;
+ } else {
+ extconf->cmd_size = (unsigned char)val;
+ }
+ if (lcd_debug_print_flag) {
+ EXTPR("%s cmd_size=%d\n",
+ extconf->name, extconf->cmd_size);
+ }
+ cmd_size = extconf->cmd_size;
+ if (cmd_size > 1) {
+ i = 0;
+ while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+ for (j = 0; j < cmd_size; j++) {
+ ret = of_property_read_u32_index(
+ of_node, "init_on",
+ (i + j), &val);
+ if (ret) {
+ EXTERR("get init_on failed\n");
+ extconf->table_init_on[i] =
+ LCD_EXTERN_INIT_END;
+ goto i2c_get_init_on_dts;
+ }
+ extconf->table_init_on[i + j] =
+ (unsigned char)val;
+ }
+ if (extconf->table_init_on[i] ==
+ LCD_EXTERN_INIT_END) {
+ break;
+ } else if (extconf->table_init_on[i] ==
+ LCD_EXTERN_INIT_GPIO) {
+ /* gpio request */
+ index = extconf->table_init_on[i+1];
+ if (index < LCD_EXTERN_GPIO_NUM_MAX)
+ lcd_extern_gpio_register(index);
+ }
+ i += cmd_size;
+ }
+ extconf->table_init_loaded = 1;
+i2c_get_init_on_dts:
+ i = 0;
+ while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+ for (j = 0; j < cmd_size; j++) {
+ ret = of_property_read_u32_index(
+ of_node, "init_off",
+ (i + j), &val);
+ if (ret) {
+ EXTERR("get init_off failed\n");
+ extconf->table_init_off[i] =
+ LCD_EXTERN_INIT_END;
+ goto i2c_get_init_off_dts;
+ }
+ extconf->table_init_off[i + j] =
+ (unsigned char)val;
+ }
+ if (extconf->table_init_off[i] ==
+ LCD_EXTERN_INIT_END) {
+ break;
+ } else if (extconf->table_init_off[i] ==
+ LCD_EXTERN_INIT_GPIO) {
+ /* gpio request */
+ index = extconf->table_init_off[i+1];
+ if (index < LCD_EXTERN_GPIO_NUM_MAX)
+ lcd_extern_gpio_register(index);
+ }
+ i += cmd_size;
+ }
+ }
+i2c_get_init_off_dts:
+ break;
+ case LCD_EXTERN_SPI:
+ ret = of_property_read_u32(of_node, "gpio_spi_cs", &val);
+ if (ret) {
+ EXTERR("get %s gpio_spi_cs failed, exit\n",
+ extconf->name);
+ extconf->spi_gpio_cs = LCD_EXTERN_GPIO_NUM_MAX;
+ } else {
+ extconf->spi_gpio_cs = val;
+ lcd_extern_gpio_register(val);
+ if (lcd_debug_print_flag) {
+ EXTPR("spi_gpio_cs: %d\n",
+ extconf->spi_gpio_cs);
+ }
+ }
+ ret = of_property_read_u32(of_node, "gpio_spi_clk", &val);
+ if (ret) {
+ EXTERR("get %s gpio_spi_clk failed, exit\n",
+ extconf->name);
+ extconf->spi_gpio_clk = LCD_EXTERN_GPIO_NUM_MAX;
+ } else {
+ extconf->spi_gpio_clk = val;
+ lcd_extern_gpio_register(val);
+ if (lcd_debug_print_flag) {
+ EXTPR("spi_gpio_clk: %d\n",
+ extconf->spi_gpio_clk);
+ }
+ }
+ ret = of_property_read_u32(of_node, "gpio_spi_data", &val);
+ if (ret) {
+ EXTERR("get %s gpio_spi_data failed, exit\n",
+ extconf->name);
+ extconf->spi_gpio_data = LCD_EXTERN_GPIO_NUM_MAX;
+ } else {
+ extconf->spi_gpio_data = val;
+ lcd_extern_gpio_register(val);
+ if (lcd_debug_print_flag) {
+ EXTPR("spi_gpio_data: %d\n",
+ extconf->spi_gpio_data);
+ }
+ }
+ ret = of_property_read_u32(of_node, "spi_clk_freq", &val);
+ if (ret) {
+ EXTERR("get %s spi_clk_freq failed, default to %dHz\n",
+ extconf->name, LCD_EXTERN_SPI_CLK_FREQ_DFT);
+ extconf->spi_clk_freq = LCD_EXTERN_SPI_CLK_FREQ_DFT;
+ } else {
+ extconf->spi_clk_freq = val;
+ if (lcd_debug_print_flag) {
+ EXTPR("spi_clk_freq: %dHz\n",
+ extconf->spi_clk_freq);
+ }
+ }
+ ret = of_property_read_u32(of_node, "spi_clk_pol", &val);
+ if (ret) {
+ EXTERR("get %s spi_clk_pol failed, default to 1\n",
+ extconf->name);
+ extconf->spi_clk_pol = 1;
+ } else {
+ extconf->spi_clk_pol = (unsigned char)val;
+ if (lcd_debug_print_flag) {
+ EXTPR("spi_clk_pol: %dHz\n",
+ extconf->spi_clk_pol);
+ }
+ }
+ ret = of_property_read_u32(of_node, "cmd_size", &val);
+ if (ret) {
+ EXTERR("get %s cmd_size failed\n", extconf->name);
+ extconf->cmd_size = 0;
+ } else {
+ extconf->cmd_size = (unsigned char)val;
+ }
+ if (lcd_debug_print_flag) {
+ EXTPR("%s cmd_size=%d\n",
+ extconf->name, extconf->cmd_size);
+ }
+ cmd_size = extconf->cmd_size;
+ if (cmd_size > 1) {
+ i = 0;
+ while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+ for (j = 0; j < cmd_size; j++) {
+ ret = of_property_read_u32_index(
+ of_node, "init_on",
+ (i + j), &val);
+ if (ret) {
+ EXTERR("get init_on failed\n");
+ extconf->table_init_on[i] =
+ LCD_EXTERN_INIT_END;
+ goto spi_get_init_on_dts;
+ }
+ extconf->table_init_on[i + j] =
+ (unsigned char)val;
+ }
+ if (extconf->table_init_on[i] ==
+ LCD_EXTERN_INIT_END) {
+ break;
+ } else if (extconf->table_init_on[i] ==
+ LCD_EXTERN_INIT_GPIO) {
+ /* gpio request */
+ index = extconf->table_init_on[i+1];
+ if (index < LCD_EXTERN_GPIO_NUM_MAX)
+ lcd_extern_gpio_register(index);
+ }
+ i += cmd_size;
+ }
+ extconf->table_init_loaded = 1;
+spi_get_init_on_dts:
+ i = 0;
+ while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+ for (j = 0; j < cmd_size; j++) {
+ ret = of_property_read_u32_index(
+ of_node, "init_off",
+ (i + j), &val);
+ if (ret) {
+ EXTERR("get init_off failed\n");
+ extconf->table_init_off[i] =
+ LCD_EXTERN_INIT_END;
+ goto spi_get_init_off_dts;
+ }
+ extconf->table_init_off[i + j] =
+ (unsigned char)val;
+ }
+ if (extconf->table_init_off[i] ==
+ LCD_EXTERN_INIT_END) {
+ break;
+ } else if (extconf->table_init_off[i] ==
+ LCD_EXTERN_INIT_GPIO) {
+ /* gpio request */
+ index = extconf->table_init_off[i+1];
+ if (index < LCD_EXTERN_GPIO_NUM_MAX)
+ lcd_extern_gpio_register(index);
+ }
+ i += cmd_size;
+ }
+ }
+spi_get_init_off_dts:
+ break;
+ case LCD_EXTERN_MIPI:
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+#endif
+
+static unsigned char aml_lcd_extern_i2c_bus_table[][2] = {
+ {LCD_EXTERN_I2C_BUS_AO, AML_I2C_MASTER_AO},
+ {LCD_EXTERN_I2C_BUS_A, AML_I2C_MASTER_A},
+ {LCD_EXTERN_I2C_BUS_B, AML_I2C_MASTER_B},
+ {LCD_EXTERN_I2C_BUS_C, AML_I2C_MASTER_C},
+ {LCD_EXTERN_I2C_BUS_D, AML_I2C_MASTER_D},
+};
+
+static unsigned char lcd_extern_get_i2c_bus_unifykey(unsigned char val)
+{
+ unsigned char i2c_bus = LCD_EXTERN_I2C_BUS_INVALID;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(aml_lcd_extern_i2c_bus_table); i++) {
+ if (aml_lcd_extern_i2c_bus_table[i][0] == val) {
+ i2c_bus = aml_lcd_extern_i2c_bus_table[i][1];
+ break;
+ }
+ }
+
+ return i2c_bus;
+}
+
+static int lcd_extern_get_config_unifykey(struct lcd_extern_config_s *extconf)
+{
+ unsigned char *para;
+ int i, j, key_len, len;
+ unsigned char cmd_size;
+ unsigned char *p;
+ const char *str;
+ struct aml_lcd_unifykey_header_s ext_header;
+ unsigned char index;
+ int ret;
+
+ extconf->index = LCD_EXTERN_INDEX_INVALID;
+ extconf->type = LCD_EXTERN_MAX;
+ extconf->table_init_loaded = 0;
+ extconf->table_init_on = lcd_extern_init_on_table;
+ extconf->table_init_off = lcd_extern_init_off_table;
+
+ para = kmalloc((sizeof(unsigned char) * LCD_UKEY_LCD_EXT_SIZE),
+ GFP_KERNEL);
+ if (!para) {
+ EXTERR("%s: Not enough memory\n", __func__);
+ return -1;
+ }
+ key_len = LCD_UKEY_LCD_EXT_SIZE;
+ memset(para, 0, (sizeof(unsigned char) * key_len));
+ ret = lcd_unifykey_get("lcd_extern", para, &key_len);
+ if (ret) {
+ kfree(para);
+ return -1;
+ }
+
+ /* check lcd_extern unifykey length */
+ len = 10 + 33 + 10;
+ ret = lcd_unifykey_len_check(key_len, len);
+ if (ret) {
+ EXTERR("unifykey length is not correct\n");
+ kfree(para);
+ return -1;
+ }
+
+ /* header: 10byte */
+ lcd_unifykey_header_check(para, &ext_header);
+ if (lcd_debug_print_flag) {
+ EXTPR("unifykey header:\n");
+ EXTPR("crc32 = 0x%08x\n", ext_header.crc32);
+ EXTPR("data_len = %d\n", ext_header.data_len);
+ EXTPR("version = 0x%04x\n", ext_header.version);
+ EXTPR("reserved = 0x%04x\n", ext_header.reserved);
+ }
+
+ /* basic: 33byte */
+ p = para + LCD_UKEY_HEAD_SIZE;
+ *(p + LCD_UKEY_EXT_NAME - 1) = '\0'; /* ensure string ending */
+ str = (const char *)p;
+ strcpy(extconf->name, str);
+ p += LCD_UKEY_EXT_NAME;
+ extconf->index = *p;
+ p += LCD_UKEY_EXT_INDEX;
+ extconf->type = *p;
+ p += LCD_UKEY_EXT_TYPE;
+ extconf->status = *p;
+ p += LCD_UKEY_EXT_STATUS;
+
+ /* type: 10byte */
+ switch (extconf->type) {
+ case LCD_EXTERN_I2C:
+ extconf->i2c_addr = *p;
+ p += LCD_UKEY_EXT_TYPE_VAL_0;
+ extconf->i2c_addr2 = *p;
+ p += LCD_UKEY_EXT_TYPE_VAL_1;
+ extconf->i2c_bus = lcd_extern_get_i2c_bus_unifykey(*p);
+ p += LCD_UKEY_EXT_TYPE_VAL_2;
+ extconf->cmd_size = *p;
+ p += LCD_UKEY_EXT_TYPE_VAL_3;
+ /* dummy pointer */
+ p += LCD_UKEY_EXT_TYPE_VAL_4;
+ p += LCD_UKEY_EXT_TYPE_VAL_5;
+ p += LCD_UKEY_EXT_TYPE_VAL_6;
+ p += LCD_UKEY_EXT_TYPE_VAL_7;
+ p += LCD_UKEY_EXT_TYPE_VAL_8;
+ p += LCD_UKEY_EXT_TYPE_VAL_9;
+
+ /* power */
+ cmd_size = extconf->cmd_size;
+ if (cmd_size >= 1) {
+ i = 0;
+ while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+ len += cmd_size;
+ ret = lcd_unifykey_len_check(key_len, len);
+ if (ret) {
+ extconf->table_init_on[i] =
+ LCD_EXTERN_INIT_END;
+ for (j = 1; j < cmd_size; j++) {
+ extconf->table_init_on[i+j] =
+ 0x0;
+ }
+ kfree(para);
+ return -1;
+ }
+ for (j = 0; j < cmd_size; j++) {
+ extconf->table_init_on[i+j] = *p;
+ p++;
+ }
+ if (extconf->table_init_on[i] ==
+ LCD_EXTERN_INIT_END) {
+ break;
+ } else if (extconf->table_init_on[i] ==
+ LCD_EXTERN_INIT_GPIO) {
+ /* gpio request */
+ index = extconf->table_init_on[i+1];
+ if (index < LCD_EXTERN_GPIO_NUM_MAX)
+ lcd_extern_gpio_register(index);
+ }
+ i += cmd_size;
+ }
+ extconf->table_init_loaded = 1;
+ i = 0;
+ while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+ len += cmd_size;
+ ret = lcd_unifykey_len_check(key_len, len);
+ if (ret) {
+ extconf->table_init_off[i] =
+ LCD_EXTERN_INIT_END;
+ for (j = 1; j < cmd_size; j++) {
+ extconf->table_init_off[i+j] =
+ 0x0;
+ }
+ kfree(para);
+ return -1;
+ }
+ for (j = 0; j < cmd_size; j++) {
+ extconf->table_init_off[i+j] = *p;
+ p++;
+ }
+ if (extconf->table_init_off[i] ==
+ LCD_EXTERN_INIT_END) {
+ break;
+ } else if (extconf->table_init_off[i] ==
+ LCD_EXTERN_INIT_GPIO) {
+ /* gpio request */
+ index = extconf->table_init_off[i+1];
+ if (index < LCD_EXTERN_GPIO_NUM_MAX)
+ lcd_extern_gpio_register(index);
+ } else {
+ i += cmd_size;
+ }
+ }
+ }
+ break;
+ case LCD_EXTERN_SPI:
+ extconf->spi_gpio_cs = *p;
+ lcd_extern_gpio_register(*p);
+ p += LCD_UKEY_EXT_TYPE_VAL_0;
+ extconf->spi_gpio_clk = *p;
+ lcd_extern_gpio_register(*p);
+ p += LCD_UKEY_EXT_TYPE_VAL_1;
+ extconf->spi_gpio_data = *p;
+ lcd_extern_gpio_register(*p);
+ p += LCD_UKEY_EXT_TYPE_VAL_2;
+ extconf->spi_clk_freq = (*p | ((*(p + 1)) << 8) |
+ ((*(p + 2)) << 16) |
+ ((*(p + 3)) << 24));
+ p += LCD_UKEY_EXT_TYPE_VAL_3;
+ p += LCD_UKEY_EXT_TYPE_VAL_4;
+ p += LCD_UKEY_EXT_TYPE_VAL_5;
+ p += LCD_UKEY_EXT_TYPE_VAL_6;
+ extconf->spi_clk_pol = *p;
+ p += LCD_UKEY_EXT_TYPE_VAL_7;
+ extconf->cmd_size = *p;
+ p += LCD_UKEY_EXT_TYPE_VAL_8;
+ /* dummy pointer */
+ p += LCD_UKEY_EXT_TYPE_VAL_9;
+
+ /* init */
+ cmd_size = extconf->cmd_size;
+ if (cmd_size >= 1) {
+ i = 0;
+ while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+ len += cmd_size;
+ ret = lcd_unifykey_len_check(key_len, len);
+ if (ret) {
+ extconf->table_init_on[i] =
+ LCD_EXTERN_INIT_END;
+ for (j = 1; j < cmd_size; j++) {
+ extconf->table_init_on[i+j] =
+ 0x0;
+ }
+ kfree(para);
+ return -1;
+ }
+ for (j = 0; j < cmd_size; j++) {
+ extconf->table_init_on[i+j] = *p;
+ p++;
+ }
+ if (extconf->table_init_on[i] ==
+ LCD_EXTERN_INIT_END) {
+ break;
+ } else if (extconf->table_init_on[i] ==
+ LCD_EXTERN_INIT_GPIO) {
+ /* gpio request */
+ index = extconf->table_init_on[i+1];
+ if (index < LCD_EXTERN_GPIO_NUM_MAX)
+ lcd_extern_gpio_register(index);
+ } else {
+ i += cmd_size;
+ }
+ }
+ extconf->table_init_loaded = 1;
+ i = 0;
+ while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+ len += cmd_size;
+ ret = lcd_unifykey_len_check(key_len, len);
+ if (ret) {
+ extconf->table_init_off[i] =
+ LCD_EXTERN_INIT_END;
+ for (j = 1; j < cmd_size; j++) {
+ extconf->table_init_off[i+j] =
+ 0x0;
+ }
+ kfree(para);
+ return -1;
+ }
+ for (j = 0; j < cmd_size; j++) {
+ extconf->table_init_off[i+j] = *p;
+ p++;
+ }
+ if (extconf->table_init_off[i] ==
+ LCD_EXTERN_INIT_END) {
+ break;
+ } else if (extconf->table_init_off[i] ==
+ LCD_EXTERN_INIT_GPIO) {
+ /* gpio request */
+ index = extconf->table_init_off[i+1];
+ if (index < LCD_EXTERN_GPIO_NUM_MAX)
+ lcd_extern_gpio_register(index);
+ } else {
+ i += cmd_size;
+ }
+ }
+ }
+ break;
+ case LCD_EXTERN_MIPI:
+ /* dummy pointer */
+ p += LCD_UKEY_EXT_TYPE_VAL_0;
+ p += LCD_UKEY_EXT_TYPE_VAL_1;
+ p += LCD_UKEY_EXT_TYPE_VAL_2;
+ p += LCD_UKEY_EXT_TYPE_VAL_3;
+ p += LCD_UKEY_EXT_TYPE_VAL_4;
+ p += LCD_UKEY_EXT_TYPE_VAL_5;
+ p += LCD_UKEY_EXT_TYPE_VAL_6;
+ p += LCD_UKEY_EXT_TYPE_VAL_7;
+ p += LCD_UKEY_EXT_TYPE_VAL_8;
+ p += LCD_UKEY_EXT_TYPE_VAL_9;
+
+ /* init */
+ /* to do */
+ break;
+ default:
+ break;
+ }
+
+ kfree(para);
+ return 0;
+}
+
+static int lcd_extern_get_config(void)
+{
+ struct device_node *child;
+ struct lcd_extern_config_s extconf;
+ int load_id = 0;
+ int ret;
+
+ if (lcd_extern_dev->of_node == NULL) {
+ EXTERR("no lcd_extern of_node exist\n");
+ return -1;
+ }
+ ret = of_property_read_u32(lcd_extern_dev->of_node,
+ "key_valid", &lcd_ext_key_valid);
+ if (ret) {
+ if (lcd_debug_print_flag)
+ EXTPR("failed to get key_valid\n");
+ lcd_ext_key_valid = 0;
+ }
+ EXTPR("key_valid: %d\n", lcd_ext_key_valid);
+
+ if (lcd_ext_key_valid) {
+ ret = lcd_unifykey_check("lcd_extern");
+ if (ret < 0)
+ load_id = 0;
+ else
+ load_id = 1;
+ }
+ if (load_id) {
+ EXTPR("%s from unifykey\n", __func__);
+ lcd_ext_config_load = 1;
+ memset(&extconf, 0, sizeof(struct lcd_extern_config_s));
+ ret = lcd_extern_get_config_unifykey(&extconf);
+ if (ret == 0)
+ lcd_extern_add_driver(&extconf);
+ } else {
+#ifdef CONFIG_OF
+ EXTPR("%s from dts\n", __func__);
+ lcd_ext_config_load = 0;
+ for_each_child_of_node(lcd_extern_dev->of_node, child) {
+ memset(&extconf, 0, sizeof(struct lcd_extern_config_s));
+ ret = lcd_extern_get_config_dts(child, &extconf);
+ if (ret == 0)
+ lcd_extern_add_driver(&extconf);
+ }
+#endif
+ }
+ return 0;
+}
+
+static int lcd_extern_add_i2c(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ int ret = 0;
+
+#ifdef LCD_EXTERN_DEFAULT_ENABLE
+ if (ext_drv->config.index == 0) {
+ ret = aml_lcd_extern_default_probe(ext_drv);
+ return ret;
+ }
+#endif
+
+ if (strcmp(ext_drv->config.name, "i2c_T5800Q") == 0) {
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_T5800Q
+ ret = aml_lcd_extern_i2c_T5800Q_probe(ext_drv);
+#endif
+ } else if (strcmp(ext_drv->config.name, "i2c_tc101") == 0) {
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_TC101
+ ret = aml_lcd_extern_i2c_tc101_probe(ext_drv);
+#endif
+ } else if (strcmp(ext_drv->config.name, "i2c_anx6345") == 0) {
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_ANX6345
+ ret = aml_lcd_extern_i2c_anx6345_probe(ext_drv);
+#endif
+ } else if (strcmp(ext_drv->config.name, "i2c_DLPC3439") == 0) {
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_DLPC3439
+ ret = aml_lcd_extern_i2c_DLPC3439_probe(ext_drv);
+#endif
+ } else {
+ EXTERR("invalid driver name: %s\n", ext_drv->config.name);
+ ret = -1;
+ }
+ return ret;
+}
+
+static int lcd_extern_add_spi(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ int ret = 0;
+
+#ifdef LCD_EXTERN_DEFAULT_ENABLE
+ if (ext_drv->config.index == 0) {
+ ret = aml_lcd_extern_default_probe(ext_drv);
+ return ret;
+ }
+#endif
+
+ if (strcmp(ext_drv->config.name, "spi_LD070WS2") == 0) {
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_SPI_LD070WS2
+ ret = aml_lcd_extern_spi_LD070WS2_probe(ext_drv);
+#endif
+ } else {
+ EXTERR("invalid driver name: %s\n", ext_drv->config.name);
+ ret = -1;
+ }
+ return ret;
+}
+
+static int lcd_extern_add_mipi(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ int ret = 0;
+
+#ifdef LCD_EXTERN_DEFAULT_ENABLE
+ if (ext_drv->config.index == 0) {
+ ret = aml_lcd_extern_default_probe(ext_drv);
+ return ret;
+ }
+#endif
+
+ if (strcmp(ext_drv->config.name, "mipi_N070ICN") == 0) {
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_N070ICN
+ ret = aml_lcd_extern_mipi_N070ICN_probe(ext_drv);
+#endif
+ } else if (strcmp(ext_drv->config.name, "mipi_KD080D13") == 0) {
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_KD080D13
+ ret = aml_lcd_extern_mipi_KD080D13_probe(ext_drv);
+#endif
+ } else {
+ EXTERR("invalid driver name: %s\n", ext_drv->config.name);
+ ret = -1;
+ }
+ return ret;
+}
+
+static int lcd_extern_add_invalid(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ return -1;
+}
+
+static int lcd_extern_add_driver(struct lcd_extern_config_s *extconf)
+{
+ struct aml_lcd_extern_driver_s *ext_drv;
+ int i;
+ int ret = 0;
+
+ if (lcd_ext_driver_num >= LCD_EXT_DRIVER_MAX) {
+ EXTERR("driver num is out of support\n");
+ return -1;
+ }
+ if (extconf->status == 0) {
+ EXTERR("driver %s[%d] status is disabled\n",
+ extconf->name, extconf->index);
+ return -1;
+ }
+
+ i = lcd_ext_driver_num;
+ lcd_ext_driver[i] =
+ kmalloc(sizeof(struct aml_lcd_extern_driver_s), GFP_KERNEL);
+ if (lcd_ext_driver[i] == NULL) {
+ EXTERR("failed to alloc driver %s[%d], not enough memory\n",
+ extconf->name, extconf->index);
+ return -1;
+ }
+
+ ext_drv = lcd_ext_driver[i];
+ /* fill config parameters */
+ ext_drv->config.index = extconf->index;
+ strcpy(ext_drv->config.name, extconf->name);
+ ext_drv->config.type = extconf->type;
+ ext_drv->config.status = extconf->status;
+ ext_drv->config.table_init_loaded = extconf->table_init_loaded;
+ ext_drv->config.table_init_on = lcd_extern_init_on_table;
+ ext_drv->config.table_init_off = lcd_extern_init_off_table;
+
+ /* fill config parameters by different type */
+ switch (ext_drv->config.type) {
+ case LCD_EXTERN_I2C:
+ ext_drv->config.i2c_addr = extconf->i2c_addr;
+ ext_drv->config.i2c_addr2 = extconf->i2c_addr2;
+ ext_drv->config.i2c_bus = extconf->i2c_bus;
+ ext_drv->config.cmd_size = extconf->cmd_size;
+ ret = lcd_extern_add_i2c(ext_drv);
+ break;
+ case LCD_EXTERN_SPI:
+ ext_drv->config.spi_gpio_cs = extconf->spi_gpio_cs;
+ ext_drv->config.spi_gpio_clk = extconf->spi_gpio_clk;
+ ext_drv->config.spi_gpio_data = extconf->spi_gpio_data;
+ ext_drv->config.spi_clk_freq = extconf->spi_clk_freq;
+ ext_drv->config.spi_clk_pol = extconf->spi_clk_pol;
+ ext_drv->config.cmd_size = extconf->cmd_size;
+ ret = lcd_extern_add_spi(ext_drv);
+ break;
+ case LCD_EXTERN_MIPI:
+ ret = lcd_extern_add_mipi(ext_drv);
+ break;
+ default:
+ ret = lcd_extern_add_invalid(ext_drv);
+ EXTERR("don't support type %d\n", ext_drv->config.type);
+ break;
+ }
+ if (ret) {
+ EXTERR("add driver failed\n");
+ kfree(lcd_ext_driver[i]);
+ lcd_ext_driver[i] = NULL;
+ return -1;
+ }
+ lcd_ext_driver_num++;
+ EXTPR("add driver %s(%d)\n",
+ ext_drv->config.name, ext_drv->config.index);
+ return 0;
+}
+
+/* debug function */
+static void lcd_extern_config_dump(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ int i, j, len;
+ struct lcd_extern_config_s *econf;
+
+ if (ext_drv == NULL)
+ return;
+
+ econf = &ext_drv->config;
+ EXTPR("driver %s(%d) info:\n", econf->name, econf->index);
+ pr_info("status: %d\n", econf->status);
+ switch (econf->type) {
+ case LCD_EXTERN_I2C:
+ pr_info("type: i2c(%d)\n", econf->type);
+ pr_info("cmd_size: %d\n"
+ "i2c_addr: 0x%02x\n"
+ "i2c_addr2: 0x%02x\n"
+ "i2c_bus: %d\n"
+ "table_loaded: %d\n",
+ econf->cmd_size, econf->i2c_addr,
+ econf->i2c_addr2, econf->i2c_bus,
+ econf->table_init_loaded);
+ len = econf->cmd_size;
+ pr_info("power on:\n");
+ i = 0;
+ while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+ if (econf->table_init_on[i] == LCD_EXTERN_INIT_END) {
+ pr_info("\n");
+ } else {
+ for (j = 0; j < len; j++) {
+ pr_info("0x%02x ",
+ econf->table_init_on[i+j]);
+ }
+ pr_info("\n");
+ }
+ i += len;
+ }
+ pr_info("power off:\n");
+ i = 0;
+ while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+ if (econf->table_init_off[i] == LCD_EXTERN_INIT_END) {
+ pr_info("\n");
+ } else {
+ for (j = 0; j < len; j++) {
+ pr_info("0x%02x ",
+ econf->table_init_off[i+j]);
+ }
+ pr_info("\n");
+ }
+ i += len;
+ }
+ break;
+ case LCD_EXTERN_SPI:
+ pr_info("type: spi(%d)\n", econf->type);
+ pr_info("cmd_size: %d\n"
+ "spi_gpio_cs: %d\n"
+ "spi_gpio_clk: %d\n"
+ "spi_gpio_data: %d\n"
+ "spi_clk_freq: %dHz\n"
+ "spi_clk_pol: %d\n"
+ "table_loaded: %d\n",
+ econf->cmd_size, econf->spi_gpio_cs,
+ econf->spi_gpio_clk, econf->spi_gpio_data,
+ econf->spi_clk_freq, econf->spi_clk_pol,
+ econf->table_init_loaded);
+ len = econf->cmd_size;
+ i = 0;
+ while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+ if (econf->table_init_on[i] == LCD_EXTERN_INIT_END) {
+ pr_info("\n");
+ } else {
+ for (j = 0; j < len; j++) {
+ pr_info("0x%02x ",
+ econf->table_init_on[i+j]);
+ }
+ pr_info("\n");
+ }
+ i += len;
+ }
+ i = 0;
+ while (i < LCD_EXTERN_INIT_TABLE_MAX) {
+ if (econf->table_init_off[i] == LCD_EXTERN_INIT_END) {
+ pr_info("\n");
+ } else {
+ for (j = 0; j < len; j++) {
+ pr_info("0x%02x ",
+ econf->table_init_off[i+j]);
+ }
+ pr_info("\n");
+ }
+ i += len;
+ }
+ break;
+ case LCD_EXTERN_MIPI:
+ pr_info("type: mipi(%d)\n", econf->type);
+ break;
+ default:
+ pr_info("not support extern_type\n");
+ break;
+ }
+ pr_info("\n");
+}
+
+static const char *lcd_extern_debug_usage_str = {
+"Usage:\n"
+" echo index <n> > info ; dump specified index driver config\n"
+" echo all > info ; dump all driver config\n"
+};
+
+static ssize_t lcd_extern_debug_help(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", lcd_extern_debug_usage_str);
+}
+
+static ssize_t lcd_extern_info_dump(struct class *class,
+ struct class_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int ret;
+ int i, index;
+ struct aml_lcd_extern_driver_s *ext_drv;
+
+ index = LCD_EXTERN_INDEX_INVALID;
+ switch (buf[0]) {
+ case 'i':
+ ret = sscanf(buf, "index %d", &index);
+ ext_drv = aml_lcd_extern_get_driver(index);
+ lcd_extern_config_dump(ext_drv);
+ break;
+ case 'a':
+ for (i = 0; i < lcd_ext_driver_num; i++)
+ lcd_extern_config_dump(lcd_ext_driver[i]);
+ break;
+ default:
+ EXTERR("invalid command\n");
+ break;
+ }
+
+ if (ret != 1 || ret != 2)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t lcd_extern_debug_key_valid_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", lcd_ext_key_valid);
+}
+
+static ssize_t lcd_extern_debug_config_load_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", lcd_ext_config_load);
+}
+
+static struct class_attribute lcd_extern_class_attrs[] = {
+ __ATTR(info, 0644,
+ lcd_extern_debug_help, lcd_extern_info_dump),
+ __ATTR(key_valid, 0644,
+ lcd_extern_debug_key_valid_show, NULL),
+ __ATTR(config_load, 0644,
+ lcd_extern_debug_config_load_show, NULL),
+};
+
+static struct class *debug_class;
+static int creat_lcd_extern_class(void)
+{
+ int i;
+
+ debug_class = class_create(THIS_MODULE, "lcd_ext");
+ if (IS_ERR(debug_class)) {
+ EXTERR("create debug class failed\n");
+ return -1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lcd_extern_class_attrs); i++) {
+ if (class_create_file(debug_class,
+ &lcd_extern_class_attrs[i])) {
+ EXTERR("create debug attribute %s failed\n",
+ lcd_extern_class_attrs[i].attr.name);
+ }
+ }
+
+ return 0;
+}
+
+static int remove_lcd_extern_class(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lcd_extern_class_attrs); i++)
+ class_remove_file(debug_class, &lcd_extern_class_attrs[i]);
+
+ class_destroy(debug_class);
+ debug_class = NULL;
+
+ return 0;
+}
+/* ********************************************************* */
+
+static int aml_lcd_extern_probe(struct platform_device *pdev)
+{
+ lcd_extern_dev = &pdev->dev;
+ lcd_ext_driver_num = 0;
+ lcd_extern_get_config(); /* also add ext_driver */
+
+ creat_lcd_extern_class();
+
+ EXTPR("%s ok\n", __func__);
+ return 0;
+}
+
+static int aml_lcd_extern_remove(struct platform_device *pdev)
+{
+ int i;
+
+ remove_lcd_extern_class();
+ for (i = 0; i < lcd_ext_driver_num; i++) {
+ kfree(lcd_ext_driver[i]);
+ lcd_ext_driver[i] = NULL;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id aml_lcd_extern_dt_match[] = {
+ {
+ .compatible = "amlogic, lcd_extern",
+ },
+ {},
+};
+#endif
+
+static struct platform_driver aml_lcd_extern_driver = {
+ .probe = aml_lcd_extern_probe,
+ .remove = aml_lcd_extern_remove,
+ .driver = {
+ .name = "lcd_extern",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = aml_lcd_extern_dt_match,
+#endif
+ },
+};
+
+static int __init aml_lcd_extern_init(void)
+{
+ int ret;
+
+ if (lcd_debug_print_flag)
+ EXTPR("%s\n", __func__);
+
+ ret = platform_driver_register(&aml_lcd_extern_driver);
+ if (ret) {
+ EXTERR("driver register failed\n");
+ return -ENODEV;
+ }
+ return ret;
+}
+
+static void __exit aml_lcd_extern_exit(void)
+{
+ platform_driver_unregister(&aml_lcd_extern_driver);
+}
+
+late_initcall(aml_lcd_extern_init);
+module_exit(aml_lcd_extern_exit);
+
+MODULE_AUTHOR("AMLOGIC");
+MODULE_DESCRIPTION("LCD extern driver");
+MODULE_LICENSE("GPL");
+
--- /dev/null
+/*
+ * 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";
+ };
+};
+
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _LCD_EXTERN_H_
+#define _LCD_EXTERN_H_
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+
+#define EXTPR(fmt, args...) pr_info("lcd extern: "fmt"", ## args)
+#define EXTERR(fmt, args...) pr_info("lcd extern: error: "fmt"", ## args)
+
+#define LCD_EXTERN_DRIVER "lcd_extern"
+
+#ifdef CONFIG_USE_OF
+extern struct device_node *aml_lcd_extern_get_dts_child(int index);
+#endif
+extern void lcd_extern_gpio_register(unsigned char index);
+extern void lcd_extern_gpio_set(unsigned char index, int value);
+extern unsigned int lcd_extern_gpio_get(unsigned char index);
+
+#define LCD_EXTERN_DEFAULT_ENABLE
+
+#ifdef LCD_EXTERN_DEFAULT_ENABLE
+extern int aml_lcd_extern_default_probe(
+ struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_T5800Q
+extern int aml_lcd_extern_i2c_T5800Q_probe(
+ struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_DLPC3439
+extern int aml_lcd_extern_i2c_DLPC3439_probe(
+ struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_TC101
+extern int aml_lcd_extern_i2c_tc101_probe(
+ struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_ANX6345
+extern int aml_lcd_extern_i2c_anx6345_probe(
+ struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_SPI_LD070WS2
+extern int aml_lcd_extern_spi_LD070WS2_probe(
+ struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_N070ICN
+extern int aml_lcd_extern_mipi_N070ICN_probe(
+ struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_KD080D13
+extern int aml_lcd_extern_mipi_KD080D13_probe(
+ struct aml_lcd_extern_driver_s *ext_drv);
+#endif
+
+#endif
+
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/mipi_KD080D13.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+
+#define LCD_EXTERN_NAME "lcd_mipi_KD080D13"
+
+/* ******************** mipi command ********************
+ * format: data_type, num, data....
+ * special: data_type=0xff, num<0xff means delay ms, num=0xff means ending.
+ */
+static unsigned char mipi_init_on_table[] = {
+ 0x39, 3, 0xf0, 0x5a, 0x5a,
+ 0x39, 3, 0xf1, 0x5a, 0x5a,
+ 0x39, 3, 0xfc, 0xa5, 0xa5,
+ 0x39, 3, 0xd0, 0x00, 0x10,
+ 0x15, 2, 0xb1, 0x10,
+ 0x39, 5, 0xb2, 0x14, 0x22, 0x2f, 0x04,
+ 0x39, 6, 0xf2, 0x02, 0x08, 0x08, 0x40, 0x10,
+ 0x15, 2, 0xb0, 0x03,
+ 0x39, 3, 0xfd, 0x23, 0x09,
+ 0x39, 11, 0xf3, 0x01, 0x93, 0x20, 0x22, 0x80, 0x05, 0x25, 0x3c, 0x26,
+ 0x00,
+ 0x39, 46, 0xf4, 0x00, 0x02, 0x03, 0x26, 0x03, 0x02, 0x09, 0x00, 0x07,
+ 0x16, 0x16, 0x03, 0x00, 0x08, 0x08, 0x03, 0x0E, 0x0F, 0x12,
+ 0x1C, 0x1D, 0x1E, 0x0C, 0x09, 0x01, 0x04, 0x02, 0x61, 0x74,
+ 0x75, 0x72, 0x83, 0x80, 0x80, 0xB0, 0x00, 0x01, 0x01, 0x28,
+ 0x04, 0x03, 0x28, 0x01, 0xD1, 0x32,
+ 0x39, 27, 0xf5, 0x84, 0x2F, 0x2F, 0x5F, 0xAB, 0x98, 0x52, 0x0F, 0x33,
+ 0x43, 0x04, 0x59, 0x54, 0x52, 0x05, 0x40, 0x60, 0x4E, 0x60,
+ 0x40, 0x27, 0x26, 0x52, 0x25, 0x6D, 0x18,
+ 0x39, 9, 0xee, 0x25, 0x00, 0x25, 0x00, 0x25, 0x00, 0x25, 0x00,
+ 0x39, 9, 0xef, 0x34, 0x12, 0x98, 0xBA, 0x20, 0x00, 0x24, 0x80,
+ 0x39, 33, 0xf7, 0x0E, 0x0E, 0x0A, 0x0A, 0x0F, 0x0F, 0x0B, 0x0B, 0x05,
+ 0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0C, 0x0C, 0x08,
+ 0x08, 0x0D, 0x0D, 0x09, 0x09, 0x04, 0x06, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01,
+ 0x39, 4, 0xbc, 0x01, 0x4e, 0x0a,
+ 0x39, 6, 0xe1, 0x03, 0x10, 0x1c, 0xa0, 0x10,
+ 0x39, 7, 0xf6, 0x60, 0x21, 0xA6, 0x00, 0x00, 0x00,
+ 0x39, 7, 0xfe, 0x00, 0x0D, 0x03, 0x21, 0x80, 0x48,
+ 0x15, 2, 0xb0, 0x22,
+ 0x39, 18, 0xfa, 0x02, 0x34, 0x09, 0x13, 0x0B, 0x0F, 0x16, 0x16, 0x17,
+ 0x1E, 0x1D, 0x1C, 0x1E, 0x1D, 0x1D, 0x1F, 0x24,
+ 0x15, 2, 0xb0, 0x22,
+ 0x39, 18, 0xfb, 0x00, 0x34, 0x07, 0x11, 0x09, 0x0D, 0x14, 0x14, 0x15,
+ 0x1C, 0x1F, 0x1C, 0x1D, 0x1D, 0x1D, 0x20, 0x26,
+ 0x15, 2, 0xb0, 0x11,
+ 0x39, 18, 0xfa, 0x20, 0x34, 0x24, 0x27, 0x19, 0x1B, 0x1F, 0x1E, 0x1B,
+ 0x1F, 0x21, 0x1F, 0x1E, 0x20, 0x1E, 0x1E, 0x21,
+ 0x15, 2, 0xb0, 0x11,
+ 0x39, 18, 0xfb, 0x1E, 0x34, 0x22, 0x25, 0x17, 0x19, 0x1D, 0x1A, 0x19,
+ 0x20, 0x1F, 0x1E, 0x20, 0x1E, 0x1E, 0x1F, 0x22,
+ 0x39, 18, 0xfa, 0x1C, 0x34, 0x1C, 0x1F, 0x13, 0x17, 0x1A, 0x18, 0x18,
+ 0x1E, 0x20, 0x21, 0x21, 0x21, 0x23, 0x22, 0x2A,
+ 0x39, 18, 0xfb, 0x1A, 0x34, 0x1A, 0x1D, 0x11, 0x15, 0x18, 0x16, 0x16,
+ 0x1C, 0x20, 0x20, 0x20, 0x1F, 0x23, 0x23, 0x2B,
+
+ 0x05, 1, 0x11,
+ 0xff, 20,
+ 0x39, 4, 0xc3, 0x40, 0x00, 0x28,
+ 0xff, 200,
+ 0x15, 2, 0x35, 0x00,
+ 0x05, 1, 0x29,
+ 0xff, 30, /* delay 30ms */
+ 0xff, 0xff, /* ending flag */
+};
+
+static unsigned char mipi_init_off_table[] = {
+ 0x05, 1, 0x28, /* display off */
+ 0xff, 30, /* delay 30ms */
+ 0x05, 1, 0x10, /* sleep in */
+ 0xff, 30, /* delay 30ms */
+ 0x39, 4, 0xc3, 0x40, 0x00, 0x20,
+ 0xff, 10,
+ 0xff, 0xff, /* ending flag */
+};
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ int ret = 0;
+
+ if (ext_drv) {
+ ext_drv->config.table_init_on = &mipi_init_on_table[0];
+ ext_drv->config.table_init_off = &mipi_init_off_table[0];
+ } else {
+ EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+int aml_lcd_extern_mipi_KD080D13_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ int ret = 0;
+
+ ret = lcd_extern_driver_update(ext_drv);
+
+ if (lcd_debug_print_flag)
+ EXTPR("%s: %d\n", __func__, ret);
+ return ret;
+}
+
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/mipi_N070ICN.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/amlogic/i2c-amlogic.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+
+#define LCD_EXTERN_NAME "lcd_mipi_N070ICN"
+
+/* ******************** mipi command ********************
+ * format: data_type, num, data....
+ * special: data_type=0xff, num<0xff means delay ms, num=0xff means ending.
+ */
+static unsigned char mipi_init_on_table[] = {
+ /* ========== Internal setting ========== */
+ 0x39, 5, 0xFF, 0xAA, 0x55, 0xA5, 0x80,
+
+ 0x39, 3, 0x6F, 0x11, 0x00, /* MIPI related Timing Setting */
+ 0x39, 3, 0xF7, 0x20, 0x00,
+
+ 0x15, 2, 0x6F, 0x06, /* Improve ESD option */
+ 0x15, 2, 0xF7, 0xA0,
+ 0x15, 2, 0x6F, 0x19,
+ 0x15, 2, 0xF7, 0x12,
+
+ 0x15, 2, 0x6F, 0x08, /* Vcom floating */
+ 0x15, 2, 0xFA, 0x40,
+ 0x15, 2, 0x6F, 0x11,
+ 0x15, 2, 0xF3, 0x01,
+
+ /* ========== page0 relative ========== */
+ 0x39, 6, 0xF0, 0x55, 0xAA, 0x52, 0x08, 0x00,
+ 0x15, 2, 0xC8, 0x80,
+
+ 0x39, 3, 0xB1, 0x6C, 0x01, /* Set WXGA resolution */
+
+ 0x15, 2, 0xB6, 0x08, /* Set source output hold time */
+
+ 0x15, 2, 0x6F, 0x02, /* EQ control function */
+ 0x15, 2, 0xB8, 0x08,
+
+ 0x39, 3, 0xBB, 0x54, 0x54, /* Set bias current for GOP and SOP */
+
+ 0x39, 3, 0xBC, 0x05, 0x05, /* Inversion setting */
+
+ 0x15, 2, 0xC7, 0x01, /* zigzag setting */
+
+ /* DSP Timing Settings update for BIST */
+ 0x39, 6, 0xBD, 0x02, 0xB0, 0x0C, 0x0A, 0x00,
+
+ /* ========== page1 relative ========== */
+ 0x39, 6, 0xF0, 0x55, 0xAA, 0x52, 0x08, 0x01,
+
+ 0x39, 3, 0xB0, 0x05, 0x05, /* Setting AVDD, AVEE clamp */
+ 0x39, 3, 0xB1, 0x05, 0x05,
+
+ 0x39, 3, 0xBC, 0x3A, 0x01, /* VGMP, VGMN, VGSP, VGSN setting */
+ 0x39, 3, 0xBD, 0x3E, 0x01,
+
+ 0x15, 2, 0xCA, 0x00, /* gate signal control */
+
+ 0x15, 2, 0xC0, 0x04, /* power IC control */
+ 0x15, 2, 0xB2, 0x00,
+ 0x15, 2, 0xBE, 0x80, /*vcom -1.88V */
+
+ 0x39, 3, 0xB3, 0x19, 0x19, /* Setting VGH=15V, VGL=-11V */
+ 0x39, 3, 0xB4, 0x12, 0x12,
+
+ 0x39, 3, 0xB9, 0x24, 0x24, /* power control for VGH, VGL */
+ 0x39, 3, 0xBA, 0x14, 0x14,
+
+ /* ========== page2 relative ========== */
+ 0x39, 6, 0xF0, 0x55, 0xAA, 0x52, 0x08, 0x02,
+
+ 0x15, 2, 0xEE, 0x01, /* Gamma setting */
+ /* Gradient Control for Gamma Voltage */
+ 0x39, 5, 0xEF, 0x09, 0x06, 0x15, 0x18,
+
+ /* ========== GOA relative ========== */
+ 0x39, 7, 0xB0, 0x00, 0x00, 0x00, 0x08, 0x00, 0x17,
+ 0x15, 2, 0x6F, 0x06,
+ 0x39, 7, 0xB0, 0x00, 0x25, 0x00, 0x30, 0x00, 0x45,
+ 0x15, 2, 0x6F, 0x0C,
+ 0x39, 5, 0xB0, 0x00, 0x56, 0x00, 0x7A,
+ 0x39, 7, 0xB1, 0x00, 0xA3, 0x00, 0xE7, 0x01, 0x20,
+ 0x15, 2, 0x6F, 0x06,
+ 0x39, 7, 0xB1, 0x01, 0x7A, 0x01, 0xC2, 0x01, 0xC5,
+ 0x15, 2, 0x6F, 0x0C,
+ 0x39, 5, 0xB1, 0x02, 0x06, 0x02, 0x5F,
+ 0x39, 7, 0xB2, 0x02, 0x92, 0x02, 0xD0, 0x02, 0xFC,
+ 0x15, 2, 0x6F, 0x06,
+ 0x39, 7, 0xB2, 0x03, 0x35, 0x03, 0x5D, 0x03, 0x8B,
+ 0x15, 2, 0x6F, 0x0C,
+ 0x39, 5, 0xB2, 0x03, 0xA2, 0x03, 0xBF,
+ 0x39, 5, 0xB3, 0x03, 0xD2, 0x03, 0xFF,
+
+ /* PAGE6 : GOUT Mapping, VGLO select */
+ 0x39, 6, 0xF0, 0x55, 0xAA, 0x52, 0x08, 0x06,
+ 0x39, 3, 0xB0, 0x00, 0x17,
+ 0x39, 3, 0xB1, 0x16, 0x15,
+ 0x39, 3, 0xB2, 0x14, 0x13,
+ 0x39, 3, 0xB3, 0x12, 0x11,
+ 0x39, 3, 0xB4, 0x10, 0x2D,
+ 0x39, 3, 0xB5, 0x01, 0x08,
+ 0x39, 3, 0xB6, 0x09, 0x31,
+ 0x39, 3, 0xB7, 0x31, 0x31,
+ 0x39, 3, 0xB8, 0x31, 0x31,
+ 0x39, 3, 0xB9, 0x31, 0x31,
+ 0x39, 3, 0xBA, 0x31, 0x31,
+ 0x39, 3, 0xBB, 0x31, 0x31,
+ 0x39, 3, 0xBC, 0x31, 0x31,
+ 0x39, 3, 0xBD, 0x31, 0x09,
+ 0x39, 3, 0xBE, 0x08, 0x01,
+ 0x39, 3, 0xBF, 0x2D, 0x10,
+ 0x39, 3, 0xC0, 0x11, 0x12,
+ 0x39, 3, 0xC1, 0x13, 0x14,
+ 0x39, 3, 0xC2, 0x15, 0x16,
+ 0x39, 3, 0xC3, 0x17, 0x00,
+ 0x39, 3, 0xE5, 0x31, 0x31,
+ 0x39, 3, 0xC4, 0x00, 0x17,
+ 0x39, 3, 0xC5, 0x16, 0x15,
+ 0x39, 3, 0xC6, 0x14, 0x13,
+ 0x39, 3, 0xC7, 0x12, 0x11,
+ 0x39, 3, 0xC8, 0x10, 0x2D,
+ 0x39, 3, 0xC9, 0x01, 0x08,
+ 0x39, 3, 0xCA, 0x09, 0x31,
+ 0x39, 3, 0xCB, 0x31, 0x31,
+ 0x39, 3, 0xCC, 0x31, 0x31,
+ 0x39, 3, 0xCD, 0x31, 0x31,
+ 0x39, 3, 0xCE, 0x31, 0x31,
+ 0x39, 3, 0xCF, 0x31, 0x31,
+ 0x39, 3, 0xD0, 0x31, 0x31,
+ 0x39, 3, 0xD1, 0x31, 0x09,
+ 0x39, 3, 0xD2, 0x08, 0x01,
+ 0x39, 3, 0xD3, 0x2D, 0x10,
+ 0x39, 3, 0xD4, 0x11, 0x12,
+ 0x39, 3, 0xD5, 0x13, 0x14,
+ 0x39, 3, 0xD6, 0x15, 0x16,
+ 0x39, 3, 0xD7, 0x17, 0x00,
+ 0x39, 3, 0xE6, 0x31, 0x31,
+ 0x39, 6, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, /* VGL level select; */
+ 0x39, 6, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x15, 2, 0xE7, 0x00,
+
+ /* ===page 3====//gate timing control */
+ 0x39, 6, 0xF0, 0x55, 0xAA, 0x52, 0x08, 0x03,
+ 0x39, 3, 0xB0, 0x20, 0x00,
+ 0x39, 3, 0xB1, 0x20, 0x00,
+ 0x39, 6, 0xB2, 0x05, 0x00, 0x42, 0x00, 0x00,
+ 0x39, 6, 0xB6, 0x05, 0x00, 0x42, 0x00, 0x00,
+ 0x39, 6, 0xBA, 0x53, 0x00, 0x42, 0x00, 0x00,
+ 0x39, 6, 0xBB, 0x53, 0x00, 0x42, 0x00, 0x00,
+ 0x15, 2, 0xC4, 0x40,
+
+ /* ===page 5==== */
+ 0x39, 6, 0xF0, 0x55, 0xAA, 0x52, 0x08, 0x05,
+ 0x39, 3, 0xB0, 0x17, 0x06,
+ 0x15, 2, 0xB8, 0x00,
+ 0x39, 6, 0xBD, 0x03, 0x01, 0x01, 0x00, 0x01,
+ 0x39, 3, 0xB1, 0x17, 0x06,
+ 0x39, 3, 0xB9, 0x00, 0x01,
+ 0x39, 3, 0xB2, 0x17, 0x06,
+ 0x39, 3, 0xBA, 0x00, 0x01,
+ 0x39, 3, 0xB3, 0x17, 0x06,
+ 0x39, 3, 0xBB, 0x0A, 0x00,
+ 0x39, 3, 0xB4, 0x17, 0x06,
+ 0x39, 3, 0xB5, 0x17, 0x06,
+ 0x39, 3, 0xB6, 0x14, 0x03,
+ 0x39, 3, 0xB7, 0x00, 0x00,
+ 0x39, 3, 0xBC, 0x02, 0x01,
+ 0x15, 2, 0xC0, 0x05,
+ 0x15, 2, 0xC4, 0xA5,
+ 0x39, 3, 0xC8, 0x03, 0x30,
+ 0x39, 3, 0xC9, 0x03, 0x51,
+ 0x39, 6, 0xD1, 0x00, 0x05, 0x03, 0x00, 0x00,
+ 0x39, 6, 0xD2, 0x00, 0x05, 0x09, 0x00, 0x00,
+ 0x15, 2, 0xE5, 0x02,
+ 0x15, 2, 0xE6, 0x02,
+ 0x15, 2, 0xE7, 0x02,
+ 0x15, 2, 0xE9, 0x02,
+ 0x15, 2, 0xED, 0x33,
+
+ 0x05, 1, 0x11, /* sleep out */
+ 0xff, 30, /* delay 30ms */
+ 0x05, 1, 0x29, /* display on */
+ 0xff, 30, /* delay 30ms */
+ 0xff, 0xff, /* ending flag */
+};
+
+static unsigned char mipi_init_off_table[] = {
+ 0x05, 1, 0x28, /* display off */
+ 0xff, 10, /* delay 10ms */
+ 0x05, 1, 0x10, /* sleep in */
+ 0xff, 10, /* delay 10ms */
+ 0xff, 0xff, /* ending flag */
+};
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ int ret = 0;
+
+ if (ext_drv) {
+ ext_drv->config.table_init_on = &mipi_init_on_table[0];
+ ext_drv->config.table_init_off = &mipi_init_off_table[0];
+ } else {
+ EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+int aml_lcd_extern_mipi_N070ICN_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ int ret = 0;
+
+ ret = lcd_extern_driver_update(ext_drv);
+
+ if (lcd_debug_print_flag)
+ EXTPR("%s: %d\n", __func__, ret);
+ return ret;
+}
+
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_extern/spi_LD070WS2.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/sysctl.h>
+#include <linux/uaccess.h>
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#include "lcd_extern.h"
+
+static struct lcd_extern_config_s *ext_config;
+
+#define LCD_EXTERN_NAME "lcd_spi_LD070WS2"
+
+#define SPI_DELAY 30 /* unit: us */
+
+#define LCD_EXTERN_CMD_SIZE 4
+static unsigned char init_on_table[] = {
+ 0x00, 0x00, 0x21, 0x00, /* reset */
+ 0x00, 0x00, 0xa5, 0x00, /* standby */
+ 0x00, 0x01, 0x30, 0x00, /* enable FRC/Dither */
+ 0x00, 0x02, 0x40, 0x00, /* enable normally black */
+ 0x00, 0x0e, 0x5f, 0x00, /* enable test mode1 */
+ 0x00, 0x0f, 0xa4, 0x00, /* enable test mode2 */
+ 0x00, 0x0d, 0x00, 0x00, /* enable SDRRS, enlarge OE width */
+ 0x00, 0x02, 0x43, 0x00, /* adjust charge sharing time */
+ 0x00, 0x0a, 0x28, 0x00, /* trigger bias reduction */
+ 0x00, 0x10, 0x41, 50, /* adopt 2 line/1 dot */ /* delay 50ms */
+ 0x00, 0x00, 0xad, 0x00, /* display on */
+ 0xff, 0x00, 0x00, 0x00, /* ending flag */
+};
+
+static unsigned char init_off_table[] = {
+ 0x00, 0x00, 0xa5, 0x00, /* standby */
+ 0xff, 0x00, 0x00, 0x00, /* ending flag */
+};
+
+static void set_lcd_csb(unsigned int v)
+{
+ lcd_extern_gpio_set(ext_config->spi_cs, v);
+ udelay(SPI_DELAY);
+}
+
+static void set_lcd_scl(unsigned int v)
+{
+ lcd_extern_gpio_set(ext_config->spi_clk, v);
+ udelay(SPI_DELAY);
+}
+
+static void set_lcd_sda(unsigned int v)
+{
+ lcd_extern_gpio_set(ext_config->spi_data, v);
+ udelay(SPI_DELAY);
+}
+
+static void spi_gpio_init(void)
+{
+ set_lcd_csb(1);
+ set_lcd_scl(1);
+ set_lcd_sda(1);
+}
+
+static void spi_gpio_off(void)
+{
+ set_lcd_sda(0);
+ set_lcd_scl(0);
+ set_lcd_csb(0);
+}
+
+static void spi_write_8(unsigned char addr, unsigned char data)
+{
+ int i;
+ unsigned int sdata;
+
+ sdata = (unsigned int)(addr & 0x3f);
+ sdata <<= 10;
+ sdata |= (data & 0xff);
+ sdata &= ~(1<<9); /* write flag */
+
+ set_lcd_csb(1);
+ set_lcd_scl(1);
+ set_lcd_sda(1);
+
+ set_lcd_csb(0);
+ for (i = 0; i < 16; i++) {
+ set_lcd_scl(0);
+ if (sdata & 0x8000)
+ set_lcd_sda(1);
+ else
+ set_lcd_sda(0);
+ sdata <<= 1;
+ set_lcd_scl(1);
+ }
+
+ set_lcd_csb(1);
+ set_lcd_scl(1);
+ set_lcd_sda(1);
+ udelay(SPI_DELAY);
+}
+
+static int lcd_extern_spi_write(unsigned char *buf, int len)
+{
+ if (len != 2) {
+ EXTERR("%s: len %d error\n", __func__, len);
+ return -1;
+ }
+ spi_write_8(buf[0], buf[1]);
+ return 0;
+}
+
+static int lcd_extern_power_cmd(unsigned char *init_table)
+{
+ int i = 0, len;
+ int ret = 0;
+
+ len = ext_config->cmd_size;
+ if (len < 1) {
+ EXTERR("%s: cmd_size %d is invalid\n", __func__, len);
+ return -1;
+ }
+
+ while (i <= LCD_EXTERN_INIT_TABLE_MAX) {
+ if (init_table[i] == LCD_EXTERN_INIT_END) {
+ break;
+ } else if (init_table[i] == LCD_EXTERN_INIT_NONE) {
+ /* do nothing, only for delay */
+ } else if (init_table[i] == LCD_EXTERN_INIT_GPIO) {
+ if (init_table[i+1] < LCD_GPIO_MAX) {
+ lcd_extern_gpio_set(init_table[i+1],
+ init_table[i+2]);
+ }
+ } else if (init_table[i] == LCD_EXTERN_INIT_CMD) {
+ ret = lcd_extern_spi_write(&init_table[i+1], (len-2));
+ } else {
+ EXTERR("%s(%d: %s): power_type %d is invalid\n",
+ __func__, ext_config->index,
+ ext_config->name, ext_config->type);
+ }
+ if (init_table[i+len-1] > 0)
+ mdelay(init_table[i+len-1]);
+ i += len;
+ }
+
+ return ret;
+}
+
+static int lcd_extern_power_ctrl(int flag)
+{
+ int ret = 0;
+
+ spi_gpio_init();
+ if (flag)
+ ret = lcd_extern_power_cmd(ext_config->table_init_on);
+ else
+ ret = lcd_extern_power_cmd(ext_config->table_init_off);
+ mdelay(10);
+ spi_gpio_off();
+
+ EXTPR("%s(%d: %s): %d\n",
+ __func__, ext_config->index, ext_config->name, flag);
+ return ret;
+}
+
+static int lcd_extern_power_on(void)
+{
+ int ret;
+
+ ret = lcd_extern_power_ctrl(1);
+ return ret;
+}
+
+static int lcd_extern_power_off(void)
+{
+ int ret;
+
+ ret = lcd_extern_power_ctrl(0);
+ return ret;
+}
+
+static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ if (ext_drv == NULL) {
+ EXTERR("%s driver is null\n", LCD_EXTERN_NAME);
+ return -1;
+ }
+
+ if (ext_drv->config.table_init_loaded == 0) {
+ ext_drv->config.table_init_on = init_on_table;
+ ext_drv->config.table_init_off = init_off_table;
+ }
+ ext_drv->power_on = lcd_extern_power_on;
+ ext_drv->power_off = lcd_extern_power_off;
+
+ return 0;
+}
+
+int aml_lcd_extern_spi_LD070WS2_probe(struct aml_lcd_extern_driver_s *ext_drv)
+{
+ int ret = 0;
+
+ ext_config = &ext_drv->config;
+ ret = lcd_extern_driver_update(ext_drv);
+
+ if (lcd_debug_print_flag)
+ EXTPR("%s: %d\n", __func__, ret);
+ return ret;
+}
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_notify.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+
+static BLOCKING_NOTIFIER_HEAD(lcd_notifier_list);
+
+/**
+ * aml_lcd_notifier_register - register a client notifier
+ * @nb: notifier block to callback on events
+ */
+int aml_lcd_notifier_register(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&lcd_notifier_list, nb);
+}
+EXPORT_SYMBOL(aml_lcd_notifier_register);
+
+/**
+ * aml_lcd_notifier_unregister - unregister a client notifier
+ * @nb: notifier block to callback on events
+ */
+int aml_lcd_notifier_unregister(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&lcd_notifier_list, nb);
+}
+EXPORT_SYMBOL(aml_lcd_notifier_unregister);
+
+/**
+ * aml_lcd_notifier_call_chain - notify clients of lcd events
+ *
+ */
+int aml_lcd_notifier_call_chain(unsigned long event, void *v)
+{
+ return blocking_notifier_call_chain(&lcd_notifier_list, event, v);
+}
+EXPORT_SYMBOL_GPL(aml_lcd_notifier_call_chain);
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_reg.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/amlogic/cpu_version.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include "lcd_common.h"
+#include "lcd_reg.h"
+
+/* ********************************************* */
+#define LCD_REG_GLOBAL_API 0
+#define LCD_REG_IOREMAP 1
+#define LCD_REG_IF LCD_REG_IOREMAP
+/* ********************************************* */
+
+#if (LCD_REG_IF == LCD_REG_IOREMAP)
+struct reg_map_s {
+ unsigned int base_addr;
+ unsigned int size;
+ void __iomem *p;
+ char flag;
+ char dummy;
+};
+static struct reg_map_s *lcd_map;
+static int lcd_map_num;
+
+#define LCD_MAP_HIUBUS 0
+#define LCD_MAP_VCBUS 1
+#define LCD_MAP_PERIPHS 2
+#define LCD_MAP_CBUS 3
+#define LCD_MAP_DSI_HOST 4
+#define LCD_MAP_DSI_PHY 5
+
+static struct reg_map_s lcd_reg_maps_gxb[] = {
+ { /* HIU */
+ .base_addr = 0xc883c000,
+ .size = 0x400,
+ .flag = 0,
+ .dummy = 0,
+ },
+ { /* VCBUS */
+ .base_addr = 0xd0100000,
+ .size = 0x10000,
+ .flag = 0,
+ .dummy = 0,
+ },
+ { /* PERIPHS */
+ .base_addr = 0xc8834400,
+ .size = 0x100,
+ .flag = 0,
+ .dummy = 0,
+ },
+ { /* CBUS */
+ .base_addr = 0xc1100000,
+ .size = 0x8000,
+ .flag = 0,
+ .dummy = 0,
+ },
+};
+
+static struct reg_map_s lcd_reg_maps_txlx[] = {
+ { /* HIU */
+ .base_addr = 0xff63c000,
+ .size = 0x400,
+ .flag = 0,
+ .dummy = 0,
+ },
+ { /* VCBUS */
+ .base_addr = 0xff900000,
+ .size = 0xa000,
+ .flag = 0,
+ .dummy = 0,
+ },
+ { /* PERIPHS */
+ .base_addr = 0xff634000,
+ .size = 0x100,
+ .flag = 0,
+ .dummy = 0,
+ },
+};
+
+static struct reg_map_s lcd_reg_maps_axg[] = {
+ { /* HIU */
+ .base_addr = 0xff63c000,
+ .size = 0x400,
+ .flag = 0,
+ .dummy = 0,
+ },
+ { /* VCBUS */
+ .base_addr = 0xff900000,
+ .size = 0xa000,
+ .flag = 0,
+ .dummy = 0,
+ },
+ { /* PERIPHS */
+ .base_addr = 0xff634000,
+ .size = 0x100,
+ .flag = 0,
+ .dummy = 0,
+ },
+ { /* CBUS, dummy */
+ .base_addr = 0xffd00000,
+ .size = 0x10,
+ .flag = 0,
+ .dummy = 1,
+ },
+ { /* mipi_dsi_host */
+ .base_addr = 0xffd00000, /* 0xffd06000 */
+ .size = 0x6400,
+ .flag = 0,
+ .dummy = 0,
+ },
+ { /* mipi_dsi_phy */
+ .base_addr = 0xff640000,
+ .size = 0x100,
+ .flag = 0,
+ .dummy = 0,
+ },
+};
+
+int lcd_ioremap(void)
+{
+ int i;
+ int ret = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ lcd_map = NULL;
+ lcd_map_num = 0;
+
+ switch (lcd_drv->chip_type) {
+ case LCD_CHIP_TXLX:
+ lcd_map = lcd_reg_maps_txlx;
+ lcd_map_num = ARRAY_SIZE(lcd_reg_maps_txlx);
+ break;
+ case LCD_CHIP_AXG:
+ lcd_map = lcd_reg_maps_axg;
+ lcd_map_num = ARRAY_SIZE(lcd_reg_maps_axg);
+ break;
+ default:
+ lcd_map = lcd_reg_maps_gxb;
+ lcd_map_num = ARRAY_SIZE(lcd_reg_maps_gxb);
+ break;
+ }
+
+ for (i = 0; i < lcd_map_num; i++) {
+ if (lcd_map[i].dummy)
+ continue;
+ lcd_map[i].p = ioremap(lcd_map[i].base_addr,
+ lcd_map[i].size);
+ if (lcd_map[i].p == NULL) {
+ lcd_map[i].flag = 0;
+ LCDPR("reg map failed: 0x%x\n",
+ lcd_map[i].base_addr);
+ ret = -1;
+ } else {
+ lcd_map[i].flag = 1;
+ if (lcd_debug_print_flag) {
+ LCDPR("reg mapped: 0x%x -> %p\n",
+ lcd_map[i].base_addr, lcd_map[i].p);
+ }
+ }
+ }
+ return ret;
+}
+
+static int check_lcd_ioremap(int n)
+{
+ if (lcd_map == NULL)
+ return -1;
+ if (n >= lcd_map_num)
+ return -1;
+
+ if (lcd_map[n].dummy) {
+ LCDERR("reg 0x%x is invalid(dummy)\n", lcd_map[n].base_addr);
+ return -1;
+ }
+ if (lcd_map[n].flag == 0) {
+ LCDERR("reg 0x%x mapped error\n", lcd_map[n].base_addr);
+ return -1;
+ }
+ return 0;
+}
+#else
+int lcd_ioremap(void)
+{
+ LCDPR("reg interface is global api\n");
+ return 0;
+}
+#endif
+
+/* register mapping check */
+#if (LCD_REG_IF == LCD_REG_IOREMAP)
+static inline void __iomem *check_lcd_vcbus_reg(unsigned int _reg)
+{
+ void __iomem *p;
+ int reg_bus;
+ unsigned int reg_offset;
+
+ reg_bus = LCD_MAP_VCBUS;
+ if (check_lcd_ioremap(reg_bus))
+ return NULL;
+
+ reg_offset = LCD_REG_OFFSET_VCBUS(_reg);
+ if (reg_offset >= lcd_map[reg_bus].size) {
+ LCDERR("invalid vcbus reg offset: 0x%04x\n", _reg);
+ return NULL;
+ }
+ p = lcd_map[reg_bus].p + reg_offset;
+ return p;
+}
+
+static inline void __iomem *check_lcd_hiu_reg(unsigned int _reg)
+{
+ void __iomem *p;
+ int reg_bus;
+ unsigned int reg_offset;
+
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB)
+ reg_bus = LCD_MAP_HIUBUS;
+ else
+ reg_bus = LCD_MAP_CBUS;
+ if (check_lcd_ioremap(reg_bus))
+ return NULL;
+
+ if (reg_bus == LCD_MAP_HIUBUS)
+ reg_offset = LCD_REG_OFFSET_HIU(_reg);
+ else
+ reg_offset = LCD_REG_OFFSET_CBUS(_reg);
+ if (reg_offset >= lcd_map[reg_bus].size) {
+ LCDERR("invalid hiu reg offset: 0x%04x\n", _reg);
+ return NULL;
+ }
+ p = lcd_map[reg_bus].p + reg_offset;
+
+ return p;
+}
+
+static inline void __iomem *check_lcd_cbus_reg(unsigned int _reg)
+{
+ void __iomem *p;
+ int reg_bus;
+ unsigned int reg_offset;
+
+ reg_bus = LCD_MAP_CBUS;
+ if (check_lcd_ioremap(reg_bus))
+ return NULL;
+
+ reg_offset = LCD_REG_OFFSET_CBUS(_reg);
+ if (reg_offset >= lcd_map[reg_bus].size) {
+ LCDERR("invalid cbus reg offset: 0x%04x\n", _reg);
+ return NULL;
+ }
+ p = lcd_map[reg_bus].p + reg_offset;
+ return p;
+}
+
+static inline void __iomem *check_lcd_periphs_reg(unsigned int _reg)
+{
+ void __iomem *p;
+ int reg_bus;
+ unsigned int reg_offset;
+
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB)
+ reg_bus = LCD_MAP_PERIPHS;
+ else
+ reg_bus = LCD_MAP_CBUS;
+ if (check_lcd_ioremap(reg_bus))
+ return NULL;
+
+ if (reg_bus == LCD_MAP_PERIPHS)
+ reg_offset = LCD_REG_OFFSET_PERIPHS(_reg);
+ else
+ reg_offset = LCD_REG_OFFSET_PERIPHS(_reg);
+ if (reg_offset >= lcd_map[reg_bus].size) {
+ LCDERR("invalid periphs reg offset: 0x%04x\n", _reg);
+ return NULL;
+ }
+ p = lcd_map[reg_bus].p + reg_offset;
+ return p;
+}
+
+static inline void __iomem *check_lcd_dsi_host_reg(unsigned int _reg)
+{
+ void __iomem *p;
+ int reg_bus;
+ unsigned int reg_offset;
+
+ reg_bus = LCD_MAP_DSI_HOST;
+ if (check_lcd_ioremap(reg_bus))
+ return NULL;
+
+ reg_offset = LCD_REG_OFFSET_DSI_HOST(_reg);
+ if (reg_offset >= lcd_map[reg_bus].size) {
+ LCDERR("invalid dsi_host reg offset: 0x%04x\n", _reg);
+ return NULL;
+ }
+ p = lcd_map[reg_bus].p + reg_offset;
+ return p;
+}
+
+static inline void __iomem *check_lcd_dsi_phy_reg(unsigned int _reg)
+{
+ void __iomem *p;
+ int reg_bus;
+ unsigned int reg_offset;
+
+ reg_bus = LCD_MAP_DSI_PHY;
+ if (check_lcd_ioremap(reg_bus))
+ return NULL;
+
+ reg_offset = LCD_REG_OFFSET_DSI_PHY(_reg);
+ if (reg_offset >= lcd_map[reg_bus].size) {
+ LCDERR("invalid dsi_phy reg offset: 0x%04x\n", _reg);
+ return NULL;
+ }
+ p = lcd_map[reg_bus].p + reg_offset;
+ return p;
+}
+#endif
+
+/* register access api */
+#if (LCD_REG_IF == LCD_REG_IOREMAP)
+unsigned int lcd_vcbus_read(unsigned int _reg)
+{
+ void __iomem *p;
+
+ p = check_lcd_vcbus_reg(_reg);
+ if (p)
+ return readl(p);
+ else
+ return -1;
+};
+
+void lcd_vcbus_write(unsigned int _reg, unsigned int _value)
+{
+ void __iomem *p;
+
+ p = check_lcd_vcbus_reg(_reg);
+ if (p)
+ writel(_value, p);
+};
+#else
+unsigned int lcd_vcbus_read(unsigned int reg)
+{
+ return aml_read_vcbus(reg);
+};
+
+void lcd_vcbus_write(unsigned int reg, unsigned int value)
+{
+ aml_write_vcbus(reg, value);
+};
+#endif
+
+void lcd_vcbus_setb(unsigned int reg, unsigned int value,
+ unsigned int _start, unsigned int _len)
+{
+ lcd_vcbus_write(reg, ((lcd_vcbus_read(reg) &
+ (~(((1L << _len)-1) << _start))) |
+ ((value & ((1L << _len)-1)) << _start)));
+}
+
+unsigned int lcd_vcbus_getb(unsigned int reg,
+ unsigned int _start, unsigned int _len)
+{
+ return (lcd_vcbus_read(reg) >> _start) & ((1L << _len)-1);
+}
+
+void lcd_vcbus_set_mask(unsigned int reg, unsigned int _mask)
+{
+ lcd_vcbus_write(reg, (lcd_vcbus_read(reg) | (_mask)));
+}
+
+void lcd_vcbus_clr_mask(unsigned int reg, unsigned int _mask)
+{
+ lcd_vcbus_write(reg, (lcd_vcbus_read(reg) & (~(_mask))));
+}
+
+#if (LCD_REG_IF == LCD_REG_IOREMAP)
+unsigned int lcd_hiu_read(unsigned int _reg)
+{
+ void __iomem *p;
+
+ p = check_lcd_hiu_reg(_reg);
+ if (p)
+ return readl(p);
+ else
+ return -1;
+};
+
+void lcd_hiu_write(unsigned int _reg, unsigned int _value)
+{
+ void __iomem *p;
+
+ p = check_lcd_hiu_reg(_reg);
+ if (p)
+ writel(_value, p);
+};
+#else
+unsigned int lcd_hiu_read(unsigned int _reg)
+{
+ return aml_read_cbus(_reg);
+};
+
+void lcd_hiu_write(unsigned int _reg, unsigned int _value)
+{
+ aml_write_cbus(_reg, _value);
+};
+#endif
+
+void lcd_hiu_setb(unsigned int _reg, unsigned int _value,
+ unsigned int _start, unsigned int _len)
+{
+ lcd_hiu_write(_reg, ((lcd_hiu_read(_reg) &
+ ~(((1L << (_len))-1) << (_start))) |
+ (((_value)&((1L<<(_len))-1)) << (_start))));
+}
+
+unsigned int lcd_hiu_getb(unsigned int _reg,
+ unsigned int _start, unsigned int _len)
+{
+ return (lcd_hiu_read(_reg) >> (_start)) & ((1L << (_len)) - 1);
+}
+
+void lcd_hiu_set_mask(unsigned int _reg, unsigned int _mask)
+{
+ lcd_hiu_write(_reg, (lcd_hiu_read(_reg) | (_mask)));
+}
+
+void lcd_hiu_clr_mask(unsigned int _reg, unsigned int _mask)
+{
+ lcd_hiu_write(_reg, (lcd_hiu_read(_reg) & (~(_mask))));
+}
+
+#if (LCD_REG_IF == LCD_REG_IOREMAP)
+unsigned int lcd_cbus_read(unsigned int _reg)
+{
+ void __iomem *p;
+
+ p = check_lcd_cbus_reg(_reg);
+ if (p)
+ return readl(p);
+ else
+ return -1;
+};
+
+void lcd_cbus_write(unsigned int _reg, unsigned int _value)
+{
+ void __iomem *p;
+
+ p = check_lcd_cbus_reg(_reg);
+ if (p)
+ writel(_value, p);
+};
+#else
+unsigned int lcd_cbus_read(unsigned int _reg)
+{
+ return aml_read_cbus(_reg);
+};
+
+void lcd_cbus_write(unsigned int _reg, unsigned int _value)
+{
+ aml_write_cbus(_reg, _value);
+};
+#endif
+
+void lcd_cbus_setb(unsigned int _reg, unsigned int _value,
+ unsigned int _start, unsigned int _len)
+{
+ lcd_cbus_write(_reg, ((lcd_cbus_read(_reg) &
+ ~(((1L << (_len))-1) << (_start))) |
+ (((_value)&((1L<<(_len))-1)) << (_start))));
+}
+
+#if (LCD_REG_IF == LCD_REG_IOREMAP)
+unsigned int lcd_periphs_read(unsigned int _reg)
+{
+ void __iomem *p;
+
+ p = check_lcd_periphs_reg(_reg);
+ if (p)
+ return readl(p);
+ else
+ return -1;
+};
+
+void lcd_periphs_write(unsigned int _reg, unsigned int _value)
+{
+ void __iomem *p;
+
+ p = check_lcd_periphs_reg(_reg);
+ if (p)
+ writel(_value, p);
+};
+#else
+unsigned int lcd_periphs_read(unsigned int _reg)
+{
+ return aml_read_cbus(_reg);
+};
+
+void lcd_periphs_write(unsigned int _reg, unsigned int _value)
+{
+ aml_write_cbus(_reg, _value);
+};
+#endif
+
+void lcd_pinmux_set_mask(unsigned int n, unsigned int _mask)
+{
+ unsigned int _reg = PERIPHS_PIN_MUX_0;
+
+ _reg += n;
+ lcd_periphs_write(_reg, (lcd_periphs_read(_reg) | (_mask)));
+}
+
+void lcd_pinmux_clr_mask(unsigned int n, unsigned int _mask)
+{
+ unsigned int _reg = PERIPHS_PIN_MUX_0;
+
+ _reg += n;
+ lcd_periphs_write(_reg, (lcd_periphs_read(_reg) & (~(_mask))));
+}
+
+#if (LCD_REG_IF == LCD_REG_IOREMAP)
+unsigned int dsi_host_read(unsigned int _reg)
+{
+ void __iomem *p;
+
+ p = check_lcd_dsi_host_reg(_reg);
+ if (p)
+ return readl(p);
+ else
+ return -1;
+};
+
+void dsi_host_write(unsigned int _reg, unsigned int _value)
+{
+ void __iomem *p;
+
+ p = check_lcd_dsi_host_reg(_reg);
+ if (p)
+ writel(_value, p);
+};
+
+void dsi_host_setb(unsigned int reg, unsigned int value,
+ unsigned int _start, unsigned int _len)
+{
+ dsi_host_write(reg, ((dsi_host_read(reg) &
+ (~(((1L << _len)-1) << _start))) |
+ ((value & ((1L << _len)-1)) << _start)));
+}
+
+unsigned int dsi_host_getb(unsigned int reg,
+ unsigned int _start, unsigned int _len)
+{
+ return (dsi_host_read(reg) >> _start) & ((1L << _len)-1);
+}
+
+void dsi_host_set_mask(unsigned int reg, unsigned int _mask)
+{
+ dsi_host_write(reg, (dsi_host_read(reg) | (_mask)));
+}
+
+void dsi_host_clr_mask(unsigned int reg, unsigned int _mask)
+{
+ dsi_host_write(reg, (dsi_host_read(reg) & (~(_mask))));
+}
+
+
+unsigned int dsi_phy_read(unsigned int _reg)
+{
+ void __iomem *p;
+
+ p = check_lcd_dsi_phy_reg(_reg);
+ if (p)
+ return readl(p);
+ else
+ return -1;
+};
+
+void dsi_phy_write(unsigned int _reg, unsigned int _value)
+{
+ void __iomem *p;
+
+ p = check_lcd_dsi_phy_reg(_reg);
+ if (p)
+ writel(_value, p);
+};
+
+void dsi_phy_setb(unsigned int reg, unsigned int value,
+ unsigned int _start, unsigned int _len)
+{
+ dsi_phy_write(reg, ((dsi_phy_read(reg) &
+ (~(((1L << _len)-1) << _start))) |
+ ((value & ((1L << _len)-1)) << _start)));
+}
+
+unsigned int dsi_phy_getb(unsigned int reg,
+ unsigned int _start, unsigned int _len)
+{
+ return (dsi_phy_read(reg) >> _start) & ((1L << _len)-1);
+}
+
+void dsi_phy_set_mask(unsigned int reg, unsigned int _mask)
+{
+ dsi_phy_write(reg, (dsi_phy_read(reg) | (_mask)));
+}
+
+void dsi_phy_clr_mask(unsigned int reg, unsigned int _mask)
+{
+ dsi_phy_write(reg, (dsi_phy_read(reg) & (~(_mask))));
+}
+#endif
+
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_reg.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __LCD_REG_H__
+#define __LCD_REG_H__
+#include <linux/amlogic/iomap.h>
+
+/* register offset address define */
+/* base & offset */
+#if 0
+#define LCD_REG_BASE_PERIPHS (0xc8834400L)
+#define LCD_REG_BASE_CBUS (0xc1100000L)
+#define LCD_REG_BASE_HIU (0xc883c000L)
+#define LCD_REG_BASE_VCBUS (0xd0100000L)
+#endif
+#define LCD_HIU_REG(reg) (reg & 0xff)
+#define LCD_PERIPHS_REG(reg) (reg & 0xff)
+
+#define LCD_REG_OFFSET_PERIPHS(reg) ((LCD_PERIPHS_REG(reg) << 2))
+#define LCD_REG_OFFSET_CBUS(reg) ((reg << 2))
+#define LCD_REG_OFFSET_HIU(reg) ((LCD_HIU_REG(reg) << 2))
+#define LCD_REG_OFFSET_VCBUS(reg) ((reg << 2))
+#define LCD_REG_OFFSET_DSI_HOST(reg) ((reg << 2))
+#define LCD_REG_OFFSET_DSI_PHY(reg) ((reg << 2))
+
+
+/* PERIPHS: 0xc8834400 */
+#define PREG_PAD_GPIO1_EN_N 0x0f
+#define PREG_PAD_GPIO1_O 0x10
+#define PREG_PAD_GPIO1_I 0x11
+#define PREG_PAD_GPIO2_EN_N 0x12
+#define PREG_PAD_GPIO2_O 0x13
+#define PREG_PAD_GPIO2_I 0x14
+#define PREG_PAD_GPIO3_EN_N 0x15
+#define PREG_PAD_GPIO3_O 0x16
+#define PREG_PAD_GPIO3_I 0x17
+#define PREG_PAD_GPIO4_EN_N 0x18
+#define PREG_PAD_GPIO4_O 0x19
+#define PREG_PAD_GPIO4_I 0x1a
+#define PREG_PAD_GPIO5_EN_N 0x1b
+#define PREG_PAD_GPIO5_O 0x1c
+#define PREG_PAD_GPIO5_I 0x1d
+
+#define PERIPHS_PIN_MUX_0 0x2c
+#define PERIPHS_PIN_MUX_1 0x2d
+#define PERIPHS_PIN_MUX_2 0x2e
+#define PERIPHS_PIN_MUX_3 0x2f
+#define PERIPHS_PIN_MUX_4 0x30
+#define PERIPHS_PIN_MUX_5 0x31
+#define PERIPHS_PIN_MUX_6 0x32
+#define PERIPHS_PIN_MUX_7 0x33
+#define PERIPHS_PIN_MUX_8 0x34
+#define PERIPHS_PIN_MUX_9 0x35
+#define PERIPHS_PIN_MUX_10 0x36
+#define PERIPHS_PIN_MUX_11 0x37
+#define PERIPHS_PIN_MUX_12 0x38
+
+
+/* HIU: HHI_CBUS_BASE = 0x10 */
+#define HHI_GCLK_MPEG0 0x1050
+#define HHI_GCLK_MPEG1 0x1051
+#define HHI_GCLK_MPEG2 0x1052
+#define HHI_GCLK_OTHER 0x1054
+
+#define HHI_VIID_PLL_CNTL4 0x1046
+#define HHI_VIID_PLL_CNTL 0x1047
+#define HHI_VIID_PLL_CNTL2 0x1048
+#define HHI_VIID_PLL_CNTL3 0x1049
+#define HHI_VIID_CLK_DIV 0x104a
+ #define DAC0_CLK_SEL 28
+ #define DAC1_CLK_SEL 24
+ #define DAC2_CLK_SEL 20
+ #define VCLK2_XD_RST 17
+ #define VCLK2_XD_EN 16
+ #define ENCL_CLK_SEL 12
+ #define VCLK2_XD 0
+#define HHI_VIID_CLK_CNTL 0x104b
+ #define VCLK2_EN 19
+ #define VCLK2_CLK_IN_SEL 16
+ #define VCLK2_SOFT_RST 15
+ #define VCLK2_DIV12_EN 4
+ #define VCLK2_DIV6_EN 3
+ #define VCLK2_DIV4_EN 2
+ #define VCLK2_DIV2_EN 1
+ #define VCLK2_DIV1_EN 0
+#define HHI_VIID_DIVIDER_CNTL 0x104c
+ #define DIV_CLK_IN_EN 16
+ #define DIV_CLK_SEL 15
+ #define DIV_POST_TCNT 12
+ #define DIV_LVDS_CLK_EN 11
+ #define DIV_LVDS_DIV2 10
+ #define DIV_POST_SEL 8
+ #define DIV_POST_SOFT_RST 7
+ #define DIV_PRE_SEL 4
+ #define DIV_PRE_SOFT_RST 3
+ #define DIV_POST_RST 1
+ #define DIV_PRE_RST 0
+#define HHI_VID_CLK_DIV 0x1059
+ #define ENCI_CLK_SEL 28
+ #define ENCP_CLK_SEL 24
+ #define ENCT_CLK_SEL 20
+ #define VCLK_XD_RST 17
+ #define VCLK_XD_EN 16
+ #define ENCL_CLK_SEL 12
+ #define VCLK_XD1 8
+ #define VCLK_XD0 0
+#define HHI_VID_CLK_CNTL 0x105f
+#define HHI_VID_CLK_CNTL2 0x1065
+ #define HDMI_TX_PIXEL_GATE_VCLK 5
+ #define VDAC_GATE_VCLK 4
+ #define ENCL_GATE_VCLK 3
+ #define ENCP_GATE_VCLK 2
+ #define ENCT_GATE_VCLK 1
+ #define ENCI_GATE_VCLK 0
+#define HHI_VID_DIVIDER_CNTL 0x1066
+#define HHI_VID_PLL_CLK_DIV 0x1068
+#define HHI_EDP_APB_CLK_CNTL 0x107b
+#define HHI_EDP_APB_CLK_CNTL_M8M2 0x1082
+#define HHI_EDP_TX_PHY_CNTL0 0x109c
+#define HHI_EDP_TX_PHY_CNTL1 0x109d
+/* m8b */
+#define HHI_VID_PLL_CNTL 0x10c8
+#define HHI_VID_PLL_CNTL2 0x10c9
+#define HHI_VID_PLL_CNTL3 0x10ca
+#define HHI_VID_PLL_CNTL4 0x10cb
+#define HHI_VID_PLL_CNTL5 0x10cc
+#define HHI_VID_PLL_CNTL6 0x10cd
+/* g9tv */
+#define HHI_HDMI_PLL_CNTL 0x10c8
+#define HHI_HDMI_PLL_CNTL2 0x10c9
+#define HHI_HDMI_PLL_CNTL3 0x10ca
+#define HHI_HDMI_PLL_CNTL4 0x10cb
+#define HHI_HDMI_PLL_CNTL5 0x10cc
+#define HHI_HDMI_PLL_CNTL6 0x10cd
+
+#define HHI_DSI_LVDS_EDP_CNTL0 0x10d1
+#define HHI_DSI_LVDS_EDP_CNTL1 0x10d2
+#define HHI_DIF_CSI_PHY_CNTL0 0x10d8
+#define HHI_DIF_CSI_PHY_CNTL1 0x10d9
+#define HHI_DIF_CSI_PHY_CNTL2 0x10da
+#define HHI_DIF_CSI_PHY_CNTL3 0x10db
+#define HHI_DIF_CSI_PHY_CNTL4 0x10dc
+#define HHI_DIF_CSI_PHY_CNTL5 0x10dd
+#define HHI_LVDS_TX_PHY_CNTL0 0x10de
+#define HHI_LVDS_TX_PHY_CNTL1 0x10df
+#define HHI_VID2_PLL_CNTL 0x10e0
+#define HHI_VID2_PLL_CNTL2 0x10e1
+#define HHI_VID2_PLL_CNTL3 0x10e2
+#define HHI_VID2_PLL_CNTL4 0x10e3
+#define HHI_VID2_PLL_CNTL5 0x10e4
+#define HHI_VID2_PLL_CNTL6 0x10e5
+#define HHI_VID_LOCK_CLK_CNTL 0x10f2
+
+/* AXG use PLL 0xff63c000 */
+#define HHI_GP0_PLL_CNTL 0x010
+#define HHI_GP0_PLL_CNTL2 0x011
+#define HHI_GP0_PLL_CNTL3 0x012
+#define HHI_GP0_PLL_CNTL4 0x013
+#define HHI_GP0_PLL_CNTL5 0x014
+#define HHI_GP0_PLL_CNTL1 0x016
+
+#define HHI_MIPI_CNTL0 0x000
+#define HHI_MIPI_CNTL1 0x001
+#define HHI_MIPI_CNTL2 0x002
+
+/* Global control: RESET_CBUS_BASE = 0x11 */
+#define VERSION_CTRL 0x1100
+#define RESET0_REGISTER 0x1101
+#define RESET1_REGISTER 0x1102
+#define RESET2_REGISTER 0x1103
+#define RESET3_REGISTER 0x1104
+#define RESET4_REGISTER 0x1105
+#define RESET5_REGISTER 0x1106
+#define RESET6_REGISTER 0x1107
+#define RESET7_REGISTER 0x1108
+#define RESET0_MASK 0x1110
+#define RESET1_MASK 0x1111
+#define RESET2_MASK 0x1112
+#define RESET3_MASK 0x1113
+#define RESET4_MASK 0x1114
+#define RESET5_MASK 0x1115
+#define RESET6_MASK 0x1116
+#define CRT_MASK 0x1117
+#define RESET7_MASK 0x1118
+
+/* ********************************
+ * TCON: VCBUS_BASE = 0x14
+ */
+/* TCON_L register */
+#define L_GAMMA_CNTL_PORT 0x1400
+#define L_GAMMA_DATA_PORT 0x1401
+#define L_GAMMA_ADDR_PORT 0x1402
+#define L_GAMMA_VCOM_HSWITCH_ADDR 0x1403
+#define L_RGB_BASE_ADDR 0x1405
+#define L_RGB_COEFF_ADDR 0x1406
+#define L_POL_CNTL_ADDR 0x1407
+#define L_DITH_CNTL_ADDR 0x1408
+#define L_GAMMA_PROBE_CTRL 0x1409
+/* read only */
+#define L_GAMMA_PROBE_COLOR_L 0x140a
+#define L_GAMMA_PROBE_COLOR_H 0x140b
+#define L_GAMMA_PROBE_HL_COLOR 0x140c
+#define L_GAMMA_PROBE_POS_X 0x140d
+#define L_GAMMA_PROBE_POS_Y 0x140e
+#define L_STH1_HS_ADDR 0x1410
+#define L_STH1_HE_ADDR 0x1411
+#define L_STH1_VS_ADDR 0x1412
+#define L_STH1_VE_ADDR 0x1413
+#define L_STH2_HS_ADDR 0x1414
+#define L_STH2_HE_ADDR 0x1415
+#define L_STH2_VS_ADDR 0x1416
+#define L_STH2_VE_ADDR 0x1417
+#define L_OEH_HS_ADDR 0x1418
+#define L_OEH_HE_ADDR 0x1419
+#define L_OEH_VS_ADDR 0x141a
+#define L_OEH_VE_ADDR 0x141b
+#define L_VCOM_HSWITCH_ADDR 0x141c
+#define L_VCOM_VS_ADDR 0x141d
+#define L_VCOM_VE_ADDR 0x141e
+#define L_CPV1_HS_ADDR 0x141f
+#define L_CPV1_HE_ADDR 0x1420
+#define L_CPV1_VS_ADDR 0x1421
+#define L_CPV1_VE_ADDR 0x1422
+#define L_CPV2_HS_ADDR 0x1423
+#define L_CPV2_HE_ADDR 0x1424
+#define L_CPV2_VS_ADDR 0x1425
+#define L_CPV2_VE_ADDR 0x1426
+#define L_STV1_HS_ADDR 0x1427
+#define L_STV1_HE_ADDR 0x1428
+#define L_STV1_VS_ADDR 0x1429
+#define L_STV1_VE_ADDR 0x142a
+#define L_STV2_HS_ADDR 0x142b
+#define L_STV2_HE_ADDR 0x142c
+#define L_STV2_VS_ADDR 0x142d
+#define L_STV2_VE_ADDR 0x142e
+#define L_OEV1_HS_ADDR 0x142f
+#define L_OEV1_HE_ADDR 0x1430
+#define L_OEV1_VS_ADDR 0x1431
+#define L_OEV1_VE_ADDR 0x1432
+#define L_OEV2_HS_ADDR 0x1433
+#define L_OEV2_HE_ADDR 0x1434
+#define L_OEV2_VS_ADDR 0x1435
+#define L_OEV2_VE_ADDR 0x1436
+#define L_OEV3_HS_ADDR 0x1437
+#define L_OEV3_HE_ADDR 0x1438
+#define L_OEV3_VS_ADDR 0x1439
+#define L_OEV3_VE_ADDR 0x143a
+#define L_LCD_PWR_ADDR 0x143b
+#define L_LCD_PWM0_LO_ADDR 0x143c
+#define L_LCD_PWM0_HI_ADDR 0x143d
+#define L_LCD_PWM1_LO_ADDR 0x143e
+#define L_LCD_PWM1_HI_ADDR 0x143f
+#define L_INV_CNT_ADDR 0x1440
+#define L_TCON_MISC_SEL_ADDR 0x1441
+#define L_DUAL_PORT_CNTL_ADDR 0x1442
+#define MLVDS_CLK_CTL1_HI 0x1443
+#define MLVDS_CLK_CTL1_LO 0x1444
+/* [31:30] enable mlvds clocks
+ * [24] mlvds_clk_half_delay 24 // Bit 0
+ * [23:0] mlvds_clk_pattern 0 // Bit 23:0
+ */
+#define L_TCON_DOUBLE_CTL 0x1449
+#define L_TCON_PATTERN_HI 0x144a
+#define L_TCON_PATTERN_LO 0x144b
+#define LDIM_BL_ADDR_PORT 0x144e
+#define LDIM_BL_DATA_PORT 0x144f
+#define L_DE_HS_ADDR 0x1451
+#define L_DE_HE_ADDR 0x1452
+#define L_DE_VS_ADDR 0x1453
+#define L_DE_VE_ADDR 0x1454
+#define L_HSYNC_HS_ADDR 0x1455
+#define L_HSYNC_HE_ADDR 0x1456
+#define L_HSYNC_VS_ADDR 0x1457
+#define L_HSYNC_VE_ADDR 0x1458
+#define L_VSYNC_HS_ADDR 0x1459
+#define L_VSYNC_HE_ADDR 0x145a
+#define L_VSYNC_VS_ADDR 0x145b
+#define L_VSYNC_VE_ADDR 0x145c
+/* bit 8 -- vfifo_mcu_enable
+ * bit 7 -- halt_vs_de
+ * bit 6 -- R8G8B8_format
+ * bit 5 -- R6G6B6_format (round to 6 bits)
+ * bit 4 -- R5G6B5_format
+ * bit 3 -- dac_dith_sel
+ * bit 2 -- lcd_mcu_enable_de -- ReadOnly
+ * bit 1 -- lcd_mcu_enable_vsync -- ReadOnly
+ * bit 0 -- lcd_mcu_enable
+ */
+#define L_LCD_MCU_CTL 0x145d
+
+/* **************************************************
+ * Dual port mLVDS registers
+ */
+/* bit 3 - enable_u_dual_mlvds_dp_clk
+ * bit 2 - enable_u_map_mlvds_r_clk
+ * bit 1 - enable_u_map_mlvds_l_clk
+ * bit 0 - dual_mlvds_en
+ */
+#define DUAL_MLVDS_CTL 0x1460
+/* bit[12:0] - dual_mlvds_line_start */
+#define DUAL_MLVDS_LINE_START 0x1461
+/* bit[12:0] - dual_mlvds_line_end */
+#define DUAL_MLVDS_LINE_END 0x1462
+/* bit[12:0] - dual_mlvds_w_pixel_start_l */
+#define DUAL_MLVDS_PIXEL_W_START_L 0x1463
+/* bit[12:0] - dual_mlvds_w_pixel_end_l */
+#define DUAL_MLVDS_PIXEL_W_END_L 0x1464
+/* bit[12:0] - dual_mlvds_w_pixel_start_r */
+#define DUAL_MLVDS_PIXEL_W_START_R 0x1465
+/* bit[12:0] - dual_mlvds_w_pixel_end_r */
+#define DUAL_MLVDS_PIXEL_W_END_R 0x1466
+/* bit[12:0] - dual_mlvds_r_pixel_start_l */
+#define DUAL_MLVDS_PIXEL_R_START_L 0x1467
+/* bit[12:0] - dual_mlvds_r_pixel_cnt_l */
+#define DUAL_MLVDS_PIXEL_R_CNT_L 0x1468
+/* bit[12:0] - dual_mlvds_r_pixel_start_r */
+#define DUAL_MLVDS_PIXEL_R_START_R 0x1469
+/* bit[12:0] - dual_mlvds_r_pixel_cnt_r */
+#define DUAL_MLVDS_PIXEL_R_CNT_R 0x146a
+/* bit[15] - v_inversion_en
+ * bit[12:0] - v_inversion_pixel
+ */
+#define V_INVERSION_PIXEL 0x1470
+/* bit[15] - v_inversion_sync_en
+ * bit[12:0] - v_inversion_line
+ */
+#define V_INVERSION_LINE 0x1471
+/* bit[15:12] - v_loop_r
+ * bit[11:10] - v_pattern_1_r
+ * bit[9:8] - v_pattern_0_r
+ * bit[7:4] - v_loop_l
+ * bit[3:2] - v_pattern_1_l
+ * bit[1:0] - v_pattern_0_l
+ */
+#define V_INVERSION_CONTROL 0x1472
+#define MLVDS2_CONTROL 0x1474
+ #define mLVDS2_RESERVED 15
+ #define mLVDS2_double_pattern 14
+ /* 13:8 // each channel has one bit */
+ #define mLVDS2_ins_reset 8
+ #define mLVDS2_dual_gate 7
+ /* 0=6Bits, 1=8Bits */
+ #define mLVDS2_bit_num 6
+ /* 0=3Pairs, 1=6Pairs */
+ #define mLVDS2_pair_num 5
+ #define mLVDS2_msb_first 4
+ #define mLVDS2_PORT_SWAP 3
+ #define mLVDS2_MLSB_SWAP 2
+ #define mLVDS2_PN_SWAP 1
+ #define mLVDS2_en 0
+#define MLVDS2_CONFIG_HI 0x1475
+#define MLVDS2_CONFIG_LO 0x1476
+ /* Bit 31:29 */
+ #define mLVDS2_reset_offset 29
+ /* Bit 28:23 */
+ #define mLVDS2_reset_length 23
+ /* Bit 22:20 */
+ #define mLVDS2_config_reserved 20
+ #define mLVDS2_reset_start_bit12 19
+ #define mLVDS2_data_write_toggle 18
+ #define mLVDS2_data_write_ini 17
+ #define mLVDS2_data_latch_1_toggle 16
+ #define mLVDS2_data_latch_1_ini 15
+ #define mLVDS2_data_latch_0_toggle 14
+ #define mLVDS2_data_latch_0_ini 13
+ /* 0=same as reset_0, 1=1 clock delay of reset_0 */
+ #define mLVDS2_reset_1_select 12
+ /* Bit 11:0 */
+ #define mLVDS2_reset_start 0
+#define MLVDS2_DUAL_GATE_WR_START 0x1477
+ /* Bit 12:0 */
+ #define mlvds2_dual_gate_wr_start 0
+#define MLVDS2_DUAL_GATE_WR_END 0x1478
+ /* Bit 12:0 */
+ #define mlvds2_dual_gate_wr_end 0
+#define MLVDS2_DUAL_GATE_RD_START 0x1479
+ /* Bit 12:0 */
+ #define mlvds2_dual_gate_rd_start 0
+#define MLVDS2_DUAL_GATE_RD_END 0x147a
+ /* Bit 12:0 */
+ #define mlvds2_dual_gate_rd_end 0
+#define MLVDS2_SECOND_RESET_CTL 0x147b
+ /* Bit 12:0 */
+ #define mLVDS2_2nd_reset_start 0
+#define MLVDS2_DUAL_GATE_CTL_HI 0x147c
+#define MLVDS2_DUAL_GATE_CTL_LO 0x147d
+ /* Bit 7:0 */
+ #define mlvds2_tcon_field_en 24
+ /* Bit 2:0 */
+ #define mlvds2_dual_gate_reserved 21
+ #define mlvds2_scan_mode_start_line_bit12 20
+ /* Bit 3:0 */
+ #define mlvds2_scan_mode_odd 16
+ /* Bit 3:0 */
+ #define mlvds2_scan_mode_even 12
+ /* Bit 11:0 */
+ #define mlvds2_scan_mode_start_line 0
+#define MLVDS2_RESET_CONFIG_HI 0x147e
+#define MLVDS2_RESET_CONFIG_LO 0x147f
+ #define mLVDS2_reset_range_enable 31
+ #define mLVDS2_reset_range_inv 30
+ #define mLVDS2_reset_config_res1 29
+ /* Bit 11:0 */
+ #define mLVDS2_reset_range_line_0 16
+ /* Bit 2:0 */
+ #define mLVDS2_reset_config_res3 13
+ /* Bit 11:0 */
+ #define mLVDS2_reset_range_line_1 0
+
+/* ************************************
+ * TCON register
+ */
+#define GAMMA_CNTL_PORT 0x1480
+ #define GAMMA_VCOM_POL 7
+ #define GAMMA_RVS_OUT 6
+ /* Read Only */
+ #define ADR_RDY 5
+ /* Read Only */
+ #define WR_RDY 4
+ /* Read Only */
+ #define RD_RDY 3
+ #define GAMMA_TR 2
+ #define GAMMA_SET 1
+ #define GAMMA_EN 0
+#define GAMMA_DATA_PORT 0x1481
+#define GAMMA_ADDR_PORT 0x1482
+ #define H_RD 12
+ #define H_AUTO_INC 11
+ #define H_SEL_R 10
+ #define H_SEL_G 9
+ #define H_SEL_B 8
+ /* 7:0 */
+ #define HADR_MSB 7
+ #define HADR 0
+#define GAMMA_VCOM_HSWITCH_ADDR 0x1483
+#define RGB_BASE_ADDR 0x1485
+#define RGB_COEFF_ADDR 0x1486
+#define POL_CNTL_ADDR 0x1487
+ /* FOR DCLK OUTPUT */
+ #define DCLK_SEL 14
+ /* FOR RGB format DVI output */
+ #define TCON_VSYNC_SEL_DVI 11
+ /* FOR RGB format DVI output */
+ #define TCON_HSYNC_SEL_DVI 10
+ /* FOR RGB format DVI output */
+ #define TCON_DE_SEL_DVI 9
+ #define CPH3_POL 8
+ #define CPH2_POL 7
+ #define CPH1_POL 6
+ #define TCON_DE_SEL 5
+ #define TCON_VS_SEL 4
+ #define TCON_HS_SEL 3
+ #define DE_POL 2
+ #define VS_POL 1
+ #define HS_POL 0
+#define DITH_CNTL_ADDR 0x1488
+ #define DITH10_EN 10
+ #define DITH8_EN 9
+ #define DITH_MD 8
+ /* 7:4 */
+ #define DITH10_CNTL_MSB 7
+ #define DITH10_CNTL 4
+ /* 3:0 */
+ #define DITH8_CNTL_MSB 3
+ #define DITH8_CNTL 0
+/* Bit 1 highlight_en
+ * Bit 0 probe_en
+ */
+#define GAMMA_PROBE_CTRL 0x1489
+/* read only
+ * Bit [15:0] probe_color[15:0]
+ */
+#define GAMMA_PROBE_COLOR_L 0x148a
+/* Read only
+ * Bit 15: if true valid probed color
+ * Bit [13:0] probe_color[29:16]
+ */
+#define GAMMA_PROBE_COLOR_H 0x148b
+/* bit 15:0, 5:6:5 color */
+#define GAMMA_PROBE_HL_COLOR 0x148c
+/* 12:0 pos_x */
+#define GAMMA_PROBE_POS_X 0x148d
+/* 12:0 pos_y */
+#define GAMMA_PROBE_POS_Y 0x148e
+#define STH1_HS_ADDR 0x1490
+#define STH1_HE_ADDR 0x1491
+#define STH1_VS_ADDR 0x1492
+#define STH1_VE_ADDR 0x1493
+#define STH2_HS_ADDR 0x1494
+#define STH2_HE_ADDR 0x1495
+#define STH2_VS_ADDR 0x1496
+#define STH2_VE_ADDR 0x1497
+#define OEH_HS_ADDR 0x1498
+#define OEH_HE_ADDR 0x1499
+#define OEH_VS_ADDR 0x149a
+#define OEH_VE_ADDR 0x149b
+#define VCOM_HSWITCH_ADDR 0x149c
+#define VCOM_VS_ADDR 0x149d
+#define VCOM_VE_ADDR 0x149e
+#define CPV1_HS_ADDR 0x149f
+#define CPV1_HE_ADDR 0x14a0
+#define CPV1_VS_ADDR 0x14a1
+#define CPV1_VE_ADDR 0x14a2
+#define CPV2_HS_ADDR 0x14a3
+#define CPV2_HE_ADDR 0x14a4
+#define CPV2_VS_ADDR 0x14a5
+#define CPV2_VE_ADDR 0x14a6
+#define STV1_HS_ADDR 0x14a7
+#define STV1_HE_ADDR 0x14a8
+#define STV1_VS_ADDR 0x14a9
+#define STV1_VE_ADDR 0x14aa
+#define STV2_HS_ADDR 0x14ab
+#define STV2_HE_ADDR 0x14ac
+#define STV2_VS_ADDR 0x14ad
+#define STV2_VE_ADDR 0x14ae
+#define OEV1_HS_ADDR 0x14af
+#define OEV1_HE_ADDR 0x14b0
+#define OEV1_VS_ADDR 0x14b1
+#define OEV1_VE_ADDR 0x14b2
+#define OEV2_HS_ADDR 0x14b3
+#define OEV2_HE_ADDR 0x14b4
+#define OEV2_VS_ADDR 0x14b5
+#define OEV2_VE_ADDR 0x14b6
+#define OEV3_HS_ADDR 0x14b7
+#define OEV3_HE_ADDR 0x14b8
+#define OEV3_VS_ADDR 0x14b9
+#define OEV3_VE_ADDR 0x14ba
+#define LCD_PWR_ADDR 0x14bb
+ #define LCD_VDD 5
+ #define LCD_VBL 4
+ #define LCD_GPI_MSB 3
+ #define LCD_GPIO 0
+#define LCD_PWM0_LO_ADDR 0x14bc
+#define LCD_PWM0_HI_ADDR 0x14bd
+#define LCD_PWM1_LO_ADDR 0x14be
+#define LCD_PWM1_HI_ADDR 0x14bf
+#define INV_CNT_ADDR 0x14c0
+ #define INV_EN 4
+ #define INV_CNT_MSB 3
+ #define INV_CNT 0
+#define TCON_MISC_SEL_ADDR 0x14c1
+ #define STH2_SEL 12
+ #define STH1_SEL 11
+ #define OEH_SEL 10
+ #define VCOM_SEL 9
+ #define DB_LINE_SW 8
+ #define CPV2_SEL 7
+ #define CPV1_SEL 6
+ #define STV2_SEL 5
+ #define STV1_SEL 4
+ #define OEV_UNITE 3
+ #define OEV3_SEL 2
+ #define OEV2_SEL 1
+ #define OEV1_SEL 0
+#define DUAL_PORT_CNTL_ADDR 0x14c2
+ #define OUTPUT_YUV 15
+ /* 14:12 */
+ #define DUAL_IDF 12
+ /* 11:9 */
+ #define DUAL_ISF 9
+ #define LCD_ANALOG_SEL_CPH3 8
+ #define LCD_ANALOG_3PHI_CLK_SEL 7
+ #define LCD_LVDS_SEL54 6
+ #define LCD_LVDS_SEL27 5
+ #define LCD_TTL_SEL 4
+ #define DUAL_LVDC_EN 3
+ #define PORT_SWP 2
+ #define RGB_SWP 1
+ #define BIT_SWP 0
+#define MLVDS_CONTROL 0x14c3
+ #define mLVDS_RESERVED 15
+ #define mLVDS_double_pattern 14
+ /* 13:8 // each channel has one bit */
+ #define mLVDS_ins_reset 8
+ #define mLVDS_dual_gate 7
+ /* 0=6Bits, 1=8Bits */
+ #define mLVDS_bit_num 6
+ /* 0=3Pairs, 1=6Pairs */
+ #define mLVDS_pair_num 5
+ #define mLVDS_msb_first 4
+ #define mLVDS_PORT_SWAP 3
+ #define mLVDS_MLSB_SWAP 2
+ #define mLVDS_PN_SWAP 1
+ #define mLVDS_en 0
+#define MLVDS_RESET_PATTERN_HI 0x14c4
+#define MLVDS_RESET_PATTERN_LO 0x14c5
+ /* Bit 47:16 */
+ #define mLVDS_reset_pattern 0
+#define MLVDS_RESET_PATTERN_EXT 0x14c6
+ /* Bit 15:0 */
+ #define mLVDS_reset_pattern_ext 0
+#define MLVDS_CONFIG_HI 0x14c7
+#define MLVDS_CONFIG_LO 0x14c8
+ /* Bit 31:29 */
+ #define mLVDS_reset_offset 29
+ /* Bit 28:23 */
+ #define mLVDS_reset_length 23
+ /* Bit 22:20 */
+ #define mLVDS_config_reserved 20
+ #define mLVDS_reset_start_bit12 19
+ #define mLVDS_data_write_toggle 18
+ #define mLVDS_data_write_ini 17
+ #define mLVDS_data_latch_1_toggle 16
+ #define mLVDS_data_latch_1_ini 15
+ #define mLVDS_data_latch_0_toggle 14
+ #define mLVDS_data_latch_0_ini 13
+ /* 0 - same as reset_0, 1 - 1 clock delay of reset_0 */
+ #define mLVDS_reset_1_select 12
+ /* Bit 11:0 */
+ #define mLVDS_reset_start 0
+#define TCON_DOUBLE_CTL 0x14c9
+ /* Bit 7:0 */
+ #define tcon_double_ini 8
+ /* Bit 7:0 */
+ #define tcon_double_inv 0
+#define TCON_PATTERN_HI 0x14ca
+#define TCON_PATTERN_LO 0x14cb
+ /* Bit 15:0 */
+ #define tcon_pattern_loop_data 16
+ /* Bit 3:0 */
+ #define tcon_pattern_loop_start 12
+ /* Bit 3:0 */
+ #define tcon_pattern_loop_end 8
+ /* Bit 7:0 */
+ #define tcon_pattern_enable 0
+#define TCON_CONTROL_HI 0x14cc
+#define TCON_CONTROL_LO 0x14cd
+ /* Bit 5:0 (enable pclk on TCON channel 7 to 2) */
+ #define tcon_pclk_enable 26
+ /* Bit 1:0 (control phy clok divide 2,4,6,8) */
+ #define tcon_pclk_div 24
+ /* Bit 23:0 (3 bit for each channel) */
+ #define tcon_delay 0
+#define LVDS_BLANK_DATA_HI 0x14ce
+#define LVDS_BLANK_DATA_LO 0x14cf
+ /* 31:30 */
+ #define LVDS_blank_data_reserved 30
+ /* 29:20 */
+ #define LVDS_blank_data_r 20
+ /* 19:10 */
+ #define LVDS_blank_data_g 10
+ /* 9:0 */
+ #define LVDS_blank_data_b 0
+#define LVDS_PACK_CNTL_ADDR 0x14d0
+ #define LVDS_USE_TCON 7
+ #define LVDS_DUAL 6
+ #define PN_SWP 5
+ #define LSB_FIRST 4
+ #define LVDS_RESV 3
+ #define ODD_EVEN_SWP 2
+ #define LVDS_REPACK 0
+/* New from M3 :
+ * Bit 15:12 -- Enable OFFSET Double Generate(TOCN7-TCON4)
+ * Bit 11:0 -- de_hs(old tcon) second offset_hs (new tcon)
+ */
+#define DE_HS_ADDR 0x14d1
+/* New from M3 :
+ * Bit 15:12 -- Enable OFFSET Double Generate(TOCN3-TCON0)
+ */
+#define DE_HE_ADDR 0x14d2
+#define DE_VS_ADDR 0x14d3
+#define DE_VE_ADDR 0x14d4
+#define HSYNC_HS_ADDR 0x14d5
+#define HSYNC_HE_ADDR 0x14d6
+#define HSYNC_VS_ADDR 0x14d7
+#define HSYNC_VE_ADDR 0x14d8
+#define VSYNC_HS_ADDR 0x14d9
+#define VSYNC_HE_ADDR 0x14da
+#define VSYNC_VS_ADDR 0x14db
+#define VSYNC_VE_ADDR 0x14dc
+/* bit 8 -- vfifo_mcu_enable
+ * bit 7 -- halt_vs_de
+ * bit 6 -- R8G8B8_format
+ * bit 5 -- R6G6B6_format (round to 6 bits)
+ * bit 4 -- R5G6B5_format
+ * bit 3 -- dac_dith_sel
+ * bit 2 -- lcd_mcu_enable_de -- ReadOnly
+ * bit 1 -- lcd_mcu_enable_vsync -- ReadOnly
+ * bit 0 -- lcd_mcu_enable
+ */
+#define LCD_MCU_CTL 0x14dd
+/* ReadOnly
+ * R5G6B5 when R5G6B5_format
+ * G8R8 when R8G8B8_format
+ * G5R10 Other
+ */
+#define LCD_MCU_DATA_0 0x14de
+/* ReadOnly
+ * G8B8 when R8G8B8_format
+ * G5B10 Other
+ */
+#define LCD_MCU_DATA_1 0x14df
+/* LVDS */
+#define LVDS_GEN_CNTL 0x14e0
+#define LVDS_PHY_CNTL0 0x14e1
+#define LVDS_PHY_CNTL1 0x14e2
+#define LVDS_PHY_CNTL2 0x14e3
+#define LVDS_PHY_CNTL3 0x14e4
+#define LVDS_PHY_CNTL4 0x14e5
+#define LVDS_PHY_CNTL5 0x14e6
+#define LVDS_SRG_TEST 0x14e8
+#define LVDS_BIST_MUX0 0x14e9
+#define LVDS_BIST_MUX1 0x14ea
+#define LVDS_BIST_FIXED0 0x14eb
+#define LVDS_BIST_FIXED1 0x14ec
+#define LVDS_BIST_CNTL0 0x14ed
+#define LVDS_CLKB_CLKA 0x14ee
+#define LVDS_PHY_CLK_CNTL 0x14ef
+#define LVDS_SER_EN 0x14f0
+#define LVDS_PHY_CNTL6 0x14f1
+#define LVDS_PHY_CNTL7 0x14f2
+#define LVDS_PHY_CNTL8 0x14f3
+#define MLVDS_CLK_CTL0_HI 0x14f4
+#define MLVDS_CLK_CTL0_LO 0x14f5
+ #define mlvds_clk_pattern_reserved 31
+ /* Bit 2:0 */
+ #define mpclk_dly 28
+ /* Bit 1:0 (control phy clok divide 2,4,6,8) */
+ #define mpclk_div 26
+ #define use_mpclk 25
+ #define mlvds_clk_half_delay 24
+ /* Bit 23:0 */
+ #define mlvds_clk_pattern 0
+#define MLVDS_DUAL_GATE_WR_START 0x14f6
+ /* Bit 12:0 */
+ #define mlvds_dual_gate_wr_start 0
+#define MLVDS_DUAL_GATE_WR_END 0x14f7
+ /* Bit 12:0 */
+ #define mlvds_dual_gate_wr_end 0
+#define MLVDS_DUAL_GATE_RD_START 0x14f8
+ /* Bit 12:0 */
+ #define mlvds_dual_gate_rd_start 0
+#define MLVDS_DUAL_GATE_RD_END 0x14f9
+ /* Bit 12:0 */
+ #define mlvds_dual_gate_rd_end 0
+#define MLVDS_SECOND_RESET_CTL 0x14fa
+ /* Bit 12:0 */
+ #define mLVDS_2nd_reset_start 0
+#define MLVDS_DUAL_GATE_CTL_HI 0x14fb
+#define MLVDS_DUAL_GATE_CTL_LO 0x14fc
+ /* Bit 7:0 */
+ #define mlvds_tcon_field_en 24
+ /* Bit 2:0 */
+ #define mlvds_dual_gate_reserved 21
+ #define mlvds_scan_mode_start_line_bit12 20
+ /* Bit 3:0 */
+ #define mlvds_scan_mode_odd 16
+ /* Bit 3:0 */
+ #define mlvds_scan_mode_even 12
+ /* Bit 11:0 */
+ #define mlvds_scan_mode_start_line 0
+#define MLVDS_RESET_CONFIG_HI 0x14fd
+#define MLVDS_RESET_CONFIG_LO 0x14fe
+ #define mLVDS_reset_range_enable 31
+ #define mLVDS_reset_range_inv 30
+ #define mLVDS_reset_config_res1 29
+ /* Bit 11:0 */
+ #define mLVDS_reset_range_line_0 16
+ /* Bit 2:0 */
+ #define mLVDS_reset_config_res3 13
+ /* Bit 11:0 */
+ #define mLVDS_reset_range_line_1 0
+
+/* **************************************************************************
+ * Vbyone registers (Note: no MinLVDS in G9tv, share the register)
+ */
+#define VBO_CTRL_L 0x1460
+#define VBO_CTRL_H 0x1461
+#define VBO_SOFT_RST 0x1462
+#define VBO_LANES 0x1463
+#define VBO_VIN_CTRL 0x1464
+#define VBO_ACT_VSIZE 0x1465
+#define VBO_REGION_00 0x1466
+#define VBO_REGION_01 0x1467
+#define VBO_REGION_02 0x1468
+#define VBO_REGION_03 0x1469
+#define VBO_VBK_CTRL_0 0x146a
+#define VBO_VBK_CTRL_1 0x146b
+#define VBO_HBK_CTRL 0x146c
+#define VBO_PXL_CTRL 0x146d
+#define VBO_LANE_SKEW_L 0x146e
+#define VBO_LANE_SKEW_H 0x146f
+#define VBO_GCLK_LANE_L 0x1470
+#define VBO_GCLK_LANE_H 0x1471
+#define VBO_GCLK_MAIN 0x1472
+#define VBO_STATUS_L 0x1473
+#define VBO_STATUS_H 0x1474
+#define VBO_LANE_OUTPUT 0x1475
+#define LCD_PORT_SWAP 0x1476
+#define VBO_TMCHK_THRD_L 0x1478
+#define VBO_TMCHK_THRD_H 0x1479
+#define VBO_FSM_HOLDER_L 0x147a
+#define VBO_FSM_HOLDER_H 0x147b
+#define VBO_INTR_STATE_CTRL 0x147c
+#define VBO_INTR_UNMASK 0x147d
+#define VBO_TMCHK_HSYNC_STATE_L 0x147e
+#define VBO_TMCHK_HSYNC_STATE_H 0x147f
+#define VBO_TMCHK_VSYNC_STATE_L 0x14f4
+#define VBO_TMCHK_VSYNC_STATE_H 0x14f5
+#define VBO_TMCHK_VDE_STATE_L 0x14f6
+#define VBO_TMCHK_VDE_STATE_H 0x14f7
+#define VBO_INTR_STATE 0x14f8
+
+/* ********************************
+ * Video Interface: VENC_VCBUS_BASE = 0x1b
+ */
+#define VENC_INTCTRL 0x1b6e
+
+/* ********************************
+ * ENCL: VCBUS_BASE = 0x1c
+ */
+/* ENCL */
+/* bit 15:8 -- vfifo2vd_vd_sel
+ * bit 7 -- vfifo2vd_drop
+ * bit 6:1 -- vfifo2vd_delay
+ * bit 0 -- vfifo2vd_en
+ */
+#define ENCL_VFIFO2VD_CTL 0x1c90
+/* bit 12:0 -- vfifo2vd_pixel_start */
+#define ENCL_VFIFO2VD_PIXEL_START 0x1c91
+/* bit 12:00 -- vfifo2vd_pixel_end */
+#define ENCL_VFIFO2VD_PIXEL_END 0x1c92
+/* bit 10:0 -- vfifo2vd_line_top_start */
+#define ENCL_VFIFO2VD_LINE_TOP_START 0x1c93
+/* bit 10:00 -- vfifo2vd_line_top_end */
+#define ENCL_VFIFO2VD_LINE_TOP_END 0x1c94
+/* bit 10:00 -- vfifo2vd_line_bot_start */
+#define ENCL_VFIFO2VD_LINE_BOT_START 0x1c95
+/* bit 10:00 -- vfifo2vd_line_bot_end */
+#define ENCL_VFIFO2VD_LINE_BOT_END 0x1c96
+#define ENCL_VFIFO2VD_CTL2 0x1c97
+#define ENCL_TST_EN 0x1c98
+#define ENCL_TST_MDSEL 0x1c99
+#define ENCL_TST_Y 0x1c9a
+#define ENCL_TST_CB 0x1c9b
+#define ENCL_TST_CR 0x1c9c
+#define ENCL_TST_CLRBAR_STRT 0x1c9d
+#define ENCL_TST_CLRBAR_WIDTH 0x1c9e
+#define ENCL_TST_VDCNT_STSET 0x1c9f
+
+/* ENCL registers */
+#define ENCL_VIDEO_EN 0x1ca0
+#define ENCL_VIDEO_Y_SCL 0x1ca1
+#define ENCL_VIDEO_PB_SCL 0x1ca2
+#define ENCL_VIDEO_PR_SCL 0x1ca3
+#define ENCL_VIDEO_Y_OFFST 0x1ca4
+#define ENCL_VIDEO_PB_OFFST 0x1ca5
+#define ENCL_VIDEO_PR_OFFST 0x1ca6
+/* ----- Video mode */
+#define ENCL_VIDEO_MODE 0x1ca7
+#define ENCL_VIDEO_MODE_ADV 0x1ca8
+/* --------------- Debug pins */
+#define ENCL_DBG_PX_RST 0x1ca9
+#define ENCL_DBG_LN_RST 0x1caa
+#define ENCL_DBG_PX_INT 0x1cab
+#define ENCL_DBG_LN_INT 0x1cac
+/* ----------- Video Advanced setting */
+#define ENCL_VIDEO_YFP1_HTIME 0x1cad
+#define ENCL_VIDEO_YFP2_HTIME 0x1cae
+#define ENCL_VIDEO_YC_DLY 0x1caf
+#define ENCL_VIDEO_MAX_PXCNT 0x1cb0
+#define ENCL_VIDEO_HAVON_END 0x1cb1
+#define ENCL_VIDEO_HAVON_BEGIN 0x1cb2
+#define ENCL_VIDEO_VAVON_ELINE 0x1cb3
+#define ENCL_VIDEO_VAVON_BLINE 0x1cb4
+#define ENCL_VIDEO_HSO_BEGIN 0x1cb5
+#define ENCL_VIDEO_HSO_END 0x1cb6
+#define ENCL_VIDEO_VSO_BEGIN 0x1cb7
+#define ENCL_VIDEO_VSO_END 0x1cb8
+#define ENCL_VIDEO_VSO_BLINE 0x1cb9
+#define ENCL_VIDEO_VSO_ELINE 0x1cba
+#define ENCL_VIDEO_MAX_LNCNT 0x1cbb
+#define ENCL_VIDEO_BLANKY_VAL 0x1cbc
+#define ENCL_VIDEO_BLANKPB_VAL 0x1cbd
+#define ENCL_VIDEO_BLANKPR_VAL 0x1cbe
+#define ENCL_VIDEO_HOFFST 0x1cbf
+#define ENCL_VIDEO_VOFFST 0x1cc0
+#define ENCL_VIDEO_RGB_CTRL 0x1cc1
+#define ENCL_VIDEO_FILT_CTRL 0x1cc2
+#define ENCL_VIDEO_OFLD_VPEQ_OFST 0x1cc3
+#define ENCL_VIDEO_OFLD_VOAV_OFST 0x1cc4
+#define ENCL_VIDEO_MATRIX_CB 0x1cc5
+#define ENCL_VIDEO_MATRIX_CR 0x1cc6
+#define ENCL_VIDEO_RGBIN_CTRL 0x1cc7
+#define ENCL_MAX_LINE_SWITCH_POINT 0x1cc8
+#define ENCL_DACSEL_0 0x1cc9
+#define ENCL_DACSEL_1 0x1cca
+
+/* ********************************
+ * ENCT: VCBUS_BASE = 0x1c
+ */
+/* ENCT */
+/* bit 15:8 -- vfifo2vd_vd_sel
+ * bit 7 -- vfifo2vd_drop
+ * bit 6:1 -- vfifo2vd_delay
+ * bit 0 -- vfifo2vd_en
+ */
+#define ENCT_VFIFO2VD_CTL 0x1c20
+/* bit 12:0 -- vfifo2vd_pixel_start */
+#define ENCT_VFIFO2VD_PIXEL_START 0x1c21
+/* bit 12:00 -- vfifo2vd_pixel_end */
+#define ENCT_VFIFO2VD_PIXEL_END 0x1c22
+/* bit 10:0 -- vfifo2vd_line_top_start */
+#define ENCT_VFIFO2VD_LINE_TOP_START 0x1c23
+/* bit 10:00 -- vfifo2vd_line_top_end */
+#define ENCT_VFIFO2VD_LINE_TOP_END 0x1c24
+/* bit 10:00 -- vfifo2vd_line_bot_start */
+#define ENCT_VFIFO2VD_LINE_BOT_START 0x1c25
+/* bit 10:00 -- vfifo2vd_line_bot_end */
+#define ENCT_VFIFO2VD_LINE_BOT_END 0x1c26
+#define ENCT_VFIFO2VD_CTL2 0x1c27
+#define ENCT_TST_EN 0x1c28
+#define ENCT_TST_MDSEL 0x1c29
+#define ENCT_TST_Y 0x1c2a
+#define ENCT_TST_CB 0x1c2b
+#define ENCT_TST_CR 0x1c2c
+#define ENCT_TST_CLRBAR_STRT 0x1c2d
+#define ENCT_TST_CLRBAR_WIDTH 0x1c2e
+#define ENCT_TST_VDCNT_STSET 0x1c2f
+
+/* ENCT registers */
+#define ENCT_VIDEO_EN 0x1c60
+#define ENCT_VIDEO_Y_SCL 0x1c61
+#define ENCT_VIDEO_PB_SCL 0x1c62
+#define ENCT_VIDEO_PR_SCL 0x1c63
+#define ENCT_VIDEO_Y_OFFST 0x1c64
+#define ENCT_VIDEO_PB_OFFST 0x1c65
+#define ENCT_VIDEO_PR_OFFST 0x1c66
+/* ----- Video mode */
+#define ENCT_VIDEO_MODE 0x1c67
+#define ENCT_VIDEO_MODE_ADV 0x1c68
+/* --------------- Debug pins */
+#define ENCT_DBG_PX_RST 0x1c69
+#define ENCT_DBG_LN_RST 0x1c6a
+#define ENCT_DBG_PX_INT 0x1c6b
+#define ENCT_DBG_LN_INT 0x1c6c
+/* ----------- Video Advanced setting */
+#define ENCT_VIDEO_YFP1_HTIME 0x1c6d
+#define ENCT_VIDEO_YFP2_HTIME 0x1c6e
+#define ENCT_VIDEO_YC_DLY 0x1c6f
+#define ENCT_VIDEO_MAX_PXCNT 0x1c70
+#define ENCT_VIDEO_HAVON_END 0x1c71
+#define ENCT_VIDEO_HAVON_BEGIN 0x1c72
+#define ENCT_VIDEO_VAVON_ELINE 0x1c73
+#define ENCT_VIDEO_VAVON_BLINE 0x1c74
+#define ENCT_VIDEO_HSO_BEGIN 0x1c75
+#define ENCT_VIDEO_HSO_END 0x1c76
+#define ENCT_VIDEO_VSO_BEGIN 0x1c77
+#define ENCT_VIDEO_VSO_END 0x1c78
+#define ENCT_VIDEO_VSO_BLINE 0x1c79
+#define ENCT_VIDEO_VSO_ELINE 0x1c7a
+#define ENCT_VIDEO_MAX_LNCNT 0x1c7b
+#define ENCT_VIDEO_BLANKY_VAL 0x1c7c
+#define ENCT_VIDEO_BLANKPB_VAL 0x1c7d
+#define ENCT_VIDEO_BLANKPR_VAL 0x1c7e
+#define ENCT_VIDEO_HOFFST 0x1c7f
+#define ENCT_VIDEO_VOFFST 0x1c80
+#define ENCT_VIDEO_RGB_CTRL 0x1c81
+#define ENCT_VIDEO_FILT_CTRL 0x1c82
+#define ENCT_VIDEO_OFLD_VPEQ_OFST 0x1c83
+#define ENCT_VIDEO_OFLD_VOAV_OFST 0x1c84
+#define ENCT_VIDEO_MATRIX_CB 0x1c85
+#define ENCT_VIDEO_MATRIX_CR 0x1c86
+#define ENCT_VIDEO_RGBIN_CTRL 0x1c87
+#define ENCT_MAX_LINE_SWITCH_POINT 0x1c88
+#define ENCT_DACSEL_0 0x1c89
+#define ENCT_DACSEL_1 0x1c8a
+
+/* ********************************
+ * Video post-processing: VPP_VCBUS_BASE = 0x1d
+ * Bit 31 vd1_bgosd_exchange_en for preblend
+ * Bit 30 vd1_bgosd_exchange_en for postblend
+ * bit 28 color management enable
+ * Bit 27, reserved
+ * Bit 26:18, reserved
+ * Bit 17, osd2 enable for preblend
+ * Bit 16, osd1 enable for preblend
+ * Bit 15, reserved
+ * Bit 14, vd1 enable for preblend
+ * Bit 13, osd2 enable for postblend
+ * Bit 12, osd1 enable for postblend
+ * Bit 11, reserved
+ * Bit 10, vd1 enable for postblend
+ * Bit 9, if true, osd1 is alpha premultipiled
+ * Bit 8, if true, osd2 is alpha premultipiled
+ * Bit 7, postblend module enable
+ * Bit 6, preblend module enable
+ * Bit 5, if true, osd2 foreground compared with osd1 in preblend
+ * Bit 4, if true, osd2 foreground compared with osd1 in postblend
+ * Bit 3,
+ * Bit 2, if true, disable resetting async fifo every vsync, otherwise every
+ * vsync the aync fifo will be reseted.
+ * Bit 1,
+ * Bit 0 if true, the output result of VPP is saturated
+ */
+#define VPP2_MISC 0x1926
+/* Bit 31 vd1_bgosd_exchange_en for preblend
+ * Bit 30 vd1_bgosd_exchange_en for postblend
+ * Bit 28 color management enable
+ * Bit 27, if true, vd2 use viu2 output as the input, otherwise use normal
+ * vd2 from memory
+ * Bit 26:18, vd2 alpha
+ * Bit 17, osd2 enable for preblend
+ * Bit 16, osd1 enable for preblend
+ * Bit 15, vd2 enable for preblend
+ * Bit 14, vd1 enable for preblend
+ * Bit 13, osd2 enable for postblend
+ * Bit 12, osd1 enable for postblend
+ * Bit 11, vd2 enable for postblend
+ * Bit 10, vd1 enable for postblend
+ * Bit 9, if true, osd1 is alpha premultipiled
+ * Bit 8, if true, osd2 is alpha premultipiled
+ * Bit 7, postblend module enable
+ * Bit 6, preblend module enable
+ * Bit 5, if true, osd2 foreground compared with osd1 in preblend
+ * Bit 4, if true, osd2 foreground compared with osd1 in postblend
+ * Bit 3,
+ * Bit 2, if true, disable resetting async fifo every vsync, otherwise every
+ * vsync the aync fifo will be reseted.
+ * Bit 1,
+ * Bit 0 if true, the output result of VPP is saturated
+ */
+#define VPP_MISC 0x1d26
+
+#define VPP2_POSTBLEND_H_SIZE 0x1921
+#define VPP_POSTBLEND_H_SIZE 0x1d21
+/* Bit 3 minus black level enable for vadj2
+ * Bit 2 Video adjustment enable for vadj2
+ * Bit 1 minus black level enable for vadj1
+ * Bit 0 Video adjustment enable for vadj1
+ */
+#define VPP_VADJ_CTRL 0x1d40
+/* Bit 16:8 brightness, signed value
+ * Bit 7:0 contrast, unsigned value,
+ * contrast from 0 <= contrast <2
+ */
+#define VPP_VADJ1_Y 0x1d41
+/* cb' = cb*ma + cr*mb
+ * cr' = cb*mc + cr*md
+ * all are bit 9:0, signed value, -2 < ma/mb/mc/md < 2
+ */
+#define VPP_VADJ1_MA_MB 0x1d42
+#define VPP_VADJ1_MC_MD 0x1d43
+/* Bit 16:8 brightness, signed value
+ * Bit 7:0 contrast, unsigned value,
+ * contrast from 0 <= contrast <2
+ */
+#define VPP_VADJ2_Y 0x1d44
+/* cb' = cb*ma + cr*mb
+ * cr' = cb*mc + cr*md
+ * all are bit 9:0, signed value, -2 < ma/mb/mc/md < 2
+ */
+#define VPP_VADJ2_MA_MB 0x1d45
+#define VPP_VADJ2_MC_MD 0x1d46
+
+#define VPP_MATRIX_CTRL 0x1d5f
+/* Bit 28:16 coef00 */
+/* Bit 12:0 coef01 */
+#define VPP_MATRIX_COEF00_01 0x1d60
+/* Bit 28:16 coef02 */
+/* Bit 12:0 coef10 */
+#define VPP_MATRIX_COEF02_10 0x1d61
+/* Bit 28:16 coef11 */
+/* Bit 12:0 coef12 */
+#define VPP_MATRIX_COEF11_12 0x1d62
+/* Bit 28:16 coef20 */
+/* Bit 12:0 coef21 */
+#define VPP_MATRIX_COEF20_21 0x1d63
+#define VPP_MATRIX_COEF22 0x1d64
+/* Bit 26:16 offset0 */
+/* Bit 10:0 offset1 */
+#define VPP_MATRIX_OFFSET0_1 0x1d65
+/* Bit 10:0 offset2 */
+#define VPP_MATRIX_OFFSET2 0x1d66
+/* Bit 26:16 pre_offset0 */
+/* Bit 10:0 pre_offset1 */
+#define VPP_MATRIX_PRE_OFFSET0_1 0x1d67
+/* Bit 10:0 pre_offset2 */
+#define VPP_MATRIX_PRE_OFFSET2 0x1d68
+
+/* ********************************
+ * VPU: VPU_VCBUS_BASE = 0x27
+ * [31:11] Reserved.
+ * [10: 8] cntl_viu_vdin_sel_data. Select VIU to VDIN data path,
+ * must clear it first before changing the path selection:
+ * 3'b000=Disable VIU to VDIN path;
+ * 3'b001=Enable VIU of ENC_I domain to VDIN;
+ * 3'b010=Enable VIU of ENC_P domain to VDIN;
+ * 3'b100=Enable VIU of ENC_T domain to VDIN;
+ * [ 6: 4] cntl_viu_vdin_sel_clk. Select which clock to VDIN path,
+ * must clear it first before changing the clock:
+ * 3'b000=Disable VIU to VDIN clock;
+ * 3'b001=Select encI clock to VDIN;
+ * 3'b010=Select encP clock to VDIN;
+ * 3'b100=Select encT clock to VDIN;
+ * [ 3: 2] cntl_viu2_sel_venc. Select which one of the encI/P/T
+ * that VIU2 connects to:
+ * 0=ENCL, 1=ENCI, 2=ENCP, 3=ENCT.
+ * [ 1: 0] cntl_viu1_sel_venc. Select which one of the encI/P/T
+ * that VIU1 connects to:
+ * 0=ENCL, 1=ENCI, 2=ENCP, 3=ENCT.
+ */
+#define VPU_VIU_VENC_MUX_CTRL 0x271a
+
+/* Bit 6 RW, gclk_mpeg_vpu_misc
+ * Bit 5 RW, gclk_mpeg_venc_l_top
+ * Bit 4 RW, gclk_mpeg_vencl_int
+ * Bit 3 RW, gclk_mpeg_vencp_int
+ * Bit 2 RW, gclk_mpeg_vi2_top
+ * Bit 1 RW, gclk_mpeg_vi_top
+ * Bit 0 RW, gclk_mpeg_venc_p_top
+ */
+#define VPU_CLK_GATE 0x2723
+#define VPU_MISC_CTRL 0x2740
+
+#define VPU_VENCL_DITH_CTRL 0x27e0
+
+#define VPU_VLOCK_CTRL 0x3000
+#define VPU_VLOCK_ADJ_EN_SYNC_CTRL 0x301d
+#define VPU_VLOCK_GCLK_EN 0x301e
+/* ******************************** */
+
+/* ***********************************************
+ * DSI Host Controller register offset address define
+ * VCBUS_BASE = 0x2c(0x2c00 - 0x2cff)
+ */
+/* DWC IP registers */
+#define MIPI_DSI_DWC_VERSION_OS 0x1800
+#define MIPI_DSI_DWC_PWR_UP_OS 0x1801
+#define MIPI_DSI_DWC_CLKMGR_CFG_OS 0x1802
+#define MIPI_DSI_DWC_DPI_VCID_OS 0x1803
+#define MIPI_DSI_DWC_DPI_COLOR_CODING_OS 0x1804
+#define MIPI_DSI_DWC_DPI_CFG_POL_OS 0x1805
+#define MIPI_DSI_DWC_DPI_LP_CMD_TIM_OS 0x1806
+#define MIPI_DSI_DWC_PCKHDL_CFG_OS 0x180b
+#define MIPI_DSI_DWC_GEN_VCID_OS 0x180c
+#define MIPI_DSI_DWC_MODE_CFG_OS 0x180d
+#define MIPI_DSI_DWC_VID_MODE_CFG_OS 0x180e
+#define MIPI_DSI_DWC_VID_PKT_SIZE_OS 0x180f
+#define MIPI_DSI_DWC_VID_NUM_CHUNKS_OS 0x1810
+#define MIPI_DSI_DWC_VID_NULL_SIZE_OS 0x1811
+#define MIPI_DSI_DWC_VID_HSA_TIME_OS 0x1812
+#define MIPI_DSI_DWC_VID_HBP_TIME_OS 0x1813
+#define MIPI_DSI_DWC_VID_HLINE_TIME_OS 0x1814
+#define MIPI_DSI_DWC_VID_VSA_LINES_OS 0x1815
+#define MIPI_DSI_DWC_VID_VBP_LINES_OS 0x1816
+#define MIPI_DSI_DWC_VID_VFP_LINES_OS 0x1817
+#define MIPI_DSI_DWC_VID_VACTIVE_LINES_OS 0x1818
+#define MIPI_DSI_DWC_EDPI_CMD_SIZE_OS 0x1819
+#define MIPI_DSI_DWC_CMD_MODE_CFG_OS 0x181a
+#define MIPI_DSI_DWC_GEN_HDR_OS 0x181b
+#define MIPI_DSI_DWC_GEN_PLD_DATA_OS 0x181c
+#define MIPI_DSI_DWC_CMD_PKT_STATUS_OS 0x181d
+#define MIPI_DSI_DWC_TO_CNT_CFG_OS 0x181e
+#define MIPI_DSI_DWC_HS_RD_TO_CNT_OS 0x181f
+#define MIPI_DSI_DWC_LP_RD_TO_CNT_OS 0x1820
+#define MIPI_DSI_DWC_HS_WR_TO_CNT_OS 0x1821
+#define MIPI_DSI_DWC_LP_WR_TO_CNT_OS 0x1822
+#define MIPI_DSI_DWC_BTA_TO_CNT_OS 0x1823
+#define MIPI_DSI_DWC_SDF_3D_OS 0x1824
+#define MIPI_DSI_DWC_LPCLK_CTRL_OS 0x1825
+#define MIPI_DSI_DWC_PHY_TMR_LPCLK_CFG_OS 0x1826
+#define MIPI_DSI_DWC_PHY_TMR_CFG_OS 0x1827
+#define MIPI_DSI_DWC_PHY_RSTZ_OS 0x1828
+#define MIPI_DSI_DWC_PHY_IF_CFG_OS 0x1829
+#define MIPI_DSI_DWC_PHY_ULPS_CTRL_OS 0x182a
+#define MIPI_DSI_DWC_PHY_TX_TRIGGERS_OS 0x182b
+#define MIPI_DSI_DWC_PHY_STATUS_OS 0x182c
+#define MIPI_DSI_DWC_PHY_TST_CTRL0_OS 0x182d
+#define MIPI_DSI_DWC_PHY_TST_CTRL1_OS 0x182e
+#define MIPI_DSI_DWC_INT_ST0_OS 0x182f
+#define MIPI_DSI_DWC_INT_ST1_OS 0x1830
+#define MIPI_DSI_DWC_INT_MSK0_OS 0x1831
+#define MIPI_DSI_DWC_INT_MSK1_OS 0x1832
+
+/* Top-level registers */
+/* [31: 3] Reserved. Default 0.
+ * [2] RW dpi_rst_n: Default 1.
+ * 1=Assert SW reset on mipi_dsi_host_dpi block. 0=Release reset.
+ * [1] RW intr_rst_n: Default 1.
+ * 1=Assert SW reset on mipi_dsi_host_intr block. 0=Release reset.
+ * [0] RW dwc_rst_n: Default 1.
+ * 1=Assert SW reset on IP core. 0=Release reset.
+ */
+#define MIPI_DSI_TOP_SW_RESET 0x18f0
+/* [31: 5] Reserved. Default 0.
+ * [4] RW manual_edpihalt: Default 0.
+ * 1=Manual suspend VencL; 0=do not suspend VencL.
+ * [3] RW auto_edpihalt_en: Default 0.
+ * 1=Enable IP's edpihalt signal to suspend VencL;
+ * 0=IP's edpihalt signal does not affect VencL.
+ * [2] RW clock_freerun: Apply to auto-clock gate only. Default 0.
+ * 0=Default, use auto-clock gating to save power;
+ * 1=use free-run clock, disable auto-clock gating, for debug mode.
+ * [1] RW enable_pixclk: A manual clock gate option, due to DWC IP does not
+ * have auto-clock gating. 1=Enable pixclk. Default 0.
+ * [0] RW enable_sysclk: A manual clock gate option, due to DWC IP does not
+ * have auto-clock gating. 1=Enable sysclk. Default 0.
+ */
+#define MIPI_DSI_TOP_CLK_CNTL 0x18f1
+/* [31:24] Reserved. Default 0.
+ * [23:20] RW dpi_color_mode: Define DPI pixel format. Default 0.
+ * 0=16-bit RGB565 config 1;
+ * 1=16-bit RGB565 config 2;
+ * 2=16-bit RGB565 config 3;
+ * 3=18-bit RGB666 config 1;
+ * 4=18-bit RGB666 config 2;
+ * 5=24-bit RGB888;
+ * 6=20-bit YCbCr 4:2:2;
+ * 7=24-bit YCbCr 4:2:2;
+ * 8=16-bit YCbCr 4:2:2;
+ * 9=30-bit RGB;
+ * 10=36-bit RGB;
+ * 11=12-bit YCbCr 4:2:0.
+ * [19] Reserved. Default 0.
+ * [18:16] RW in_color_mode: Define VENC data width. Default 0.
+ * 0=30-bit pixel;
+ * 1=24-bit pixel;
+ * 2=18-bit pixel, RGB666;
+ * 3=16-bit pixel, RGB565.
+ * [15:14] RW chroma_subsample: Define method of chroma subsampling. Default 0.
+ * Applicable to YUV422 or YUV420 only.
+ * 0=Use even pixel's chroma;
+ * 1=Use odd pixel's chroma;
+ * 2=Use averaged value between even and odd pair.
+ * [13:12] RW comp2_sel: Select which component to be Cr or B: Default 2.
+ * 0=comp0; 1=comp1; 2=comp2.
+ * [11:10] RW comp1_sel: Select which component to be Cb or G: Default 1.
+ * 0=comp0; 1=comp1; 2=comp2.
+ * [9: 8] RW comp0_sel: Select which component to be Y or R: Default 0.
+ * 0=comp0; 1=comp1; 2=comp2.
+ * [7] Reserved. Default 0.
+ * [6] RW de_pol: Default 0.
+ * If DE input is active low, set to 1 to invert to active high.
+ * [5] RW hsync_pol: Default 0.
+ * If HS input is active low, set to 1 to invert to active high.
+ * [4] RW vsync_pol: Default 0.
+ * If VS input is active low, set to 1 to invert to active high.
+ * [3] RW dpicolorm: Signal to IP. Default 0.
+ * [2] RW dpishutdn: Signal to IP. Default 0.
+ * [1] Reserved. Default 0.
+ * [0] Reserved. Default 0.
+ */
+#define MIPI_DSI_TOP_CNTL 0x18f2
+#define MIPI_DSI_TOP_SUSPEND_CNTL 0x18f3
+#define MIPI_DSI_TOP_SUSPEND_LINE 0x18f4
+#define MIPI_DSI_TOP_SUSPEND_PIX 0x18f5
+#define MIPI_DSI_TOP_MEAS_CNTL 0x18f6
+/* [0] R stat_edpihalt: edpihalt signal from IP. Default 0. */
+#define MIPI_DSI_TOP_STAT 0x18f7
+#define MIPI_DSI_TOP_MEAS_STAT_TE0 0x18f8
+#define MIPI_DSI_TOP_MEAS_STAT_TE1 0x18f9
+#define MIPI_DSI_TOP_MEAS_STAT_VS0 0x18fa
+#define MIPI_DSI_TOP_MEAS_STAT_VS1 0x18fb
+/* [31:16] RW intr_stat/clr. Default 0.
+ * For each bit, read as this interrupt level status,
+ * write 1 to clear.
+ * [31:22] Reserved
+ * [ 21] stat/clr of eof interrupt
+ * [ 21] vde_fall interrupt
+ * [ 19] stat/clr of de_rise interrupt
+ * [ 18] stat/clr of vs_fall interrupt
+ * [ 17] stat/clr of vs_rise interrupt
+ * [ 16] stat/clr of dwc_edpite interrupt
+ * [15: 0] RW intr_enable. Default 0.
+ * For each bit, 1=enable this interrupt, 0=disable.
+ * [15: 6] Reserved
+ * [ 5] eof interrupt
+ * [ 4] de_fall interrupt
+ * [ 3] de_rise interrupt
+ * [ 2] vs_fall interrupt
+ * [ 1] vs_rise interrupt
+ * [ 0] dwc_edpite interrupt
+ */
+#define MIPI_DSI_TOP_INTR_CNTL_STAT 0x18fc
+// 31: 2 Reserved. Default 0.
+// 1: 0 RW mem_pd. Default 3.
+#define MIPI_DSI_TOP_MEM_PD 0x18fd
+
+/* ***********************************************
+ * DSI PHY register offset address define
+ */
+#define MIPI_DSI_PHY_START 0xff640000
+#define MIPI_DSI_PHY_END 0xff641fff
+/* [31] soft reset for the phy.
+ * 1: reset. 0: dessert the reset.
+ * [30] clock lane soft reset.
+ * [29] data byte lane 3 soft reset.
+ * [28] data byte lane 2 soft reset.
+ * [27] data byte lane 1 soft reset.
+ * [26] data byte lane 0 soft reset.
+ * [25] mipi dsi pll clock selection.
+ * 1: clock from fixed 850Mhz clock source. 0: from VID2 PLL.
+ * [12] mipi HSbyteclk enable.
+ * [11] mipi divider clk selection.
+ * 1: select the mipi DDRCLKHS from clock divider.
+ * 0: from PLL clock.
+ * [10] mipi clock divider control.
+ * 1: /4. 0: /2.
+ * [9] mipi divider output enable.
+ * [8] mipi divider counter enable.
+ * [7] PLL clock enable.
+ * [5] LPDT data endian.
+ * 1 = transfer the high bit first. 0 : transfer the low bit first.
+ * [4] HS data endian.
+ * [3] force data byte lane in stop mode.
+ * [2] force data byte lane 0 in receiver mode.
+ * [1] write 1 to sync the txclkesc input. the internal logic have to
+ * use txclkesc to decide Txvalid and Txready.
+ * [0] enalbe the MIPI DSI PHY TxDDRClk.
+ */
+#define MIPI_DSI_PHY_CTRL 0x0
+/* [31] clk lane tx_hs_en control selection.
+ * 1: from register. 0: use clk lane state machine.
+ * [30] register bit for clock lane tx_hs_en.
+ * [29] clk lane tx_lp_en contrl selection.
+ * 1: from register. 0: from clk lane state machine.
+ * [28] register bit for clock lane tx_lp_en.
+ * [27] chan0 tx_hs_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [26] register bit for chan0 tx_hs_en.
+ * [25] chan0 tx_lp_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [24] register bit from chan0 tx_lp_en.
+ * [23] chan0 rx_lp_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [22] register bit from chan0 rx_lp_en.
+ * [21] chan0 contention detection enable control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [20] register bit from chan0 contention dectection enable.
+ * [19] chan1 tx_hs_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [18] register bit for chan1 tx_hs_en.
+ * [17] chan1 tx_lp_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [16] register bit from chan1 tx_lp_en.
+ * [15] chan2 tx_hs_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [14] register bit for chan2 tx_hs_en.
+ * [13] chan2 tx_lp_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [12] register bit from chan2 tx_lp_en.
+ * [11] chan3 tx_hs_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [10] register bit for chan3 tx_hs_en.
+ * [9] chan3 tx_lp_en control selection.
+ * 1: from register. 0: from chan0 state machine.
+ * [8] register bit from chan3 tx_lp_en.
+ * [4] clk chan power down. this bit is also used as the power down
+ * of the whole MIPI_DSI_PHY.
+ * [3] chan3 power down.
+ * [2] chan2 power down.
+ * [1] chan1 power down.
+ * [0] chan0 power down.
+ */
+#define MIPI_DSI_CHAN_CTRL 0x1
+/* [24] rx turn watch dog triggered.
+ * [23] rx esc watchdog triggered.
+ * [22] mbias ready.
+ * [21] txclkesc synced and ready.
+ * [20:17] clk lane state. {mbias_ready, tx_stop, tx_ulps, tx_hs_active}
+ * [16:13] chan3 state{0, tx_stop, tx_ulps, tx_hs_active}
+ * [12:9] chan2 state.{0, tx_stop, tx_ulps, tx_hs_active}
+ * [8:5] chan1 state. {0, tx_stop, tx_ulps, tx_hs_active}
+ * [4:0] chan0 state. {TX_STOP, tx_ULPS, hs_active, direction, rxulpsesc}
+ */
+#define MIPI_DSI_CHAN_STS 0x2
+/* [31:24] TCLK_PREPARE.
+ * [23:16] TCLK_ZERO.
+ * [15:8] TCLK_POST.
+ * [7:0] TCLK_TRAIL.
+ */
+#define MIPI_DSI_CLK_TIM 0x3
+/* [31:24] THS_PREPARE.
+ * [23:16] THS_ZERO.
+ * [15:8] THS_TRAIL.
+ * [7:0] THS_EXIT.
+ */
+#define MIPI_DSI_HS_TIM 0x4
+/* [31:24] tTA_GET.
+ * [23:16] tTA_GO.
+ * [15:8] tTA_SURE.
+ * [7:0] tLPX.
+ */
+#define MIPI_DSI_LP_TIM 0x5
+/* wait time to MIPI DIS analog ready. */
+#define MIPI_DSI_ANA_UP_TIM 0x6
+/* TINIT. */
+#define MIPI_DSI_INIT_TIM 0x7
+/* TWAKEUP. */
+#define MIPI_DSI_WAKEUP_TIM 0x8
+/* when in RxULPS check state, after the the logic enable the analog,
+ * how long we should wait to check the lP state .
+ */
+#define MIPI_DSI_LPOK_TIM 0x9
+/* Watchdog for RX low power state no finished. */
+#define MIPI_DSI_LP_WCHDOG 0xa
+/* tMBIAS, after send power up signals to analog,
+ * how long we should wait for analog powered up.
+ */
+#define MIPI_DSI_ANA_CTRL 0xb
+/* [31:8] reserved for future.
+ * [7:0] tCLK_PRE.
+ */
+#define MIPI_DSI_CLK_TIM1 0xc
+/* watchdog for turn around waiting time. */
+#define MIPI_DSI_TURN_WCHDOG 0xd
+/* When in RxULPS state, how frequency we should to check
+ * if the TX side out of ULPS state.
+ */
+#define MIPI_DSI_ULPS_CHECK 0xe
+
+#define MIPI_DSI_TEST_CTRL0 0xf
+
+#define MIPI_DSI_TEST_CTRL1 0x10
+
+/* ***********************************************
+ * register access api
+ */
+
+extern int lcd_ioremap(void);
+extern unsigned int lcd_vcbus_read(unsigned int _reg);
+extern void lcd_vcbus_write(unsigned int _reg, unsigned int _value);
+extern void lcd_vcbus_setb(unsigned int reg, unsigned int value,
+ unsigned int _start, unsigned int _len);
+extern unsigned int lcd_vcbus_getb(unsigned int reg,
+ unsigned int _start, unsigned int _len);
+extern void lcd_vcbus_set_mask(unsigned int reg, unsigned int _mask);
+extern void lcd_vcbus_clr_mask(unsigned int reg, unsigned int _mask);
+
+extern unsigned int lcd_hiu_read(unsigned int _reg);
+extern void lcd_hiu_write(unsigned int _reg, unsigned int _value);
+extern void lcd_hiu_setb(unsigned int _reg, unsigned int _value,
+ unsigned int _start, unsigned int _len);
+extern unsigned int lcd_hiu_getb(unsigned int _reg,
+ unsigned int _start, unsigned int _len);
+extern void lcd_hiu_set_mask(unsigned int _reg, unsigned int _mask);
+extern void lcd_hiu_clr_mask(unsigned int _reg, unsigned int _mask);
+
+extern unsigned int lcd_cbus_read(unsigned int _reg);
+extern void lcd_cbus_write(unsigned int _reg, unsigned int _value);
+extern void lcd_cbus_setb(unsigned int _reg, unsigned int _value,
+ unsigned int _start, unsigned int _len);
+
+extern unsigned int lcd_periphs_read(unsigned int _reg);
+extern void lcd_periphs_write(unsigned int _reg, unsigned int _value);
+extern void lcd_pinmux_set_mask(unsigned int _reg, unsigned int _mask);
+extern void lcd_pinmux_clr_mask(unsigned int _reg, unsigned int _mask);
+
+extern unsigned int dsi_host_read(unsigned int _reg);
+extern void dsi_host_write(unsigned int _reg, unsigned int _value);
+extern void dsi_host_setb(unsigned int reg, unsigned int value,
+ unsigned int _start, unsigned int _len);
+extern unsigned int dsi_host_getb(unsigned int reg,
+ unsigned int _start, unsigned int _len);
+extern void dsi_host_set_mask(unsigned int reg, unsigned int _mask);
+extern void dsi_host_clr_mask(unsigned int reg, unsigned int _mask);
+extern unsigned int dsi_phy_read(unsigned int _reg);
+extern void dsi_phy_write(unsigned int _reg, unsigned int _value);
+extern void dsi_phy_setb(unsigned int reg, unsigned int value,
+ unsigned int _start, unsigned int _len);
+extern unsigned int dsi_phy_getb(unsigned int reg,
+ unsigned int _start, unsigned int _len);
+extern void dsi_phy_set_mask(unsigned int reg, unsigned int _mask);
+extern void dsi_phy_clr_mask(unsigned int reg, unsigned int _mask);
+
+#endif
+
+
--- /dev/null
+
+config AMLOGIC_LCD_GAMMA_DEBUG
+ bool "LCD Gamma debug support"
+ default n
+ help
+ Amlogic LCD gamma debug
--- /dev/null
+obj-y += lcd_tablet.o lcd_drv.o mipi_dsi_util.o
\ No newline at end of file
--- /dev/null
+/*
+ * 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>;
+ };
+};
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_drv.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#ifdef CONFIG_AML_VPU
+#include <linux/amlogic/vpu.h>
+#endif
+#include <linux/amlogic/media/vout/vinfo.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include "lcd_tablet.h"
+#include "mipi_dsi_util.h"
+#include "../lcd_reg.h"
+#include "../lcd_common.h"
+
+static int lcd_type_supported(struct lcd_config_s *pconf)
+{
+ int lcd_type = pconf->lcd_basic.lcd_type;
+ int ret = -1;
+
+ switch (lcd_type) {
+ case LCD_TTL:
+ case LCD_LVDS:
+ case LCD_VBYONE:
+ case LCD_MIPI:
+ ret = 0;
+ break;
+ default:
+ LCDERR("invalid lcd type: %s(%d)\n",
+ lcd_type_type_to_str(lcd_type), lcd_type);
+ break;
+ }
+ return ret;
+}
+
+static void lcd_lvds_phy_set(struct lcd_config_s *pconf, int status)
+{
+ unsigned int vswing, preem, clk_vswing, clk_preem, channel_on;
+ unsigned int data32;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s: %d\n", __func__, status);
+
+ if (status) {
+ vswing = pconf->lcd_control.lvds_config->phy_vswing;
+ preem = pconf->lcd_control.lvds_config->phy_preem;
+ clk_vswing = pconf->lcd_control.lvds_config->phy_clk_vswing;
+ clk_preem = pconf->lcd_control.lvds_config->phy_clk_preem;
+ if (vswing > 7) {
+ LCDERR("%s: wrong vswing_level=0x%x, use default\n",
+ __func__, vswing);
+ vswing = LVDS_PHY_VSWING_DFT;
+ }
+ if (preem > 7) {
+ LCDERR("%s: wrong preem_level=0x%x, use default\n",
+ __func__, preem);
+ preem = LVDS_PHY_PREEM_DFT;
+ }
+ if (clk_vswing > 7) {
+ LCDERR("%s: wrong clk_vswing_level=0x%x, use default\n",
+ __func__, clk_vswing);
+ clk_vswing = LVDS_PHY_CLK_VSWING_DFT;
+ }
+ if (clk_preem > 7) {
+ LCDERR("%s: wrong clk_preem_level=0x%x, use default\n",
+ __func__, clk_preem);
+ clk_preem = LVDS_PHY_CLK_PREEM_DFT;
+ }
+ channel_on = lcd_lvds_channel_on_value(pconf);
+
+ data32 = 0x606cca80 | (vswing << 26) | (preem << 0);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, data32);
+ /*lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x6c6cca80);*/
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x0000006c);
+ data32 = (channel_on << 16) | 0x0800 | /* DIF_TX_CTL5 */
+ (clk_vswing << 8) | (clk_preem << 5); /* DIF_TX_CTL4 */
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, data32);
+ /*lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x0fff0800);*/
+ } else {
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x0);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x0);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x0);
+ }
+}
+
+static void lcd_vbyone_phy_set(struct lcd_config_s *pconf, int status)
+{
+ unsigned int vswing, preem, ext_pullup;
+ unsigned int data32;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s: %d\n", __func__, status);
+
+ if (status) {
+ ext_pullup =
+ (pconf->lcd_control.vbyone_config->phy_vswing >> 4) & 1;
+ vswing = pconf->lcd_control.vbyone_config->phy_vswing & 0xf;
+ preem = pconf->lcd_control.vbyone_config->phy_preem;
+ if (vswing > 7) {
+ LCDERR("%s: wrong vswing_level=%d, use default\n",
+ __func__, vswing);
+ vswing = VX1_PHY_VSWING_DFT;
+ }
+ if (preem > 7) {
+ LCDERR("%s: wrong preemphasis_level=%d, use default\n",
+ __func__, preem);
+ preem = VX1_PHY_PREEM_DFT;
+ }
+ data32 = 0x6e0ec900 | (vswing << 3) | (ext_pullup << 10);
+ if (ext_pullup)
+ data32 &= ~(1 << 15);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, data32);
+ data32 = 0x00000a7c | (preem << 20);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, data32);
+ /*lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x6e0ec918);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x00000a7c);*/
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x00ff0800);
+ } else {
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x0);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x0);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x0);
+ }
+}
+
+static void lcd_mipi_phy_set(struct lcd_config_s *pconf, int status)
+{
+ unsigned int phy_reg, phy_bit, phy_width;
+ unsigned int lane_cnt;
+
+ if (status) {
+ /* HHI_MIPI_CNTL0 */
+ /* DIF_REF_CTL1:31-16bit, DIF_REF_CTL0:15-0bit */
+ lcd_hiu_setb(HHI_MIPI_CNTL0, 0x1b8, 16, 10);
+ lcd_hiu_setb(HHI_MIPI_CNTL0, 1, 26, 1); /* bandgap */
+ lcd_hiu_setb(HHI_MIPI_CNTL0, 1, 29, 1); /* current */
+ lcd_hiu_setb(HHI_MIPI_CNTL0, 1, 31, 1);
+ lcd_hiu_setb(HHI_MIPI_CNTL0, 0x8, 0, 16);
+
+ /* HHI_MIPI_CNTL1 */
+ /* DIF_REF_CTL2:15-0bit */
+ lcd_hiu_write(HHI_MIPI_CNTL1, (0x001e << 0));
+
+ /* HHI_MIPI_CNTL2 */
+ /* DIF_TX_CTL1:31-16bit, DIF_TX_CTL0:15-0bit */
+ lcd_hiu_write(HHI_MIPI_CNTL2, (0x26e0 << 16) | (0xfc59 << 0));
+
+ phy_reg = HHI_MIPI_CNTL2;
+ phy_bit = BIT_PHY_LANE_AXG;
+ phy_width = PHY_LANE_WIDTH_AXG;
+ switch (pconf->lcd_control.mipi_config->lane_num) {
+ case 1:
+ lane_cnt = DSI_LANE_COUNT_1;
+ break;
+ case 2:
+ lane_cnt = DSI_LANE_COUNT_2;
+ break;
+ case 3:
+ lane_cnt = DSI_LANE_COUNT_3;
+ break;
+ case 4:
+ lane_cnt = DSI_LANE_COUNT_4;
+ break;
+ default:
+ lane_cnt = 0;
+ break;
+ }
+ lcd_hiu_setb(phy_reg, lane_cnt, phy_bit, phy_width);
+ } else {
+ lcd_hiu_setb(HHI_MIPI_CNTL0, 0, 16, 10);
+ lcd_hiu_setb(HHI_MIPI_CNTL0, 0, 31, 1);
+ lcd_hiu_setb(HHI_MIPI_CNTL0, 0, 0, 16);
+ lcd_hiu_write(HHI_MIPI_CNTL1, 0x6);
+ lcd_hiu_write(HHI_MIPI_CNTL2, 0x00200000);
+ }
+}
+
+static void lcd_tcon_set(struct lcd_config_s *pconf)
+{
+ struct lcd_timing_s *tcon_adr = &pconf->lcd_timing;
+
+ lcd_vcbus_write(L_RGB_BASE_ADDR, 0);
+ lcd_vcbus_write(L_RGB_COEFF_ADDR, 0x400);
+ aml_lcd_notifier_call_chain(LCD_EVENT_GAMMA_UPDATE, NULL);
+
+ switch (pconf->lcd_basic.lcd_bits) {
+ case 6:
+ lcd_vcbus_write(L_DITH_CNTL_ADDR, 0x600);
+ break;
+ case 8:
+ lcd_vcbus_write(L_DITH_CNTL_ADDR, 0x400);
+ break;
+ case 10:
+ default:
+ lcd_vcbus_write(L_DITH_CNTL_ADDR, 0x0);
+ break;
+ }
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_LVDS:
+ lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 0, 1);
+ if (pconf->lcd_timing.vsync_pol)
+ lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 1, 1);
+ break;
+ case LCD_VBYONE:
+ if (pconf->lcd_timing.hsync_pol)
+ lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 0, 1);
+ if (pconf->lcd_timing.vsync_pol)
+ lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 1, 1);
+ break;
+ case LCD_MIPI:
+ /* lcd_vcbus_setb(L_POL_CNTL_ADDR, 3, 0, 2); */
+ break;
+ default:
+ break;
+ }
+
+ /* DE signal for TTL m8,m8m2 */
+ lcd_vcbus_write(L_OEH_HS_ADDR, tcon_adr->de_hs_addr);
+ lcd_vcbus_write(L_OEH_HE_ADDR, tcon_adr->de_he_addr);
+ lcd_vcbus_write(L_OEH_VS_ADDR, tcon_adr->de_vs_addr);
+ lcd_vcbus_write(L_OEH_VE_ADDR, tcon_adr->de_ve_addr);
+ /* DE signal for TTL m8b */
+ lcd_vcbus_write(L_OEV1_HS_ADDR, tcon_adr->de_hs_addr);
+ lcd_vcbus_write(L_OEV1_HE_ADDR, tcon_adr->de_he_addr);
+ lcd_vcbus_write(L_OEV1_VS_ADDR, tcon_adr->de_vs_addr);
+ lcd_vcbus_write(L_OEV1_VE_ADDR, tcon_adr->de_ve_addr);
+
+ /* Hsync signal for TTL m8,m8m2 */
+ if (tcon_adr->hsync_pol == 0) {
+ lcd_vcbus_write(L_STH1_HS_ADDR, tcon_adr->hs_he_addr);
+ lcd_vcbus_write(L_STH1_HE_ADDR, tcon_adr->hs_hs_addr);
+ } else {
+ lcd_vcbus_write(L_STH1_HS_ADDR, tcon_adr->hs_hs_addr);
+ lcd_vcbus_write(L_STH1_HE_ADDR, tcon_adr->hs_he_addr);
+ }
+ lcd_vcbus_write(L_STH1_VS_ADDR, tcon_adr->hs_vs_addr);
+ lcd_vcbus_write(L_STH1_VE_ADDR, tcon_adr->hs_ve_addr);
+
+ /* Vsync signal for TTL m8,m8m2 */
+ lcd_vcbus_write(L_STV1_HS_ADDR, tcon_adr->vs_hs_addr);
+ lcd_vcbus_write(L_STV1_HE_ADDR, tcon_adr->vs_he_addr);
+ if (tcon_adr->vsync_pol == 0) {
+ lcd_vcbus_write(L_STV1_VS_ADDR, tcon_adr->vs_ve_addr);
+ lcd_vcbus_write(L_STV1_VE_ADDR, tcon_adr->vs_vs_addr);
+ } else {
+ lcd_vcbus_write(L_STV1_VS_ADDR, tcon_adr->vs_vs_addr);
+ lcd_vcbus_write(L_STV1_VE_ADDR, tcon_adr->vs_ve_addr);
+ }
+
+ /* DE signal */
+ lcd_vcbus_write(L_DE_HS_ADDR, tcon_adr->de_hs_addr);
+ lcd_vcbus_write(L_DE_HE_ADDR, tcon_adr->de_he_addr);
+ lcd_vcbus_write(L_DE_VS_ADDR, tcon_adr->de_vs_addr);
+ lcd_vcbus_write(L_DE_VE_ADDR, tcon_adr->de_ve_addr);
+
+ /* Hsync signal */
+ lcd_vcbus_write(L_HSYNC_HS_ADDR, tcon_adr->hs_hs_addr);
+ lcd_vcbus_write(L_HSYNC_HE_ADDR, tcon_adr->hs_he_addr);
+ lcd_vcbus_write(L_HSYNC_VS_ADDR, tcon_adr->hs_vs_addr);
+ lcd_vcbus_write(L_HSYNC_VE_ADDR, tcon_adr->hs_ve_addr);
+
+ /* Vsync signal */
+ lcd_vcbus_write(L_VSYNC_HS_ADDR, tcon_adr->vs_hs_addr);
+ lcd_vcbus_write(L_VSYNC_HE_ADDR, tcon_adr->vs_he_addr);
+ lcd_vcbus_write(L_VSYNC_VS_ADDR, tcon_adr->vs_vs_addr);
+ lcd_vcbus_write(L_VSYNC_VE_ADDR, tcon_adr->vs_ve_addr);
+
+ lcd_vcbus_write(L_INV_CNT_ADDR, 0);
+ lcd_vcbus_write(L_TCON_MISC_SEL_ADDR,
+ ((1 << STV1_SEL) | (1 << STV2_SEL)));
+
+ if (lcd_vcbus_read(VPP_MISC) & VPP_OUT_SATURATE)
+ lcd_vcbus_write(VPP_MISC,
+ lcd_vcbus_read(VPP_MISC) & ~(VPP_OUT_SATURATE));
+}
+
+static void lcd_ttl_control_set(struct lcd_config_s *pconf)
+{
+ unsigned int clk_pol, rb_swap, bit_swap;
+
+ clk_pol = pconf->lcd_control.ttl_config->clk_pol;
+ rb_swap = (pconf->lcd_control.ttl_config->swap_ctrl >> 1) & 1;
+ bit_swap = (pconf->lcd_control.ttl_config->swap_ctrl >> 0) & 1;
+
+ lcd_vcbus_setb(L_POL_CNTL_ADDR, clk_pol, 6, 1);
+ lcd_vcbus_setb(L_DUAL_PORT_CNTL_ADDR, rb_swap, 1, 1);
+ lcd_vcbus_setb(L_DUAL_PORT_CNTL_ADDR, bit_swap, 0, 1);
+}
+
+static void lcd_lvds_clk_util_set(struct lcd_config_s *pconf)
+{
+ unsigned int phy_div;
+
+ if (pconf->lcd_control.lvds_config->dual_port)
+ phy_div = 2;
+ else
+ phy_div = 1;
+
+ /* set fifo_clk_sel: div 7 */
+ lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL0, (1 << 6));
+ /* set cntl_ser_en: 8-channel to 1 */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0xfff, 16, 12);
+
+ /* decoupling fifo enable, gated clock enable */
+ lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL1,
+ (1 << 30) | ((phy_div - 1) << 25) | (1 << 24));
+ /* decoupling fifo write enable after fifo enable */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL1, 1, 31, 1);
+}
+
+static void lcd_lvds_control_set(struct lcd_config_s *pconf)
+{
+ unsigned int bit_num = 1;
+ unsigned int pn_swap, port_swap, lane_reverse;
+ unsigned int dual_port, fifo_mode;
+ unsigned int lvds_repack = 1;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ lcd_lvds_clk_util_set(pconf);
+
+ lvds_repack = (pconf->lcd_control.lvds_config->lvds_repack) & 0x3;
+ pn_swap = (pconf->lcd_control.lvds_config->pn_swap) & 0x1;
+ dual_port = (pconf->lcd_control.lvds_config->dual_port) & 0x1;
+ port_swap = (pconf->lcd_control.lvds_config->port_swap) & 0x1;
+ lane_reverse = (pconf->lcd_control.lvds_config->lane_reverse) & 0x1;
+
+ switch (pconf->lcd_basic.lcd_bits) {
+ case 10:
+ bit_num = 0;
+ if (lvds_repack == 1)
+ lvds_repack = 2;
+ break;
+ case 8:
+ bit_num = 1;
+ break;
+ case 6:
+ bit_num = 2;
+ break;
+ case 4:
+ bit_num = 3;
+ break;
+ default:
+ bit_num = 1;
+ break;
+ }
+ if (dual_port)
+ fifo_mode = 0x3;
+ else
+ fifo_mode = 0x1;
+
+ lcd_vcbus_write(LVDS_PACK_CNTL_ADDR,
+ (lvds_repack << 0) | /* repack //[1:0] */
+ (0 << 3) | /* reserve */
+ (0 << 4) | /* lsb first */
+ (pn_swap << 5) | /* pn swap */
+ (dual_port << 6) | /* dual port */
+ (0 << 7) | /* use tcon control */
+ (bit_num << 8) | /* 0:10bits, 1:8bits, 2:6bits, 3:4bits */
+ (0 << 10) | /* r_select //0:R, 1:G, 2:B, 3:0 */
+ (1 << 12) | /* g_select //0:R, 1:G, 2:B, 3:0 */
+ (2 << 14)); /* b_select //0:R, 1:G, 2:B, 3:0 */
+
+ lcd_vcbus_setb(LCD_PORT_SWAP, port_swap, 12, 1);
+
+ if (lane_reverse)
+ lcd_vcbus_setb(LVDS_GEN_CNTL, 0x03, 13, 2);
+
+ lcd_vcbus_write(LVDS_GEN_CNTL,
+ (lcd_vcbus_read(LVDS_GEN_CNTL) |
+ (1 << 4) | (fifo_mode << 0)));
+
+ lcd_vcbus_setb(LVDS_GEN_CNTL, 1, 3, 1);
+}
+
+static void lcd_lvds_disable(void)
+{
+ lcd_vcbus_setb(LVDS_GEN_CNTL, 0, 3, 1); /* disable lvds fifo */
+}
+
+static void lcd_venc_set(struct lcd_config_s *pconf)
+{
+ unsigned int h_active, v_active;
+ unsigned int video_on_pixel, video_on_line;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+ h_active = pconf->lcd_basic.h_active;
+ v_active = pconf->lcd_basic.v_active;
+ video_on_pixel = pconf->lcd_timing.video_on_pixel;
+ video_on_line = pconf->lcd_timing.video_on_line;
+
+ lcd_vcbus_write(ENCL_VIDEO_EN, 0);
+
+ lcd_vcbus_write(VPU_VIU_VENC_MUX_CTRL, ((0 << 0) | (0 << 2)));
+ lcd_vcbus_write(ENCL_VIDEO_MODE, 0);
+ lcd_vcbus_write(ENCL_VIDEO_MODE_ADV, 0x0418); /* Sampling rate: 1 */
+
+ lcd_vcbus_write(ENCL_VIDEO_FILT_CTRL, 0x1000); /* bypass filter */
+ lcd_vcbus_write(ENCL_VIDEO_MAX_PXCNT, pconf->lcd_basic.h_period - 1);
+ lcd_vcbus_write(ENCL_VIDEO_MAX_LNCNT, pconf->lcd_basic.v_period - 1);
+ lcd_vcbus_write(ENCL_VIDEO_HAVON_BEGIN, video_on_pixel);
+ lcd_vcbus_write(ENCL_VIDEO_HAVON_END, h_active - 1 + video_on_pixel);
+ lcd_vcbus_write(ENCL_VIDEO_VAVON_BLINE, video_on_line);
+ lcd_vcbus_write(ENCL_VIDEO_VAVON_ELINE, v_active - 1 + video_on_line);
+
+ lcd_vcbus_write(ENCL_VIDEO_HSO_BEGIN, pconf->lcd_timing.hs_hs_addr);
+ lcd_vcbus_write(ENCL_VIDEO_HSO_END, pconf->lcd_timing.hs_he_addr);
+ lcd_vcbus_write(ENCL_VIDEO_VSO_BEGIN, pconf->lcd_timing.vs_hs_addr);
+ lcd_vcbus_write(ENCL_VIDEO_VSO_END, pconf->lcd_timing.vs_he_addr);
+ lcd_vcbus_write(ENCL_VIDEO_VSO_BLINE, pconf->lcd_timing.vs_vs_addr);
+ lcd_vcbus_write(ENCL_VIDEO_VSO_ELINE, pconf->lcd_timing.vs_ve_addr);
+ lcd_vcbus_write(ENCL_VIDEO_RGBIN_CTRL, 3);
+
+ lcd_vcbus_write(ENCL_VIDEO_EN, 1);
+
+ aml_lcd_notifier_call_chain(LCD_EVENT_BACKLIGHT_UPDATE, NULL);
+}
+
+static void lcd_vbyone_sync_pol(int hsync_pol, int vsync_pol)
+{
+ lcd_vcbus_setb(VBO_VIN_CTRL, hsync_pol, 4, 1);
+ lcd_vcbus_setb(VBO_VIN_CTRL, vsync_pol, 5, 1);
+
+ lcd_vcbus_setb(VBO_VIN_CTRL, hsync_pol, 6, 1);
+ lcd_vcbus_setb(VBO_VIN_CTRL, vsync_pol, 7, 1);
+}
+
+static void lcd_vbyone_clk_util_set(struct lcd_config_s *pconf)
+{
+ unsigned int lcd_bits;
+ unsigned int div_sel, phy_div;
+
+ phy_div = pconf->lcd_control.vbyone_config->phy_div;
+
+ lcd_bits = 10;
+ switch (lcd_bits) {
+ case 6:
+ div_sel = 0;
+ break;
+ case 8:
+ div_sel = 2;
+ break;
+ case 10:
+ div_sel = 3;
+ break;
+ default:
+ div_sel = 3;
+ break;
+ }
+ /* set fifo_clk_sel */
+ lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL0, (div_sel << 6));
+ /* set cntl_ser_en: 8-channel to 1 */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0xfff, 16, 12);
+
+ /* decoupling fifo enable, gated clock enable */
+ lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL1,
+ (1 << 30) | ((phy_div - 1) << 25) | (1 << 24));
+ /* decoupling fifo write enable after fifo enable */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL1, 1, 31, 1);
+}
+
+static int lcd_vbyone_lanes_set(int lane_num, int byte_mode, int region_num,
+ int hsize, int vsize)
+{
+ int sublane_num;
+ int region_size[4];
+ int tmp;
+
+ switch (lane_num) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+ default:
+ return -1;
+ }
+ switch (region_num) {
+ case 1:
+ case 2:
+ case 4:
+ break;
+ default:
+ return -1;
+ }
+ if (lane_num % region_num)
+ return -1;
+ switch (byte_mode) {
+ case 3:
+ case 4:
+ break;
+ default:
+ return -1;
+ }
+ if (lcd_debug_print_flag) {
+ LCDPR("byte_mode=%d, lane_num=%d, region_num=%d\n",
+ byte_mode, lane_num, region_num);
+ }
+
+ sublane_num = lane_num / region_num; /* lane num in each region */
+ lcd_vcbus_setb(VBO_LANES, (lane_num - 1), 0, 3);
+ lcd_vcbus_setb(VBO_LANES, (region_num - 1), 4, 2);
+ lcd_vcbus_setb(VBO_LANES, (sublane_num - 1), 8, 3);
+ lcd_vcbus_setb(VBO_LANES, (byte_mode - 1), 11, 2);
+
+ if (region_num > 1) {
+ region_size[3] = (hsize / lane_num) * sublane_num;
+ tmp = (hsize % lane_num);
+ region_size[0] = region_size[3] + (((tmp / sublane_num) > 0) ?
+ sublane_num : (tmp % sublane_num));
+ region_size[1] = region_size[3] + (((tmp / sublane_num) > 1) ?
+ sublane_num : (tmp % sublane_num));
+ region_size[2] = region_size[3] + (((tmp / sublane_num) > 2) ?
+ sublane_num : (tmp % sublane_num));
+ lcd_vcbus_write(VBO_REGION_00, region_size[0]);
+ lcd_vcbus_write(VBO_REGION_01, region_size[1]);
+ lcd_vcbus_write(VBO_REGION_02, region_size[2]);
+ lcd_vcbus_write(VBO_REGION_03, region_size[3]);
+ }
+ lcd_vcbus_write(VBO_ACT_VSIZE, vsize);
+ /* different from FBC code!!! */
+ /* lcd_vcbus_setb(VBO_CTRL_H,0x80,11,5); */
+ /* different from simulation code!!! */
+ lcd_vcbus_setb(VBO_CTRL_H, 0x0, 0, 4);
+ lcd_vcbus_setb(VBO_CTRL_H, 0x1, 9, 1);
+ /* lcd_vcbus_setb(VBO_CTRL_L,enable,0,1); */
+
+ return 0;
+}
+
+static void lcd_vbyone_sw_reset(void)
+{
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ /* force PHY to 0 */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2);
+ lcd_vcbus_write(VBO_SOFT_RST, 0x1ff);
+ udelay(5);
+ /* realease PHY */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2);
+ lcd_vcbus_write(VBO_SOFT_RST, 0);
+}
+
+static void lcd_vbyone_wait_timing_stable(void)
+{
+ unsigned int timing_state;
+ int i = 200;
+
+ timing_state = lcd_vcbus_read(VBO_INTR_STATE) & 0x1ff;
+ while ((timing_state) && (i > 0)) {
+ /* clear video timing error intr */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x7, 0, 3);
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 3);
+ mdelay(2);
+ timing_state = lcd_vcbus_read(VBO_INTR_STATE) & 0x1ff;
+ i--;
+ };
+ if (lcd_debug_print_flag) {
+ LCDPR("vbyone timing state: 0x%03x, i=%d\n",
+ timing_state, (200 - i));
+ }
+ mdelay(2);
+}
+
+static void lcd_vbyone_control_set(struct lcd_config_s *pconf)
+{
+ int lane_count, byte_mode, region_num, hsize, vsize, color_fmt;
+ int vin_color, vin_bpp;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ hsize = pconf->lcd_basic.h_active;
+ vsize = pconf->lcd_basic.v_active;
+ lane_count = pconf->lcd_control.vbyone_config->lane_count; /* 8 */
+ region_num = pconf->lcd_control.vbyone_config->region_num; /* 2 */
+ byte_mode = pconf->lcd_control.vbyone_config->byte_mode; /* 4 */
+ color_fmt = pconf->lcd_control.vbyone_config->color_fmt; /* 4 */
+
+ lcd_vbyone_clk_util_set(pconf);
+#if 0
+ switch (color_fmt) {
+ case 0:/* SDVT_VBYONE_18BPP_RGB */
+ vin_color = 4;
+ vin_bpp = 2;
+ break;
+ case 1:/* SDVT_VBYONE_18BPP_YCBCR444 */
+ vin_color = 0;
+ vin_bpp = 2;
+ break;
+ case 2:/* SDVT_VBYONE_24BPP_RGB */
+ vin_color = 4;
+ vin_bpp = 1;
+ break;
+ case 3:/* SDVT_VBYONE_24BPP_YCBCR444 */
+ vin_color = 0;
+ vin_bpp = 1;
+ break;
+ case 4:/* SDVT_VBYONE_30BPP_RGB */
+ vin_color = 4;
+ vin_bpp = 0;
+ break;
+ case 5:/* SDVT_VBYONE_30BPP_YCBCR444 */
+ vin_color = 0;
+ vin_bpp = 0;
+ break;
+ default:
+ LCDERR("vbyone COLOR_FORMAT unsupport\n");
+ return;
+ }
+#else
+ vin_color = 4; /* fixed RGB */
+ vin_bpp = 0; /* fixed 30bbp 4:4:4 */
+#endif
+
+ /* set Vbyone vin color format */
+ lcd_vcbus_setb(VBO_VIN_CTRL, vin_color, 8, 3);
+ lcd_vcbus_setb(VBO_VIN_CTRL, vin_bpp, 11, 2);
+
+ lcd_vbyone_lanes_set(lane_count, byte_mode, region_num, hsize, vsize);
+ /*set hsync/vsync polarity to let the polarity is low active
+ inside the VbyOne */
+ lcd_vbyone_sync_pol(0, 0);
+
+ /* below line copy from simulation */
+ /* gate the input when vsync asserted */
+ lcd_vcbus_setb(VBO_VIN_CTRL, 1, 0, 2);
+ /* lcd_vcbus_write(VBO_VBK_CTRL_0,0x13);
+ //lcd_vcbus_write(VBO_VBK_CTRL_1,0x56);
+ //lcd_vcbus_write(VBO_HBK_CTRL,0x3478);
+ //lcd_vcbus_setb(VBO_PXL_CTRL,0x2,0,4);
+ //lcd_vcbus_setb(VBO_PXL_CTRL,0x3,VBO_PXL_CTR1_BIT,VBO_PXL_CTR1_WID);
+ //set_vbyone_ctlbits(1,0,0); */
+
+ /* PAD select: */
+ if ((lane_count == 1) || (lane_count == 2))
+ lcd_vcbus_setb(LCD_PORT_SWAP, 1, 9, 2);
+ else if (lane_count == 4)
+ lcd_vcbus_setb(LCD_PORT_SWAP, 2, 9, 2);
+ else
+ lcd_vcbus_setb(LCD_PORT_SWAP, 0, 9, 2);
+ /* lcd_vcbus_setb(LCD_PORT_SWAP, 1, 8, 1);//reverse lane output order */
+
+ /* Mux pads in combo-phy: 0 for dsi; 1 for lvds or vbyone; 2 for edp */
+ lcd_hiu_write(HHI_DSI_LVDS_EDP_CNTL0, 0x1);
+ lcd_vcbus_setb(VBO_CTRL_L, 1, 0, 1);
+
+ /*force vencl clk enable, otherwise, it might auto turn off by mipi DSI
+ //lcd_vcbus_setb(VPU_MISC_CTRL, 1, 0, 1); */
+
+ lcd_vbyone_wait_timing_stable();
+ lcd_vbyone_sw_reset();
+}
+
+static void lcd_vbyone_disable(void)
+{
+ lcd_vcbus_setb(VBO_CTRL_L, 0, 0, 1);
+}
+
+static void lcd_tablet_vbyone_wait_stable(void)
+{
+ int i = 5000;
+
+ while (((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) != 0x20) && (i > 0)) {
+ udelay(50);
+ i--;
+ }
+ LCDPR("%s status: 0x%x, i=%d\n",
+ __func__, lcd_vcbus_read(VBO_STATUS_L), (5000 - i));
+}
+
+static void lcd_vx1_wait_hpd(void)
+{
+ int i = 0;
+
+ if (lcd_debug_print_flag)
+ LCDPR("vx1 wait hpd to low ...\n");
+
+ while (lcd_vcbus_read(VBO_STATUS_L) & 0x40) {
+ if (i++ >= 10000)
+ break;
+ udelay(50);
+ }
+ mdelay(10);
+ if (lcd_vcbus_read(VBO_STATUS_L) & 0x40)
+ LCDPR("%s: hpd=%d\n", __func__,
+ ((lcd_vcbus_read(VBO_STATUS_L) >> 6) & 0x1));
+ else
+ LCDPR("%s: hpd=%d, i=%d\n", __func__,
+ ((lcd_vcbus_read(VBO_STATUS_L) >> 6) & 0x1), i);
+}
+
+static unsigned int vbyone_lane_num[] = {
+ 1,
+ 2,
+ 4,
+ 8,
+ 8,
+};
+
+#define VBYONE_BIT_RATE_MAX 3100 /* MHz */
+#define VBYONE_BIT_RATE_MIN 600
+static void lcd_vbyone_config_set(struct lcd_config_s *pconf)
+{
+ unsigned int band_width, bit_rate, pclk, phy_div;
+ unsigned int byte_mode, lane_count, minlane;
+ unsigned int lcd_bits;
+ unsigned int temp, i;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ /* auto calculate bandwidth, clock */
+ lane_count = pconf->lcd_control.vbyone_config->lane_count;
+ lcd_bits = 10; /* pconf->lcd_basic.lcd_bits */
+ byte_mode = (lcd_bits == 10) ? 4 : 3;
+ /* byte_mode * byte2bit * 8/10_encoding * pclk =
+ byte_mode * 8 * 10 / 8 * pclk */
+ pclk = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+ band_width = byte_mode * 10 * pclk;
+
+ temp = VBYONE_BIT_RATE_MAX * 1000;
+ temp = (band_width + temp - 1) / temp;
+ for (i = 0; i < 4; i++) {
+ if (temp <= vbyone_lane_num[i])
+ break;
+ }
+ minlane = vbyone_lane_num[i];
+ if (lane_count < minlane) {
+ LCDERR("vbyone lane_num(%d) is less than min(%d)\n",
+ lane_count, minlane);
+ lane_count = minlane;
+ pconf->lcd_control.vbyone_config->lane_count = lane_count;
+ LCDPR("change to min lane_num %d\n", minlane);
+ }
+
+ bit_rate = band_width / minlane;
+ phy_div = lane_count / minlane;
+ if (phy_div == 8) {
+ phy_div /= 2;
+ bit_rate /= 2;
+ }
+ if (bit_rate > (VBYONE_BIT_RATE_MAX * 1000)) {
+ LCDERR("vbyone bit rate(%dKHz) is out of max(%dKHz)\n",
+ bit_rate, (VBYONE_BIT_RATE_MAX * 1000));
+ }
+ if (bit_rate < (VBYONE_BIT_RATE_MIN * 1000)) {
+ LCDERR("vbyone bit rate(%dKHz) is out of min(%dKHz)\n",
+ bit_rate, (VBYONE_BIT_RATE_MIN * 1000));
+ }
+ bit_rate = bit_rate * 1000; /* Hz */
+
+ pconf->lcd_control.vbyone_config->phy_div = phy_div;
+ pconf->lcd_control.vbyone_config->bit_rate = bit_rate;
+
+ if (lcd_debug_print_flag) {
+ LCDPR("lane_count=%u, bit_rate = %uMHz, pclk=%u.%03uMhz\n",
+ lane_count, (bit_rate / 1000000),
+ (pclk / 1000), (pclk % 1000));
+ }
+}
+
+void lcd_tablet_clk_update(struct lcd_config_s *pconf)
+{
+#ifdef CONFIG_AML_VPU
+ request_vpu_clk_vmod(pconf->lcd_timing.lcd_clk, VPU_VENCL);
+#endif
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_VBYONE:
+ lcd_vbyone_config_set(pconf);
+ break;
+ case LCD_MIPI:
+ lcd_mipi_dsi_config_set(pconf);
+ break;
+ default:
+ break;
+ }
+
+ lcd_clk_generate_parameter(pconf);
+ lcd_clk_set(pconf);
+ if (pconf->lcd_basic.lcd_type == LCD_VBYONE)
+ lcd_tablet_vbyone_wait_stable();
+}
+
+void lcd_tablet_config_update(struct lcd_config_s *pconf)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct vinfo_s *info;
+
+ /* update lcd config sync_duration */
+ info = lcd_drv->lcd_info;
+ pconf->lcd_timing.sync_duration_num = info->sync_duration_num;
+ pconf->lcd_timing.sync_duration_den = info->sync_duration_den;
+
+ /* update clk & timing config */
+ lcd_vmode_change(pconf);
+ info->video_clk = pconf->lcd_timing.lcd_clk;
+ /* update interface timing */
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_VBYONE:
+ lcd_vbyone_config_set(pconf);
+ break;
+ case LCD_MIPI:
+ lcd_mipi_dsi_config_set(pconf);
+ break;
+ default:
+ break;
+ }
+}
+
+void lcd_tablet_config_post_update(struct lcd_config_s *pconf)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ /* update interface timing */
+ switch (lcd_drv->lcd_config->lcd_basic.lcd_type) {
+ case LCD_MIPI:
+ lcd_mipi_dsi_config_post(lcd_drv->lcd_config);
+ break;
+ default:
+ break;
+ }
+}
+
+void lcd_tablet_driver_init_pre(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+ int ret;
+
+ pconf = lcd_drv->lcd_config;
+ LCDPR("tablet driver init(ver %s): %s\n", lcd_drv->version,
+ lcd_type_type_to_str(pconf->lcd_basic.lcd_type));
+ ret = lcd_type_supported(pconf);
+ if (ret)
+ return;
+
+ lcd_tablet_config_update(pconf);
+ lcd_tablet_config_post_update(pconf);
+#ifdef CONFIG_AML_VPU
+ request_vpu_clk_vmod(pconf->lcd_timing.lcd_clk, VPU_VENCL);
+ switch_vpu_mem_pd_vmod(VPU_VENCL, VPU_MEM_POWER_ON);
+#endif
+ lcd_clk_gate_switch(1);
+
+ lcd_clk_set(pconf);
+ lcd_venc_set(pconf);
+ lcd_tcon_set(pconf);
+ lcd_drv->lcd_test_pattern_restore();
+}
+
+int lcd_tablet_driver_init(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+ int ret;
+
+ pconf = lcd_drv->lcd_config;
+ ret = lcd_type_supported(pconf);
+ if (ret)
+ return -1;
+
+ /* init driver */
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_TTL:
+ lcd_ttl_control_set(pconf);
+ lcd_ttl_pinmux_set(1);
+ break;
+ case LCD_LVDS:
+ lcd_lvds_control_set(pconf);
+ lcd_lvds_phy_set(pconf, 1);
+ break;
+ case LCD_VBYONE:
+ lcd_vbyone_pinmux_set(1);
+ lcd_vbyone_control_set(pconf);
+ lcd_vx1_wait_hpd();
+ lcd_vbyone_phy_set(pconf, 1);
+ lcd_tablet_vbyone_wait_stable();
+ case LCD_MIPI:
+ lcd_mipi_phy_set(pconf, 1);
+ lcd_mipi_control_set(pconf, 1);
+ break;
+ default:
+ break;
+ }
+
+ lcd_vcbus_write(VENC_INTCTRL, 0x200);
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s finished\n", __func__);
+
+ return 0;
+}
+
+void lcd_tablet_driver_disable(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+ int ret;
+
+ LCDPR("disable driver\n");
+ pconf = lcd_drv->lcd_config;
+ ret = lcd_type_supported(pconf);
+ if (ret)
+ return;
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_TTL:
+ lcd_ttl_pinmux_set(0);
+ break;
+ case LCD_LVDS:
+ lcd_lvds_phy_set(pconf, 0);
+ lcd_lvds_disable();
+ break;
+ case LCD_VBYONE:
+ lcd_vbyone_phy_set(pconf, 0);
+ lcd_vbyone_pinmux_set(0);
+ lcd_vbyone_disable();
+ case LCD_MIPI:
+ mipi_dsi_link_off(pconf);
+ lcd_mipi_phy_set(pconf, 0);
+ lcd_mipi_control_set(pconf, 0);
+ break;
+ default:
+ break;
+ }
+
+ lcd_vcbus_write(ENCL_VIDEO_EN, 0); /* disable encl */
+
+ lcd_clk_disable();
+ lcd_clk_gate_switch(0);
+#ifdef CONFIG_AML_VPU
+ switch_vpu_mem_pd_vmod(VPU_VENCL, VPU_MEM_POWER_DOWN);
+ release_vpu_clk_vmod(VPU_VENCL);
+#endif
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s finished\n", __func__);
+}
+
+void lcd_tablet_driver_tiny_enable(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+ int ret;
+
+ pconf = lcd_drv->lcd_config;
+ ret = lcd_type_supported(pconf);
+ if (ret)
+ return;
+
+ /* init driver */
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_TTL:
+ lcd_ttl_control_set(pconf);
+ lcd_ttl_pinmux_set(1);
+ break;
+ case LCD_LVDS:
+ lcd_lvds_control_set(pconf);
+ lcd_lvds_phy_set(pconf, 1);
+ break;
+ case LCD_VBYONE:
+ lcd_vbyone_pinmux_set(1);
+ lcd_vbyone_control_set(pconf);
+ lcd_vx1_wait_hpd();
+ lcd_vbyone_phy_set(pconf, 1);
+ lcd_tablet_vbyone_wait_stable();
+ case LCD_MIPI:
+ lcd_mipi_phy_set(pconf, 1);
+ lcd_mipi_control_set(pconf, 1);
+ break;
+ default:
+ break;
+ }
+
+ LCDPR("enable driver\n");
+}
+
+void lcd_tablet_driver_tiny_disable(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+ int ret;
+
+ LCDPR("disable driver\n");
+ pconf = lcd_drv->lcd_config;
+ ret = lcd_type_supported(pconf);
+ if (ret)
+ return;
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_TTL:
+ lcd_ttl_pinmux_set(0);
+ break;
+ case LCD_LVDS:
+ lcd_lvds_phy_set(pconf, 0);
+ lcd_lvds_disable();
+ break;
+ case LCD_VBYONE:
+ lcd_vbyone_phy_set(pconf, 0);
+ lcd_vbyone_pinmux_set(0);
+ lcd_vbyone_disable();
+ case LCD_MIPI:
+ mipi_dsi_link_off(pconf);
+ lcd_mipi_phy_set(pconf, 0);
+ lcd_mipi_control_set(pconf, 0);
+ break;
+ default:
+ break;
+ }
+}
+
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tablet/lcd_tablet.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#ifdef CONFIG_AML_VPU
+#include <linux/amlogic/vpu.h>
+#endif
+#include <linux/amlogic/media/vout/vinfo.h>
+#include <linux/amlogic/media/vout/vout_notify.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include "lcd_tablet.h"
+#include "../lcd_reg.h"
+#include "../lcd_common.h"
+#include "mipi_dsi_util.h"
+
+#define PANEL_NAME "panel"
+
+/* **************************************************
+ * vout server api
+ * **************************************************
+ */
+static enum vmode_e lcd_validate_vmode(char *mode)
+{
+ if (mode == NULL)
+ return VMODE_MAX;
+
+ if ((strncmp(mode, PANEL_NAME, strlen(PANEL_NAME))) == 0)
+ return VMODE_LCD;
+
+ return VMODE_MAX;
+}
+
+static struct vinfo_s *lcd_get_current_info(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ return lcd_drv->lcd_info;
+}
+
+static int lcd_set_current_vmode(enum vmode_e mode)
+{
+ int ret = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ mutex_lock(&lcd_vout_mutex);
+
+ if (!(mode & VMODE_INIT_BIT_MASK)) {
+ if (VMODE_LCD == (mode & VMODE_MODE_BIT_MASK)) {
+ lcd_drv->driver_init_pre();
+ ret = lcd_drv->driver_init();
+ } else {
+ ret = -EINVAL;
+ }
+ }
+
+ lcd_vcbus_write(VPP_POSTBLEND_H_SIZE, lcd_drv->lcd_info->width);
+
+ mutex_unlock(&lcd_vout_mutex);
+ return ret;
+}
+
+static int lcd_vmode_is_supported(enum vmode_e mode)
+{
+ mode &= VMODE_MODE_BIT_MASK;
+ if (lcd_debug_print_flag)
+ LCDPR("%s vmode = %d\n", __func__, mode);
+
+ if (mode == VMODE_LCD)
+ return true;
+ return false;
+}
+
+static int lcd_vout_disable(enum vmode_e cur_vmod)
+{
+ aml_lcd_notifier_call_chain(LCD_EVENT_IF_POWER_OFF, NULL);
+ LCDPR("%s finished\n", __func__);
+ return 0;
+}
+
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+struct lcd_vframe_match_s {
+ int fps;
+ int frame_rate; /* *100 */
+ unsigned int duration_num;
+ unsigned int duration_den;
+};
+
+static struct lcd_vframe_match_s lcd_vframe_match_table_1[] = {
+ {5000, 5000, 50, 1},
+ {2500, 5000, 50, 1},
+ {6000, 6000, 60, 1},
+ {3000, 6000, 60, 1},
+ {2400, 6000, 60, 1},
+ {2397, 5994, 5994, 100},
+ {2997, 5994, 5994, 100},
+ {5994, 5994, 5994, 100},
+};
+
+static struct lcd_vframe_match_s lcd_vframe_match_table_2[] = {
+ {5000, 5000, 50, 1},
+ {2500, 5000, 50, 1},
+ {6000, 6000, 60, 1},
+ {3000, 6000, 60, 1},
+ {2400, 4800, 48, 1},
+ {2397, 5994, 5994, 100},
+ {2997, 5994, 5994, 100},
+ {5994, 5994, 5994, 100},
+};
+
+static int lcd_framerate_automation_set_mode(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ /* update lcd config sync_duration, for calculate */
+ lcd_drv->lcd_config->lcd_timing.sync_duration_num =
+ lcd_drv->lcd_info->sync_duration_num;
+ lcd_drv->lcd_config->lcd_timing.sync_duration_den =
+ lcd_drv->lcd_info->sync_duration_den;
+
+ /* update clk & timing config */
+ lcd_tablet_config_update(lcd_drv->lcd_config);
+ /* update interface timing if needed, current no need */
+#ifdef CONFIG_AML_VPU
+ request_vpu_clk_vmod(
+ lcd_drv->lcd_config->lcd_timing.lcd_clk, VPU_VENCL);
+#endif
+
+ /* change clk parameter */
+ switch (lcd_drv->lcd_config->lcd_timing.clk_change) {
+ case LCD_CLK_PLL_CHANGE:
+ lcd_clk_generate_parameter(lcd_drv->lcd_config);
+ lcd_clk_set(lcd_drv->lcd_config);
+ break;
+ case LCD_CLK_FRAC_UPDATE:
+ lcd_clk_update(lcd_drv->lcd_config);
+ break;
+ default:
+ break;
+ }
+ lcd_tablet_config_post_update(lcd_drv->lcd_config);
+ lcd_venc_change(lcd_drv->lcd_config);
+
+ vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE,
+ &lcd_drv->lcd_info->mode);
+
+ return 0;
+}
+#endif
+
+static int lcd_set_vframe_rate_hint(int duration)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct vinfo_s *info;
+ int fr_policy;
+ unsigned int frame_rate = 6000;
+ unsigned int duration_num = 60, duration_den = 1;
+ struct lcd_vframe_match_s *vtable = lcd_vframe_match_table_1;
+ int fps, i, n;
+
+ if (lcd_drv->lcd_status == 0) {
+ LCDPR("%s: lcd is disabled, exit\n", __func__);
+ return 0;
+ }
+
+ info = lcd_drv->lcd_info;
+
+ fr_policy = lcd_drv->fr_auto_policy;
+ switch (fr_policy) {
+ case 1:
+ vtable = lcd_vframe_match_table_1;
+ n = ARRAY_SIZE(lcd_vframe_match_table_1);
+ break;
+ case 2:
+ vtable = lcd_vframe_match_table_2;
+ n = ARRAY_SIZE(lcd_vframe_match_table_2);
+ break;
+ default:
+ LCDPR("%s: fr_auto_policy = %d, disabled\n",
+ __func__, fr_policy);
+ return 0;
+ }
+ fps = get_vsource_fps(duration);
+ for (i = 0; i < n; i++) {
+ if (fps == vtable[i].fps) {
+ frame_rate = vtable[i].frame_rate;
+ duration_num = vtable[i].duration_num;
+ duration_den = vtable[i].duration_den;
+ }
+ }
+ LCDPR("%s: policy = %d, duration = %d, fps = %d, frame_rate = %d\n",
+ __func__, fr_policy, duration, fps, frame_rate);
+
+ /* if the sync_duration is same as current */
+ if ((duration_num == info->sync_duration_num) &&
+ (duration_den == info->sync_duration_den)) {
+ LCDPR("%s: sync_duration is the same, exit\n", __func__);
+ return 0;
+ }
+
+ /* update vinfo */
+ info->sync_duration_num = duration_num;
+ info->sync_duration_den = duration_den;
+
+ lcd_framerate_automation_set_mode();
+#endif
+ return 0;
+}
+
+static int lcd_set_vframe_rate_end_hint(void)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct vinfo_s *info;
+
+ if (lcd_drv->lcd_status == 0) {
+ LCDPR("%s: lcd is disabled, exit\n", __func__);
+ return 0;
+ }
+
+ if (lcd_debug_print_flag)
+ LCDPR("fr_auto_policy = %d\n", lcd_drv->fr_auto_policy);
+ if (lcd_drv->fr_auto_policy) {
+ info = lcd_drv->lcd_info;
+ LCDPR("%s: return mode = %s, policy = %d\n", __func__,
+ info->name, lcd_drv->fr_auto_policy);
+
+ /* update vinfo */
+ info->sync_duration_num = lcd_drv->std_duration.duration_num;
+ info->sync_duration_den = lcd_drv->std_duration.duration_den;
+
+ lcd_framerate_automation_set_mode();
+ }
+#endif
+ return 0;
+}
+
+static int lcd_set_vframe_rate_policy(int policy)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ lcd_drv->fr_auto_policy = policy;
+ LCDPR("%s: %d\n", __func__, lcd_drv->fr_auto_policy);
+#endif
+ return 0;
+}
+
+static int lcd_get_vframe_rate_policy(void)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ return lcd_drv->fr_auto_policy;
+#else
+ return 0;
+#endif
+}
+
+#ifdef CONFIG_PM
+static int lcd_suspend(void)
+{
+ mutex_lock(&lcd_power_mutex);
+ aml_lcd_notifier_call_chain(LCD_EVENT_POWER_OFF, NULL);
+ lcd_resume_flag = 0;
+ LCDPR("%s finished\n", __func__);
+ mutex_unlock(&lcd_power_mutex);
+ return 0;
+}
+static int lcd_resume(void)
+{
+ mutex_lock(&lcd_power_mutex);
+ if (lcd_resume_flag == 0) {
+ lcd_resume_flag = 1;
+ aml_lcd_notifier_call_chain(LCD_EVENT_POWER_ON, NULL);
+ LCDPR("%s finished\n", __func__);
+ }
+ mutex_unlock(&lcd_power_mutex);
+ return 0;
+}
+#endif
+
+static struct vout_server_s lcd_vout_server = {
+ .name = "lcd_vout_server",
+ .op = {
+ .get_vinfo = lcd_get_current_info,
+ .set_vmode = lcd_set_current_vmode,
+ .validate_vmode = lcd_validate_vmode,
+ .vmode_is_supported = lcd_vmode_is_supported,
+ .disable = lcd_vout_disable,
+ .set_vframe_rate_hint = lcd_set_vframe_rate_hint,
+ .set_vframe_rate_end_hint = lcd_set_vframe_rate_end_hint,
+ .set_vframe_rate_policy = lcd_set_vframe_rate_policy,
+ .get_vframe_rate_policy = lcd_get_vframe_rate_policy,
+#ifdef CONFIG_PM
+ .vout_suspend = lcd_suspend,
+ .vout_resume = lcd_resume,
+#endif
+ },
+};
+
+static void lcd_tablet_vinfo_update(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct vinfo_s *vinfo;
+ struct lcd_config_s *pconf;
+
+ vinfo = lcd_drv->lcd_info;
+ pconf = lcd_drv->lcd_config;
+ if (vinfo) {
+ vinfo->name = PANEL_NAME;
+ vinfo->mode = VMODE_LCD;
+ vinfo->width = pconf->lcd_basic.h_active;
+ vinfo->height = pconf->lcd_basic.v_active;
+ vinfo->field_height = pconf->lcd_basic.v_active;
+ vinfo->aspect_ratio_num = pconf->lcd_basic.screen_width;
+ vinfo->aspect_ratio_den = pconf->lcd_basic.screen_height;
+ vinfo->screen_real_width = pconf->lcd_basic.screen_width;
+ vinfo->screen_real_height = pconf->lcd_basic.screen_height;
+ vinfo->sync_duration_num = pconf->lcd_timing.sync_duration_num;
+ vinfo->sync_duration_den = pconf->lcd_timing.sync_duration_den;
+ vinfo->video_clk = pconf->lcd_timing.lcd_clk;
+ vinfo->viu_color_fmt = COLOR_FMT_RGB444;
+
+ lcd_hdr_vinfo_update();
+ }
+}
+
+static void lcd_tablet_vinfo_update_default(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct vinfo_s *vinfo;
+ unsigned int h_active, v_active;
+
+ if (lcd_drv->lcd_info == NULL) {
+ LCDERR("no lcd_info exist\n");
+ return;
+ }
+
+ h_active = lcd_vcbus_read(ENCL_VIDEO_HAVON_END)
+ - lcd_vcbus_read(ENCL_VIDEO_HAVON_BEGIN) + 1;
+ v_active = lcd_vcbus_read(ENCL_VIDEO_VAVON_ELINE)
+ - lcd_vcbus_read(ENCL_VIDEO_VAVON_BLINE) + 1;
+
+ vinfo = lcd_drv->lcd_info;
+ if (vinfo) {
+ vinfo->name = PANEL_NAME;
+ vinfo->mode = VMODE_LCD;
+ vinfo->width = h_active;
+ vinfo->height = v_active;
+ vinfo->field_height = v_active;
+ vinfo->aspect_ratio_num = h_active;
+ vinfo->aspect_ratio_den = v_active;
+ vinfo->screen_real_width = h_active;
+ vinfo->screen_real_height = v_active;
+ vinfo->sync_duration_num = 60;
+ vinfo->sync_duration_den = 1;
+ vinfo->video_clk = 0;
+ vinfo->viu_color_fmt = COLOR_FMT_RGB444;
+ }
+}
+
+void lcd_tablet_vout_server_init(void)
+{
+ lcd_tablet_vinfo_update_default();
+ vout_register_server(&lcd_vout_server);
+}
+
+/* **************************************************
+ * lcd tablet config
+ * **************************************************
+ */
+static void lcd_config_print(struct lcd_config_s *pconf)
+{
+ LCDPR("%s, %s, %dbit, %dx%d\n",
+ pconf->lcd_basic.model_name,
+ lcd_type_type_to_str(pconf->lcd_basic.lcd_type),
+ pconf->lcd_basic.lcd_bits,
+ pconf->lcd_basic.h_active, pconf->lcd_basic.v_active);
+
+ if (lcd_debug_print_flag == 0)
+ return;
+
+ LCDPR("h_period = %d\n", pconf->lcd_basic.h_period);
+ LCDPR("v_period = %d\n", pconf->lcd_basic.v_period);
+ LCDPR("screen_width = %d\n", pconf->lcd_basic.screen_width);
+ LCDPR("screen_height = %d\n", pconf->lcd_basic.screen_height);
+
+ LCDPR("h_period_min = %d\n", pconf->lcd_basic.h_period_min);
+ LCDPR("h_period_max = %d\n", pconf->lcd_basic.h_period_max);
+ LCDPR("v_period_min = %d\n", pconf->lcd_basic.v_period_min);
+ LCDPR("v_period_max = %d\n", pconf->lcd_basic.v_period_max);
+ LCDPR("pclk_min = %d\n", pconf->lcd_basic.lcd_clk_min);
+ LCDPR("pclk_max = %d\n", pconf->lcd_basic.lcd_clk_max);
+
+ LCDPR("hsync_width = %d\n", pconf->lcd_timing.hsync_width);
+ LCDPR("hsync_bp = %d\n", pconf->lcd_timing.hsync_bp);
+ LCDPR("hsync_pol = %d\n", pconf->lcd_timing.hsync_pol);
+ LCDPR("vsync_width = %d\n", pconf->lcd_timing.vsync_width);
+ LCDPR("vsync_bp = %d\n", pconf->lcd_timing.vsync_bp);
+ LCDPR("vsync_pol = %d\n", pconf->lcd_timing.vsync_pol);
+
+ LCDPR("fr_adjust_type = %d\n", pconf->lcd_timing.fr_adjust_type);
+ LCDPR("ss_level = %d\n", pconf->lcd_timing.ss_level);
+ LCDPR("clk_auto = %d\n", pconf->lcd_timing.clk_auto);
+
+ if (pconf->lcd_basic.lcd_type == LCD_TTL) {
+ LCDPR("clk_pol = %d\n",
+ pconf->lcd_control.ttl_config->clk_pol);
+ LCDPR("sync_valid = %d\n",
+ pconf->lcd_control.ttl_config->sync_valid);
+ LCDPR("swap_ctrl = %d\n",
+ pconf->lcd_control.ttl_config->swap_ctrl);
+ } else if (pconf->lcd_basic.lcd_type == LCD_LVDS) {
+ LCDPR("lvds_repack = %d\n",
+ pconf->lcd_control.lvds_config->lvds_repack);
+ LCDPR("pn_swap = %d\n",
+ pconf->lcd_control.lvds_config->pn_swap);
+ LCDPR("dual_port = %d\n",
+ pconf->lcd_control.lvds_config->dual_port);
+ LCDPR("port_swap = %d\n",
+ pconf->lcd_control.lvds_config->port_swap);
+ LCDPR("lane_reverse = %d\n",
+ pconf->lcd_control.lvds_config->lane_reverse);
+ } else if (pconf->lcd_basic.lcd_type == LCD_VBYONE) {
+ LCDPR("lane_count = %d\n",
+ pconf->lcd_control.vbyone_config->lane_count);
+ LCDPR("byte_mode = %d\n",
+ pconf->lcd_control.vbyone_config->byte_mode);
+ LCDPR("region_num = %d\n",
+ pconf->lcd_control.vbyone_config->region_num);
+ LCDPR("color_fmt = %d\n",
+ pconf->lcd_control.vbyone_config->color_fmt);
+ } else if (pconf->lcd_basic.lcd_type == LCD_MIPI) {
+ LCDPR("mipi_lane_num = %d\n",
+ pconf->lcd_control.mipi_config->lane_num);
+ LCDPR("bit_rate_max = %d\n",
+ pconf->lcd_control.mipi_config->bit_rate_max);
+ LCDPR("factor_numerator = %d\n",
+ pconf->lcd_control.mipi_config->factor_numerator);
+ LCDPR("operation_mode_init = %d\n",
+ pconf->lcd_control.mipi_config->operation_mode_init);
+ LCDPR("operation_mode_display = %d\n",
+ pconf->lcd_control.mipi_config->operation_mode_display);
+ LCDPR("video_mode_type = %d\n",
+ pconf->lcd_control.mipi_config->video_mode_type);
+ LCDPR("clk_lp_continuous = %d\n",
+ pconf->lcd_control.mipi_config->clk_lp_continuous);
+ LCDPR("phy_stop_wait = %d\n",
+ pconf->lcd_control.mipi_config->phy_stop_wait);
+ LCDPR("extern_init = %d\n",
+ pconf->lcd_control.mipi_config->extern_init);
+ }
+}
+
+static int lcd_init_load_from_dts(struct lcd_config_s *pconf,
+ struct device *dev)
+{
+ int ret = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_TTL:
+ if (lcd_drv->lcd_status) /* lock pinmux if lcd in on */
+ lcd_ttl_pinmux_set(1);
+ else
+ lcd_ttl_pinmux_set(0);
+ break;
+ case LCD_VBYONE:
+ if (lcd_drv->lcd_status) /* lock pinmux if lcd in on */
+ lcd_vbyone_pinmux_set(1);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int lcd_config_load_from_dts(struct lcd_config_s *pconf,
+ struct device *dev)
+{
+ int ret = 0;
+ const char *str;
+ unsigned int para[10];
+ struct device_node *child;
+ struct lvds_config_s *lvdsconf;
+
+ child = of_get_child_by_name(dev->of_node, pconf->lcd_propname);
+ if (child == NULL) {
+ LCDERR("failed to get %s\n", pconf->lcd_propname);
+ return -1;
+ }
+
+ ret = of_property_read_string(child, "model_name", &str);
+ if (ret) {
+ LCDERR("failed to get model_name\n");
+ strcpy(pconf->lcd_basic.model_name, pconf->lcd_propname);
+ } else {
+ strcpy(pconf->lcd_basic.model_name, str);
+ }
+
+ ret = of_property_read_string(child, "interface", &str);
+ if (ret) {
+ LCDERR("failed to get interface\n");
+ str = "invalid";
+ }
+ pconf->lcd_basic.lcd_type = lcd_type_str_to_type(str);
+
+ ret = of_property_read_u32_array(child, "basic_setting", ¶[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;
+}
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tablet/mipi_dsi_util.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#endif
+#include "../lcd_reg.h"
+#include "../lcd_clk_config.h"
+#include "mipi_dsi_util.h"
+
+/* *************************************************************
+ * Define MIPI DSI Default config
+ */
+/* Range [0,3] */
+#define MIPI_DSI_VIRTUAL_CHAN_ID 0
+/* Define DSI command transfer type: high speed or low power */
+#define MIPI_DSI_CMD_TRANS_TYPE DCS_TRANS_LP
+/* Define if DSI command need ack: req_ack or no_ack */
+#define MIPI_DSI_DCS_ACK_TYPE MIPI_DSI_DCS_NO_ACK
+/* Applicable only to video mode. Define data transfer method:
+ * non-burst sync pulse; non-burst sync event; or burst.
+ */
+
+#define MIPI_DSI_COLOR_18BIT COLOR_18BIT_CFG_1
+#define MIPI_DSI_COLOR_24BIT COLOR_24BIT
+#define MIPI_DSI_TEAR_SWITCH MIPI_DCS_DISABLE_TEAR
+#define CMD_TIMEOUT_CNT 3000
+/* ************************************************************* */
+
+static char *operation_mode_table[] = {
+ "VIDEO",
+ "COMMAND",
+};
+
+static char *video_mode_type_table[] = {
+ "SYNC_PULSE",
+ "SYNC_EVENT",
+ "BURST_MODE",
+};
+
+static char *video_data_type_table[] = {
+ "COLOR_16BIT_CFG_1",
+ "COLOR_16BIT_CFG_2",
+ "COLOR_16BIT_CFG_3",
+ "COLOR_18BIT_CFG_1",
+ "COLOR_18BIT_CFG_2(loosely)",
+ "COLOR_24BIT",
+ "COLOR_20BIT_LOOSE",
+ "COLOR_24_BIT_YCBCR",
+ "COLOR_16BIT_YCBCR",
+ "COLOR_30BIT",
+ "COLOR_36BIT",
+ "COLOR_12BIT",
+ "COLOR_RGB_111",
+ "COLOR_RGB_332",
+ "COLOR_RGB_444",
+ "un-support type",
+};
+
+static char *phy_stop_wait_table[] = {
+ "AUTO",
+ "STANDARD",
+ "SLOW",
+};
+
+static struct dsi_phy_s dsi_phy_config;
+static struct dsi_vid_s dsi_vconf;
+static unsigned char dsi_init_on_table_dft[] = {
+ 0x05, 1, 0x11,
+ 0xff, 50,
+ 0x05, 1, 0x29,
+ 0xff, 20,
+ 0xff, 0xff,
+};
+static unsigned short dsi_rx_n;
+
+static void mipi_dsi_init_table_print(struct dsi_config_s *dconf, int on_off)
+{
+ int i, j, n;
+ int n_max;
+ unsigned char *dsi_table;
+ char str[200];
+ int len = 0;
+
+ if (on_off) {
+ if (dconf->dsi_init_on == NULL)
+ return;
+ dsi_table = dconf->dsi_init_on;
+ n_max = DSI_INIT_ON_MAX;
+ pr_info("DSI INIT ON:\n");
+ } else {
+ if (dconf->dsi_init_off == NULL)
+ return;
+ dsi_table = dconf->dsi_init_off;
+ n_max = DSI_INIT_OFF_MAX;
+ pr_info("DSI INIT OFF:\n");
+ }
+ i = 0;
+ n = 0;
+ while (i < n_max) {
+ if (dsi_table[i] == 0xff) {
+ n = 2;
+ if (dsi_table[i+1] == 0xff) {
+ pr_info(" 0x%02x,0x%02x,\n",
+ dsi_table[i], dsi_table[i+1]);
+ break;
+ } else {
+ pr_info(" 0x%02x,%d,\n",
+ dsi_table[i], dsi_table[i+1]);
+ }
+ } else if ((dsi_table[i] & 0xf) == 0x0) {
+ pr_info("dsi_init_%s wrong data_type: 0x%02x\n",
+ on_off ? "on" : "off", dsi_table[i]);
+ break;
+ } else {
+ n = (DSI_CMD_INDEX + 1) + dsi_table[i+DSI_CMD_INDEX];
+ len = 0;
+ for (j = 0; j < n; j++) {
+ if (j == DSI_CMD_INDEX) {
+ len += sprintf(str+len, "%d,",
+ dsi_table[i+j]);
+ } else {
+ len += sprintf(str+len, "0x%02x,",
+ dsi_table[i+j]);
+ }
+ }
+ if (len > 0)
+ pr_info(" %s\n", str);
+ }
+ i += n;
+ }
+}
+
+static void mipi_dsi_print_dphy_info(struct dsi_config_s *dconf)
+{
+ unsigned int temp;
+
+ temp = ((1000000 * 100) / (dconf->bit_rate / 1000)) * 8;
+ pr_info("MIPI DSI DPHY timing (unit: ns)\n"
+ " UI: %d.%02d\n"
+ " LP LPX: %d\n"
+ " LP TA_SURE: %d\n"
+ " LP TA_GO: %d\n"
+ " LP TA_GET: %d\n"
+ " HS EXIT: %d\n"
+ " HS TRAIL: %d\n"
+ " HS ZERO: %d\n"
+ " HS PREPARE: %d\n"
+ " CLK TRAIL: %d\n"
+ " CLK POST: %d\n"
+ " CLK ZERO: %d\n"
+ " CLK PREPARE: %d\n"
+ " CLK PRE: %d\n"
+ " INIT: %d\n"
+ " WAKEUP: %d\n\n",
+ (temp / 8 / 100), ((temp / 8) % 100),
+ (temp * dsi_phy_config.lp_lpx / 100),
+ (temp * dsi_phy_config.lp_ta_sure / 100),
+ (temp * dsi_phy_config.lp_ta_go / 100),
+ (temp * dsi_phy_config.lp_ta_get / 100),
+ (temp * dsi_phy_config.hs_exit / 100),
+ (temp * dsi_phy_config.hs_trail / 100),
+ (temp * dsi_phy_config.hs_zero / 100),
+ (temp * dsi_phy_config.hs_prepare / 100),
+ (temp * dsi_phy_config.clk_trail / 100),
+ (temp * dsi_phy_config.clk_post / 100),
+ (temp * dsi_phy_config.clk_zero / 100),
+ (temp * dsi_phy_config.clk_prepare / 100),
+ (temp * dsi_phy_config.clk_pre / 100),
+ (temp * dsi_phy_config.init / 100),
+ (temp * dsi_phy_config.wakeup / 100));
+}
+
+void mipi_dsi_print_info(struct lcd_config_s *pconf)
+{
+ unsigned int esc_clk, factor;
+ struct dsi_config_s *dconf;
+
+ dconf = pconf->lcd_control.mipi_config;
+ esc_clk = dconf->bit_rate / 8 / dsi_phy_config.lp_tesc;
+ factor = dconf->factor_numerator;
+ factor = ((factor * 1000 / dconf->factor_denominator) + 5) / 10;
+
+ pr_info("MIPI DSI Config:\n"
+ " lane num: %d\n"
+ " bit rate max: %dMHz\n"
+ " bit rate: %d.%03dMHz\n"
+ " pclk lanebyte factor: %d(/100)\n"
+ " operation mode:\n"
+ " init: %s(%d)\n"
+ " display: %s(%d)\n"
+ " video mode type: %s(%d)\n"
+ " clk lp continuous: %d\n"
+ " phy stop wait: %s(%d)\n"
+ " data format: %s\n"
+ " lp escape clock: %d.%03dMHz\n",
+ dconf->lane_num, dconf->bit_rate_max,
+ (dconf->bit_rate / 1000000), (dconf->bit_rate % 1000000) / 1000,
+ factor,
+ operation_mode_table[dconf->operation_mode_init],
+ dconf->operation_mode_init,
+ operation_mode_table[dconf->operation_mode_display],
+ dconf->operation_mode_display,
+ video_mode_type_table[dconf->video_mode_type],
+ dconf->video_mode_type,
+ dconf->clk_lp_continuous,
+ phy_stop_wait_table[dconf->phy_stop_wait],
+ dconf->phy_stop_wait,
+ video_data_type_table[dconf->dpi_data_format],
+ (esc_clk / 1000000), (esc_clk % 1000000) / 1000);
+
+ mipi_dsi_init_table_print(dconf, 1); /* dsi_init_on table */
+ mipi_dsi_init_table_print(dconf, 0); /* dsi_init_off table */
+
+ pr_info("extern init: %d\n\n", dconf->extern_init);
+
+ mipi_dsi_print_dphy_info(dconf);
+}
+
+int lcd_mipi_dsi_init_table_detect(struct device_node *m_node,
+ struct dsi_config_s *dconf, int on_off)
+{
+ int ret = 0;
+ unsigned char *dsi_table;
+ unsigned char propname[15];
+ int i, j;
+ int n_max;
+ unsigned int *para; /* num 100 array */
+ unsigned int val;
+
+ if (on_off) {
+ if (dconf->dsi_init_on == NULL)
+ return -1;
+ dsi_table = dconf->dsi_init_on;
+ n_max = DSI_INIT_ON_MAX;
+ sprintf(propname, "dsi_init_on");
+ } else {
+ if (dconf->dsi_init_off == NULL)
+ return -1;
+ dsi_table = dconf->dsi_init_off;
+ n_max = DSI_INIT_OFF_MAX;
+ sprintf(propname, "dsi_init_off");
+ }
+
+ para = kmalloc(sizeof(unsigned int)*100, GFP_KERNEL);
+ if (para == NULL) {
+ LCDERR("%s: Not enough memory\n", __func__);
+ return -1;
+ }
+ ret = of_property_read_u32_index(m_node, propname, 0, ¶[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<<BIT_DPI_COLOR_MODE) &
+ ~(0x7<<BIT_IN_COLOR_MODE) &
+ ~(0x3<<BIT_CHROMA_SUBSAMPLE));
+ dsi_host_write(MIPI_DSI_TOP_CNTL,
+ (temp |
+ (dpi_data_format << BIT_DPI_COLOR_MODE) |
+ (venc_data_width << BIT_IN_COLOR_MODE) |
+ (chroma_subsample << BIT_CHROMA_SUBSAMPLE)));
+ /* 2.3 Configure Signal polarity */
+ dsi_host_write(MIPI_DSI_DWC_DPI_CFG_POL_OS,
+ (0x0 << BIT_COLORM_ACTIVE_LOW) |
+ (0x0 << BIT_SHUTD_ACTIVE_LOW) |
+ (0 << BIT_HSYNC_ACTIVE_LOW) |
+ (0 << BIT_VSYNC_ACTIVE_LOW) |
+ (0x0 << BIT_DATAEN_ACTIVE_LOW));
+
+ if (operation_mode == OPERATION_VIDEO_MODE) {
+ /* 3.1 Configure Low power and video mode type settings */
+ dsi_host_write(MIPI_DSI_DWC_VID_MODE_CFG_OS,
+ (1 << BIT_LP_HFP_EN) | /* enalbe lp */
+ (1 << BIT_LP_HBP_EN) | /* enalbe lp */
+ (1 << BIT_LP_VCAT_EN) | /* enalbe lp */
+ (1 << BIT_LP_VFP_EN) | /* enalbe lp */
+ (1 << BIT_LP_VBP_EN) | /* enalbe lp */
+ (1 << BIT_LP_VSA_EN) | /* enalbe lp */
+ (1 << BIT_FRAME_BTA_ACK_EN) |
+ /* enable BTA after one
+ * frame, TODO, need check
+ */
+ /* (1 << BIT_LP_CMD_EN) | */
+ /* enable the command*/
+ /* transmission only in lp mode */
+ (vid_mode_type << BIT_VID_MODE_TYPE));
+ /* burst non burst mode */
+ /* [23:16]outvact, [7:0]invact */
+ dsi_host_write(MIPI_DSI_DWC_DPI_LP_CMD_TIM_OS,
+ (4 << 16) | (4 << 0));
+
+ /* 3.2 Configure video packet size settings */
+ if (vid_mode_type == BURST_MODE) {
+ /* should be one line in pixels, such as 480/240... */
+ dsi_host_write(MIPI_DSI_DWC_VID_PKT_SIZE_OS,
+ p->lcd_basic.h_active);
+ } else { /* non-burst mode */
+ /* in unit of pixels,
+ * (pclk period/byte clk period)*num_of_lane
+ * should be integer
+ */
+ dsi_host_write(MIPI_DSI_DWC_VID_PKT_SIZE_OS,
+ dsi_vconf.pixel_per_chunk);
+ }
+
+ /* 3.3 Configure number of chunks and null packet size
+ * for one line
+ */
+ if (vid_mode_type == BURST_MODE) {
+ dsi_host_write(MIPI_DSI_DWC_VID_NUM_CHUNKS_OS, 0);
+ dsi_host_write(MIPI_DSI_DWC_VID_NULL_SIZE_OS, 0);
+ } else { /* non burst mode */
+ /* HACT/VID_PKT_SIZE */
+ dsi_host_write(MIPI_DSI_DWC_VID_NUM_CHUNKS_OS,
+ dsi_vconf.num_of_chunk);
+ /* video null size */
+ dsi_host_write(MIPI_DSI_DWC_VID_NULL_SIZE_OS,
+ dsi_vconf.vid_null_size);
+ }
+
+ /* 4 Configure the video relative parameters according to
+ * the output type
+ */
+ /* include horizontal timing and vertical line */
+ dsi_host_write(MIPI_DSI_DWC_VID_HLINE_TIME_OS, dsi_vconf.hline);
+ dsi_host_write(MIPI_DSI_DWC_VID_HSA_TIME_OS, dsi_vconf.hsa);
+ dsi_host_write(MIPI_DSI_DWC_VID_HBP_TIME_OS, dsi_vconf.hbp);
+ dsi_host_write(MIPI_DSI_DWC_VID_VSA_LINES_OS, dsi_vconf.vsa);
+ dsi_host_write(MIPI_DSI_DWC_VID_VBP_LINES_OS, dsi_vconf.vbp);
+ dsi_host_write(MIPI_DSI_DWC_VID_VFP_LINES_OS, dsi_vconf.vfp);
+ dsi_host_write(MIPI_DSI_DWC_VID_VACTIVE_LINES_OS,
+ dsi_vconf.vact);
+ } /* operation_mode == OPERATION_VIDEO_MODE */
+
+ /* ----------------------------------------------------- */
+ /* Finish Configuration */
+ /* ----------------------------------------------------- */
+
+ /* Inner clock divider settings */
+ dsi_host_write(MIPI_DSI_DWC_CLKMGR_CFG_OS,
+ (0x1 << BIT_TO_CLK_DIV) |
+ (dsi_phy_config.lp_tesc << BIT_TX_ESC_CLK_DIV));
+ /* Packet header settings //move to mipi_dcs_set */
+ /* dsi_host_write( MIPI_DSI_DWC_PCKHDL_CFG_OS,
+ * (1 << BIT_CRC_RX_EN) |
+ * (1 << BIT_ECC_RX_EN) |
+ * (0 << BIT_BTA_EN) |
+ * (0 << BIT_EOTP_RX_EN) |
+ * (0 << BIT_EOTP_TX_EN) );
+ */
+ /* operation mode setting: video/command mode */
+ dsi_host_write(MIPI_DSI_DWC_MODE_CFG_OS, operation_mode);
+
+ /* Phy Timer */
+ if ((output_type != TV_ENC_LCD240x160_dsi) &&
+ (output_type != TV_ENC_LCD1920x1200p) &&
+ (output_type != TV_ENC_LCD2560x1600) &&
+ (output_type != TV_ENC_LCD768x1024p)) {
+ dsi_host_write(MIPI_DSI_DWC_PHY_TMR_CFG_OS, 0x03320000);
+ } else {
+ dsi_host_write(MIPI_DSI_DWC_PHY_TMR_CFG_OS, 0x090f0000);
+ }
+
+ /* Configure DPHY Parameters */
+ if ((output_type != TV_ENC_LCD240x160_dsi) &&
+ (output_type != TV_ENC_LCD1920x1200p) &&
+ (output_type != TV_ENC_LCD2560x1600) &&
+ (output_type != TV_ENC_LCD768x1024p)) {
+ dsi_host_write(MIPI_DSI_DWC_PHY_TMR_LPCLK_CFG_OS, 0x870025);
+ } else {
+ dsi_host_write(MIPI_DSI_DWC_PHY_TMR_LPCLK_CFG_OS, 0x260017);
+ }
+}
+
+/* *************************************************************
+ * mipi dsi command support
+ */
+
+static inline void print_mipi_cmd_status(int cnt, unsigned int status)
+{
+ if (cnt == 0) {
+ LCDPR("cmd error: status=0x%04x, int0=0x%06x, int1=0x%06x\n",
+ status,
+ dsi_host_read(MIPI_DSI_DWC_INT_ST0_OS),
+ dsi_host_read(MIPI_DSI_DWC_INT_ST1_OS));
+ }
+}
+
+#ifdef DSI_CMD_READ_VALID
+static void dsi_bta_control(int en)
+{
+ if (en) {
+ dsi_host_setb(MIPI_DSI_DWC_CMD_MODE_CFG_OS,
+ MIPI_DSI_DCS_REQ_ACK, BIT_ACK_RQST_EN, 1);
+ dsi_host_setb(MIPI_DSI_DWC_PCKHDL_CFG_OS,
+ MIPI_DSI_DCS_REQ_ACK, BIT_BTA_EN, 1);
+ } else {
+ dsi_host_setb(MIPI_DSI_DWC_PCKHDL_CFG_OS,
+ MIPI_DSI_DCS_NO_ACK, BIT_BTA_EN, 1);
+ dsi_host_setb(MIPI_DSI_DWC_CMD_MODE_CFG_OS,
+ MIPI_DSI_DCS_NO_ACK, BIT_ACK_RQST_EN, 1);
+ }
+}
+
+/* *************************************************************
+ * Function: generic_if_rd
+ * Generic interface read, address has to be MIPI_DSI_DWC_GEN_PLD_DATA_OS
+ */
+static unsigned int generic_if_rd(unsigned int address)
+{
+ unsigned int data_out;
+
+ if (address != MIPI_DSI_DWC_GEN_PLD_DATA_OS)
+ LCDERR(" Error Address : %x\n", address);
+
+ data_out = dsi_host_read(address);
+ return data_out;
+}
+#endif
+
+/* *************************************************************
+ * Function: generic_if_wr
+ * Generic interface write, address has to be
+ * MIPI_DSI_DWC_GEN_PLD_DATA_OS,
+ * MIPI_DSI_DWC_GEN_HDR_OS,
+ * MIPI_DSI_DWC_GEN_VCID_OS
+ */
+static unsigned int generic_if_wr(unsigned int address, unsigned int data_in)
+{
+ if ((address != MIPI_DSI_DWC_GEN_HDR_OS) &&
+ (address != MIPI_DSI_DWC_GEN_PLD_DATA_OS)) {
+ LCDERR(" Error Address : 0x%x\n", address);
+ }
+
+ if (lcd_debug_print_flag)
+ LCDPR("address 0x%x = 0x%08x\n", address, data_in);
+
+ dsi_host_write(address, data_in);
+
+ return 0;
+}
+
+/* *************************************************************
+ * Function: wait_bta_ack
+ * Poll to check if the BTA ack is finished
+ */
+static void wait_bta_ack(void)
+{
+ unsigned int phy_status, i;
+
+ /* Check if phydirection is RX */
+ i = CMD_TIMEOUT_CNT;
+ do {
+ udelay(10);
+ i--;
+ phy_status = dsi_host_read(MIPI_DSI_DWC_PHY_STATUS_OS);
+ } while ((((phy_status & 0x2) >> BIT_PHY_DIRECTION) == 0x0) && (i > 0));
+ if (i == 0)
+ LCDERR("phy direction error: RX\n");
+
+ /* Check if phydirection is return to TX */
+ i = CMD_TIMEOUT_CNT;
+ do {
+ udelay(10);
+ i--;
+ phy_status = dsi_host_read(MIPI_DSI_DWC_PHY_STATUS_OS);
+ } while (((phy_status & 0x2) >> BIT_PHY_DIRECTION) == 0x1);
+ if (i == 0)
+ LCDERR("phy direction error: TX\n");
+}
+
+/* *************************************************************
+ * Function: wait_cmd_fifo_empty
+ * Poll to check if the generic command fifo is empty
+ */
+static void wait_cmd_fifo_empty(void)
+{
+ unsigned int cmd_status;
+ int i = CMD_TIMEOUT_CNT;
+
+ do {
+ udelay(10);
+ i--;
+ cmd_status = dsi_host_read(MIPI_DSI_DWC_CMD_PKT_STATUS_OS);
+ } while ((((cmd_status >> BIT_GEN_CMD_EMPTY) & 0x1) != 0x1) && (i > 0));
+ print_mipi_cmd_status(i, cmd_status);
+}
+
+#if 0
+/* *************************************************************
+ * Function: wait_for_generic_read_response
+ * Wait for generic read response
+ */
+static unsigned int wait_for_generic_read_response(void)
+{
+ unsigned int timeout, phy_status, data_out;
+
+ phy_status = dsi_host_read(MIPI_DSI_DWC_PHY_STATUS_OS);
+ for (timeout = 0; timeout < 50; timeout++) {
+ if (((phy_status & 0x40) >> BIT_PHY_RXULPSESC0LANE) == 0x0)
+ break;
+ phy_status = dsi_host_read(MIPI_DSI_DWC_PHY_STATUS_OS);
+ udelay(1);
+ }
+ phy_status = dsi_host_read(MIPI_DSI_DWC_PHY_STATUS_OS);
+ for (timeout = 0; timeout < 50; timeout++) {
+ if (((phy_status & 0x40) >> BIT_PHY_RXULPSESC0LANE) == 0x1)
+ break;
+ phy_status = dsi_host_read(MIPI_DSI_DWC_PHY_STATUS_OS);
+ udelay(1);
+ }
+
+ data_out = dsi_host_read(MIPI_DSI_DWC_GEN_PLD_DATA_OS);
+ return data_out;
+}
+
+/* *************************************************************
+ * Function: generic_read_packet_0_para
+ * Generic Read Packet 0 Parameter with Generic Interface
+ * Supported DCS Command: DCS_SET_ADDRESS_MODE,
+ * DCS_SET_GAMMA_CURVE,
+ * DCS_SET_PIXEL_FORMAT,
+ * DCS_SET_TEAR_ON
+ */
+static unsigned int generic_read_packet_0_para(unsigned char data_type,
+ unsigned char vc_id, unsigned char dcs_command)
+{
+ unsigned int read_data;
+
+ /* lcd_print(" para is %x, dcs_command is %x\n", para, dcs_command); */
+ /* lcd_print(" vc_id %x, data_type is %x\n", vc_id, data_type); */
+ generic_if_wr(MIPI_DSI_DWC_GEN_HDR_OS,
+ ((0 << BIT_GEN_WC_MSBYTE) |
+ (((unsigned int)dcs_command) << BIT_GEN_WC_LSBYTE) |
+ (((unsigned int)vc_id) << BIT_GEN_VC) |
+ (((unsigned int)data_type) << BIT_GEN_DT)));
+
+ read_data = wait_for_generic_read_response();
+
+ return read_data;
+}
+#endif
+
+static void dsi_set_max_return_pkt_size(struct dsi_cmd_request_s *req)
+{
+ unsigned int d_para[2];
+
+ d_para[0] = (unsigned int)(req->payload[2] & 0xff);
+ d_para[1] = (unsigned int)(req->payload[3] & 0xff);
+ dsi_rx_n = (unsigned short)((d_para[1] << 8) | d_para[0]);
+ generic_if_wr(MIPI_DSI_DWC_GEN_HDR_OS,
+ ((d_para[1] << BIT_GEN_WC_MSBYTE) |
+ (d_para[0] << BIT_GEN_WC_LSBYTE) |
+ (((unsigned int)req->vc_id) << BIT_GEN_VC) |
+ (DT_SET_MAX_RET_PKT_SIZE << BIT_GEN_DT)));
+ if (req->req_ack == MIPI_DSI_DCS_REQ_ACK)
+ wait_bta_ack();
+ else if (req->req_ack == MIPI_DSI_DCS_NO_ACK)
+ wait_cmd_fifo_empty();
+}
+
+#ifdef DSI_CMD_READ_VALID
+static int dsi_generic_read_packet(struct dsi_cmd_request_s *req,
+ unsigned char *r_data)
+{
+ unsigned int d_para[2], read_data;
+ unsigned int i, j, done;
+
+ switch (req->data_type) {
+ case DT_GEN_RD_1:
+ d_para[0] = (req->pld_count == 0) ?
+ 0 : (((unsigned int)req->payload[2]) & 0xff);
+ d_para[1] = 0;
+ break;
+ case DT_GEN_RD_2:
+ d_para[0] = (req->pld_count == 0) ?
+ 0 : (((unsigned int)req->payload[2]) & 0xff);
+ d_para[1] = (req->pld_count < 2) ?
+ 0 : (((unsigned int)req->payload[3]) & 0xff);
+ break;
+ case DT_GEN_RD_0:
+ default:
+ d_para[0] = 0;
+ d_para[1] = 0;
+ break;
+ }
+
+ if (MIPI_DSI_DCS_ACK_TYPE == MIPI_DSI_DCS_NO_ACK)
+ dsi_bta_control(1);
+ generic_if_wr(MIPI_DSI_DWC_GEN_HDR_OS,
+ ((d_para[1] << BIT_GEN_WC_MSBYTE) |
+ (d_para[0] << BIT_GEN_WC_LSBYTE) |
+ (((unsigned int)req->vc_id) << BIT_GEN_VC) |
+ (((unsigned int)req->data_type) << BIT_GEN_DT)));
+ wait_bta_ack();
+ i = 0;
+ done = 0;
+ while (done == 0) {
+ read_data = generic_if_rd(MIPI_DSI_DWC_GEN_PLD_DATA_OS);
+ for (j = 0; j < 4; j++) {
+ if (i < dsi_rx_n) {
+ r_data[i] = (unsigned char)
+ ((read_data >> (j*8)) & 0xff);
+ i++;
+ } else {
+ done = 1;
+ break;
+ }
+ }
+ }
+ if (MIPI_DSI_DCS_ACK_TYPE == MIPI_DSI_DCS_NO_ACK)
+ dsi_bta_control(0);
+
+ return dsi_rx_n;
+}
+
+static int dsi_dcs_read_packet(struct dsi_cmd_request_s *req,
+ unsigned char *r_data)
+{
+ unsigned int d_command, read_data;
+ unsigned int i, j, done;
+
+ d_command = ((unsigned int)req->payload[2]) & 0xff;
+
+ if (MIPI_DSI_DCS_ACK_TYPE == MIPI_DSI_DCS_NO_ACK)
+ dsi_bta_control(1);
+ generic_if_wr(MIPI_DSI_DWC_GEN_HDR_OS,
+ ((0 << BIT_GEN_WC_MSBYTE) |
+ (d_command << BIT_GEN_WC_LSBYTE) |
+ (((unsigned int)req->vc_id) << BIT_GEN_VC) |
+ (((unsigned int)req->data_type) << BIT_GEN_DT)));
+ wait_bta_ack();
+ i = 0;
+ done = 0;
+ while (done == 0) {
+ read_data = generic_if_rd(MIPI_DSI_DWC_GEN_PLD_DATA_OS);
+ for (j = 0; j < 4; j++) {
+ if (i < dsi_rx_n) {
+ r_data[i] = (unsigned char)
+ ((read_data >> (j*8)) & 0xff);
+ i++;
+ } else {
+ done = 1;
+ break;
+ }
+ }
+ }
+
+ if (MIPI_DSI_DCS_ACK_TYPE == MIPI_DSI_DCS_NO_ACK)
+ dsi_bta_control(0);
+
+ return dsi_rx_n;
+}
+#endif
+
+/* *************************************************************
+ * Function: generic_write_short_packet
+ * Generic Write Short Packet with Generic Interface
+ * Supported Data Type: DT_GEN_SHORT_WR_0,
+ DT_GEN_SHORT_WR_1,
+ DT_GEN_SHORT_WR_2,
+ */
+static void dsi_generic_write_short_packet(struct dsi_cmd_request_s *req)
+{
+ unsigned int d_para[2];
+
+ switch (req->data_type) {
+ case DT_GEN_SHORT_WR_1:
+ d_para[0] = (req->pld_count == 0) ?
+ 0 : (((unsigned int)req->payload[2]) & 0xff);
+ d_para[1] = 0;
+ break;
+ case DT_GEN_SHORT_WR_2:
+ d_para[0] = (req->pld_count == 0) ?
+ 0 : (((unsigned int)req->payload[2]) & 0xff);
+ d_para[1] = (req->pld_count < 2) ?
+ 0 : (((unsigned int)req->payload[3]) & 0xff);
+ break;
+ case DT_GEN_SHORT_WR_0:
+ default:
+ d_para[0] = 0;
+ d_para[1] = 0;
+ break;
+ }
+
+ generic_if_wr(MIPI_DSI_DWC_GEN_HDR_OS,
+ ((d_para[1] << BIT_GEN_WC_MSBYTE) |
+ (d_para[0] << BIT_GEN_WC_LSBYTE) |
+ (((unsigned int)req->vc_id) << BIT_GEN_VC) |
+ (((unsigned int)req->data_type) << BIT_GEN_DT)));
+ if (req->req_ack == MIPI_DSI_DCS_REQ_ACK)
+ wait_bta_ack();
+ else if (req->req_ack == MIPI_DSI_DCS_NO_ACK)
+ wait_cmd_fifo_empty();
+}
+
+/* *************************************************************
+ * Function: dcs_write_short_packet
+ * DCS Write Short Packet with Generic Interface
+ * Supported Data Type: DT_DCS_SHORT_WR_0, DT_DCS_SHORT_WR_1,
+ */
+static void dsi_dcs_write_short_packet(struct dsi_cmd_request_s *req)
+{
+ unsigned int d_command, d_para;
+
+ d_command = ((unsigned int)req->payload[2]) & 0xff;
+ d_para = (req->pld_count < 2) ?
+ 0 : (((unsigned int)req->payload[3]) & 0xff);
+
+ generic_if_wr(MIPI_DSI_DWC_GEN_HDR_OS,
+ ((d_para << BIT_GEN_WC_MSBYTE) |
+ (d_command << BIT_GEN_WC_LSBYTE) |
+ (((unsigned int)req->vc_id) << BIT_GEN_VC) |
+ (((unsigned int)req->data_type) << BIT_GEN_DT)));
+ if (req->req_ack == MIPI_DSI_DCS_REQ_ACK)
+ wait_bta_ack();
+ else if (req->req_ack == MIPI_DSI_DCS_NO_ACK)
+ wait_cmd_fifo_empty();
+}
+
+/* *************************************************************
+ * Function: dsi_write_long_packet
+ * Write Long Packet with Generic Interface
+ * Supported Data Type: DT_GEN_LONG_WR, DT_DCS_LONG_WR
+ */
+static void dsi_write_long_packet(struct dsi_cmd_request_s *req)
+{
+ unsigned int d_command, payload_data, header_data;
+ unsigned int cmd_status;
+ unsigned int i, j, data_index, n, temp;
+
+ /* payload[2] start (payload[0]: data_type, payload[1]: data_cnt) */
+ data_index = DSI_CMD_INDEX + 1;
+ d_command = ((unsigned int)req->payload[data_index]) & 0xff;
+
+ /* Write Payload Register First */
+ n = (req->pld_count+3)/4;
+ for (i = 0; i < n; i++) {
+ payload_data = 0;
+ if (i < (req->pld_count/4))
+ temp = 4;
+ else
+ temp = req->pld_count % 4;
+ for (j = 0; j < temp; j++) {
+ payload_data |= (((unsigned int)
+ req->payload[data_index+(i*4)+j]) << (j*8));
+ }
+
+ /* Check the pld fifo status before write to it,
+ * do not need check every word
+ */
+ if ((i == (n/3)) || (i == (n/2))) {
+ j = CMD_TIMEOUT_CNT;
+ do {
+ udelay(10);
+ j--;
+ cmd_status = dsi_host_read(
+ MIPI_DSI_DWC_CMD_PKT_STATUS_OS);
+ } while ((((cmd_status >> BIT_GEN_PLD_W_FULL) & 0x1) ==
+ 0x1) && (j > 0));
+ print_mipi_cmd_status(j, cmd_status);
+ }
+ /* Use direct memory write to save time when in
+ * WRITE_MEMORY_CONTINUE
+ */
+ if (d_command == DCS_WRITE_MEMORY_CONTINUE) {
+ dsi_host_write(MIPI_DSI_DWC_GEN_PLD_DATA_OS,
+ payload_data);
+ } else {
+ generic_if_wr(MIPI_DSI_DWC_GEN_PLD_DATA_OS,
+ payload_data);
+ }
+ }
+
+ /* Check cmd fifo status before write to it */
+ j = CMD_TIMEOUT_CNT;
+ do {
+ udelay(10);
+ j--;
+ cmd_status = dsi_host_read(MIPI_DSI_DWC_CMD_PKT_STATUS_OS);
+ } while ((((cmd_status >> BIT_GEN_CMD_FULL) & 0x1) == 0x1) && (j > 0));
+ print_mipi_cmd_status(j, cmd_status);
+ /* Write Header Register */
+ /* include command */
+ header_data = ((((unsigned int)req->pld_count) << BIT_GEN_WC_LSBYTE) |
+ (((unsigned int)req->vc_id) << BIT_GEN_VC) |
+ (((unsigned int)req->data_type) << BIT_GEN_DT));
+ generic_if_wr(MIPI_DSI_DWC_GEN_HDR_OS, header_data);
+ if (req->req_ack == MIPI_DSI_DCS_REQ_ACK)
+ wait_bta_ack();
+ else if (req->req_ack == MIPI_DSI_DCS_NO_ACK)
+ wait_cmd_fifo_empty();
+}
+
+/* *************************************************************
+ * Function: dsi_write_cmd
+ * Supported Data Type: DT_GEN_SHORT_WR_0, DT_GEN_SHORT_WR_1, DT_GEN_SHORT_WR_2,
+ * DT_DCS_SHORT_WR_0, DT_DCS_SHORT_WR_1,
+ * DT_GEN_LONG_WR, DT_DCS_LONG_WR,
+ * DT_SET_MAX_RET_PKT_SIZE
+ * DT_GEN_RD_0, DT_GEN_RD_1, DT_GEN_RD_2,
+ * DT_DCS_RD_0
+ * Return: command number
+ */
+int dsi_write_cmd(unsigned char *payload)
+{
+ int i = 0, j = 0, num = 0;
+#ifdef DSI_CMD_READ_VALID
+ int k = 0, n = 0;
+ unsigned char rd_data[100];
+#endif
+ struct dsi_cmd_request_s dsi_cmd_req;
+ unsigned char vc_id = MIPI_DSI_VIRTUAL_CHAN_ID;
+ unsigned int req_ack = MIPI_DSI_DCS_ACK_TYPE;
+
+ /* mipi command(payload) */
+ /* format: data_type, num, data.... */
+ /* special: data_type=0xff,
+ * num<0xff means delay ms, num=0xff means ending.
+ */
+ while (i < DSI_CMD_SIZE_MAX) {
+ if (payload[i] == 0xff) {
+ j = 2;
+ if (payload[i+1] == 0xff)
+ break;
+ else
+ mdelay(payload[i+1]);
+ } else if ((payload[i] & 0xf) == 0x0) {
+ LCDERR("data_type: 0x%02x\n", payload[i]);
+ break;
+ } else {
+ /* payload[i+DSI_CMD_INDEX] is data count */
+ j = (DSI_CMD_INDEX + 1) + payload[i+DSI_CMD_INDEX];
+ dsi_cmd_req.data_type = payload[i];
+ dsi_cmd_req.vc_id = (vc_id & 0x3);
+ dsi_cmd_req.payload = &payload[i];
+ dsi_cmd_req.pld_count = payload[i+DSI_CMD_INDEX];
+ dsi_cmd_req.req_ack = req_ack;
+ switch (dsi_cmd_req.data_type) {/* analysis data_type */
+ case DT_GEN_SHORT_WR_0:
+ case DT_GEN_SHORT_WR_1:
+ case DT_GEN_SHORT_WR_2:
+ dsi_generic_write_short_packet(&dsi_cmd_req);
+ break;
+ case DT_DCS_SHORT_WR_0:
+ case DT_DCS_SHORT_WR_1:
+ dsi_dcs_write_short_packet(&dsi_cmd_req);
+ break;
+ case DT_DCS_LONG_WR:
+ case DT_GEN_LONG_WR:
+ dsi_write_long_packet(&dsi_cmd_req);
+ break;
+ case DT_TURN_ON:
+ dsi_host_setb(MIPI_DSI_TOP_CNTL, 1, 2, 1);
+ mdelay(20); /* wait for vsync trigger */
+ dsi_host_setb(MIPI_DSI_TOP_CNTL, 0, 2, 1);
+ mdelay(20); /* wait for vsync trigger */
+ break;
+ case DT_SHUT_DOWN:
+ dsi_host_setb(MIPI_DSI_TOP_CNTL, 1, 2, 1);
+ mdelay(20); /* wait for vsync trigger */
+ break;
+ case DT_SET_MAX_RET_PKT_SIZE:
+ dsi_set_max_return_pkt_size(&dsi_cmd_req);
+ break;
+#ifdef DSI_CMD_READ_VALID
+ case DT_GEN_RD_0:
+ case DT_GEN_RD_1:
+ case DT_GEN_RD_2:
+ /* need BTA ack */
+ dsi_cmd_req.req_ack = MIPI_DSI_DCS_REQ_ACK;
+ dsi_cmd_req.pld_count =
+ (dsi_cmd_req.pld_count > 2) ?
+ 2 : dsi_cmd_req.pld_count;
+ n = dsi_generic_read_packet(&dsi_cmd_req,
+ &rd_data[0]);
+ LCDPR("generic read data");
+ for (k = 0; k < dsi_cmd_req.pld_count; k++) {
+ pr_info(" 0x%02x",
+ dsi_cmd_req.payload[k+2]);
+ }
+ for (k = 0; k < n; k++)
+ pr_info("0x%02x ", rd_data[k]);
+ pr_info("\n");
+ break;
+ case DT_DCS_RD_0:
+ /* need BTA ack */
+ dsi_cmd_req.req_ack = MIPI_DSI_DCS_REQ_ACK;
+ n = dsi_dcs_read_packet(&dsi_cmd_req,
+ &rd_data[0]);
+ pr_info("dcs read data 0x%02x:\n",
+ dsi_cmd_req.payload[2]);
+ for (k = 0; k < n; k++)
+ pr_info("0x%02x ", rd_data[k]);
+ pr_info("\n");
+ break;
+#endif
+ default:
+ LCDPR("[warning]un-support data_type: 0x%02x\n",
+ dsi_cmd_req.data_type);
+
+ break;
+ }
+ }
+ i += j;
+ num++;
+ }
+
+ return num;
+}
+
+#ifdef DSI_CMD_READ_VALID
+/* *************************************************************
+ * Function: dsi_read_single
+ * Supported Data Type: DT_GEN_RD_0, DT_GEN_RD_1, DT_GEN_RD_2,
+ * DT_DCS_RD_0
+ * Return: data count
+ */
+int dsi_read_single(unsigned char *payload, unsigned char *rd_data,
+ unsigned int rd_byte_len)
+{
+ int num = 0;
+ unsigned char temp[4];
+ unsigned char vc_id = MIPI_DSI_VIRTUAL_CHAN_ID;
+ unsigned int req_ack;
+ struct dsi_cmd_request_s dsi_cmd_req;
+
+ req_ack = MIPI_DSI_DCS_ACK_TYPE;
+ dsi_cmd_req.data_type = DT_SET_MAX_RET_PKT_SIZE;
+ dsi_cmd_req.vc_id = (vc_id & 0x3);
+ temp[0] = dsi_cmd_req.data_type;
+ temp[1] = 2;
+ temp[2] = (unsigned char)((rd_byte_len >> 0) & 0xff);
+ temp[3] = (unsigned char)((rd_byte_len >> 8) & 0xff);
+ dsi_cmd_req.payload = &temp[0];
+ dsi_cmd_req.pld_count = 2;
+ dsi_cmd_req.req_ack = req_ack;
+ dsi_set_max_return_pkt_size(&dsi_cmd_req);
+
+ /* payload struct: */
+ /* data_type, data_cnt, command, parameters... */
+ req_ack = MIPI_DSI_DCS_REQ_ACK; /* need BTA ack */
+ dsi_cmd_req.data_type = payload[0];
+ dsi_cmd_req.vc_id = (vc_id & 0x3);
+ dsi_cmd_req.payload = &payload[0];
+ dsi_cmd_req.pld_count = payload[DSI_CMD_INDEX];
+ dsi_cmd_req.req_ack = req_ack;
+ switch (dsi_cmd_req.data_type) {/* analysis data_type */
+ case DT_GEN_RD_0:
+ case DT_GEN_RD_1:
+ case DT_GEN_RD_2:
+ num = dsi_generic_read_packet(&dsi_cmd_req, rd_data);
+ break;
+ case DT_DCS_RD_0:
+ num = dsi_dcs_read_packet(&dsi_cmd_req, rd_data);
+ break;
+ default:
+ LCDPR("read un-support data_type: 0x%02x\n",
+ dsi_cmd_req.data_type);
+ break;
+ }
+
+ return num;
+}
+#else
+int dsi_read_single(unsigned char *payload, unsigned char *rd_data,
+ unsigned int rd_byte_len)
+{
+ LCDPR("Don't support mipi-dsi read command\n");
+ return 0;
+}
+#endif
+
+static void mipi_dsi_phy_config(struct dsi_phy_s *dphy, unsigned int dsi_ui)
+{
+ unsigned int temp, t_ui;
+
+ t_ui = (1000000 * 100) / (dsi_ui / 1000); /* 0.01ns*100 */
+ temp = t_ui * 8; /* lane_byte cycle time */
+
+ dphy->lp_tesc = ((DPHY_TIME_LP_TESC(t_ui) + temp - 1) / temp) & 0xff;
+ dphy->lp_lpx = ((DPHY_TIME_LP_LPX(t_ui) + temp - 1) / temp) & 0xff;
+ dphy->lp_ta_sure = ((DPHY_TIME_LP_TA_SURE(t_ui) + temp - 1) / temp) &
+ 0xff;
+ dphy->lp_ta_go = ((DPHY_TIME_LP_TA_GO(t_ui) + temp - 1) / temp) & 0xff;
+ dphy->lp_ta_get = ((DPHY_TIME_LP_TA_GETX(t_ui) + temp - 1) / temp) &
+ 0xff;
+ dphy->hs_exit = ((DPHY_TIME_HS_EXIT(t_ui) + temp - 1) / temp) & 0xff;
+ dphy->hs_trail = ((DPHY_TIME_HS_TRAIL(t_ui) + temp - 1) / temp) & 0xff;
+ dphy->hs_prepare = ((DPHY_TIME_HS_PREPARE(t_ui) + temp - 1) / temp) &
+ 0xff;
+ dphy->hs_zero = ((DPHY_TIME_HS_ZERO(t_ui) + temp - 1) / temp) & 0xff;
+ dphy->clk_trail = ((DPHY_TIME_CLK_TRAIL(t_ui) + temp - 1) / temp) &
+ 0xff;
+ dphy->clk_post = ((DPHY_TIME_CLK_POST(t_ui) + temp - 1) / temp) & 0xff;
+ dphy->clk_prepare = ((DPHY_TIME_CLK_PREPARE(t_ui) + temp - 1) / temp) &
+ 0xff;
+ dphy->clk_zero = ((DPHY_TIME_CLK_ZERO(t_ui) + temp - 1) / temp) & 0xff;
+ dphy->clk_pre = ((DPHY_TIME_CLK_PRE(t_ui) + temp - 1) / temp) & 0xff;
+ dphy->init = (DPHY_TIME_INIT(t_ui) + temp - 1) / temp;
+ dphy->wakeup = (DPHY_TIME_WAKEUP(t_ui) + temp - 1) / temp;
+
+ if (lcd_debug_print_flag) {
+ LCDPR("%s:\n"
+ "lp_tesc = 0x%02x\n"
+ "lp_lpx = 0x%02x\n"
+ "lp_ta_sure = 0x%02x\n"
+ "lp_ta_go = 0x%02x\n"
+ "lp_ta_get = 0x%02x\n"
+ "hs_exit = 0x%02x\n"
+ "hs_trail = 0x%02x\n"
+ "hs_zero = 0x%02x\n"
+ "hs_prepare = 0x%02x\n"
+ "clk_trail = 0x%02x\n"
+ "clk_post = 0x%02x\n"
+ "clk_zero = 0x%02x\n"
+ "clk_prepare = 0x%02x\n"
+ "clk_pre = 0x%02x\n"
+ "init = 0x%02x\n"
+ "wakeup = 0x%02x\n",
+ __func__,
+ dphy->lp_tesc, dphy->lp_lpx, dphy->lp_ta_sure,
+ dphy->lp_ta_go, dphy->lp_ta_get, dphy->hs_exit,
+ dphy->hs_trail, dphy->hs_zero, dphy->hs_prepare,
+ dphy->clk_trail, dphy->clk_post,
+ dphy->clk_zero, dphy->clk_prepare, dphy->clk_pre,
+ dphy->init, dphy->wakeup);
+ }
+}
+
+static void mipi_dsi_video_config(struct lcd_config_s *pconf)
+{
+ unsigned short h_period, hs_width, hs_bp;
+ unsigned int den, num;
+ unsigned short v_period, v_active, vs_width, vs_bp;
+
+ h_period = pconf->lcd_basic.h_period;
+ hs_width = pconf->lcd_timing.hsync_width;
+ hs_bp = pconf->lcd_timing.hsync_bp;
+ den = pconf->lcd_control.mipi_config->factor_denominator;
+ num = pconf->lcd_control.mipi_config->factor_numerator;
+
+ dsi_vconf.hline = (h_period * den + num - 1) / num;
+ dsi_vconf.hsa = (hs_width * den + num - 1) / num;
+ dsi_vconf.hbp = (hs_bp * den + num - 1) / num;
+
+ v_period = pconf->lcd_basic.v_period;
+ v_active = pconf->lcd_basic.v_active;
+ vs_width = pconf->lcd_timing.vsync_width;
+ vs_bp = pconf->lcd_timing.vsync_bp;
+ dsi_vconf.vsa = vs_width;
+ dsi_vconf.vbp = vs_bp;
+ dsi_vconf.vfp = v_period - v_active - vs_bp - vs_width;
+ dsi_vconf.vact = v_active;
+
+ if (lcd_debug_print_flag) {
+ LCDPR(" ============= VIDEO TIMING SETTING =============\n");
+ LCDPR(" HLINE = %d\n", dsi_vconf.hline);
+ LCDPR(" HSA = %d\n", dsi_vconf.hsa);
+ LCDPR(" HBP = %d\n", dsi_vconf.hbp);
+ LCDPR(" VSA = %d\n", dsi_vconf.vsa);
+ LCDPR(" VBP = %d\n", dsi_vconf.vbp);
+ LCDPR(" VFP = %d\n", dsi_vconf.vfp);
+ LCDPR(" VACT = %d\n", dsi_vconf.vact);
+ LCDPR(" ================================================\n");
+ }
+}
+
+#define DSI_PACKET_HEADER_CRC 6 /* 4(header)+2(CRC) */
+static void mipi_dsi_non_burst_chunk_config(struct lcd_config_s *pconf)
+{
+ int pixel_per_chunk = 0, num_of_chunk = 0, vid_null_size = 0;
+ int byte_per_chunk = 0, total_bytes_per_chunk = 0, chunk_overhead = 0;
+ int bit_rate_pclk_factor;
+ int lane_num;
+ int i, done;
+
+ i = 1;
+ done = 0;
+ lane_num = (int)(pconf->lcd_control.mipi_config->lane_num);
+ bit_rate_pclk_factor = pconf->lcd_control.mipi_config->bit_rate /
+ pconf->lcd_timing.lcd_clk;
+ while ((i <= (pconf->lcd_basic.h_active/8)) && (done == 0)) {
+ pixel_per_chunk = i * 8;
+ if (pconf->lcd_control.mipi_config->dpi_data_format ==
+ COLOR_18BIT_CFG_1) {
+ /* 18bit (4*18/8=9byte) */
+ byte_per_chunk = pixel_per_chunk * 9/4;
+ } else {
+ /* 24bit or 18bit-loosely */
+ byte_per_chunk = pixel_per_chunk * 3;
+ }
+ total_bytes_per_chunk =
+ (lane_num * pixel_per_chunk * bit_rate_pclk_factor) / 8;
+ num_of_chunk = pconf->lcd_basic.h_active / pixel_per_chunk;
+ /* byte_per_chunk+6=valid_payload */
+ chunk_overhead = total_bytes_per_chunk -
+ (byte_per_chunk + DSI_PACKET_HEADER_CRC);
+ if (chunk_overhead >= DSI_PACKET_HEADER_CRC) {
+ /* if room for null_vid's head(4)+crc(2) */
+ /* chunk_overhead-null_vid's head(4)+crc(2) =
+ * null_vid's payload
+ */
+ vid_null_size = chunk_overhead - DSI_PACKET_HEADER_CRC;
+ done = 1;
+ } else if (chunk_overhead >= 0) {
+ vid_null_size = 0;
+ done = 1;
+ } else {
+ vid_null_size = 0;
+ }
+ i++;
+ }
+ if (done == 0) {
+ LCDPR(" No room packet header & CRC, chunk_overhead is %d\n",
+ chunk_overhead);
+ }
+
+ dsi_vconf.pixel_per_chunk = pixel_per_chunk;
+ dsi_vconf.num_of_chunk = num_of_chunk;
+ dsi_vconf.vid_null_size = vid_null_size;
+ if (lcd_debug_print_flag) {
+ LCDPR(" ============== NON_BURST SETTINGS =============\n");
+ LCDPR(" pixel_per_chunk = %d\n", pixel_per_chunk);
+ LCDPR(" num_of_chunk = %d\n", num_of_chunk);
+ LCDPR(" total_bytes_per_chunk = %d\n", total_bytes_per_chunk);
+ LCDPR(" byte_per_chunk = %d\n", byte_per_chunk);
+ LCDPR(" chunk_overhead = %d\n", chunk_overhead);
+ LCDPR(" vid_null_size = %d\n", vid_null_size);
+ LCDPR(" ===============================================\n");
+ }
+}
+
+static void mipi_dsi_host_init(struct lcd_config_s *pconf)
+{
+ unsigned int op_mode_init;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ op_mode_init = pconf->lcd_control.mipi_config->operation_mode_init;
+ mipi_dcs_set(MIPI_DSI_CMD_TRANS_TYPE, /* 0: high speed, 1: low power */
+ MIPI_DSI_DCS_ACK_TYPE, /* if need bta ack check */
+ MIPI_DSI_TEAR_SWITCH); /* enable tear ack */
+
+ set_mipi_dsi_host(MIPI_DSI_VIRTUAL_CHAN_ID, /* Virtual channel id */
+ 0, /* Chroma sub sample, only for YUV 422 or 420, even or odd */
+ op_mode_init, /* DSI operation mode, video or command */
+ pconf);
+}
+
+static void mipi_dsi_link_on(struct lcd_config_s *pconf)
+{
+ unsigned int op_mode_init, op_mode_disp;
+ struct dsi_config_s *dconf;
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+ struct aml_lcd_extern_driver_s *lcd_ext;
+#endif
+ unsigned int temp = 0;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ dconf = pconf->lcd_control.mipi_config;
+ op_mode_init = dconf->operation_mode_init;
+ op_mode_disp = dconf->operation_mode_display;
+
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+ if (dconf->extern_init < LCD_EXTERN_INDEX_INVALID) {
+ lcd_ext = aml_lcd_extern_get_driver(dconf->extern_init);
+ if (lcd_ext == NULL) {
+ LCDPR("no lcd_extern driver\n");
+ } else {
+ if (lcd_ext->config.table_init_on) {
+ temp += dsi_write_cmd(
+ lcd_ext->config.table_init_on);
+ LCDPR("[extern]%s dsi init on\n",
+ lcd_ext->config.name);
+ }
+ }
+ }
+#endif
+
+ if (dconf->dsi_init_on) {
+ temp += dsi_write_cmd(dconf->dsi_init_on);
+ LCDPR("dsi init on\n");
+ }
+
+ if (temp == 0) {
+ LCDPR("[warning]: no init command, use default\n");
+ dsi_write_cmd(dsi_init_on_table_dft);
+ }
+
+ if (op_mode_disp != op_mode_init) {
+ set_mipi_dsi_host(MIPI_DSI_VIRTUAL_CHAN_ID,
+ 0, /* Chroma sub sample, only for
+ * YUV 422 or 420, even or odd
+ */
+ op_mode_disp, /* DSI operation mode, video or command */
+ pconf);
+ }
+}
+
+void mipi_dsi_link_off(struct lcd_config_s *pconf)
+{
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+ struct aml_lcd_extern_driver_s *lcd_ext;
+ int ext_index;
+#endif
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ if (pconf->lcd_control.mipi_config->dsi_init_off) {
+ dsi_write_cmd(pconf->lcd_control.mipi_config->dsi_init_off);
+ LCDPR("dsi init off\n");
+ }
+
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+ ext_index = pconf->lcd_control.mipi_config->extern_init;
+ if (ext_index < LCD_EXTERN_INDEX_INVALID) {
+ lcd_ext = aml_lcd_extern_get_driver(ext_index);
+ if (lcd_ext == NULL) {
+ LCDPR("no lcd_extern driver\n");
+ } else {
+ if (lcd_ext->config.table_init_off) {
+ dsi_write_cmd(lcd_ext->config.table_init_off);
+ LCDPR("[extern]%s dsi init off\n", lcd_ext->config.name);
+ }
+ }
+ }
+#endif
+}
+
+void lcd_mipi_dsi_config_set(struct lcd_config_s *pconf)
+{
+ unsigned int pclk, bit_rate, lcd_bits;
+ unsigned int bit_rate_max, bit_rate_min, pll_out_fmin;
+ struct dsi_config_s *dconf = pconf->lcd_control.mipi_config;
+ struct lcd_clk_config_s *cConf = get_lcd_clk_config();
+ int n;
+ unsigned int temp;
+
+ /* unit in kHz for calculation */
+ pll_out_fmin = cConf->pll_out_fmin;
+ pclk = pconf->lcd_timing.lcd_clk / 1000;
+
+ /* data format */
+ if (pconf->lcd_basic.lcd_bits == 6) {
+ dconf->venc_data_width = MIPI_DSI_VENC_COLOR_18B;
+ dconf->dpi_data_format = MIPI_DSI_COLOR_18BIT;
+ if (dconf->dpi_data_format == COLOR_18BIT_CFG_2)
+ lcd_bits = 8;
+ else
+ lcd_bits = 6;
+ } else {
+ dconf->venc_data_width = MIPI_DSI_VENC_COLOR_24B;
+ dconf->dpi_data_format = MIPI_DSI_COLOR_24BIT;
+ lcd_bits = 8;
+ }
+
+ /* bit rate max */
+ if (dconf->bit_rate_max == 0) { /* auto calculate */
+ if ((dconf->operation_mode_display == OPERATION_VIDEO_MODE) &&
+ (dconf->video_mode_type != BURST_MODE)) {
+ temp = pclk * 4 * lcd_bits;
+ bit_rate = temp / dconf->lane_num;
+ } else {
+ temp = pclk * 3 * lcd_bits;
+ bit_rate = temp / dconf->lane_num;
+ }
+ n = 0;
+ bit_rate_min = 0;
+ bit_rate_max = 0;
+ while ((bit_rate_min < pll_out_fmin) && (n < 100)) {
+ bit_rate_max = bit_rate + (pclk / 2) + (n * pclk);
+ bit_rate_min = bit_rate_max - pclk;
+ n++;
+ }
+ dconf->bit_rate_max = bit_rate_max / 1000; /* unit: MHz*/
+ if (dconf->bit_rate_max > MIPI_PHY_CLK_MAX)
+ dconf->bit_rate_max = MIPI_PHY_CLK_MAX;
+
+ LCDPR("mipi dsi bit_rate max=%dMHz\n", dconf->bit_rate_max);
+ } else { /* user define */
+ if (dconf->bit_rate_max < pll_out_fmin / 1000) {
+ LCDERR("can't support bit_rate %dMHz (min=%dMHz)\n",
+ dconf->bit_rate_max, (pll_out_fmin / 1000));
+ }
+ if (dconf->bit_rate_max > MIPI_PHY_CLK_MAX) {
+ LCDPR("[warning]: bit_rate_max %dMHz is out of standard (%dMHz)\n",
+ dconf->bit_rate_max, MIPI_PHY_CLK_MAX);
+ }
+ }
+
+ /* Venc resolution format */
+ switch (dconf->phy_stop_wait) {
+ case 1: /* standard */
+ dconf->venc_fmt = TV_ENC_LCD768x1024p;
+ break;
+ case 2: /* slow */
+ dconf->venc_fmt = TV_ENC_LCD1280x720;
+ break;
+ case 0: /* auto */
+ default:
+ if ((pconf->lcd_basic.h_active != 240) &&
+ (pconf->lcd_basic.h_active != 768) &&
+ (pconf->lcd_basic.h_active != 1920) &&
+ (pconf->lcd_basic.h_active != 2560))
+ dconf->venc_fmt = TV_ENC_LCD1280x720;
+ else
+ dconf->venc_fmt = TV_ENC_LCD768x1024p;
+ break;
+ }
+}
+
+/* bit_rate is confirm by clk_genrate, so internal clk config must after that */
+void lcd_mipi_dsi_config_post(struct lcd_config_s *pconf)
+{
+ unsigned int pclk, lanebyteclk;
+ unsigned int den, num;
+ struct dsi_config_s *dconf = pconf->lcd_control.mipi_config;
+
+ pclk = pconf->lcd_timing.lcd_clk / 1000;
+
+ /* pclk lanebyteclk factor */
+ if (dconf->factor_numerator == 0) {
+ lanebyteclk = dconf->bit_rate / 8 / 1000;
+ LCDPR("pixel_clk = %d.%03dMHz, bit_rate = %d.%03dMHz\n",
+ (pclk / 1000), (pclk % 1000),
+ (dconf->bit_rate / 1000000),
+ ((dconf->bit_rate / 1000) % 1000));
+ dconf->factor_numerator = pclk;
+ dconf->factor_denominator = lanebyteclk;
+ }
+ num = dconf->factor_numerator;
+ den = dconf->factor_denominator;
+ LCDPR("num=%d, den=%d, factor=%d.%02d\n",
+ num, den, (den / num), ((den % num) * 100 / num));
+
+ /* video config */
+ if (dconf->operation_mode_display == OPERATION_VIDEO_MODE) {
+ mipi_dsi_video_config(pconf);
+ if (dconf->video_mode_type != BURST_MODE)
+ mipi_dsi_non_burst_chunk_config(pconf);
+ }
+
+ /* phy config */
+ mipi_dsi_phy_config(&dsi_phy_config, dconf->bit_rate);
+}
+
+static void mipi_dsi_host_on(struct lcd_config_s *pconf)
+{
+ if (lcd_debug_print_flag)
+ mipi_dsi_print_info(pconf);
+
+ startup_mipi_dsi_host();
+ mipi_dsi_host_init(pconf);
+ dsi_phy_config_set(pconf);
+
+ mipi_dsi_link_on(pconf);
+}
+
+static void mipi_dsi_host_off(void)
+{
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ /* Power down DSI */
+ dsi_host_write(MIPI_DSI_DWC_PWR_UP_OS, 0);
+
+ /* Power down D-PHY, do not have to close dphy */
+ /* dsi_host_write(MIPI_DSI_DWC_PHY_RSTZ_OS,
+ * (dsi_host_read( MIPI_DSI_DWC_PHY_RSTZ_OS ) & 0xc));
+ */
+ /* dsi_host_write(MIPI_DSI_DWC_PHY_RSTZ_OS, 0xc); */
+
+ dsi_phy_write(MIPI_DSI_CHAN_CTRL, 0x1f);
+ //LCDPR("MIPI_DSI_PHY_CTRL=0x%x\n", dsi_phy_read(MIPI_DSI_PHY_CTRL));
+ dsi_phy_setb(MIPI_DSI_PHY_CTRL, 0, 7, 1);
+}
+
+void lcd_mipi_control_set(struct lcd_config_s *pconf, int status)
+{
+ if (lcd_debug_print_flag)
+ LCDPR("%s: %d\n", __func__, status);
+
+ if (pconf->lcd_control.mipi_config == NULL) {
+ LCDERR("%s: dsi config is NULL\n", __func__);
+ return;
+ }
+
+ if (status)
+ mipi_dsi_host_on(pconf);
+ else
+ mipi_dsi_host_off();
+}
+
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tablet/mipi_dsi_util.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef MIPI_DSI_UTIL_H
+#define MIPI_DSI_UTIL_H
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+
+
+/* ********************************************************
+ * MIPI DSI Data Type/ MIPI DCS Command Type Definitions
+ * Pheripheral to Host
+ */
+enum mipi_dsi_data_type_host_e {
+ DT_VSS = 0x01,
+ DT_VSE = 0x11,
+ DT_HSS = 0x21,
+ DT_HSE = 0x31,
+ DT_EOTP = 0x08,
+ DT_CMOFF = 0x02,
+ DT_CMON = 0x12,
+ DT_SHUT_DOWN = 0x22,
+ DT_TURN_ON = 0x32,
+ DT_GEN_SHORT_WR_0 = 0x03,
+ DT_GEN_SHORT_WR_1 = 0x13,
+ DT_GEN_SHORT_WR_2 = 0x23,
+ DT_GEN_RD_0 = 0x04,
+ DT_GEN_RD_1 = 0x14,
+ DT_GEN_RD_2 = 0x24,
+ DT_DCS_SHORT_WR_0 = 0x05,
+ DT_DCS_SHORT_WR_1 = 0x15,
+ DT_DCS_RD_0 = 0x06,
+ DT_SET_MAX_RET_PKT_SIZE = 0x37,
+ DT_NULL_PKT = 0x09,
+ DT_BLANK_PKT = 0x19,
+ DT_GEN_LONG_WR = 0x29,
+ DT_DCS_LONG_WR = 0x39,
+ DT_20BIT_LOOSE_YCBCR = 0x0c,
+ DT_24BIT_YCBCR = 0x1c,
+ DT_16BIT_YCBCR = 0x2c,
+ DT_30BIT_RGB_101010 = 0x0d,
+ DT_36BIT_RGB_121212 = 0x1d,
+ DT_12BIT_YCBCR = 0x3d,
+ DT_16BIT_RGB_565 = 0x0e,
+ DT_18BIT_RGB_666 = 0x1e,
+ DT_18BIT_LOOSE_RGB_666 = 0x2e,
+ DT_24BIT_RGB_888 = 0x3e
+};
+
+/* DCS Command List */
+#define DCS_ENTER_IDLE_MODE 0x39
+#define DCS_ENTER_INVERT_MODE 0x21
+#define DCS_ENTER_NORMAL_MODE 0x13
+#define DCS_ENTER_PARTIAL_MODE 0x12
+#define DCS_ENTER_SLEEP_MODE 0x10
+#define DCS_EXIT_IDLE_MODE 0x38
+#define DCS_EXIT_INVERT_MODE 0x20
+#define DCS_EXIT_SLEEP_MODE 0x11
+#define DCS_GET_3D_CONTROL 0x3f
+#define DCS_GET_ADDRESS_MODE 0x0b
+#define DCS_GET_BLUE_CHANNEL 0x08
+#define DCS_GET_DIAGNOSTIC_RESULT 0x0f
+#define DCS_GET_DISPLAY_MODE 0x0d
+#define DCS_GET_GREEN_CHANNEL 0x07
+#define DCS_GET_PIXEL_FORMAT 0x0c
+#define DCS_GET_POWER_MODE 0x0a
+#define DCS_GET_RED_CHANNEL 0x06
+#define DCS_GET_SCANLINE 0x45
+#define DCS_GET_SIGNAL_MODE 0x0e
+#define DCS_NOP 0x00
+#define DCS_READ_DDB_CONTINUE 0xa8
+#define DCS_READ_DDB_START 0xa1
+#define DCS_READ_MEMORY_CONTINUE 0x3e
+#define DCS_READ_MEMORY_START 0x2e
+#define DCS_SET_3D_CONTROL 0x3d
+#define DCS_SET_ADDRESS_MODE 0x36
+#define DCS_SET_COLUMN_ADDRESS 0x2a
+#define DCS_SET_DISPLAY_OFF 0x28
+#define DCS_SET_DISPLAY_ON 0x29
+#define DCS_SET_GAMMA_CURVE 0x26
+#define DCS_SET_PAGE_ADDRESS 0x2b
+#define DCS_SET_PARTIAL_COLUMNS 0x31
+#define DCS_SET_PARTIAL_ROWS 0x30
+#define DCS_SET_PIXEL_FORMAT 0x3a
+#define DCS_SET_SCROLL_AREA 0x33
+#define DCS_SET_SCROLL_START 0x37
+#define DCS_SET_TEAR_OFF 0x34
+#define DCS_SET_TEAR_ON 0x35
+#define DCS_SET_TEAR_SCANLINE 0x44
+#define DCS_SET_VSYNC_TIMING 0x40
+#define DCS_SOFT_RESET 0x01
+#define DCS_WRITE_LUT 0x2d
+#define DCS_WRITE_MEMORY_CONTINUE 0x3c
+#define DCS_WRITE_MEMORY_START 0x2c
+
+/* Pheripheral to Host
+ * normal: 0x87(LPDT), data_type, 0, 0, ecc. (write or tearing-effect)
+ * error: 0x87(LPDT), 0x02, error_code[15:0], ecc.
+ * short read: 0x87, data_type, data0, data1, ecc
+ * long read: 0x87, data_type, word_cnt[15:0], ecc, data0, ... data(N-1),
+ * checksum(or 0)[15:0].
+ */
+enum mipi_dsi_data_type_peripheral_e {
+ DT_RESP_TE = 0xba,
+ DT_RESP_ACK = 0x84,
+ DT_RESP_ACK_ERR = 0x02,
+ DT_RESP_EOT = 0x08,
+ DT_RESP_GEN_READ_1 = 0x11,
+ DT_RESP_GEN_READ_2 = 0x12,
+ DT_RESP_GEN_READ_LONG = 0x1a,
+ DT_RESP_DCS_READ_LONG = 0x1c,
+ DT_RESP_DCS_READ_1 = 0x21,
+ DT_RESP_DCS_READ_2 = 0x22,
+};
+
+struct dsi_cmd_request_s {
+ unsigned char data_type;
+ unsigned char vc_id;
+ unsigned char *payload;
+ unsigned short pld_count;
+ unsigned int req_ack;
+};
+
+/* MIPI DCS Pixel-to-Byte Format */
+#define DCS_PF_RSVD 0x0
+#define DCS_PF_3BIT 0x1
+#define DCS_PF_8BIT 0x2
+#define DCS_PF_12BIT 0x3
+#define DCS_PF_16BIT 0x5
+#define DCS_PF_18BIT 0x6
+#define DCS_PF_24BIT 0x7
+
+/* MIPI DSI/VENC Color Format Definitions */
+#define MIPI_DSI_VENC_COLOR_30B 0x0
+#define MIPI_DSI_VENC_COLOR_24B 0x1
+#define MIPI_DSI_VENC_COLOR_18B 0x2
+#define MIPI_DSI_VENC_COLOR_16B 0x3
+
+#define COLOR_16BIT_CFG_1 0x0
+#define COLOR_16BIT_CFG_2 0x1
+#define COLOR_16BIT_CFG_3 0x2
+#define COLOR_18BIT_CFG_1 0x3
+#define COLOR_18BIT_CFG_2 0x4
+#define COLOR_24BIT 0x5
+#define COLOR_20BIT_LOOSE 0x6
+#define COLOR_24_BIT_YCBCR 0x7
+#define COLOR_16BIT_YCBCR 0x8
+#define COLOR_30BIT 0x9
+#define COLOR_36BIT 0xa
+#define COLOR_12BIT 0xb
+#define COLOR_RGB_111 0xc
+#define COLOR_RGB_332 0xd
+#define COLOR_RGB_444 0xe
+
+/* MIPI DSI Relative REGISTERs Definitions */
+/* For MIPI_DSI_TOP_CNTL */
+#define BIT_DPI_COLOR_MODE 20
+#define BIT_IN_COLOR_MODE 16
+#define BIT_CHROMA_SUBSAMPLE 14
+#define BIT_COMP2_SEL 12
+#define BIT_COMP1_SEL 10
+#define BIT_COMP0_SEL 8
+#define BIT_DE_POL 6
+#define BIT_HSYNC_POL 5
+#define BIT_VSYNC_POL 4
+#define BIT_DPICOLORM 3
+#define BIT_DPISHUTDN 2
+#define BIT_EDPITE_INTR_PULSE 1
+#define BIT_ERR_INTR_PULSE 0
+
+/* For MIPI_DSI_DWC_CLKMGR_CFG_OS */
+#define BIT_TO_CLK_DIV 8
+#define BIT_TX_ESC_CLK_DIV 0
+
+/* For MIPI_DSI_DWC_PCKHDL_CFG_OS */
+#define BIT_CRC_RX_EN 4
+#define BIT_ECC_RX_EN 3
+#define BIT_BTA_EN 2
+#define BIT_EOTP_RX_EN 1
+#define BIT_EOTP_TX_EN 0
+
+/* For MIPI_DSI_DWC_VID_MODE_CFG_OS */
+#define BIT_LP_CMD_EN 15
+#define BIT_FRAME_BTA_ACK_EN 14
+#define BIT_LP_HFP_EN 13
+#define BIT_LP_HBP_EN 12
+#define BIT_LP_VCAT_EN 11
+#define BIT_LP_VFP_EN 10
+#define BIT_LP_VBP_EN 9
+#define BIT_LP_VSA_EN 8
+#define BIT_VID_MODE_TYPE 0
+
+/* For MIPI_DSI_DWC_PHY_STATUS_OS */
+#define BIT_PHY_ULPSACTIVENOT3LANE 12
+#define BIT_PHY_STOPSTATE3LANE 11
+#define BIT_PHY_ULPSACTIVENOT2LANE 10
+#define BIT_PHY_STOPSTATE2LANE 9
+#define BIT_PHY_ULPSACTIVENOT1LANE 8
+#define BIT_PHY_STOPSTATE1LANE 7
+#define BIT_PHY_RXULPSESC0LANE 6
+#define BIT_PHY_ULPSACTIVENOT0LANE 5
+#define BIT_PHY_STOPSTATE0LANE 4
+#define BIT_PHY_ULPSACTIVENOTCLK 3
+#define BIT_PHY_STOPSTATECLKLANE 2
+#define BIT_PHY_DIRECTION 1
+#define BIT_PHY_LOCK 0
+
+/* For MIPI_DSI_DWC_PHY_IF_CFG_OS */
+#define BIT_PHY_STOP_WAIT_TIME 8
+#define BIT_N_LANES 0
+
+/* For MIPI_DSI_DWC_DPI_COLOR_CODING_OS */
+#define BIT_LOOSELY18_EN 8
+#define BIT_DPI_COLOR_CODING 0
+
+/* For MIPI_DSI_DWC_GEN_HDR_OS */
+#define BIT_GEN_WC_MSBYTE 16
+#define BIT_GEN_WC_LSBYTE 8
+#define BIT_GEN_VC 6
+#define BIT_GEN_DT 0
+
+/* For MIPI_DSI_DWC_LPCLK_CTRL_OS */
+#define BIT_AUTOCLKLANE_CTRL 1
+#define BIT_TXREQUESTCLKHS 0
+
+/* For MIPI_DSI_DWC_DPI_CFG_POL_OS */
+#define BIT_COLORM_ACTIVE_LOW 4
+#define BIT_SHUTD_ACTIVE_LOW 3
+#define BIT_HSYNC_ACTIVE_LOW 2
+#define BIT_VSYNC_ACTIVE_LOW 1
+#define BIT_DATAEN_ACTIVE_LOW 0
+
+/* For MIPI_DSI_DWC_CMD_MODE_CFG_OS */
+#define BIT_MAX_RD_PKT_SIZE 24
+#define BIT_DCS_LW_TX 19
+#define BIT_DCS_SR_0P_TX 18
+#define BIT_DCS_SW_1P_TX 17
+#define BIT_DCS_SW_0P_TX 16
+#define BIT_GEN_LW_TX 14
+#define BIT_GEN_SR_2P_TX 13
+#define BIT_GEN_SR_1P_TX 12
+#define BIT_GEN_SR_0P_TX 11
+#define BIT_GEN_SW_2P_TX 10
+#define BIT_GEN_SW_1P_TX 9
+#define BIT_GEN_SW_0P_TX 8
+#define BIT_ACK_RQST_EN 1
+#define BIT_TEAR_FX_EN 0
+
+/* For MIPI_DSI_DWC_CMD_PKT_STATUS_OS */
+/* For DBI no use full */
+#define BIT_DBI_RD_CMD_BUSY 14
+#define BIT_DBI_PLD_R_FULL 13
+#define BIT_DBI_PLD_R_EMPTY 12
+#define BIT_DBI_PLD_W_FULL 11
+#define BIT_DBI_PLD_W_EMPTY 10
+#define BIT_DBI_CMD_FULL 9
+#define BIT_DBI_CMD_EMPTY 8
+/* For Generic interface */
+#define BIT_GEN_RD_CMD_BUSY 6
+#define BIT_GEN_PLD_R_FULL 5
+#define BIT_GEN_PLD_R_EMPTY 4
+#define BIT_GEN_PLD_W_FULL 3
+#define BIT_GEN_PLD_W_EMPTY 2
+#define BIT_GEN_CMD_FULL 1
+#define BIT_GEN_CMD_EMPTY 0
+
+/* For MIPI_DSI_TOP_MEAS_CNTL */
+/* measure vsync control */
+#define BIT_CNTL_MEAS_VSYNC 10
+/* tear measure enable */
+#define BIT_EDPITE_MEAS_EN 9
+/* not clear the counter */
+#define BIT_EDPITE_ACCUM_MEAS_EN 8
+#define BIT_EDPITE_VSYNC_SPAN 0
+
+/* For MIPI_DSI_TOP_STAT */
+/* signal from halt */
+#define BIT_STAT_EDPIHALT 31
+/* line number when edpite pulse */
+#define BIT_STAT_TE_LINE 16
+/* pixel number when edpite pulse */
+#define BIT_STAT_TE_PIXEL 0
+
+/* For MIPI_DSI_TOP_INTR_CNTL_STAT */
+/* State/Clear for pic_eof */
+#define BIT_STAT_CLR_DWC_PIC_EOF 21
+/* State/Clear for de_fall */
+#define BIT_STAT_CLR_DWC_DE_FALL 20
+/* State/Clear for de_rise */
+#define BIT_STAT_CLR_DWC_DE_RISE 19
+/* State/Clear for vs_fall */
+#define BIT_STAT_CLR_DWC_VS_FALL 18
+/* State/Clear for vs_rise */
+#define BIT_STAT_CLR_DWC_VS_RISE 17
+/* State/Clear for edpite */
+#define BIT_STAT_CLR_DWC_EDPITE 16
+/* end of picture */
+#define BIT_PIC_EOF 5
+/* data enable fall */
+#define BIT_DE_FALL 4
+/* data enable rise */
+#define BIT_DE_RISE 3
+/* vsync fall */
+#define BIT_VS_FALL 2
+/* vsync rise */
+#define BIT_VS_RISE 1
+/* edpite int enable */
+#define BIT_EDPITE_INT_EN 0
+
+/* For MIPI_DSI_TOP_MEAS_CNTL */
+/* vsync measure enable */
+#define BIT_VSYNC_MEAS_EN 19
+/* vsync accumulate measure */
+#define BIT_VSYNC_ACCUM_MEAS_EN 18
+/* vsync span */
+#define BIT_VSYNC_SPAN 10
+/* tearing measure enable */
+#define BIT_TE_MEAS_EN 9
+/* tearing accumulate measure */
+#define BIT_TE_ACCUM_MEAS_EN 8
+/* tearing span */
+#define BIT_TE_SPAN 0
+
+/* For MIPI_DSI_DWC_INT_ST0_OS */
+/* LP1 contention error from lane0 */
+#define BIT_DPHY_ERR_4 20
+/* LP0 contention error from lane0 */
+#define BIT_DPHY_ERR_3 19
+/* ErrControl error from lane0 */
+#define BIT_DPHY_ERR_2 18
+/* ErrSyncEsc error from lane0 */
+#define BIT_DPHY_ERR_1 17
+/* ErrEsc escape error lane0 */
+#define BIT_DPHY_ERR_0 16
+#define BIT_ACK_ERR_15 15
+#define BIT_ACK_ERR_14 14
+#define BIT_ACK_ERR_13 13
+#define BIT_ACK_ERR_12 12
+#define BIT_ACK_ERR_11 11
+#define BIT_ACK_ERR_10 10
+#define BIT_ACK_ERR_9 9
+#define BIT_ACK_ERR_8 8
+#define BIT_ACK_ERR_7 7
+#define BIT_ACK_ERR_6 6
+#define BIT_ACK_ERR_5 5
+#define BIT_ACK_ERR_4 4
+#define BIT_ACK_ERR_3 3
+#define BIT_ACK_ERR_2 2
+#define BIT_ACK_ERR_1 1
+#define BIT_ACK_ERR_0 0
+
+/* Command transfer type in command mode */
+#define DCS_TRANS_HS 0
+#define DCS_TRANS_LP 1
+
+#define MIPI_DSI_DCS_NO_ACK 0
+#define MIPI_DSI_DCS_REQ_ACK 1
+
+/* DSI Tear Defines */
+#define MIPI_DCS_SET_TEAR_ON_MODE_0 0
+#define MIPI_DCS_SET_TEAR_ON_MODE_1 1
+#define MIPI_DCS_ENABLE_TEAR 1
+#define MIPI_DCS_DISABLE_TEAR 0
+
+/* Pixel FIFO Depth */
+#define PIXEL_FIFO_DEPTH 1440
+
+#define BYTE_PER_PIXEL_COLOR_16BIT_CFG_1 2
+#define BYTE_PER_PIXEL_COLOR_16BIT_CFG_2 2
+#define BYTE_PER_PIXEL_COLOR_16BIT_CFG_3 2
+#define BYTE_PER_PIXEL_COLOR_18BIT_CFG_1 3
+#define BYTE_PER_PIXEL_COLOR_18BIT_CFG_2 3
+#define BYTE_PER_PIXEL_COLOR_24BIT 3
+#define BYTE_PER_PIXEL_COLOR_20BIT_LOOSE 3
+#define BYTE_PER_PIXEL_COLOR_24_BIT_YCBCR 3
+#define BYTE_PER_PIXEL_COLOR_16BIT_YCBCR 2
+#define BYTE_PER_PIXEL_COLOR_30BIT 4
+#define BYTE_PER_PIXEL_COLOR_36BIT 5
+/* in fact it should be 1.5(12bit) */
+#define BYTE_PER_PIXEL_COLOR_12BIT 3
+
+/* Tearing Interrupt Bit */
+#define INT_TEARING 6
+
+enum tv_enc_lcd_type_e {
+ TV_ENC_LCD480x234 = 0,
+ /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+ TV_ENC_LCD480x234_dsi36b = 1,
+ TV_ENC_LCD240x160 = 2,
+ /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+ TV_ENC_LCD240x160_dsi36b = 3,
+ TV_ENC_LCD720x480 = 4,
+ /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+ TV_ENC_LCD720x480_dsi36b = 5,
+ TV_ENC_LCD720x576 = 6,
+ /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+ TV_ENC_LCD720x576_dsi36b = 7,
+ TV_ENC_LCD1280x720 = 8,
+ /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+ TV_ENC_LCD1280x720_dsi36b = 9,
+ TV_ENC_LCD1920x1080 = 10,
+ /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+ TV_ENC_LCD1920x1080_dsi36b = 11,
+ TV_ENC_LCD1920x2205 = 12,
+ /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+ TV_ENC_LCD1920x2205_dsi36b = 13,
+ TV_ENC_LCD2560x1600 = 14,
+ /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+ TV_ENC_LCD2560x1600_dsi36b = 15,
+ TV_ENC_LCD3840x2440 = 16,
+ /* For MIPI_DSI 36-bit color: sample rate=2, 1 pixel per 2 cycle */
+ TV_ENC_LCD3840x2440_dsi36b = 17,
+ TV_ENC_LCD3840x2160p_vic03 = 18,
+ TV_ENC_LCD4096x2160p_vic04 = 19,
+ TV_ENC_LCD640x480 = 20,
+ TV_ENC_LCD1920x1200p = 21,
+ TV_ENC_LCD240x160_dsi = 22,
+ TV_ENC_LCD240x160_slow = 23,
+ TV_ENC_LCD3840x2160p_vic01 = 24,
+ TV_ENC_LCD2048x1536 = 25,
+ TV_ENC_LCD768x1024p = 26,
+ TV_ENC_LCD_TYPE_MAX
+}; /* tv encoder output format */
+
+/* DCS COMMAND LIST */
+#define DCS_CMD_CODE_ENTER_IDLE_MODE 0x0
+#define DCS_CMD_CODE_ENTER_INVERT_MODE 0x1
+#define DCS_CMD_CODE_ENTER_NORMAL_MODE 0x2
+#define DCS_CMD_CODE_ENTER_PARTIAL_MODE 0x3
+#define DCS_CMD_CODE_ENTER_SLEEP_MODE 0x4
+#define DCS_CMD_CODE_EXIT_IDLE_MODE 0x5
+#define DCS_CMD_CODE_EXIT_INVERT_MODE 0x6
+#define DCS_CMD_CODE_EXIT_SLEEP_MODE 0x7
+#define DCS_CMD_CODE_NOP 0x8
+#define DCS_CMD_CODE_SET_DISPLAY_OFF 0x9
+#define DCS_CMD_CODE_SET_DISPLAY_ON 0xa
+#define DCS_CMD_CODE_SET_TEAR_OFF 0xb
+#define DCS_CMD_CODE_SOFT_RESET 0xc
+
+/* DPHY standard timing */
+/* unit: MHz */
+#define MIPI_PHY_CLK_MAX 1000
+
+/* **** DPHY timing parameter Value (unit: 0.01ns) **** */
+/* >100ns (4M) */
+#define DPHY_TIME_LP_TESC(ui) (250 * 100)
+/* >50ns */
+#define DPHY_TIME_LP_LPX(ui) (100 * 100)
+/* (lpx, 2*lpx) */
+#define DPHY_TIME_LP_TA_SURE(ui) DPHY_TIME_LP_LPX(ui)
+/* 4*lpx */
+#define DPHY_TIME_LP_TA_GO(ui) (4 * DPHY_TIME_LP_LPX(ui))
+/* 5*lpx */
+#define DPHY_TIME_LP_TA_GETX(ui) (5 * DPHY_TIME_LP_LPX(ui))
+/* >100ns */
+#define DPHY_TIME_HS_EXIT(ui) (120 * 100)
+/* max(8*ui, 60+4*ui), (teot)<105+12*ui */
+#define DPHY_TIME_HS_TRAIL(ui) ((ui > (60 * 100 / 4)) ? \
+ (8 * ui) : ((60 * 100) + 4 * ui))
+/* (40+4*ui, 85+6*ui) */
+#define DPHY_TIME_HS_PREPARE(ui) (50 * 100 + 4 * t_ui)
+/* hs_prepare+hs_zero >145+10*ui */
+#define DPHY_TIME_HS_ZERO(ui) (160 * 100 + 10 * ui - \
+ DPHY_TIME_HS_PREPARE(ui))
+/* >60ns, (teot)<105+12*ui */
+#define DPHY_TIME_CLK_TRAIL(ui) (70 * 100)
+/* >60+52*ui */
+#define DPHY_TIME_CLK_POST(ui) (70 * 100 + 52 * ui)
+/* (38, 95) */
+#define DPHY_TIME_CLK_PREPARE(ui) (50 * 100)
+/* clk_prepare+clk_zero > 300 */
+#define DPHY_TIME_CLK_ZERO(ui) (320 * 100 - DPHY_TIME_CLK_PREPARE(ui))
+/* >8*ui */
+#define DPHY_TIME_CLK_PRE(ui) (10 * ui)
+/* >100us */
+#define DPHY_TIME_INIT(ui) (110 * 1000 * 100)
+/* >1ms */
+#define DPHY_TIME_WAKEUP(ui) (1020 * 1000 * 100)
+
+
+struct dsi_phy_s {
+ unsigned int lp_tesc;
+ unsigned int lp_lpx;
+ unsigned int lp_ta_sure;
+ unsigned int lp_ta_go;
+ unsigned int lp_ta_get;
+ unsigned int hs_exit;
+ unsigned int hs_trail;
+ unsigned int hs_zero;
+ unsigned int hs_prepare;
+ unsigned int clk_trail;
+ unsigned int clk_post;
+ unsigned int clk_zero;
+ unsigned int clk_prepare;
+ unsigned int clk_pre;
+ unsigned int init;
+ unsigned int wakeup;
+};
+
+struct dsi_vid_s {
+ unsigned int hline;
+ unsigned int hsa;
+ unsigned int hbp;
+ unsigned int vsa;
+ unsigned int vbp;
+ unsigned int vfp;
+ unsigned int vact;
+
+ /* for non-burst chunk overhead */
+ unsigned int pixel_per_chunk;
+ unsigned int num_of_chunk;
+ unsigned int vid_null_size;
+};
+
+#define DSI_CMD_SIZE_MAX 2000
+/* #define DSI_CMD_READ_VALID // DPHY don't support for M8 */
+
+extern void mipi_dsi_print_info(struct lcd_config_s *pconf);
+extern int lcd_mipi_dsi_init_table_detect(struct device_node *m_node,
+ struct dsi_config_s *dconf, int on_off);
+extern void lcd_mipi_dsi_config_set(struct lcd_config_s *pconf);
+extern void lcd_mipi_dsi_config_post(struct lcd_config_s *pconf);
+extern void mipi_dsi_link_off(struct lcd_config_s *pconf);
+extern void lcd_mipi_control_set(struct lcd_config_s *pconf, int status);
+
+#endif
--- /dev/null
+obj-$(CONFIG_AMLOGIC_LCD_TV) += lcd_tv.o lcd_drv.o
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tv/lcd_drv.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#ifdef CONFIG_AML_VPU
+#include <linux/amlogic/vpu.h>
+#endif
+#include <linux/amlogic/media/vout/vinfo.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include "lcd_tv.h"
+#include "../lcd_reg.h"
+#include "../lcd_common.h"
+
+/* interrupt source */
+#define INT_VIU_VSYNC 35
+#define INT_VENC_VX1 110
+
+static int vx1_fsm_acq_st;
+
+#define VX1_TRAINING_TIMEOUT 60 /* vsync cnt */
+static int vx1_training_wait_cnt;
+static int vx1_training_stable_cnt;
+static int vx1_timeout_reset_flag;
+static struct tasklet_struct lcd_vx1_reset_tasklet;
+static int lcd_vx1_intr_request;
+
+#define VX1_HPLL_INTERVAL (HZ)
+static struct timer_list vx1_hpll_timer;
+
+static int lcd_type_supported(struct lcd_config_s *pconf)
+{
+ int lcd_type = pconf->lcd_basic.lcd_type;
+ int ret = -1;
+
+ switch (lcd_type) {
+ case LCD_LVDS:
+ case LCD_VBYONE:
+ ret = 0;
+ break;
+ default:
+ LCDERR("invalid lcd type: %s(%d)\n",
+ lcd_type_type_to_str(lcd_type), lcd_type);
+ break;
+ }
+ return ret;
+}
+
+static void lcd_vbyone_phy_set(struct lcd_config_s *pconf, int status)
+{
+ unsigned int vswing, preem, ext_pullup;
+ unsigned int data32;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s: %d\n", __func__, status);
+
+ if (status) {
+ ext_pullup =
+ (pconf->lcd_control.vbyone_config->phy_vswing >> 4) & 1;
+ vswing = pconf->lcd_control.vbyone_config->phy_vswing & 0xf;
+ preem = pconf->lcd_control.vbyone_config->phy_preem;
+ if (vswing > 7) {
+ LCDERR("%s: wrong vswing_level=%d, use default\n",
+ __func__, vswing);
+ vswing = VX1_PHY_VSWING_DFT;
+ }
+ if (preem > 7) {
+ LCDERR("%s: wrong preemphasis_level=%d, use default\n",
+ __func__, preem);
+ preem = VX1_PHY_PREEM_DFT;
+ }
+ data32 = 0x6e0ec900 | (vswing << 3) | (ext_pullup << 10);
+ if (ext_pullup)
+ data32 &= ~(1 << 15);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, data32);
+ data32 = 0x00000a7c | (preem << 20);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, data32);
+ /*lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x6e0ec918);*/
+ /*lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x00000a7c);*/
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x00ff0800);
+ } else {
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x0);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x0);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x0);
+ }
+}
+
+static void lcd_lvds_phy_set(struct lcd_config_s *pconf, int status)
+{
+ unsigned int vswing, preem, clk_vswing, clk_preem, channel_on;
+ unsigned int data32;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s: %d\n", __func__, status);
+
+ if (status) {
+ vswing = pconf->lcd_control.lvds_config->phy_vswing;
+ preem = pconf->lcd_control.lvds_config->phy_preem;
+ clk_vswing = pconf->lcd_control.lvds_config->phy_clk_vswing;
+ clk_preem = pconf->lcd_control.lvds_config->phy_clk_preem;
+ if (vswing > 7) {
+ LCDERR("%s: wrong vswing_level=%d, use default\n",
+ __func__, vswing);
+ vswing = LVDS_PHY_VSWING_DFT;
+ }
+ if (preem > 7) {
+ LCDERR("%s: wrong preem_level=%d, use default\n",
+ __func__, preem);
+ preem = LVDS_PHY_PREEM_DFT;
+ }
+ if (clk_vswing > 3) {
+ LCDERR("%s: wrong clk_vswing_level=%d, use default\n",
+ __func__, clk_vswing);
+ clk_vswing = LVDS_PHY_CLK_VSWING_DFT;
+ }
+ if (clk_preem > 7) {
+ LCDERR("%s: wrong clk_preem_level=%d, use default\n",
+ __func__, clk_preem);
+ clk_preem = LVDS_PHY_CLK_PREEM_DFT;
+ }
+ channel_on = lcd_lvds_channel_on_value(pconf);
+
+ data32 = 0x606cca80 | (vswing << 26) | (preem << 0);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, data32);
+ /*lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x6c6cca80);*/
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x0000006c);
+ data32 = (channel_on << 16) | 0x0800 | /* DIF_TX_CTL5 */
+ (clk_vswing << 8) | (clk_preem << 5); /* DIF_TX_CTL4 */
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, data32);
+ /*lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x0fff0800);*/
+ } else {
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, 0x0);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, 0x0);
+ lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, 0x0);
+ }
+}
+
+static void lcd_tcon_set(struct lcd_config_s *pconf)
+{
+ lcd_vcbus_write(L_RGB_BASE_ADDR, 0);
+ lcd_vcbus_write(L_RGB_COEFF_ADDR, 0x400);
+ aml_lcd_notifier_call_chain(LCD_EVENT_GAMMA_UPDATE, NULL);
+
+ switch (pconf->lcd_basic.lcd_bits) {
+ case 8:
+ lcd_vcbus_write(L_DITH_CNTL_ADDR, 0x400);
+ break;
+ case 6:
+ lcd_vcbus_write(L_DITH_CNTL_ADDR, 0x600);
+ break;
+ case 10:
+ default:
+ lcd_vcbus_write(L_DITH_CNTL_ADDR, 0x0);
+ break;
+ }
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_LVDS:
+ lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 0, 1);
+ if (pconf->lcd_timing.vsync_pol)
+ lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 1, 1);
+ break;
+ case LCD_VBYONE:
+ if (pconf->lcd_timing.hsync_pol)
+ lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 0, 1);
+ if (pconf->lcd_timing.vsync_pol)
+ lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 1, 1);
+ break;
+ default:
+ break;
+ }
+
+ if (lcd_vcbus_read(VPP_MISC) & VPP_OUT_SATURATE)
+ lcd_vcbus_write(VPP_MISC,
+ lcd_vcbus_read(VPP_MISC) & ~(VPP_OUT_SATURATE));
+}
+
+static void lcd_lvds_clk_util_set(struct lcd_config_s *pconf)
+{
+ unsigned int phy_div;
+
+ if (pconf->lcd_control.lvds_config->dual_port)
+ phy_div = 2;
+ else
+ phy_div = 1;
+
+ /* set fifo_clk_sel: div 7 */
+ lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL0, (1 << 6));
+ /* set cntl_ser_en: 8-channel to 1 */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0xfff, 16, 12);
+
+ /* decoupling fifo enable, gated clock enable */
+ lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL1,
+ (1 << 30) | ((phy_div - 1) << 25) | (1 << 24));
+ /* decoupling fifo write enable after fifo enable */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL1, 1, 31, 1);
+}
+
+static void lcd_lvds_control_set(struct lcd_config_s *pconf)
+{
+ unsigned int bit_num = 1;
+ unsigned int pn_swap, port_swap, lane_reverse;
+ unsigned int dual_port, fifo_mode;
+ unsigned int lvds_repack = 1;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ lcd_lvds_clk_util_set(pconf);
+
+ lvds_repack = (pconf->lcd_control.lvds_config->lvds_repack) & 0x3;
+ pn_swap = (pconf->lcd_control.lvds_config->pn_swap) & 0x1;
+ dual_port = (pconf->lcd_control.lvds_config->dual_port) & 0x1;
+ port_swap = (pconf->lcd_control.lvds_config->port_swap) & 0x1;
+ lane_reverse = (pconf->lcd_control.lvds_config->lane_reverse) & 0x1;
+
+ switch (pconf->lcd_basic.lcd_bits) {
+ case 10:
+ bit_num = 0;
+ if (lvds_repack == 1)
+ lvds_repack = 2;
+ break;
+ case 8:
+ bit_num = 1;
+ break;
+ case 6:
+ bit_num = 2;
+ break;
+ case 4:
+ bit_num = 3;
+ break;
+ default:
+ bit_num = 1;
+ break;
+ }
+ if (dual_port)
+ fifo_mode = 0x3;
+ else
+ fifo_mode = 0x1;
+
+ lcd_vcbus_write(LVDS_PACK_CNTL_ADDR,
+ (lvds_repack << 0) | /* repack //[1:0] */
+ (0 << 3) | /* reserve */
+ (0 << 4) | /* lsb first */
+ (pn_swap << 5) | /* pn swap */
+ (dual_port << 6) | /* dual port */
+ (0 << 7) | /* use tcon control */
+ (bit_num << 8) | /* 0:10bits, 1:8bits, 2:6bits, 3:4bits */
+ (0 << 10) | /* r_select //0:R, 1:G, 2:B, 3:0 */
+ (1 << 12) | /* g_select //0:R, 1:G, 2:B, 3:0 */
+ (2 << 14)); /* b_select //0:R, 1:G, 2:B, 3:0 */
+
+ lcd_vcbus_setb(LCD_PORT_SWAP, port_swap, 12, 1);
+
+ if (lane_reverse)
+ lcd_vcbus_setb(LVDS_GEN_CNTL, 0x03, 13, 2);
+
+ lcd_vcbus_write(LVDS_GEN_CNTL,
+ (lcd_vcbus_read(LVDS_GEN_CNTL) | (1 << 4) | (fifo_mode << 0)));
+
+ lcd_vcbus_setb(LVDS_GEN_CNTL, 1, 3, 1);
+}
+
+static void lcd_lvds_disable(void)
+{
+ lcd_vcbus_setb(LVDS_GEN_CNTL, 0, 3, 1); /* disable lvds fifo */
+}
+
+#if 0
+static void lcd_vbyone_ctlbits(int p3d_en, int p3d_lr, int mode)
+{
+ if (mode == 0) { /* insert at the first pixel */
+ lcd_vcbus_setb(VBO_PXL_CTRL,
+ (1 << p3d_en) | (p3d_lr & 0x1), 0, 4);
+ } else {
+ lcd_vcbus_setb(VBO_VBK_CTRL_0,
+ (1 << p3d_en) | (p3d_lr & 0x1), 0, 2);
+ }
+}
+#endif
+
+static void lcd_vbyone_sync_pol(int hsync_pol, int vsync_pol)
+{
+ lcd_vcbus_setb(VBO_VIN_CTRL, hsync_pol, 4, 1);
+ lcd_vcbus_setb(VBO_VIN_CTRL, vsync_pol, 5, 1);
+
+ lcd_vcbus_setb(VBO_VIN_CTRL, hsync_pol, 6, 1);
+ lcd_vcbus_setb(VBO_VIN_CTRL, vsync_pol, 7, 1);
+}
+
+static void lcd_vbyone_clk_util_set(struct lcd_config_s *pconf)
+{
+ unsigned int lcd_bits;
+ unsigned int div_sel, phy_div;
+
+ phy_div = pconf->lcd_control.vbyone_config->phy_div;
+
+ lcd_bits = 10;
+ switch (lcd_bits) {
+ case 6:
+ div_sel = 0;
+ break;
+ case 8:
+ div_sel = 2;
+ break;
+ case 10:
+ div_sel = 3;
+ break;
+ default:
+ div_sel = 3;
+ break;
+ }
+ /* set fifo_clk_sel */
+ lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL0, (div_sel << 6));
+ /* set cntl_ser_en: 8-channel to 1 */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0xfff, 16, 12);
+
+ /* decoupling fifo enable, gated clock enable */
+ lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL1,
+ (1 << 30) | ((phy_div - 1) << 25) | (1 << 24));
+ /* decoupling fifo write enable after fifo enable */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL1, 1, 31, 1);
+}
+
+static int lcd_vbyone_lanes_set(int lane_num, int byte_mode, int region_num,
+ int hsize, int vsize)
+{
+ int sublane_num;
+ int region_size[4];
+ int tmp;
+
+ switch (lane_num) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+ default:
+ return -1;
+ }
+ switch (region_num) {
+ case 1:
+ case 2:
+ case 4:
+ break;
+ default:
+ return -1;
+ }
+ if (lane_num % region_num)
+ return -1;
+ switch (byte_mode) {
+ case 3:
+ case 4:
+ break;
+ default:
+ return -1;
+ }
+ if (lcd_debug_print_flag) {
+ LCDPR("byte_mode=%d, lane_num=%d, region_num=%d\n",
+ byte_mode, lane_num, region_num);
+ }
+
+ sublane_num = lane_num / region_num; /* lane num in each region */
+ lcd_vcbus_setb(VBO_LANES, (lane_num - 1), 0, 3);
+ lcd_vcbus_setb(VBO_LANES, (region_num - 1), 4, 2);
+ lcd_vcbus_setb(VBO_LANES, (sublane_num - 1), 8, 3);
+ lcd_vcbus_setb(VBO_LANES, (byte_mode - 1), 11, 2);
+
+ if (region_num > 1) {
+ region_size[3] = (hsize / lane_num) * sublane_num;
+ tmp = (hsize % lane_num);
+ region_size[0] = region_size[3] + (((tmp / sublane_num) > 0) ?
+ sublane_num : (tmp % sublane_num));
+ region_size[1] = region_size[3] + (((tmp / sublane_num) > 1) ?
+ sublane_num : (tmp % sublane_num));
+ region_size[2] = region_size[3] + (((tmp / sublane_num) > 2) ?
+ sublane_num : (tmp % sublane_num));
+ lcd_vcbus_write(VBO_REGION_00, region_size[0]);
+ lcd_vcbus_write(VBO_REGION_01, region_size[1]);
+ lcd_vcbus_write(VBO_REGION_02, region_size[2]);
+ lcd_vcbus_write(VBO_REGION_03, region_size[3]);
+ }
+ lcd_vcbus_write(VBO_ACT_VSIZE, vsize);
+ /* different from FBC code!!! */
+ /* lcd_vcbus_setb(VBO_CTRL_H,0x80,11,5); */
+ /* different from simulation code!!! */
+ lcd_vcbus_setb(VBO_CTRL_H, 0x0, 0, 4);
+ lcd_vcbus_setb(VBO_CTRL_H, 0x1, 9, 1);
+ /* lcd_vcbus_setb(VBO_CTRL_L,enable,0,1); */
+
+ return 0;
+}
+
+static void lcd_vbyone_sw_reset(void)
+{
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ /* force PHY to 0 */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2);
+ lcd_vcbus_write(VBO_SOFT_RST, 0x1ff);
+ udelay(5);
+ /* realease PHY */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2);
+ lcd_vcbus_write(VBO_SOFT_RST, 0);
+}
+
+static void lcd_vbyone_wait_timing_stable(void)
+{
+ unsigned int timing_state;
+ int i = 200;
+
+ timing_state = lcd_vcbus_read(VBO_INTR_STATE) & 0x1ff;
+ while ((timing_state) && (i > 0)) {
+ /* clear video timing error intr */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x7, 0, 3);
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 3);
+ mdelay(2);
+ timing_state = lcd_vcbus_read(VBO_INTR_STATE) & 0x1ff;
+ i--;
+ };
+ if (lcd_debug_print_flag) {
+ LCDPR("vbyone timing state: 0x%03x, i=%d\n",
+ timing_state, (200 - i));
+ }
+ mdelay(2);
+}
+
+static void lcd_vbyone_control_set(struct lcd_config_s *pconf)
+{
+ int lane_count, byte_mode, region_num, hsize, vsize, color_fmt;
+ int vin_color, vin_bpp;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ hsize = pconf->lcd_basic.h_active;
+ vsize = pconf->lcd_basic.v_active;
+ lane_count = pconf->lcd_control.vbyone_config->lane_count; /* 8 */
+ region_num = pconf->lcd_control.vbyone_config->region_num; /* 2 */
+ byte_mode = pconf->lcd_control.vbyone_config->byte_mode; /* 4 */
+ color_fmt = pconf->lcd_control.vbyone_config->color_fmt; /* 4 */
+
+ lcd_vbyone_clk_util_set(pconf);
+#if 0
+ switch (color_fmt) {
+ case 0:/* SDVT_VBYONE_18BPP_RGB */
+ vin_color = 4;
+ vin_bpp = 2;
+ break;
+ case 1:/* SDVT_VBYONE_18BPP_YCBCR444 */
+ vin_color = 0;
+ vin_bpp = 2;
+ break;
+ case 2:/* SDVT_VBYONE_24BPP_RGB */
+ vin_color = 4;
+ vin_bpp = 1;
+ break;
+ case 3:/* SDVT_VBYONE_24BPP_YCBCR444 */
+ vin_color = 0;
+ vin_bpp = 1;
+ break;
+ case 4:/* SDVT_VBYONE_30BPP_RGB */
+ vin_color = 4;
+ vin_bpp = 0;
+ break;
+ case 5:/* SDVT_VBYONE_30BPP_YCBCR444 */
+ vin_color = 0;
+ vin_bpp = 0;
+ break;
+ default:
+ LCDERR("vbyone COLOR_FORMAT unsupport\n");
+ return;
+ }
+#else
+ vin_color = 4; /* fixed RGB */
+ vin_bpp = 0; /* fixed 30bbp 4:4:4 */
+#endif
+
+ /* set Vbyone vin color format */
+ lcd_vcbus_setb(VBO_VIN_CTRL, vin_color, 8, 3);
+ lcd_vcbus_setb(VBO_VIN_CTRL, vin_bpp, 11, 2);
+
+ lcd_vbyone_lanes_set(lane_count, byte_mode, region_num, hsize, vsize);
+ /*set hsync/vsync polarity to let the polarity is low active*/
+ /*inside the VbyOne */
+ lcd_vbyone_sync_pol(0, 0);
+
+ /* below line copy from simulation */
+ /* gate the input when vsync asserted */
+ lcd_vcbus_setb(VBO_VIN_CTRL, 1, 0, 2);
+ /* lcd_vcbus_write(VBO_VBK_CTRL_0,0x13);
+ * lcd_vcbus_write(VBO_VBK_CTRL_1,0x56);
+ * lcd_vcbus_write(VBO_HBK_CTRL,0x3478);
+ * lcd_vcbus_setb(VBO_PXL_CTRL,0x2,0,4);
+ * lcd_vcbus_setb(VBO_PXL_CTRL,0x3,VBO_PXL_CTR1_BIT,VBO_PXL_CTR1_WID);
+ * set_vbyone_ctlbits(1,0,0);
+ */
+
+ /* PAD select: */
+ if ((lane_count == 1) || (lane_count == 2))
+ lcd_vcbus_setb(LCD_PORT_SWAP, 1, 9, 2);
+ else if (lane_count == 4)
+ lcd_vcbus_setb(LCD_PORT_SWAP, 2, 9, 2);
+ else
+ lcd_vcbus_setb(LCD_PORT_SWAP, 0, 9, 2);
+ /* lcd_vcbus_setb(LCD_PORT_SWAP, 1, 8, 1);//reverse lane output order */
+
+ /* Mux pads in combo-phy: 0 for dsi; 1 for lvds or vbyone; 2 for edp */
+ lcd_hiu_write(HHI_DSI_LVDS_EDP_CNTL0, 0x1);
+ lcd_vcbus_setb(VBO_CTRL_L, 1, 0, 1);
+
+ /* force vencl clk enable, otherwise,
+ * it might auto turn off by mipi DSI
+ */
+ /* lcd_vcbus_setb(VPU_MISC_CTRL, 1, 0, 1); */
+
+ lcd_vbyone_wait_timing_stable();
+ lcd_vbyone_sw_reset();
+}
+
+static void lcd_vbyone_disable(void)
+{
+ lcd_vcbus_setb(VBO_CTRL_L, 0, 0, 1);
+}
+
+#define VBYONE_INTR_UNMASK 0x2bff /* 0x2a00 */
+ /* enable htpdn_fail,lockn_fail,acq_hold */
+void lcd_vbyone_interrupt_enable(int flag)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct vbyone_config_s *vx1_conf;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s: %d\n", __func__, flag);
+
+ vx1_conf = lcd_drv->lcd_config->lcd_control.vbyone_config;
+ if (flag) {
+ if (vx1_conf->intr_en) {
+ vx1_fsm_acq_st = 0;
+ /* clear interrupt */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x01ff, 0, 9);
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 9);
+
+ /* set hold in FSM_ACQ */
+ lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0xffff, 0, 16);
+ /* enable interrupt */
+ lcd_vcbus_setb(VBO_INTR_UNMASK,
+ VBYONE_INTR_UNMASK, 0, 15);
+ } else {
+ /* mask interrupt */
+ lcd_vcbus_write(VBO_INTR_UNMASK, 0x0);
+ if (vx1_conf->vsync_intr_en) {
+ /* keep holder for vsync monitor enabled */
+ /* set hold in FSM_ACQ */
+ lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0xffff, 0, 16);
+ } else {
+ /* release holder for vsync monitor disabled */
+ /* release hold in FSM_ACQ */
+ lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0, 0, 16);
+ }
+
+ vx1_fsm_acq_st = 0;
+ /* clear interrupt */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x01ff, 0, 9);
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 9);
+ }
+ } else {
+ /* mask interrupt */
+ lcd_vcbus_write(VBO_INTR_UNMASK, 0x0);
+ /* release hold in FSM_ACQ */
+ lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0, 0, 16);
+ lcd_vx1_intr_request = 0;
+ }
+}
+
+static void lcd_vbyone_interrupt_init(void)
+{
+ /* set hold in FSM_ACQ */
+ lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0xffff, 0, 16);
+ /* set hold in FSM_CDR */
+ lcd_vcbus_setb(VBO_FSM_HOLDER_H, 0, 0, 16);
+ /* not wait lockn to 1 in FSM_ACQ */
+ lcd_vcbus_setb(VBO_CTRL_L, 1, 10, 1);
+ /* lcd_vcbus_setb(VBO_CTRL_L, 0, 9, 1);*/ /*use sw pll_lock */
+ /* reg_pll_lock = 1 to realease force to FSM_ACQ*/
+ /*lcd_vcbus_setb(VBO_CTRL_H, 1, 13, 1); */
+
+ /* vx1 interrupt setting */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 12, 1); /* intr pulse width */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x01ff, 0, 9); /* clear interrupt */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 9);
+
+ vx1_fsm_acq_st = 0;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+}
+
+void lcd_vbyone_wait_stable(void)
+{
+ int i = 5000;
+
+ while (((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) != 0x20) && (i > 0)) {
+ udelay(50);
+ i--;
+ }
+ LCDPR("%s status: 0x%x, i=%d\n",
+ __func__, lcd_vcbus_read(VBO_STATUS_L), (5000 - i));
+ vx1_training_wait_cnt = 0;
+ vx1_training_stable_cnt = 0;
+ vx1_fsm_acq_st = 0;
+ lcd_vbyone_interrupt_enable(1);
+}
+
+#define LCD_VX1_WAIT_STABLE_DELAY 300 /* ms */
+static void lcd_vx1_wait_stable_delayed(struct work_struct *work)
+{
+ if (lcd_vx1_intr_request == 0)
+ return;
+
+ lcd_vbyone_wait_stable();
+}
+
+static void lcd_vx1_wait_hpd(void)
+{
+ int i = 0;
+
+ if (lcd_debug_print_flag)
+ LCDPR("vx1 wait hpd to low ...\n");
+
+ while (lcd_vcbus_read(VBO_STATUS_L) & 0x40) {
+ if (i++ >= 10000)
+ break;
+ udelay(50);
+ }
+ mdelay(10); /* add 10ms delay for compatibility */
+ if (lcd_vcbus_read(VBO_STATUS_L) & 0x40)
+ LCDPR("%s: hpd=%d\n", __func__,
+ ((lcd_vcbus_read(VBO_STATUS_L) >> 6) & 0x1));
+ else
+ LCDPR("%s: hpd=%d, i=%d\n", __func__,
+ ((lcd_vcbus_read(VBO_STATUS_L) >> 6) & 0x1), i);
+}
+
+#define LCD_PCLK_TOLERANCE 2000000 /* 2M */
+#define LCD_ENCL_CLK_ERR_CNT_MAX 3
+static unsigned char lcd_encl_clk_err_cnt;
+static void lcd_vx1_hpll_timer_handler(unsigned long arg)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ int encl_clk;
+#if 0
+ int pclk, pclk_min, pclk_max;
+#endif
+
+ if (lcd_drv->lcd_status == 0)
+ goto vx1_hpll_timer_end;
+
+#if 0
+ pclk = lcd_drv->lcd_config->lcd_timing.lcd_clk;
+ pclk_min = pclk - LCD_PCLK_TOLERANCE;
+ pclk_max = pclk + LCD_PCLK_TOLERANCE;
+ encl_clk = lcd_encl_clk_msr();
+ if ((encl_clk < pclk_min) || (encl_clk > pclk_max)) {
+ LCDPR("%s: pll frequency error: %d\n", __func__, encl_clk);
+ lcd_pll_reset();
+ }
+#else
+ encl_clk = lcd_encl_clk_msr();
+ if (encl_clk == 0)
+ lcd_encl_clk_err_cnt++;
+ else
+ lcd_encl_clk_err_cnt = 0;
+ if (lcd_encl_clk_err_cnt >= LCD_ENCL_CLK_ERR_CNT_MAX) {
+ LCDPR("%s: pll frequency error: %d\n", __func__, encl_clk);
+ lcd_pll_reset();
+ lcd_encl_clk_err_cnt = 0;
+ }
+#endif
+
+vx1_hpll_timer_end:
+ vx1_hpll_timer.expires = jiffies + VX1_HPLL_INTERVAL;
+ add_timer(&vx1_hpll_timer);
+}
+
+static void lcd_vx1_hold_reset(void)
+{
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ vx1_fsm_acq_st = 0;
+ lcd_vcbus_write(VBO_INTR_UNMASK, 0x0); /* mask interrupt */
+ /* clear interrupt */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x01ff, 0, 9);
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 9);
+
+ /* clear FSM_continue */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1);
+
+ /* force PHY to 0 */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2);
+ lcd_vcbus_write(VBO_SOFT_RST, 0x1ff);
+ udelay(5);
+ /* clear lockn raising flag */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 7, 1);
+ /* realease PHY */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2);
+ /* clear lockn raising flag */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 7, 1);
+ lcd_vcbus_write(VBO_SOFT_RST, 0);
+
+ /* enable interrupt */
+ lcd_vcbus_setb(VBO_INTR_UNMASK, VBYONE_INTR_UNMASK, 0, 15);
+}
+
+static void lcd_vx1_timeout_reset(unsigned long data)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ if (vx1_timeout_reset_flag == 0)
+ return;
+
+ LCDPR("%s\n", __func__);
+ if (vx1_timeout_reset_flag == 1)
+ lcd_drv->module_reset();
+ else
+ lcd_drv->module_tiny_reset();
+ if (lcd_drv->lcd_config->lcd_control.vbyone_config->intr_en)
+ lcd_vx1_hold_reset();
+ vx1_timeout_reset_flag = 0;
+}
+
+static irqreturn_t lcd_vbyone_vsync_isr(int irq, void *dev_id)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct vbyone_config_s *vx1_conf;
+
+ if (lcd_drv->lcd_status == 0)
+ return IRQ_HANDLED;
+ if (lcd_vcbus_read(VBO_STATUS_L) & 0x40) /* hpd detect */
+ return IRQ_HANDLED;
+
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 0, 1);
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 1);
+
+ vx1_conf = lcd_drv->lcd_config->lcd_control.vbyone_config;
+ if (vx1_conf->vsync_intr_en == 0) {
+ vx1_training_wait_cnt = 0;
+ return IRQ_HANDLED;
+ }
+
+#if 0
+ if (vx1_conf->intr_en == 0) {
+ if ((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) != 0x20)
+ if (vx1_timeout_reset_flag == 0) {
+ vx1_timeout_reset_flag = 1;
+ tasklet_schedule(&lcd_vx1_reset_tasklet);
+ }
+ }
+ return IRQ_HANDLED;
+ }
+#endif
+
+#if 1
+ if (vx1_training_wait_cnt >= VX1_TRAINING_TIMEOUT) {
+ if ((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) != 0x20) {
+ if (vx1_timeout_reset_flag == 0) {
+ vx1_timeout_reset_flag = 1;
+ tasklet_schedule(&lcd_vx1_reset_tasklet);
+ }
+ } else {
+ vx1_training_stable_cnt++;
+ if (vx1_training_stable_cnt >= 5) {
+ vx1_training_wait_cnt = 0;
+ vx1_training_stable_cnt = 0;
+ }
+ }
+ } else {
+ vx1_training_wait_cnt++;
+ }
+#else
+ if ((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) != 0x20) {
+ if (vx1_training_wait_cnt >= VX1_TRAINING_TIMEOUT) {
+ if (vx1_timeout_reset_flag == 0) {
+ vx1_timeout_reset_flag = 1;
+ tasklet_schedule(&lcd_vx1_reset_tasklet);
+ }
+ } else {
+ vx1_training_wait_cnt++;
+ }
+ } else {
+ vx1_training_stable_cnt++;
+ if (vx1_training_stable_cnt >= 5) {
+ vx1_training_wait_cnt = 0;
+ vx1_training_stable_cnt = 0;
+ }
+ }
+#endif
+
+ return IRQ_HANDLED;
+}
+
+#define VX1_LOCKN_WAIT_TIMEOUT 50
+static int vx1_lockn_wait_cnt;
+
+#define VX1_FSM_ACQ_NEXT_STEP_CONTINUE 0
+#define VX1_FSM_ACQ_NEXT_RELEASE_HOLDER 1
+#define VX1_FSM_ACQ_NEXT VX1_FSM_ACQ_NEXT_STEP_CONTINUE
+static irqreturn_t lcd_vbyone_interrupt_handler(int irq, void *dev_id)
+{
+ unsigned int data32, data32_1;
+ int encl_clk;
+
+ lcd_vcbus_write(VBO_INTR_UNMASK, 0x0); /* mask interrupt */
+
+ encl_clk = lcd_encl_clk_msr();
+ data32 = (lcd_vcbus_read(VBO_INTR_STATE) & 0x7fff);
+#if 0
+ if (data32 & 0x1ff) { /* timing error */
+ LCDPR("vx1 timing err: VDE_CHK_LH=0x%04x,0x%04x\n",
+ lcd_vcbus_read(VBO_TMCHK_VDE_STATE_L),
+ lcd_vcbus_read(VBO_TMCHK_VDE_STATE_H));
+ }
+#endif
+ /* clear the interrupt */
+ data32_1 = ((data32 >> 9) << 3);
+ if (data32 & 0x1c0)
+ data32_1 |= (1 << 2);
+ if (data32 & 0x38)
+ data32_1 |= (1 << 1);
+ if (data32 & 0x7)
+ data32_1 |= (1 << 0);
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, data32_1, 0, 9);
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 9);
+ LCDPR("vx1 intr status = 0x%04x, encl_clkmsr = %d",
+ data32, encl_clk);
+
+ if (data32 & 0x200) {
+ LCDPR("vx1 htpdn fall occurred\n");
+ vx1_fsm_acq_st = 0;
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1);
+ }
+#if 0
+ if (data32 & 0x400) {
+ LCDPR("vx1 htpdn raise occurred\n");
+ vx1_fsm_acq_st = 0;
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1);
+ }
+#endif
+ if (data32 & 0x800) {
+ LCDPR("vx1 lockn fall occurred\n");
+ vx1_fsm_acq_st = 0;
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1);
+ if (vx1_lockn_wait_cnt++ > VX1_LOCKN_WAIT_TIMEOUT) {
+#if 0
+ LCDPR("vx1 sw reset for lockn timeout\n");
+ /* force PHY to 0 */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2);
+ lcd_vcbus_write(VBO_SOFT_RST, 0x1ff);
+ udelay(5);
+ /* clear lockn raising flag */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 7, 1);
+ /* realease PHY */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2);
+ /* clear lockn raising flag */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 7, 1);
+ lcd_vcbus_write(VBO_SOFT_RST, 0);
+ vx1_lockn_wait_cnt = 0;
+#else
+ if (vx1_timeout_reset_flag == 0) {
+ vx1_timeout_reset_flag = 1;
+ tasklet_schedule(&lcd_vx1_reset_tasklet);
+ vx1_lockn_wait_cnt = 0;
+ return IRQ_HANDLED;
+ }
+#endif
+ }
+ }
+#if 0
+ if (data32 & 0x1000) {
+ LCDPR("vx1 lockn raise occurred\n");
+ vx1_fsm_acq_st = 0;
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1);
+ }
+#endif
+ if (data32 & 0x2000) {
+ /* LCDPR("vx1 fsm_acq wait end\n"); */
+ if (lcd_debug_print_flag) {
+ LCDPR("vx1 status 0: 0x%x, fsm_acq_st: %d\n",
+ lcd_vcbus_read(VBO_STATUS_L), vx1_fsm_acq_st);
+ }
+ if (vx1_fsm_acq_st == 0) {
+ /* clear FSM_continue */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1);
+ LCDPR("vx1 sw reset\n");
+ /* force PHY to 0 */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2);
+ lcd_vcbus_write(VBO_SOFT_RST, 0x1ff);
+ udelay(5);
+ /* clear lockn raising flag */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 7, 1);
+ /* realease PHY */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2);
+ /* clear lockn raising flag */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 7, 1);
+ lcd_vcbus_write(VBO_SOFT_RST, 0);
+ vx1_fsm_acq_st = 1;
+ } else {
+ vx1_fsm_acq_st = 2;
+ /* set FSM_continue */
+#if (VX1_FSM_ACQ_NEXT == VX1_FSM_ACQ_NEXT_RELEASE_HOLDER)
+ lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0, 0, 16);
+#else
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1);
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 15, 1);
+#endif
+ }
+ LCDPR("vx1 status 1: 0x%x, fsm_acq_st: %d\n",
+ lcd_vcbus_read(VBO_STATUS_L), vx1_fsm_acq_st);
+ }
+
+ if (data32 & 0x1ff) {
+ LCDPR("vx1 reset for timing err\n");
+ vx1_fsm_acq_st = 0;
+#if 1
+ /* force PHY to 0 */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2);
+ lcd_vcbus_write(VBO_SOFT_RST, 0x1ff);
+ udelay(5);
+ /* clear lockn raising flag */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 7, 1);
+ /* realease PHY */
+ lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2);
+ /* clear lockn raising flag */
+ lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 7, 1);
+ lcd_vcbus_write(VBO_SOFT_RST, 0);
+#else
+ if (vx1_timeout_reset_flag == 0) {
+ vx1_timeout_reset_flag = 2;
+ tasklet_schedule(&lcd_vx1_reset_tasklet);
+ return IRQ_HANDLED;
+ }
+#endif
+ }
+
+ udelay(20);
+ if ((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) == 0x20) {
+ vx1_lockn_wait_cnt = 0;
+ /* vx1_training_wait_cnt = 0; */
+#if (VX1_FSM_ACQ_NEXT == VX1_FSM_ACQ_NEXT_RELEASE_HOLDER)
+ lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0xffff, 0, 16);
+#endif
+ LCDPR("vx1 fsm stable\n");
+ }
+
+ /* enable interrupt */
+ lcd_vcbus_setb(VBO_INTR_UNMASK, VBYONE_INTR_UNMASK, 0, 15);
+
+ return IRQ_HANDLED;
+}
+
+static void lcd_venc_set(struct lcd_config_s *pconf)
+{
+ unsigned int h_active, v_active;
+ unsigned int video_on_pixel, video_on_line;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ h_active = pconf->lcd_basic.h_active;
+ v_active = pconf->lcd_basic.v_active;
+ video_on_pixel = pconf->lcd_timing.video_on_pixel;
+ video_on_line = pconf->lcd_timing.video_on_line;
+
+ lcd_vcbus_write(ENCL_VIDEO_EN, 0);
+
+ lcd_vcbus_write(VPU_VIU_VENC_MUX_CTRL, (0 << 0) | (3 << 2));
+ /* Enable Hsync and equalization pulse switch in center;
+ * bit[14] cfg_de_v = 1
+ */
+ lcd_vcbus_write(ENCL_VIDEO_MODE, 40);
+ lcd_vcbus_write(ENCL_VIDEO_MODE_ADV, 0x18); /* Sampling rate: 1 */
+
+ /* bypass filter */
+ lcd_vcbus_write(ENCL_VIDEO_FILT_CTRL, 0x1000);
+ lcd_vcbus_write(ENCL_VIDEO_MAX_PXCNT, pconf->lcd_basic.h_period - 1);
+ lcd_vcbus_write(ENCL_VIDEO_MAX_LNCNT, pconf->lcd_basic.v_period - 1);
+
+ lcd_vcbus_write(ENCL_VIDEO_HAVON_BEGIN, video_on_pixel);
+ lcd_vcbus_write(ENCL_VIDEO_HAVON_END, h_active - 1 + video_on_pixel);
+ lcd_vcbus_write(ENCL_VIDEO_VAVON_BLINE, video_on_line);
+ lcd_vcbus_write(ENCL_VIDEO_VAVON_ELINE, v_active - 1 + video_on_line);
+
+ lcd_vcbus_write(ENCL_VIDEO_HSO_BEGIN, pconf->lcd_timing.hs_hs_addr);
+ lcd_vcbus_write(ENCL_VIDEO_HSO_END, pconf->lcd_timing.hs_he_addr);
+ lcd_vcbus_write(ENCL_VIDEO_VSO_BEGIN, pconf->lcd_timing.vs_hs_addr);
+ lcd_vcbus_write(ENCL_VIDEO_VSO_END, pconf->lcd_timing.vs_he_addr);
+ lcd_vcbus_write(ENCL_VIDEO_VSO_BLINE, pconf->lcd_timing.vs_vs_addr);
+ lcd_vcbus_write(ENCL_VIDEO_VSO_ELINE, pconf->lcd_timing.vs_ve_addr);
+
+ lcd_vcbus_write(ENCL_VIDEO_RGBIN_CTRL, 3);
+
+ lcd_vcbus_write(ENCL_VIDEO_EN, 1);
+
+ aml_lcd_notifier_call_chain(LCD_EVENT_BACKLIGHT_UPDATE, NULL);
+}
+
+static unsigned int vbyone_lane_num[] = {
+ 1,
+ 2,
+ 4,
+ 8,
+ 8,
+};
+
+#define VBYONE_BIT_RATE_MAX 3100 /* MHz */
+#define VBYONE_BIT_RATE_MIN 600
+static void lcd_vbyone_config_set(struct lcd_config_s *pconf)
+{
+ unsigned int band_width, bit_rate, pclk, phy_div;
+ unsigned int byte_mode, lane_count, minlane;
+ unsigned int lcd_bits;
+ unsigned int temp, i;
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ /* auto calculate bandwidth, clock */
+ lane_count = pconf->lcd_control.vbyone_config->lane_count;
+ lcd_bits = 10; /* pconf->lcd_basic.lcd_bits */
+ byte_mode = (lcd_bits == 10) ? 4 : 3;
+ /* byte_mode * byte2bit * 8/10_encoding * pclk =
+ * byte_mode * 8 * 10 / 8 * pclk
+ */
+ pclk = pconf->lcd_timing.lcd_clk / 1000; /* kHz */
+ band_width = byte_mode * 10 * pclk;
+
+ temp = VBYONE_BIT_RATE_MAX * 1000;
+ temp = (band_width + temp - 1) / temp;
+ for (i = 0; i < 4; i++) {
+ if (temp <= vbyone_lane_num[i])
+ break;
+ }
+ minlane = vbyone_lane_num[i];
+ if (lane_count < minlane) {
+ LCDERR("vbyone lane_num(%d) is less than min(%d)\n",
+ lane_count, minlane);
+ lane_count = minlane;
+ pconf->lcd_control.vbyone_config->lane_count = lane_count;
+ LCDPR("change to min lane_num %d\n", minlane);
+ }
+
+ bit_rate = band_width / minlane;
+ phy_div = lane_count / minlane;
+ if (phy_div == 8) {
+ phy_div /= 2;
+ bit_rate /= 2;
+ }
+ if (bit_rate > (VBYONE_BIT_RATE_MAX * 1000)) {
+ LCDERR("vbyone bit rate(%dKHz) is out of max(%dKHz)\n",
+ bit_rate, (VBYONE_BIT_RATE_MAX * 1000));
+ }
+ if (bit_rate < (VBYONE_BIT_RATE_MIN * 1000)) {
+ LCDERR("vbyone bit rate(%dKHz) is out of min(%dKHz)\n",
+ bit_rate, (VBYONE_BIT_RATE_MIN * 1000));
+ }
+ bit_rate = bit_rate * 1000; /* Hz */
+
+ pconf->lcd_control.vbyone_config->phy_div = phy_div;
+ pconf->lcd_control.vbyone_config->bit_rate = bit_rate;
+
+ if (lcd_debug_print_flag) {
+ LCDPR("lane_count=%u, bit_rate = %uMHz, pclk=%u.%03uMhz\n",
+ lane_count, (bit_rate / 1000000),
+ (pclk / 1000), (pclk % 1000));
+ }
+}
+
+void lcd_tv_clk_update(struct lcd_config_s *pconf)
+{
+#ifdef CONFIG_AML_VPU
+ request_vpu_clk_vmod(pconf->lcd_timing.lcd_clk, VPU_VENCL);
+#endif
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_VBYONE:
+ lcd_vbyone_config_set(pconf);
+ lcd_vbyone_interrupt_enable(0);
+ break;
+ default:
+ break;
+ }
+ lcd_clk_generate_parameter(pconf);
+ lcd_clk_set(pconf);
+ if (pconf->lcd_basic.lcd_type == LCD_VBYONE)
+ lcd_vbyone_wait_stable();
+}
+
+void lcd_tv_config_update(struct lcd_config_s *pconf)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct vinfo_s *info;
+
+ /* update lcd config sync_duration */
+ info = lcd_drv->lcd_info;
+ pconf->lcd_timing.sync_duration_num = info->sync_duration_num;
+ pconf->lcd_timing.sync_duration_den = info->sync_duration_den;
+
+ /* update clk & timing config */
+ lcd_vmode_change(pconf);
+ info->video_clk = pconf->lcd_timing.lcd_clk;
+ /* update interface timing */
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_VBYONE:
+ lcd_vbyone_config_set(pconf);
+ break;
+ default:
+ break;
+ }
+}
+
+void lcd_tv_driver_init_pre(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+ int ret;
+
+ pconf = lcd_drv->lcd_config;
+ LCDPR("tv driver init(ver %s): %s\n", lcd_drv->version,
+ lcd_type_type_to_str(pconf->lcd_basic.lcd_type));
+ ret = lcd_type_supported(pconf);
+ if (ret)
+ return;
+
+#ifdef CONFIG_AML_VPU
+ request_vpu_clk_vmod(pconf->lcd_timing.lcd_clk, VPU_VENCL);
+ switch_vpu_mem_pd_vmod(VPU_VENCL, VPU_MEM_POWER_ON);
+#endif
+ lcd_clk_gate_switch(1);
+
+ /* init driver */
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_VBYONE:
+ lcd_vbyone_interrupt_enable(0);
+ break;
+ default:
+ break;
+ }
+
+ lcd_clk_set(pconf);
+ lcd_venc_set(pconf);
+ lcd_tcon_set(pconf);
+ lcd_drv->lcd_test_pattern_restore();
+}
+
+int lcd_tv_driver_init(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+ int ret;
+
+ pconf = lcd_drv->lcd_config;
+ ret = lcd_type_supported(pconf);
+ if (ret)
+ return -1;
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_LVDS:
+ lcd_lvds_control_set(pconf);
+ lcd_lvds_phy_set(pconf, 1);
+ break;
+ case LCD_VBYONE:
+ lcd_vbyone_pinmux_set(1);
+ lcd_vbyone_control_set(pconf);
+ lcd_vx1_wait_hpd();
+ lcd_vbyone_phy_set(pconf, 1);
+ lcd_vx1_intr_request = 1;
+ queue_delayed_work(lcd_drv->workqueue,
+ &lcd_drv->lcd_vx1_delayed_work,
+ msecs_to_jiffies(LCD_VX1_WAIT_STABLE_DELAY));
+ break;
+ default:
+ break;
+ }
+
+ lcd_vcbus_write(VENC_INTCTRL, 0x200);
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s finished\n", __func__);
+ return 0;
+}
+
+void lcd_tv_driver_disable(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+ int ret;
+
+ vx1_training_wait_cnt = 0;
+ vx1_timeout_reset_flag = 0;
+
+ LCDPR("disable driver\n");
+ pconf = lcd_drv->lcd_config;
+ ret = lcd_type_supported(pconf);
+ if (ret)
+ return;
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_LVDS:
+ lcd_lvds_phy_set(pconf, 0);
+ lcd_lvds_disable();
+ break;
+ case LCD_VBYONE:
+ lcd_vbyone_interrupt_enable(0);
+ lcd_vbyone_phy_set(pconf, 0);
+ lcd_vbyone_pinmux_set(0);
+ lcd_vbyone_disable();
+ break;
+ default:
+ break;
+ }
+
+ lcd_vcbus_write(ENCL_VIDEO_EN, 0); /* disable encl */
+
+ lcd_clk_disable();
+ lcd_clk_gate_switch(0);
+#ifdef CONFIG_AML_VPU
+ switch_vpu_mem_pd_vmod(VPU_VENCL, VPU_MEM_POWER_DOWN);
+ release_vpu_clk_vmod(VPU_VENCL);
+#endif
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s finished\n", __func__);
+}
+
+int lcd_tv_driver_change(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+ int ret;
+
+ pconf = lcd_drv->lcd_config;
+ LCDPR("tv driver change(ver %s): %s\n", lcd_drv->version,
+ lcd_type_type_to_str(pconf->lcd_basic.lcd_type));
+ ret = lcd_type_supported(pconf);
+ if (ret)
+ return -1;
+
+ lcd_tv_config_update(pconf);
+#ifdef CONFIG_AML_VPU
+ request_vpu_clk_vmod(pconf->lcd_timing.lcd_clk, VPU_VENCL);
+#endif
+
+ if (lcd_drv->lcd_status == 0) {
+ /* only change parameters when panel is off */
+ switch (pconf->lcd_timing.clk_change) {
+ case LCD_CLK_PLL_CHANGE:
+ lcd_clk_generate_parameter(pconf);
+ break;
+ default:
+ break;
+ }
+ } else {
+ if (pconf->lcd_basic.lcd_type == LCD_VBYONE)
+ lcd_vbyone_interrupt_enable(0);
+
+ switch (pconf->lcd_timing.clk_change) {
+ case LCD_CLK_PLL_CHANGE:
+ lcd_clk_generate_parameter(pconf);
+ lcd_clk_set(pconf);
+ break;
+ case LCD_CLK_FRAC_UPDATE:
+ lcd_clk_update(pconf);
+ break;
+ default:
+ break;
+ }
+ lcd_venc_change(pconf);
+
+ if (pconf->lcd_basic.lcd_type == LCD_VBYONE)
+ lcd_vbyone_wait_stable();
+ }
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s finished\n", __func__);
+ return 0;
+}
+
+void lcd_tv_driver_tiny_enable(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+ int ret;
+
+ pconf = lcd_drv->lcd_config;
+ ret = lcd_type_supported(pconf);
+ if (ret)
+ return;
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_LVDS:
+ lcd_lvds_control_set(pconf);
+ lcd_lvds_phy_set(pconf, 1);
+ break;
+ case LCD_VBYONE:
+ lcd_vbyone_pinmux_set(1);
+ lcd_vbyone_control_set(pconf);
+ lcd_vx1_wait_hpd();
+ lcd_vbyone_phy_set(pconf, 1);
+ lcd_vbyone_wait_stable();
+ break;
+ default:
+ break;
+ }
+
+ LCDPR("enable driver\n");
+}
+
+void lcd_tv_driver_tiny_disable(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+ int ret;
+
+ LCDPR("disable driver\n");
+ pconf = lcd_drv->lcd_config;
+ ret = lcd_type_supported(pconf);
+ if (ret)
+ return;
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_LVDS:
+ lcd_lvds_phy_set(pconf, 0);
+ lcd_lvds_disable();
+ break;
+ case LCD_VBYONE:
+ lcd_vbyone_interrupt_enable(0);
+ lcd_vbyone_phy_set(pconf, 0);
+ lcd_vbyone_pinmux_set(0);
+ lcd_vbyone_disable();
+ break;
+ default:
+ break;
+ }
+}
+
+#define VBYONE_IRQF IRQF_SHARED /* IRQF_DISABLED */ /* IRQF_SHARED */
+
+void lcd_vbyone_interrupt_up(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ lcd_vbyone_interrupt_init();
+ vx1_lockn_wait_cnt = 0;
+ vx1_training_wait_cnt = 0;
+ vx1_timeout_reset_flag = 0;
+ vx1_training_stable_cnt = 0;
+ lcd_vx1_intr_request = 0;
+ lcd_encl_clk_err_cnt = 0;
+
+ tasklet_init(&lcd_vx1_reset_tasklet, lcd_vx1_timeout_reset,
+ 123);
+
+ INIT_DELAYED_WORK(&lcd_drv->lcd_vx1_delayed_work,
+ lcd_vx1_wait_stable_delayed);
+
+ if (request_irq(INT_VIU_VSYNC, &lcd_vbyone_vsync_isr,
+ IRQF_SHARED, "vbyone_vsync", (void *)"vbyone_vsync")) {
+ LCDERR("can't request vsync_irq for vbyone\n");
+ } else {
+ if (lcd_debug_print_flag)
+ LCDPR("request vbyone vsync_irq successful\n");
+ }
+
+ if (request_irq(INT_VENC_VX1, &lcd_vbyone_interrupt_handler,
+ VBYONE_IRQF, "vbyone", (void *)"vbyone")) {
+ LCDERR("can't request irq for vbyone\n");
+ } else {
+ if (lcd_debug_print_flag)
+ LCDPR("request vbyone irq successful\n");
+ }
+ lcd_vx1_intr_request = 1;
+ lcd_vbyone_interrupt_enable(1);
+
+ /* add timer to monitor hpll frequency */
+ init_timer(&vx1_hpll_timer);
+ /* vx1_hpll_timer.data = NULL; */
+ vx1_hpll_timer.function = lcd_vx1_hpll_timer_handler;
+ vx1_hpll_timer.expires = jiffies + VX1_HPLL_INTERVAL;
+ add_timer(&vx1_hpll_timer);
+ LCDPR("add vbyone hpll timer handler\n");
+}
+
+void lcd_vbyone_interrupt_down(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ del_timer_sync(&vx1_hpll_timer);
+
+ lcd_vbyone_interrupt_enable(0);
+ free_irq(INT_VENC_VX1, (void *)"vbyone");
+ free_irq(INT_VIU_VSYNC, (void *)"vbyone");
+ tasklet_kill(&lcd_vx1_reset_tasklet);
+ cancel_delayed_work(&lcd_drv->lcd_vx1_delayed_work);
+
+ if (lcd_debug_print_flag)
+ LCDPR("free vbyone irq\n");
+}
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#ifdef CONFIG_AML_VPU
+#include <linux/amlogic/vpu.h>
+#endif
+#include <linux/amlogic/media/vout/vinfo.h>
+#include <linux/amlogic/media/vout/vout_notify.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include "lcd_tv.h"
+#include "../lcd_reg.h"
+#include "../lcd_common.h"
+
+static unsigned int lcd_output_vmode;
+static char lcd_output_name[30];
+
+/* lcd mode function */
+static unsigned int lcd_std_frame_rate[] = {
+ 50,
+ 60,
+ 48,
+};
+
+struct lcd_vmode_info_s {
+ char *name;
+ enum vmode_e mode;
+ unsigned int width;
+ unsigned int height;
+ unsigned int frame_rate;
+};
+
+enum lcd_vmode_e {
+ LCD_VMODE_600P = 0,
+ LCD_VMODE_768P,
+ LCD_VMODE_1080P,
+ LCD_VMODE_2160P,
+ LCD_VMODE_MAX,
+};
+
+static struct lcd_vmode_info_s lcd_vmode_info[] = {
+ {
+ .name = "600p",
+ .mode = VMODE_LCD,
+ .width = 1024,
+ .height = 600,
+ .frame_rate = 60,
+ },
+ {
+ .name = "768p",
+ .mode = VMODE_LCD,
+ .width = 1366,
+ .height = 768,
+ .frame_rate = 60,
+ },
+ {
+ .name = "1080p",
+ .mode = VMODE_LCD,
+ .width = 1920,
+ .height = 1080,
+ .frame_rate = 60,
+ },
+ {
+ .name = "2160p",
+ .mode = VMODE_LCD,
+ .width = 3840,
+ .height = 2160,
+ .frame_rate = 60,
+ },
+ {
+ .name = "invalid",
+ .mode = VMODE_INIT_NULL,
+ .width = 1920,
+ .height = 1080,
+ .frame_rate = 60,
+ },
+};
+
+static int lcd_vmode_is_mached(int index)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+
+ pconf = lcd_drv->lcd_config;
+ if ((pconf->lcd_basic.h_active == lcd_vmode_info[index].width) &&
+ (pconf->lcd_basic.v_active == lcd_vmode_info[index].height))
+ return 0;
+ else
+ return -1;
+}
+
+static int lcd_get_vmode(enum vmode_e mode)
+{
+ int lcd_vmode = LCD_VMODE_MAX;
+ int i, count = ARRAY_SIZE(lcd_vmode_info) - 1;
+ int ret;
+
+ for (i = 0; i < count; i++) {
+ if (mode == lcd_vmode_info[i].mode) {
+ ret = lcd_vmode_is_mached(i);
+ if (ret == 0) {
+ lcd_vmode = i;
+ break;
+ }
+ }
+ }
+ return lcd_vmode;
+}
+
+static int lcd_outputmode_to_lcd_vmode(const char *mode)
+{
+ int lcd_vmode = LCD_VMODE_MAX;
+ int i, count = ARRAY_SIZE(lcd_vmode_info) - 1;
+ char temp[30], *p;
+ int n;
+
+ p = strchr(mode, 'p');
+ if (p == NULL)
+ return LCD_VMODE_MAX;
+ n = p - mode + 1;
+ strncpy(temp, mode, n);
+ temp[n] = '\0';
+ if (lcd_debug_print_flag)
+ LCDPR("outputmode=%s, lcd_vmode=%s\n", mode, temp);
+
+ for (i = 0; i < count; i++) {
+ if (strcmp(temp, lcd_vmode_info[i].name) == 0) {
+ lcd_vmode = i;
+ break;
+ }
+ }
+ return lcd_vmode;
+}
+
+static int lcd_outputmode_to_lcd_frame_rate(const char *mode)
+{
+ int frame_rate = 0;
+ char temp[30], *p;
+ int n, i, ret = 0;
+
+ p = strchr(mode, 'p');
+ if (p == NULL)
+ return 0;
+ n = p - mode + 1;
+ strncpy(temp, mode+n, (strlen(mode)-n));
+ p = strchr(temp, 'h');
+ if (p == NULL)
+ return 0;
+ *p = '\0';
+ ret = kstrtoint(temp, 10, &n);
+ if (lcd_debug_print_flag)
+ LCDPR("outputmode=%s, frame_rate=%d\n", mode, n);
+
+ for (i = 0; i < ARRAY_SIZE(lcd_std_frame_rate); i++) {
+ if (n == lcd_std_frame_rate[i]) {
+ frame_rate = n;
+ break;
+ }
+ }
+ return frame_rate;
+}
+
+static void lcd_vmode_vinfo_update(enum vmode_e mode)
+{
+ struct lcd_vmode_info_s *info;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct lcd_config_s *pconf;
+
+ lcd_output_vmode = lcd_get_vmode(mode);
+ info = &lcd_vmode_info[lcd_output_vmode];
+ sprintf(lcd_output_name, "%s%dhz", info->name, info->frame_rate);
+ if (lcd_debug_print_flag) {
+ LCDPR("%s vmode = %d, lcd_vmode = %d, outputmode = %s\n",
+ __func__, mode, lcd_output_vmode, lcd_output_name);
+ }
+
+ /* store standard duration */
+ lcd_drv->std_duration.duration_num = info->frame_rate;
+ lcd_drv->std_duration.duration_den = 1;
+
+ /* update vinfo */
+ pconf = lcd_drv->lcd_config;
+ lcd_drv->lcd_info->name = lcd_output_name;
+ lcd_drv->lcd_info->mode = info->mode;
+ lcd_drv->lcd_info->width = info->width;
+ lcd_drv->lcd_info->height = info->height;
+ lcd_drv->lcd_info->field_height = info->height;
+ lcd_drv->lcd_info->aspect_ratio_num = pconf->lcd_basic.screen_width;
+ lcd_drv->lcd_info->aspect_ratio_den = pconf->lcd_basic.screen_height;
+ lcd_drv->lcd_info->screen_real_width = pconf->lcd_basic.screen_width;
+ lcd_drv->lcd_info->screen_real_height = pconf->lcd_basic.screen_height;
+ lcd_drv->lcd_info->sync_duration_num = info->frame_rate;
+ lcd_drv->lcd_info->sync_duration_den = 1;
+ lcd_drv->lcd_info->video_clk = pconf->lcd_timing.lcd_clk;
+
+ lcd_hdr_vinfo_update();
+}
+
+/* vout server api */
+static enum vmode_e lcd_validate_vmode(char *mode)
+{
+ int lcd_vmode, frame_rate;
+ int ret;
+
+ if (lcd_vout_serve_bypass) {
+ LCDPR("vout_serve bypass\n");
+ return VMODE_MAX;
+ }
+ if (mode == NULL)
+ return VMODE_MAX;
+
+ lcd_vmode = lcd_outputmode_to_lcd_vmode(mode);
+ ret = lcd_vmode_is_mached(lcd_vmode);
+ if (ret) {
+ LCDERR("%s: outputmode is not support\n", __func__);
+ return VMODE_MAX;
+ }
+ frame_rate = lcd_outputmode_to_lcd_frame_rate(mode);
+ if (frame_rate == 0) {
+ LCDERR("%s: frame_rate is not support\n", __func__);
+ } else {
+ lcd_vmode_info[lcd_vmode].frame_rate = frame_rate;
+ if (lcd_vmode < LCD_VMODE_MAX)
+ return lcd_vmode_info[lcd_vmode].mode;
+ else
+ return VMODE_MAX;
+ }
+
+ return VMODE_MAX;
+}
+
+static struct vinfo_s *lcd_get_current_info(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ return lcd_drv->lcd_info;
+}
+
+static int lcd_set_current_vmode(enum vmode_e mode)
+{
+ int ret = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ if (lcd_vout_serve_bypass) {
+ LCDPR("vout_serve bypass\n");
+ return 0;
+ }
+ mutex_lock(&lcd_vout_mutex);
+
+ /* do not change mode value here, for bit mask is useful */
+ lcd_vmode_vinfo_update(mode & VMODE_MODE_BIT_MASK);
+
+ if (!(mode & VMODE_INIT_BIT_MASK)) {
+ switch (mode & VMODE_MODE_BIT_MASK) {
+ case VMODE_LCD:
+ ret = lcd_drv->driver_change();
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ }
+
+ lcd_vcbus_write(VPP_POSTBLEND_H_SIZE, lcd_drv->lcd_info->width);
+
+ mutex_unlock(&lcd_vout_mutex);
+ return ret;
+}
+
+static int lcd_vmode_is_supported(enum vmode_e mode)
+{
+ int lcd_vmode;
+
+ mode &= VMODE_MODE_BIT_MASK;
+ lcd_vmode = lcd_get_vmode(mode);
+ if (lcd_debug_print_flag) {
+ LCDPR("%s vmode = %d, lcd_vmode = %d(%s)\n",
+ __func__, mode, lcd_vmode,
+ lcd_vmode_info[lcd_vmode].name);
+ }
+
+ if (lcd_vmode < LCD_VMODE_MAX)
+ return true;
+ else
+ return false;
+}
+
+static int lcd_vout_disable(enum vmode_e cur_vmod)
+{
+ return 0;
+}
+
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+struct lcd_vframe_match_s {
+ int fps;
+ int frame_rate; /* *100 */
+ unsigned int duration_num;
+ unsigned int duration_den;
+};
+
+static struct lcd_vframe_match_s lcd_vframe_match_table_1[] = {
+ {5000, 5000, 50, 1},
+ {2500, 5000, 50, 1},
+ {6000, 6000, 60, 1},
+ {3000, 6000, 60, 1},
+ {2400, 6000, 60, 1},
+ {2397, 5994, 5994, 100},
+ {2997, 5994, 5994, 100},
+ {5994, 5994, 5994, 100},
+};
+
+static struct lcd_vframe_match_s lcd_vframe_match_table_2[] = {
+ {5000, 5000, 50, 1},
+ {2500, 5000, 50, 1},
+ {6000, 6000, 60, 1},
+ {3000, 6000, 60, 1},
+ {2400, 4800, 48, 1},
+ {2397, 5994, 5994, 100},
+ {2997, 5994, 5994, 100},
+ {5994, 5994, 5994, 100},
+};
+
+static int lcd_framerate_automation_set_mode(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ /* update interface timing */
+ lcd_tv_config_update(lcd_drv->lcd_config);
+#ifdef CONFIG_AML_VPU
+ request_vpu_clk_vmod(
+ lcd_drv->lcd_config->lcd_timing.lcd_clk, VPU_VENCL);
+#endif
+
+ if (lcd_drv->lcd_config->lcd_basic.lcd_type == LCD_VBYONE)
+ lcd_vbyone_interrupt_enable(0);
+ /* change clk parameter */
+ switch (lcd_drv->lcd_config->lcd_timing.clk_change) {
+ case LCD_CLK_PLL_CHANGE:
+ lcd_clk_generate_parameter(lcd_drv->lcd_config);
+ lcd_clk_set(lcd_drv->lcd_config);
+ break;
+ case LCD_CLK_FRAC_UPDATE:
+ lcd_clk_update(lcd_drv->lcd_config);
+ break;
+ default:
+ break;
+ }
+ lcd_venc_change(lcd_drv->lcd_config);
+ if (lcd_drv->lcd_config->lcd_basic.lcd_type == LCD_VBYONE)
+ lcd_vbyone_wait_stable();
+
+ vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE,
+ &lcd_drv->lcd_info->mode);
+
+ return 0;
+}
+#endif
+
+static int lcd_set_vframe_rate_hint(int duration)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct vinfo_s *info;
+ int fr_policy;
+ unsigned int frame_rate = 6000;
+ unsigned int duration_num = 60, duration_den = 1;
+ struct lcd_vframe_match_s *vtable = lcd_vframe_match_table_1;
+ int fps, i, n;
+
+ if (lcd_vout_serve_bypass) {
+ LCDPR("vout_serve bypass\n");
+ return 0;
+ }
+ if (lcd_drv->lcd_status == 0) {
+ LCDPR("%s: lcd is disabled, exit\n", __func__);
+ return 0;
+ }
+
+ info = lcd_drv->lcd_info;
+
+ fr_policy = lcd_drv->fr_auto_policy;
+ switch (fr_policy) {
+ case 1:
+ vtable = lcd_vframe_match_table_1;
+ n = ARRAY_SIZE(lcd_vframe_match_table_1);
+ break;
+ case 2:
+ vtable = lcd_vframe_match_table_2;
+ n = ARRAY_SIZE(lcd_vframe_match_table_2);
+ break;
+ default:
+ LCDPR("%s: fr_auto_policy = %d, disabled\n",
+ __func__, fr_policy);
+ return 0;
+ }
+ fps = get_vsource_fps(duration);
+ for (i = 0; i < n; i++) {
+ if (fps == vtable[i].fps) {
+ frame_rate = vtable[i].frame_rate;
+ duration_num = vtable[i].duration_num;
+ duration_den = vtable[i].duration_den;
+ }
+ }
+ LCDPR("%s: policy = %d, duration = %d, fps = %d, frame_rate = %d\n",
+ __func__, fr_policy, duration, fps, frame_rate);
+
+ /* if the sync_duration is same as current */
+ if ((duration_num == info->sync_duration_num) &&
+ (duration_den == info->sync_duration_den)) {
+ LCDPR("%s: sync_duration is the same, exit\n", __func__);
+ return 0;
+ }
+
+ /* update vinfo */
+ info->sync_duration_num = duration_num;
+ info->sync_duration_den = duration_den;
+
+ lcd_framerate_automation_set_mode();
+#endif
+ return 0;
+}
+
+static int lcd_set_vframe_rate_end_hint(void)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct vinfo_s *info;
+
+ if (lcd_vout_serve_bypass) {
+ LCDPR("vout_serve bypass\n");
+ return 0;
+ }
+ if (lcd_drv->lcd_status == 0) {
+ LCDPR("%s: lcd is disabled, exit\n", __func__);
+ return 0;
+ }
+
+ if (lcd_debug_print_flag)
+ LCDPR("fr_auto_policy = %d\n", lcd_drv->fr_auto_policy);
+ if (lcd_drv->fr_auto_policy) {
+ info = lcd_drv->lcd_info;
+ LCDPR("%s: return mode = %s, policy = %d\n", __func__,
+ info->name, lcd_drv->fr_auto_policy);
+
+ /* update vinfo */
+ info->sync_duration_num = lcd_drv->std_duration.duration_num;
+ info->sync_duration_den = lcd_drv->std_duration.duration_den;
+
+ lcd_framerate_automation_set_mode();
+ }
+#endif
+ return 0;
+}
+
+static int lcd_set_vframe_rate_policy(int policy)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ if (lcd_vout_serve_bypass) {
+ LCDPR("vout_serve bypass\n");
+ return 0;
+ }
+ lcd_drv->fr_auto_policy = policy;
+ LCDPR("%s: %d\n", __func__, lcd_drv->fr_auto_policy);
+#endif
+ return 0;
+}
+
+static int lcd_get_vframe_rate_policy(void)
+{
+#ifdef CONFIG_AML_VOUT_FRAMERATE_AUTOMATION
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ return lcd_drv->fr_auto_policy;
+#else
+ return 0;
+#endif
+}
+
+#ifdef CONFIG_PM
+static int lcd_suspend(void)
+{
+ mutex_lock(&lcd_power_mutex);
+ aml_lcd_notifier_call_chain(LCD_EVENT_POWER_OFF, NULL);
+ lcd_resume_flag = 0;
+ LCDPR("%s finished\n", __func__);
+ mutex_unlock(&lcd_power_mutex);
+ return 0;
+}
+
+static int lcd_resume(void)
+{
+ mutex_lock(&lcd_power_mutex);
+ if (lcd_resume_flag == 0) {
+ lcd_resume_flag = 1;
+ aml_lcd_notifier_call_chain(LCD_EVENT_POWER_ON, NULL);
+ LCDPR("%s finished\n", __func__);
+ }
+ mutex_unlock(&lcd_power_mutex);
+ return 0;
+}
+
+#endif
+
+static struct vout_server_s lcd_vout_server = {
+ .name = "lcd_vout_server",
+ .op = {
+ .get_vinfo = lcd_get_current_info,
+ .set_vmode = lcd_set_current_vmode,
+ .validate_vmode = lcd_validate_vmode,
+ .vmode_is_supported = lcd_vmode_is_supported,
+ .disable = lcd_vout_disable,
+ .set_vframe_rate_hint = lcd_set_vframe_rate_hint,
+ .set_vframe_rate_end_hint = lcd_set_vframe_rate_end_hint,
+ .set_vframe_rate_policy = lcd_set_vframe_rate_policy,
+ .get_vframe_rate_policy = lcd_get_vframe_rate_policy,
+#ifdef CONFIG_PM
+ .vout_suspend = lcd_suspend,
+ .vout_resume = lcd_resume,
+#endif
+ },
+};
+
+static void lcd_vinfo_update_default(void)
+{
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+ struct vinfo_s *vinfo;
+ unsigned int h_active, v_active;
+ char *mode;
+
+ if (lcd_drv->lcd_info == NULL) {
+ LCDERR("no lcd_info exist\n");
+ return;
+ }
+
+ mode = get_vout_mode_uboot();
+ h_active = lcd_vcbus_read(ENCL_VIDEO_HAVON_END)
+ - lcd_vcbus_read(ENCL_VIDEO_HAVON_BEGIN) + 1;
+ v_active = lcd_vcbus_read(ENCL_VIDEO_VAVON_ELINE)
+ - lcd_vcbus_read(ENCL_VIDEO_VAVON_BLINE) + 1;
+
+ vinfo = lcd_drv->lcd_info;
+ if (vinfo) {
+ vinfo->name = mode;
+ vinfo->mode = VMODE_LCD;
+ vinfo->width = h_active;
+ vinfo->height = v_active;
+ vinfo->field_height = v_active;
+ vinfo->aspect_ratio_num = h_active;
+ vinfo->aspect_ratio_den = v_active;
+ vinfo->screen_real_width = h_active;
+ vinfo->screen_real_height = v_active;
+ vinfo->sync_duration_num = 60;
+ vinfo->sync_duration_den = 1;
+ vinfo->video_clk = 0;
+ vinfo->viu_color_fmt = COLOR_FMT_RGB444;
+ }
+}
+
+void lcd_tv_vout_server_init(void)
+{
+ lcd_vinfo_update_default();
+ vout_register_server(&lcd_vout_server);
+}
+
+/* lcd tv config */
+static void lcd_config_print(struct lcd_config_s *pconf)
+{
+ LCDPR("%s, %s, %dbit, %dx%d\n",
+ pconf->lcd_basic.model_name,
+ lcd_type_type_to_str(pconf->lcd_basic.lcd_type),
+ pconf->lcd_basic.lcd_bits,
+ pconf->lcd_basic.h_active, pconf->lcd_basic.v_active);
+
+ if (lcd_debug_print_flag == 0)
+ return;
+
+ LCDPR("h_period = %d\n", pconf->lcd_basic.h_period);
+ LCDPR("v_period = %d\n", pconf->lcd_basic.v_period);
+ LCDPR("screen_width = %d\n", pconf->lcd_basic.screen_width);
+ LCDPR("screen_height = %d\n", pconf->lcd_basic.screen_height);
+
+ LCDPR("h_period_min = %d\n", pconf->lcd_basic.h_period_min);
+ LCDPR("h_period_max = %d\n", pconf->lcd_basic.h_period_max);
+ LCDPR("v_period_min = %d\n", pconf->lcd_basic.v_period_min);
+ LCDPR("v_period_max = %d\n", pconf->lcd_basic.v_period_max);
+ LCDPR("pclk_min = %d\n", pconf->lcd_basic.lcd_clk_min);
+ LCDPR("pclk_max = %d\n", pconf->lcd_basic.lcd_clk_max);
+
+ LCDPR("hsync_width = %d\n", pconf->lcd_timing.hsync_width);
+ LCDPR("hsync_bp = %d\n", pconf->lcd_timing.hsync_bp);
+ LCDPR("hsync_pol = %d\n", pconf->lcd_timing.hsync_pol);
+ LCDPR("vsync_width = %d\n", pconf->lcd_timing.vsync_width);
+ LCDPR("vsync_bp = %d\n", pconf->lcd_timing.vsync_bp);
+ LCDPR("vsync_pol = %d\n", pconf->lcd_timing.vsync_pol);
+
+ LCDPR("fr_adjust_type = %d\n", pconf->lcd_timing.fr_adjust_type);
+ LCDPR("ss_level = %d\n", pconf->lcd_timing.ss_level);
+ LCDPR("clk_auto = %d\n", pconf->lcd_timing.clk_auto);
+
+ if (pconf->lcd_basic.lcd_type == LCD_VBYONE) {
+ LCDPR("lane_count = %d\n",
+ pconf->lcd_control.vbyone_config->lane_count);
+ LCDPR("byte_mode = %d\n",
+ pconf->lcd_control.vbyone_config->byte_mode);
+ LCDPR("region_num = %d\n",
+ pconf->lcd_control.vbyone_config->region_num);
+ LCDPR("color_fmt = %d\n",
+ pconf->lcd_control.vbyone_config->color_fmt);
+ } else if (pconf->lcd_basic.lcd_type == LCD_LVDS) {
+ LCDPR("lvds_repack = %d\n",
+ pconf->lcd_control.lvds_config->lvds_repack);
+ LCDPR("pn_swap = %d\n",
+ pconf->lcd_control.lvds_config->pn_swap);
+ LCDPR("dual_port = %d\n",
+ pconf->lcd_control.lvds_config->dual_port);
+ LCDPR("port_swap = %d\n",
+ pconf->lcd_control.lvds_config->port_swap);
+ LCDPR("lane_reverse = %d\n",
+ pconf->lcd_control.lvds_config->lane_reverse);
+ }
+}
+
+static int lcd_init_load_from_dts(struct lcd_config_s *pconf,
+ struct device *dev)
+{
+ int ret = 0;
+ struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
+
+ switch (pconf->lcd_basic.lcd_type) {
+ case LCD_VBYONE:
+ if (lcd_drv->lcd_status) /* lock pinmux if lcd in on */
+ lcd_vbyone_pinmux_set(1);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int lcd_config_load_from_dts(struct lcd_config_s *pconf,
+ struct device *dev)
+{
+ int ret = 0;
+ const char *str;
+ unsigned int para[10];
+ struct device_node *child;
+ struct lvds_config_s *lvdsconf;
+
+ child = of_get_child_by_name(dev->of_node, pconf->lcd_propname);
+ if (child == NULL) {
+ LCDERR("failed to get %s\n", pconf->lcd_propname);
+ return -1;
+ }
+
+ ret = of_property_read_string(child, "model_name", &str);
+ if (ret) {
+ LCDERR("failed to get model_name\n");
+ strcpy(pconf->lcd_basic.model_name, pconf->lcd_propname);
+ } else {
+ strcpy(pconf->lcd_basic.model_name, str);
+ }
+
+ ret = of_property_read_string(child, "interface", &str);
+ if (ret) {
+ LCDERR("failed to get interface\n");
+ str = "invalid";
+ }
+ pconf->lcd_basic.lcd_type = lcd_type_str_to_type(str);
+
+ ret = of_property_read_u32_array(child, "basic_setting", ¶[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;
+}
+
--- /dev/null
+/*
+ * 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>;
+ };
+};
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_tv/lcd_tv.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __AML_LCD_TV_H__
+#define __AML_LCD_TV_H__
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+
+extern void lcd_tv_config_update(struct lcd_config_s *pconf);
+extern void lcd_tv_driver_init_pre(void);
+extern int lcd_tv_driver_init(void);
+extern void lcd_tv_driver_disable(void);
+extern int lcd_tv_driver_change(void);
+extern void lcd_tv_driver_tiny_enable(void);
+extern void lcd_tv_driver_tiny_disable(void);
+
+extern void lcd_vbyone_wait_stable(void);
+extern void lcd_vbyone_interrupt_up(void);
+extern void lcd_vbyone_interrupt_down(void);
+
+#endif
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_unifykey.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+
+#define LCDUKEY(fmt, args...) pr_info("lcd ukey: "fmt"", ## args)
+#define LCDUKEYERR(fmt, args...) pr_info("lcd ukey err: error: "fmt"", ## args)
+
+#ifdef CONFIG_KEY_MANAGE
+static unsigned int cal_crc32(unsigned int crc, const unsigned char *buf,
+ int buf_len) {
+ unsigned int s_crc32[16] = {
+ 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
+ 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+ 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
+ 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
+ };
+ unsigned int crcu32 = crc;
+ unsigned char b;
+
+ if (buf_len <= 0)
+ return 0;
+ if (!buf)
+ return 0;
+
+ crcu32 = ~crcu32;
+ while (buf_len--) {
+ b = *buf++;
+ crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)];
+ crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)];
+ }
+
+ return ~crcu32;
+}
+
+int lcd_unifykey_len_check(int key_len, int len)
+{
+ if (key_len < len) {
+ LCDUKEYERR("invalid unifykey length %d, need %d\n",
+ key_len, len);
+ return -1;
+ }
+ return 0;
+}
+
+int lcd_unifykey_header_check(unsigned char *buf,
+ struct aml_lcd_unifykey_header_s *header)
+{
+ header->crc32 = (buf[0] | (buf[1] << 8) |
+ (buf[2] << 16) | (buf[3] << 24));
+ header->data_len = (buf[4] | (buf[5] << 8));
+ header->version = (buf[6] | (buf[7] << 8));
+ header->reserved = (buf[8] | (buf[9] << 8));
+
+ return 0;
+}
+
+int lcd_unifykey_check(char *key_name)
+{
+ unsigned int key_exist, keypermit, key_len;
+ unsigned char buf[550];
+ struct aml_lcd_unifykey_header_s key_header;
+ int retry_cnt = 0;
+ unsigned int key_crc32;
+ int ret;
+
+ key_exist = 0;
+ key_len = 0;
+ ret = key_unify_query(key_name, &key_exist, &keypermit);
+ if (ret < 0) {
+ if (lcd_debug_print_flag)
+ LCDUKEYERR("%s query exist error\n", key_name);
+ return -1;
+ }
+ if (key_exist == 0) {
+ if (lcd_debug_print_flag)
+ LCDUKEYERR("%s is not exist\n", key_name);
+ return -1;
+ }
+
+ ret = key_unify_size(key_name, &key_len);
+ if (ret < 0) {
+ LCDUKEYERR("%s query size error\n", key_name);
+ return -1;
+ }
+ if (key_len == 0) {
+ if (lcd_debug_print_flag)
+ LCDUKEY("%s size is zero\n", key_name);
+ return -1;
+ }
+ if (lcd_debug_print_flag)
+ LCDUKEY("%s size: %d\n", key_name, key_len);
+
+lcd_unifykey_read:
+ ret = key_unify_read(key_name, buf, key_len, &key_len);
+ if (ret < 0) {
+ LCDUKEYERR("%s unify read error\n", key_name);
+ return -1;
+ }
+
+ /* check header */
+ if (key_len <= LCD_UKEY_HEAD_SIZE) {
+ LCDUKEYERR("%s unify key_len %d error\n", key_name, key_len);
+ return -1;
+ }
+ lcd_unifykey_header_check(buf, &key_header);
+ if (key_len != key_header.data_len) { /* length check */
+ if (lcd_debug_print_flag) {
+ LCDUKEYERR("data_len %d is not match key_len %d\n",
+ key_header.data_len, key_len);
+ }
+ if (retry_cnt < LCD_UKEY_RETRY_CNT_MAX) {
+ retry_cnt++;
+ goto lcd_unifykey_read;
+ } else {
+ LCDUKEYERR("%s: load unifykey failed\n", key_name);
+ return -1;
+ }
+ }
+ key_crc32 = cal_crc32(0, &buf[4], (key_len - 4)); /* except crc32 */
+ if (lcd_debug_print_flag) {
+ LCDUKEY("crc32: 0x%08x, header_crc32: 0x%08x\n",
+ key_crc32, key_header.crc32);
+ }
+ if (key_crc32 != key_header.crc32) { /* crc32 check */
+ LCDUKEYERR("crc32 0x%08x is not match header_crc32 0x%08x\n",
+ key_header.crc32, key_crc32);
+ if (retry_cnt < LCD_UKEY_RETRY_CNT_MAX) {
+ retry_cnt++;
+ goto lcd_unifykey_read;
+ } else {
+ LCDUKEYERR("%s: load unifykey failed\n", key_name);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int lcd_unifykey_get(char *key_name, unsigned char *buf, int *len)
+{
+ int key_len;
+ int ret;
+
+ key_len = 0;
+ ret = lcd_unifykey_check(key_name);
+ if (ret < 0)
+ return -1;
+ ret = key_unify_size(key_name, &key_len);
+ if (key_len > *len) {
+ LCDUKEYERR("%s size(%d) is bigger than buf_size(%d)\n",
+ key_name, key_len, *len);
+ return -1;
+ }
+ *len = key_len;
+
+ ret = key_unify_read(key_name, buf, key_len, &key_len);
+ if (ret < 0) {
+ LCDUKEYERR("%s unify read error\n", key_name);
+ return -1;
+ }
+ return 0;
+}
+
+void lcd_unifykey_print(void)
+{
+ unsigned char buf[600];
+ char *key_name;
+ unsigned int key_len;
+ int i, j;
+ int ret;
+
+ key_name = "lcd";
+ key_len = LCD_UKEY_LCD_SIZE;
+ ret = lcd_unifykey_get(key_name, buf, &key_len);
+ if (ret < 0)
+ return;
+ LCDUKEY("%s: %s: %d\n", __func__, key_name, key_len);
+ i = 0;
+ while (1) {
+ pr_info("0x%08x: ", (i * 16));
+ for (j = 0; j < 16; j++) {
+ if ((i*16+j) < key_len) {
+ pr_info("0x%02x ", buf[i*16+j]);
+ } else {
+ pr_info("\n");
+ goto exit_print_lcd;
+ }
+ }
+ pr_info("\n");
+ i++;
+ }
+
+exit_print_lcd:
+ key_name = "lcd_extern";
+ key_len = LCD_UKEY_LCD_EXT_SIZE;
+ ret = lcd_unifykey_get(key_name, buf, &key_len);
+ if (ret < 0)
+ return;
+ LCDUKEY("%s: %s: %d\n", __func__, key_name, key_len);
+ i = 0;
+ while (1) {
+ pr_info("0x%08x: ", (i * 16));
+ for (j = 0; j < 16; j++) {
+ if ((i*16+j) < key_len) {
+ pr_info("0x%02x ", buf[i*16+j]);
+ } else {
+ pr_info("\n");
+ goto exit_print_lcd_ext;
+ }
+ }
+ pr_info("\n");
+ i++;
+ }
+
+exit_print_lcd_ext:
+ key_name = "backlight";
+ key_len = LCD_UKEY_BL_SIZE;
+ ret = lcd_unifykey_get(key_name, buf, &key_len);
+ if (ret < 0)
+ return;
+ LCDUKEY("%s: %s: %d\n", __func__, key_name, key_len);
+ i = 0;
+ while (1) {
+ pr_info("0x%08x: ", (i * 16));
+ for (j = 0; j < 16; j++) {
+ if ((i*16+j) < key_len) {
+ pr_info("0x%02x ", buf[i*16+j]);
+ } else {
+ pr_info("\n");
+ goto exit_print_backlight;
+ }
+ }
+ pr_info("\n");
+ i++;
+ }
+
+exit_print_backlight:
+ return;
+}
+
+#else
+/* dummy driver */
+int lcd_unifykey_len_check(int key_len, int len)
+{
+ LCDUKEYERR("Don't support unifykey\n");
+ return -1;
+}
+
+int lcd_unifykey_header_check(unsigned char *buf,
+ struct aml_lcd_unifykey_header_s *header)
+{
+ LCDUKEYERR("Don't support unifykey\n");
+ return -1;
+}
+
+int lcd_unifykey_check(char *key_name)
+{
+ LCDUKEYERR("Don't support unifykey\n");
+ return -1;
+}
+
+int lcd_unifykey_get(char *key_name, unsigned char *buf, int *len)
+{
+ LCDUKEYERR("Don't support unifykey\n");
+ return -1;
+}
+
+void lcd_unifykey_print(void)
+{
+ LCDUKEYERR("Don't support unifykey\n");
+}
+
+#endif
+
--- /dev/null
+/*
+ * drivers/amlogic/media/vout/lcd/lcd_vout.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/clk.h>
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#endif
+#include <linux/reset.h>
+#include <linux/amlogic/cpu_version.h>
+#include <linux/amlogic/media/vout/vinfo.h>
+#include <linux/amlogic/media/vout/vout_notify.h>
+#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
+#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
+#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
+#endif
+#include "lcd_reg.h"
+#include "lcd_common.h"
+
+#define LCD_CDEV_NAME "lcd"
+
+unsigned char lcd_debug_print_flag;
+unsigned char lcd_resume_flag;
+static struct aml_lcd_drv_s *lcd_driver;
+
+struct mutex lcd_power_mutex;
+struct mutex lcd_vout_mutex;
+int lcd_vout_serve_bypass;
+
+static char lcd_propname[20] = "lvds_0";
+
+struct lcd_cdev_s {
+ dev_t devno;
+ struct cdev cdev;
+ struct device *dev;
+};
+
+static struct lcd_cdev_s *lcd_cdev;
+
+/* lcd config define */
+static struct ttl_config_s lcd_ttl_config = {
+ .clk_pol = 0,
+ .sync_valid = ((1 << 1) | (1 << 0)),
+ .swap_ctrl = ((0 << 1) | (0 << 0)),
+};
+
+static struct lvds_config_s lcd_lvds_config = {
+ .lvds_vswing = 1,
+ .lvds_repack = 1,
+ .dual_port = 0,
+ .pn_swap = 0,
+ .port_swap = 0,
+ .lane_reverse = 0,
+ .port_sel = 0,
+ .phy_vswing = LVDS_PHY_VSWING_DFT,
+ .phy_preem = LVDS_PHY_PREEM_DFT,
+};
+
+static struct vbyone_config_s lcd_vbyone_config = {
+ .lane_count = 8,
+ .region_num = 2,
+ .byte_mode = 4,
+ .color_fmt = 4,
+ .phy_div = 1,
+ .bit_rate = 0,
+ .phy_vswing = VX1_PHY_VSWING_DFT,
+ .phy_preem = VX1_PHY_PREEM_DFT,
+ .intr_en = 1,
+ .vsync_intr_en = 1,
+};
+
+static unsigned char dsi_init_on_table[DSI_INIT_ON_MAX] = {0xff, 0xff};
+static unsigned char dsi_init_off_table[DSI_INIT_OFF_MAX] = {0xff, 0xff};
+
+static struct dsi_config_s lcd_mipi_config = {
+ .lane_num = 4,
+ .bit_rate_max = 550, /* MHz */
+ .factor_numerator = 0,
+ .factor_denominator = 100,
+ .operation_mode_init = 1, /* 0=video mode, 1=command mode */
+ .operation_mode_display = 0, /* 0=video mode, 1=command mode */
+ .video_mode_type = 2, /* 0=sync_pulse, 1=sync_event, 2=burst */
+ .clk_lp_continuous = 1, /* 0=stop, 1=continue */
+ .phy_stop_wait = 0, /* 0=auto, 1=standard, 2=slow */
+
+ .dsi_init_on = &dsi_init_on_table[0],
+ .dsi_init_off = &dsi_init_off_table[0],
+ .extern_init = 0xff,
+ /* ext_index if needed, must match ext_config index;
+ * 0xff for invalid
+ */
+};
+
+static struct lcd_power_ctrl_s lcd_power_config = {
+ .cpu_gpio = {
+ {.flag = 0,},
+ {.flag = 0,},
+ {.flag = 0,},
+ {.flag = 0,},
+ {.flag = 0,},
+ {.flag = 0,},
+ {.flag = 0,},
+ {.flag = 0,},
+ {.flag = 0,},
+ {.flag = 0,},
+ },
+ .power_on_step = {
+ {
+ .type = LCD_POWER_TYPE_MAX,
+ },
+ },
+ .power_off_step = {
+ {
+ .type = LCD_POWER_TYPE_MAX,
+ },
+ },
+};
+
+static struct lcd_config_s lcd_config_dft = {
+ .lcd_propname = lcd_propname,
+ .lcd_basic = {
+ .lcd_type = LCD_TYPE_MAX,
+ },
+ .lcd_timing = {
+ .lcd_clk = 0,
+ .clk_auto = 1,
+ .ss_level = 0,
+ .fr_adjust_type = 0,
+ },
+ .hdr_info = {
+ .hdr_support = 0,
+ .features = 0,
+ .primaries_r_x = 0,
+ .primaries_r_y = 0,
+ .primaries_g_x = 0,
+ .primaries_g_y = 0,
+ .primaries_b_x = 0,
+ .primaries_b_y = 0,
+ .white_point_x = 0,
+ .white_point_y = 0,
+ .luma_max = 0,
+ .luma_min = 0,
+ .luma_avg = 0,
+ },
+ .lcd_control = {
+ .ttl_config = &lcd_ttl_config,
+ .lvds_config = &lcd_lvds_config,
+ .vbyone_config = &lcd_vbyone_config,
+ .mipi_config = &lcd_mipi_config,
+ },
+ .lcd_power = &lcd_power_config,
+};
+
+static struct vinfo_s lcd_vinfo = {
+ .name = "panel",
+ .mode = VMODE_LCD,
+ .viu_color_fmt = COLOR_FMT_RGB444,
+};
+
+struct aml_lcd_drv_s *aml_lcd_get_driver(void)
+{
+ return lcd_driver;
+}
+/* ********************************************************* */
+
+static void lcd_chip_detect(void)
+{
+ unsigned int cpu_type;
+
+ cpu_type = get_cpu_type();
+ switch (cpu_type) {
+ case MESON_CPU_MAJOR_ID_GXTVBB:
+ lcd_driver->chip_type = LCD_CHIP_GXTVBB;
+ break;
+ case MESON_CPU_MAJOR_ID_GXL:
+ lcd_driver->chip_type = LCD_CHIP_GXL;
+ break;
+ case MESON_CPU_MAJOR_ID_GXM:
+ lcd_driver->chip_type = LCD_CHIP_GXM;
+ break;
+ case MESON_CPU_MAJOR_ID_TXL:
+ lcd_driver->chip_type = LCD_CHIP_TXL;
+ break;
+ case MESON_CPU_MAJOR_ID_TXLX:
+ lcd_driver->chip_type = LCD_CHIP_TXLX;
+ break;
+ case MESON_CPU_MAJOR_ID_AXG:
+ lcd_driver->chip_type = LCD_CHIP_AXG;
+ break;
+ default:
+ lcd_driver->chip_type = LCD_CHIP_MAX;
+ }
+
+ if (lcd_debug_print_flag)
+ LCDPR("check chip: %d\n", lcd_driver->chip_type);
+}
+
+static void lcd_power_tiny_ctrl(int status)
+{
+ struct lcd_power_ctrl_s *lcd_power = lcd_driver->lcd_config->lcd_power;
+ struct lcd_power_step_s *power_step;
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+ struct aml_lcd_extern_driver_s *ext_drv;
+#endif
+ int i, index;
+
+ LCDPR("%s: %d\n", __func__, status);
+ i = 0;
+ while (i < LCD_PWR_STEP_MAX) {
+ if (status)
+ power_step = &lcd_power->power_on_step[i];
+ else
+ power_step = &lcd_power->power_off_step[i];
+
+ if (power_step->type >= LCD_POWER_TYPE_MAX)
+ break;
+ if (lcd_debug_print_flag) {
+ LCDPR("power_tiny_ctrl: %d, step %d\n", status, i);
+ LCDPR("type=%d, index=%d, value=%d, delay=%d\n",
+ power_step->type, power_step->index,
+ power_step->value, power_step->delay);
+ }
+ switch (power_step->type) {
+ case LCD_POWER_TYPE_CPU:
+ index = power_step->index;
+ lcd_cpu_gpio_set(index, power_step->value);
+ break;
+ case LCD_POWER_TYPE_PMU:
+ LCDPR("to do\n");
+ break;
+ case LCD_POWER_TYPE_SIGNAL:
+ if (status)
+ lcd_driver->driver_tiny_enable();
+ else
+ lcd_driver->driver_tiny_disable();
+ break;
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+ case LCD_POWER_TYPE_EXTERN:
+ index = power_step->index;
+ ext_drv = aml_lcd_extern_get_driver(index);
+ if (ext_drv) {
+ if (status) {
+ if (ext_drv->power_on)
+ ext_drv->power_on();
+ else
+ LCDERR("no ext power on\n");
+ } else {
+ if (ext_drv->power_off)
+ ext_drv->power_off();
+ else
+ LCDERR("no ext power off\n");
+ }
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ if (power_step->delay)
+ mdelay(power_step->delay);
+ i++;
+ }
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s: %d finished\n", __func__, status);
+}
+
+static void lcd_power_ctrl(int status)
+{
+ struct lcd_power_ctrl_s *lcd_power = lcd_driver->lcd_config->lcd_power;
+ struct lcd_power_step_s *power_step;
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+ struct aml_lcd_extern_driver_s *ext_drv;
+#endif
+ int i, index;
+ int ret = 0;
+
+ LCDPR("%s: %d\n", __func__, status);
+ i = 0;
+ while (i < LCD_PWR_STEP_MAX) {
+ if (status)
+ power_step = &lcd_power->power_on_step[i];
+ else
+ power_step = &lcd_power->power_off_step[i];
+
+ if (power_step->type >= LCD_POWER_TYPE_MAX)
+ break;
+ if (lcd_debug_print_flag) {
+ LCDPR("power_ctrl: %d, step %d\n", status, i);
+ LCDPR("type=%d, index=%d, value=%d, delay=%d\n",
+ power_step->type, power_step->index,
+ power_step->value, power_step->delay);
+ }
+ switch (power_step->type) {
+ case LCD_POWER_TYPE_CPU:
+ index = power_step->index;
+ lcd_cpu_gpio_set(index, power_step->value);
+ break;
+ case LCD_POWER_TYPE_PMU:
+ LCDPR("to do\n");
+ break;
+ case LCD_POWER_TYPE_SIGNAL:
+ if (status)
+ ret = lcd_driver->driver_init();
+ else
+ lcd_driver->driver_disable();
+ break;
+#ifdef CONFIG_AMLOGIC_LCD_EXTERN
+ case LCD_POWER_TYPE_EXTERN:
+ index = power_step->index;
+ ext_drv = aml_lcd_extern_get_driver(index);
+ if (ext_drv) {
+ if (status) {
+ if (ext_drv->power_on)
+ ext_drv->power_on();
+ else
+ LCDERR("no ext power on\n");
+ } else {
+ if (ext_drv->power_off)
+ ext_drv->power_off();
+ else
+ LCDERR("no ext power off\n");
+ }
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ if (power_step->delay)
+ mdelay(power_step->delay);
+ i++;
+ }
+
+ if (lcd_debug_print_flag)
+ LCDPR("%s: %d finished\n", __func__, status);
+}
+
+static void lcd_module_enable(void)
+{
+ mutex_lock(&lcd_vout_mutex);
+
+ lcd_driver->driver_init_pre();
+ lcd_driver->power_ctrl(1);
+ lcd_driver->lcd_status = 1;
+
+ mutex_unlock(&lcd_vout_mutex);
+}
+
+static void lcd_module_disable(void)
+{
+ mutex_lock(&lcd_vout_mutex);
+
+ lcd_driver->lcd_status = 0;
+ lcd_driver->power_ctrl(0);
+
+ mutex_unlock(&lcd_vout_mutex);
+}
+
+static void lcd_module_reset(void)
+{
+ mutex_lock(&lcd_vout_mutex);
+
+ lcd_driver->lcd_status = 0;
+ lcd_driver->power_ctrl(0);
+
+ msleep(500);
+
+ lcd_driver->driver_init_pre();
+ lcd_driver->power_ctrl(1);
+ lcd_driver->lcd_status = 1;
+
+ mutex_unlock(&lcd_vout_mutex);
+}
+
+static void lcd_module_tiny_reset(void)
+{
+ mutex_lock(&lcd_vout_mutex);
+
+ lcd_driver->lcd_status = 0;
+ lcd_power_tiny_ctrl(0);
+ mdelay(500);
+ lcd_power_tiny_ctrl(1);
+ lcd_driver->lcd_status = 1;
+
+ mutex_unlock(&lcd_vout_mutex);
+}
+
+/* lcd notify */
+static int lcd_power_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ if (lcd_debug_print_flag)
+ LCDPR("%s: 0x%lx\n", __func__, event);
+
+ if (event & LCD_EVENT_LCD_ON)
+ lcd_module_enable();
+ else if (event & LCD_EVENT_LCD_OFF)
+ lcd_module_disable();
+ else
+ return NOTIFY_DONE;
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block lcd_power_nb = {
+ .notifier_call = lcd_power_notifier,
+ .priority = LCD_PRIORITY_POWER_LCD,
+};
+
+static int lcd_interface_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ if (lcd_debug_print_flag)
+ LCDPR("%s: 0x%lx\n", __func__, event);
+
+ if (event & LCD_EVENT_IF_ON)
+ lcd_driver->power_tiny_ctrl(1);
+ else if (event & LCD_EVENT_IF_OFF)
+ lcd_driver->power_tiny_ctrl(0);
+ else
+ return NOTIFY_DONE;
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block lcd_interface_nb = {
+ .notifier_call = lcd_interface_notifier,
+ .priority = LCD_PRIORITY_POWER_LCD,
+};
+
+static int lcd_bl_select_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ unsigned int *index;
+
+ /* LCDPR("%s: 0x%lx\n", __func__, event); */
+ if ((event & LCD_EVENT_BACKLIGHT_SEL) == 0)
+ return NOTIFY_DONE;
+
+ index = (unsigned int *)data;
+ *index = lcd_driver->lcd_config->backlight_index;
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block lcd_bl_select_nb = {
+ .notifier_call = lcd_bl_select_notifier,
+};
+/* **************************************** */
+
+/* ************************************************************* */
+/* lcd ioctl */
+/* ************************************************************* */
+static int lcd_io_open(struct inode *inode, struct file *file)
+{
+ struct lcd_cdev_s *lcd_cdev;
+
+ LCDPR("%s\n", __func__);
+ lcd_cdev = container_of(inode->i_cdev, struct lcd_cdev_s, cdev);
+ file->private_data = lcd_cdev;
+ return 0;
+}
+
+static int lcd_io_release(struct inode *inode, struct file *file)
+{
+ LCDPR("%s\n", __func__);
+ file->private_data = NULL;
+ return 0;
+}
+
+static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ void __user *argp;
+ int mcd_nr;
+ struct lcd_hdr_info_s *hdr_info = &lcd_driver->lcd_config->hdr_info;
+
+ mcd_nr = _IOC_NR(cmd);
+ LCDPR("%s: cmd_dir = 0x%x, cmd_nr = 0x%x\n",
+ __func__, _IOC_DIR(cmd), mcd_nr);
+
+ argp = (void __user *)arg;
+ switch (mcd_nr) {
+ case LCD_IOC_NR_GET_HDR_INFO:
+ if (copy_to_user(argp, hdr_info, sizeof(struct lcd_hdr_info_s)))
+ ret = -EFAULT;
+ break;
+ case LCD_IOC_NR_SET_HDR_INFO:
+ if (copy_from_user(hdr_info, argp,
+ sizeof(struct lcd_hdr_info_s))) {
+ ret = -EFAULT;
+ } else {
+ lcd_hdr_vinfo_update();
+ if (lcd_debug_print_flag) {
+ LCDPR("set hdr_info:\n"
+ "hdr_support %d\n"
+ "features %d\n"
+ "primaries_r_x %d\n"
+ "primaries_r_y %d\n"
+ "primaries_g_x %d\n"
+ "primaries_g_y %d\n"
+ "primaries_b_x %d\n"
+ "primaries_b_y %d\n"
+ "white_point_x %d\n"
+ "white_point_y %d\n"
+ "luma_max %d\n"
+ "luma_min %d\n\n",
+ hdr_info->hdr_support,
+ hdr_info->features,
+ hdr_info->primaries_r_x,
+ hdr_info->primaries_r_y,
+ hdr_info->primaries_g_x,
+ hdr_info->primaries_g_y,
+ hdr_info->primaries_b_x,
+ hdr_info->primaries_b_y,
+ hdr_info->white_point_x,
+ hdr_info->white_point_y,
+ hdr_info->luma_max,
+ hdr_info->luma_min);
+ }
+ }
+ break;
+ default:
+ LCDERR("not support ioctl cmd_nr: 0x%x\n", mcd_nr);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long lcd_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned long ret;
+
+ arg = (unsigned long)compat_ptr(arg);
+ ret = lcd_ioctl(file, cmd, arg);
+ return ret;
+}
+#endif
+
+static const struct file_operations lcd_fops = {
+ .owner = THIS_MODULE,
+ .open = lcd_io_open,
+ .release = lcd_io_release,
+ .unlocked_ioctl = lcd_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = lcd_compat_ioctl,
+#endif
+};
+
+static int lcd_fops_create(void)
+{
+ int ret = 0;
+
+ lcd_cdev = kmalloc(sizeof(struct lcd_cdev_s), GFP_KERNEL);
+ if (!lcd_cdev) {
+ LCDERR("%s: failed to allocate lcd_cdev\n", __func__);
+ return -1;
+ }
+
+ ret = alloc_chrdev_region(&lcd_cdev->devno, 0, 1, LCD_CDEV_NAME);
+ if (ret < 0) {
+ LCDERR("%s: failed to alloc devno\n", __func__);
+ kfree(lcd_cdev);
+ lcd_cdev = NULL;
+ return -1;
+ }
+
+ cdev_init(&lcd_cdev->cdev, &lcd_fops);
+ lcd_cdev->cdev.owner = THIS_MODULE;
+ ret = cdev_add(&lcd_cdev->cdev, lcd_cdev->devno, 1);
+ if (ret) {
+ LCDERR("%s: failed to add cdev\n", __func__);
+ unregister_chrdev_region(lcd_cdev->devno, 1);
+ kfree(lcd_cdev);
+ lcd_cdev = NULL;
+ return -1;
+ }
+
+ lcd_cdev->dev = device_create(lcd_driver->lcd_debug_class, NULL,
+ lcd_cdev->devno, NULL, LCD_CDEV_NAME);
+ if (IS_ERR(lcd_cdev->dev)) {
+ LCDERR("%s: failed to add device\n", __func__);
+ ret = PTR_ERR(lcd_cdev->dev);
+ cdev_del(&lcd_cdev->cdev);
+ unregister_chrdev_region(lcd_cdev->devno, 1);
+ kfree(lcd_cdev);
+ lcd_cdev = NULL;
+ return -1;
+ }
+
+ LCDPR("%s OK\n", __func__);
+ return 0;
+}
+
+static void lcd_fops_remove(void)
+{
+ cdev_del(&lcd_cdev->cdev);
+ unregister_chrdev_region(lcd_cdev->devno, 1);
+ kfree(lcd_cdev);
+ lcd_cdev = NULL;
+}
+/* ************************************************************* */
+
+static void lcd_init_vout(void)
+{
+ switch (lcd_driver->lcd_mode) {
+#ifdef CONFIG_AMLOGIC_LCD_TV
+ case LCD_MODE_TV:
+ lcd_tv_vout_server_init();
+ break;
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_TABLET
+ case LCD_MODE_TABLET:
+ lcd_tablet_vout_server_init();
+ break;
+#endif
+ default:
+ LCDERR("invalid lcd mode: %d\n", lcd_driver->lcd_mode);
+ break;
+ }
+}
+
+static int lcd_mode_probe(struct device *dev)
+{
+ int ret;
+
+ switch (lcd_driver->lcd_mode) {
+#ifdef CONFIG_AMLOGIC_LCD_TV
+ case LCD_MODE_TV:
+ lcd_tv_probe(dev);
+ break;
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_TABLET
+ case LCD_MODE_TABLET:
+ lcd_tablet_probe(dev);
+ break;
+#endif
+ default:
+ LCDERR("invalid lcd mode: %d\n", lcd_driver->lcd_mode);
+ break;
+ }
+
+ lcd_class_creat();
+ lcd_fops_create();
+
+ ret = aml_lcd_notifier_register(&lcd_interface_nb);
+ if (ret)
+ LCDERR("register aml_bl_select_notifier failed\n");
+ ret = aml_lcd_notifier_register(&lcd_bl_select_nb);
+ if (ret)
+ LCDERR("register aml_bl_select_notifier failed\n");
+ ret = aml_lcd_notifier_register(&lcd_power_nb);
+ if (ret)
+ LCDPR("register lcd_power_notifier failed\n");
+
+ /* add notifier for video sync_duration info refresh */
+ vout_notifier_call_chain(VOUT_EVENT_MODE_CHANGE,
+ &lcd_driver->lcd_info->mode);
+
+ return 0;
+}
+
+static int lcd_mode_remove(struct device *dev)
+{
+ switch (lcd_driver->lcd_mode) {
+#ifdef CONFIG_AMLOGIC_LCD_TV
+ case LCD_MODE_TV:
+ lcd_tv_remove(dev);
+ break;
+#endif
+#ifdef CONFIG_AMLOGIC_LCD_TABLET
+ case LCD_MODE_TABLET:
+ lcd_tablet_remove(dev);
+ break;
+#endif
+ default:
+ LCDPR("invalid lcd mode\n");
+ break;
+ }
+ return 0;
+}
+
+static void lcd_config_probe_delayed(struct work_struct *work)
+{
+ int key_init_flag = 0;
+ int i = 0;
+ int ret;
+
+ if (lcd_driver->lcd_key_valid) {
+ key_init_flag = key_unify_get_init_flag();
+ while (key_init_flag == 0) {
+ if (i++ >= LCD_UNIFYKEY_WAIT_TIMEOUT)
+ break;
+ msleep(20);
+ key_init_flag = key_unify_get_init_flag();
+ }
+ LCDPR("key_init_flag=%d, i=%d\n", key_init_flag, i);
+ }
+ ret = lcd_mode_probe(lcd_driver->dev);
+ if (ret) {
+ kfree(lcd_driver);
+ lcd_driver = NULL;
+ LCDERR("probe exit\n");
+ }
+}
+
+static void lcd_config_default(void)
+{
+ struct lcd_config_s *pconf;
+
+ pconf = lcd_driver->lcd_config;
+ pconf->lcd_basic.h_active = lcd_vcbus_read(ENCL_VIDEO_HAVON_END)
+ - lcd_vcbus_read(ENCL_VIDEO_HAVON_BEGIN) + 1;
+ pconf->lcd_basic.v_active = lcd_vcbus_read(ENCL_VIDEO_VAVON_ELINE)
+ - lcd_vcbus_read(ENCL_VIDEO_VAVON_BLINE) + 1;
+ if (lcd_vcbus_read(ENCL_VIDEO_EN)) {
+ lcd_driver->lcd_status = 1;
+ lcd_resume_flag = 1;
+ } else {
+ lcd_driver->lcd_status = 0;
+ lcd_resume_flag = 0;
+ }
+ LCDPR("status: %d\n", lcd_driver->lcd_status);
+}
+
+static int lcd_config_probe(void)
+{
+ const char *str;
+ unsigned int val;
+ int ret = 0;
+
+ if (lcd_driver->dev->of_node == NULL) {
+ LCDERR("dev of_node is null\n");
+ lcd_driver->lcd_mode = LCD_MODE_MAX;
+ return -1;
+ }
+
+ /* lcd driver assign */
+ ret = of_property_read_string(lcd_driver->dev->of_node, "mode", &str);
+ if (ret) {
+ str = "none";
+ LCDERR("failed to get mode\n");
+ return -1;
+ }
+ lcd_driver->lcd_mode = lcd_mode_str_to_mode(str);
+ ret = of_property_read_u32(lcd_driver->dev->of_node,
+ "fr_auto_policy", &val);
+ if (ret) {
+ if (lcd_debug_print_flag)
+ LCDPR("failed to get fr_auto_policy\n");
+ lcd_driver->fr_auto_policy = 0;
+ } else {
+ lcd_driver->fr_auto_policy = (unsigned char)val;
+ }
+ ret = of_property_read_u32(lcd_driver->dev->of_node, "key_valid", &val);
+ if (ret) {
+ if (lcd_debug_print_flag)
+ LCDPR("failed to get key_valid\n");
+ lcd_driver->lcd_key_valid = 0;
+ } else {
+ lcd_driver->lcd_key_valid = (unsigned char)val;
+ }
+ LCDPR("detect mode: %s, fr_auto_policy: %d, key_valid: %d\n",
+ str, lcd_driver->fr_auto_policy, lcd_driver->lcd_key_valid);
+
+ lcd_clktree_probe();
+
+ lcd_driver->lcd_info = &lcd_vinfo;
+ lcd_driver->lcd_config = &lcd_config_dft;
+ lcd_driver->lcd_config->pinmux_flag = 0;
+ lcd_driver->lcd_test_flag = 0;
+ lcd_driver->power_ctrl = lcd_power_ctrl;
+ lcd_driver->module_reset = lcd_module_reset;
+ lcd_driver->power_tiny_ctrl = lcd_power_tiny_ctrl;
+ lcd_driver->module_tiny_reset = lcd_module_tiny_reset;
+ lcd_config_default();
+ lcd_init_vout();
+
+ if (lcd_driver->lcd_key_valid) {
+ if (lcd_driver->workqueue) {
+ queue_delayed_work(lcd_driver->workqueue,
+ &lcd_driver->lcd_probe_delayed_work,
+ msecs_to_jiffies(2000));
+ } else {
+ LCDPR("Warning: no lcd_probe_delayed workqueue\n");
+ ret = lcd_mode_probe(lcd_driver->dev);
+ if (ret) {
+ kfree(lcd_driver);
+ lcd_driver = NULL;
+ LCDERR("probe exit\n");
+ }
+ }
+ } else {
+ ret = lcd_mode_probe(lcd_driver->dev);
+ if (ret) {
+ kfree(lcd_driver);
+ lcd_driver = NULL;
+ LCDERR("probe exit\n");
+ }
+ }
+
+ return 0;
+}
+
+static void lcd_resume_work(struct work_struct *p_work)
+{
+ mutex_lock(&lcd_power_mutex);
+ lcd_resume_flag = 1;
+ aml_lcd_notifier_call_chain(LCD_EVENT_POWER_ON, NULL);
+ LCDPR("%s finished\n", __func__);
+ mutex_unlock(&lcd_power_mutex);
+}
+
+static int lcd_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+#ifdef LCD_DEBUG_INFO
+ lcd_debug_print_flag = 1;
+#else
+ lcd_debug_print_flag = 0;
+#endif
+ lcd_driver = kmalloc(sizeof(struct aml_lcd_drv_s), GFP_KERNEL);
+ if (!lcd_driver) {
+ LCDERR("%s: lcd driver no enough memory\n", __func__);
+ return -ENOMEM;
+ }
+ lcd_driver->dev = &pdev->dev;
+
+ mutex_init(&lcd_vout_mutex);
+ mutex_init(&lcd_power_mutex);
+ lcd_vout_serve_bypass = 0;
+
+ /* init workqueue */
+ INIT_DELAYED_WORK(&lcd_driver->lcd_probe_delayed_work,
+ lcd_config_probe_delayed);
+ lcd_driver->workqueue = create_singlethread_workqueue("lcd_work_queue");
+ if (lcd_driver->workqueue == NULL)
+ LCDERR("can't create lcd workqueue\n");
+
+ INIT_WORK(&(lcd_driver->lcd_resume_work), lcd_resume_work);
+
+ lcd_chip_detect();
+ lcd_ioremap();
+ lcd_clk_config_probe();
+ ret = lcd_config_probe();
+
+ LCDPR("%s %s\n", __func__, (ret ? "failed" : "ok"));
+ return 0;
+}
+
+static int lcd_remove(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = cancel_delayed_work(&lcd_driver->lcd_probe_delayed_work);
+ if (lcd_driver->workqueue)
+ destroy_workqueue(lcd_driver->workqueue);
+
+ if (lcd_driver) {
+ aml_lcd_notifier_unregister(&lcd_power_nb);
+ aml_lcd_notifier_unregister(&lcd_bl_select_nb);
+ aml_lcd_notifier_unregister(&lcd_interface_nb);
+
+ lcd_fops_remove();
+ lcd_class_remove();
+ if (!IS_ERR(lcd_driver->vencl_top))
+ devm_clk_put(lcd_driver->dev, lcd_driver->vencl_top);
+ if (!IS_ERR(lcd_driver->vencl_int))
+ devm_clk_put(lcd_driver->dev, lcd_driver->vencl_int);
+
+ lcd_mode_remove(lcd_driver->dev);
+ kfree(lcd_driver);
+ lcd_driver = NULL;
+ }
+
+ LCDPR("%s\n", __func__);
+ return 0;
+}
+
+static int lcd_resume(struct platform_device *pdev)
+{
+ queue_work(lcd_driver->workqueue, &(lcd_driver->lcd_resume_work));
+
+ return 0;
+}
+
+static int lcd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ mutex_lock(&lcd_power_mutex);
+ if (lcd_driver->lcd_status) {
+ aml_lcd_notifier_call_chain(LCD_EVENT_POWER_OFF, NULL);
+ lcd_resume_flag = 0;
+ LCDPR("%s finished\n", __func__);
+ }
+ mutex_unlock(&lcd_power_mutex);
+ return 0;
+}
+
+static void lcd_shutdown(struct platform_device *pdev)
+{
+ if (lcd_debug_print_flag)
+ LCDPR("%s\n", __func__);
+
+ if (lcd_driver->lcd_status)
+ aml_lcd_notifier_call_chain(LCD_EVENT_POWER_OFF, NULL);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id lcd_dt_match[] = {
+ {
+ .compatible = "amlogic, lcd",
+ },
+ {},
+};
+#endif
+
+static struct platform_driver lcd_platform_driver = {
+ .probe = lcd_probe,
+ .remove = lcd_remove,
+ .suspend = lcd_suspend,
+ .resume = lcd_resume,
+ .shutdown = lcd_shutdown,
+ .driver = {
+ .name = "mesonlcd",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = lcd_dt_match,
+#endif
+ },
+};
+
+static int __init lcd_init(void)
+{
+ if (platform_driver_register(&lcd_platform_driver)) {
+ LCDERR("failed to register lcd driver module\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit lcd_exit(void)
+{
+ platform_driver_unregister(&lcd_platform_driver);
+}
+
+subsys_initcall(lcd_init);
+module_exit(lcd_exit);
+
+static int __init lcd_panel_type_para_setup(char *str)
+{
+ if (str != NULL)
+ sprintf(lcd_propname, "%s", str);
+
+ LCDPR("panel_type: %s\n", lcd_propname);
+ return 0;
+}
+__setup("panel_type=", lcd_panel_type_para_setup);
+
+MODULE_DESCRIPTION("Meson LCD Panel Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Amlogic, Inc.");
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) };
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*/
"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[] = {
FUNCTION(tsin_a),
FUNCTION(tsin_b),
FUNCTION(tsout_a),
- FUNCTION(lcd),
+ FUNCTION(lcd_ttl),
FUNCTION(nor),
FUNCTION(dvp),
FUNCTION(dmic),
--- /dev/null
+/*
+ * include/linux/amlogic/media/vout/lcd/aml_bl.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __INC_AML_BL_H
+#define __INC_AML_BL_H
+#include <linux/workqueue.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/pinctrl/consumer.h>
+
+#define BLPR(fmt, args...) pr_info("bl: "fmt"", ## args)
+#define BLERR(fmt, args...) pr_err("bl error: "fmt"", ## args)
+#define AML_BL_NAME "aml-bl"
+
+#define BL_LEVEL_MAX 255
+#define BL_LEVEL_MIN 10
+#define BL_LEVEL_OFF 1
+
+#define BL_LEVEL_MID 128
+#define BL_LEVEL_MID_MAPPED BL_LEVEL_MID
+#define BL_LEVEL_DEFAULT BL_LEVEL_MID
+
+#define XTAL_FREQ_HZ (24*1000*1000) /* unit: Hz */
+#define XTAL_HALF_FREQ_HZ (24*1000*500) /* 24M/2 in HZ */
+
+#define BL_FREQ_DEFAULT 1000 /* unit: HZ */
+#define BL_FREQ_VS_DEFAULT 2 /* multiple 2 of vfreq */
+
+enum bl_chip_type_e {
+ BL_CHIP_GXTVBB,
+ BL_CHIP_GXL,
+ BL_CHIP_GXM,
+ BL_CHIP_TXL,
+ BL_CHIP_TXLX,
+ BL_CHIP_AXG,
+ BL_CHIP_MAX,
+};
+
+/* for lcd backlight power */
+enum bl_ctrl_method_e {
+ BL_CTRL_GPIO = 0,
+ BL_CTRL_PWM,
+ BL_CTRL_PWM_COMBO,
+ BL_CTRL_LOCAL_DIMING,
+ BL_CTRL_EXTERN,
+ BL_CTRL_MAX,
+};
+
+enum bl_pwm_method_e {
+ BL_PWM_NEGATIVE = 0,
+ BL_PWM_POSITIVE,
+ BL_PWM_METHOD_MAX,
+};
+
+enum bl_pwm_port_e {
+ BL_PWM_A = 0,
+ BL_PWM_B,
+ BL_PWM_C,
+ BL_PWM_D,
+ BL_PWM_E,
+ BL_PWM_F,
+ BL_PWM_VS,
+ BL_PWM_MAX,
+};
+
+enum bl_off_policy_e {
+ BL_OFF_POLICY_NONE = 0,
+ BL_OFF_POLICY_ALWAYS,
+ BL_OFF_POLICY_ONCE,
+ BL_OFF_POLICY_MAX,
+};
+
+#define BL_GPIO_OUTPUT_LOW 0
+#define BL_GPIO_OUTPUT_HIGH 1
+#define BL_GPIO_INPUT 2
+
+#define BL_GPIO_MAX 0xff
+#define BL_GPIO_NUM_MAX 5
+struct bl_gpio_s {
+ char name[15];
+ struct gpio_desc *gpio;
+ int flag;
+};
+
+struct bl_pwm_config_s {
+ unsigned int index;
+ struct pwm_device *bl_pwm_ch;
+ struct aml_pwm_chip *bl_pwm_chip;
+ enum bl_pwm_method_e pwm_method;
+ enum bl_pwm_port_e pwm_port;
+ unsigned int level_max;
+ unsigned int level_min;
+ unsigned int pwm_freq; /* pwm_vs: 1~4(vfreq), pwm: freq(unit: Hz) */
+ unsigned int pwm_duty; /* unit: % */
+ unsigned int pwm_duty_max; /* unit: % */
+ unsigned int pwm_duty_min; /* unit: % */
+ unsigned int pwm_cnt; /* internal used for pwm control */
+ unsigned int pwm_pre_div; /* internal used for pwm control */
+ unsigned int pwm_max; /* internal used for pwm control */
+ unsigned int pwm_min; /* internal used for pwm control */
+ unsigned int pwm_level; /* internal used for pwm control */
+};
+
+struct bl_config_s {
+ char name[30];
+ unsigned int level_default;
+ unsigned int level_min;
+ unsigned int level_max;
+ unsigned int level_mid;
+ unsigned int level_mid_mapping;
+
+ enum bl_ctrl_method_e method;
+ unsigned int en_gpio;
+ unsigned int en_gpio_on;
+ unsigned int en_gpio_off;
+ unsigned int power_on_delay;
+ unsigned int power_off_delay;
+ unsigned int dim_max;
+ unsigned int dim_min;
+
+ struct bl_pwm_config_s *bl_pwm;
+ struct bl_pwm_config_s *bl_pwm_combo0;
+ struct bl_pwm_config_s *bl_pwm_combo1;
+ unsigned int pwm_on_delay;
+ unsigned int pwm_off_delay;
+
+ struct bl_gpio_s bl_gpio[BL_GPIO_NUM_MAX];
+ struct pinctrl *pin;
+ unsigned int pinmux_flag;
+};
+
+#define BL_INDEX_DEFAULT 0
+
+/* backlight_properties: state */
+/* Flags used to signal drivers of state changes */
+/* Upper 4 bits in bl props are reserved for driver internal use */
+#define BL_STATE_LCD_ON (1 << 3)
+#define BL_STATE_BL_POWER_ON (1 << 1)
+#define BL_STATE_BL_ON (1 << 0)
+struct aml_bl_drv_s {
+ unsigned int index;
+ unsigned int level;
+ unsigned int state;
+ enum bl_chip_type_e chip_type;
+ struct device *dev;
+ struct bl_config_s *bconf;
+ struct backlight_device *bldev;
+ struct workqueue_struct *workqueue;
+ struct delayed_work bl_delayed_work;
+};
+
+extern enum bl_chip_type_e aml_bl_check_chip(void);
+extern struct aml_bl_drv_s *aml_bl_get_driver(void);
+extern void bl_pwm_config_init(struct bl_pwm_config_s *bl_pwm);
+extern enum bl_pwm_port_e bl_pwm_str_to_pwm(const char *str);
+
+#define BL_GPIO_OUTPUT_LOW 0
+#define BL_GPIO_OUTPUT_HIGH 1
+#define BL_GPIO_INPUT 2
+
+#endif
+
--- /dev/null
+/*
+ * include/linux/amlogic/media/vout/lcd/aml_bl_extern.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __AMLOGIC_BL_EXTERN_H_
+#define __AMLOGIC_BL_EXTERN_H_
+
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/pinctrl/consumer.h>
+
+enum bl_extern_type_e {
+ BL_EXTERN_I2C = 0,
+ BL_EXTERN_SPI,
+ BL_EXTERN_OTHER,
+ BL_EXTERN_MAX,
+};
+
+struct bl_extern_config_s {
+ const char *name;
+ enum bl_extern_type_e type;
+ struct gpio_desc *gpio;
+ unsigned char gpio_on;
+ unsigned char gpio_off;
+
+ int i2c_addr;
+ int i2c_bus;
+ struct gpio_desc *spi_cs;
+ struct gpio_desc *spi_clk;
+ struct gpio_desc *spi_data;
+ unsigned int dim_min;
+ unsigned int dim_max;
+ /* unsigned int level_min; */
+ /* unsigned int level_max; */
+};
+
+/*******global API******/
+struct aml_bl_extern_driver_t {
+ const char *name;
+ enum bl_extern_type_e type;
+ int (*power_on)(void);
+ int (*power_off)(void);
+ int (*set_level)(unsigned int level);
+};
+
+#define BL_EXTERN_DRIVER "bl_extern"
+
+
+#define bl_extern_gpio_free(gpio) gpiod_free(gpio, BL_EXTERN_DRIVER)
+#define bl_extern_gpio_input(gpio) gpiod_direction_input(gpio)
+#define bl_extern_gpio_output(gpio, val) gpiod_direction_output(gpio, val)
+#define bl_extern_gpio_get_value(gpio) gpiod_get_value(gpio)
+#define bl_extern_gpio_set_value(gpio, val) gpiod_set_value(gpio, val)
+
+
+extern struct aml_bl_extern_driver_t *aml_bl_extern_get_driver(void);
+extern int bl_extern_driver_check(void);
+extern int get_bl_extern_dt_data(struct device dev,
+ struct bl_extern_config_s *pdata);
+
+extern void get_bl_ext_level(struct bl_extern_config_s *bl_ext_cfg);
+
+#endif
+
--- /dev/null
+/*
+ * include/linux/amlogic/media/vout/lcd/aml_ldim.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _INC_AML_LDIM_H_
+#define _INC_AML_LDIM_H_
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/amlogic/media/vout/aml_bl.h>
+#include <linux/spi/spi.h>
+
+enum ldim_dev_type_e {
+ LDIM_DEV_TYPE_NORMAL = 0,
+ LDIM_DEV_TYPE_SPI,
+ LDIM_DEV_TYPE_I2C,
+ LDIM_DEV_TYPE_MAX,
+};
+
+#define LDIM_SPI_INIT_ON_SIZE 300
+#define LDIM_SPI_INIT_OFF_SIZE 20
+struct ldim_dev_config_s {
+ char name[20];
+ char pinmux_name[20];
+ unsigned char type;
+ int cs_hold_delay;
+ int cs_clk_delay;
+ int en_gpio;
+ int en_gpio_on;
+ int en_gpio_off;
+ int lamp_err_gpio;
+ unsigned char fault_check;
+ unsigned char write_check;
+
+ unsigned int dim_min;
+ unsigned int dim_max;
+ unsigned char cmd_size;
+ unsigned char *init_on;
+ unsigned char *init_off;
+
+ struct bl_pwm_config_s pwm_config;
+};
+
+/*******global API******/
+struct aml_ldim_driver_s {
+ int valid_flag;
+ int dev_index;
+ int static_pic_flag;
+ struct ldim_dev_config_s *ldev_conf;
+ unsigned short *ldim_matrix_buf;
+ int (*init)(void);
+ int (*power_on)(void);
+ int (*power_off)(void);
+ int (*set_level)(unsigned int level);
+ int (*pinmux_ctrl)(char *pin_str, int status);
+ int (*pwm_vs_update)(void);
+ int (*device_power_on)(void);
+ int (*device_power_off)(void);
+ int (*device_bri_update)(unsigned short *buf, unsigned char len);
+ int (*device_bri_check)(void);
+ void (*config_print)(void);
+ void (*test_ctrl)(int flag);
+ struct pinctrl *pin;
+ struct device *dev;
+ struct spi_device *spi;
+ struct spi_board_info *spi_dev;
+};
+
+extern struct aml_ldim_driver_s *aml_ldim_get_driver(void);
+extern int aml_ldim_probe(struct platform_device *pdev);
+extern int aml_ldim_remove(void);
+
+#endif
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * include/linux/amlogic/media/vout/lcd/lcd_notify.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _INC_LCD_NOTIFY_H_
+#define _INC_LCD_NOTIFY_H_
+
+#include <linux/notifier.h>
+
+#define LCD_PRIORITY_INIT_CONFIG 4
+#define LCD_PRIORITY_INIT_VOUT 3
+
+#define LCD_PRIORITY_BLACK_SCREEN 3
+#define LCD_PRIORITY_POWER_BL_OFF 2
+#define LCD_PRIORITY_POWER_LCD 1
+#define LCD_PRIORITY_POWER_BL_ON 0
+
+#define LCD_EVENT_BL_ON (1 << 0)
+#define LCD_EVENT_LCD_ON (1 << 1)
+#define LCD_EVENT_IF_ON (1 << 2)
+#define LCD_EVENT_POWER_ON (LCD_EVENT_BL_ON | LCD_EVENT_LCD_ON)
+#define LCD_EVENT_IF_POWER_ON (LCD_EVENT_BL_ON | LCD_EVENT_IF_ON)
+#define LCD_EVENT_BLACK_SCREEN (1 << 3)
+#define LCD_EVENT_BL_OFF (1 << 4)
+#define LCD_EVENT_LCD_OFF (1 << 5)
+#define LCD_EVENT_IF_OFF (1 << 6)
+#define LCD_EVENT_POWER_OFF (LCD_EVENT_BL_OFF | LCD_EVENT_LCD_OFF |\
+ LCD_EVENT_BLACK_SCREEN)
+#define LCD_EVENT_IF_POWER_OFF (LCD_EVENT_BL_OFF | LCD_EVENT_IF_OFF |\
+ LCD_EVENT_BLACK_SCREEN)
+
+/* lcd backlight index select */
+#define LCD_EVENT_BACKLIGHT_SEL (1 << 8)
+/* lcd backlight pwm_vs vfreq change occurred */
+#define LCD_EVENT_BACKLIGHT_UPDATE (1 << 9)
+
+#define LCD_EVENT_GAMMA_UPDATE (1 << 10)
+
+/* lcd frame rate change occurred */
+#define LCD_EVENT_FRAME_RATE_ADJUST (1 << 12)
+/* lcd config change occurred */
+#define LCD_EVENT_CONFIG_UPDATE (1 << 13)
+/* lcd bist pattern test occurred */
+#define LCD_EVENT_TEST_PATTERN (1 << 14)
+
+extern int aml_lcd_notifier_register(struct notifier_block *nb);
+extern int aml_lcd_notifier_unregister(struct notifier_block *nb);
+extern int aml_lcd_notifier_call_chain(unsigned long event, void *v);
+
+#endif /* _INC_LCD_NOTIFY_H_ */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * include/linux/amlogic/media/vout/lcd/lcd_vout.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _INC_LCD_VOUT_H
+#define _INC_LCD_VOUT_H
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/amlogic/media/vout/vout_notify.h>
+
+/* debug print define */
+/* #define LCD_DEBUG_INFO */
+extern unsigned char lcd_debug_print_flag;
+#define LCDPR(fmt, args...) pr_info("lcd: "fmt"", ## args)
+#define LCDERR(fmt, args...) pr_err("lcd: error: "fmt"", ## args)
+
+/* clk parameter bit define */
+/* pll_ctrl, div_ctrl, clk_ctrl */
+
+/* ******** pll_ctrl ******** */
+#define PLL_CTRL_OD3 20 /* [21:20] */
+#define PLL_CTRL_OD2 18 /* [19:18] */
+#define PLL_CTRL_OD1 16 /* [17:16] */
+#define PLL_CTRL_N 9 /* [13:9] */
+#define PLL_CTRL_M 0 /* [8:0] */
+
+/* ******** div_ctrl ******** */
+#define DIV_CTRL_EDP_DIV1 24 /* [26:24] */
+#define DIV_CTRL_EDP_DIV0 20 /* [23:20] */
+#define DIV_CTRL_DIV_POST 12 /* [14:12] */
+#define DIV_CTRL_DIV_PRE 8 /* [10:8] */
+#define DIV_CTRL_DIV_SEL 8 /* [15:8] */
+#define DIV_CTRL_XD 0 /* [7:0] */
+
+/* ******** clk_ctrl ******** */
+#define CLK_CTRL_LEVEL 12 /* [14:12] */
+#define CLK_CTRL_FRAC 0 /* [11:0] */
+
+/* VENC to TCON sync delay */
+#define TTL_DELAY 13
+
+/* ******** AXG ******** */
+/* bit[15:11] */
+#define BIT_PHY_LANE_AXG 11
+#define PHY_LANE_WIDTH_AXG 5
+
+/* MIPI-DSI */
+#define DSI_LANE_0 (1 << 4)
+#define DSI_LANE_1 (1 << 3)
+#define DSI_LANE_CLK (1 << 2)
+#define DSI_LANE_2 (1 << 1)
+#define DSI_LANE_3 (1 << 0)
+#define DSI_LANE_COUNT_1 (DSI_LANE_CLK | DSI_LANE_0)
+#define DSI_LANE_COUNT_2 (DSI_LANE_CLK | DSI_LANE_0 | DSI_LANE_1)
+#define DSI_LANE_COUNT_3 (DSI_LANE_CLK | DSI_LANE_0 |\
+ DSI_LANE_1 | DSI_LANE_2)
+#define DSI_LANE_COUNT_4 (DSI_LANE_CLK | DSI_LANE_0 |\
+ DSI_LANE_1 | DSI_LANE_2 | DSI_LANE_3)
+
+
+/* global control define */
+enum lcd_mode_e {
+ LCD_MODE_TV = 0,
+ LCD_MODE_TABLET,
+ LCD_MODE_MAX,
+};
+
+
+enum lcd_chip_e {
+ LCD_CHIP_GXTVBB = 0,
+ LCD_CHIP_GXL, /* 1 */
+ LCD_CHIP_GXM, /* 2 */
+ LCD_CHIP_TXL, /* 3 */
+ LCD_CHIP_TXLX, /* 4 */
+ LCD_CHIP_AXG, /* 5 */
+ LCD_CHIP_MAX,
+};
+
+
+enum lcd_type_e {
+ LCD_TTL = 0,
+ LCD_LVDS,
+ LCD_VBYONE,
+ LCD_MIPI,
+ LCD_TYPE_MAX,
+};
+
+#define MOD_LEN_MAX 30
+struct lcd_basic_s {
+ char model_name[MOD_LEN_MAX];
+ enum lcd_type_e lcd_type;
+ unsigned short lcd_bits;
+
+ unsigned short h_active; /* Horizontal display area */
+ unsigned short v_active; /* Vertical display area */
+ unsigned short h_period; /* Horizontal total period time */
+ unsigned short v_period; /* Vertical total period time */
+ unsigned short h_period_min;
+ unsigned short h_period_max;
+ unsigned short v_period_min;
+ unsigned short v_period_max;
+ unsigned int lcd_clk_min;
+ unsigned int lcd_clk_max;
+
+ unsigned int screen_width; /* screen physical width(unit: mm) */
+ unsigned int screen_height; /* screen physical height(unit: mm) */
+};
+
+#define LCD_CLK_FRAC_UPDATE (1 << 0)
+#define LCD_CLK_PLL_CHANGE (1 << 1)
+struct lcd_timing_s {
+ unsigned char clk_auto; /* clk parameters auto generation */
+ unsigned int lcd_clk; /* pixel clock(unit: Hz) */
+ unsigned int lcd_clk_dft; /* internal used */
+ unsigned int h_period_dft; /* internal used */
+ unsigned int v_period_dft; /* internal used */
+ unsigned char clk_change; /* internal used */
+ unsigned int pll_ctrl; /* pll settings */
+ unsigned int div_ctrl; /* divider settings */
+ unsigned int clk_ctrl; /* clock settings */
+
+ unsigned char fr_adjust_type; /* 0=clock, 1=htotal, 2=vtotal */
+ unsigned char ss_level;
+
+ unsigned int sync_duration_num;
+ unsigned int sync_duration_den;
+
+ unsigned short video_on_pixel;
+ unsigned short video_on_line;
+
+ unsigned short hsync_width;
+ unsigned short hsync_bp;
+ unsigned short hsync_pol;
+ unsigned short vsync_width;
+ unsigned short vsync_bp;
+ unsigned short vsync_pol;
+ /* unsigned int vsync_h_phase; // [31]sign, [15:0]value */
+ unsigned int h_offset;
+ unsigned int v_offset;
+
+ unsigned short de_hs_addr;
+ unsigned short de_he_addr;
+ unsigned short de_vs_addr;
+ unsigned short de_ve_addr;
+
+ unsigned short hs_hs_addr;
+ unsigned short hs_he_addr;
+ unsigned short hs_vs_addr;
+ unsigned short hs_ve_addr;
+
+ unsigned short vs_hs_addr;
+ unsigned short vs_he_addr;
+ unsigned short vs_vs_addr;
+ unsigned short vs_ve_addr;
+};
+
+/* HDR info define */
+struct lcd_hdr_info_s {
+ unsigned int hdr_support;
+ unsigned int features;
+ unsigned int primaries_r_x;
+ unsigned int primaries_r_y;
+ unsigned int primaries_g_x;
+ unsigned int primaries_g_y;
+ unsigned int primaries_b_x;
+ unsigned int primaries_b_y;
+ unsigned int white_point_x;
+ unsigned int white_point_y;
+ unsigned int luma_max;
+ unsigned int luma_min;
+ unsigned int luma_avg;
+};
+
+struct ttl_config_s {
+ unsigned int clk_pol;
+ unsigned int sync_valid; /* [1]DE, [0]hvsync */
+ unsigned int swap_ctrl; /* [1]rb swap, [0]bit swap */
+};
+
+#define LVDS_PHY_VSWING_DFT 3
+#define LVDS_PHY_PREEM_DFT 0
+#define LVDS_PHY_CLK_VSWING_DFT 0
+#define LVDS_PHY_CLK_PREEM_DFT 0
+struct lvds_config_s {
+ unsigned int lvds_vswing;
+ unsigned int lvds_repack;
+ unsigned int dual_port;
+ unsigned int pn_swap;
+ unsigned int port_swap;
+ unsigned int lane_reverse;
+ unsigned int port_sel;
+ unsigned int phy_vswing;
+ unsigned int phy_preem;
+ unsigned int phy_clk_vswing;
+ unsigned int phy_clk_preem;
+};
+
+#define VX1_PHY_VSWING_DFT 3
+#define VX1_PHY_PREEM_DFT 0
+struct vbyone_config_s {
+ unsigned int lane_count;
+ unsigned int region_num;
+ unsigned int byte_mode;
+ unsigned int color_fmt;
+ unsigned int phy_div;
+ unsigned int bit_rate;
+ unsigned int phy_vswing; /*[4]:ext_pullup, [3:0]vswing*/
+ unsigned int phy_preem;
+ unsigned int intr_en;
+ unsigned int vsync_intr_en;
+};
+
+/* mipi-dsi config */
+/* Operation mode parameters */
+#define OPERATION_VIDEO_MODE 0
+#define OPERATION_COMMAND_MODE 1
+
+#define SYNC_PULSE 0x0
+#define SYNC_EVENT 0x1
+#define BURST_MODE 0x2
+
+/* command config */
+#define DSI_CMD_INDEX 1 /* byte[1] */
+
+#define DSI_INIT_ON_MAX 100
+#define DSI_INIT_OFF_MAX 30
+
+struct dsi_config_s {
+ unsigned char lane_num;
+ unsigned int bit_rate_max; /* MHz */
+ unsigned int bit_rate_min; /* MHz*/
+ unsigned int bit_rate; /* Hz */
+ unsigned int factor_numerator;
+ unsigned int factor_denominator; /* 100 */
+ unsigned char operation_mode_init; /* 0=video mode, 1=command mode */
+ unsigned char operation_mode_display; /* 0=video mode, 1=command mode */
+ unsigned char video_mode_type; /* 0=sync_pulse, 1=sync_event, 2=burst */
+ unsigned char clk_lp_continuous; /* 0=stop, 1=continue */
+ unsigned char phy_stop_wait; /* 0=auto, 1=standard, 2=slow */
+
+ unsigned int venc_data_width;
+ unsigned int dpi_data_format;
+ unsigned int venc_fmt;
+
+ unsigned char *dsi_init_on;
+ unsigned char *dsi_init_off;
+ unsigned char extern_init;
+};
+
+struct lcd_control_config_s {
+ struct ttl_config_s *ttl_config;
+ struct lvds_config_s *lvds_config;
+ struct vbyone_config_s *vbyone_config;
+ struct dsi_config_s *mipi_config;
+};
+
+/* power control define */
+enum lcd_power_type_e {
+ LCD_POWER_TYPE_CPU = 0,
+ LCD_POWER_TYPE_PMU,
+ LCD_POWER_TYPE_SIGNAL,
+ LCD_POWER_TYPE_EXTERN,
+ LCD_POWER_TYPE_MAX,
+};
+
+enum lcd_pmu_gpio_e {
+ LCD_PMU_GPIO0 = 0,
+ LCD_PMU_GPIO1,
+ LCD_PMU_GPIO2,
+ LCD_PMU_GPIO3,
+ LCD_PMU_GPIO4,
+ LCD_PMU_GPIO_MAX,
+};
+
+#define LCD_GPIO_MAX 0xff
+#define LCD_GPIO_OUTPUT_LOW 0
+#define LCD_GPIO_OUTPUT_HIGH 1
+#define LCD_GPIO_INPUT 2
+
+/* Power Control */
+#define LCD_CPU_GPIO_NUM_MAX 10
+struct lcd_cpu_gpio_s {
+ char name[15];
+ struct gpio_desc *gpio;
+ int flag;
+};
+
+#define LCD_PMU_GPIO_NUM_MAX 3
+struct lcd_pmu_gpio_s {
+ char name[15];
+ int gpio;
+};
+
+#define LCD_PWR_STEP_MAX 15
+struct lcd_power_step_s {
+ unsigned char type;
+ unsigned int index; /* point to lcd_cpu/pmu_gpio_s or lcd_extern */
+ unsigned int value;
+ unsigned int delay;
+};
+
+struct lcd_power_ctrl_s {
+ struct lcd_cpu_gpio_s cpu_gpio[LCD_CPU_GPIO_NUM_MAX];
+ struct lcd_pmu_gpio_s pmu_gpio[LCD_PMU_GPIO_NUM_MAX];
+ struct lcd_power_step_s power_on_step[LCD_PWR_STEP_MAX];
+ struct lcd_power_step_s power_off_step[LCD_PWR_STEP_MAX];
+ int power_on_step_max; /* internal use for debug */
+ int power_off_step_max; /* internal use for debug */
+};
+
+struct lcd_clk_gate_ctrl_s {
+ struct reset_control *encl;
+ struct reset_control *vencl;
+};
+
+struct lcd_config_s {
+ char *lcd_propname;
+ unsigned int backlight_index;
+ struct lcd_basic_s lcd_basic;
+ struct lcd_timing_s lcd_timing;
+ struct lcd_hdr_info_s hdr_info;
+ struct lcd_control_config_s lcd_control;
+ struct lcd_power_ctrl_s *lcd_power;
+ struct pinctrl *pin;
+ unsigned char pinmux_flag;
+ struct lcd_clk_gate_ctrl_s rstc;
+};
+
+struct lcd_duration_s {
+ unsigned int duration_num;
+ unsigned int duration_den;
+};
+
+struct aml_lcd_drv_s {
+ char *version;
+ enum lcd_chip_e chip_type;
+ unsigned char lcd_mode;
+ unsigned char lcd_status;
+ unsigned char lcd_key_valid;
+ unsigned char lcd_config_load;
+ unsigned char lcd_test_flag;
+ unsigned char lcd_mute;
+
+ struct clk *vencl_top;
+ struct clk *vencl_int;
+ struct clk *dsi_host;
+ struct clk *dsi_phy;
+ struct clk *dsi_meas;
+
+ struct device *dev;
+ struct lcd_config_s *lcd_config;
+ struct vinfo_s *lcd_info;
+ struct class *lcd_debug_class;
+ int fr_auto_policy;
+ struct lcd_duration_s std_duration;
+
+ void (*vout_server_init)(void);
+ void (*driver_init_pre)(void);
+ int (*driver_init)(void);
+ void (*driver_disable)(void);
+ int (*driver_change)(void);
+ void (*module_reset)(void);
+ void (*power_tiny_ctrl)(int status);
+ void (*driver_tiny_enable)(void);
+ void (*driver_tiny_disable)(void);
+ void (*module_tiny_reset)(void);
+ void (*lcd_test_pattern_restore)(void);
+ void (*power_ctrl)(int status);
+
+ struct workqueue_struct *workqueue;
+ struct delayed_work lcd_probe_delayed_work;
+ struct delayed_work lcd_vx1_delayed_work;
+ struct work_struct lcd_resume_work;
+};
+
+extern struct aml_lcd_drv_s *aml_lcd_get_driver(void);
+
+/* IOCTL define */
+#define LCD_IOC_TYPE 'C'
+#define LCD_IOC_NR_GET_HDR_INFO 0x0
+#define LCD_IOC_NR_SET_HDR_INFO 0x1
+
+#define LCD_IOC_CMD_GET_HDR_INFO \
+ _IOR(LCD_IOC_TYPE, LCD_IOC_NR_GET_HDR_INFO, struct lcd_hdr_info_s)
+#define LCD_IOC_CMD_SET_HDR_INFO \
+ _IOW(LCD_IOC_TYPE, LCD_IOC_NR_SET_HDR_INFO, struct lcd_hdr_info_s)
+
+#endif
#define _VINFO_H_
#include <linux/amlogic/media/vfm/video_common.h>
+
/* the MSB is represent vmode set by vmode_init */
#define VMODE_INIT_BIT_MASK 0x8000
#define VMODE_MODE_BIT_MASK 0xff
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,