blob: dc01bf9464d1402e1b4de0e3b1bc81a6b9aa6ada [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 <asm/arch/io.h>
#include <amlogic/media/vout/lcd/aml_lcd.h>
#include "lcd_common.h"
#include "lcd_tcon.h"
static struct lcd_tcon_spi_s tcon_spi = {
.block_cnt = 0,
.init_flag = 0,
.spi_block = NULL,
.ext_buf = NULL,
.data_read = NULL,
.data_conv = NULL,
};
struct lcd_tcon_spi_s *lcd_tcon_spi_get(void)
{
return &tcon_spi;
}
static void lcd_tcon_spi_print(void)
{
struct lcd_tcon_spi_block_s *spi_block;
int i, j;
if (tcon_spi.version == 0) {
LCDPR("tcon_spi invalid for version 0\n");
return;
}
printf("lcd_tcon_spi info:\n");
printf("version = %d\n", tcon_spi.version);
printf("block_cnt = %d\n", tcon_spi.block_cnt);
printf("init_flag = 0x%x\n", tcon_spi.init_flag);
if (tcon_spi.init_flag == 0)
return;
for (i = 0; i < tcon_spi.block_cnt; i++) {
spi_block = tcon_spi.spi_block[i];
printf("spi_block %d:\n"
"data_type 0x%02x\n"
"data_index %d\n"
"data_flag 0x%08x\n"
"spi_offset 0x%08x\n"
"spi_size 0x%08x\n"
"param_cnt 0x%08x\n",
i, spi_block->data_type,
spi_block->data_index, spi_block->data_flag,
spi_block->spi_offset, spi_block->spi_size,
spi_block->param_cnt);
for (j = 0; j < spi_block->param_cnt; j++) {
printf("param_%d 0x%08x\n",
j, spi_block->param[j]);
}
}
printf("\n");
}
#ifdef CONFIG_AML_LCD_EXTERN
static int lcd_tcon_spi_ext_update(struct lcd_extern_driver_s *ext_drv)
{
unsigned char *buf;
unsigned int size, crc;
if (!ext_drv) {
LCDERR("%s: ext_drv is null\n", __func__);
return -1;
}
buf = tcon_spi.ext_buf;
/* write lcd_extern unifykey and driver init_on_table */
memcpy(ext_drv->config->table_init_on,
&buf[LCD_UKEY_EXT_INIT],
tcon_spi.ext_init_on_cnt);
ext_drv->config->table_init_on_cnt = tcon_spi.ext_init_on_cnt;
/* update size & crc, then write to unifykey */
size = LCD_UKEY_EXT_INIT + tcon_spi.ext_init_on_cnt +
tcon_spi.ext_init_off_cnt;
buf[4] = size & 0xff;
buf[5] = (size >> 8) & 0xff;
buf[6] = (size >> 16) & 0xff;
buf[7] = (size >> 24) & 0xff;
crc = (unsigned int)(crc32(0, &buf[4], (size - 4)));
buf[0] = crc & 0xff;
buf[1] = (crc >> 8) & 0xff;
buf[2] = (crc >> 16) & 0xff;
buf[3] = (crc >> 24) & 0xff;
lcd_unifykey_write("lcd_extern", buf, size);
return 0;
}
static int lcd_tcon_spi_update_data(struct lcd_tcon_spi_block_s *spi_block,
unsigned char *cmp_buf, int flag, int i,
unsigned int offset, unsigned int data_len)
{
if (flag) {
if (spi_block->data_new_size > offset + data_len) {
memcpy(&cmp_buf[i + 2], &spi_block->new_buf[offset],
data_len - 1);
spi_block->new_buf[0] = data_len;
}
} else {
memcpy(&cmp_buf[i + 1], spi_block->new_buf,
spi_block->data_new_size);
}
return 0;
}
/* for ext_data, need update cmd table when compare */
static int lcd_tcon_spi_ext_cmp(unsigned char index,
struct lcd_tcon_spi_block_s *spi_block)
{
unsigned char *cmp_buf, *tmp_buf;
unsigned int cmp_buf_size, ext_size;
unsigned char type, cnt;
int i = 0, j = 0, k;
unsigned int offset = 0, data_len = 0;
int flag = 0;
if (!spi_block->new_buf) {
LCDERR("%s: new_buf is null\n", __func__);
return -1;
}
if (!tcon_spi.ext_buf) {
LCDERR("%s: ext_buf is null\n", __func__);
return -1;
}
ext_size = LCD_EXTERN_INIT_ON_MAX + LCD_EXTERN_INIT_OFF_MAX;
tmp_buf = (unsigned char *)malloc((ext_size * sizeof(unsigned char)));
if (!tmp_buf) {
LCDERR("%s: failed to alloc tmp_buf\n", __func__);
return -1;
}
memset(tmp_buf, 0, (ext_size * sizeof(unsigned char)));
cmp_buf = &tcon_spi.ext_buf[LCD_UKEY_EXT_INIT];
cmp_buf_size = tcon_spi.ext_init_on_cnt;
while ((i + 1) < cmp_buf_size) {
type = cmp_buf[i];
cnt = cmp_buf[i + 1];
if (type == 0xff)
break;
if ((i + 2 + cnt) > cmp_buf_size)
break;
if ((((type >> 4) & 0xf) == 0xb) ||
(((type >> 4) & 0xf) == 0xd) ||
(((type >> 4) & 0xf) == 0xa)) {
if (index != (type & 0xf))
goto lcd_tcon_spi_ext_cmp_next;
if (((type >> 4) & 0xf) == 0xa) {
flag = 1;
data_len = cmp_buf[i + 1];
offset = cmp_buf[i + 2];
} else {
j = i + cnt + 2;
}
k = cmp_buf_size - j + tcon_spi.ext_init_off_cnt;
if (cnt == 0) {
if (spi_block->new_buf[0]) { /* new cnt */
/* save data behind */
memcpy(tmp_buf, &cmp_buf[j], k);
/* update current data */
lcd_tcon_spi_update_data(spi_block,
cmp_buf, flag,
i, offset,
data_len);
/* recover data behind */
j = i + spi_block->new_buf[0] + 2;
memcpy(&cmp_buf[j], tmp_buf, k);
tcon_spi.ext_init_on_cnt +=
spi_block->new_buf[0];
goto lcd_tcon_spi_ext_cmp_diff;
}
goto lcd_tcon_spi_ext_cmp_no_diff;
}
if (memcmp(&cmp_buf[i + 2], spi_block->new_buf,
spi_block->data_new_size)) {
/* save data behind */
memcpy(tmp_buf, &cmp_buf[j], k);
/* update current data */
lcd_tcon_spi_update_data(spi_block,
cmp_buf, flag,
i, offset,
data_len);
/* recover data behind */
j = i + spi_block->new_buf[0] + 2;
memcpy(&cmp_buf[j], tmp_buf, k);
tcon_spi.ext_init_on_cnt =
tcon_spi.ext_init_on_cnt - cnt +
spi_block->new_buf[0];
goto lcd_tcon_spi_ext_cmp_diff;
}
}
lcd_tcon_spi_ext_cmp_next:
i += (cnt + 2);
}
lcd_tcon_spi_ext_cmp_no_diff:
free(tmp_buf);
return 0;
lcd_tcon_spi_ext_cmp_diff:
free(tmp_buf);
return -1;
}
#endif
static int lcd_tcon_spi_data_cmp(struct lcd_tcon_spi_block_s *spi_block,
unsigned char *cmp_buf)
{
unsigned int raw_data_check;
raw_data_check = cmp_buf[4] | (cmp_buf[5] << 8) |
(cmp_buf[6] << 16) | (cmp_buf[7] << 24);
if (raw_data_check != spi_block->data_raw_check)
return -1;
return 0;
}
static int lcd_tcon_spi_data_load(void)
{
struct tcon_mem_map_table_s *mm_table = get_lcd_tcon_mm_table();
#ifdef CONFIG_AML_LCD_EXTERN
struct lcd_extern_driver_s *ext_drv = NULL;
unsigned int ext_index;
unsigned int ext_need_update = 0;
#endif
unsigned int i, j, size, new_size;
int ret;
if (tcon_spi.version == 0)
return 0;
if (mm_table->version == 0)
return 0;
if (!tcon_spi.spi_block) {
LCDERR("%s: spi_block buf is null\n", __func__);
return -1;
}
if (!tcon_spi.data_read) {
LCDERR("%s: data_read is null\n", __func__);
return -1;
}
if (!tcon_spi.data_conv) {
LCDERR("%s: data_conv is null\n", __func__);
return -1;
}
for (i = 0; i < tcon_spi.block_cnt; i++) {
switch (tcon_spi.spi_block[i]->data_type) {
case LCD_TCON_DATA_BLOCK_TYPE_DEMURA_LUT:
case LCD_TCON_DATA_BLOCK_TYPE_ACC_LUT:
if (!mm_table->data_mem_vaddr) {
LCDERR("%s %d: data_mem error\n", __func__, i);
continue;
}
ret = tcon_spi.data_read(tcon_spi.spi_block[i]);
if (ret)
continue;
j = tcon_spi.spi_block[i]->data_index;
/* update tcon data buf */
if (!mm_table->data_mem_vaddr[j]) {
/* no default bin file exist */
ret = tcon_spi.data_conv(tcon_spi.spi_block[i]);
if (ret)
continue;
if (!tcon_spi.spi_block[i]->new_buf) {
LCDERR("%s: spi_block[%d] new_buf is null\n",
__func__, i);
continue;
}
/* note: all the tcon data buf size must align to 32byte */
new_size = lcd_tcon_data_size_align(tcon_spi.spi_block[i]->data_new_size);
mm_table->data_mem_vaddr[j] = (unsigned char *)malloc(new_size);
if (!mm_table->data_mem_vaddr[j]) {
LCDERR("%s: Not enough memory\n",
__func__);
continue;
}
memset(mm_table->data_mem_vaddr[j], 0, new_size);
memcpy(mm_table->data_mem_vaddr[j],
tcon_spi.spi_block[i]->new_buf,
tcon_spi.spi_block[i]->data_new_size);
} else {
ret = lcd_tcon_spi_data_cmp(tcon_spi.spi_block[i],
mm_table->data_mem_vaddr[j]);
if (ret == 0)
continue;
ret = tcon_spi.data_conv(tcon_spi.spi_block[i]);
if (ret) {
free(mm_table->data_mem_vaddr[j]);
mm_table->data_mem_vaddr[j] = NULL;
LCDERR("%s: block_data[%d] disabled\n",
__func__, i);
continue;
}
if (!tcon_spi.spi_block[i]->new_buf) {
LCDERR("%s: spi_block[%d] new_buf is null\n",
__func__, i);
continue;
}
size = mm_table->data_mem_vaddr[j][8] |
(mm_table->data_mem_vaddr[j][9] << 8) |
(mm_table->data_mem_vaddr[j][10] << 16) |
(mm_table->data_mem_vaddr[j][11] << 24);
if (tcon_spi.spi_block[i]->data_new_size > size) {
LCDERR("%s: block_data[%d] size is not match\n",
__func__, i);
continue;
}
new_size = lcd_tcon_data_size_align(size);
memset(mm_table->data_mem_vaddr[j], 0, new_size);
memcpy(mm_table->data_mem_vaddr[j],
tcon_spi.spi_block[i]->new_buf,
tcon_spi.spi_block[i]->data_new_size);
}
break;
case LCD_TCON_DATA_BLOCK_TYPE_EXT: /* pmu */
#ifdef CONFIG_AML_LCD_EXTERN
if (!tcon_spi.ext_buf)
break;
ext_index = (tcon_spi.spi_block[i]->data_index >> 8) &
0xff;
if (ext_drv) {
if (ext_drv->config->index != ext_index) {
LCDERR
("%s: don't support multi ext device for tcon data\n",
__func__);
continue;
}
} else {
ext_drv = lcd_extern_get_driver(ext_index);
if (!ext_drv)
break;
}
tcon_spi.ext_init_on_cnt =
ext_drv->config->table_init_on_cnt;
tcon_spi.ext_init_off_cnt =
ext_drv->config->table_init_off_cnt;
j = tcon_spi.spi_block[i]->data_index & 0xff;
ret = tcon_spi.data_read(tcon_spi.spi_block[i]);
if (ret)
continue;
ret = tcon_spi.data_conv(tcon_spi.spi_block[i]);
if (ret)
continue;
if (!tcon_spi.spi_block[i]->new_buf) {
LCDERR("%s: spi_block[%d] new_buf is null\n",
__func__, i);
continue;
}
ret = lcd_tcon_spi_ext_cmp(j, tcon_spi.spi_block[i]);
if (ret)
ext_need_update = 1;
#endif
break;
default:
break;
}
}
#ifdef CONFIG_AML_LCD_EXTERN
if (ext_need_update)
lcd_tcon_spi_ext_update(ext_drv);
#endif
for (i = 0; i < tcon_spi.block_cnt; i++) {
if (tcon_spi.spi_block[i]->param) {
free(tcon_spi.spi_block[i]->param);
tcon_spi.spi_block[i]->param = NULL;
}
if (tcon_spi.spi_block[i]->raw_buf) {
free(tcon_spi.spi_block[i]->raw_buf);
tcon_spi.spi_block[i]->raw_buf = NULL;
}
if (tcon_spi.spi_block[i]->temp_buf) {
free(tcon_spi.spi_block[i]->temp_buf);
tcon_spi.spi_block[i]->temp_buf = NULL;
}
if (tcon_spi.spi_block[i]->new_buf) {
free(tcon_spi.spi_block[i]->new_buf);
tcon_spi.spi_block[i]->new_buf = NULL;
}
}
return 0;
}
static int lcd_tcon_spi_data_parse(void)
{
unsigned char *para, *p;
#ifdef CONFIG_AML_LCD_EXTERN
unsigned int ext_size;
#ifdef CONFIG_CMD_INI
unsigned char *data_buf = (unsigned char *)handle_lcd_ext_buf_get();
unsigned int data_buf_size;
#endif
#endif
struct lcd_tcon_spi_unifykey_header_s spi_header;
unsigned int i, j, n, block_size;
int key_len, len, ret;
if (tcon_spi.init_flag) /* already parsed */
return 0;
ret = lcd_unifykey_check_exist("lcd_tcon_spi");
if (ret)
return -1;
para = (unsigned char *)
malloc(sizeof(unsigned char) * LCD_UKEY_TCON_SPI_SIZE);
if (!para) {
LCDERR("%s: Not enough memory\n", __func__);
return -1;
}
key_len = LCD_UKEY_TCON_SPI_SIZE;
memset(para, 0, (sizeof(unsigned char) * key_len));
ret = lcd_unifykey_get("lcd_tcon_spi", para, &key_len);
if (ret)
goto lcd_tcon_spi_data_parse_err0;
/* check lcd_tcon_spi unifykey length */
len = 10;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
LCDERR("lcd_tcon_spi unifykey length is not correct\n");
goto lcd_tcon_spi_data_parse_err0;
}
/* header: 16byte */
memcpy(&spi_header, para, LCD_UKEY_TCON_SPI_HEAD_SIZE);
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("lcd_tcon_spi unifykey header:\n");
LCDPR("crc32 = 0x%08x\n", spi_header.crc32);
LCDPR("data_size = %d\n", spi_header.data_size);
LCDPR("version = %d\n", spi_header.version);
LCDPR("block_cnt = %d\n", spi_header.block_cnt);
}
tcon_spi.version = spi_header.version;
tcon_spi.block_cnt = spi_header.block_cnt;
if (tcon_spi.version == 0) {
free(para);
return 0;
}
if (tcon_spi.block_cnt == 0) {
LCDERR("%s: block_cnt 0, exit\n", __func__);
free(para);
return 0;
}
if (tcon_spi.block_cnt > LCD_UKEY_TCON_SPI_BLOCK_CNT_MAX) {
LCDERR("%s: lcd_tcon_spi block_cnt %d out of support(max %d), limit to %d\n",
__func__, tcon_spi.block_cnt,
LCD_UKEY_TCON_SPI_BLOCK_CNT_MAX,
LCD_UKEY_TCON_SPI_BLOCK_CNT_MAX);
tcon_spi.block_cnt = LCD_UKEY_TCON_SPI_BLOCK_CNT_MAX;
}
len = LCD_UKEY_TCON_SPI_HEAD_SIZE + LCD_UKEY_TCON_SPI_BLOCK_SIZE_PRE;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
LCDERR("lcd_tcon_spi unifykey length is not correct\n");
goto lcd_tcon_spi_data_parse_err0;
}
tcon_spi.spi_block = (struct lcd_tcon_spi_block_s **)malloc
(tcon_spi.block_cnt * sizeof(struct lcd_tcon_spi_block_s *));
if (!tcon_spi.spi_block) {
LCDERR("failed to alloc tcon_spi\n");
goto lcd_tcon_spi_data_parse_err0;
}
memset(tcon_spi.spi_block, 0,
(tcon_spi.block_cnt * sizeof(struct lcd_tcon_spi_block_s *)));
len = LCD_UKEY_TCON_SPI_HEAD_SIZE;
p = para + len;
#ifdef CONFIG_AML_LCD_EXTERN
ext_size = LCD_UKEY_EXT_INIT + LCD_EXTERN_INIT_ON_MAX +
LCD_EXTERN_INIT_OFF_MAX;
#endif
for (i = 0; i < tcon_spi.block_cnt; i++) {
tcon_spi.spi_block[i] = (struct lcd_tcon_spi_block_s *)malloc
(sizeof(struct lcd_tcon_spi_block_s));
if (!tcon_spi.spi_block[i]) {
LCDERR("failed to alloc tcon_spi_block\n");
for (j = 0; j < i; j++) {
free(tcon_spi.spi_block[j]);
tcon_spi.spi_block[j] = NULL;
}
goto lcd_tcon_spi_data_parse_err1;
}
memset(tcon_spi.spi_block[i], 0,
sizeof(struct lcd_tcon_spi_block_s));
memcpy(tcon_spi.spi_block[i], p,
LCD_UKEY_TCON_SPI_BLOCK_SIZE_PRE);
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("lcd_tcon_spi block %d:\n", i);
LCDPR(" data_type = 0x%02x\n",
tcon_spi.spi_block[i]->data_type);
LCDPR(" data_index = %d\n",
tcon_spi.spi_block[i]->data_index);
LCDPR(" data_flag = %d\n",
tcon_spi.spi_block[i]->data_flag);
LCDPR(" spi_offset = 0x%08x\n",
tcon_spi.spi_block[i]->spi_offset);
LCDPR(" spi_size = 0x%08x\n",
tcon_spi.spi_block[i]->spi_size);
LCDPR(" param_cnt = %d\n",
tcon_spi.spi_block[i]->param_cnt);
}
block_size = LCD_UKEY_TCON_SPI_BLOCK_SIZE_PRE +
tcon_spi.spi_block[i]->param_cnt * 4;
len += block_size;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
LCDERR("lcd_tcon_spi unifykey length is incorrect\n");
goto lcd_tcon_spi_data_parse_err0;
}
if (tcon_spi.spi_block[i]->param_cnt > 0) {
tcon_spi.spi_block[i]->param = (unsigned int *)malloc
(tcon_spi.spi_block[i]->param_cnt * sizeof(unsigned int));
if (!tcon_spi.spi_block[i]->param) {
LCDERR("failed to alloc spi_block[%d] param\n", i);
for (j = 0; j <= i; j++) {
free(tcon_spi.spi_block[j]);
tcon_spi.spi_block[j] = NULL;
}
goto lcd_tcon_spi_data_parse_err1;
}
memset(tcon_spi.spi_block[i]->param, 0,
tcon_spi.spi_block[i]->param_cnt * sizeof(unsigned int));
n = LCD_UKEY_TCON_SPI_BLOCK_SIZE_PRE;
for (j = 0; j < tcon_spi.spi_block[i]->param_cnt; j++) {
tcon_spi.spi_block[i]->param[j] = p[n] |
(p[n + 1] << 8) |
(p[n + 2] << 16) |
(p[n + 3] << 24);
n += 4;
}
}
#ifdef CONFIG_AML_LCD_EXTERN
if (tcon_spi.spi_block[i]->data_type == LCD_TCON_DATA_BLOCK_TYPE_EXT &&
!tcon_spi.ext_buf) {
tcon_spi.ext_buf = (unsigned char *)malloc
((ext_size * sizeof(unsigned char)));
if (!tcon_spi.ext_buf) {
LCDERR("failed to alloc ext_buf\n");
for (j = 0; j <= i; j++) {
free(tcon_spi.spi_block[j]->raw_buf);
tcon_spi.spi_block[j]->raw_buf = NULL;
if (tcon_spi.spi_block[j]->param) {
free(tcon_spi.spi_block[j]->param);
tcon_spi.spi_block[j]->param = NULL;
}
free(tcon_spi.spi_block[j]);
tcon_spi.spi_block[j] = NULL;
}
goto lcd_tcon_spi_data_parse_err1;
}
memset(tcon_spi.ext_buf, 0,
(ext_size * sizeof(unsigned char)));
#ifdef CONFIG_CMD_INI
if (data_buf) {
data_buf_size = data_buf[4] |
(data_buf[5] << 8) |
(data_buf[6] << 16) |
(data_buf[7] << 24);
memcpy(tcon_spi.ext_buf, data_buf,
data_buf_size);
}
#endif
}
#endif
p += block_size;
}
tcon_spi.init_flag = 1;
free(para);
return 0;
lcd_tcon_spi_data_parse_err1:
free(tcon_spi.spi_block);
tcon_spi.spi_block = NULL;
lcd_tcon_spi_data_parse_err0:
free(para);
return -1;
}
int lcd_tcon_spi_data_probe(struct lcd_drv_s *lcd_drv)
{
int ret;
ret = lcd_tcon_spi_data_parse();
if (ret)
return -1;
lcd_drv->lcd_tcon_spi_print = lcd_tcon_spi_print;
lcd_drv->lcd_tcon_spi_data_load = lcd_tcon_spi_data_load;
return 0;
}