blob: 8750d912992ea9183dcff61f2e976c16c66f4e3d [file] [log] [blame]
/*
* drivers/amlogic/media/vout/lcd/lcd_common.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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#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},
{"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];
}
/* ***************************************
* lcd gpio
* *************************************** */
static struct lcd_cpu_gpio_s lcd_gpio[LCD_CPU_GPIO_NUM_MAX] = {
{.name = "invalid", .probe_flag = 0, .register_flag = 0,},
{.name = "invalid", .probe_flag = 0, .register_flag = 0,},
{.name = "invalid", .probe_flag = 0, .register_flag = 0,},
{.name = "invalid", .probe_flag = 0, .register_flag = 0,},
{.name = "invalid", .probe_flag = 0, .register_flag = 0,},
{.name = "invalid", .probe_flag = 0, .register_flag = 0,},
{.name = "invalid", .probe_flag = 0, .register_flag = 0,},
{.name = "invalid", .probe_flag = 0, .register_flag = 0,},
{.name = "invalid", .probe_flag = 0, .register_flag = 0,},
{.name = "invalid", .probe_flag = 0, .register_flag = 0,},
};
void lcd_cpu_gpio_info_print(void)
{
int i;
LCDPR("cpu gpio info:\n");
for (i = 0; i < LCD_CPU_GPIO_NUM_MAX; i++) {
if (strcmp(lcd_gpio[i].name, "invalid") == 0)
break;
if (lcd_gpio[i].probe_flag == 1) {
printf("%d: %s, register=%d\n",
i, lcd_gpio[i].name, lcd_gpio[i].register_flag);
}
}
printf("\n");
}
int lcd_gpio_probe(const char *name, int index)
{
if (name == NULL) {
LCDERR("%s: gpio name is null\n", __func__);
return -1;
}
if (index >= LCD_CPU_GPIO_NUM_MAX) {
LCDERR("%s: invalid gpio: %d\n", __func__, index);
return -1;
}
if (lcd_gpio[index].probe_flag == 1) {
if (lcd_debug_print_flag) {
LCDPR("gpio %s(%d) is already probed\n",
lcd_gpio[index].name, index);
}
return 0;
}
if (lcd_debug_print_flag)
LCDPR("probe gpio: %s(%d)\n", name, index);
strcpy(lcd_gpio[index].name, name);
/* init gpio flag */
lcd_gpio[index].probe_flag = 1;
lcd_gpio[index].register_flag = 0;
return 0;
}
static int lcd_gpio_request(int index)
{
int ret = 0;
if (index >= LCD_CPU_GPIO_NUM_MAX) {
LCDERR("%s: invalid gpio: %d\n", __func__, index);
return -1;
}
if (lcd_gpio[index].probe_flag == 0) {
LCDERR("gpio %d is not probed\n", index);
return -1;
}
if (lcd_gpio[index].register_flag == 1) {
if (lcd_debug_print_flag) {
LCDPR("gpio %s(%d) is already registered\n",
lcd_gpio[index].name, index);
}
return 0;
}
ret = dm_gpio_lookup_name(lcd_gpio[index].name, &lcd_gpio[index].gpio);
if (ret) {
LCDERR("lookup gpio: wrong name %s\n", lcd_gpio[index].name);
return -1;
}
ret = dm_gpio_request(&lcd_gpio[index].gpio, "aml_lcd");
if (ret) {
LCDERR("request gpio %s(%d) failed\n", lcd_gpio[index].name, index);
return -1;
}
if (lcd_debug_print_flag)
LCDPR("request gpio: %s(%d)\n", lcd_gpio[index].name, index);
lcd_gpio[index].register_flag = 1;
return 0;
}
int lcd_gpio_set(int index, int value)
{
int ret = 0;
if (index >= LCD_CPU_GPIO_NUM_MAX) {
LCDERR("%s: invalid gpio: %d\n", __func__, index);
return -1;
}
if (lcd_gpio[index].register_flag == 0) {
ret = lcd_gpio_request(index);
if (ret)
return -1;
}
switch (value) {
case LCD_GPIO_OUTPUT_LOW:
ret = dm_gpio_set_dir_flags(&lcd_gpio[index].gpio, GPIOD_IS_OUT);
if (ret) {
LCDERR("set gpio %s(%d) direction failed\n",
lcd_gpio[index].name, index);
return ret;
}
dm_gpio_set_value(&lcd_gpio[index].gpio, 0);
break;
case LCD_GPIO_OUTPUT_HIGH:
ret = dm_gpio_set_dir_flags(&lcd_gpio[index].gpio, GPIOD_IS_OUT);
if (ret) {
LCDERR("set gpio %s(%d) direction failed\n",
lcd_gpio[index].name, index);
return ret;
}
dm_gpio_set_value(&lcd_gpio[index].gpio, 1);
break;
case LCD_GPIO_INPUT:
default:
ret = dm_gpio_set_dir_flags(&lcd_gpio[index].gpio, GPIOD_IS_IN);
if (ret) {
LCDERR("set gpio %s(%d) direction failed\n",
lcd_gpio[index].name, index);
return ret;
}
break;
}
if (lcd_debug_print_flag) {
LCDPR("gpio: %s(%d), value: %d\n",
lcd_gpio[index].name, index, value);
}
return 0;
}
unsigned int lcd_gpio_input_get(int index)
{
int ret;
unsigned int value;
if (index >= LCD_CPU_GPIO_NUM_MAX) {
LCDERR("%s: invalid gpio: %d\n", __func__, index);
return 0;
}
if (lcd_gpio[index].register_flag == 0) {
ret = lcd_gpio_request(index);
if (ret)
return 0;
}
value = dm_gpio_get_value(&lcd_gpio[index].gpio);
return value;
}
/* *************************************** */
unsigned int lcd_lvds_channel_on_value(struct lcd_config_s *pconf)
{
unsigned int channel_on = 0;
if (pconf->lcd_control.lvds_config->dual_port == 0) {
if (pconf->lcd_control.lvds_config->lane_reverse == 0) {
switch (pconf->lcd_basic.lcd_bits) {
case 6:
channel_on = 0xf;
break;
case 8:
channel_on = 0x1f;
break;
case 10:
default:
channel_on = 0x3f;
break;
}
} else {
switch (pconf->lcd_basic.lcd_bits) {
case 6:
channel_on = 0x3c;
break;
case 8:
channel_on = 0x3e;
break;
case 10:
default:
channel_on = 0x3f;
break;
}
}
if (pconf->lcd_control.lvds_config->port_swap == 1)
channel_on = (channel_on << 6); /* use channel B */
} else {
if (pconf->lcd_control.lvds_config->lane_reverse == 0) {
switch (pconf->lcd_basic.lcd_bits) {
case 6:
channel_on = 0x3cf;
break;
case 8:
channel_on = 0x7df;
break;
case 10:
default:
channel_on = 0xfff;
break;
}
} else {
switch (pconf->lcd_basic.lcd_bits) {
case 6:
channel_on = 0xf3c;
break;
case 8:
channel_on = 0xfbe;
break;
case 10:
default:
channel_on = 0xfff;
break;
}
}
}
return channel_on;
}
void lcd_pinmux_set(int status)
{
struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
struct lcd_config_s *pconf;
int i;
if (lcd_debug_print_flag)
LCDPR("%s: %d\n", __func__, status);
pconf = lcd_drv->lcd_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) {
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) {
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) {
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++;
}
}
}
int lcd_power_load_from_dts(struct lcd_config_s *pconf, const void *dt_blob, int child_offset)
{
char *propdata;
unsigned int i, j, temp;
propdata = (char *)fdt_getprop(dt_blob, child_offset, "power_on_step", NULL);
if (propdata == NULL) {
LCDERR("failed to get power_on_step\n");
return 0;
} else {
i = 0;
while (i < LCD_PWR_STEP_MAX) {
j = 4 * i;
temp = be32_to_cpup((((u32*)propdata)+j));
pconf->lcd_power->power_on_step[i].type = temp;
if (temp == 0xff)
break;
temp = be32_to_cpup((((u32*)propdata)+j+1));
pconf->lcd_power->power_on_step[i].index = temp;
temp = be32_to_cpup((((u32*)propdata)+j+2));
pconf->lcd_power->power_on_step[i].value = temp;
temp = be32_to_cpup((((u32*)propdata)+j+3));
pconf->lcd_power->power_on_step[i].delay = temp;
i++;
}
}
propdata = (char *)fdt_getprop(dt_blob, child_offset, "power_off_step", NULL);
if (propdata == NULL) {
LCDERR("failed to get power_off_step\n");
return 0;
} else {
i = 0;
while (i < LCD_PWR_STEP_MAX) {
j = 4 * i;
temp = be32_to_cpup((((u32*)propdata)+j));
pconf->lcd_power->power_off_step[i].type = temp;
if (temp == 0xff)
break;
temp = be32_to_cpup((((u32*)propdata)+j+1));
pconf->lcd_power->power_off_step[i].index = temp;
temp = be32_to_cpup((((u32*)propdata)+j+2));
pconf->lcd_power->power_off_step[i].value = temp;
temp = be32_to_cpup((((u32*)propdata)+j+3));
pconf->lcd_power->power_off_step[i].delay = temp;
i++;
}
}
return 0;
}
int lcd_power_load_from_unifykey(struct lcd_config_s *pconf,
unsigned char *buf, int key_len, int len)
{
int i, j;
unsigned char *p;
int ret = 0;
/* power: (5byte * n) */
p = buf + len;
if (lcd_debug_print_flag)
LCDPR("power_on step:\n");
i = 0;
while (i < LCD_PWR_STEP_MAX) {
len += 5;
ret = aml_lcd_unifykey_len_check(key_len, len);
if (ret) {
pconf->lcd_power->power_on_step[i].type = 0xff;
pconf->lcd_power->power_on_step[i].index = 0;
pconf->lcd_power->power_on_step[i].value = 0;
pconf->lcd_power->power_on_step[i].delay = 0;
LCDERR("unifykey power_on length is incorrect\n");
return -1;
}
pconf->lcd_power->power_on_step[i].type = *(p + LCD_UKEY_PWR_TYPE + 5*i);
pconf->lcd_power->power_on_step[i].index = *(p + LCD_UKEY_PWR_INDEX + 5*i);
pconf->lcd_power->power_on_step[i].value = *(p + LCD_UKEY_PWR_VAL + 5*i);
pconf->lcd_power->power_on_step[i].delay =
(*(p + LCD_UKEY_PWR_DELAY + 5*i) |
((*(p + LCD_UKEY_PWR_DELAY + 5*i + 1)) << 8));
if (lcd_debug_print_flag) {
LCDPR("step %d: type=%d, index=%d, value=%d, delay=%d\n",
i, pconf->lcd_power->power_on_step[i].type,
pconf->lcd_power->power_on_step[i].index,
pconf->lcd_power->power_on_step[i].value,
pconf->lcd_power->power_on_step[i].delay);
}
if (pconf->lcd_power->power_on_step[i].type >= LCD_POWER_TYPE_MAX)
break;
i++;
}
if (lcd_debug_print_flag)
LCDPR("power_off step:\n");
p += (5*(i + 1));
j = 0;
while (j < LCD_PWR_STEP_MAX) {
len += 5;
ret = aml_lcd_unifykey_len_check(key_len, len);
if (ret) {
pconf->lcd_power->power_off_step[j].type = 0xff;
pconf->lcd_power->power_off_step[j].index = 0;
pconf->lcd_power->power_off_step[j].value = 0;
pconf->lcd_power->power_off_step[j].delay = 0;
LCDERR("unifykey power_off length is incorrect\n");
return -1;
}
pconf->lcd_power->power_off_step[j].type = *(p + LCD_UKEY_PWR_TYPE + 5*j);
pconf->lcd_power->power_off_step[j].index = *(p + LCD_UKEY_PWR_INDEX + 5*j);
pconf->lcd_power->power_off_step[j].value = *(p + LCD_UKEY_PWR_VAL + 5*j);
pconf->lcd_power->power_off_step[j].delay =
(*(p + LCD_UKEY_PWR_DELAY + 5*j) |
((*(p + LCD_UKEY_PWR_DELAY + 5*j + 1)) << 8));
if (lcd_debug_print_flag) {
LCDPR("step %d: type=%d, index=%d, value=%d, delay=%d\n",
j, pconf->lcd_power->power_off_step[j].type,
pconf->lcd_power->power_off_step[j].index,
pconf->lcd_power->power_off_step[j].value,
pconf->lcd_power->power_off_step[j].delay);
}
if (pconf->lcd_power->power_off_step[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_from_bsp(struct lcd_config_s *pconf)
{
char propname[50];
struct lcd_pinmux_ctrl_s *pinmux;
unsigned int i, j;
int pinmux_index = 0, set_cnt = 0, clr_cnt = 0;
if (lcd_debug_print_flag)
LCDPR("%s\n", __func__);
if (pconf->lcd_pinmux == NULL) {
LCDERR("%s: lcd_pinmux is NULL for lcd.c\n", __func__);
return -1;
}
switch (pconf->lcd_basic.lcd_type) {
case LCD_TTL:
/* data */
if (pconf->lcd_basic.lcd_bits == 6)
pinmux_index = 0;
else
pinmux_index = 1;
sprintf(propname, "%s", lcd_ttl_pinmux_str[pinmux_index]);
pinmux = pconf->lcd_pinmux;
for (i = 0; i < LCD_PINMX_MAX; i++) {
pinmux += i;
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;
}
}
/* sync */
switch (pconf->lcd_control.ttl_config->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]);
pinmux = pconf->lcd_pinmux;
for (i = 0; i < LCD_PINMX_MAX; i++) {
pinmux += i;
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+set_cnt][0] = pinmux->pinmux_set[j][0];
pconf->pinmux_set[j+set_cnt][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+clr_cnt][0] = pinmux->pinmux_clr[j][0];
pconf->pinmux_clr[j+clr_cnt][1] = pinmux->pinmux_clr[j][1];
clr_cnt++;
}
break;
}
}
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;
}
break;
case LCD_LVDS:
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;
break;
case LCD_MLVDS:
sprintf(propname, "lcd_minilvds_pin");
pinmux = pconf->lcd_pinmux;
for (i = 0; i < LCD_PINMX_MAX; i++) {
pinmux += i;
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;
}
}
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;
}
break;
case LCD_VBYONE:
sprintf(propname, "lcd_vbyone_pin");
pinmux = pconf->lcd_pinmux;
for (i = 0; i < LCD_PINMX_MAX; i++) {
pinmux += i;
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;
}
}
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;
}
break;
case LCD_MIPI:
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;
break;
default:
LCDERR("%s: invalid lcd type\n", __func__);
break;
}
return 0;
}
int lcd_pinmux_load_config(const void *dt_blob, struct lcd_config_s *pconf)
{
int i;
lcd_pinmux_load_from_bsp(pconf);
if (lcd_debug_print_flag) {
i = 0;
while (i < LCD_PINMUX_NUM) {
if (pconf->pinmux_set[i][0] == LCD_PINMUX_END)
break;
LCDPR("pinmux_set: %d, 0x%08x\n",
pconf->pinmux_set[i][0], pconf->pinmux_set[i][1]);
i++;
}
i = 0;
while (i < LCD_PINMUX_NUM) {
if (pconf->pinmux_clr[i][0] == LCD_PINMUX_END)
break;
LCDPR("pinmux_clr: %d, 0x%08x\n",
pconf->pinmux_clr[i][0], pconf->pinmux_clr[i][1]);
i++;
}
}
return 0;
}
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->lcd_basic.lcd_type) {
case LCD_TTL:
h_delay = TTL_DELAY;
break;
default:
h_delay = 0;
break;
}
h_period = pconf->lcd_basic.h_period;
v_period = pconf->lcd_basic.v_period;
h_active = pconf->lcd_basic.h_active;
v_active = pconf->lcd_basic.v_active;
hsync_bp = pconf->lcd_timing.hsync_bp;
hsync_width = pconf->lcd_timing.hsync_width;
vsync_bp = pconf->lcd_timing.vsync_bp;
vsync_width = pconf->lcd_timing.vsync_width;
de_hstart = h_period - h_active - 1;
de_vstart = v_period - v_active;
pconf->lcd_timing.video_on_pixel = de_hstart - h_delay;
pconf->lcd_timing.video_on_line = de_vstart;
pconf->lcd_timing.de_hs_addr = de_hstart;
pconf->lcd_timing.de_he_addr = de_hstart + h_active;
pconf->lcd_timing.de_vs_addr = de_vstart;
pconf->lcd_timing.de_ve_addr = de_vstart + v_active - 1;
hstart = (de_hstart + h_period - hsync_bp - hsync_width) % h_period;
hend = (de_hstart + h_period - hsync_bp) % h_period;
pconf->lcd_timing.hs_hs_addr = hstart;
pconf->lcd_timing.hs_he_addr = hend;
pconf->lcd_timing.hs_vs_addr = 0;
pconf->lcd_timing.hs_ve_addr = v_period - 1;
pconf->lcd_timing.vs_hs_addr = (hstart + h_period) % h_period;
pconf->lcd_timing.vs_he_addr = pconf->lcd_timing.vs_hs_addr;
vstart = (de_vstart + v_period - vsync_bp - vsync_width) % v_period;
vend = (de_vstart + v_period - vsync_bp) % v_period;
pconf->lcd_timing.vs_vs_addr = vstart;
pconf->lcd_timing.vs_ve_addr = vend;
if (lcd_debug_print_flag) {
LCDPR("hs_hs_addr=%d, hs_he_addr=%d, hs_vs_addr=%d, hs_ve_addr=%d\n",
pconf->lcd_timing.hs_hs_addr, pconf->lcd_timing.hs_he_addr,
pconf->lcd_timing.hs_vs_addr, pconf->lcd_timing.hs_ve_addr);
LCDPR("vs_hs_addr=%d, vs_he_addr=%d, vs_vs_addr=%d, vs_ve_addr=%d\n",
pconf->lcd_timing.vs_hs_addr, pconf->lcd_timing.vs_he_addr,
pconf->lcd_timing.vs_vs_addr, pconf->lcd_timing.vs_ve_addr);
}
}
int lcd_vmode_change(struct lcd_config_s *pconf)
{
unsigned char type = pconf->lcd_timing.fr_adjust_type;
/* use default value to avoid offset */
unsigned int pclk = pconf->lcd_timing.lcd_clk_dft;
unsigned int h_period = pconf->lcd_timing.h_period_dft;
unsigned int v_period = pconf->lcd_timing.v_period_dft;
unsigned int pclk_min = pconf->lcd_basic.lcd_clk_min;
unsigned int pclk_max = pconf->lcd_basic.lcd_clk_max;
unsigned int duration_num = pconf->lcd_timing.sync_duration_num;
unsigned int duration_den = pconf->lcd_timing.sync_duration_den;
char str[100];
int len = 0;
pconf->lcd_timing.clk_change = 0; /* clear clk flag */
switch (type) {
case 0: /* pixel clk adjust */
pclk = (h_period * v_period) / duration_den * duration_num;
if (pconf->lcd_timing.lcd_clk != pclk)
pconf->lcd_timing.clk_change = LCD_CLK_PLL_CHANGE;
break;
case 1: /* htotal adjust */
h_period = ((pclk / v_period) * duration_den * 100) /
duration_num;
h_period = (h_period + 99) / 100; /* round off */
if (pconf->lcd_basic.h_period != h_period) {
/* check clk frac update */
pclk = (h_period * v_period) / duration_den *
duration_num;
if (pconf->lcd_timing.lcd_clk != pclk) {
pconf->lcd_timing.clk_change =
LCD_CLK_FRAC_UPDATE;
}
}
break;
case 2: /* vtotal adjust */
v_period = ((pclk / h_period) * duration_den * 100) /
duration_num;
v_period = (v_period + 99) / 100; /* round off */
if (pconf->lcd_basic.v_period != v_period) {
/* check clk frac update */
pclk = (h_period * v_period) / duration_den *
duration_num;
if (pconf->lcd_timing.lcd_clk != pclk) {
pconf->lcd_timing.clk_change =
LCD_CLK_FRAC_UPDATE;
}
}
break;
case 3: /* free adjust, use min/max range to calculate */
v_period = ((pclk / h_period) * duration_den * 100) /
duration_num;
v_period = (v_period + 99) / 100; /* round off */
if (v_period > pconf->lcd_basic.v_period_max) {
v_period = pconf->lcd_basic.v_period_max;
h_period = ((pclk / v_period) * duration_den * 100) /
duration_num;
h_period = (h_period + 99) / 100; /* round off */
if (h_period > pconf->lcd_basic.h_period_max) {
h_period = pconf->lcd_basic.h_period_max;
pclk = (h_period * v_period) / duration_den *
duration_num;
if (pconf->lcd_timing.lcd_clk != pclk) {
if (pclk > pclk_max) {
pclk = pclk_max;
LCDERR("invalid vmode\n");
return -1;
}
pconf->lcd_timing.clk_change =
LCD_CLK_PLL_CHANGE;
}
}
} else if (v_period < pconf->lcd_basic.v_period_min) {
v_period = pconf->lcd_basic.v_period_min;
h_period = ((pclk / v_period) * duration_den * 100) /
duration_num;
h_period = (h_period + 99) / 100; /* round off */
if (h_period < pconf->lcd_basic.h_period_min) {
h_period = pconf->lcd_basic.h_period_min;
pclk = (h_period * v_period) / duration_den *
duration_num;
if (pconf->lcd_timing.lcd_clk != pclk) {
if (pclk < pclk_min) {
pclk = pclk_min;
LCDERR("invalid vmode\n");
return -1;
}
pconf->lcd_timing.clk_change =
LCD_CLK_PLL_CHANGE;
}
}
}
/* check clk frac update */
if ((pconf->lcd_timing.clk_change & LCD_CLK_PLL_CHANGE) == 0) {
pclk = (h_period * v_period) / duration_den *
duration_num;
if (pconf->lcd_timing.lcd_clk != pclk) {
pconf->lcd_timing.clk_change =
LCD_CLK_FRAC_UPDATE;
}
}
break;
case 4: /* hdmi mode */
if ((duration_num / duration_den) == 59) {
/* pixel clk adjust */
pclk = (h_period * v_period) / duration_den * duration_num;
if (pconf->lcd_timing.lcd_clk != pclk)
pconf->lcd_timing.clk_change = LCD_CLK_PLL_CHANGE;
} else {
/* htotal adjust */
h_period = ((pclk / v_period) * duration_den * 100) /
duration_num;
h_period = (h_period + 99) / 100; /* round off */
if (pconf->lcd_basic.h_period != h_period) {
/* check clk frac update */
pclk = (h_period * v_period) / duration_den *
duration_num;
if (pconf->lcd_timing.lcd_clk != pclk) {
pconf->lcd_timing.clk_change =
LCD_CLK_FRAC_UPDATE;
}
}
}
break;
default:
LCDERR("%s: invalid fr_adjust_type: %d\n", __func__, type);
return 0;
}
if (pconf->lcd_basic.v_period != v_period) {
len += sprintf(str+len, "v_period %u->%u",
pconf->lcd_basic.v_period, v_period);
/* update v_period */
pconf->lcd_basic.v_period = v_period;
}
if (pconf->lcd_basic.h_period != h_period) {
if (len > 0)
len += sprintf(str+len, ", ");
len += sprintf(str+len, "h_period %u->%u",
pconf->lcd_basic.h_period, h_period);
/* update h_period */
pconf->lcd_basic.h_period = h_period;
}
if (pconf->lcd_timing.lcd_clk != pclk) {
if (len > 0)
len += sprintf(str+len, ", ");
len += sprintf(str+len, "pclk %u.%03uMHz->%u.%03uMHz",
(pconf->lcd_timing.lcd_clk / 1000000),
((pconf->lcd_timing.lcd_clk / 1000) % 1000),
(pclk / 1000000), ((pclk / 1000) % 1000));
pconf->lcd_timing.lcd_clk = pclk;
}
if (lcd_debug_print_flag) {
if (len > 0)
LCDPR("%s: %s\n", __func__, str);
}
return 0;
}