blob: 647114e455937f3a5200540d09258beb488cd8b7 [file] [log] [blame]
/*
* drivers/display/lcd/aml_lcd.c
*
* 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 named License,
* or 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 <common.h>
#include <malloc.h>
#include <asm/cpu_id.h>
#include <asm/arch/gpio.h>
#include <vpp.h>
#ifdef CONFIG_OF_LIBFDT
#include <libfdt.h>
#endif
#include <amlogic/keyunify.h>
#include <amlogic/aml_lcd.h>
#ifdef CONFIG_AML_LCD_EXTERN
#include <amlogic/aml_lcd_extern.h>
#endif
#include "aml_lcd_reg.h"
#include "aml_lcd_common.h"
#include "aml_lcd_clk_config.h"
#ifdef CONFIG_AML_LCD_TABLET
#include "lcd_tablet/mipi_dsi_util.h"
#endif
#define PANEL_NAME "panel"
unsigned int lcd_debug_print_flag;
unsigned int lcd_debug_test;
static struct aml_lcd_drv_s aml_lcd_driver;
static struct lcd_boot_ctrl_s boot_ctrl;
static struct lcd_debug_ctrl_s debug_ctrl;
static void lcd_chip_detect(void)
{
#if 1
unsigned int cpu_type;
unsigned int rev_type;
cpu_type = get_cpu_id().family_id;
rev_type = get_cpu_id().chip_rev;
switch (cpu_type) {
case MESON_CPU_MAJOR_ID_GXTVBB:
aml_lcd_driver.chip_type = LCD_CHIP_GXTVBB;
break;
case MESON_CPU_MAJOR_ID_GXL:
aml_lcd_driver.chip_type = LCD_CHIP_GXL;
break;
case MESON_CPU_MAJOR_ID_GXM:
aml_lcd_driver.chip_type = LCD_CHIP_GXM;
break;
case MESON_CPU_MAJOR_ID_TXL:
aml_lcd_driver.chip_type = LCD_CHIP_TXL;
break;
case MESON_CPU_MAJOR_ID_TXLX:
aml_lcd_driver.chip_type = LCD_CHIP_TXLX;
break;
case MESON_CPU_MAJOR_ID_AXG:
aml_lcd_driver.chip_type = LCD_CHIP_AXG;
break;
case MESON_CPU_MAJOR_ID_TXHD:
aml_lcd_driver.chip_type = LCD_CHIP_TXHD;
break;
case MESON_CPU_MAJOR_ID_G12A:
aml_lcd_driver.chip_type = LCD_CHIP_G12A;
break;
case MESON_CPU_MAJOR_ID_G12B:
aml_lcd_driver.chip_type = LCD_CHIP_G12B;
break;
case MESON_CPU_MAJOR_ID_TL1:
aml_lcd_driver.chip_type = LCD_CHIP_TL1;
aml_lcd_driver.rev_type = rev_type;
break;
case MESON_CPU_MAJOR_ID_SM1:
aml_lcd_driver.chip_type = LCD_CHIP_SM1;
break;
case MESON_CPU_MAJOR_ID_TM2:
aml_lcd_driver.chip_type = LCD_CHIP_TM2;
aml_lcd_driver.rev_type = rev_type;
break;
case MESON_CPU_MAJOR_ID_T5:
aml_lcd_driver.chip_type = LCD_CHIP_T5;
aml_lcd_driver.rev_type = rev_type;
break;
case MESON_CPU_MAJOR_ID_T5D:
aml_lcd_driver.chip_type = LCD_CHIP_T5D;
aml_lcd_driver.rev_type = rev_type;
break;
case MESON_CPU_MAJOR_ID_T5W:
aml_lcd_driver.chip_type = LCD_CHIP_T5W;
aml_lcd_driver.rev_type = rev_type;
break;
default:
aml_lcd_driver.chip_type = LCD_CHIP_T5W;
//aml_lcd_driver.chip_type = LCD_CHIP_MAX;
break;
}
#else
aml_lcd_driver.chip_type = LCD_CHIP_TM2;
#endif
lcd_pinmux_probe(aml_lcd_driver.chip_type);
if (lcd_debug_print_flag)
LCDPR("check chip: %d\n", aml_lcd_driver.chip_type);
}
static int lcd_check_valid(void)
{
if (aml_lcd_driver.config_check == NULL) {
LCDERR("invalid lcd config\n");
return -1;
}
return 0;
}
static void lcd_power_ctrl(int status)
{
struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
struct lcd_power_ctrl_s *lcd_power;
struct lcd_power_step_s *power_step;
char *str;
unsigned int i, wait, gpio;
int value = LCD_PMU_GPIO_NUM_MAX;
#ifdef CONFIG_AML_LCD_EXTERN
struct aml_lcd_extern_driver_s *ext_drv;
#endif
i = 0;
lcd_power = lcd_drv->lcd_config->lcd_power;
if (status) {
/* check if factory test */
if (lcd_drv->factory_lcd_power_on_step) {
LCDPR("%s: factory test power_on_step!\n", __func__);
power_step = lcd_drv->factory_lcd_power_on_step;
} else {
power_step = &lcd_power->power_on_step[0];
}
} else {
power_step = &lcd_power->power_off_step[0];
}
while (i < LCD_PWR_STEP_MAX) {
if (power_step->type >= LCD_POWER_TYPE_MAX)
break;
if (lcd_debug_print_flag) {
LCDPR("power_ctrl: %d, step %d: type=%d, index=%d, value=%d, delay=%d\n",
status, i, power_step->type, power_step->index,
power_step->value, power_step->delay);
}
switch (power_step->type) {
case LCD_POWER_TYPE_CPU:
if (power_step->index < LCD_CPU_GPIO_NUM_MAX) {
str = lcd_power->cpu_gpio[power_step->index];
gpio = aml_lcd_gpio_name_map_num(str);
aml_lcd_gpio_set(gpio, power_step->value);
} else {
LCDERR("cpu_gpio index: %d\n", power_step->index);
}
break;
case LCD_POWER_TYPE_PMU:
if (power_step->index < LCD_PMU_GPIO_NUM_MAX)
LCDPR("to do\n");
else
LCDERR("pmu_gpio index: %d\n", power_step->index);
break;
case LCD_POWER_TYPE_SIGNAL:
if (status)
lcd_drv->driver_init();
else
lcd_drv->driver_disable();
break;
#ifdef CONFIG_AML_LCD_EXTERN
case LCD_POWER_TYPE_EXTERN:
ext_drv = aml_lcd_extern_get_driver(power_step->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
case LCD_POWER_TYPE_WAIT_GPIO:
if (power_step->index < LCD_CPU_GPIO_NUM_MAX) {
str = lcd_power->cpu_gpio[power_step->index];
gpio = aml_lcd_gpio_name_map_num(str);
aml_lcd_gpio_set(gpio, LCD_GPIO_INPUT);
} else {
LCDERR(
"wait_gpio index: %d\n", power_step->index);
break;
}
LCDPR("lcd_power_type_wait_gpio wait\n");
for (wait = 0; wait < power_step->delay; wait++) {
value = aml_lcd_gpio_input_get(gpio);
if (value == power_step->value) {
LCDPR(
"get value: %d, wait ok\n", value);
break;
}
mdelay(1);
}
if (wait == power_step->delay)
LCDERR(
"get value: %d, wait timeout!\n", value);
break;
case LCD_POWER_TYPE_CLK_SS:
break;
#ifdef CONFIG_AML_LCD_TCON
case LCD_POWER_TYPE_TCON_SPI_DATA_LOAD:
if (lcd_drv->lcd_tcon_spi_data_load)
lcd_drv->lcd_tcon_spi_data_load();
break;
#endif
default:
break;
}
if (power_step->type != LCD_POWER_TYPE_WAIT_GPIO) {
if (power_step->delay > 0)
mdelay(power_step->delay);
}
i++;
power_step++;
}
if (lcd_debug_print_flag)
LCDPR("%s: %d\n", __func__, status);
}
static void lcd_gamma_init(void)
{
lcd_wait_vsync();
vpp_disable_lcd_gamma_table();
vpp_init_lcd_gamma_table();
lcd_wait_vsync();
vpp_enable_lcd_gamma_table();
}
static void lcd_encl_on(void)
{
struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
lcd_drv->driver_init_pre();
if (lcd_debug_test)
aml_lcd_debug_test(lcd_debug_test);
lcd_gamma_init();
lcd_vcbus_write(VENC_INTCTRL, 0x200);
lcd_drv->lcd_status |= LCD_STATUS_ENCL_ON;
}
static void lcd_interface_on(void)
{
struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
struct lcd_config_s *pconf = lcd_drv->lcd_config;
lcd_power_ctrl(1);
pconf->retry_enable_cnt = 0;
while (pconf->retry_enable_flag) {
if (pconf->retry_enable_cnt++ >= LCD_ENABLE_RETRY_MAX)
break;
LCDPR("retry enable...%d\n", pconf->retry_enable_cnt);
lcd_power_ctrl(0);
mdelay(1000);
lcd_power_ctrl(1);
}
pconf->retry_enable_cnt = 0;
lcd_drv->lcd_status |= LCD_STATUS_IF_ON;
}
static void lcd_backlight_enable(void)
{
struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
aml_bl_pwm_config_update(lcd_drv->bl_config);
aml_bl_set_level(lcd_drv->bl_config->level_default);
aml_bl_power_ctrl(1, 1);
}
static void lcd_module_enable(char *mode, unsigned int frac)
{
unsigned int sync_duration;
struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
struct lcd_config_s *pconf = lcd_drv->lcd_config;
int ret;
ret = lcd_drv->config_check(mode, frac);
if (ret) {
LCDERR("init exit\n");
return;
}
sync_duration = pconf->lcd_timing.sync_duration_num;
sync_duration = (sync_duration * 100 /
pconf->lcd_timing.sync_duration_den);
LCDPR("enable: %s, %s, %ux%u@%u.%2uHz\n", pconf->lcd_basic.model_name,
lcd_type_type_to_str(pconf->lcd_basic.lcd_type),
pconf->lcd_basic.h_active, pconf->lcd_basic.v_active,
(sync_duration / 100), (sync_duration % 100));
if ((lcd_drv->lcd_status & LCD_STATUS_ENCL_ON) == 0)
lcd_encl_on();
if ((lcd_drv->lcd_status & LCD_STATUS_IF_ON) == 0) {
if (boot_ctrl.init_level == LCD_INIT_LEVEL_NORMAL) {
lcd_interface_on();
lcd_backlight_enable();
} else {
LCDPR("bypass interface for init_level %d\n",
boot_ctrl.init_level);
}
}
if (!lcd_debug_test)
aml_lcd_mute_setting(0);
}
static void lcd_module_disable(void)
{
struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
LCDPR("disable: %s\n", lcd_drv->lcd_config->lcd_basic.model_name);
aml_lcd_mute_setting(1);
if (lcd_drv->lcd_status & LCD_STATUS_IF_ON) {
aml_bl_power_ctrl(0, 1);
lcd_power_ctrl(0);
}
lcd_vcbus_write(ENCL_VIDEO_EN, 0);
lcd_clk_disable();
lcd_drv->lcd_status = 0;
}
static void lcd_module_prepare(char *mode, unsigned int frac)
{
struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
int ret;
ret = lcd_drv->config_check(mode, frac);
if (ret) {
LCDERR("prepare exit\n");
return;
}
if ((lcd_drv->lcd_status & LCD_STATUS_ENCL_ON) == 0)
lcd_encl_on();
}
static void lcd_vbyone_filter_flag_print(struct lcd_config_s *pconf)
{
struct vbyone_config_s *vx1_conf = pconf->lcd_control.vbyone_config;
switch (aml_lcd_driver.chip_type) {
case LCD_CHIP_TXL:
case LCD_CHIP_TXLX:
LCDPR("vx1_sw_filter_en: %d\n", vx1_conf->vx1_sw_filter_en);
LCDPR("vx1_sw_filter_time: %d\n", vx1_conf->vx1_sw_filter_time);
LCDPR("vx1_sw_filter_cnt: %d\n", vx1_conf->vx1_sw_filter_cnt);
LCDPR("vx1_sw_filter_retry_cnt: %d\n", vx1_conf->vx1_sw_filter_retry_cnt);
LCDPR("vx1_sw_filter_retry_delay: %d\n", vx1_conf->vx1_sw_filter_retry_delay);
LCDPR("vx1_sw_cdr_detect_time: %d\n", vx1_conf->vx1_sw_cdr_detect_time);
LCDPR("vx1_sw_cdr_detect_cnt: %d\n", vx1_conf->vx1_sw_cdr_detect_cnt);
LCDPR("vx1_sw_cdr_timeout_cnt: %d\n", vx1_conf->vx1_sw_cdr_timeout_cnt);
break;
default:
break;
}
}
static void lcd_vbyone_filter_env_init(struct lcd_config_s *pconf)
{
struct vbyone_config_s *vx1_conf = pconf->lcd_control.vbyone_config;
unsigned int temp = 0;
temp = getenv_ulong("lcd_debug_vx1_sw_filter", 10, 0);
if (!temp)
return;
LCDPR("%s\n", __func__);
temp = getenv_ulong("vx1_sw_filter_en", 10, 0xffff);
if (temp != 0xffff) {
vx1_conf->vx1_sw_filter_en = temp;
LCDPR("vx1_sw_filter_en: %d\n", vx1_conf->vx1_sw_filter_en);
}
/* 100us */
temp = getenv_ulong("vx1_sw_filter_time", 10, 0xffff);
if (temp != 0xffff) {
vx1_conf->vx1_sw_filter_time = temp;
LCDPR("vx1_sw_filter_time: %d\n", vx1_conf->vx1_sw_filter_time);
}
temp = getenv_ulong("vx1_sw_filter_cnt", 10, 0xffff);
if (temp != 0xffff) {
vx1_conf->vx1_sw_filter_cnt = temp;
LCDPR("vx1_sw_filter_cnt: %d\n", vx1_conf->vx1_sw_filter_cnt);
}
temp = getenv_ulong("vx1_sw_filter_retry_cnt", 10, 0xffff);
if (temp != 0xffff) {
vx1_conf->vx1_sw_filter_cnt = temp;
LCDPR("vx1_sw_filter_retry_cnt: %d\n",
vx1_conf->vx1_sw_filter_retry_cnt);
}
/* ms */
temp = getenv_ulong("vx1_sw_filter_retry_delay", 10, 0xffff);
if (temp != 0xffff) {
vx1_conf->vx1_sw_filter_retry_delay = temp;
LCDPR("vx1_sw_filter_retry_delay: %d\n",
vx1_conf->vx1_sw_filter_retry_delay);
}
/* us * 100 */
temp = getenv_ulong("vx1_sw_cdr_detect_time", 10, 0xffff);
if (temp != 0xffff) {
vx1_conf->vx1_sw_filter_cnt = temp;
LCDPR("vx1_sw_cdr_detect_time: %d\n",
vx1_conf->vx1_sw_cdr_detect_time);
}
temp = getenv_ulong("vx1_sw_cdr_detect_cnt", 10, 0xffff);
if (temp != 0xffff) {
vx1_conf->vx1_sw_cdr_detect_cnt = temp;
LCDPR("vx1_sw_cdr_detect_cnt: %d\n",
vx1_conf->vx1_sw_cdr_detect_cnt);
}
temp = getenv_ulong("vx1_sw_cdr_timeout_cnt", 10, 0xffff);
if (temp != 0xffff) {
vx1_conf->vx1_sw_cdr_timeout_cnt = temp;
LCDPR("vx1_sw_cdr_timeout_cnt: %d\n",
vx1_conf->vx1_sw_cdr_timeout_cnt);
}
}
#ifdef CONFIG_AML_LCD_EXTERN
static int lcd_extern_load_config(char *dt_addr, struct lcd_config_s *pconf)
{
struct lcd_power_step_s *power_step;
int index, i = 0;
aml_lcd_extern_init();
/* mipi extern_init is special */
if (pconf->lcd_basic.lcd_type == LCD_MIPI) {
index = pconf->lcd_control.mipi_config->extern_init;
if (index < LCD_EXTERN_INDEX_INVALID)
aml_lcd_extern_probe(dt_addr, index);
}
while (i < LCD_PWR_STEP_MAX) {
power_step = &pconf->lcd_power->power_on_step[i];
if (power_step->type >= LCD_POWER_TYPE_MAX)
break;
if (power_step->type == LCD_POWER_TYPE_EXTERN) {
if (lcd_debug_print_flag) {
LCDPR("power_on: step %d: type=%d, index=%d\n",
i, power_step->type, power_step->index);
}
index = power_step->index;
if (index < LCD_EXTERN_INDEX_INVALID)
aml_lcd_extern_probe(dt_addr, index);
}
i++;
}
return 0;
}
#endif
#ifdef CONFIG_OF_LIBFDT
static int lcd_init_load_from_dts(char *dt_addr)
{
struct lcd_config_s *pconf = aml_lcd_driver.lcd_config;
int parent_offset;
char *propdata, *p;
const char *str;
int i, j, temp;
if (!dt_addr) {
LCDERR("%s: dt_addr is null\n", __func__);
return -1;
}
/* check bl_key_valid */
parent_offset = fdt_path_offset(dt_addr, "/backlight");
if (parent_offset < 0) {
LCDERR("not find /backlight node: %s\n", fdt_strerror(parent_offset));
aml_lcd_driver.bl_config->bl_key_valid = 0;
}
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "key_valid", NULL);
if (propdata == NULL) {
LCDERR("failed to get key_valid\n");
aml_lcd_driver.bl_config->bl_key_valid = 0;
} else {
aml_lcd_driver.bl_config->bl_key_valid = (unsigned char)(be32_to_cpup((u32*)propdata));
}
parent_offset = fdt_path_offset(dt_addr, "/lcd");
if (parent_offset < 0) {
LCDERR("not find /lcd node: %s\n", fdt_strerror(parent_offset));
return -1;
}
/* check lcd_mode & lcd_key_valid */
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "mode", NULL);
if (propdata == NULL) {
LCDERR("failed to get mode\n");
return -1;
} else {
pconf->lcd_mode = lcd_mode_str_to_mode(propdata);
}
str = propdata;
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "key_valid", NULL);
if (propdata == NULL) {
LCDERR("failed to get key_valid\n");
pconf->lcd_key_valid = 0;
} else {
pconf->lcd_key_valid = (unsigned char)(be32_to_cpup((u32*)propdata));
}
LCDPR("detect mode: %s, key_valid: %d\n", str, pconf->lcd_key_valid);
/* check lcd_clk_path */
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "clk_path", NULL);
if (propdata == NULL) {
if (lcd_debug_print_flag)
LCDPR("failed to get clk_path\n");
pconf->lcd_clk_path = 0;
} else {
pconf->lcd_clk_path = (unsigned char)(be32_to_cpup((u32*)propdata));
LCDPR("detect lcd_clk_path: %d\n", pconf->lcd_clk_path);
}
temp = getenv_ulong("lcd_clk_path", 10, 0xffff);
if (temp != 0xffff) {
if (temp)
pconf->lcd_clk_path = 1;
else
pconf->lcd_clk_path = 0;
LCDPR("lcd_clk_path flag set clk_path: %d\n",
pconf->lcd_clk_path);
}
i = 0;
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "lcd_cpu_gpio_names", NULL);
if (propdata == NULL) {
LCDPR("failed to get lcd_cpu_gpio_names\n");
} else {
p = propdata;
while (i < LCD_CPU_GPIO_NUM_MAX) {
str = p;
if (strlen(str) == 0)
break;
strcpy(pconf->lcd_power->cpu_gpio[i], str);
if (lcd_debug_print_flag) {
LCDPR("i=%d, gpio=%s\n",
i, pconf->lcd_power->cpu_gpio[i]);
}
p += strlen(p) + 1;
i++;
}
}
for (j = i; j < LCD_CPU_GPIO_NUM_MAX; j++)
strcpy(pconf->lcd_power->cpu_gpio[j], "invalid");
return 0;
}
#endif
static int lcd_init_load_from_bsp(void)
{
struct lcd_config_s *pconf = aml_lcd_driver.lcd_config;
int i, j, temp;
/*pconf->lcd_key_valid = 0;
aml_lcd_driver.bl_config->bl_key_valid = 0;*/
LCDPR("detect mode: %s, key_valid: %d\n",
lcd_mode_mode_to_str(pconf->lcd_mode), pconf->lcd_key_valid);
i = 0;
while (i < LCD_CPU_GPIO_NUM_MAX) {
if (strcmp(pconf->lcd_power->cpu_gpio[i], "invalid") == 0)
break;
i++;
}
for (j = i; j < LCD_CPU_GPIO_NUM_MAX; j++) {
strcpy(pconf->lcd_power->cpu_gpio[j], "invalid");
}
temp = getenv_ulong("lcd_clk_path", 10, 0);
if (temp)
pconf->lcd_clk_path = 1;
else
pconf->lcd_clk_path = 0;
LCDPR("lcd_clk_path flag set clk_path: %d\n", pconf->lcd_clk_path);
return 0;
}
static int lcd_config_load_id_check(char *dt_addr)
{
int load_id = 0, ret;
#ifdef CONFIG_OF_LIBFDT
int parent_offset;
if (dt_addr) {
if (fdt_check_header(dt_addr) < 0) {
LCDERR("check dts: %s, load default lcd parameters\n",
fdt_strerror(fdt_check_header(dt_addr)));
} else {
parent_offset = fdt_path_offset(dt_addr, "/lcd");
if (parent_offset < 0) {
LCDERR("not find /lcd node: %s\n",
fdt_strerror(parent_offset));
load_id = 0x0;
} else {
load_id = 0x1;
}
}
} else {
load_id = 0x0;
}
#endif
switch (debug_ctrl.debug_para_source) {
case 1:
LCDPR("lcd_debug_para: 1,dts\n");
load_id = 0x1;
break;
case 2:
LCDPR("lcd_debug_para: 2,unifykey\n");
break;
case 3:
LCDPR("lcd_debug_para: 3,bsp\n");
load_id = 0x0;
break;
default:
break;
}
if (load_id & 0x1) {
#ifdef CONFIG_OF_LIBFDT
ret = lcd_init_load_from_dts(dt_addr);
if (ret)
return -1;
#endif
} else {
ret = lcd_init_load_from_bsp();
if (ret)
return -1;
}
if (debug_ctrl.debug_para_source == 1) {
aml_lcd_driver.bl_config->bl_key_valid = 0;
aml_lcd_driver.lcd_config->lcd_key_valid = 0;
} else if (debug_ctrl.debug_para_source == 2) {
aml_lcd_driver.bl_config->bl_key_valid = 1;
aml_lcd_driver.lcd_config->lcd_key_valid = 1;
}
if (aml_lcd_driver.unifykey_test_flag) {
aml_lcd_driver.bl_config->bl_key_valid = 1;
aml_lcd_driver.lcd_config->lcd_key_valid = 1;
LCDPR("force bl_key_valid & lcd_key_valid to 1\n");
}
if (aml_lcd_driver.lcd_config->lcd_key_valid) {
ret = aml_lcd_unifykey_check("lcd");
if (ret == 0) {
LCDPR("load lcd_config from unifykey\n");
load_id |= 0x10;
return load_id;
}
}
if (load_id & 0x1)
LCDPR("load config from dts\n");
else
LCDPR("load config from bsp\n");
return load_id;
}
static int lcd_mode_probe(char *dt_addr, int load_id)
{
int ret = 0;
switch (debug_ctrl.debug_lcd_mode) {
case 1:
LCDPR("lcd_debug_mode: 1,tv\n");
aml_lcd_driver.lcd_config->lcd_mode = LCD_MODE_TV;
break;
case 2:
LCDPR("lcd_debug_mode: 2,tablet\n");
aml_lcd_driver.lcd_config->lcd_mode = LCD_MODE_TABLET;
break;
default:
break;
}
/* load lcd config */
switch (aml_lcd_driver.lcd_config->lcd_mode) {
case LCD_MODE_TV:
#ifdef CONFIG_AML_LCD_TV
ret = get_lcd_tv_config(dt_addr, load_id);
#endif
break;
case LCD_MODE_TABLET:
#ifdef CONFIG_AML_LCD_TABLET
ret = get_lcd_tablet_config(dt_addr, load_id);
#endif
break;
default:
ret = -1;
LCDERR("invalid lcd mode: %d\n", aml_lcd_driver.lcd_config->lcd_mode);
break;
}
if (ret) {
aml_lcd_driver.config_check = NULL;
LCDERR("invalid lcd config\n");
return -1;
}
if (aml_lcd_driver.lcd_config->lcd_basic.lcd_type == LCD_VBYONE)
lcd_vbyone_filter_env_init(aml_lcd_driver.lcd_config);
#ifdef CONFIG_AML_LCD_TCON
lcd_tcon_probe(dt_addr, &aml_lcd_driver, load_id);
#endif
#ifdef CONFIG_AML_LCD_EXTERN
lcd_extern_load_config(dt_addr, aml_lcd_driver.lcd_config);
#endif
/* load bl config */
if (aml_lcd_driver.bl_config->bl_key_valid) {
ret = aml_lcd_unifykey_check("backlight");
if (ret == 0) {
LCDPR("load backlight_config from unifykey\n");
load_id |= 0x10;
} else {
load_id &= ~(0x10);
}
} else {
load_id &= ~(0x10);
}
aml_bl_config_load(dt_addr, load_id);
if (lcd_debug_print_flag) {
if (aml_lcd_driver.lcd_config->lcd_basic.lcd_type == LCD_VBYONE)
lcd_vbyone_filter_flag_print(aml_lcd_driver.lcd_config);
}
return 0;
}
static int lcd_config_probe(void)
{
int load_id = 0;
char *dt_addr;
unsigned long dtb_mem;
dtb_mem = getenv_ulong("dtb_mem_addr", 16, 0);
if (dtb_mem) {
dt_addr = (char *)dtb_mem;
} else {
LCDPR("no dtb_mem_addr\n");
#ifdef CONFIG_DTB_MEM_ADDR
dt_addr = (char *)CONFIG_DTB_MEM_ADDR;
#endif
}
load_id = lcd_config_load_id_check(dt_addr);
/* default setting */
aml_lcd_driver.lcd_config->retry_enable_flag = 0;
aml_lcd_driver.lcd_config->retry_enable_cnt = 0;
lcd_phy_probe();
lcd_clk_config_probe();
lcd_mode_probe(dt_addr, load_id);
aml_lcd_debug_probe(&aml_lcd_driver);
return 0;
}
static void lcd_update_boot_ctrl_bootargs(void)
{
struct lcd_config_s *pconf = aml_lcd_driver.lcd_config;
unsigned int value = 0;
char ctrl_str[20];
boot_ctrl.lcd_type = pconf->lcd_basic.lcd_type;
boot_ctrl.lcd_bits = pconf->lcd_basic.lcd_bits;
switch (pconf->lcd_basic.lcd_type) {
case LCD_TTL:
boot_ctrl.advanced_flag = pconf->lcd_control.ttl_config->sync_valid;
break;
case LCD_P2P:
boot_ctrl.advanced_flag = pconf->lcd_control.p2p_config->p2p_type;
break;
default:
break;
}
boot_ctrl.custom_pinmux = pconf->customer_pinmux ? 1 : 0;
/*create new env "lcd_ctrl", define as below:
*bit[3:0]: lcd_type
*bit[7:4]: lcd bits
*bit[15:8]: advanced flag(p2p_type when lcd_type=p2p)
*bit[17]: reserved
*bit[16]: custom pinmux flag
*bit[19:18]: lcd_init_level
*high 12bit for debug flag
*bit[23:20]: lcd debug print flag
*bit[27:24]: lcd test pattern
*bit[29:28]: lcd debug para source(0=normal, 1=dts, 2=unifykey,
*3=bsp for uboot)
*bit[31:30]: lcd mode(0=normal, 1=tv; 2=tablet, 3=TBD)
*/
value |= (boot_ctrl.lcd_type & 0xf);
value |= (boot_ctrl.lcd_bits & 0xf) << 4;
value |= (boot_ctrl.advanced_flag & 0xff) << 8;
value |= (boot_ctrl.custom_pinmux & 0x1) << 16;
value |= (boot_ctrl.init_level & 0x3) << 18;
sprintf(ctrl_str, "0x%08x", value);
setenv("lcd_ctrl", ctrl_str);
value |= (lcd_debug_print_flag & 0xf) << 20;
value |= (lcd_debug_test & 0xf) << 24;
value |= (debug_ctrl.debug_para_source & 0x3) << 28;
value |= (debug_ctrl.debug_lcd_mode & 0x3) << 30;
if (strlen(pconf->lcd_basic.model_name) > 0)
setenv("panel_name", pconf->lcd_basic.model_name);
//================================
//debug bootargs
//================================
value = 0;
memset(ctrl_str, 0, 20);
debug_ctrl.debug_print_flag = lcd_debug_print_flag;
debug_ctrl.debug_test_pattern = lcd_debug_test;
/*
*bit[31:30]: lcd mode(0=normal, 1=tv; 2=tablet, 3=TBD)
*bit[29:28]: lcd debug para source(0=normal, 1=dts, 2=unifykey,
* 3=bsp for uboot)
*bit[27:16]: reserved
*bit[15:8]: lcd test pattern
*bit[7:0]: lcd debug print flag
*/
value |= (debug_ctrl.debug_print_flag & 0xff);
value |= (debug_ctrl.debug_test_pattern & 0xff) << 8;
value |= (debug_ctrl.debug_para_source & 0x3) << 28;
value |= (debug_ctrl.debug_lcd_mode & 0x3) << 30;
sprintf(ctrl_str, "0x%08x", value);
setenv("lcd_debug", ctrl_str);
}
static struct phy_config_s lcd_phy_cfg = {
.vswing = 0,
.vcm = 0,
.ref_bias = 0,
.odt = 0,
.mode = 0,
.flag = 0,
};
int lcd_probe(void)
{
int ret = 0;
lcd_debug_print_flag = getenv_ulong("lcd_debug_print", 10, 0);
LCDPR("lcd_debug_print flag: %d\n", lcd_debug_print_flag);
lcd_debug_test = getenv_ulong("lcd_debug_test", 10, 0);
debug_ctrl.debug_para_source = getenv_ulong("lcd_debug_para", 10, 0);
debug_ctrl.debug_lcd_mode = getenv_ulong("lcd_debug_mode", 10, 0);
boot_ctrl.init_level = getenv_ulong("lcd_init_level", 10, 0);
lcd_chip_detect();
lcd_config_bsp_init();
aml_lcd_driver.lcd_config->lcd_control.phy_cfg = &lcd_phy_cfg;
ret = lcd_config_probe();
if (ret)
return 0;
lcd_update_boot_ctrl_bootargs();
aml_bl_power_ctrl(0, 0); /* init backlight ctrl port */
mdelay(10);
return 0;
}
int lcd_remove(void)
{
#ifdef CONFIG_AML_LCD_EXTERN
aml_lcd_extern_remove();
#endif
return 0;
}
#define LCD_WAIT_VSYNC_TIMEOUT 50000
void lcd_wait_vsync(void)
{
struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
int reg, line_cnt, line_cnt_previous;
int i = 0;
if (lcd_drv->chip_type == LCD_CHIP_T5W)
reg = VPU_VENCP_STAT;
else
reg = ENCL_INFO_READ;
line_cnt = 0x1fff;
line_cnt_previous = lcd_vcbus_getb(reg, 16, 13);
while (i++ < LCD_WAIT_VSYNC_TIMEOUT) {
line_cnt = lcd_vcbus_getb(reg, 16, 13);
if (line_cnt < line_cnt_previous)
break;
line_cnt_previous = line_cnt;
udelay(2);
}
/*LCDPR("line_cnt=%d, line_cnt_previous=%d, i=%d\n",
* line_cnt, line_cnt_previous, i);
*/
}
/* ********************************************** *
lcd driver API
* ********************************************** */
static int lcd_outputmode_check(char *mode, unsigned int frac)
{
if (aml_lcd_driver.outputmode_check)
return aml_lcd_driver.outputmode_check(mode, frac);
LCDERR("invalid lcd config\n");
return -1;
}
static void lcd_prepare(char *mode, unsigned int frac)
{
if (lcd_check_valid())
return;
if (aml_lcd_driver.lcd_status & LCD_STATUS_ENCL_ON)
LCDPR("already enabled\n");
else
lcd_module_prepare(mode, frac);
}
static void lcd_enable(char *mode, unsigned int frac)
{
if (lcd_check_valid())
return;
if (aml_lcd_driver.lcd_status & LCD_STATUS_IF_ON)
LCDPR("already enabled\n");
else
lcd_module_enable(mode, frac);
}
static void lcd_disable(void)
{
if (lcd_check_valid())
return;
if (aml_lcd_driver.lcd_status & LCD_STATUS_ENCL_ON)
lcd_module_disable();
else
LCDPR("already disabled\n");
}
static void aml_lcd_set_ss(unsigned int level, unsigned int freq, unsigned int mode)
{
struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
unsigned int temp;
int ret;
if (lcd_check_valid())
return;
if (aml_lcd_driver.lcd_status) {
temp = lcd_drv->lcd_config->lcd_timing.ss_level;
ret = lcd_set_ss(level, freq, mode);
if (ret == 0) {
if (level < 0xff) {
temp &= ~(0xff);
temp |= level;
lcd_drv->lcd_config->lcd_timing.ss_level = temp;
}
if (freq < 0xff) {
temp &= ~((0xf << LCD_CLK_SS_BIT_FREQ) << 8);
temp |= ((freq << LCD_CLK_SS_BIT_FREQ) << 8);
lcd_drv->lcd_config->lcd_timing.ss_level = temp;
}
if (mode < 0xff) {
temp &= ~((0xf << LCD_CLK_SS_BIT_MODE) << 8);
temp |= ((mode << LCD_CLK_SS_BIT_MODE) << 8);
lcd_drv->lcd_config->lcd_timing.ss_level = temp;
}
}
} else {
LCDPR("already disabled\n");
}
}
static void aml_lcd_get_ss(void)
{
if (lcd_check_valid())
return;
if (aml_lcd_driver.lcd_status)
lcd_get_ss();
else
LCDPR("already disabled\n");
}
static void aml_lcd_test(int num)
{
if (lcd_check_valid())
return;
if (aml_lcd_driver.lcd_status)
aml_lcd_debug_test(num);
else
LCDPR("already disabled\n");
}
static void aml_lcd_clk(void)
{
if (lcd_check_valid())
return;
lcd_clk_config_print();
}
static void aml_lcd_info(void)
{
if (lcd_check_valid())
return;
aml_lcd_info_print();
}
static void aml_lcd_reg(void)
{
if (lcd_check_valid())
return;
aml_lcd_reg_print();
}
static void lcd_vbyone_rst(void)
{
if (lcd_check_valid())
return;
aml_lcd_vbyone_rst();
}
int lcd_vbyone_cdr(void)
{
if (lcd_check_valid())
return -1;
return aml_lcd_vbyone_cdr();
}
int lcd_vbyone_lock(void)
{
if (lcd_check_valid())
return -1;
return aml_lcd_vbyone_lock();
}
static void aml_set_backlight_level(int level)
{
aml_bl_set_level(level);
}
static int aml_get_backlight_level(void)
{
return aml_bl_get_level();
}
static void aml_backlight_power_on(void)
{
aml_bl_power_ctrl(1, 1);
}
static void aml_backlight_power_off(void)
{
aml_bl_power_ctrl(0, 1);
}
static void aml_lcd_key_test(void)
{
if (aml_lcd_driver.unifykey_test_flag) {
aml_lcd_unifykey_test();
lcd_config_probe();
} else {
printf("lcd unifykey test disabled\n");
}
}
static void aml_lcd_key_dump(unsigned int flag)
{
unsigned int key_flag = LCD_UKEY_DEBUG_NORMAL;
if (flag & (1 << 0)) {
key_flag = LCD_UKEY_DEBUG_NORMAL;
} else if (flag & (1 << 1)) {
#ifdef CONFIG_AML_LCD_TCON
switch (aml_lcd_driver.chip_type) {
case LCD_CHIP_TXHD:
key_flag = (LCD_UKEY_DEBUG_TCON | LCD_UKEY_TCON_SIZE);
break;
case LCD_CHIP_TL1:
case LCD_CHIP_TM2:
key_flag = (LCD_UKEY_DEBUG_TCON | LCD_UKEY_TCON_SIZE_NEW);
break;
default:
break;
}
#endif
}
aml_lcd_unifykey_dump(key_flag);
}
static void aml_lcd_debug_print_set(unsigned int flag)
{
char str[5];
lcd_debug_print_flag = flag;
sprintf(str, "%d", flag);
setenv("lcd_debug_print", str);
LCDPR("set debug_print_flag: %d\n", flag);
}
static struct aml_lcd_drv_s aml_lcd_driver = {
.lcd_status = 0,
.lcd_config = &lcd_config_dft,
.bl_config = &bl_config_dft,
.config_check = NULL,
.lcd_probe = lcd_probe,
.lcd_outputmode_check = lcd_outputmode_check,
.lcd_prepare = lcd_prepare,
.lcd_enable = lcd_enable,
.lcd_disable = lcd_disable,
.lcd_set_ss = aml_lcd_set_ss,
.lcd_get_ss = aml_lcd_get_ss,
.lcd_test = aml_lcd_test,
.lcd_prbs = aml_lcd_prbs_test,
.lcd_clk = aml_lcd_clk,
.lcd_info = aml_lcd_info,
.lcd_reg = aml_lcd_reg,
#ifdef CONFIG_AML_LCD_TCON
.lcd_tcon_reg_print = NULL,
.lcd_tcon_table_print = NULL,
.lcd_tcon_vac_print = NULL,
.lcd_tcon_demura_print = NULL,
.lcd_tcon_acc_print = NULL,
.lcd_tcon_data_print = NULL,
.lcd_tcon_spi_print = NULL,
.lcd_tcon_spi_data_load = NULL,
.lcd_tcon_reg_read = NULL,
.lcd_tcon_reg_write = NULL,
#endif
.lcd_vbyone_rst = lcd_vbyone_rst,
.lcd_vbyone_cdr = lcd_vbyone_cdr,
.lcd_vbyone_lock = lcd_vbyone_lock,
.bl_on = aml_backlight_power_on,
.bl_off = aml_backlight_power_off,
.set_bl_level = aml_set_backlight_level,
.get_bl_level = aml_get_backlight_level,
.bl_config_print = aml_bl_config_print,
.unifykey_test_flag = 0, /* default disable unifykey test */
.unifykey_test = aml_lcd_key_test,
.unifykey_dump = aml_lcd_key_dump,
.debug_print_set = aml_lcd_debug_print_set,
/* for factory test */
.factory_lcd_power_on_step = NULL,
.factory_bl_power_on_delay = -1,
};
struct aml_lcd_drv_s *aml_lcd_get_driver(void)
{
return &aml_lcd_driver;
}