blob: 4fa4a3389844bea40f4cc7f627c8fafa0a147dd2 [file] [log] [blame]
/*
* drivers/display/lcd/lcd_tablet/lcd_tablet.c
*
* Copyright (C) 2015 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 <common.h>
#include <malloc.h>
#include <asm/arch/gpio.h>
#ifdef CONFIG_OF_LIBFDT
#include <libfdt.h>
#endif
#include <amlogic/aml_lcd.h>
#include "../aml_lcd_reg.h"
#include "../aml_lcd_common.h"
#include "lcd_tablet.h"
#include "mipi_dsi_util.h"
static int check_lcd_output_mode(char *mode)
{
if (strcmp(mode, "panel") != 0) {
LCDERR("outputmode[%s] is not support\n", mode);
return -1;
}
return 0;
}
static void lcd_list_support_mode(void)
{
printf("panel\n");
}
static void lcd_config_load_print(struct lcd_config_s *pconf)
{
if (lcd_debug_print_flag == 0)
return;
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);
LCDPR("h_period = %d\n", pconf->lcd_basic.h_period);
LCDPR("v_period = %d\n", pconf->lcd_basic.v_period);
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);
LCDPR("clk = %dHz\n", pconf->lcd_timing.lcd_clk);
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) {
if (pconf->lcd_control.mipi_config->check_en) {
LCDPR("check_reg = 0x%02x\n",
pconf->lcd_control.mipi_config->check_reg);
LCDPR("check_cnt = %d\n",
pconf->lcd_control.mipi_config->check_cnt);
}
LCDPR("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("pclk_lanebyteclk_factor = %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_disp = %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_always_hs = %d\n",
pconf->lcd_control.mipi_config->clk_always_hs);
LCDPR("phy_switch = %d\n",
pconf->lcd_control.mipi_config->phy_switch);
LCDPR("extern_init = %d\n",
pconf->lcd_control.mipi_config->extern_init);
}
}
#ifdef CONFIG_OF_LIBFDT
static int lcd_config_load_from_dts(char *dt_addr, struct lcd_config_s *pconf)
{
int parent_offset;
int child_offset;
char propname[30];
char *propdata;
unsigned int temp;
int len;
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;
}
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "pinctrl_version", NULL);
if (propdata) {
pconf->pinctrl_ver = (unsigned char)(be32_to_cpup((u32*)propdata));
} else {
pconf->pinctrl_ver = 0;
}
LCDPR("pinctrl_version: %d\n", pconf->pinctrl_ver);
/* check panel_type */
char *panel_type = getenv("panel_type");
if (panel_type == NULL) {
LCDERR("no panel_type, use default lcd config\n ");
return -1;
}
LCDPR("use panel_type=%s\n", panel_type);
sprintf(propname, "/lcd/%s", panel_type);
child_offset = fdt_path_offset(dt_addr, propname);
if (child_offset < 0) {
LCDERR("not find /lcd/%s node: %s\n",
panel_type, fdt_strerror(child_offset));
return -1;
}
propdata = (char *)fdt_getprop(dt_addr, child_offset, "model_name", NULL);
if (propdata == NULL) {
LCDERR("failed to get model_name\n");
strcpy(pconf->lcd_basic.model_name, panel_type);
} else {
strcpy(pconf->lcd_basic.model_name, propdata);
}
propdata = (char *)fdt_getprop(dt_addr, child_offset, "interface", NULL);
if (propdata == NULL) {
LCDERR("failed to get interface\n");
return -1;
} else {
pconf->lcd_basic.lcd_type = lcd_type_str_to_type(propdata);
}
propdata = (char *)fdt_getprop(dt_addr, child_offset, "basic_setting", NULL);
if (propdata == NULL) {
LCDERR("failed to get basic_setting\n");
return -1;
} else {
pconf->lcd_basic.h_active = be32_to_cpup((u32*)propdata);
pconf->lcd_basic.v_active = be32_to_cpup((((u32*)propdata)+1));
pconf->lcd_basic.h_period = be32_to_cpup((((u32*)propdata)+2));
pconf->lcd_basic.v_period = be32_to_cpup((((u32*)propdata)+3));
pconf->lcd_basic.lcd_bits = be32_to_cpup((((u32*)propdata)+4));
pconf->lcd_basic.screen_width = be32_to_cpup((((u32*)propdata)+5));
pconf->lcd_basic.screen_height = be32_to_cpup((((u32*)propdata)+6));
}
propdata = (char *)fdt_getprop(dt_addr, child_offset, "range_setting", NULL);
if (propdata == NULL) {
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 = be32_to_cpup((u32*)propdata);
pconf->lcd_basic.h_period_max = be32_to_cpup((((u32*)propdata)+1));
pconf->lcd_basic.v_period_min = be32_to_cpup((((u32*)propdata)+2));
pconf->lcd_basic.v_period_max = be32_to_cpup((((u32*)propdata)+3));
pconf->lcd_basic.lcd_clk_min = be32_to_cpup((((u32*)propdata)+4));
pconf->lcd_basic.lcd_clk_max = be32_to_cpup((((u32*)propdata)+5));
}
propdata = (char *)fdt_getprop(dt_addr, child_offset, "lcd_timing", NULL);
if (propdata == NULL) {
LCDERR("failed to get lcd_timing\n");
return -1;
} else {
pconf->lcd_timing.hsync_width = (unsigned short)(be32_to_cpup((u32*)propdata));
pconf->lcd_timing.hsync_bp = (unsigned short)(be32_to_cpup((((u32*)propdata)+1)));
pconf->lcd_timing.hsync_pol = (unsigned short)(be32_to_cpup((((u32*)propdata)+2)));
pconf->lcd_timing.vsync_width = (unsigned short)(be32_to_cpup((((u32*)propdata)+3)));
pconf->lcd_timing.vsync_bp = (unsigned short)(be32_to_cpup((((u32*)propdata)+4)));
pconf->lcd_timing.vsync_pol = (unsigned short)(be32_to_cpup((((u32*)propdata)+5)));
}
propdata = (char *)fdt_getprop(dt_addr, child_offset, "clk_attr", NULL);
if (propdata == NULL) {
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)(be32_to_cpup((u32*)propdata));
pconf->lcd_timing.ss_level = (unsigned char)(be32_to_cpup((((u32*)propdata)+1)));
pconf->lcd_timing.clk_auto = (unsigned char)(be32_to_cpup((((u32*)propdata)+2)));
temp = be32_to_cpup((((u32*)propdata)+3));
if (temp > 0) {
pconf->lcd_timing.lcd_clk = temp;
} 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) {
propdata = (char *)fdt_getprop(dt_addr, child_offset, "clk_para", NULL);
if (propdata == NULL) {
LCDERR("failed to get clk_para\n");
pconf->lcd_timing.pll_ctrl = 0x00140248;
pconf->lcd_timing.div_ctrl = 0x00000901;
pconf->lcd_timing.clk_ctrl = 0x000000c0;
} else {
pconf->lcd_timing.pll_ctrl = be32_to_cpup((u32*)propdata);
pconf->lcd_timing.div_ctrl = be32_to_cpup((((u32*)propdata)+1));
pconf->lcd_timing.clk_ctrl = be32_to_cpup((((u32*)propdata)+2));
}
}
switch (pconf->lcd_basic.lcd_type) {
case LCD_TTL:
propdata = (char *)fdt_getprop(dt_addr, child_offset, "ttl_attr", NULL);
if (propdata == NULL) {
LCDERR("failed to get ttl_attr\n");
} else {
pconf->lcd_control.ttl_config->clk_pol = be32_to_cpup((u32*)propdata);
pconf->lcd_control.ttl_config->sync_valid =
(((be32_to_cpup((((u32*)propdata)+1))) << 1) |
((be32_to_cpup((((u32*)propdata)+2))) << 0));
pconf->lcd_control.ttl_config->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 == NULL) {
LCDERR("failed to get lvds_attr\n");
} else {
len = len / 4;
if (len == 5) {
pconf->lcd_control.lvds_config->lvds_repack = be32_to_cpup((u32*)propdata);
pconf->lcd_control.lvds_config->dual_port = be32_to_cpup((((u32*)propdata)+1));
pconf->lcd_control.lvds_config->pn_swap = be32_to_cpup((((u32*)propdata)+2));
pconf->lcd_control.lvds_config->port_swap = be32_to_cpup((((u32*)propdata)+3));
pconf->lcd_control.lvds_config->lane_reverse = be32_to_cpup((((u32*)propdata)+4));
} else if (len == 4) {
pconf->lcd_control.lvds_config->lvds_repack = be32_to_cpup((u32*)propdata);
pconf->lcd_control.lvds_config->dual_port = be32_to_cpup((((u32*)propdata)+1));
pconf->lcd_control.lvds_config->pn_swap = be32_to_cpup((((u32*)propdata)+2));
pconf->lcd_control.lvds_config->port_swap = be32_to_cpup((((u32*)propdata)+3));
pconf->lcd_control.lvds_config->lane_reverse = 0;
} else {
LCDERR("invalid lvds_attr parameters cnt: %d\n", len);
}
}
propdata = (char *)fdt_getprop(dt_addr, child_offset, "phy_attr", &len);
if (propdata == NULL) {
if (lcd_debug_print_flag)
LCDPR("failed to get phy_attr\n");
pconf->lcd_control.lvds_config->phy_vswing = LVDS_PHY_VSWING_DFT;
pconf->lcd_control.lvds_config->phy_preem = LVDS_PHY_PREEM_DFT;
} else {
len = len / 4;
if (len == 4) {
pconf->lcd_control.lvds_config->phy_vswing = be32_to_cpup((u32*)propdata);
pconf->lcd_control.lvds_config->phy_preem = be32_to_cpup((((u32*)propdata)+1));
pconf->lcd_control.lvds_config->phy_clk_vswing = be32_to_cpup((((u32*)propdata)+2));
pconf->lcd_control.lvds_config->phy_clk_preem = be32_to_cpup((((u32*)propdata)+3));
if (lcd_debug_print_flag) {
LCDPR("set phy vswing=0x%x, preemphasis=0x%x\n",
pconf->lcd_control.lvds_config->phy_vswing,
pconf->lcd_control.lvds_config->phy_preem);
LCDPR("set phy clk_vswing=0x%x, clk_preemphasis=0x%x\n",
pconf->lcd_control.lvds_config->phy_clk_vswing,
pconf->lcd_control.lvds_config->phy_clk_preem);
}
} else if (len == 2) {
pconf->lcd_control.lvds_config->phy_vswing = be32_to_cpup((u32*)propdata);
pconf->lcd_control.lvds_config->phy_preem = be32_to_cpup((((u32*)propdata)+1));
pconf->lcd_control.lvds_config->phy_clk_vswing = LVDS_PHY_CLK_VSWING_DFT;
pconf->lcd_control.lvds_config->phy_clk_preem = LVDS_PHY_CLK_PREEM_DFT;
if (lcd_debug_print_flag) {
LCDPR("set phy vswing=0x%x, preemphasis=0x%x\n",
pconf->lcd_control.lvds_config->phy_vswing,
pconf->lcd_control.lvds_config->phy_preem);
}
} else {
LCDERR("invalid phy_attr parameters cnt: %d\n", len);
}
}
break;
case LCD_VBYONE:
propdata = (char *)fdt_getprop(dt_addr, child_offset, "vbyone_attr", NULL);
if (propdata == NULL) {
LCDERR("failed to get vbyone_attr\n");
} else {
pconf->lcd_control.vbyone_config->lane_count = be32_to_cpup((u32*)propdata);
pconf->lcd_control.vbyone_config->region_num = be32_to_cpup((((u32*)propdata)+1));
pconf->lcd_control.vbyone_config->byte_mode = be32_to_cpup((((u32*)propdata)+2));
pconf->lcd_control.vbyone_config->color_fmt = be32_to_cpup((((u32*)propdata)+3));
}
propdata = (char *)fdt_getprop(dt_addr, child_offset, "phy_attr", NULL);
if (propdata == NULL) {
if (lcd_debug_print_flag)
LCDPR("failed to get phy_attr\n");
pconf->lcd_control.vbyone_config->phy_vswing = VX1_PHY_VSWING_DFT;
pconf->lcd_control.vbyone_config->phy_preem = VX1_PHY_PREEM_DFT;
} else {
pconf->lcd_control.vbyone_config->phy_vswing = be32_to_cpup((u32*)propdata);
pconf->lcd_control.vbyone_config->phy_preem = be32_to_cpup((((u32*)propdata)+1));
if (lcd_debug_print_flag) {
LCDPR("set phy vswing=%d, preemphasis=%d\n",
pconf->lcd_control.vbyone_config->phy_vswing,
pconf->lcd_control.vbyone_config->phy_preem);
}
}
break;
case LCD_MIPI:
propdata = (char *)fdt_getprop(dt_addr, child_offset, "mipi_attr", NULL);
if (propdata == NULL) {
LCDERR("failed to get mipi_attr\n");
} else {
pconf->lcd_control.mipi_config->lane_num = be32_to_cpup((u32*)propdata);
pconf->lcd_control.mipi_config->bit_rate_max = be32_to_cpup((((u32*)propdata)+1));
pconf->lcd_control.mipi_config->factor_numerator = be32_to_cpup((((u32*)propdata)+2));
pconf->lcd_control.mipi_config->factor_denominator = 100;
pconf->lcd_control.mipi_config->operation_mode_init = be32_to_cpup((((u32*)propdata)+3));
pconf->lcd_control.mipi_config->operation_mode_display = be32_to_cpup((((u32*)propdata)+4));
pconf->lcd_control.mipi_config->video_mode_type = be32_to_cpup((((u32*)propdata)+5));
pconf->lcd_control.mipi_config->clk_always_hs = be32_to_cpup((((u32*)propdata)+6));
pconf->lcd_control.mipi_config->phy_switch = be32_to_cpup((((u32*)propdata)+7));
}
lcd_mipi_dsi_init_table_detect(dt_addr, child_offset, pconf->lcd_control.mipi_config, 1);
lcd_mipi_dsi_init_table_detect(dt_addr, child_offset, pconf->lcd_control.mipi_config, 0);
propdata = (char *)fdt_getprop(dt_addr, child_offset, "extern_init", NULL);
if (propdata == NULL) {
LCDERR("failed to get extern_init\n");
} else {
pconf->lcd_control.mipi_config->extern_init = be32_to_cpup((u32*)propdata);
}
break;
default:
LCDERR("invalid lcd type\n");
break;
}
/* check power_step */
lcd_power_load_from_dts(pconf, dt_addr, child_offset);
propdata = (char *)fdt_getprop(dt_addr, child_offset, "backlight_index", NULL);
if (propdata == NULL) {
LCDERR("failed to get backlight_index\n");
pconf->backlight_index = 0xff;
return 0;
} else {
pconf->backlight_index = be32_to_cpup((u32*)propdata);
}
return 0;
}
#endif
static int lcd_config_load_from_bsp(struct lcd_config_s *pconf)
{
struct ext_lcd_config_s *ext_lcd = NULL;
char *panel_type = getenv("panel_type");
unsigned int i, j;
unsigned int temp;
struct lcd_power_step_s *power_step;
if (panel_type == NULL) {
LCDERR("no panel_type, use default lcd config\n ");
return -1;
}
for (i = 0 ; i < LCD_NUM_MAX ; i++) {
ext_lcd = &ext_lcd_config[i];
if (strcmp(ext_lcd->panel_type, panel_type) == 0)
break;
if (strcmp(ext_lcd->panel_type, "invalid") == 0) {
i = LCD_NUM_MAX;
break;
}
}
if (i >= LCD_NUM_MAX) {
LCDERR("can't find %s, use default lcd config\n ", panel_type);
return -1;
}
LCDPR("use panel_type=%s\n", panel_type);
strcpy(pconf->lcd_basic.model_name, panel_type);
pconf->lcd_basic.lcd_type = ext_lcd->lcd_type;
pconf->lcd_basic.lcd_bits = ext_lcd->lcd_bits;
pconf->lcd_basic.h_active = ext_lcd->h_active;
pconf->lcd_basic.v_active = ext_lcd->v_active;
pconf->lcd_basic.h_period = ext_lcd->h_period;
pconf->lcd_basic.v_period = ext_lcd->v_period;
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;
pconf->lcd_timing.hsync_width = ext_lcd->hsync_width;
pconf->lcd_timing.hsync_bp = ext_lcd->hsync_bp;
pconf->lcd_timing.hsync_pol = ext_lcd->hsync_pol;
pconf->lcd_timing.vsync_width = ext_lcd->vsync_width;
pconf->lcd_timing.vsync_bp = ext_lcd->vsync_bp;
pconf->lcd_timing.vsync_pol = ext_lcd->vsync_pol;
/* fr_adjust_type */
temp = ext_lcd->customer_val_0;
if (temp == Rsv_val)
pconf->lcd_timing.fr_adjust_type = 0;
else
pconf->lcd_timing.fr_adjust_type = (unsigned char)temp;
/* ss_level */
temp = ext_lcd->customer_val_1;
if (temp == Rsv_val)
pconf->lcd_timing.ss_level = 0;
else
pconf->lcd_timing.ss_level = (unsigned char)temp;
/* clk_auto_generate */
temp = ext_lcd->customer_val_2;
if (temp == Rsv_val)
pconf->lcd_timing.clk_auto = 1;
else
pconf->lcd_timing.clk_auto = (unsigned char)temp;
/* lcd_clk */
temp = ext_lcd->customer_val_3;
if (temp == Rsv_val)
pconf->lcd_timing.lcd_clk = 60;
else
pconf->lcd_timing.lcd_clk = temp;
if (pconf->lcd_basic.lcd_type == LCD_TTL) {
pconf->lcd_control.ttl_config->clk_pol = ext_lcd->lcd_spc_val0;
pconf->lcd_control.ttl_config->sync_valid =
((ext_lcd->lcd_spc_val1 << 1) |
(ext_lcd->lcd_spc_val2 << 0));
pconf->lcd_control.ttl_config->swap_ctrl =
((ext_lcd->lcd_spc_val3 << 1) |
(ext_lcd->lcd_spc_val4 << 0));
} else if (pconf->lcd_basic.lcd_type == LCD_LVDS) {
pconf->lcd_control.lvds_config->lvds_repack = ext_lcd->lcd_spc_val0;
pconf->lcd_control.lvds_config->dual_port = ext_lcd->lcd_spc_val1;
pconf->lcd_control.lvds_config->pn_swap = ext_lcd->lcd_spc_val2;
pconf->lcd_control.lvds_config->port_swap = ext_lcd->lcd_spc_val3;
pconf->lcd_control.lvds_config->lane_reverse = ext_lcd->lcd_spc_val4;
pconf->lcd_control.lvds_config->phy_vswing = LVDS_PHY_VSWING_DFT;
pconf->lcd_control.lvds_config->phy_preem = LVDS_PHY_PREEM_DFT;
pconf->lcd_control.lvds_config->phy_clk_vswing = LVDS_PHY_CLK_VSWING_DFT;
pconf->lcd_control.lvds_config->phy_clk_preem = LVDS_PHY_CLK_PREEM_DFT;
} else if (pconf->lcd_basic.lcd_type == LCD_VBYONE) {
pconf->lcd_control.vbyone_config->lane_count = ext_lcd->lcd_spc_val0;
pconf->lcd_control.vbyone_config->region_num = ext_lcd->lcd_spc_val1;
pconf->lcd_control.vbyone_config->byte_mode = ext_lcd->lcd_spc_val2;
pconf->lcd_control.vbyone_config->color_fmt = ext_lcd->lcd_spc_val3;
pconf->lcd_control.vbyone_config->phy_vswing = VX1_PHY_VSWING_DFT;
pconf->lcd_control.vbyone_config->phy_preem = VX1_PHY_PREEM_DFT;
} else if (pconf->lcd_basic.lcd_type == LCD_MIPI) {
pconf->lcd_control.mipi_config->lane_num = ext_lcd->lcd_spc_val0;
pconf->lcd_control.mipi_config->bit_rate_max = ext_lcd->lcd_spc_val1;
pconf->lcd_control.mipi_config->factor_numerator = ext_lcd->lcd_spc_val2;
pconf->lcd_control.mipi_config->operation_mode_init = ext_lcd->lcd_spc_val3;
pconf->lcd_control.mipi_config->operation_mode_display = ext_lcd->lcd_spc_val4;
pconf->lcd_control.mipi_config->video_mode_type = ext_lcd->lcd_spc_val5;
pconf->lcd_control.mipi_config->clk_always_hs = ext_lcd->lcd_spc_val6;
pconf->lcd_control.mipi_config->phy_switch = ext_lcd->lcd_spc_val7;
pconf->lcd_control.mipi_config->factor_denominator = 100;
lcd_mipi_dsi_init_table_check_bsp(pconf->lcd_control.mipi_config, 1);
lcd_mipi_dsi_init_table_check_bsp(pconf->lcd_control.mipi_config, 0);
if (ext_lcd->lcd_spc_val9 == Rsv_val)
pconf->lcd_control.mipi_config->extern_init = 0xff;
else
pconf->lcd_control.mipi_config->extern_init = ext_lcd->lcd_spc_val9;
}
i = 0;
while (i < LCD_PWR_STEP_MAX) {
power_step = &ext_lcd->power_on_step[i];
if (lcd_debug_print_flag) {
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->lcd_power->power_on_step[i].type = power_step->type;
pconf->lcd_power->power_on_step[i].index = power_step->index;
pconf->lcd_power->power_on_step[i].value = power_step->value;
pconf->lcd_power->power_on_step[i].delay = power_step->delay;
if (power_step->type >= LCD_POWER_TYPE_MAX)
break;
else
i++;
}
i = 0;
while (i < LCD_PWR_STEP_MAX) {
power_step = &ext_lcd->power_off_step[i];
if (lcd_debug_print_flag) {
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->lcd_power->power_off_step[i].type = power_step->type;
pconf->lcd_power->power_off_step[i].index = power_step->index;
pconf->lcd_power->power_off_step[i].value = power_step->value;
pconf->lcd_power->power_off_step[i].delay = power_step->delay;
if (power_step->type >= LCD_POWER_TYPE_MAX)
break;
else
i++;
}
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");
}
return 0;
}
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 = (unsigned char *)malloc(sizeof(unsigned char) * LCD_UKEY_LCD_SIZE);
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 = aml_lcd_unifykey_get("lcd", para, &key_len);
if (ret) {
free(para);
return -1;
}
/* step 1: check header */
len = LCD_UKEY_HEAD_SIZE;
ret = aml_lcd_unifykey_len_check(key_len, len);
if (ret) {
LCDERR("unifykey header length is incorrect\n");
free(para);
return -1;
}
aml_lcd_unifykey_header_check(para, &lcd_header);
LCDPR("unifykey version: 0x%04x\n", 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) {
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 = aml_lcd_unifykey_len_check(key_len, len);
if (ret) {
LCDERR("unifykey parameters length is incorrect\n");
free(para);
return -1;
}
/* basic: 36byte */
p = para;
*(p + LCD_UKEY_MODEL_NAME - 1) = '\0'; /* ensure string ending */
str = (const char *)(p + LCD_UKEY_HEAD_SIZE);
strcpy(pconf->lcd_basic.model_name, str);
pconf->lcd_basic.lcd_type = *(p + LCD_UKEY_INTERFACE);
pconf->lcd_basic.lcd_bits = *(p + LCD_UKEY_LCD_BITS);
pconf->lcd_basic.screen_width = (*(p + LCD_UKEY_SCREEN_WIDTH) |
((*(p + LCD_UKEY_SCREEN_WIDTH + 1)) << 8));
pconf->lcd_basic.screen_height = (*(p + LCD_UKEY_SCREEN_HEIGHT) |
((*(p + LCD_UKEY_SCREEN_HEIGHT + 1)) << 8));
/* timing: 18byte */
pconf->lcd_basic.h_active = (*(p + LCD_UKEY_H_ACTIVE) |
((*(p + LCD_UKEY_H_ACTIVE + 1)) << 8));
pconf->lcd_basic.v_active = (*(p + LCD_UKEY_V_ACTIVE)) |
((*(p + LCD_UKEY_V_ACTIVE + 1)) << 8);
pconf->lcd_basic.h_period = (*(p + LCD_UKEY_H_PERIOD)) |
((*(p + LCD_UKEY_H_PERIOD + 1)) << 8);
pconf->lcd_basic.v_period = (*(p + LCD_UKEY_V_PERIOD)) |
((*(p + LCD_UKEY_V_PERIOD + 1)) << 8);
pconf->lcd_timing.hsync_width = (*(p + LCD_UKEY_HS_WIDTH) |
((*(p + LCD_UKEY_HS_WIDTH + 1)) << 8));
pconf->lcd_timing.hsync_bp = (*(p + LCD_UKEY_HS_BP) | ((*(p + LCD_UKEY_HS_BP + 1)) << 8));
pconf->lcd_timing.hsync_pol = *(p + LCD_UKEY_HS_POL);
pconf->lcd_timing.vsync_width = (*(p + LCD_UKEY_VS_WIDTH) |
((*(p + LCD_UKEY_VS_WIDTH + 1)) << 8));
pconf->lcd_timing.vsync_bp = (*(p + LCD_UKEY_VS_BP) |
((*(p + LCD_UKEY_VS_BP + 1)) << 8));
pconf->lcd_timing.vsync_pol = *(p + LCD_UKEY_VS_POL);
/* customer: 31byte */
pconf->lcd_timing.fr_adjust_type = *(p + LCD_UKEY_FR_ADJ_TYPE);
pconf->lcd_timing.ss_level = *(p + LCD_UKEY_SS_LEVEL);
pconf->lcd_timing.clk_auto = *(p + LCD_UKEY_CLK_AUTO_GEN);
pconf->lcd_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));
pconf->lcd_basic.h_period_min = (*(p + LCD_UKEY_H_PERIOD_MIN) |
((*(p + LCD_UKEY_H_PERIOD_MIN + 1)) << 8));
pconf->lcd_basic.h_period_max = (*(p + LCD_UKEY_H_PERIOD_MAX) |
((*(p + LCD_UKEY_H_PERIOD_MAX + 1)) << 8));
pconf->lcd_basic.v_period_min = (*(p + LCD_UKEY_V_PERIOD_MIN) |
((*(p + LCD_UKEY_V_PERIOD_MIN + 1)) << 8));
pconf->lcd_basic.v_period_max = (*(p + LCD_UKEY_V_PERIOD_MAX) |
((*(p + LCD_UKEY_V_PERIOD_MAX + 1)) << 8));
pconf->lcd_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->lcd_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));
/* interface: 20byte */
if (pconf->lcd_basic.lcd_type == LCD_LVDS) {
if (lcd_header.version == 2) {
pconf->lcd_control.lvds_config->lvds_repack =
(*(p + LCD_UKEY_IF_ATTR_0) |
((*(p + LCD_UKEY_IF_ATTR_0 + 1)) << 8)) & 0xff;
pconf->lcd_control.lvds_config->dual_port =
(*(p + LCD_UKEY_IF_ATTR_1) |
((*(p + LCD_UKEY_IF_ATTR_1 + 1)) << 8)) & 0xff;
pconf->lcd_control.lvds_config->pn_swap =
(*(p + LCD_UKEY_IF_ATTR_2) |
((*(p + LCD_UKEY_IF_ATTR_2 + 1)) << 8)) & 0xff;
pconf->lcd_control.lvds_config->port_swap =
(*(p + LCD_UKEY_IF_ATTR_3) |
((*(p + LCD_UKEY_IF_ATTR_3 + 1)) << 8)) & 0xff;
pconf->lcd_control.lvds_config->lane_reverse =
(*(p + LCD_UKEY_IF_ATTR_4) |
((*(p + LCD_UKEY_IF_ATTR_4 + 1)) << 8)) & 0xff;
} else {
pconf->lcd_control.lvds_config->lvds_repack =
(*(p + LCD_UKEY_IF_ATTR_0) |
((*(p + LCD_UKEY_IF_ATTR_0 + 1)) << 8)) & 0xff;
pconf->lcd_control.lvds_config->dual_port =
(*(p + LCD_UKEY_IF_ATTR_1) |
((*(p + LCD_UKEY_IF_ATTR_1 + 1)) << 8)) & 0xff;
pconf->lcd_control.lvds_config->pn_swap =
(*(p + LCD_UKEY_IF_ATTR_2) |
((*(p + LCD_UKEY_IF_ATTR_2 + 1)) << 8)) & 0xff;
pconf->lcd_control.lvds_config->port_swap =
(*(p + LCD_UKEY_IF_ATTR_3) |
((*(p + LCD_UKEY_IF_ATTR_3 + 1)) << 8)) & 0xff;
pconf->lcd_control.lvds_config->phy_vswing =
(*(p + LCD_UKEY_IF_ATTR_4) |
((*(p + LCD_UKEY_IF_ATTR_4 + 1)) << 8)) & 0xff;
pconf->lcd_control.lvds_config->phy_preem =
(*(p + LCD_UKEY_IF_ATTR_5) |
((*(p + LCD_UKEY_IF_ATTR_5 + 1)) << 8)) & 0xff;
pconf->lcd_control.lvds_config->phy_clk_vswing =
(*(p + LCD_UKEY_IF_ATTR_6) |
((*(p + LCD_UKEY_IF_ATTR_6 + 1)) << 8)) & 0xff;
pconf->lcd_control.lvds_config->phy_clk_preem =
(*(p + LCD_UKEY_IF_ATTR_7) |
((*(p + LCD_UKEY_IF_ATTR_7 + 1)) << 8)) & 0xff;
pconf->lcd_control.lvds_config->lane_reverse = 0;
}
} else if (pconf->lcd_basic.lcd_type == LCD_TTL) {
pconf->lcd_control.ttl_config->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 */
pconf->lcd_control.ttl_config->sync_valid = (temp << 1);
temp = (*(p + LCD_UKEY_IF_ATTR_2) |
((*(p + LCD_UKEY_IF_ATTR_2 + 1)) << 8)) & 0x1; /* hvsync_valid */
pconf->lcd_control.ttl_config->sync_valid |= (temp << 0);
temp = (*(p + LCD_UKEY_IF_ATTR_3) |
((*(p + LCD_UKEY_IF_ATTR_3 + 1)) << 8)) & 0x1; /* rb_swap */
pconf->lcd_control.ttl_config->swap_ctrl = (temp << 1);
temp = (*(p + LCD_UKEY_IF_ATTR_4) |
((*(p + LCD_UKEY_IF_ATTR_4 + 1)) << 8)) & 0x1; /* bit_swap */
pconf->lcd_control.ttl_config->swap_ctrl |= (temp << 0);
} else if (pconf->lcd_basic.lcd_type == LCD_VBYONE) {
if (lcd_header.version == 2) {
pconf->lcd_control.vbyone_config->lane_count =
(*(p + LCD_UKEY_IF_ATTR_0) |
((*(p + LCD_UKEY_IF_ATTR_0 + 1)) << 8)) & 0xff;
pconf->lcd_control.vbyone_config->region_num =
(*(p + LCD_UKEY_IF_ATTR_1) |
((*(p + LCD_UKEY_IF_ATTR_1 + 1)) << 8)) & 0xff;
pconf->lcd_control.vbyone_config->byte_mode =
(*(p + LCD_UKEY_IF_ATTR_2) |
((*(p + LCD_UKEY_IF_ATTR_2 + 1)) << 8)) & 0xff;
pconf->lcd_control.vbyone_config->color_fmt =
(*(p + LCD_UKEY_IF_ATTR_3) |
((*(p + LCD_UKEY_IF_ATTR_3 + 1)) << 8)) & 0xff;
} else {
pconf->lcd_control.vbyone_config->lane_count =
(*(p + LCD_UKEY_IF_ATTR_0) |
((*(p + LCD_UKEY_IF_ATTR_0 + 1)) << 8)) & 0xff;
pconf->lcd_control.vbyone_config->region_num =
(*(p + LCD_UKEY_IF_ATTR_1) |
((*(p + LCD_UKEY_IF_ATTR_1 + 1)) << 8)) & 0xff;
pconf->lcd_control.vbyone_config->byte_mode =
(*(p + LCD_UKEY_IF_ATTR_2) |
((*(p + LCD_UKEY_IF_ATTR_2 + 1)) << 8)) & 0xff;
pconf->lcd_control.vbyone_config->color_fmt =
(*(p + LCD_UKEY_IF_ATTR_3) |
((*(p + LCD_UKEY_IF_ATTR_3 + 1)) << 8)) & 0xff;
pconf->lcd_control.vbyone_config->phy_vswing =
(*(p + LCD_UKEY_IF_ATTR_4) |
((*(p + LCD_UKEY_IF_ATTR_4 + 1)) << 8)) & 0xff;
pconf->lcd_control.vbyone_config->phy_preem =
(*(p + LCD_UKEY_IF_ATTR_5) |
((*(p + LCD_UKEY_IF_ATTR_5 + 1)) << 8)) & 0xff;
}
} else
LCDERR("unsupport lcd_type: %d\n", pconf->lcd_basic.lcd_type);
if (lcd_header.version == 2) {
/* phy: 10byte */ /* v2 */
if (pconf->lcd_basic.lcd_type == LCD_LVDS) {
pconf->lcd_control.lvds_config->phy_vswing = *(p +LCD_UKEY_PHY_ATTR_0);
pconf->lcd_control.lvds_config->phy_preem = *(p + LCD_UKEY_PHY_ATTR_1);
pconf->lcd_control.lvds_config->phy_clk_vswing = *(p + LCD_UKEY_PHY_ATTR_2);
pconf->lcd_control.lvds_config->phy_clk_preem = *(p + LCD_UKEY_PHY_ATTR_3);
} else if (pconf->lcd_basic.lcd_type == LCD_VBYONE) {
pconf->lcd_control.vbyone_config->phy_vswing =
(*(p + LCD_UKEY_PHY_ATTR_0) |
((*(p + LCD_UKEY_PHY_ATTR_0 + 1)) << 8)) & 0xff;
pconf->lcd_control.vbyone_config->phy_preem =
(*(p + LCD_UKEY_PHY_ATTR_1) |
((*(p + LCD_UKEY_PHY_ATTR_1 + 1)) << 8)) & 0xff;
}
}
/* step 3: check power sequence */
ret = lcd_power_load_from_unifykey(pconf, para, key_len, len);
if (ret < 0) {
free(para);
return -1;
}
free(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 h_period = pconf->lcd_basic.h_period;
unsigned int v_period = pconf->lcd_basic.v_period;
unsigned int clk = pconf->lcd_timing.lcd_clk;
unsigned int sync_duration;
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.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;
pconf->lcd_timing.sync_duration_num = sync_duration;
pconf->lcd_timing.sync_duration_den = 100;
lcd_timing_init_config(pconf);
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;
}
static int lcd_config_check(char *mode)
{
int ret;
ret = check_lcd_output_mode(mode);
if (ret)
return -1;
return 0;
}
int get_lcd_tablet_config(char *dt_addr, int load_id)
{
struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
int ret = 0;
strcpy(lcd_drv->version, LCD_DRV_VERSION);
lcd_drv->list_support_mode = lcd_list_support_mode;
lcd_drv->config_check = lcd_config_check;
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;
if (load_id & 0x10) { /* unifykey */
ret = lcd_config_load_from_unifykey(lcd_drv->lcd_config);
ret = lcd_pinmux_load_config(dt_addr, lcd_drv->lcd_config);
} else if (load_id & 0x1) { /* dts */
#ifdef CONFIG_OF_LIBFDT
ret = lcd_config_load_from_dts(dt_addr, lcd_drv->lcd_config);
#endif
ret = lcd_pinmux_load_config(dt_addr, lcd_drv->lcd_config);
} else { /* bsp */
ret = lcd_config_load_from_bsp(lcd_drv->lcd_config);
}
if (ret)
return -1;
lcd_config_init(lcd_drv->lcd_config);
lcd_config_load_print(lcd_drv->lcd_config);
return 0;
}