blob: ea7a55f7fc77be8ccdc2cd50a51567787d787e31 [file] [log] [blame]
/*
* drivers/display/lcd/aml_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.
*
*/
#include <common.h>
#include <malloc.h>
#include <amlogic/aml_lcd.h>
#include "aml_lcd_reg.h"
#include "aml_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];
}
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, char *dt_addr, int child_offset)
{
char *propdata;
unsigned int i, j, temp;
propdata = (char *)fdt_getprop(dt_addr, 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_addr, 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 */
};
#ifdef CONFIG_OF_LIBFDT
static int lcd_pinmux_load_from_dts(char *dt_addr, struct lcd_config_s *pconf)
{
int parent_offset;
char propname[50];
char *propdata;
unsigned int i, j, temp;
int pinmux_index = 0, pinmux_set_cnt = 0, pinmux_clr_cnt = 0;
int len;
if (lcd_debug_print_flag)
LCDPR("%s\n", __func__);
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, "/pinmux/%s", lcd_ttl_pinmux_str[pinmux_index]);
parent_offset = fdt_path_offset(dt_addr, propname);
if (parent_offset < 0) {
LCDERR("not find %s node\n", propname);
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 -1;
} else {
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "amlogic,setmask", &len);
if (propdata == NULL) {
LCDERR("failed to get amlogic,setmask\n");
pconf->pinmux_set[0][0] = LCD_PINMUX_END;
pconf->pinmux_set[0][1] = 0x0;
} else {
temp = len / 8;
for (i = 0; i < temp; i++) {
pconf->pinmux_set[i][0] = be32_to_cpup((((u32*)propdata)+2*i));
pconf->pinmux_set[i][1] = be32_to_cpup((((u32*)propdata)+2*i+1));
}
if (temp < (LCD_PINMUX_NUM - 1)) {
pconf->pinmux_set[temp][0] = LCD_PINMUX_END;
pconf->pinmux_set[temp][1] = 0x0;
}
pinmux_set_cnt = temp;
}
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "amlogic,clrmask", &len);
if (propdata == NULL) {
LCDERR("failed to get amlogic,clrmask\n");
pconf->pinmux_clr[0][0] = LCD_PINMUX_END;
pconf->pinmux_clr[0][1] = 0x0;
} else {
temp = len / 8;
for (i = 0; i < temp; i++) {
pconf->pinmux_clr[i][0] = be32_to_cpup((((u32*)propdata)+2*i));
pconf->pinmux_clr[i][1] = be32_to_cpup((((u32*)propdata)+2*i+1));
}
if (temp < (LCD_PINMUX_NUM - 1)) {
pconf->pinmux_clr[temp][0] = LCD_PINMUX_END;
pconf->pinmux_clr[temp][1] = 0x0;
}
pinmux_clr_cnt = temp;
}
}
/* 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, "/pinmux/%s", lcd_ttl_pinmux_str[pinmux_index]);
parent_offset = fdt_path_offset(dt_addr, propname);
if (parent_offset < 0) {
return -1;
} else {
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "amlogic,setmask", &len);
if (propdata == NULL) {
LCDERR("failed to get amlogic,setmask\n");
} else {
temp = len / 8;
j = pinmux_set_cnt + temp;
if (j < LCD_PINMUX_NUM) {
for (i = 0; i < temp; i++) {
pconf->pinmux_set[i+pinmux_set_cnt][0] = be32_to_cpup((((u32*)propdata)+2*i));
pconf->pinmux_set[i+pinmux_set_cnt][1] = be32_to_cpup((((u32*)propdata)+2*i+1));
}
if (j < (LCD_PINMUX_NUM - 1)) {
pconf->pinmux_set[j][0] = LCD_PINMUX_END;
pconf->pinmux_set[j][1] = 0x0;
}
} else {
LCDERR("pinmux_set cnt is out of support\n");
}
}
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "amlogic,clrmask", &len);
if (propdata == NULL) {
LCDERR("failed to get amlogic,clrmask\n");
} else {
temp = len / 8;
j = pinmux_clr_cnt + temp;
if (j < LCD_PINMUX_NUM) {
for (i = 0; i < temp; i++) {
pconf->pinmux_clr[i+pinmux_clr_cnt][0] = be32_to_cpup((((u32*)propdata)+2*i));
pconf->pinmux_clr[i+pinmux_clr_cnt][1] = be32_to_cpup((((u32*)propdata)+2*i+1));
}
if (j < (LCD_PINMUX_NUM - 1)) {
pconf->pinmux_clr[j][0] = LCD_PINMUX_END;
pconf->pinmux_clr[j][1] = 0x0;
}
} else {
LCDERR("pinmux_clr cnt is out of support\n");
}
}
}
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_VBYONE:
sprintf(propname, "/pinmux/lcd_vbyone_pin");
parent_offset = fdt_path_offset(dt_addr, propname);
if (parent_offset < 0) {
LCDERR("not find %s node\n", propname);
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 -1;
} else {
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "amlogic,setmask", &len);
if (propdata == NULL) {
LCDERR("failed to get amlogic,setmask\n");
pconf->pinmux_set[0][0] = LCD_PINMUX_END;
pconf->pinmux_set[0][1] = 0x0;
} else {
temp = len / 8;
for (i = 0; i < temp; i++) {
pconf->pinmux_set[i][0] = be32_to_cpup((((u32*)propdata)+2*i));
pconf->pinmux_set[i][1] = be32_to_cpup((((u32*)propdata)+2*i+1));
}
if (temp < (LCD_PINMUX_NUM - 1)) {
pconf->pinmux_set[temp][0] = LCD_PINMUX_END;
pconf->pinmux_set[temp][1] = 0x0;
}
}
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "amlogic,clrmask", &len);
if (propdata == NULL) {
LCDERR("failed to get amlogic,clrmask\n");
pconf->pinmux_clr[0][0] = LCD_PINMUX_END;
pconf->pinmux_clr[0][1] = 0x0;
} else {
temp = len / 8;
for (i = 0; i < temp; i++) {
pconf->pinmux_clr[i][0] = be32_to_cpup((((u32*)propdata)+2*i));
pconf->pinmux_clr[i][1] = be32_to_cpup((((u32*)propdata)+2*i+1));
}
if (temp < (LCD_PINMUX_NUM - 1)) {
pconf->pinmux_clr[temp][0] = LCD_PINMUX_END;
pconf->pinmux_clr[temp][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;
}
#endif
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(char *dt_addr, struct lcd_config_s *pconf)
{
int ret = 0;
int i;
switch (pconf->pinctrl_ver) {
case 0:
#ifdef CONFIG_OF_LIBFDT
ret = lcd_pinmux_load_from_dts(dt_addr, pconf);
#endif
break;
case 2:
ret = lcd_pinmux_load_from_bsp(pconf);
break;
case 1: /* pinmux use default */
default:
break;
}
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 ret;
}
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);
}
}
#if 0
/* change clock(frame_rate) for different vmode */
int lcd_vmode_change(struct lcd_config_s *pconf)
{
unsigned int pclk = pconf->lcd_timing.lcd_clk;
unsigned int h_period = pconf->lcd_basic.h_period;
unsigned int v_period = pconf->lcd_basic.v_period;
unsigned char type = pconf->lcd_timing.fr_adjust_type;
unsigned int sync_duration_num = pconf->lcd_timing.sync_duration_num;
unsigned int sync_duration_den = pconf->lcd_timing.sync_duration_den;
/* frame rate adjust */
switch (type) {
case 1: /* htotal adjust */
h_period = ((pclk / v_period) * sync_duration_den * 10) / sync_duration_num;
h_period = (h_period + 5) / 10; /* round off */
if (pconf->lcd_basic.h_period != h_period) {
LCDPR("vmode_change: adjust h_period %u -> %u\n",
pconf->lcd_basic.h_period, h_period);
pconf->lcd_basic.h_period = h_period;
/* check clk frac update */
pclk = (h_period * v_period) / sync_duration_den * sync_duration_num;
if (pconf->lcd_timing.lcd_clk != pclk)
pconf->lcd_timing.lcd_clk = pclk;
}
break;
case 2: /* vtotal adjust */
v_period = ((pclk / h_period) * sync_duration_den * 10) / sync_duration_num;
v_period = (v_period + 5) / 10; /* round off */
if (pconf->lcd_basic.v_period != v_period) {
LCDPR("vmode_change: adjust v_period %u -> %u\n",
pconf->lcd_basic.v_period, v_period);
pconf->lcd_basic.v_period = v_period;
/* check clk frac update */
pclk = (h_period * v_period) / sync_duration_den * sync_duration_num;
if (pconf->lcd_timing.lcd_clk != pclk)
pconf->lcd_timing.lcd_clk = pclk;
}
break;
case 0: /* pixel clk adjust */
default:
pclk = (h_period * v_period * sync_duration_num) / sync_duration_den;
if (pconf->lcd_timing.lcd_clk != pclk) {
LCDPR("vmode_change: adjust pclk %u.%03uMHz -> %u.%03uMHz\n",
(pconf->lcd_timing.lcd_clk / 1000000),
((pconf->lcd_timing.lcd_clk / 1000) % 1000),
(pclk / 1000000), ((pclk / 1000) % 1000));
pconf->lcd_timing.lcd_clk = pclk;
}
break;
}
return 0;
}
#else
int lcd_vmode_change(struct lcd_config_s *pconf)
{
unsigned char type = pconf->lcd_timing.fr_adjust_type;
/* use default value to avoid offset */
unsigned int pclk = pconf->lcd_timing.lcd_clk_dft;
unsigned int h_period = pconf->lcd_timing.h_period_dft;
unsigned int v_period = pconf->lcd_timing.v_period_dft;
unsigned int pclk_min = pconf->lcd_basic.lcd_clk_min;
unsigned int pclk_max = pconf->lcd_basic.lcd_clk_max;
unsigned int duration_num = pconf->lcd_timing.sync_duration_num;
unsigned int duration_den = pconf->lcd_timing.sync_duration_den;
char str[100];
int len = 0;
pconf->lcd_timing.clk_change = 0; /* clear clk 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;
}
#endif