blob: 4566a3daa9f23c5d8a2cf24705cf69c86e385a84 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <common.h>
#include <malloc.h>
#include <dm.h>
#include <asm/gpio.h>
#include <amlogic/media/vout/lcd/aml_lcd.h>
#include "lcd_reg.h"
#include "lcd_common.h"
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},
{"minilvds", LCD_MLVDS},
{"p2p", LCD_P2P},
{"edp", LCD_EDP},
{"invalid", LCD_TYPE_MAX},
};
int lcd_type_str_to_type(const char *str)
{
int type = LCD_TYPE_MAX;
int i;
for (i = 0; i < ARRAY_SIZE(lcd_type_match_table); 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)
{
char *name = lcd_type_match_table[LCD_TYPE_MAX].name;
int i;
for (i = 0; i < ARRAY_SIZE(lcd_type_match_table); i++) {
if (type == lcd_type_match_table[i].type) {
name = lcd_type_match_table[i].name;
break;
}
}
return name;
}
static char *lcd_mode_table[] = {
"tv",
"tablet",
"invalid",
};
int lcd_mode_str_to_mode(const char *str)
{
int 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];
}
static void lcd_config_load_print(struct aml_lcd_drv_s *pdrv)
{
struct lcd_config_s *pconf = &pdrv->config;
union lcd_ctrl_config_u *pctrl;
LCDPR("[%d]: %s, %s, %dbit, %dx%d\n",
pdrv->index,
pconf->basic.model_name,
lcd_type_type_to_str(pconf->basic.lcd_type),
pconf->basic.lcd_bits,
pconf->basic.h_active, pconf->basic.v_active);
if ((lcd_debug_print_flag & LCD_DBG_PR_NORMAL) == 0)
return;
LCDPR("h_period = %d\n", pconf->basic.h_period);
LCDPR("v_period = %d\n", pconf->basic.v_period);
LCDPR("h_period_min = %d\n", pconf->basic.h_period_min);
LCDPR("h_period_max = %d\n", pconf->basic.h_period_max);
LCDPR("v_period_min = %d\n", pconf->basic.v_period_min);
LCDPR("v_period_max = %d\n", pconf->basic.v_period_max);
LCDPR("pclk_min = %d\n", pconf->basic.lcd_clk_min);
LCDPR("pclk_max = %d\n", pconf->basic.lcd_clk_max);
LCDPR("hsync_width = %d\n", pconf->timing.hsync_width);
LCDPR("hsync_bp = %d\n", pconf->timing.hsync_bp);
LCDPR("hsync_pol = %d\n", pconf->timing.hsync_pol);
LCDPR("vsync_width = %d\n", pconf->timing.vsync_width);
LCDPR("vsync_bp = %d\n", pconf->timing.vsync_bp);
LCDPR("vsync_pol = %d\n", pconf->timing.vsync_pol);
LCDPR("fr_adjust_type = %d\n", pconf->timing.fr_adjust_type);
LCDPR("ss_level = %d\n", pconf->timing.ss_level);
LCDPR("clk_auto = %d\n", pconf->timing.clk_auto);
LCDPR("pixel_clk = %d\n", pconf->timing.lcd_clk);
pctrl = &pconf->control;
if (pconf->basic.lcd_type == LCD_TTL) {
LCDPR("clk_pol = %d\n", pctrl->ttl_cfg.clk_pol);
LCDPR("sync_valid = %d\n", pctrl->ttl_cfg.sync_valid);
LCDPR("swap_ctrl = %d\n", pctrl->ttl_cfg.swap_ctrl);
} else if (pconf->basic.lcd_type == LCD_LVDS) {
LCDPR("lvds_repack = %d\n", pctrl->lvds_cfg.lvds_repack);
LCDPR("pn_swap = %d\n", pctrl->lvds_cfg.pn_swap);
LCDPR("dual_port = %d\n", pctrl->lvds_cfg.dual_port);
LCDPR("port_swap = %d\n", pctrl->lvds_cfg.port_swap);
LCDPR("lane_reverse = %d\n", pctrl->lvds_cfg.lane_reverse);
LCDPR("phy_vswing = 0x%x\n", pctrl->lvds_cfg.phy_vswing);
LCDPR("phy_preem = 0x%x\n", pctrl->lvds_cfg.phy_preem);
} else if (pconf->basic.lcd_type == LCD_VBYONE) {
LCDPR("lane_count = %d\n", pctrl->vbyone_cfg.lane_count);
LCDPR("byte_mode = %d\n", pctrl->vbyone_cfg.byte_mode);
LCDPR("region_num = %d\n", pctrl->vbyone_cfg.region_num);
LCDPR("color_fmt = %d\n", pctrl->vbyone_cfg.color_fmt);
LCDPR("phy_vswing = 0x%x\n", pctrl->vbyone_cfg.phy_vswing);
LCDPR("phy_preem = 0x%x\n", pctrl->vbyone_cfg.phy_preem);
} else if (pconf->basic.lcd_type == LCD_MLVDS) {
LCDPR("channel_num = %d\n", pctrl->mlvds_cfg.channel_num);
LCDPR("channel_sel0 = %d\n", pctrl->mlvds_cfg.channel_sel0);
LCDPR("channel_sel1 = %d\n", pctrl->mlvds_cfg.channel_sel1);
LCDPR("clk_phase = %d\n", pctrl->mlvds_cfg.clk_phase);
LCDPR("phy_vswing = 0x%x\n", pctrl->mlvds_cfg.phy_vswing);
LCDPR("phy_preem = 0x%x\n", pctrl->mlvds_cfg.phy_preem);
} else if (pconf->basic.lcd_type == LCD_P2P) {
LCDPR("p2p_type = %d\n", pctrl->p2p_cfg.p2p_type);
LCDPR("lane_num = %d\n", pctrl->p2p_cfg.lane_num);
LCDPR("channel_sel0 = %d\n", pctrl->p2p_cfg.channel_sel0);
LCDPR("channel_sel1 = %d\n", pctrl->p2p_cfg.channel_sel1);
LCDPR("phy_vswing = 0x%x\n", pctrl->p2p_cfg.phy_vswing);
LCDPR("phy_preem = 0x%x\n", pctrl->p2p_cfg.phy_preem);
} else if (pconf->basic.lcd_type == LCD_MIPI) {
if (pctrl->mipi_cfg.check_en) {
LCDPR("check_reg = 0x%02x\n",
pctrl->mipi_cfg.check_reg);
LCDPR("check_cnt = %d\n",
pctrl->mipi_cfg.check_cnt);
}
LCDPR("lane_num = %d\n", pctrl->mipi_cfg.lane_num);
LCDPR("bit_rate_max = %d\n", pctrl->mipi_cfg.bit_rate_max);
LCDPR("pclk_lanebyteclk_factor = %d\n", pctrl->mipi_cfg.factor_numerator);
LCDPR("operation_mode_init = %d\n", pctrl->mipi_cfg.operation_mode_init);
LCDPR("operation_mode_disp = %d\n", pctrl->mipi_cfg.operation_mode_display);
LCDPR("video_mode_type = %d\n", pctrl->mipi_cfg.video_mode_type);
LCDPR("clk_always_hs = %d\n", pctrl->mipi_cfg.clk_always_hs);
LCDPR("phy_switch = %d\n", pctrl->mipi_cfg.phy_switch);
LCDPR("extern_init = %d\n", pctrl->mipi_cfg.extern_init);
} else if (pconf->basic.lcd_type == LCD_EDP) {
LCDPR("max_lane_count = %d\n", pctrl->edp_cfg.max_lane_count);
LCDPR("max_link_rate = %d\n", pctrl->edp_cfg.max_link_rate);
LCDPR("training_mode = %d\n", pctrl->edp_cfg.training_mode);
LCDPR("dpcd_caps_en = %d\n", pctrl->edp_cfg.dpcd_caps_en);
LCDPR("sync_clk_mode = %d\n", pctrl->edp_cfg.sync_clk_mode);
LCDPR("lane_count = %d\n", pctrl->edp_cfg.lane_count);
LCDPR("link_rate = %d\n", pctrl->edp_cfg.link_rate);
LCDPR("bit_rate = %d\n", pctrl->edp_cfg.bit_rate);
LCDPR("training_settings = %d\n", pctrl->edp_cfg.training_settings);
LCDPR("main_stream_enable = %d\n", pctrl->edp_cfg.main_stream_enable);
LCDPR("phy_vswing = 0x%x\n", pctrl->edp_cfg.phy_vswing);
LCDPR("phy_preem = 0x%x\n", pctrl->edp_cfg.phy_preem);
}
}
static int lcd_power_load_from_dts(struct aml_lcd_drv_s *pdrv,
char *dt_addr, int child_offset)
{
struct lcd_power_step_s *pstep;
char *propdata;
unsigned int i, j, temp;
pstep = pdrv->config.power.power_on_step;
propdata = (char *)fdt_getprop(dt_addr, child_offset, "power_on_step", NULL);
if (!propdata) {
LCDERR("[%d]: failed to get power_on_step\n", pdrv->index);
return 0;
} else {
i = 0;
while (i < LCD_PWR_STEP_MAX) {
j = 4 * i;
temp = be32_to_cpup((((u32*)propdata) + j));
pstep[i].type = temp;
if (temp == 0xff)
break;
temp = be32_to_cpup((((u32*)propdata) + j + 1));
pstep[i].index = temp;
temp = be32_to_cpup((((u32*)propdata) + j + 2));
pstep[i].value = temp;
temp = be32_to_cpup((((u32*)propdata) + j + 3));
pstep[i].delay = temp;
if (pstep[i].type == LCD_POWER_TYPE_CLK_SS) {
temp = pstep[i].value;
pdrv->config.timing.ss_level |= temp << 8;
}
i++;
}
}
pstep = pdrv->config.power.power_off_step;
propdata = (char *)fdt_getprop(dt_addr, child_offset, "power_off_step", NULL);
if (!propdata) {
LCDERR("[%d]: failed to get power_off_step\n", pdrv->index);
return 0;
} else {
i = 0;
while (i < LCD_PWR_STEP_MAX) {
j = 4 * i;
temp = be32_to_cpup((((u32*)propdata) + j));
pstep[i].type = temp;
if (temp == 0xff)
break;
temp = be32_to_cpup((((u32*)propdata) + j + 1));
pstep[i].index = temp;
temp = be32_to_cpup((((u32*)propdata) + j + 2));
pstep[i].value = temp;
temp = be32_to_cpup((((u32*)propdata) + j + 3));
pstep[i].delay = temp;
i++;
}
}
return 0;
}
static int lcd_power_load_from_unifykey(struct aml_lcd_drv_s *pdrv,
unsigned char *buf, int key_len, int len)
{
struct lcd_power_step_s *pstep;
int i, j, temp;
unsigned char *p;
int ret = 0;
/* power: (5byte * n) */
pstep = pdrv->config.power.power_on_step;
p = buf + len;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: power_on step:\n", pdrv->index);
i = 0;
while (i < LCD_PWR_STEP_MAX) {
len += 5;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
pstep[i].type = 0xff;
pstep[i].index = 0;
pstep[i].value = 0;
pstep[i].delay = 0;
LCDERR("unifykey power_on length is incorrect\n");
return -1;
}
pstep[i].type = *(p + LCD_UKEY_PWR_TYPE + 5 * i);
pstep[i].index = *(p + LCD_UKEY_PWR_INDEX + 5 * i);
pstep[i].value = *(p + LCD_UKEY_PWR_VAL + 5 * i);
pstep[i].delay = (*(p + LCD_UKEY_PWR_DELAY + 5 * i) |
((*(p + LCD_UKEY_PWR_DELAY + 5 * i + 1)) << 8));
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("step %d: type=%d, index=%d, value=%d, delay=%d\n",
i, pstep[i].type, pstep[i].index,
pstep[i].value, pstep[i].delay);
}
if (pstep[i].type >= LCD_POWER_TYPE_MAX)
break;
if (pstep[i].type == LCD_POWER_TYPE_CLK_SS) {
temp = pstep[i].value;
pdrv->config.timing.ss_level |= temp << 8;
}
i++;
}
pstep = pdrv->config.power.power_off_step;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: power_off step:\n", pdrv->index);
p += (5 * (i + 1));
j = 0;
while (j < LCD_PWR_STEP_MAX) {
len += 5;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
pstep[j].type = 0xff;
pstep[j].index = 0;
pstep[j].value = 0;
pstep[j].delay = 0;
LCDERR("unifykey power_off length is incorrect\n");
return -1;
}
pstep[j].type = *(p + LCD_UKEY_PWR_TYPE + 5 * j);
pstep[j].index = *(p + LCD_UKEY_PWR_INDEX + 5 * j);
pstep[j].value = *(p + LCD_UKEY_PWR_VAL + 5 * j);
pstep[j].delay = (*(p + LCD_UKEY_PWR_DELAY + 5 * j) |
((*(p + LCD_UKEY_PWR_DELAY + 5 * j + 1)) << 8));
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("step %d: type=%d, index=%d, value=%d, delay=%d\n",
j, pstep[j].type, pstep[j].index,
pstep[j].value, pstep[j].delay);
}
if (pstep[j].type >= LCD_POWER_TYPE_MAX)
break;
j++;
}
return ret;
}
static const char *lcd_ttl_pinmux_str[] = {
"lcd_ttl_rgb_6bit_on", /* 0 */
"lcd_ttl_rgb_8bit_on", /* 1 */
"lcd_ttl_de_on_pin", /* 2 */
"lcd_ttl_hvsync_on_pin", /* 3 */
"lcd_ttl_de_hvsync_on_pin", /* 4 */
};
static int lcd_pinmux_load_ttl(struct lcd_pinmux_ctrl_s *pinmux, struct lcd_config_s *pconf)
{
char propname[30];
int pinmux_index = 0, set_cnt = 0, clr_cnt = 0;
unsigned int i, j;
/* data */
if (pconf->basic.lcd_bits == 6)
pinmux_index = 0;
else
pinmux_index = 1;
sprintf(propname, "%s", lcd_ttl_pinmux_str[pinmux_index]);
for (i = 0; i < LCD_PINMX_MAX; i++) {
if (!pinmux)
break;
if (!pinmux->name)
break;
if (strncmp(pinmux->name, "invalid", 7) == 0)
break;
if (strncmp(pinmux->name, propname,
strlen(propname)) == 0) {
for (j = 0; j < LCD_PINMUX_NUM; j++ ) {
if (pinmux->pinmux_set[j][0] == LCD_PINMUX_END)
break;
pconf->pinmux_set[j][0] = pinmux->pinmux_set[j][0];
pconf->pinmux_set[j][1] = pinmux->pinmux_set[j][1];
set_cnt++;
}
for (j = 0; j < LCD_PINMUX_NUM; j++ ) {
if (pinmux->pinmux_clr[j][0] == LCD_PINMUX_END)
break;
pconf->pinmux_clr[j][0] = pinmux->pinmux_clr[j][0];
pconf->pinmux_clr[j][1] = pinmux->pinmux_clr[j][1];
clr_cnt++;
}
break;
}
pinmux++;
}
/* sync */
switch (pconf->control.ttl_cfg.sync_valid) {
case 0x1: /* hvsync */
pinmux_index = 3;
break;
case 0x2: /* de */
pinmux_index = 2;
break;
case 0x3: /* de + hvsync */
default:
pinmux_index = 4;
break;
}
sprintf(propname, "%s", lcd_ttl_pinmux_str[pinmux_index]);
for (i = 0; i < LCD_PINMX_MAX; i++) {
if (!pinmux)
break;
if (!pinmux->name)
break;
if (strncmp(pinmux->name, "invalid", 7) == 0)
break;
if (strncmp(pinmux->name, propname, strlen(propname)) == 0) {
for (j = 0; j < LCD_PINMUX_NUM; j++ ) {
if (pinmux->pinmux_set[j][0] == LCD_PINMUX_END)
break;
pconf->pinmux_set[j][0] = pinmux->pinmux_set[j][0];
pconf->pinmux_set[j][1] = pinmux->pinmux_set[j][1];
set_cnt++;
}
for (j = 0; j < LCD_PINMUX_NUM; j++ ) {
if (pinmux->pinmux_clr[j][0] == LCD_PINMUX_END)
break;
pconf->pinmux_clr[j][0] = pinmux->pinmux_clr[j][0];
pconf->pinmux_clr[j][1] = pinmux->pinmux_clr[j][1];
clr_cnt++;
}
break;
}
pinmux++;
}
if (set_cnt < LCD_PINMUX_NUM) {
pconf->pinmux_set[set_cnt][0] = LCD_PINMUX_END;
pconf->pinmux_set[set_cnt][1] = 0x0;
}
if (clr_cnt < LCD_PINMUX_NUM) {
pconf->pinmux_clr[clr_cnt][0] = LCD_PINMUX_END;
pconf->pinmux_clr[clr_cnt][1] = 0x0;
}
return 0;
}
static int lcd_pinmux_load_config(struct aml_lcd_drv_s *pdrv)
{
struct lcd_pinmux_ctrl_s *pinmux;
struct lcd_config_s *pconf = &pdrv->config;
char propname[30];
int set_cnt = 0, clr_cnt = 0;
unsigned int i, j;
int ret = 0;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s\n", pdrv->index, __func__);
if (!pdrv->data->dft_conf[pdrv->index]) {
LCDERR("[%d]: %s: dft_conf is NULL\n", pdrv->index, __func__);
return -1;
}
pinmux = pdrv->data->dft_conf[pdrv->index]->lcd_pinmux;
if (!pinmux) {
LCDERR("[%d]: %s: lcd_pinmux is NULL\n", pdrv->index, __func__);
return -1;
}
if (pconf->basic.lcd_type == LCD_TTL) {
ret = lcd_pinmux_load_ttl(pinmux, pconf);
if (ret)
return -1;
goto lcd_pinmux_load_config_next;
}
switch (pconf->basic.lcd_type) {
case LCD_VBYONE:
sprintf(propname, "lcd_vbyone_pin");
break;
case LCD_MLVDS:
sprintf(propname, "lcd_minilvds_pin");
break;
case LCD_P2P:
if (pconf->control.p2p_cfg.p2p_type == P2P_USIT)
sprintf(propname, "lcd_p2p_usit_pin");
else
sprintf(propname, "lcd_p2p_pin");
break;
case LCD_EDP:
sprintf(propname, "lcd_edp_pin");
break;
default:
pconf->pinmux_set[0][0] = LCD_PINMUX_END;
pconf->pinmux_set[0][1] = 0x0;
pconf->pinmux_clr[0][0] = LCD_PINMUX_END;
pconf->pinmux_clr[0][1] = 0x0;
return 0;
}
for (i = 0; i < LCD_PINMX_MAX; i++) {
if (!pinmux->name)
break;
if (strncmp(pinmux->name, "invalid", 7) == 0)
break;
if (strncmp(pinmux->name, propname, strlen(propname)) == 0) {
for (j = 0; j < LCD_PINMUX_NUM; j++) {
if (pinmux->pinmux_set[j][0] == LCD_PINMUX_END)
break;
pconf->pinmux_set[j][0] = pinmux->pinmux_set[j][0];
pconf->pinmux_set[j][1] = pinmux->pinmux_set[j][1];
set_cnt++;
}
for (j = 0; j < LCD_PINMUX_NUM; j++) {
if (pinmux->pinmux_clr[j][0] == LCD_PINMUX_END)
break;
pconf->pinmux_clr[j][0] = pinmux->pinmux_clr[j][0];
pconf->pinmux_clr[j][1] = pinmux->pinmux_clr[j][1];
clr_cnt++;
}
break;
}
pinmux++;
}
if (set_cnt < LCD_PINMUX_NUM) {
pconf->pinmux_set[set_cnt][0] = LCD_PINMUX_END;
pconf->pinmux_set[set_cnt][1] = 0x0;
}
if (clr_cnt < LCD_PINMUX_NUM) {
pconf->pinmux_clr[clr_cnt][0] = LCD_PINMUX_END;
pconf->pinmux_clr[clr_cnt][1] = 0x0;
}
lcd_pinmux_load_config_next:
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
i = 0;
while (i < LCD_PINMUX_NUM) {
if (pdrv->config.pinmux_set[i][0] == LCD_PINMUX_END)
break;
LCDPR("pinmux_set: %d, 0x%08x\n",
pdrv->config.pinmux_set[i][0],
pdrv->config.pinmux_set[i][1]);
i++;
}
i = 0;
while (i < LCD_PINMUX_NUM) {
if (pdrv->config.pinmux_clr[i][0] == LCD_PINMUX_END)
break;
LCDPR("pinmux_clr: %d, 0x%08x\n",
pdrv->config.pinmux_clr[i][0],
pdrv->config.pinmux_clr[i][1]);
i++;
}
}
return 0;
}
static int lcd_config_load_from_dts(char *dt_addr, struct aml_lcd_drv_s *pdrv)
{
#ifdef CONFIG_OF_LIBFDT
struct lcd_config_s *pconf = &pdrv->config;
union lcd_ctrl_config_u *pctrl;
int parent_offset;
int child_offset;
char parent_str[10], type_str[20], propname[30];
char *propdata;
unsigned int temp;
int len;
LCDPR("config load from dts\n");
if (pdrv->index == 0) {
sprintf(parent_str, "/lcd");
sprintf(type_str, "panel_type");
} else {
sprintf(parent_str, "/lcd%d", pdrv->index);
sprintf(type_str, "panel%d_type", pdrv->index);
}
parent_offset = fdt_path_offset(dt_addr, parent_str);
if (parent_offset < 0) {
LCDERR("not find %s node: %s\n",
parent_str, fdt_strerror(parent_offset));
return -1;
}
/* check panel_type */
char *panel_type = env_get(type_str);
if (!panel_type) {
LCDERR("[%d]: no %s\n", pdrv->index, type_str);
return -1;
}
LCDPR("[%d]: use %s=%s\n", pdrv->index, type_str, panel_type);
snprintf(propname, 30, "%s/%s", parent_str, panel_type);
child_offset = fdt_path_offset(dt_addr, propname);
if (child_offset < 0) {
LCDERR("[%d]: not find %s node: %s\n",
pdrv->index, propname, fdt_strerror(child_offset));
return -1;
}
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"model_name", NULL);
if (!propdata) {
LCDERR("[%d]: failed to get model_name\n", pdrv->index);
strncpy(pconf->basic.model_name, panel_type,
sizeof(pconf->basic.model_name) - 1);
} else {
strncpy(pconf->basic.model_name, propdata,
sizeof(pconf->basic.model_name) - 1);
}
pconf->basic.model_name[sizeof(pconf->basic.model_name) - 1] = '\0';
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"interface", NULL);
if (!propdata) {
LCDERR("[%d]: failed to get interface\n", pdrv->index);
return -1;
}
pconf->basic.lcd_type = lcd_type_str_to_type(propdata);
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"basic_setting", NULL);
if (!propdata) {
LCDERR("[%d]: failed to get basic_setting\n", pdrv->index);
return -1;
}
pconf->basic.h_active = be32_to_cpup((u32 *)propdata);
pconf->basic.v_active = be32_to_cpup((((u32 *)propdata) + 1));
pconf->basic.h_period = be32_to_cpup((((u32 *)propdata) + 2));
pconf->basic.v_period = be32_to_cpup((((u32 *)propdata) + 3));
pconf->basic.lcd_bits = be32_to_cpup((((u32 *)propdata) + 4));
pconf->basic.screen_width = be32_to_cpup((((u32 *)propdata) + 5));
pconf->basic.screen_height = be32_to_cpup((((u32 *)propdata) + 6));
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"range_setting", NULL);
if (!propdata) {
pconf->basic.h_period_min = pconf->basic.h_period;
pconf->basic.h_period_max = pconf->basic.h_period;
pconf->basic.v_period_min = pconf->basic.v_period;
pconf->basic.v_period_max = pconf->basic.v_period;
pconf->basic.lcd_clk_min = 0;
pconf->basic.lcd_clk_max = 0;
} else {
pconf->basic.h_period_min = be32_to_cpup((u32 *)propdata);
pconf->basic.h_period_max =
be32_to_cpup((((u32 *)propdata) + 1));
pconf->basic.v_period_min =
be32_to_cpup((((u32 *)propdata) + 2));
pconf->basic.v_period_max =
be32_to_cpup((((u32 *)propdata) + 3));
pconf->basic.lcd_clk_min =
be32_to_cpup((((u32 *)propdata) + 4));
pconf->basic.lcd_clk_max =
be32_to_cpup((((u32 *)propdata) + 5));
}
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"lcd_timing", NULL);
if (!propdata) {
LCDERR("[%d]: failed to get lcd_timing\n", pdrv->index);
return -1;
}
pconf->timing.hsync_width =
(unsigned short)(be32_to_cpup((u32 *)propdata));
pconf->timing.hsync_bp =
(unsigned short)(be32_to_cpup((((u32 *)propdata) + 1)));
pconf->timing.hsync_pol =
(unsigned short)(be32_to_cpup((((u32 *)propdata) + 2)));
pconf->timing.vsync_width =
(unsigned short)(be32_to_cpup((((u32 *)propdata) + 3)));
pconf->timing.vsync_bp =
(unsigned short)(be32_to_cpup((((u32 *)propdata) + 4)));
pconf->timing.vsync_pol =
(unsigned short)(be32_to_cpup((((u32 *)propdata) + 5)));
propdata = (char *)fdt_getprop(dt_addr, child_offset, "clk_attr", NULL);
if (!propdata) {
LCDERR("[%d]: failed to get clk_attr\n", pdrv->index);
pconf->timing.fr_adjust_type = 0xff;
pconf->timing.ss_level = 0;
pconf->timing.clk_auto = 1;
pconf->timing.lcd_clk = 60;
} else {
pconf->timing.fr_adjust_type =
(unsigned char)(be32_to_cpup((u32 *)propdata));
pconf->timing.ss_level =
be32_to_cpup((((u32 *)propdata) + 1));
pconf->timing.clk_auto =
(unsigned char)(be32_to_cpup((((u32 *)propdata) + 2)));
temp = be32_to_cpup((((u32 *)propdata) + 3));
if (temp > 0) {
pconf->timing.lcd_clk = temp;
} else {
pconf->timing.lcd_clk = 60;
LCDPR("[%d]: lcd_clk is 0, default to 60Hz\n", pdrv->index);
}
}
pctrl = &pconf->control;
switch (pconf->basic.lcd_type) {
case LCD_TTL:
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"ttl_attr", NULL);
if (!propdata) {
LCDERR("[%d]: failed to get ttl_attr\n", pdrv->index);
return -1;
}
pctrl->ttl_cfg.clk_pol = be32_to_cpup((u32 *)propdata);
pctrl->ttl_cfg.sync_valid =
(((be32_to_cpup((((u32 *)propdata) + 1))) << 1) |
((be32_to_cpup((((u32 *)propdata) + 2))) << 0));
pctrl->ttl_cfg.swap_ctrl =
(((be32_to_cpup((((u32 *)propdata) + 3))) << 1) |
((be32_to_cpup((((u32 *)propdata) + 4))) << 0));
break;
case LCD_LVDS:
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"lvds_attr", &len);
if (!propdata) {
LCDERR("[%d]: failed to get lvds_attr\n", pdrv->index);
return -1;
}
len = len / 4;
if (len == 5) {
pctrl->lvds_cfg.lvds_repack =
be32_to_cpup((u32 *)propdata);
pctrl->lvds_cfg.dual_port =
be32_to_cpup((((u32 *)propdata) + 1));
pctrl->lvds_cfg.pn_swap =
be32_to_cpup((((u32 *)propdata) + 2));
pctrl->lvds_cfg.port_swap =
be32_to_cpup((((u32 *)propdata) + 3));
pctrl->lvds_cfg.lane_reverse =
be32_to_cpup((((u32 *)propdata) + 4));
} else if (len == 4) {
pctrl->lvds_cfg.lvds_repack =
be32_to_cpup((u32 *)propdata);
pctrl->lvds_cfg.dual_port =
be32_to_cpup((((u32 *)propdata) + 1));
pctrl->lvds_cfg.pn_swap =
be32_to_cpup((((u32 *)propdata) + 2));
pctrl->lvds_cfg.port_swap =
be32_to_cpup((((u32 *)propdata) + 3));
pctrl->lvds_cfg.lane_reverse = 0;
} else {
LCDERR("[%d]: invalid lvds_attr parameters cnt: %d\n",
pdrv->index, len);
return -1;
}
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"phy_attr", &len);
if (!propdata) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: failed to get phy_attr\n", pdrv->index);
pctrl->lvds_cfg.phy_vswing = LVDS_PHY_VSWING_DFT;
pctrl->lvds_cfg.phy_preem = LVDS_PHY_PREEM_DFT;
} else {
pctrl->lvds_cfg.phy_vswing =
be32_to_cpup((u32 *)propdata);
pctrl->lvds_cfg.phy_preem =
be32_to_cpup((((u32 *)propdata) + 1));
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: set phy vswing=0x%x, preem=0x%x\n",
pdrv->index,
pctrl->lvds_cfg.phy_vswing,
pctrl->lvds_cfg.phy_preem);
}
}
break;
case LCD_VBYONE:
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"vbyone_attr", NULL);
if (!propdata) {
LCDERR("[%d]: failed to get vbyone_attr\n", pdrv->index);
return -1;
}
pctrl->vbyone_cfg.lane_count =
be32_to_cpup((u32 *)propdata);
pctrl->vbyone_cfg.region_num =
be32_to_cpup((((u32 *)propdata) + 1));
pctrl->vbyone_cfg.byte_mode =
be32_to_cpup((((u32 *)propdata) + 2));
pctrl->vbyone_cfg.color_fmt =
be32_to_cpup((((u32 *)propdata) + 3));
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"phy_attr", NULL);
if (!propdata) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: failed to get phy_attr\n", pdrv->index);
pctrl->vbyone_cfg.phy_vswing = VX1_PHY_VSWING_DFT;
pctrl->vbyone_cfg.phy_preem = VX1_PHY_PREEM_DFT;
} else {
pctrl->vbyone_cfg.phy_vswing =
be32_to_cpup((u32 *)propdata);
pctrl->vbyone_cfg.phy_preem =
be32_to_cpup((((u32 *)propdata) + 1));
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("set phy vswing=0x%x, preem=0x%x\n",
pctrl->vbyone_cfg.phy_vswing,
pctrl->vbyone_cfg.phy_preem);
}
}
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"vbyone_ctrl_flag", NULL);
if (!propdata) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: failed to get vbyone_ctrl_flag\n", pdrv->index);
pctrl->vbyone_cfg.ctrl_flag = 0;
pctrl->vbyone_cfg.power_on_reset_delay = VX1_PWR_ON_RESET_DLY_DFT;
pctrl->vbyone_cfg.hpd_data_delay = VX1_HPD_DATA_DELAY_DFT;
pctrl->vbyone_cfg.cdr_training_hold = VX1_CDR_TRAINING_HOLD_DFT;
} else {
pctrl->vbyone_cfg.ctrl_flag = be32_to_cpup((u32 *)propdata);
LCDPR("vbyone ctrl_flag=0x%x\n", pctrl->vbyone_cfg.ctrl_flag);
}
if (pctrl->vbyone_cfg.ctrl_flag & 0x7) {
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"vbyone_ctrl_timing", NULL);
if (!propdata) {
LCDPR("[%d]: failed to get vbyone_ctrl_timing\n", pdrv->index);
pctrl->vbyone_cfg.power_on_reset_delay =
VX1_PWR_ON_RESET_DLY_DFT;
pctrl->vbyone_cfg.hpd_data_delay =
VX1_HPD_DATA_DELAY_DFT;
pctrl->vbyone_cfg.cdr_training_hold =
VX1_CDR_TRAINING_HOLD_DFT;
} else {
pctrl->vbyone_cfg.power_on_reset_delay =
be32_to_cpup((u32 *)propdata);
pctrl->vbyone_cfg.hpd_data_delay =
be32_to_cpup((((u32 *)propdata) + 1));
pctrl->vbyone_cfg.cdr_training_hold =
be32_to_cpup((((u32 *)propdata) + 2));
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: power_on_reset_delay: %d\n",
pdrv->index,
pctrl->vbyone_cfg.power_on_reset_delay);
LCDPR("[%d]: hpd_data_delay: %d\n",
pdrv->index,
pctrl->vbyone_cfg.hpd_data_delay);
LCDPR("[%d]: cdr_training_hold: %d\n",
pdrv->index,
pctrl->vbyone_cfg.cdr_training_hold);
}
}
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"hw_filter", NULL);
if (!propdata) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: failed to get hw_filter\n", pdrv->index);
pctrl->vbyone_cfg.hw_filter_time = 0;
pctrl->vbyone_cfg.hw_filter_cnt = 0;
} else {
pctrl->vbyone_cfg.hw_filter_time =
be32_to_cpup((u32 *)propdata);
pctrl->vbyone_cfg.hw_filter_cnt =
be32_to_cpup((((u32 *)propdata) + 1));
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: vbyone hw_filter=0x%x 0x%x\n",
pdrv->index,
pctrl->vbyone_cfg.hw_filter_time,
pctrl->vbyone_cfg.hw_filter_cnt);
}
}
break;
case LCD_MLVDS:
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"minilvds_attr", &len);
if (!propdata) {
LCDERR("[%d]: failed to get minilvds_attr\n", pdrv->index);
return -1;
}
pctrl->mlvds_cfg.channel_num =
be32_to_cpup((u32 *)propdata);
pctrl->mlvds_cfg.channel_sel0 =
be32_to_cpup((((u32 *)propdata) + 1));
pctrl->mlvds_cfg.channel_sel1 =
be32_to_cpup((((u32 *)propdata) + 2));
pctrl->mlvds_cfg.clk_phase =
be32_to_cpup((((u32 *)propdata) + 3));
pctrl->mlvds_cfg.pn_swap =
be32_to_cpup((((u32 *)propdata) + 4));
pctrl->mlvds_cfg.bit_swap =
be32_to_cpup((((u32 *)propdata) + 5));
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"phy_attr", &len);
if (!propdata) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: failed to get phy_attr\n", pdrv->index);
pctrl->mlvds_cfg.phy_vswing = LVDS_PHY_VSWING_DFT;
pctrl->mlvds_cfg.phy_preem = LVDS_PHY_PREEM_DFT;
} else {
pctrl->mlvds_cfg.phy_vswing =
be32_to_cpup((u32 *)propdata);
pctrl->mlvds_cfg.phy_preem =
be32_to_cpup((((u32 *)propdata) + 1));
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: set phy vswing=0x%x, preem=0x%x\n",
pdrv->index,
pctrl->mlvds_cfg.phy_vswing,
pctrl->mlvds_cfg.phy_preem);
}
}
break;
case LCD_P2P:
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"p2p_attr", NULL);
if (!propdata) {
LCDERR("[%d]: failed to get p2p_attr\n", pdrv->index);
return -1;
}
pctrl->p2p_cfg.p2p_type =
be32_to_cpup((u32 *)propdata);
pctrl->p2p_cfg.lane_num =
be32_to_cpup((((u32 *)propdata) + 1));
pctrl->p2p_cfg.channel_sel0 =
be32_to_cpup((((u32 *)propdata) + 2));
pctrl->p2p_cfg.channel_sel1 =
be32_to_cpup((((u32 *)propdata) + 3));
pctrl->p2p_cfg.pn_swap =
be32_to_cpup((((u32 *)propdata) + 4));
pctrl->p2p_cfg.bit_swap =
be32_to_cpup((((u32 *)propdata) + 5));
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"phy_attr", NULL);
if (!propdata) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: failed to get phy_attr\n", pdrv->index);
pctrl->p2p_cfg.phy_vswing = 0x5;
pctrl->p2p_cfg.phy_preem = 0x1;
} else {
pctrl->p2p_cfg.phy_vswing = be32_to_cpup((u32 *)propdata);
pctrl->p2p_cfg.phy_preem =
be32_to_cpup((((u32 *)propdata) + 1));
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: set phy vswing=0x%x, preem=0x%x\n",
pdrv->index,
pctrl->p2p_cfg.phy_vswing,
pctrl->p2p_cfg.phy_preem);
}
}
break;
case LCD_MIPI:
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"mipi_attr", NULL);
if (!propdata) {
LCDERR("[%d]: failed to get mipi_attr\n", pdrv->index);
return -1;
}
pctrl->mipi_cfg.lane_num =
be32_to_cpup((u32 *)propdata);
pctrl->mipi_cfg.bit_rate_max =
be32_to_cpup((((u32 *)propdata) + 1));
pctrl->mipi_cfg.factor_numerator =
be32_to_cpup((((u32 *)propdata) + 2));
pctrl->mipi_cfg.factor_denominator = 100;
pctrl->mipi_cfg.operation_mode_init =
be32_to_cpup((((u32 *)propdata) + 3));
pctrl->mipi_cfg.operation_mode_display =
be32_to_cpup((((u32 *)propdata) + 4));
pctrl->mipi_cfg.video_mode_type =
be32_to_cpup((((u32 *)propdata) + 5));
pctrl->mipi_cfg.clk_always_hs =
be32_to_cpup((((u32 *)propdata) + 6));
pctrl->mipi_cfg.phy_switch =
be32_to_cpup((((u32 *)propdata) + 7));
pctrl->mipi_cfg.check_en = 0;
pctrl->mipi_cfg.check_reg = 0xff;
pctrl->mipi_cfg.check_cnt = 0;
#ifdef CONFIG_AML_LCD_TABLET
lcd_mipi_dsi_init_table_detect(dt_addr, child_offset,
&pctrl->mipi_cfg, 1);
lcd_mipi_dsi_init_table_detect(dt_addr, child_offset,
&pctrl->mipi_cfg, 0);
#endif
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"extern_init", NULL);
if (!propdata) {
LCDERR("[%d]: failed to get extern_init\n", pdrv->index);
} else {
pctrl->mipi_cfg.extern_init =
be32_to_cpup((u32 *)propdata);
}
#ifdef CONFIG_AML_LCD_TABLET
mipi_dsi_config_init(pconf);
#endif
break;
case LCD_EDP:
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"edp_attr", NULL);
if (!propdata) {
LCDERR("[%d]: failed to get edp_attr\n", pdrv->index);
return -1;
}
pctrl->edp_cfg.max_lane_count =
(unsigned char)be32_to_cpup((u32 *)propdata);
pctrl->edp_cfg.max_link_rate =
(unsigned char)be32_to_cpup((((u32 *)propdata) + 1));
pctrl->edp_cfg.training_mode =
(unsigned char)be32_to_cpup((((u32 *)propdata) + 2));
pctrl->edp_cfg.dpcd_caps_en =
(unsigned char)be32_to_cpup((((u32 *)propdata) + 3));
pctrl->edp_cfg.sync_clk_mode =
(unsigned char)be32_to_cpup((((u32 *)propdata) + 4));
pctrl->edp_cfg.scramb_mode =
(unsigned char)be32_to_cpup((((u32 *)propdata) + 5));
pctrl->edp_cfg.enhanced_framing_en =
(unsigned char)be32_to_cpup((((u32 *)propdata) + 6));
pctrl->edp_cfg.edid_en =
(unsigned char)be32_to_cpup((((u32 *)propdata) + 7));
pctrl->edp_cfg.lane_count = pctrl->edp_cfg.max_lane_count;
pctrl->edp_cfg.link_rate = pctrl->edp_cfg.max_link_rate;
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"phy_attr", NULL);
if (!propdata) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: failed to get phy_attr\n", pdrv->index);
pctrl->edp_cfg.phy_vswing = 0x5;
pctrl->edp_cfg.phy_preem = 0x1;
} else {
pctrl->edp_cfg.phy_vswing = be32_to_cpup((u32 *)propdata);
pctrl->edp_cfg.phy_preem = be32_to_cpup((((u32 *)propdata) + 1));
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: set phy vswing=0x%x, preem=0x%x\n",
pdrv->index,
pctrl->edp_cfg.phy_vswing,
pctrl->edp_cfg.phy_preem);
}
}
break;
default:
LCDERR("invalid lcd type\n");
break;
}
/* check power_step */
lcd_power_load_from_dts(pdrv, dt_addr, child_offset);
propdata = (char *)fdt_getprop(dt_addr, child_offset,
"backlight_index", NULL);
if (!propdata) {
LCDERR("[%d]: failed to get backlight_index\n", pdrv->index);
pconf->backlight_index = 0xff;
} else {
pconf->backlight_index = be32_to_cpup((u32 *)propdata);
#ifdef CONFIG_AML_LCD_BACKLIGHT
aml_bl_index_add(pdrv->index, pconf->backlight_index);
#endif
}
#endif
return 0;
}
static int lcd_config_load_from_unifykey(struct aml_lcd_drv_s *pdrv)
{
struct lcd_config_s *pconf = &pdrv->config;
union lcd_ctrl_config_u *pctrl;
struct lcd_unifykey_header_s lcd_header;
unsigned char *para;
char key_str[10];
int key_len, len;
unsigned char *p;
const char *str;
unsigned int temp;
int ret;
LCDPR("config load from unifykey\n");
key_len = LCD_UKEY_LCD_SIZE;
para = (unsigned char *)malloc(sizeof(unsigned char) * key_len);
if (!para) {
LCDERR("[%d]: %s: Not enough memory\n", pdrv->index, __func__);
return -1;
}
memset(para, 0, (sizeof(unsigned char) * key_len));
if (pdrv->index == 0)
sprintf(key_str, "lcd");
else
sprintf(key_str, "lcd%d", pdrv->index);
ret = lcd_unifykey_get(key_str, para, &key_len);
if (ret) {
free(para);
return -1;
}
/* step 1: check header */
len = LCD_UKEY_HEAD_SIZE;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
LCDERR("[%d]: unifykey header length is incorrect\n", pdrv->index);
free(para);
return -1;
}
lcd_unifykey_header_check(para, &lcd_header);
LCDPR("[%d]: unifykey version: 0x%04x\n", pdrv->index, lcd_header.version);
switch (lcd_header.version) {
case 2:
len = LCD_UKEY_DATA_LEN_V2; /*10+36+18+31+20+44+10*/
break;
default:
len = LCD_UKEY_DATA_LEN_V1; /*10+36+18+31+20*/
break;
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("unifykey header:\n");
LCDPR("crc32 = 0x%08x\n", lcd_header.crc32);
LCDPR("data_len = %d\n", lcd_header.data_len);
LCDPR("reserved = 0x%04x\n", lcd_header.reserved);
}
/* step 2: check lcd parameters */
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
LCDERR("[%d]: unifykey parameters length is incorrect\n", pdrv->index);
free(para);
return -1;
}
/* basic: 36byte */
p = para;
str = (const char *)(p + LCD_UKEY_HEAD_SIZE);
strncpy(pconf->basic.model_name, str,
sizeof(pconf->basic.model_name) - 1);
pconf->basic.model_name[sizeof(pconf->basic.model_name) - 1] = '\0';
pconf->basic.lcd_type = *(p + LCD_UKEY_INTERFACE);
pconf->basic.lcd_bits = *(p + LCD_UKEY_LCD_BITS);
pconf->basic.screen_width = (*(p + LCD_UKEY_SCREEN_WIDTH) |
((*(p + LCD_UKEY_SCREEN_WIDTH + 1)) << 8));
pconf->basic.screen_height = (*(p + LCD_UKEY_SCREEN_HEIGHT) |
((*(p + LCD_UKEY_SCREEN_HEIGHT + 1)) << 8));
/* timing: 18byte */
pconf->basic.h_active = (*(p + LCD_UKEY_H_ACTIVE) |
((*(p + LCD_UKEY_H_ACTIVE + 1)) << 8));
pconf->basic.v_active = (*(p + LCD_UKEY_V_ACTIVE)) |
((*(p + LCD_UKEY_V_ACTIVE + 1)) << 8);
pconf->basic.h_period = (*(p + LCD_UKEY_H_PERIOD)) |
((*(p + LCD_UKEY_H_PERIOD + 1)) << 8);
pconf->basic.v_period = (*(p + LCD_UKEY_V_PERIOD)) |
((*(p + LCD_UKEY_V_PERIOD + 1)) << 8);
pconf->timing.hsync_width = (*(p + LCD_UKEY_HS_WIDTH) |
((*(p + LCD_UKEY_HS_WIDTH + 1)) << 8));
pconf->timing.hsync_bp = (*(p + LCD_UKEY_HS_BP) |
((*(p + LCD_UKEY_HS_BP + 1)) << 8));
pconf->timing.hsync_pol = *(p + LCD_UKEY_HS_POL);
pconf->timing.vsync_width = (*(p + LCD_UKEY_VS_WIDTH) |
((*(p + LCD_UKEY_VS_WIDTH + 1)) << 8));
pconf->timing.vsync_bp = (*(p + LCD_UKEY_VS_BP) |
((*(p + LCD_UKEY_VS_BP + 1)) << 8));
pconf->timing.vsync_pol = *(p + LCD_UKEY_VS_POL);
/* customer: 31byte */
pconf->timing.fr_adjust_type = *(p + LCD_UKEY_FR_ADJ_TYPE);
pconf->timing.ss_level = *(p + LCD_UKEY_SS_LEVEL);
pconf->timing.clk_auto = *(p + LCD_UKEY_CLK_AUTO_GEN);
pconf->timing.lcd_clk = (*(p + LCD_UKEY_PCLK) |
((*(p + LCD_UKEY_PCLK + 1)) << 8) |
((*(p + LCD_UKEY_PCLK + 2)) << 16) |
((*(p + LCD_UKEY_PCLK + 3)) << 24));
if (pconf->timing.lcd_clk == 0) {
pconf->timing.lcd_clk = 60;
LCDERR("[%d]: lcd_clk is 0, default to 60Hz\n", pdrv->index);
}
pconf->basic.h_period_min = (*(p + LCD_UKEY_H_PERIOD_MIN) |
((*(p + LCD_UKEY_H_PERIOD_MIN + 1)) << 8));
pconf->basic.h_period_max = (*(p + LCD_UKEY_H_PERIOD_MAX) |
((*(p + LCD_UKEY_H_PERIOD_MAX + 1)) << 8));
pconf->basic.v_period_min = (*(p + LCD_UKEY_V_PERIOD_MIN) |
((*(p + LCD_UKEY_V_PERIOD_MIN + 1)) << 8));
pconf->basic.v_period_max = (*(p + LCD_UKEY_V_PERIOD_MAX) |
((*(p + LCD_UKEY_V_PERIOD_MAX + 1)) << 8));
pconf->basic.lcd_clk_min = (*(p + LCD_UKEY_PCLK_MIN) |
((*(p + LCD_UKEY_PCLK_MIN + 1)) << 8) |
((*(p + LCD_UKEY_PCLK_MIN + 2)) << 16) |
((*(p + LCD_UKEY_PCLK_MIN + 3)) << 24));
pconf->basic.lcd_clk_max = (*(p + LCD_UKEY_PCLK_MAX) |
((*(p + LCD_UKEY_PCLK_MAX + 1)) << 8) |
((*(p + LCD_UKEY_PCLK_MAX + 2)) << 16) |
((*(p + LCD_UKEY_PCLK_MAX + 3)) << 24));
pctrl = &pconf->control;
/* interface: 20byte */
switch (pconf->basic.lcd_type) {
case LCD_TTL:
pctrl->ttl_cfg.clk_pol =
(*(p + LCD_UKEY_IF_ATTR_0) |
((*(p + LCD_UKEY_IF_ATTR_0 + 1)) << 8)) & 0x1;
temp = (*(p + LCD_UKEY_IF_ATTR_1) |
((*(p + LCD_UKEY_IF_ATTR_1 + 1)) << 8)) & 0x1; /* de_valid */
pctrl->ttl_cfg.sync_valid = (temp << 1);
temp = (*(p + LCD_UKEY_IF_ATTR_2) |
((*(p + LCD_UKEY_IF_ATTR_2 + 1)) << 8)) & 0x1; /* hvsync_valid */
pctrl->ttl_cfg.sync_valid |= (temp << 0);
temp = (*(p + LCD_UKEY_IF_ATTR_3) |
((*(p + LCD_UKEY_IF_ATTR_3 + 1)) << 8)) & 0x1; /* rb_swap */
pctrl->ttl_cfg.swap_ctrl = (temp << 1);
temp = (*(p + LCD_UKEY_IF_ATTR_4) |
((*(p + LCD_UKEY_IF_ATTR_4 + 1)) << 8)) & 0x1; /* bit_swap */
pctrl->ttl_cfg.swap_ctrl |= (temp << 0);
break;
case LCD_LVDS:
pctrl->lvds_cfg.lvds_repack =
*(p + LCD_UKEY_IF_ATTR_0) |
((*(p + LCD_UKEY_IF_ATTR_0 + 1)) << 8);
pctrl->lvds_cfg.dual_port =
*(p + LCD_UKEY_IF_ATTR_1) |
((*(p + LCD_UKEY_IF_ATTR_1 + 1)) << 8);
pctrl->lvds_cfg.pn_swap =
*(p + LCD_UKEY_IF_ATTR_2) |
((*(p + LCD_UKEY_IF_ATTR_2 + 1)) << 8);
pctrl->lvds_cfg.port_swap =
*(p + LCD_UKEY_IF_ATTR_3) |
((*(p + LCD_UKEY_IF_ATTR_3 + 1)) << 8);
if (lcd_header.version == 1) {
pctrl->lvds_cfg.phy_vswing =
*(p + LCD_UKEY_IF_ATTR_4) |
((*(p + LCD_UKEY_IF_ATTR_4 + 1)) << 8);
pctrl->lvds_cfg.phy_preem =
*(p + LCD_UKEY_IF_ATTR_5) |
((*(p + LCD_UKEY_IF_ATTR_5 + 1)) << 8);
pctrl->lvds_cfg.lane_reverse =
*(p + LCD_UKEY_IF_ATTR_8) |
((*(p + LCD_UKEY_IF_ATTR_8 + 1)) << 8);
} else {
pctrl->lvds_cfg.lane_reverse =
*(p + LCD_UKEY_IF_ATTR_4) |
((*(p + LCD_UKEY_IF_ATTR_4 + 1)) << 8);
pctrl->lvds_cfg.phy_vswing = *(p + LCD_UKEY_PHY_ATTR_0);
pctrl->lvds_cfg.phy_preem = *(p + LCD_UKEY_PHY_ATTR_1);
}
break;
case LCD_VBYONE:
pctrl->vbyone_cfg.lane_count =
*(p + LCD_UKEY_IF_ATTR_0) |
((*(p + LCD_UKEY_IF_ATTR_0 + 1)) << 8);
pctrl->vbyone_cfg.region_num =
*(p + LCD_UKEY_IF_ATTR_1) |
((*(p + LCD_UKEY_IF_ATTR_1 + 1)) << 8);
pctrl->vbyone_cfg.byte_mode =
*(p + LCD_UKEY_IF_ATTR_2) |
((*(p + LCD_UKEY_IF_ATTR_2 + 1)) << 8);
pctrl->vbyone_cfg.color_fmt =
*(p + LCD_UKEY_IF_ATTR_3) |
((*(p + LCD_UKEY_IF_ATTR_3 + 1)) << 8);
if (lcd_header.version == 1) {
pctrl->vbyone_cfg.phy_vswing =
*(p + LCD_UKEY_IF_ATTR_4) |
((*(p + LCD_UKEY_IF_ATTR_4 + 1)) << 8);
pctrl->vbyone_cfg.phy_preem =
*(p + LCD_UKEY_IF_ATTR_5) |
((*(p + LCD_UKEY_IF_ATTR_5 + 1)) << 8);
pctrl->vbyone_cfg.hw_filter_time =
*(p + LCD_UKEY_IF_ATTR_8) |
((*(p + LCD_UKEY_IF_ATTR_8 + 1)) << 8);
pctrl->vbyone_cfg.hw_filter_cnt =
*(p + LCD_UKEY_IF_ATTR_9) |
((*(p + LCD_UKEY_IF_ATTR_9 + 1)) << 8);
pctrl->vbyone_cfg.ctrl_flag = 0;
pctrl->vbyone_cfg.power_on_reset_delay = VX1_PWR_ON_RESET_DLY_DFT;
pctrl->vbyone_cfg.hpd_data_delay = VX1_HPD_DATA_DELAY_DFT;
pctrl->vbyone_cfg.cdr_training_hold = VX1_CDR_TRAINING_HOLD_DFT;
} else {
pctrl->vbyone_cfg.ctrl_flag =
*(p + LCD_UKEY_CTRL_FLAG) |
((*(p + LCD_UKEY_CTRL_FLAG + 1)) << 8) |
((*(p + LCD_UKEY_CTRL_FLAG + 2)) << 16) |
((*(p + LCD_UKEY_CTRL_FLAG + 3)) << 24);
pctrl->vbyone_cfg.power_on_reset_delay =
*(p + LCD_UKEY_CTRL_ATTR_0) |
((*(p + LCD_UKEY_CTRL_ATTR_0 + 1)) << 8);
pctrl->vbyone_cfg.hpd_data_delay =
*(p + LCD_UKEY_CTRL_ATTR_1) |
((*(p + LCD_UKEY_CTRL_ATTR_1 + 1)) << 8);
pctrl->vbyone_cfg.cdr_training_hold =
*(p + LCD_UKEY_CTRL_ATTR_2) |
((*(p + LCD_UKEY_CTRL_ATTR_2 + 1)) << 8);
pctrl->vbyone_cfg.phy_vswing = *(p + LCD_UKEY_PHY_ATTR_0);
pctrl->vbyone_cfg.phy_preem = *(p + LCD_UKEY_PHY_ATTR_1);
}
break;
case LCD_MLVDS:
pctrl->mlvds_cfg.channel_num =
*(p + LCD_UKEY_IF_ATTR_0) |
((*(p + LCD_UKEY_IF_ATTR_0 + 1)) << 8);
pctrl->mlvds_cfg.channel_sel0 =
*(p + LCD_UKEY_IF_ATTR_1) |
((*(p + LCD_UKEY_IF_ATTR_1 + 1)) << 8) |
((*(p + LCD_UKEY_IF_ATTR_2)) << 16) |
((*(p + LCD_UKEY_IF_ATTR_2 + 1)) << 24);
pctrl->mlvds_cfg.channel_sel1 =
*(p + LCD_UKEY_IF_ATTR_3) |
((*(p + LCD_UKEY_IF_ATTR_3 + 1)) << 8) |
((*(p + LCD_UKEY_IF_ATTR_4)) << 16) |
((*(p + LCD_UKEY_IF_ATTR_4 + 1)) << 24);
pctrl->mlvds_cfg.clk_phase =
*(p + LCD_UKEY_IF_ATTR_5) |
((*(p + LCD_UKEY_IF_ATTR_5 + 1)) << 8);
pctrl->mlvds_cfg.pn_swap =
*(p + LCD_UKEY_IF_ATTR_6) |
((*(p + LCD_UKEY_IF_ATTR_6 + 1)) << 8);
pctrl->mlvds_cfg.bit_swap =
*(p + LCD_UKEY_IF_ATTR_7) |
((*(p + LCD_UKEY_IF_ATTR_7 + 1)) << 8);
if (lcd_header.version == 1) {
pctrl->mlvds_cfg.phy_vswing =
*(p + LCD_UKEY_IF_ATTR_8) |
((*(p + LCD_UKEY_IF_ATTR_8 + 1)) << 8);
pctrl->mlvds_cfg.phy_preem =
*(p + LCD_UKEY_IF_ATTR_9) |
((*(p + LCD_UKEY_IF_ATTR_9 + 1)) << 8);
} else {
pctrl->mlvds_cfg.phy_vswing = *(p + LCD_UKEY_PHY_ATTR_0);
pctrl->mlvds_cfg.phy_preem = *(p + LCD_UKEY_PHY_ATTR_1);
}
break;
case LCD_P2P:
pctrl->p2p_cfg.p2p_type =
*(p + LCD_UKEY_IF_ATTR_0) |
((*(p + LCD_UKEY_IF_ATTR_0 + 1)) << 8);
pctrl->p2p_cfg.lane_num =
*(p + LCD_UKEY_IF_ATTR_1) |
((*(p + LCD_UKEY_IF_ATTR_1 + 1)) << 8);
pctrl->p2p_cfg.channel_sel0 =
*(p + LCD_UKEY_IF_ATTR_2) |
((*(p + LCD_UKEY_IF_ATTR_2 + 1)) << 8) |
(*(p + LCD_UKEY_IF_ATTR_3) << 16) |
((*(p + LCD_UKEY_IF_ATTR_3 + 1)) << 24);
pctrl->p2p_cfg.channel_sel1 =
*(p + LCD_UKEY_IF_ATTR_4) |
((*(p + LCD_UKEY_IF_ATTR_4 + 1)) << 8) |
(*(p + LCD_UKEY_IF_ATTR_5) << 16) |
((*(p + LCD_UKEY_IF_ATTR_5 + 1)) << 24);
pctrl->p2p_cfg.pn_swap =
*(p + LCD_UKEY_IF_ATTR_6) |
((*(p + LCD_UKEY_IF_ATTR_6 + 1)) << 8);
pctrl->p2p_cfg.bit_swap =
*(p + LCD_UKEY_IF_ATTR_7) |
((*(p + LCD_UKEY_IF_ATTR_7 + 1)) << 8);
pctrl->p2p_cfg.phy_vswing =
*(p + LCD_UKEY_IF_ATTR_8) |
((*(p + LCD_UKEY_IF_ATTR_8 + 1)) << 8);
pctrl->p2p_cfg.phy_preem =
*(p + LCD_UKEY_IF_ATTR_9) |
((*(p + LCD_UKEY_IF_ATTR_9 + 1)) << 8);
break;
default:
LCDERR("[%d]: unsupport lcd_type: %d\n",
pdrv->index, pconf->basic.lcd_type);
break;
}
/* step 3: check power sequence */
ret = lcd_power_load_from_unifykey(pdrv, para, key_len, len);
if (ret < 0) {
free(para);
return -1;
}
#ifdef CONFIG_AML_LCD_BACKLIGHT
aml_bl_index_add(pdrv->index, 0);
#endif
free(para);
return 0;
}
static int lcd_config_load_from_bsp(struct aml_lcd_drv_s *pdrv)
{
struct ext_lcd_config_s *ext_lcd;
struct lcd_config_s *pconf = &pdrv->config;
union lcd_ctrl_config_u *pctrl;
struct lcd_power_step_s *power_step;
char *panel_type, str[15];
unsigned int i, done;
unsigned int temp;
LCDPR("config load from bsp\n");
if (pdrv->index >= LCD_MAX_DRV) {
LCDERR("[%d]: invalid drv index %d\n", pdrv->index, pdrv->index);
return -1;
}
if (pdrv->index == 0)
sprintf(str, "panel_type");
else
sprintf(str, "panel%d_type", pdrv->index);
panel_type = env_get(str);
if (!panel_type) {
LCDERR("[%d]: no %s exist\n", pdrv->index, str);
return -1;
}
if (!pdrv->data->dft_conf[pdrv->index]) {
LCDERR("[%d]: %s: dft_conf is NULL\n", pdrv->index, __func__);
return -1;
}
ext_lcd = pdrv->data->dft_conf[pdrv->index]->ext_lcd;
if (!ext_lcd) {
LCDERR("[%d]: %s: ext_lcd is NULL\n", pdrv->index, __func__);
return -1;
}
done = 0;
for (i = 0 ; i < LCD_NUM_MAX ; i++) {
if (strcmp(ext_lcd->panel_type, panel_type) == 0) {
done = 1;
break;
}
if (strcmp(ext_lcd->panel_type, "invalid") == 0)
break;
ext_lcd++;
}
if (done == 0) {
LCDERR("[%d]: can't find %s\n ", pdrv->index, panel_type);
return -1;
}
LCDPR("[%d]: use default %s=%s\n", pdrv->index, str, panel_type);
strncpy(pconf->basic.model_name, panel_type,
sizeof(pconf->basic.model_name) - 1);
pconf->basic.model_name[sizeof(pconf->basic.model_name) - 1] = '\0';
pconf->basic.lcd_type = ext_lcd->lcd_type;
pconf->basic.lcd_bits = ext_lcd->lcd_bits;
pconf->basic.h_active = ext_lcd->h_active;
pconf->basic.v_active = ext_lcd->v_active;
pconf->basic.h_period = ext_lcd->h_period;
pconf->basic.v_period = ext_lcd->v_period;
pconf->basic.h_period_min = pconf->basic.h_period;
pconf->basic.h_period_max = pconf->basic.h_period;
pconf->basic.v_period_min = pconf->basic.v_period;
pconf->basic.v_period_max = pconf->basic.v_period;
pconf->basic.lcd_clk_min = 0;
pconf->basic.lcd_clk_max = 0;
pconf->timing.hsync_width = ext_lcd->hsync_width;
pconf->timing.hsync_bp = ext_lcd->hsync_bp;
pconf->timing.hsync_pol = ext_lcd->hsync_pol;
pconf->timing.vsync_width = ext_lcd->vsync_width;
pconf->timing.vsync_bp = ext_lcd->vsync_bp;
pconf->timing.vsync_pol = ext_lcd->vsync_pol;
/* fr_adjust_type */
temp = ext_lcd->customer_val_0;
if (temp == Rsv_val)
pconf->timing.fr_adjust_type = 0;
else
pconf->timing.fr_adjust_type = (unsigned char)temp;
/* ss_level */
temp = ext_lcd->customer_val_1;
if (temp == Rsv_val)
pconf->timing.ss_level = 0;
else
pconf->timing.ss_level = temp;
/* clk_auto_generate */
temp = ext_lcd->customer_val_2;
if (temp == Rsv_val)
pconf->timing.clk_auto = 1;
else
pconf->timing.clk_auto = (unsigned char)temp;
/* lcd_clk */
temp = ext_lcd->customer_val_3;
if (temp == Rsv_val)
pconf->timing.lcd_clk = 60;
else
pconf->timing.lcd_clk = temp;
pctrl = &pconf->control;
switch (pconf->basic.lcd_type) {
case LCD_TTL:
pctrl->ttl_cfg.clk_pol = ext_lcd->lcd_spc_val0;
pctrl->ttl_cfg.sync_valid = (ext_lcd->lcd_spc_val1 << 1) |
(ext_lcd->lcd_spc_val2 << 0);
pctrl->ttl_cfg.swap_ctrl = (ext_lcd->lcd_spc_val3 << 1) |
(ext_lcd->lcd_spc_val4 << 0);
break;
case LCD_LVDS:
pctrl->lvds_cfg.lvds_repack = ext_lcd->lcd_spc_val0;
pctrl->lvds_cfg.dual_port = ext_lcd->lcd_spc_val1;
pctrl->lvds_cfg.pn_swap = ext_lcd->lcd_spc_val2;
pctrl->lvds_cfg.port_swap = ext_lcd->lcd_spc_val3;
pctrl->lvds_cfg.lane_reverse = ext_lcd->lcd_spc_val4;
pctrl->lvds_cfg.phy_vswing = ext_lcd->lcd_spc_val5;
pctrl->lvds_cfg.phy_preem = ext_lcd->lcd_spc_val6;
pctrl->lvds_cfg.phy_clk_vswing = ext_lcd->lcd_spc_val7;
pctrl->lvds_cfg.phy_clk_preem = ext_lcd->lcd_spc_val8;
break;
case LCD_VBYONE:
pctrl->vbyone_cfg.lane_count = ext_lcd->lcd_spc_val0;
pctrl->vbyone_cfg.region_num = ext_lcd->lcd_spc_val1;
pctrl->vbyone_cfg.byte_mode = ext_lcd->lcd_spc_val2;
pctrl->vbyone_cfg.color_fmt = ext_lcd->lcd_spc_val3;
pctrl->vbyone_cfg.phy_vswing = ext_lcd->lcd_spc_val4;
pctrl->vbyone_cfg.phy_preem = ext_lcd->lcd_spc_val5;
if (ext_lcd->lcd_spc_val8 == Rsv_val ||
ext_lcd->lcd_spc_val9 == Rsv_val) {
pctrl->vbyone_cfg.hw_filter_time = 0;
pctrl->vbyone_cfg.hw_filter_cnt = 0;
} else {
pctrl->vbyone_cfg.hw_filter_time =
ext_lcd->lcd_spc_val8;
pctrl->vbyone_cfg.hw_filter_cnt =
ext_lcd->lcd_spc_val9;
}
pctrl->vbyone_cfg.ctrl_flag = 0;
pctrl->vbyone_cfg.power_on_reset_delay =
VX1_PWR_ON_RESET_DLY_DFT;
pctrl->vbyone_cfg.hpd_data_delay =
VX1_HPD_DATA_DELAY_DFT;
pctrl->vbyone_cfg.cdr_training_hold =
VX1_CDR_TRAINING_HOLD_DFT;
break;
case LCD_MLVDS:
pctrl->mlvds_cfg.channel_num = ext_lcd->lcd_spc_val0;
pctrl->mlvds_cfg.channel_sel0 = ext_lcd->lcd_spc_val1;
pctrl->mlvds_cfg.channel_sel1 = ext_lcd->lcd_spc_val2;
pctrl->mlvds_cfg.clk_phase = ext_lcd->lcd_spc_val3;
pctrl->mlvds_cfg.pn_swap = ext_lcd->lcd_spc_val4;
pctrl->mlvds_cfg.bit_swap = ext_lcd->lcd_spc_val5;
pctrl->mlvds_cfg.phy_vswing = ext_lcd->lcd_spc_val6;
pctrl->mlvds_cfg.phy_preem = ext_lcd->lcd_spc_val7;
break;
case LCD_P2P:
pctrl->p2p_cfg.p2p_type = ext_lcd->lcd_spc_val0;
pctrl->p2p_cfg.lane_num = ext_lcd->lcd_spc_val1;
pctrl->p2p_cfg.channel_sel0 = ext_lcd->lcd_spc_val2;
pctrl->p2p_cfg.channel_sel1 = ext_lcd->lcd_spc_val3;
pctrl->p2p_cfg.pn_swap = ext_lcd->lcd_spc_val4;
pctrl->p2p_cfg.bit_swap = ext_lcd->lcd_spc_val5;
pctrl->p2p_cfg.phy_vswing = ext_lcd->lcd_spc_val6;
pctrl->p2p_cfg.phy_preem = ext_lcd->lcd_spc_val7;
break;
case LCD_MIPI:
pctrl->mipi_cfg.lane_num = ext_lcd->lcd_spc_val0;
pctrl->mipi_cfg.bit_rate_max = ext_lcd->lcd_spc_val1;
pctrl->mipi_cfg.factor_numerator = ext_lcd->lcd_spc_val2;
pctrl->mipi_cfg.operation_mode_init = ext_lcd->lcd_spc_val3;
pctrl->mipi_cfg.operation_mode_display = ext_lcd->lcd_spc_val4;
pctrl->mipi_cfg.video_mode_type = ext_lcd->lcd_spc_val5;
pctrl->mipi_cfg.clk_always_hs = ext_lcd->lcd_spc_val6;
pctrl->mipi_cfg.phy_switch = ext_lcd->lcd_spc_val7;
pctrl->mipi_cfg.factor_denominator = 100;
pctrl->mipi_cfg.check_en = 0;
pctrl->mipi_cfg.check_reg = 0xff;
pctrl->mipi_cfg.check_cnt = 0;
#ifdef CONFIG_AML_LCD_TABLET
lcd_mipi_dsi_init_table_check_bsp(&pctrl->mipi_cfg, 1);
lcd_mipi_dsi_init_table_check_bsp(&pctrl->mipi_cfg, 0);
#endif
if (ext_lcd->lcd_spc_val9 == Rsv_val)
pctrl->mipi_cfg.extern_init = 0xff;
else
pctrl->mipi_cfg.extern_init = ext_lcd->lcd_spc_val9;
#ifdef CONFIG_AML_LCD_TABLET
mipi_dsi_config_init(pconf);
#endif
break;
case LCD_EDP:
pctrl->edp_cfg.max_lane_count = ext_lcd->lcd_spc_val0;
pctrl->edp_cfg.max_link_rate = ext_lcd->lcd_spc_val1;
pctrl->edp_cfg.training_mode = ext_lcd->lcd_spc_val2;
pctrl->edp_cfg.dpcd_caps_en = ext_lcd->lcd_spc_val3;
pctrl->edp_cfg.sync_clk_mode = ext_lcd->lcd_spc_val4;
pctrl->edp_cfg.scramb_mode = ext_lcd->lcd_spc_val5;
pctrl->edp_cfg.enhanced_framing_en = ext_lcd->lcd_spc_val6;
pctrl->edp_cfg.edid_en = ext_lcd->lcd_spc_val7;
pctrl->edp_cfg.pn_swap = 0;
pctrl->edp_cfg.phy_vswing = ext_lcd->lcd_spc_val8;
pctrl->edp_cfg.phy_preem = ext_lcd->lcd_spc_val9;
pctrl->edp_cfg.lane_count = pctrl->edp_cfg.max_lane_count;
pctrl->edp_cfg.link_rate = pctrl->edp_cfg.max_link_rate;
break;
default:
break;
}
i = 0;
while (i < LCD_PWR_STEP_MAX) {
power_step = &ext_lcd->power_on_step[i];
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("power_on: step %d: type=%d, index=%d, value=%d, delay=%d\n",
i, power_step->type, power_step->index,
power_step->value, power_step->delay);
}
pconf->power.power_on_step[i].type = power_step->type;
pconf->power.power_on_step[i].index = power_step->index;
pconf->power.power_on_step[i].value = power_step->value;
pconf->power.power_on_step[i].delay = power_step->delay;
if (power_step->type >= LCD_POWER_TYPE_MAX)
break;
i++;
}
i = 0;
while (i < LCD_PWR_STEP_MAX) {
power_step = &ext_lcd->power_off_step[i];
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("power_off: step %d: type=%d, index=%d, value=%d, delay=%d\n",
i, power_step->type, power_step->index,
power_step->value, power_step->delay);
}
pconf->power.power_off_step[i].type = power_step->type;
pconf->power.power_off_step[i].index = power_step->index;
pconf->power.power_off_step[i].value = power_step->value;
pconf->power.power_off_step[i].delay = power_step->delay;
if (power_step->type >= LCD_POWER_TYPE_MAX)
break;
i++;
}
#ifdef CONFIG_AML_LCD_BACKLIGHT
aml_bl_index_add(pdrv->index, 0);
#endif
return 0;
}
int lcd_get_config(char *dt_addr, int load_id, struct aml_lcd_drv_s *pdrv)
{
int ret;
if (load_id & 0x10)
ret = lcd_config_load_from_unifykey(pdrv);
else if (load_id & 0x1)
ret = lcd_config_load_from_dts(dt_addr, pdrv);
else
ret = lcd_config_load_from_bsp(pdrv);
if (ret)
return -1;
lcd_config_load_print(pdrv);
lcd_pinmux_load_config(pdrv);
#ifdef CONFIG_AML_LCD_TCON
lcd_tcon_probe(dt_addr, pdrv, load_id);
#endif
return 0;
}
static unsigned int vbyone_lane_num[] = {
1,
2,
4,
8,
8,
};
#define VBYONE_BIT_RATE_MAX 3100 //MHz
#define VBYONE_BIT_RATE_MIN 600
void lcd_vbyone_config_set(struct aml_lcd_drv_s *pdrv)
{
struct lcd_config_s *pconf = &pdrv->config;
unsigned int band_width, bit_rate, pclk, phy_div;
unsigned int byte_mode, lane_count, minlane;
unsigned int temp, i;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s\n", pdrv->index, __func__);
//auto calculate bandwidth, clock
lane_count = pconf->control.vbyone_cfg.lane_count;
byte_mode = pconf->control.vbyone_cfg.byte_mode;
/* byte_mode * byte2bit * 8/10_encoding * pclk =
byte_mode * 8 * 10 / 8 * pclk */
pclk = pconf->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->control.vbyone_cfg.lane_count = lane_count;
LCDPR("change to min lane_num %d\n", minlane);
}
bit_rate = band_width / lane_count;
phy_div = lane_count / lane_count;
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->control.vbyone_cfg.phy_div = phy_div;
pconf->timing.bit_rate = bit_rate;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("lane_count=%u, bit_rate = %uMHz, pclk=%u.%03uMhz\n",
lane_count, (bit_rate / 1000000),
(pclk / 1000), (pclk % 1000));
}
}
void lcd_mlvds_config_set(struct aml_lcd_drv_s *pdrv)
{
struct lcd_config_s *pconf = &pdrv->config;
unsigned int bit_rate, pclk;
unsigned int lcd_bits, channel_num;
unsigned int channel_sel0, channel_sel1, pi_clk_sel = 0;
unsigned int i, temp;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s\n", pdrv->index, __func__);
lcd_bits = pconf->basic.lcd_bits;
channel_num = pconf->control.mlvds_cfg.channel_num;
pclk = pconf->timing.lcd_clk / 1000;
bit_rate = lcd_bits * 3 * pclk / channel_num;
pconf->timing.bit_rate = bit_rate * 1000;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("channel_num=%u, bit_rate=%u.%03uMHz, pclk=%u.%03uMhz\n",
channel_num, (bit_rate / 1000), (bit_rate % 1000),
(pclk / 1000), (pclk % 1000));
}
/* pi_clk select */
channel_sel0 = pconf->control.mlvds_cfg.channel_sel0;
channel_sel1 = pconf->control.mlvds_cfg.channel_sel1;
/* mlvds channel: //tx 12 channels
* 0: clk_a
* 1: d0_a
* 2: d1_a
* 3: d2_a
* 4: d3_a
* 5: d4_a
* 6: clk_b
* 7: d0_b
* 8: d1_b
* 9: d2_b
* 10: d3_b
* 11: d4_b
*/
for (i = 0; i < 8; i++) {
temp = (channel_sel0 >> (i * 4)) & 0xf;
if (temp == 0 || temp == 6)
pi_clk_sel |= (1 << i);
}
for (i = 0; i < 4; i++) {
temp = (channel_sel1 >> (i * 4)) & 0xf;
if (temp == 0 || temp == 6)
pi_clk_sel |= (1 << (i + 8));
}
pconf->control.mlvds_cfg.pi_clk_sel = pi_clk_sel;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("channel_sel0=0x%08x, channel_sel1=0x%08x, pi_clk_sel=0x%03x\n",
channel_sel0, channel_sel1, pi_clk_sel);
}
}
void lcd_p2p_config_set(struct aml_lcd_drv_s *pdrv)
{
struct lcd_config_s *pconf = &pdrv->config;
unsigned int bit_rate, pclk;
unsigned int lcd_bits, lane_num;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s\n", pdrv->index, __func__);
lcd_bits = pconf->basic.lcd_bits;
lane_num = pconf->control.p2p_cfg.lane_num;
pclk = pconf->timing.lcd_clk / 1000;
switch (pconf->control.p2p_cfg.p2p_type) {
case P2P_CEDS:
if (pclk >= 600000)
bit_rate = pclk * 3 * lcd_bits / lane_num;
else
bit_rate = pclk * (3 * lcd_bits + 4) / lane_num;
break;
case P2P_CHPI: /* 8/10 coding */
bit_rate = (pclk * 3 * lcd_bits * 10 / 8) / lane_num;
break;
default:
bit_rate = pclk * 3 * lcd_bits / lane_num;
break;
}
pconf->timing.bit_rate = bit_rate * 1000;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("lane_num=%u, bit_rate=%u.%03uMHz, pclk=%u.%03uMhz\n",
lane_num, (bit_rate / 1000), (bit_rate % 1000),
(pclk / 1000), (pclk % 1000));
}
}
void lcd_mipi_dsi_config_set(struct aml_lcd_drv_s *pdrv)
{
struct lcd_config_s *pconf = &pdrv->config;
unsigned int pclk, bit_rate;
unsigned int bit_rate_max, bit_rate_min, pll_out_fmin = 0;
struct dsi_config_s *dconf = &pconf->control.mipi_cfg;
struct lcd_clk_config_s *cconf;
unsigned int temp, n;
dconf = &pconf->control.mipi_cfg;
/* unit in kHz for calculation */
cconf = get_lcd_clk_config(pdrv);
if (cconf && cconf->data)
pll_out_fmin = cconf->data->pll_out_fmin;
pclk = pconf->timing.lcd_clk / 1000;
/* 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 * dconf->data_bits;
bit_rate = temp / dconf->lane_num;
} else {
temp = pclk * 3 * dconf->data_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_BIT_RATE_MAX)
dconf->bit_rate_max = MIPI_BIT_RATE_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("invalid mipi-dsi bit_rate %dMHz (min=%dMHz)\n",
dconf->bit_rate_max, (pll_out_fmin / 1000));
}
if (dconf->bit_rate_max > MIPI_BIT_RATE_MAX) {
LCDPR("[warning]: invalid mipi-dsi bit_rate_max %dMHz (max=%dMHz)\n",
dconf->bit_rate_max, MIPI_BIT_RATE_MAX);
}
}
}
void lcd_edp_config_set(struct aml_lcd_drv_s *pdrv)
{
//todo
}
void lcd_timing_init_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->basic.lcd_type) {
case LCD_TTL:
h_delay = TTL_DELAY;
break;
default:
h_delay = 0;
break;
}
h_period = pconf->basic.h_period;
v_period = pconf->basic.v_period;
h_active = pconf->basic.h_active;
v_active = pconf->basic.v_active;
hsync_bp = pconf->timing.hsync_bp;
hsync_width = pconf->timing.hsync_width;
vsync_bp = pconf->timing.vsync_bp;
vsync_width = pconf->timing.vsync_width;
de_hstart = hsync_bp + hsync_width;
de_vstart = vsync_bp + vsync_width;
pconf->timing.video_on_pixel = de_hstart - h_delay;
pconf->timing.video_on_line = de_vstart;
pconf->timing.de_hs_addr = de_hstart;
pconf->timing.de_he_addr = de_hstart + h_active;
pconf->timing.de_vs_addr = de_vstart;
pconf->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->timing.hs_hs_addr = hstart;
pconf->timing.hs_he_addr = hend;
pconf->timing.hs_vs_addr = 0;
pconf->timing.hs_ve_addr = v_period - 1;
pconf->timing.vs_hs_addr = (hstart + h_period) % h_period;
pconf->timing.vs_he_addr = pconf->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->timing.vs_vs_addr = vstart;
pconf->timing.vs_ve_addr = vend;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("hs_hs_addr=%d, hs_he_addr=%d, hs_vs_addr=%d, hs_ve_addr=%d\n",
pconf->timing.hs_hs_addr, pconf->timing.hs_he_addr,
pconf->timing.hs_vs_addr, pconf->timing.hs_ve_addr);
LCDPR("vs_hs_addr=%d, vs_he_addr=%d, vs_vs_addr=%d, vs_ve_addr=%d\n",
pconf->timing.vs_hs_addr, pconf->timing.vs_he_addr,
pconf->timing.vs_vs_addr, pconf->timing.vs_ve_addr);
}
}
int lcd_vmode_change(struct lcd_config_s *pconf)
{
unsigned char type = pconf->timing.fr_adjust_type;
/* use default value to avoid offset */
unsigned int pclk = pconf->timing.lcd_clk_dft;
unsigned int h_period = pconf->timing.h_period_dft;
unsigned int v_period = pconf->timing.v_period_dft;
unsigned int pclk_min = pconf->basic.lcd_clk_min;
unsigned int pclk_max = pconf->basic.lcd_clk_max;
unsigned int duration_num = pconf->timing.sync_duration_num;
unsigned int duration_den = pconf->timing.sync_duration_den;
char str[100];
int len = 0;
pconf->timing.clk_change = 0; /* clear clk flag */
switch (type) {
case 0: /* pixel clk adjust */
pclk = (h_period * v_period) / duration_den * duration_num;
if (pconf->timing.lcd_clk != pclk)
pconf->timing.clk_change = LCD_CLK_PLL_CHANGE;
break;
case 1: /* htotal adjust */
h_period = ((pclk / v_period) * duration_den * 100) / duration_num;
h_period = (h_period + 99) / 100; /* round off */
if (pconf->basic.h_period != h_period) {
/* check clk frac update */
pclk = (h_period * v_period) / duration_den * duration_num;
if (pconf->timing.lcd_clk != pclk)
pconf->timing.clk_change = LCD_CLK_FRAC_UPDATE;
}
break;
case 2: /* vtotal adjust */
v_period = ((pclk / h_period) * duration_den * 100) / duration_num;
v_period = (v_period + 99) / 100; /* round off */
if (pconf->basic.v_period != v_period) {
/* check clk frac update */
pclk = (h_period * v_period) / duration_den * duration_num;
if (pconf->timing.lcd_clk != pclk)
pconf->timing.clk_change = LCD_CLK_FRAC_UPDATE;
}
break;
case 3: /* free adjust, use min/max range to calculate */
v_period = ((pclk / h_period) * duration_den * 100) / duration_num;
v_period = (v_period + 99) / 100; /* round off */
if (v_period > pconf->basic.v_period_max) {
v_period = pconf->basic.v_period_max;
h_period = ((pclk / v_period) * duration_den * 100) / duration_num;
h_period = (h_period + 99) / 100; /* round off */
if (h_period > pconf->basic.h_period_max) {
h_period = pconf->basic.h_period_max;
pclk = (h_period * v_period) / duration_den * duration_num;
if (pconf->timing.lcd_clk != pclk) {
if (pclk > pclk_max) {
pclk = pclk_max;
LCDERR("invalid vmode\n");
return -1;
}
pconf->timing.clk_change = LCD_CLK_PLL_CHANGE;
}
}
} else if (v_period < pconf->basic.v_period_min) {
v_period = pconf->basic.v_period_min;
h_period = ((pclk / v_period) * duration_den * 100) / duration_num;
h_period = (h_period + 99) / 100; /* round off */
if (h_period < pconf->basic.h_period_min) {
h_period = pconf->basic.h_period_min;
pclk = (h_period * v_period) / duration_den * duration_num;
if (pconf->timing.lcd_clk != pclk) {
if (pclk < pclk_min) {
pclk = pclk_min;
LCDERR("invalid vmode\n");
return -1;
}
pconf->timing.clk_change = LCD_CLK_PLL_CHANGE;
}
}
}
/* check clk frac update */
if ((pconf->timing.clk_change & LCD_CLK_PLL_CHANGE) == 0) {
pclk = (h_period * v_period) / duration_den * duration_num;
if (pconf->timing.lcd_clk != pclk)
pconf->timing.clk_change = LCD_CLK_FRAC_UPDATE;
}
break;
case 4: /* hdmi mode */
if ((duration_num / duration_den) == 59) {
/* pixel clk adjust */
pclk = (h_period * v_period) / duration_den * duration_num;
if (pconf->timing.lcd_clk != pclk)
pconf->timing.clk_change = LCD_CLK_PLL_CHANGE;
} else {
/* htotal adjust */
h_period = ((pclk / v_period) * duration_den * 100) / duration_num;
h_period = (h_period + 99) / 100; /* round off */
if (pconf->basic.h_period != h_period) {
/* check clk frac update */
pclk = (h_period * v_period) / duration_den * duration_num;
if (pconf->timing.lcd_clk != pclk)
pconf->timing.clk_change = LCD_CLK_FRAC_UPDATE;
}
}
break;
default:
LCDERR("%s: invalid fr_adjust_type: %d\n", __func__, type);
return 0;
}
if (pconf->basic.v_period != v_period) {
len += sprintf(str+len, "v_period %u->%u",
pconf->basic.v_period, v_period);
/* update v_period */
pconf->basic.v_period = v_period;
}
if (pconf->basic.h_period != h_period) {
if (len > 0)
len += sprintf(str+len, ", ");
len += sprintf(str+len, "h_period %u->%u",
pconf->basic.h_period, h_period);
/* update h_period */
pconf->basic.h_period = h_period;
}
if (pconf->timing.lcd_clk != pclk) {
if (len > 0)
len += sprintf(str+len, ", ");
len += sprintf(str+len, "pclk %u.%03uMHz->%u.%03uMHz",
(pconf->timing.lcd_clk / 1000000),
((pconf->timing.lcd_clk / 1000) % 1000),
(pclk / 1000000), ((pclk / 1000) % 1000));
pconf->timing.lcd_clk = pclk;
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
if (len > 0)
LCDPR("%s: %s\n", __func__, str);
}
return 0;
}
void lcd_pinmux_set(struct aml_lcd_drv_s *pdrv, int status)
{
struct lcd_config_s *pconf;
int i;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("[%d]: %s: %d\n", pdrv->index, __func__, status);
pconf = &pdrv->config;
if (status) {
i = 0;
while (i < LCD_PINMUX_NUM) {
if (pconf->pinmux_clr[i][0] == LCD_PINMUX_END)
break;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("pinmux_clr: %d, 0x%08x\n",
pconf->pinmux_clr[i][0],
pconf->pinmux_clr[i][1]);
}
lcd_pinmux_clr_mask(pconf->pinmux_clr[i][0],
pconf->pinmux_clr[i][1]);
i++;
}
i = 0;
while (i < LCD_PINMUX_NUM) {
if (pconf->pinmux_set[i][0] == LCD_PINMUX_END)
break;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("pinmux_set: %d, 0x%08x\n",
pconf->pinmux_set[i][0],
pconf->pinmux_set[i][1]);
}
lcd_pinmux_set_mask(pconf->pinmux_set[i][0],
pconf->pinmux_set[i][1]);
i++;
}
} else {
i = 0;
while (i < LCD_PINMUX_NUM) {
if (pconf->pinmux_set[i][0] == LCD_PINMUX_END)
break;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("pinmux_clr: %d, 0x%08x\n",
pconf->pinmux_set[i][0],
pconf->pinmux_set[i][1]);
}
lcd_pinmux_clr_mask(pconf->pinmux_set[i][0],
pconf->pinmux_set[i][1]);
i++;
}
}
}