blob: 2d197fa2d4c2b8427cbf19b087100dd00debeedf [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
*
* Copyright (C) 2019 Amlogic, Inc. All rights reserved.
*
*/
#include <linux/init.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/reset.h>
#include <linux/of_reserved_mem.h>
#include <linux/cma.h>
#include <linux/dma-contiguous.h>
#include <linux/dma-mapping.h>
#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
#ifdef CONFIG_AMLOGIC_BACKLIGHT
#include <linux/amlogic/media/vout/lcd/aml_bl.h>
#endif
#include <linux/fs.h>
#include <linux/uaccess.h>
#ifdef CONFIG_AMLOGIC_TEE
#include <linux/amlogic/tee.h>
#endif
#include "lcd_common.h"
#include "lcd_reg.h"
#include "lcd_tcon.h"
/*#include "tcon_ceds.h"*/
static struct lcd_tcon_config_s *lcd_tcon_conf;
static struct tcon_rmem_s tcon_rmem;
static struct tcon_mem_map_table_s tcon_mm_table;
static struct lcd_tcon_local_cfg_s tcon_local_cfg;
/* **********************************
* tcon common function
* **********************************
*/
int lcd_tcon_valid_check(void)
{
if (!lcd_tcon_conf) {
LCDERR("invalid tcon data\n");
return -1;
}
if (lcd_tcon_conf->tcon_valid == 0) {
LCDERR("invalid tcon\n");
return -1;
}
return 0;
}
struct lcd_tcon_config_s *get_lcd_tcon_config(void)
{
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return NULL;
return lcd_tcon_conf;
}
struct tcon_rmem_s *get_lcd_tcon_rmem(void)
{
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return NULL;
return &tcon_rmem;
}
struct tcon_mem_map_table_s *get_lcd_tcon_mm_table(void)
{
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return NULL;
return &tcon_mm_table;
}
unsigned int lcd_tcon_data_size_align(unsigned int size)
{
unsigned int new_size;
/* ready for burst 128bit */
new_size = ((size + 15) / 16) * 16;
return new_size;
}
unsigned char lcd_tcon_checksum(unsigned char *buf, unsigned int len)
{
unsigned int temp = 0;
unsigned int i;
if (!buf)
return 0;
if (len == 0)
return 0;
for (i = 0; i < len; i++)
temp += buf[i];
return (unsigned char)(temp & 0xff);
}
unsigned char lcd_tcon_lrc(unsigned char *buf, unsigned int len)
{
unsigned char temp = 0;
unsigned int i;
if (!buf)
return 0xff;
if (len == 0)
return 0xff;
temp = buf[0];
for (i = 1; i < len; i++)
temp = temp ^ buf[i];
return temp;
}
static unsigned char *lcd_tcon_paddrtovaddr(unsigned long paddr,
unsigned int mem_size)
{
unsigned int highmem_flag = 0;
int max_mem_size = 0;
void *vaddr = NULL;
if (tcon_rmem.flag == 0) {
LCDPR("%s: invalid paddr\n", __func__);
return NULL;
}
highmem_flag = PageHighMem(phys_to_page(paddr));
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("%s: paddr 0x%lx highmem_flag:%d\n",
__func__, paddr, highmem_flag);
}
if (highmem_flag) {
max_mem_size = PAGE_ALIGN(mem_size);
max_mem_size = roundup(max_mem_size, PAGE_SIZE);
vaddr = lcd_vmap(paddr, max_mem_size);
if (!vaddr) {
LCDPR("tcon paddr mapping error: addr: 0x%lx\n",
(unsigned long)paddr);
return NULL;
}
/*LCDPR("tcon vaddr: 0x%p\n", vaddr);*/
} else {
vaddr = phys_to_virt(paddr);
if (!vaddr) {
LCDERR("tcon vaddr mapping failed: 0x%lx\n",
(unsigned long)paddr);
return NULL;
}
/*LCDPR("tcon vaddr: 0x%p\n", vaddr);*/
}
return (unsigned char *)vaddr;
}
/* **********************************
* tcon function api
* **********************************
*/
unsigned int lcd_tcon_reg_read(struct aml_lcd_drv_s *pdrv, unsigned int addr)
{
unsigned int val;
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return 0;
if (addr < lcd_tcon_conf->top_reg_base) {
if (lcd_tcon_conf->core_reg_width == 8)
val = lcd_tcon_read_byte(pdrv, addr);
else
val = lcd_tcon_read(pdrv, addr);
} else {
val = lcd_tcon_read(pdrv, addr);
}
return val;
}
void lcd_tcon_reg_write(struct aml_lcd_drv_s *pdrv,
unsigned int addr, unsigned int val)
{
unsigned char temp;
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return;
if (addr < lcd_tcon_conf->top_reg_base) {
if (lcd_tcon_conf->core_reg_width == 8) {
temp = (unsigned char)val;
lcd_tcon_write_byte(pdrv, addr, temp);
} else {
lcd_tcon_write(pdrv, addr, val);
}
} else {
lcd_tcon_write(pdrv, addr, val);
}
}
#define PR_BUF_MAX 200
void lcd_tcon_reg_table_print(void)
{
int i, j, n, cnt;
char *buf;
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return;
if (!tcon_mm_table.core_reg_table) {
LCDERR("%s: reg_table is null\n", __func__);
return;
}
buf = kcalloc(PR_BUF_MAX, sizeof(char), GFP_KERNEL);
if (!buf)
return;
LCDPR("%s:\n", __func__);
cnt = tcon_mm_table.core_reg_table_size;
for (i = 0; i < cnt; i += 16) {
n = snprintf(buf, PR_BUF_MAX, "%04x: ", i);
for (j = 0; j < 16; j++) {
if ((i + j) >= cnt)
break;
n += snprintf(buf + n, PR_BUF_MAX, " %02x",
tcon_mm_table.core_reg_table[i + j]);
}
buf[n] = '\0';
pr_info("%s\n", buf);
}
kfree(buf);
}
void lcd_tcon_reg_readback_print(struct aml_lcd_drv_s *pdrv)
{
int i, j, n, cnt, offset;
char *buf;
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return;
buf = kcalloc(PR_BUF_MAX, sizeof(char), GFP_KERNEL);
if (!buf)
return;
LCDPR("%s:\n", __func__);
cnt = tcon_mm_table.core_reg_table_size;
offset = lcd_tcon_conf->core_reg_start;
if (lcd_tcon_conf->core_reg_width == 8) {
for (i = offset; i < cnt; i += 16) {
n = snprintf(buf, PR_BUF_MAX, "%04x: ", i);
for (j = 0; j < 16; j++) {
if ((i + j) >= cnt)
break;
n += snprintf(buf + n, PR_BUF_MAX, " %02x",
lcd_tcon_read_byte(pdrv, i + j));
}
buf[n] = '\0';
pr_info("%s\n", buf);
}
} else {
if (lcd_tcon_conf->reg_table_width == 32) {
cnt /= 4;
for (i = offset; i < cnt; i += 4) {
n = snprintf(buf, PR_BUF_MAX, "%04x: ", i);
for (j = 0; j < 4; j++) {
if ((i + j) >= cnt)
break;
n += snprintf(buf + n, PR_BUF_MAX, " %08x",
lcd_tcon_read(pdrv, i + j));
}
buf[n] = '\0';
pr_info("%s\n", buf);
}
} else {
for (i = offset; i < cnt; i += 16) {
n = snprintf(buf, PR_BUF_MAX, "%04x: ", i);
for (j = 0; j < 16; j++) {
if ((i + j) >= cnt)
break;
n += snprintf(buf + n, PR_BUF_MAX, " %02x",
lcd_tcon_read(pdrv, i + j));
}
buf[n] = '\0';
pr_info("%s\n", buf);
}
}
}
kfree(buf);
}
unsigned int lcd_tcon_table_read(unsigned int addr)
{
unsigned char *table8;
unsigned int *table32, size = 0, val = 0;
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return 0;
if (!tcon_mm_table.core_reg_table) {
LCDERR("tcon reg_table is null\n");
return 0;
}
if (lcd_tcon_conf->core_reg_width == 8)
size = tcon_mm_table.core_reg_table_size;
else
size = tcon_mm_table.core_reg_table_size / 4;
if (addr >= size) {
LCDERR("invalid tcon reg_table addr: 0x%04x\n", addr);
return 0;
}
if (lcd_tcon_conf->core_reg_width == 8) {
table8 = tcon_mm_table.core_reg_table;
val = table8[addr];
} else {
table32 = (unsigned int *)tcon_mm_table.core_reg_table;
val = table32[addr];
}
return val;
}
unsigned int lcd_tcon_table_write(unsigned int addr, unsigned int val)
{
unsigned char *table8;
unsigned int *table32, size = 0, read_val = 0;
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return 0;
if (!tcon_mm_table.core_reg_table) {
LCDERR("tcon reg_table is null\n");
return 0;
}
if (lcd_tcon_conf->core_reg_width == 8)
size = tcon_mm_table.core_reg_table_size;
else
size = tcon_mm_table.core_reg_table_size / 4;
if (addr >= size) {
LCDERR("invalid tcon reg_table addr: 0x%04x\n", addr);
return 0;
}
if (lcd_tcon_conf->core_reg_width == 8) {
table8 = tcon_mm_table.core_reg_table;
table8[addr] = (unsigned char)(val & 0xff);
read_val = table8[addr];
} else {
table32 = (unsigned int *)tcon_mm_table.core_reg_table;
table32[addr] = val;
read_val = table32[addr];
}
return read_val;
}
int lcd_tcon_info_print(char *buf, int offset)
{
unsigned int size, cnt, m;
unsigned char *mem_vaddr;
char *str;
int len = 0, n, ret;
int i;
ret = lcd_tcon_valid_check();
if (ret)
return len;
n = lcd_debug_info_len(len + offset);
if (tcon_mm_table.version == 0) {
len += snprintf((buf + len), n,
"\ntcon info:\n"
"core_reg_width: %d\n"
"reg_table_len: %d\n"
"tcon_bin_ver: %s\n"
"tcon_rmem_flag: %d\n"
"tcon_data_flag: %d\n"
"reserved_mem paddr: 0x%lx\n"
"reserved_mem size: 0x%x\n"
"bin_path_mem_paddr: 0x%lx\n"
"bin_path_mem_vaddr: 0x%p\n"
"bin_path_mem_size: 0x%x\n"
"vac_mem paddr: 0x%lx\n"
"vac_mem vaddr: 0x%p\n"
"vac_mem size: 0x%x\n"
"demura_set_mem paddr: 0x%lx\n"
"demura_set_mem vaddr: 0x%p\n"
"demura_set_mem size: 0x%x\n"
"demura_lut_mem paddr: 0x%lx\n"
"demura_lut_mem vaddr: 0x%p\n"
"demura_lut_mem size: 0x%x\n"
"acc_lut_mem paddr: 0x%lx\n"
"acc_lut_mem vaddr: 0x%p\n"
"acc_lut_mem size: 0x%x\n\n",
lcd_tcon_conf->core_reg_width,
lcd_tcon_conf->reg_table_len,
tcon_local_cfg.bin_ver,
tcon_rmem.flag,
tcon_mm_table.tcon_data_flag,
(unsigned long)tcon_rmem.rsv_mem_paddr,
tcon_rmem.rsv_mem_size,
(unsigned long)tcon_rmem.bin_path_rmem.mem_paddr,
tcon_rmem.bin_path_rmem.mem_vaddr,
tcon_rmem.bin_path_rmem.mem_size,
(unsigned long)tcon_rmem.vac_rmem.mem_paddr,
tcon_rmem.vac_rmem.mem_vaddr,
tcon_rmem.vac_rmem.mem_size,
(unsigned long)tcon_rmem.demura_set_rmem.mem_paddr,
tcon_rmem.demura_set_rmem.mem_vaddr,
tcon_rmem.demura_set_rmem.mem_size,
(unsigned long)tcon_rmem.demura_lut_rmem.mem_paddr,
tcon_rmem.demura_lut_rmem.mem_vaddr,
tcon_rmem.demura_lut_rmem.mem_size,
(unsigned long)tcon_rmem.acc_lut_rmem.mem_paddr,
tcon_rmem.acc_lut_rmem.mem_vaddr,
tcon_rmem.acc_lut_rmem.mem_size);
if (tcon_rmem.axi_rmem) {
for (i = 0; i < lcd_tcon_conf->axi_bank; i++) {
n = lcd_debug_info_len(len + offset);
len += snprintf((buf + len), n,
"axi_mem[%d]_paddr: 0x%lx\n"
"axi_mem[%d]_vaddr: 0x%p\n"
"axi_mem[%d]_size: 0x%x\n",
i,
(unsigned long)tcon_rmem.axi_rmem[i].mem_paddr,
i, tcon_rmem.axi_rmem[i].mem_vaddr,
i, tcon_rmem.axi_rmem[i].mem_size);
}
}
} else {
len += snprintf((buf + len), n,
"\ntcon info:\n"
"core_reg_width: %d\n"
"reg_table_len: %d\n"
"tcon_bin_ver: %s\n"
"tcon_rmem_flag: %d\n"
"tcon_data_flag: %d\n"
"reserved_mem paddr: 0x%lx\n"
"reserved_mem size: 0x%x\n"
"bin_path_mem_paddr: 0x%lx\n"
"bin_path_mem_vaddr: 0x%p\n"
"bin_path_mem_size: 0x%x\n\n",
lcd_tcon_conf->core_reg_width,
lcd_tcon_conf->reg_table_len,
tcon_local_cfg.bin_ver,
tcon_rmem.flag,
tcon_mm_table.tcon_data_flag,
(unsigned long)tcon_rmem.rsv_mem_paddr,
tcon_rmem.rsv_mem_size,
(unsigned long)tcon_rmem.bin_path_rmem.mem_paddr,
tcon_rmem.bin_path_rmem.mem_vaddr,
tcon_rmem.bin_path_rmem.mem_size);
if (tcon_rmem.axi_rmem) {
for (i = 0; i < lcd_tcon_conf->axi_bank; i++) {
n = lcd_debug_info_len(len + offset);
len += snprintf((buf + len), n,
"axi_mem[%d]_paddr: 0x%lx\n"
"axi_mem[%d]_size: 0x%x\n",
i,
(unsigned long)tcon_rmem.axi_rmem[i].mem_paddr,
i, tcon_rmem.axi_rmem[i].mem_size);
}
}
if (tcon_mm_table.data_mem_vaddr) {
for (i = 0; i < tcon_mm_table.block_cnt; i++) {
n = lcd_debug_info_len(len + offset);
len += snprintf((buf + len), n,
"data_mem_vaddr[%d]: 0x%p\n",
i, tcon_mm_table.data_mem_vaddr[i]);
}
}
if (tcon_mm_table.data_multi_cnt > 0 && tcon_mm_table.data_multi) {
for (i = 0; i < tcon_mm_table.data_multi_cnt; i++) {
n = lcd_debug_info_len(len + offset);
len += snprintf((buf + len), n,
"data_multi[%d] type: 0x%x\n"
"data_multi[%d] list_cnt: %d\n"
"data_multi[%d] sel_id: %d\n",
i, tcon_mm_table.data_multi[i].block_type,
i, tcon_mm_table.data_multi[i].list_cnt,
i, tcon_mm_table.data_multi[i].sel_id);
}
}
}
if (tcon_rmem.bin_path_rmem.mem_vaddr) {
mem_vaddr = tcon_rmem.bin_path_rmem.mem_vaddr;
size = mem_vaddr[4] |
(mem_vaddr[5] << 8) |
(mem_vaddr[6] << 16) |
(mem_vaddr[7] << 24);
cnt = mem_vaddr[16] |
(mem_vaddr[17] << 8) |
(mem_vaddr[18] << 16) |
(mem_vaddr[19] << 24);
if (size < (32 + 256 * cnt))
return len;
if (cnt > 32)
return len;
n = lcd_debug_info_len(len + offset);
len += snprintf((buf + len), n, "\n");
for (i = 0; i < cnt; i++) {
m = 32 + 256 * i;
size = mem_vaddr[m] |
(mem_vaddr[m + 1] << 8) |
(mem_vaddr[m + 2] << 16) |
(mem_vaddr[m + 3] << 24);
str = (char *)&mem_vaddr[m + 4];
n = lcd_debug_info_len(len + offset);
len += snprintf((buf + len), n,
"tcon_path[%d]: size: 0x%x, %s\n",
i, size, str);
}
}
n = lcd_debug_info_len(len + offset);
len += snprintf((buf + len), n,
"\ntcon_status:\n"
"vac_valid: %d\n"
"demura_valid: %d\n"
"acc_valid: %d\n",
((tcon_mm_table.valid_flag >> LCD_TCON_DATA_VALID_VAC) & 0x1),
((tcon_mm_table.valid_flag >> LCD_TCON_DATA_VALID_DEMURA) & 0x1),
((tcon_mm_table.valid_flag >> LCD_TCON_DATA_VALID_ACC) & 0x1));
return len;
}
int lcd_tcon_core_reg_get(struct aml_lcd_drv_s *pdrv,
unsigned char *buf, unsigned int size)
{
unsigned int reg_max = 0, offset = 0, val;
int i, ret;
ret = lcd_tcon_valid_check();
if (ret)
return -1;
if (size > lcd_tcon_conf->reg_table_len) {
LCDERR("%s: size 0x%x is not enough(0x%x)\n",
__func__, size, lcd_tcon_conf->reg_table_len);
return -1;
}
offset = lcd_tcon_conf->core_reg_start;
if (lcd_tcon_conf->core_reg_width == 8) {
for (i = offset; i < size; i++)
buf[i] = lcd_tcon_read_byte(pdrv, i);
} else {
reg_max = size / 4;
for (i = offset; i < reg_max; i++) {
val = lcd_tcon_read(pdrv, i);
buf[i * 4] = val & 0xff;
buf[i * 4 + 1] = (val >> 8) & 0xff;
buf[i * 4 + 2] = (val >> 16) & 0xff;
buf[i * 4 + 3] = (val >> 24) & 0xff;
}
}
return 0;
}
int lcd_tcon_od_set(struct aml_lcd_drv_s *pdrv, int flag)
{
unsigned int reg, bit, temp;
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return -1;
if (lcd_tcon_conf->reg_core_od == REG_LCD_TCON_MAX) {
LCDERR("%s: invalid od reg\n", __func__);
return -1;
}
if (flag) {
if (tcon_rmem.flag == 0) {
LCDERR("%s: invalid memory, disable od function\n",
__func__);
return -1;
}
}
if (!(pdrv->status & LCD_STATUS_IF_ON))
return -1;
reg = lcd_tcon_conf->reg_core_od;
bit = lcd_tcon_conf->bit_od_en;
temp = flag ? 1 : 0;
if (lcd_tcon_conf->core_reg_width == 8)
lcd_tcon_setb_byte(pdrv, reg, temp, bit, 1);
else
lcd_tcon_setb(pdrv, reg, temp, bit, 1);
msleep(100);
LCDPR("%s: %d\n", __func__, flag);
return 0;
}
int lcd_tcon_od_get(struct aml_lcd_drv_s *pdrv)
{
unsigned int reg, bit, temp;
int ret = 0;
ret = lcd_tcon_valid_check();
if (ret)
return 0;
if (lcd_tcon_conf->reg_core_od == REG_LCD_TCON_MAX) {
LCDERR("%s: invalid od reg\n", __func__);
return 0;
}
reg = lcd_tcon_conf->reg_core_od;
bit = lcd_tcon_conf->bit_od_en;
if (lcd_tcon_conf->core_reg_width == 8)
temp = lcd_tcon_read_byte(pdrv, reg);
else
temp = lcd_tcon_read(pdrv, reg);
ret = ((temp >> bit) & 1);
return ret;
}
void lcd_tcon_global_reset(struct aml_lcd_drv_s *pdrv)
{
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return;
if (lcd_tcon_conf->tcon_global_reset) {
lcd_tcon_conf->tcon_global_reset(pdrv);
LCDPR("reset tcon\n");
}
}
int lcd_tcon_gamma_set_pattern(struct aml_lcd_drv_s *pdrv,
unsigned int bit_width, unsigned int gamma_r,
unsigned int gamma_g, unsigned int gamma_b)
{
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return -1;
if (lcd_tcon_conf->tcon_gamma_pattern) {
ret = lcd_tcon_conf->tcon_gamma_pattern(pdrv,
bit_width, gamma_r,
gamma_g, gamma_b);
LCDPR("%s: %dbits gamma r:0x%x g:0x%x b:0x%x\n",
__func__, bit_width, gamma_r, gamma_g, gamma_b);
}
return ret;
}
int lcd_tcon_core_update(struct aml_lcd_drv_s *pdrv)
{
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return -1;
lcd_tcon_core_reg_set(pdrv, lcd_tcon_conf, &tcon_mm_table);
return 0;
}
int lcd_tcon_enable(struct aml_lcd_drv_s *pdrv)
{
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return -1;
if (lcd_tcon_conf->tcon_enable)
lcd_tcon_conf->tcon_enable(pdrv);
return 0;
}
void lcd_tcon_disable(struct aml_lcd_drv_s *pdrv)
{
unsigned int i;
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return;
LCDPR("%s\n", __func__);
/* release data*/
if (tcon_mm_table.version) {
for (i = 0; i < tcon_mm_table.block_cnt; i++) {
if (!tcon_mm_table.data_mem_vaddr[i])
continue;
if (!tcon_mm_table.data_size)
continue;
memset(tcon_mm_table.data_mem_vaddr[i], 0,
tcon_mm_table.data_size[i]);
}
tcon_mm_table.valid_flag = 0;
tcon_mm_table.tcon_data_flag = 0;
lcd_tcon_data_multi_remvoe(&tcon_mm_table);
}
if (lcd_tcon_conf->tcon_disable)
lcd_tcon_conf->tcon_disable(pdrv);
}
/* return:
* 0: matched
* -1: not match
* 1: default
*/
static int lcd_tcon_data_multi_match_check(struct aml_lcd_drv_s *pdrv, unsigned char *data_buf)
{
struct lcd_config_s *pconf;
#ifdef CONFIG_AMLOGIC_BACKLIGHT
struct aml_bl_drv_s *bldrv;
struct bl_pwm_config_s *bl_pwm = NULL;
#endif
struct lcd_tcon_data_block_header_s *block_header;
struct lcd_tcon_data_block_ext_header_s *ext_header;
union lcd_tcon_data_part_u data_part;
unsigned char *p, part_type;
unsigned int size, data_offset, offset, i, j, k;
unsigned short part_cnt, block_ctrl_flag, ctrl_data_flag, ctrl_sub_type;
unsigned int data_byte, data_cnt, data, min, max;
unsigned int sync_num, sync_den, temp;
pconf = &pdrv->config;
block_header = (struct lcd_tcon_data_block_header_s *)data_buf;
p = data_buf + LCD_TCON_DATA_BLOCK_HEADER_SIZE;
ext_header = (struct lcd_tcon_data_block_ext_header_s *)p;
part_cnt = ext_header->part_cnt;
block_ctrl_flag = block_header->block_ctrl;
data_offset = LCD_TCON_DATA_BLOCK_HEADER_SIZE + block_header->ext_header_size;
size = 0;
for (i = 0; i < part_cnt; i++) {
p = data_buf + data_offset;
part_type = p[LCD_TCON_DATA_PART_NAME_SIZE + 3];
if (lcd_debug_print_flag & LCD_DBG_PR_ISR)
LCDPR("%s: %s, type = 0x%02x,\n", __func__, p, part_type);
switch (part_type) {
case LCD_TCON_DATA_PART_TYPE_CONTROL:
block_ctrl_flag = 0;
data_part.ctrl = (struct lcd_tcon_data_part_ctrl_s *)p;
offset = LCD_TCON_DATA_PART_CTRL_SIZE_PRE;
size = offset + (data_part.ctrl->data_cnt *
data_part.ctrl->data_byte_width);
if ((size + data_offset) > block_header->block_size)
goto lcd_tcon_data_multi_match_check_err_size;
ctrl_data_flag = data_part.ctrl->ctrl_data_flag;
ctrl_sub_type = data_part.ctrl->ctrl_sub_type;
if (ctrl_data_flag != LCD_TCON_DATA_CTRL_FLAG_MULTI)
break;
data_byte = data_part.ctrl->data_byte_width;
data_cnt = data_part.ctrl->data_cnt;
k = offset;
data = 0;
min = 0;
max = 0;
if (ctrl_sub_type == LCD_TCON_DATA_CTRL_MULTI_VFREQ) {
sync_num = pconf->timing.sync_duration_num;
sync_den = pconf->timing.sync_duration_den;
temp = sync_num * 100 / sync_den;
if (data_cnt != 2)
goto lcd_tcon_data_multi_match_check_err_data_cnt;
for (j = 0; j < data_byte; j++)
min |= (p[k + j] << (j * 8));
k += data_byte;
for (j = 0; j < data_byte; j++)
max |= (p[k + j] << (j * 8));
if (temp < min || temp > max)
goto lcd_tcon_data_multi_match_check_exit;
} else if (ctrl_sub_type == LCD_TCON_DATA_CTRL_MULTI_BL_LEVEL) {
#ifdef CONFIG_AMLOGIC_BACKLIGHT
bldrv = aml_bl_get_driver(pdrv->index);
if (!bldrv)
goto lcd_tcon_data_multi_match_check_err_type;
temp = bldrv->level;
if (data_cnt != 2)
goto lcd_tcon_data_multi_match_check_err_data_cnt;
for (j = 0; j < data_byte; j++)
min |= (p[k + j] << (j * 8));
k += data_byte;
for (j = 0; j < data_byte; j++)
max |= (p[k + j] << (j * 8));
if (temp < min || temp > max)
goto lcd_tcon_data_multi_match_check_exit;
#endif
} else if (ctrl_sub_type == LCD_TCON_DATA_CTRL_MULTI_BL_PWM_DUTY) {
#ifdef CONFIG_AMLOGIC_BACKLIGHT
bldrv = aml_bl_get_driver(pdrv->index);
if (!bldrv)
goto lcd_tcon_data_multi_match_check_err_type;
if (data_cnt != 3)
goto lcd_tcon_data_multi_match_check_err_data_cnt;
for (j = 0; j < data_byte; j++)
data |= (p[k + j] << (j * 8));
k += data_byte;
for (j = 0; j < data_byte; j++)
min |= (p[k + j] << (j * 8));
k += data_byte;
for (j = 0; j < data_byte; j++)
max |= (p[k + j] << (j * 8));
switch (bldrv->bconf.method) {
case BL_CTRL_PWM:
bl_pwm = bldrv->bconf.bl_pwm;
break;
case BL_CTRL_PWM_COMBO:
if (data == 0)
bl_pwm = bldrv->bconf.bl_pwm_combo0;
else
bl_pwm = bldrv->bconf.bl_pwm_combo1;
break;
default:
break;
}
if (!bl_pwm)
goto lcd_tcon_data_multi_match_check_err_type;
temp = bl_pwm->pwm_duty;
if (temp < min || temp > max)
goto lcd_tcon_data_multi_match_check_exit;
#endif
} else if (ctrl_sub_type == LCD_TCON_DATA_CTRL_DEFAULT) {
return 1;
}
break;
default:
if (block_ctrl_flag)
goto lcd_tcon_data_multi_match_check_err_ctrl;
return 0;
}
data_offset += size;
}
return 0;
lcd_tcon_data_multi_match_check_exit:
if (lcd_debug_print_flag & LCD_DBG_PR_ISR)
LCDPR("%s: block %s control exit\n", __func__, block_header->name);
return -1;
lcd_tcon_data_multi_match_check_err_data_cnt:
LCDERR("%s: block %s data_cnt error\n", __func__, block_header->name);
return -1;
lcd_tcon_data_multi_match_check_err_ctrl:
LCDERR("%s: block %s need control part\n", __func__, block_header->name);
return -1;
#ifdef CONFIG_AMLOGIC_BACKLIGHT
lcd_tcon_data_multi_match_check_err_type:
LCDERR("%s: block %s control type invalid\n", __func__, block_header->name);
return -1;
#endif
lcd_tcon_data_multi_match_check_err_size:
LCDERR("%s: block %s size error\n", __func__, block_header->name);
return -1;
}
static int lcd_tcon_data_multi_update(struct aml_lcd_drv_s *pdrv,
struct tcon_mem_map_table_s *mm_table)
{
struct tcon_data_multi_s *data_multi = NULL;
struct tcon_data_list_s *temp_list, *dft_list, *match_list;
int i, ret = 0;
if (!mm_table || !mm_table->data_multi)
return 0;
if (mm_table->data_multi_cnt == 0)
return 0;
for (i = 0; i < mm_table->data_multi_cnt; i++) {
data_multi = &mm_table->data_multi[i];
temp_list = data_multi->list_header;
dft_list = NULL;
match_list = NULL;
while (temp_list) {
/* bypass current data */
if (data_multi->sel_id == temp_list->id) {
temp_list = temp_list->next;
continue;
}
ret = lcd_tcon_data_multi_match_check(pdrv, temp_list->block_vaddr);
if (ret == 0) {
match_list = temp_list;
break;
}
if (ret == 1)
dft_list = temp_list;
temp_list = temp_list->next;
}
if (!match_list) {
if (dft_list) {
ret = lcd_tcon_data_common_parse_set(pdrv,
dft_list->block_vaddr, 0);
if (ret == 0) {
data_multi->sel_id = dft_list->id;
data_multi->list_cur = dft_list;
}
if (lcd_debug_print_flag & LCD_DBG_PR_ISR) {
LCDPR("%s: sel default, type=0x%x, sel_id=%d\n",
__func__, data_multi->block_type,
data_multi->sel_id);
}
}
} else {
ret = lcd_tcon_data_common_parse_set(pdrv, match_list->block_vaddr, 0);
if (ret == 0) {
data_multi->sel_id = match_list->id;
data_multi->list_cur = match_list;
}
if (lcd_debug_print_flag & LCD_DBG_PR_ISR) {
LCDPR("%s: type=0x%x, sel_id=%d\n",
__func__, data_multi->block_type,
data_multi->sel_id);
}
}
}
return ret;
}
void lcd_tcon_vsync_isr(struct aml_lcd_drv_s *pdrv)
{
if (tcon_mm_table.version)
lcd_tcon_data_multi_update(pdrv, &tcon_mm_table);
}
/* **********************************
* tcon config
* **********************************
*/
static int lcd_tcon_vac_load(void)
{
unsigned int n;
unsigned char *buff = tcon_rmem.vac_rmem.mem_vaddr;
unsigned int data_cnt;
unsigned char data_checksum, data_lrc, temp_checksum, temp_lrc;
if (tcon_rmem.vac_rmem.mem_size == 0 || !buff)
return -1;
data_cnt = (buff[0] |
(buff[1] << 8) |
(buff[2] << 16) |
(buff[3] << 24));
if (data_cnt == 0) {
LCDPR("%s: vac data_cnt is zero\n", __func__);
return -1;
}
data_checksum = buff[4];
data_lrc = buff[5];
temp_checksum = lcd_tcon_checksum(&buff[8], data_cnt);
temp_lrc = lcd_tcon_lrc(&buff[8], data_cnt);
if (data_checksum != temp_checksum) {
LCDERR("%s: vac_data checksum error\n", __func__);
return -1;
}
if (data_lrc != temp_lrc) {
LCDERR("%s: vac_data lrc error\n", __func__);
return -1;
}
if (buff[6] != 0x55 || buff[7] != 0xaa) {
LCDERR("%s: vac_data pattern error\n", __func__);
return -1;
}
if (lcd_debug_print_flag & LCD_DBG_PR_ADV) {
for (n = 0; n < 30; n++)
LCDPR("%s: vac_data[%d]: 0x%x", __func__,
n, buff[n * 1]);
}
return 0;
}
static int lcd_tcon_demura_set_load(void)
{
unsigned int n;
char *buff = tcon_rmem.demura_set_rmem.mem_vaddr;
unsigned int data_cnt;
unsigned char data_checksum, data_lrc, temp_checksum, temp_lrc;
if (tcon_rmem.demura_set_rmem.mem_size == 0 || !buff)
return -1;
data_cnt = (buff[0] |
(buff[1] << 8) |
(buff[2] << 16) |
(buff[3] << 24));
if (data_cnt == 0) {
LCDPR("%s: demura_set data_cnt is zero\n", __func__);
return -1;
}
data_checksum = buff[4];
data_lrc = buff[5];
temp_checksum = lcd_tcon_checksum(&buff[8], data_cnt);
temp_lrc = lcd_tcon_lrc(&buff[8], data_cnt);
if (data_checksum != temp_checksum) {
LCDERR("%s: demura_set checksum error\n", __func__);
return -1;
}
if (data_lrc != temp_lrc) {
LCDERR("%s: demura_set lrc error\n", __func__);
return -1;
}
if (buff[6] != 0x55 || buff[7] != 0xaa) {
LCDERR("%s: demura_set pattern error\n", __func__);
return -1;
}
if (lcd_debug_print_flag & LCD_DBG_PR_ADV) {
for (n = 0; n < 30; n++)
LCDPR("%s: demura_set[%d]: 0x%x",
__func__, n, buff[n]);
}
return 0;
}
static int lcd_tcon_demura_lut_load(void)
{
unsigned int n;
char *buff = tcon_rmem.demura_lut_rmem.mem_vaddr;
unsigned int data_cnt;
unsigned char data_checksum, data_lrc, temp_checksum, temp_lrc;
if (tcon_rmem.demura_lut_rmem.mem_size == 0 || !buff)
return -1;
data_cnt = (buff[0] |
(buff[1] << 8) |
(buff[2] << 16) |
(buff[3] << 24));
if (data_cnt == 0) {
LCDPR("%s: demura_lut data_cnt is zero\n", __func__);
return -1;
}
data_checksum = buff[4];
data_lrc = buff[5];
temp_checksum = lcd_tcon_checksum(&buff[8], data_cnt);
temp_lrc = lcd_tcon_lrc(&buff[8], data_cnt);
if (data_checksum != temp_checksum) {
LCDERR("%s: demura_lut checksum error\n", __func__);
return -1;
}
if (data_lrc != temp_lrc) {
LCDERR("%s: demura_lut lrc error\n", __func__);
return -1;
}
if (buff[6] != 0x55 || buff[7] != 0xaa) {
LCDERR("%s: demura_lut pattern error\n", __func__);
return -1;
}
if (lcd_debug_print_flag & LCD_DBG_PR_ADV) {
for (n = 0; n < 30; n++)
LCDPR("%s: demura_lut[%d]: 0x%x\n",
__func__, n, buff[n]);
}
return 0;
}
static int lcd_tcon_acc_lut_load(void)
{
unsigned int n;
char *buff = tcon_rmem.acc_lut_rmem.mem_vaddr;
unsigned int data_cnt;
unsigned char data_checksum, data_lrc, temp_checksum, temp_lrc;
if (tcon_rmem.acc_lut_rmem.mem_size == 0 || !buff)
return -1;
data_cnt = (buff[0] |
(buff[1] << 8) |
(buff[2] << 16) |
(buff[3] << 24));
if (data_cnt == 0) {
LCDPR("%s: acc_lut data_cnt is zero\n", __func__);
return -1;
}
data_checksum = buff[4];
data_lrc = buff[5];
temp_checksum = lcd_tcon_checksum(&buff[8], data_cnt);
temp_lrc = lcd_tcon_lrc(&buff[8], data_cnt);
if (data_checksum != temp_checksum) {
LCDERR("%s: acc_lut checksum error\n", __func__);
return -1;
}
if (data_lrc != temp_lrc) {
LCDERR("%s: acc_lut lrc error\n", __func__);
return -1;
}
if (buff[6] != 0x55 || buff[7] != 0xaa) {
LCDERR("%s: acc_lut pattern error\n", __func__);
return -1;
}
if (lcd_debug_print_flag & LCD_DBG_PR_ADV) {
for (n = 0; n < 30; n++)
LCDPR("%s: acc_lut[%d]: 0x%x\n",
__func__, n, buff[n]);
}
return 0;
}
static inline void lcd_tcon_data_list_add(struct tcon_data_list_s *list_header,
struct tcon_data_list_s *data_list)
{
struct tcon_data_list_s *temp_list;
if (!data_list)
return;
if (!list_header) { /* new list */
list_header = data_list;
return;
}
temp_list = list_header;
while (temp_list->next)
temp_list = temp_list->next;
temp_list->next = data_list;
}
static inline void lcd_tcon_data_list_remove(struct tcon_data_list_s *list_header)
{
struct tcon_data_list_s *cur_list, *next_list;
if (!list_header)
return;
/* add to exist list */
cur_list = list_header;
while (cur_list) {
next_list = cur_list->next;
kfree(cur_list);
cur_list = next_list;
}
}
/* return:
* 0: find
* -1: failed
*/
static int lcd_tcon_data_multi_default_find(unsigned char *data_buf)
{
struct lcd_tcon_data_block_header_s *block_header;
struct lcd_tcon_data_block_ext_header_s *ext_header;
union lcd_tcon_data_part_u data_part;
unsigned char *p, part_type;
unsigned int size, data_offset, offset, i;
unsigned short part_cnt, block_ctrl_flag, ctrl_data_flag, ctrl_sub_type;
block_header = (struct lcd_tcon_data_block_header_s *)data_buf;
p = data_buf + LCD_TCON_DATA_BLOCK_HEADER_SIZE;
ext_header = (struct lcd_tcon_data_block_ext_header_s *)p;
part_cnt = ext_header->part_cnt;
block_ctrl_flag = block_header->block_ctrl;
data_offset = LCD_TCON_DATA_BLOCK_HEADER_SIZE + block_header->ext_header_size;
size = 0;
for (i = 0; i < part_cnt; i++) {
p = data_buf + data_offset;
part_type = p[LCD_TCON_DATA_PART_NAME_SIZE + 3];
switch (part_type) {
case LCD_TCON_DATA_PART_TYPE_CONTROL:
block_ctrl_flag = 0;
data_part.ctrl = (struct lcd_tcon_data_part_ctrl_s *)p;
offset = LCD_TCON_DATA_PART_CTRL_SIZE_PRE;
size = offset + (data_part.ctrl->data_cnt *
data_part.ctrl->data_byte_width);
if ((size + data_offset) > block_header->block_size)
goto lcd_tcon_data_multi_match_check_err_size;
ctrl_data_flag = data_part.ctrl->ctrl_data_flag;
ctrl_sub_type = data_part.ctrl->ctrl_sub_type;
if (ctrl_data_flag != LCD_TCON_DATA_CTRL_FLAG_MULTI)
break;
if (ctrl_sub_type == LCD_TCON_DATA_CTRL_DEFAULT)
return 0;
break;
default:
if (block_ctrl_flag)
goto lcd_tcon_data_multi_match_check_err_ctrl;
return -1;
}
data_offset += size;
}
return -1;
lcd_tcon_data_multi_match_check_err_ctrl:
LCDERR("%s: block %s need control part\n", __func__, block_header->name);
return -1;
lcd_tcon_data_multi_match_check_err_size:
LCDERR("%s: block %s size error\n", __func__, block_header->name);
return -1;
}
int lcd_tcon_data_multi_add(struct tcon_mem_map_table_s *mm_table,
struct lcd_tcon_data_block_header_s *block_header,
unsigned int index)
{
struct tcon_data_multi_s *data_multi = NULL;
struct tcon_data_list_s *data_list;
int i, ret;
if (!mm_table->data_multi)
return -1;
if (mm_table->data_multi_cnt > 0) {
if ((mm_table->data_multi_cnt + 1) >= mm_table->block_cnt) {
LCDERR("%s: multi block %s invalid\n",
__func__, block_header->name);
return -1;
}
}
/* create list */
data_list = kmalloc(sizeof(*data_list), GFP_KERNEL);
if (!data_list)
return -1;
data_list->block_vaddr = mm_table->data_mem_vaddr[index];
data_list->next = NULL;
data_list->id = index;
for (i = 0; i < mm_table->data_multi_cnt; i++) {
if (mm_table->data_multi[i].block_type == block_header->block_type) {
data_multi = &mm_table->data_multi[i];
break;
}
}
if (!data_multi) { /* create new list */
data_multi = &mm_table->data_multi[mm_table->data_multi_cnt];
mm_table->data_multi_cnt++;
data_multi->block_type = block_header->block_type;
data_multi->list_header = NULL;
data_multi->list_cnt = 0;
data_multi->sel_id = 0xff; /* default invalid */
}
lcd_tcon_data_list_add(data_multi->list_header, data_list);
ret = lcd_tcon_data_multi_default_find(data_list->block_vaddr);
if (ret == 0) {
data_multi->sel_id = data_list->id;
data_multi->list_cur = data_list;
}
data_multi->list_cnt++;
return 0;
}
int lcd_tcon_data_multi_remvoe(struct tcon_mem_map_table_s *mm_table)
{
struct tcon_data_multi_s *data_multi = NULL;
int i;
if (!mm_table->data_multi)
return 0;
if (mm_table->data_multi_cnt == 0)
return 0;
for (i = 0; i < mm_table->data_multi_cnt; i++) {
data_multi = &mm_table->data_multi[i];
data_multi->block_type = LCD_TCON_DATA_BLOCK_TYPE_NONE;
data_multi->list_cnt = 0;
data_multi->flag = 0xff;
lcd_tcon_data_list_remove(data_multi->list_header);
data_multi->list_header = NULL;
}
return 0;
}
int lcd_tcon_data_load(void)
{
struct aml_lcd_drv_s *pdrv = aml_lcd_get_driver(0);
unsigned char *table = tcon_mm_table.core_reg_table;
struct file *filp = NULL;
mm_segment_t old_fs = get_fs();
loff_t pos = 0;
char *str;
struct lcd_tcon_data_block_header_s block_header;
struct tcon_data_priority_s *data_prio;
int i, j, data_cnt = 0, demura_cnt = 0;
unsigned int n = 0, size, priority;
unsigned int header_size = LCD_TCON_DATA_BLOCK_HEADER_SIZE;
int ret;
if (!table) {
LCDERR("%s: no tcon bin table\n", __func__);
return -1;
}
LCDPR("%s\n", __func__);
if (tcon_mm_table.version == 0) {
if (pdrv->data->chip_type == LCD_CHIP_TL1 ||
pdrv->data->chip_type == LCD_CHIP_TM2) {
ret = lcd_tcon_vac_load();
if (ret == 0)
tcon_mm_table.valid_flag |= LCD_TCON_DATA_VALID_VAC;
ret = lcd_tcon_demura_set_load();
if (ret) {
table[0x178] = 0x38;
table[0x17c] = 0x20;
table[0x181] = 0x00;
table[0x23d] &= ~(1 << 0);
} else {
ret = lcd_tcon_demura_lut_load();
if (ret) {
table[0x178] = 0x38;
table[0x17c] = 0x20;
table[0x181] = 0x00;
table[0x23d] &= ~(1 << 0);
} else {
tcon_mm_table.valid_flag |= LCD_TCON_DATA_VALID_DEMURA;
}
}
}
ret = lcd_tcon_acc_lut_load();
if (ret == 0)
tcon_mm_table.valid_flag |= LCD_TCON_DATA_VALID_ACC;
} else {
if (!tcon_mm_table.data_mem_vaddr) {
LCDERR("%s: data_mem error\n", __func__);
return -1;
}
if (!tcon_mm_table.data_priority) {
LCDERR("%s: data_priority error\n", __func__);
return -1;
}
if (!tcon_mm_table.data_size) {
LCDERR("%s: data_size error\n", __func__);
return -1;
}
data_prio = tcon_mm_table.data_priority;
for (i = 0; i < tcon_mm_table.block_cnt; i++) {
n = 32 + i * 256;
size = tcon_mm_table.data_size[i];
str = (char *)&tcon_rmem.bin_path_rmem.mem_vaddr[n + 4];
set_fs(KERNEL_DS);
filp = filp_open(str, O_RDONLY, 0);
if (IS_ERR(filp)) {
LCDERR("%s: read %s error\n", __func__, str);
set_fs(old_fs);
return -1;
}
if (!tcon_mm_table.data_mem_vaddr[i]) {
data_cnt = vfs_read(filp, (void *)&block_header,
header_size, &pos);
if (data_cnt != header_size) {
LCDERR("read block head failed, data_cnt: %d\n",
data_cnt);
goto lcd_tcon_data_load_next;
}
size = block_header.block_size;
tcon_mm_table.data_size[i] = size;
tcon_mm_table.data_mem_vaddr[i] =
kcalloc(size, sizeof(unsigned char), GFP_KERNEL);
if (!tcon_mm_table.data_mem_vaddr[i]) {
LCDERR("%s: Not enough memory\n", __func__);
goto lcd_tcon_data_load_next;
}
}
pos = 0; /* must reset pos for file read */
data_cnt =
vfs_read(filp, (char *)tcon_mm_table.data_mem_vaddr[i], size, &pos);
if (data_cnt != size) {
LCDERR("%s: data_cnt: %d, data_size: %d\n",
__func__, data_cnt, size);
goto lcd_tcon_data_load_next;
}
memcpy((void *)&block_header, (void *)tcon_mm_table.data_mem_vaddr[i],
header_size);
tcon_mm_table.valid_flag |= block_header.block_flag;
if (block_header.block_flag == LCD_TCON_DATA_VALID_DEMURA)
demura_cnt++;
/* insertion sort for block data init_priority */
data_prio[i].index = i;
/*data_prio[i].priority = block_header.init_priority;*/
/* update init_priority by index */
priority = i;
data_prio[i].priority = priority;
if (i > 0) {
j = i - 1;
while (j >= 0) {
if (priority > data_prio[j].priority)
break;
if (priority == data_prio[j].priority) {
LCDERR("%s: block %d init_prio same as block %d\n",
__func__, data_prio[i].index,
data_prio[j].index);
return -1;
}
data_prio[j + 1].index = data_prio[j].index;
data_prio[j + 1].priority = data_prio[j].priority;
j--;
}
data_prio[j + 1].index = i;
data_prio[j + 1].priority = priority;
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("%s %d: size=0x%x, type=0x%02x, name=%s, init_priority=%d\n",
__func__, i,
block_header.block_size,
block_header.block_type,
block_header.name,
priority);
}
lcd_tcon_data_load_next:
vfs_fsync(filp, 0);
filp_close(filp, NULL);
set_fs(old_fs);
}
/* specially check demura setting */
if (pdrv->data->chip_type == LCD_CHIP_TL1 ||
pdrv->data->chip_type == LCD_CHIP_TM2) {
if (demura_cnt < 2) {
tcon_mm_table.valid_flag &=
~LCD_TCON_DATA_VALID_DEMURA;
/* disable demura */
table[0x178] = 0x38;
table[0x17c] = 0x20;
table[0x181] = 0x00;
table[0x23d] &= ~(1 << 0);
}
}
}
tcon_mm_table.tcon_data_flag = 1;
return 0;
}
static int lcd_tcon_bin_path_update(unsigned int size)
{
unsigned char *mem_vaddr;
unsigned int data_size, block_cnt;
unsigned int data_crc32, temp_crc32;
if (!tcon_rmem.bin_path_rmem.mem_vaddr) {
LCDERR("%s: get mem error\n", __func__);
return -1;
}
mem_vaddr = tcon_rmem.bin_path_rmem.mem_vaddr;
data_size = mem_vaddr[4] |
(mem_vaddr[5] << 8) |
(mem_vaddr[6] << 16) |
(mem_vaddr[7] << 24);
if (data_size < 32) { /* header size */
LCDERR("%s: tcon_bin_path data_size error\n", __func__);
return -1;
}
block_cnt = mem_vaddr[16] |
(mem_vaddr[17] << 8) |
(mem_vaddr[18] << 16) |
(mem_vaddr[19] << 24);
if (block_cnt > 32) {
LCDERR("%s: tcon_bin_path block_cnt error\n", __func__);
return -1;
}
data_crc32 = mem_vaddr[0] |
(mem_vaddr[1] << 8) |
(mem_vaddr[2] << 16) |
(mem_vaddr[3] << 24);
temp_crc32 = cal_crc32(0, &mem_vaddr[4], (data_size - 4));
if (data_crc32 != temp_crc32) {
LCDERR("%s: tcon_bin_path data crc error\n", __func__);
return -1;
}
tcon_mm_table.version = mem_vaddr[8] |
(mem_vaddr[9] << 8) |
(mem_vaddr[10] << 16) |
(mem_vaddr[11] << 24);
tcon_mm_table.data_load_level = mem_vaddr[12] |
(mem_vaddr[13] << 8) |
(mem_vaddr[14] << 16) |
(mem_vaddr[15] << 24);
tcon_mm_table.block_cnt = block_cnt;
return 0;
}
static int lcd_tcon_mm_table_config_v0(void)
{
unsigned int max_size;
max_size = lcd_tcon_conf->axi_mem_size +
lcd_tcon_conf->bin_path_size +
lcd_tcon_conf->vac_size +
lcd_tcon_conf->demura_set_size +
lcd_tcon_conf->demura_lut_size +
lcd_tcon_conf->acc_lut_size;
if (tcon_rmem.rsv_mem_size < max_size) {
LCDERR("%s: tcon mem size 0x%x is not enough, need 0x%x\n",
__func__, tcon_rmem.rsv_mem_size, max_size);
return -1;
}
if (tcon_mm_table.block_cnt != 4) {
LCDERR("%s: tcon data block_cnt %d invalid\n",
__func__, tcon_mm_table.block_cnt);
return -1;
}
tcon_rmem.vac_rmem.mem_size = lcd_tcon_conf->vac_size;
tcon_rmem.vac_rmem.mem_paddr = tcon_rmem.bin_path_rmem.mem_paddr +
tcon_rmem.bin_path_rmem.mem_size;
tcon_rmem.vac_rmem.mem_vaddr =
lcd_tcon_paddrtovaddr(tcon_rmem.vac_rmem.mem_paddr,
tcon_rmem.vac_rmem.mem_size);
if ((lcd_debug_print_flag & LCD_DBG_PR_NORMAL) &&
tcon_rmem.vac_rmem.mem_size > 0)
LCDPR("vac_mem paddr: 0x%08x, vaddr: 0x%p, size: 0x%x\n",
(unsigned int)tcon_rmem.vac_rmem.mem_paddr,
tcon_rmem.vac_rmem.mem_vaddr,
tcon_rmem.vac_rmem.mem_size);
tcon_rmem.demura_set_rmem.mem_size = lcd_tcon_conf->demura_set_size;
tcon_rmem.demura_set_rmem.mem_paddr = tcon_rmem.vac_rmem.mem_paddr +
tcon_rmem.vac_rmem.mem_size;
tcon_rmem.demura_set_rmem.mem_vaddr =
lcd_tcon_paddrtovaddr(tcon_rmem.demura_set_rmem.mem_paddr,
tcon_rmem.demura_set_rmem.mem_size);
if ((lcd_debug_print_flag & LCD_DBG_PR_NORMAL) &&
tcon_rmem.demura_set_rmem.mem_size > 0)
LCDPR("demura_set_mem paddr: 0x%08x, vaddr: 0x%p, size: 0x%x\n",
(unsigned int)tcon_rmem.demura_set_rmem.mem_paddr,
tcon_rmem.demura_set_rmem.mem_vaddr,
tcon_rmem.demura_set_rmem.mem_size);
tcon_rmem.demura_lut_rmem.mem_size = lcd_tcon_conf->demura_lut_size;
tcon_rmem.demura_lut_rmem.mem_paddr =
tcon_rmem.demura_set_rmem.mem_paddr +
tcon_rmem.demura_set_rmem.mem_size;
tcon_rmem.demura_lut_rmem.mem_vaddr =
lcd_tcon_paddrtovaddr(tcon_rmem.demura_lut_rmem.mem_paddr,
tcon_rmem.demura_lut_rmem.mem_size);
if ((lcd_debug_print_flag & LCD_DBG_PR_NORMAL) &&
tcon_rmem.demura_lut_rmem.mem_size > 0)
LCDPR("demura_lut_mem paddr: 0x%08x, vaddr: 0x%p, size: 0x%x\n",
(unsigned int)tcon_rmem.demura_lut_rmem.mem_paddr,
tcon_rmem.demura_lut_rmem.mem_vaddr,
tcon_rmem.demura_lut_rmem.mem_size);
tcon_rmem.acc_lut_rmem.mem_size = lcd_tcon_conf->acc_lut_size;
tcon_rmem.acc_lut_rmem.mem_paddr = tcon_rmem.demura_lut_rmem.mem_paddr +
tcon_rmem.demura_lut_rmem.mem_size;
tcon_rmem.acc_lut_rmem.mem_vaddr =
lcd_tcon_paddrtovaddr(tcon_rmem.acc_lut_rmem.mem_paddr,
tcon_rmem.acc_lut_rmem.mem_size);
if ((lcd_debug_print_flag & LCD_DBG_PR_NORMAL) &&
tcon_rmem.acc_lut_rmem.mem_size > 0)
LCDPR("acc_lut_mem paddr: 0x%08x, vaddr: 0x%p, size: 0x%x\n",
(unsigned int)tcon_rmem.acc_lut_rmem.mem_paddr,
tcon_rmem.acc_lut_rmem.mem_vaddr,
tcon_rmem.acc_lut_rmem.mem_size);
return 0;
}
static int lcd_tcon_mm_table_config_v1(void)
{
unsigned char *mem_vaddr;
unsigned int cnt, data_size, n, i;
if (tcon_mm_table.block_cnt > 32) {
LCDERR("%s: tcon data block_cnt %d invalid\n",
__func__, tcon_mm_table.block_cnt);
return -1;
}
if (tcon_mm_table.data_mem_vaddr)
return 0;
if (tcon_mm_table.block_cnt == 0) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("%s: block_cnt is zero\n", __func__);
return 0;
}
cnt = tcon_mm_table.block_cnt;
tcon_mm_table.data_mem_vaddr = kcalloc(cnt, sizeof(unsigned char *), GFP_KERNEL);
if (!tcon_mm_table.data_mem_vaddr)
return -1;
tcon_mm_table.data_priority =
kcalloc(cnt, sizeof(struct tcon_data_priority_s), GFP_KERNEL);
if (!tcon_mm_table.data_priority)
return -1;
memset(tcon_mm_table.data_priority, 0xff, cnt * sizeof(struct tcon_data_priority_s));
tcon_mm_table.data_size = kcalloc(cnt, sizeof(unsigned int), GFP_KERNEL);
if (!tcon_mm_table.data_size)
return -1;
tcon_mm_table.data_multi = kcalloc(cnt, sizeof(struct tcon_data_multi_s), GFP_KERNEL);
if (!tcon_mm_table.data_multi)
return -1;
mem_vaddr = tcon_rmem.bin_path_rmem.mem_vaddr;
for (i = 0; i < tcon_mm_table.block_cnt; i++) {
n = 32 + (i * 256);
data_size = mem_vaddr[n] |
(mem_vaddr[n + 1] << 8) |
(mem_vaddr[n + 2] << 16) |
(mem_vaddr[n + 3] << 24);
if (data_size == 0) {
LCDERR("%s: block[%d] size is zero\n", __func__, i);
continue;
}
tcon_mm_table.data_mem_vaddr[i] =
kcalloc(data_size, sizeof(unsigned char), GFP_KERNEL);
if (!tcon_mm_table.data_mem_vaddr[i])
continue;
tcon_mm_table.data_size[i] = data_size;
}
return 0;
}
static void lcd_tcon_axi_mem_config_tl1(void)
{
unsigned int size[3] = {4162560, 4162560, 1960440};
unsigned int total_size = 0, temp_size = 0;
int i;
for (i = 0; i < lcd_tcon_conf->axi_bank; i++)
total_size += size[i];
if (total_size > tcon_rmem.axi_mem_size) {
LCDERR("%s: tcon axi_mem size 0x%x is not enough, need 0x%x\n",
__func__, tcon_rmem.axi_mem_size, total_size);
return;
}
tcon_rmem.axi_rmem =
kcalloc(lcd_tcon_conf->axi_bank,
sizeof(struct tcon_rmem_config_s), GFP_KERNEL);
if (!tcon_rmem.axi_rmem)
return;
for (i = 0; i < lcd_tcon_conf->axi_bank; i++) {
tcon_rmem.axi_rmem[i].mem_paddr =
tcon_rmem.axi_mem_paddr + temp_size;
tcon_rmem.axi_rmem[i].mem_vaddr = NULL;
tcon_rmem.axi_rmem[i].mem_size = size[i];
temp_size += size[i];
}
}
static void lcd_tcon_axi_mem_config_t5(void)
{
unsigned int size[2] = {0x00800000, 0x200000};
unsigned int reg[2] = {0x261, 0x1a9};
unsigned int total_size = 0, temp_size = 0;
int i;
for (i = 0; i < lcd_tcon_conf->axi_bank; i++)
total_size += size[i];
if (total_size > tcon_rmem.axi_mem_size) {
LCDERR("%s: tcon axi_mem size 0x%x is not enough, need 0x%x\n",
__func__, tcon_rmem.axi_mem_size, total_size);
return;
}
tcon_rmem.axi_rmem =
kcalloc(lcd_tcon_conf->axi_bank,
sizeof(struct tcon_rmem_config_s), GFP_KERNEL);
if (!tcon_rmem.axi_rmem)
return;
lcd_tcon_conf->axi_reg = kcalloc(lcd_tcon_conf->axi_bank,
sizeof(unsigned int), GFP_KERNEL);
if (!lcd_tcon_conf->axi_reg) {
kfree(tcon_rmem.axi_rmem);
return;
}
temp_size = 0;
for (i = 0; i < lcd_tcon_conf->axi_bank; i++) {
tcon_rmem.axi_rmem[i].mem_paddr =
tcon_rmem.axi_mem_paddr + temp_size;
tcon_rmem.axi_rmem[i].mem_vaddr = NULL;
tcon_rmem.axi_rmem[i].mem_size = size[i];
temp_size += size[i];
lcd_tcon_conf->axi_reg[i] = reg[i];
}
}
static void lcd_tcon_axi_mem_config_t5d(void)
{
unsigned int size = 0x00500000;
unsigned int reg = 0x261;
if (size > tcon_rmem.axi_mem_size) {
LCDERR("%s: tcon axi_mem size 0x%x is not enough, need 0x%x\n",
__func__, tcon_rmem.axi_mem_size, size);
return;
}
tcon_rmem.axi_rmem =
kzalloc(sizeof(struct tcon_rmem_config_s), GFP_KERNEL);
if (!tcon_rmem.axi_rmem)
return;
lcd_tcon_conf->axi_reg = kzalloc(sizeof(unsigned int), GFP_KERNEL);
if (!lcd_tcon_conf->axi_reg) {
kfree(tcon_rmem.axi_rmem);
return;
}
tcon_rmem.axi_rmem->mem_paddr = tcon_rmem.axi_mem_paddr;
tcon_rmem.axi_rmem->mem_vaddr = NULL;
tcon_rmem.axi_rmem->mem_size = size;
*lcd_tcon_conf->axi_reg = reg;
}
static void lcd_tcon_axi_mem_secure_tl1(void)
{
#ifdef CONFIG_AMLOGIC_TEE
int ret;
tcon_local_cfg.secure_cfg.handle = 0;
ret = tee_protect_mem_by_type(TEE_MEM_TYPE_TCON,
tcon_rmem.axi_mem_paddr,
tcon_rmem.axi_mem_size,
&tcon_local_cfg.secure_cfg.handle);
if (ret) {
tcon_local_cfg.secure_cfg.protect = 0;
LCDERR("%s: tee_protect_mem failed! protect 0, ret: %d\n",
__func__, ret);
} else {
tcon_local_cfg.secure_cfg.protect = 1;
}
#endif
}
static void lcd_tcon_axi_mem_secure_t5(void)
{
/* only protect od mem */
#ifdef CONFIG_AMLOGIC_TEE
int ret;
if (!tcon_rmem.axi_rmem)
return;
tcon_local_cfg.secure_cfg.handle = 0;
ret = tee_protect_mem_by_type(TEE_MEM_TYPE_TCON,
tcon_rmem.axi_rmem[0].mem_paddr,
tcon_rmem.axi_rmem[0].mem_size,
&tcon_local_cfg.secure_cfg.handle);
if (ret) {
tcon_local_cfg.secure_cfg.protect = 0;
LCDERR("%s: tee_protect_mem failed! protect 0, ret: %d\n",
__func__, ret);
LCDERR("%s: mem_start: 0x%08x, mem_size: 0x%x\n",
__func__, (unsigned int)tcon_rmem.axi_rmem[0].mem_paddr,
tcon_rmem.axi_rmem[0].mem_size);
} else {
tcon_local_cfg.secure_cfg.protect = 1;
}
#endif
}
static int lcd_tcon_mem_config(void)
{
unsigned int max_size;
int ret;
max_size = lcd_tcon_conf->axi_mem_size + lcd_tcon_conf->bin_path_size;
if (tcon_rmem.rsv_mem_size < max_size) {
LCDERR("%s: tcon mem size 0x%x is not enough, need 0x%x\n",
__func__, tcon_rmem.rsv_mem_size, max_size);
return -1;
}
tcon_rmem.axi_mem_size = lcd_tcon_conf->axi_mem_size;
tcon_rmem.axi_mem_paddr = tcon_rmem.rsv_mem_paddr;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("%s: axi_mem paddr: 0x%08x, size: 0x%x\n",
__func__, (unsigned int)tcon_rmem.axi_mem_paddr,
tcon_rmem.axi_mem_size);
if (lcd_tcon_conf->tcon_axi_mem_config)
lcd_tcon_conf->tcon_axi_mem_config();
if (lcd_tcon_conf->tcon_axi_mem_secure)
lcd_tcon_conf->tcon_axi_mem_secure();
tcon_rmem.bin_path_rmem.mem_size = lcd_tcon_conf->bin_path_size;
tcon_rmem.bin_path_rmem.mem_paddr =
tcon_rmem.axi_mem_paddr + tcon_rmem.axi_mem_size;
tcon_rmem.bin_path_rmem.mem_vaddr =
lcd_tcon_paddrtovaddr(tcon_rmem.bin_path_rmem.mem_paddr,
tcon_rmem.bin_path_rmem.mem_size);
if ((lcd_debug_print_flag & LCD_DBG_PR_NORMAL) &&
tcon_rmem.bin_path_rmem.mem_size > 0)
LCDPR("tcon bin_path paddr: 0x%08x, vaddr: 0x%p, size: 0x%x\n",
(unsigned int)tcon_rmem.bin_path_rmem.mem_paddr,
tcon_rmem.bin_path_rmem.mem_vaddr,
tcon_rmem.bin_path_rmem.mem_size);
ret = lcd_tcon_bin_path_update(tcon_rmem.bin_path_rmem.mem_size);
if (ret)
return -1;
if (tcon_mm_table.version == 0)
ret = lcd_tcon_mm_table_config_v0();
else
ret = lcd_tcon_mm_table_config_v1();
return ret;
}
#ifdef CONFIG_AMLOGIC_TEE
int lcd_tcon_mem_tee_get_status(void)
{
return tcon_local_cfg.secure_cfg.protect;
}
int lcd_tcon_mem_tee_unprotect(void)
{
if (tcon_local_cfg.secure_cfg.protect) {
tee_unprotect_mem(tcon_local_cfg.secure_cfg.handle);
tcon_local_cfg.secure_cfg.protect = 0;
}
return tcon_local_cfg.secure_cfg.protect;
}
#endif
#ifdef CONFIG_CMA
static int lcd_tcon_cma_mem_config(struct cma *cma)
{
struct aml_lcd_drv_s *pdrv = aml_lcd_get_driver(0);
unsigned int mem_size;
tcon_rmem.rsv_mem_paddr = cma_get_base(cma);
LCDPR("tcon resv_mem base:0x%lx, size:0x%lx\n",
(unsigned long)tcon_rmem.rsv_mem_paddr,
cma_get_size(cma));
mem_size = lcd_tcon_conf->rsv_mem_size;
if (cma_get_size(cma) < mem_size) {
tcon_rmem.flag = 0;
} else {
tcon_rmem.rsv_mem_vaddr =
dma_alloc_from_contiguous(pdrv->dev, (mem_size >> PAGE_SHIFT), 0, 0);
if (!tcon_rmem.rsv_mem_vaddr) {
LCDERR("tcon resv_mem alloc failed\n");
} else {
LCDPR("tcon resv_mem dma_alloc=0x%x\n", mem_size);
tcon_rmem.rsv_mem_size = mem_size;
tcon_rmem.flag = 2; /* cma memory */
}
}
return 0;
}
#endif
static irqreturn_t lcd_tcon_isr(int irq, void *dev_id)
{
struct aml_lcd_drv_s *pdrv = aml_lcd_get_driver(0);
unsigned int temp;
if ((pdrv->status & LCD_STATUS_IF_ON) == 0)
return IRQ_HANDLED;
temp = lcd_tcon_read(pdrv, TCON_INTR_RO);
if (temp & 0x2) {
LCDPR("%s: tcon sw_reset triggered\n", __func__);
lcd_tcon_core_update(pdrv);
}
if (temp & 0x40)
LCDPR("%s: tcon ddr interface error triggered\n", __func__);
return IRQ_HANDLED;
}
static void lcd_tcon_intr_init(struct aml_lcd_drv_s *pdrv)
{
unsigned int tcon_irq = 0;
if (!pdrv->res_tcon_irq) {
LCDERR("res_tcon_irq is null\n");
return;
}
tcon_irq = pdrv->res_tcon_irq->start;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("tcon_irq: %d\n", tcon_irq);
if (request_irq(tcon_irq, lcd_tcon_isr, IRQF_SHARED,
"lcd_tcon", (void *)"lcd_tcon")) {
LCDERR("can't request lcd_tcon irq\n");
} else {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDPR("request lcd_tcon irq successful\n");
}
lcd_tcon_write(pdrv, TCON_INTR_MASKN, TCON_INTR_MASKN_VAL);
}
static int lcd_tcon_load_init_data_from_unifykey(void)
{
int key_len, data_len, ret;
data_len = tcon_mm_table.core_reg_table_size;
tcon_mm_table.core_reg_table =
kcalloc(data_len, sizeof(unsigned char), GFP_KERNEL);
if (!tcon_mm_table.core_reg_table)
return -1;
key_len = data_len;
ret = lcd_unifykey_get_no_header("lcd_tcon", tcon_mm_table.core_reg_table, &key_len);
if (ret)
goto lcd_tcon_load_init_data_err;
if (key_len != data_len)
goto lcd_tcon_load_init_data_err;
memset(tcon_local_cfg.bin_ver, 0, TCON_BIN_VER_LEN);
LCDPR("tcon: load init data len: %d\n", data_len);
return 0;
lcd_tcon_load_init_data_err:
kfree(tcon_mm_table.core_reg_table);
tcon_mm_table.core_reg_table = NULL;
LCDERR("%s: !!!!!!tcon unifykey load error!!!!!!\n", __func__);
return -1;
}
static int lcd_tcon_load_init_data_from_unifykey_new(void)
{
int key_len, data_len;
unsigned char *buf, *p;
struct lcd_tcon_init_block_header_s *data_header;
int ret;
data_len = tcon_mm_table.core_reg_table_size +
LCD_TCON_DATA_BLOCK_HEADER_SIZE;
buf = kcalloc(data_len, sizeof(unsigned char), GFP_KERNEL);
if (!buf)
return -1;
key_len = data_len;
ret = lcd_unifykey_get_tcon("lcd_tcon", buf, &key_len);
if (ret)
goto lcd_tcon_load_init_data_new_err;
if (key_len != data_len)
goto lcd_tcon_load_init_data_new_err;
data_header = (struct lcd_tcon_init_block_header_s *)buf;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("unifykey header:\n");
LCDPR("crc32 = 0x%08x\n", data_header->crc32);
LCDPR("block_size = %d\n", data_header->block_size);
LCDPR("chipid = %d\n", data_header->chipid);
LCDPR("name = %s\n", data_header->name);
}
memcpy(tcon_local_cfg.bin_ver, data_header->version,
LCD_TCON_INIT_BIN_VERSION_SIZE);
tcon_local_cfg.bin_ver[TCON_BIN_VER_LEN - 1] = '\0';
data_len = tcon_mm_table.core_reg_table_size;
tcon_mm_table.core_reg_table = kcalloc(data_len, sizeof(unsigned char), GFP_KERNEL);
if (!tcon_mm_table.core_reg_table)
goto lcd_tcon_load_init_data_new_err;
p = buf + LCD_TCON_DATA_BLOCK_HEADER_SIZE;
memcpy(tcon_mm_table.core_reg_table, p, data_len);
kfree(buf);
LCDPR("tcon: load init data len: %d, ver: %s\n",
data_len, tcon_local_cfg.bin_ver);
return 0;
lcd_tcon_load_init_data_new_err:
kfree(buf);
LCDERR("%s: !!!!!!tcon unifykey load error!!!!!!\n", __func__);
return -1;
}
static int lcd_tcon_get_config(struct aml_lcd_drv_s *pdrv)
{
tcon_mm_table.core_reg_table_size = lcd_tcon_conf->reg_table_len;
if (lcd_tcon_conf->core_reg_ver)
lcd_tcon_load_init_data_from_unifykey_new();
else
lcd_tcon_load_init_data_from_unifykey();
if (tcon_mm_table.tcon_data_flag == 0)
lcd_tcon_data_load();
pdrv->tcon_status = tcon_mm_table.valid_flag;
lcd_tcon_intr_init(pdrv);
return 0;
}
static void lcd_tcon_get_config_work(struct work_struct *work)
{
struct aml_lcd_drv_s *pdrv;
bool is_init;
int i = 0;
pdrv = container_of(work, struct aml_lcd_drv_s, tcon_config_work);
is_init = lcd_unifykey_init_get();
while (!is_init) {
if (i++ >= LCD_UNIFYKEY_WAIT_TIMEOUT)
break;
msleep(LCD_UNIFYKEY_RETRY_INTERVAL);
is_init = lcd_unifykey_init_get();
}
if (is_init)
lcd_tcon_get_config(pdrv);
else
LCDPR("tcon: key_init_flag=%d, i=%d\n", is_init, i);
}
/* **********************************
* tcon match data
* **********************************
*/
static struct lcd_tcon_config_s tcon_data_tl1 = {
.tcon_valid = 0,
.core_reg_ver = 0,
.core_reg_width = LCD_TCON_CORE_REG_WIDTH_TL1,
.reg_table_width = LCD_TCON_TABLE_WIDTH_TL1,
.reg_table_len = LCD_TCON_TABLE_LEN_TL1,
.core_reg_start = TCON_CORE_REG_START_TL1,
.top_reg_base = TCON_TOP_BASE,
.reg_top_ctrl = TCON_TOP_CTRL,
.bit_en = BIT_TOP_EN_TL1,
.reg_core_od = REG_CORE_OD_TL1,
.bit_od_en = BIT_OD_EN_TL1,
.reg_ctrl_timing_base = REG_LCD_TCON_MAX,
.ctrl_timing_offset = CTRL_TIMING_OFFSET_TL1,
.ctrl_timing_cnt = CTRL_TIMING_CNT_TL1,
.axi_bank = LCD_TCON_AXI_BANK_TL1,
/*rsv_mem(12M) axi_mem(10M) bin_path(10K)
* |----------------|-------------|
*/
.rsv_mem_size = 0x00c00000,
.axi_mem_size = 0x00a00000,
.bin_path_size = 0x00002800,
.vac_size = 0x00002000, /* 8k */
.demura_set_size = 0x00001000, /* 4k */
.demura_lut_size = 0x00120000, /* 1152k */
.acc_lut_size = 0x00001000, /* 4k */
.axi_reg = NULL,
.tcon_axi_mem_config = lcd_tcon_axi_mem_config_tl1,
.tcon_axi_mem_secure = lcd_tcon_axi_mem_secure_tl1,
.tcon_global_reset = NULL,
.tcon_gamma_pattern = lcd_tcon_gamma_pattern_tl1,
.tcon_enable = lcd_tcon_enable_tl1,
.tcon_disable = lcd_tcon_disable_tl1,
};
static struct lcd_tcon_config_s tcon_data_t5 = {
.tcon_valid = 0,
.core_reg_ver = 1, /* new version with header */
.core_reg_width = LCD_TCON_CORE_REG_WIDTH_T5,
.reg_table_width = LCD_TCON_TABLE_WIDTH_T5,
.reg_table_len = LCD_TCON_TABLE_LEN_T5,
.core_reg_start = TCON_CORE_REG_START_T5,
.top_reg_base = TCON_TOP_BASE,
.reg_top_ctrl = REG_LCD_TCON_MAX,
.bit_en = BIT_TOP_EN_T5,
.reg_core_od = REG_CORE_OD_T5,
.bit_od_en = BIT_OD_EN_T5,
.reg_ctrl_timing_base = REG_LCD_TCON_MAX,
.ctrl_timing_offset = CTRL_TIMING_OFFSET_T5,
.ctrl_timing_cnt = CTRL_TIMING_CNT_T5,
.axi_bank = LCD_TCON_AXI_BANK_T5,
/*rsv_mem(12M) axi_mem(10M) bin_path(10K)
* |----------------|-------------|
*/
.rsv_mem_size = 0x00c00000,
.axi_mem_size = 0x00a00000,
.bin_path_size = 0x00002800,
.vac_size = 0,
.demura_set_size = 0,
.demura_lut_size = 0,
.acc_lut_size = 0,
.axi_reg = NULL,
.tcon_axi_mem_config = lcd_tcon_axi_mem_config_t5,
.tcon_axi_mem_secure = lcd_tcon_axi_mem_secure_t5,
.tcon_global_reset = lcd_tcon_global_reset_t5,
.tcon_gamma_pattern = lcd_tcon_gamma_pattern_t5,
.tcon_enable = lcd_tcon_enable_t5,
.tcon_disable = lcd_tcon_disable_t5,
};
static struct lcd_tcon_config_s tcon_data_t5d = {
.tcon_valid = 0,
.core_reg_ver = 1, /* new version with header */
.core_reg_width = LCD_TCON_CORE_REG_WIDTH_T5D,
.reg_table_width = LCD_TCON_TABLE_WIDTH_T5D,
.reg_table_len = LCD_TCON_TABLE_LEN_T5D,
.core_reg_start = TCON_CORE_REG_START_T5D,
.reg_top_ctrl = REG_LCD_TCON_MAX,
.bit_en = BIT_TOP_EN_T5D,
.reg_core_od = REG_CORE_OD_T5D,
.bit_od_en = BIT_OD_EN_T5D,
.reg_ctrl_timing_base = REG_LCD_TCON_MAX,
.ctrl_timing_offset = CTRL_TIMING_OFFSET_T5D,
.ctrl_timing_cnt = CTRL_TIMING_CNT_T5D,
.axi_bank = LCD_TCON_AXI_BANK_T5D,
.rsv_mem_size = 0x00800000, /* 8M */
.axi_mem_size = 0x00500000, /* 5M */
.bin_path_size = 0x00002800, /* 10K */
.vac_size = 0,
.demura_set_size = 0,
.demura_lut_size = 0,
.acc_lut_size = 0,
.axi_reg = NULL,
.tcon_axi_mem_config = lcd_tcon_axi_mem_config_t5d,
.tcon_axi_mem_secure = lcd_tcon_axi_mem_secure_t5,
.tcon_global_reset = lcd_tcon_global_reset_t5,
.tcon_gamma_pattern = lcd_tcon_gamma_pattern_t5,
.tcon_enable = lcd_tcon_enable_t5,
.tcon_disable = lcd_tcon_disable_t5,
};
static struct lcd_tcon_config_s tcon_data_t3 = {
.tcon_valid = 0,
.core_reg_ver = 1, /* new version with header */
.core_reg_width = LCD_TCON_CORE_REG_WIDTH_T5,
.reg_table_width = LCD_TCON_TABLE_WIDTH_T5,
.reg_table_len = LCD_TCON_TABLE_LEN_T5,
.core_reg_start = TCON_CORE_REG_START_T5,
.top_reg_base = TCON_TOP_BASE,
.reg_top_ctrl = REG_LCD_TCON_MAX,
.bit_en = BIT_TOP_EN_T5,
.reg_core_od = REG_CORE_OD_T5,
.bit_od_en = BIT_OD_EN_T5,
.reg_ctrl_timing_base = REG_LCD_TCON_MAX,
.ctrl_timing_offset = CTRL_TIMING_OFFSET_T5,
.ctrl_timing_cnt = CTRL_TIMING_CNT_T5,
.axi_bank = LCD_TCON_AXI_BANK_T5,
/*rsv_mem(12M) axi_mem(10M) bin_path(10K)
* |----------------|-------------|
*/
.rsv_mem_size = 0x00c00000,
.axi_mem_size = 0x00a00000,
.bin_path_size = 0x00002800,
.vac_size = 0,
.demura_set_size = 0,
.demura_lut_size = 0,
.acc_lut_size = 0,
.axi_reg = NULL,
.tcon_axi_mem_config = lcd_tcon_axi_mem_config_t5,
.tcon_axi_mem_secure = lcd_tcon_axi_mem_secure_t5,
.tcon_global_reset = lcd_tcon_global_reset_t3,
.tcon_gamma_pattern = lcd_tcon_gamma_pattern_t5,
.tcon_enable = lcd_tcon_enable_t5,
.tcon_disable = lcd_tcon_disable_t3,
};
int lcd_tcon_probe(struct aml_lcd_drv_s *pdrv)
{
struct cma *cma;
unsigned int mem_size;
int ret = 0;
lcd_tcon_conf = NULL;
switch (pdrv->data->chip_type) {
case LCD_CHIP_TL1:
case LCD_CHIP_TM2:
lcd_tcon_conf = &tcon_data_tl1;
break;
case LCD_CHIP_T5:
lcd_tcon_conf = &tcon_data_t5;
break;
case LCD_CHIP_T5D:
lcd_tcon_conf = &tcon_data_t5d;
break;
case LCD_CHIP_T3:
lcd_tcon_conf = &tcon_data_t3;
break;
case LCD_CHIP_T5W:
lcd_tcon_conf = &tcon_data_t5;
break;
default:
break;
}
if (!lcd_tcon_conf)
return 0;
switch (pdrv->config.basic.lcd_type) {
case LCD_MLVDS:
lcd_tcon_conf->tcon_valid = 1;
break;
case LCD_P2P:
if (pdrv->data->chip_type == LCD_CHIP_T5D)
lcd_tcon_conf->tcon_valid = 0;
else
lcd_tcon_conf->tcon_valid = 1;
break;
default:
break;
}
if (lcd_tcon_conf->tcon_valid == 0)
return 0;
/* init reserved memory */
ret = of_reserved_mem_device_init(pdrv->dev);
if (ret) {
LCDERR("tcon: init reserved memory failed\n");
} else {
if (!(void *)tcon_rmem.rsv_mem_paddr) {
#ifdef CONFIG_CMA
cma = dev_get_cma_area(pdrv->dev);
if (cma) {
lcd_tcon_cma_mem_config(cma);
lcd_tcon_mem_config();
} else {
LCDERR("tcon: NO CMA\n");
}
#else
LCDERR("tcon axi_mem alloc failed\n");
#endif
} else {
mem_size = tcon_rmem.rsv_mem_size;
if (mem_size < lcd_tcon_conf->rsv_mem_size) {
tcon_rmem.flag = 0;
} else {
tcon_rmem.flag = 1; /* reserved memory */
LCDPR("tcon resv_mem base:0x%lx, size:0x%x\n",
(unsigned long)tcon_rmem.rsv_mem_paddr,
mem_size);
lcd_tcon_mem_config();
}
}
}
INIT_WORK(&pdrv->tcon_config_work, lcd_tcon_get_config_work);
if (lcd_unifykey_init_get())
ret = lcd_tcon_get_config(pdrv);
else
lcd_queue_work(&pdrv->tcon_config_work);
return ret;
}
int lcd_tcon_remove(struct aml_lcd_drv_s *pdrv)
{
int i;
kfree(tcon_mm_table.core_reg_table);
tcon_mm_table.core_reg_table = NULL;
if (tcon_mm_table.version) {
if (tcon_mm_table.data_mem_vaddr) {
for (i = 0; i < tcon_mm_table.block_cnt; i++) {
if (!tcon_mm_table.data_mem_vaddr[i])
continue;
kfree(tcon_mm_table.data_mem_vaddr[i]);
tcon_mm_table.data_mem_vaddr[i] = NULL;
}
kfree(tcon_mm_table.data_mem_vaddr);
tcon_mm_table.data_mem_vaddr = NULL;
}
} else {
lcd_unmap_phyaddr(tcon_rmem.vac_rmem.mem_vaddr);
lcd_unmap_phyaddr(tcon_rmem.demura_set_rmem.mem_vaddr);
lcd_unmap_phyaddr(tcon_rmem.demura_lut_rmem.mem_vaddr);
lcd_unmap_phyaddr(tcon_rmem.acc_lut_rmem.mem_vaddr);
tcon_rmem.vac_rmem.mem_vaddr = NULL;
tcon_rmem.demura_set_rmem.mem_vaddr = NULL;
tcon_rmem.demura_lut_rmem.mem_vaddr = NULL;
tcon_rmem.acc_lut_rmem.mem_vaddr = NULL;
}
if (tcon_rmem.flag == 2) {
LCDPR("tcon free memory: base:0x%lx, size:0x%x\n",
(unsigned long)tcon_rmem.rsv_mem_paddr,
tcon_rmem.rsv_mem_size);
#ifdef CONFIG_CMA
dma_release_from_contiguous(pdrv->dev,
tcon_rmem.rsv_mem_vaddr,
tcon_rmem.rsv_mem_size >> PAGE_SHIFT);
#endif
}
if (lcd_tcon_conf) {
/* lcd_tcon_conf == NULL; */
lcd_tcon_conf->tcon_valid = 0;
}
return 0;
}
static int __init tcon_buf_device_init(struct reserved_mem *rmem, struct device *dev)
{
return 0;
}
static const struct reserved_mem_ops tcon_buf_ops = {
.device_init = tcon_buf_device_init,
};
static int __init tcon_buf_setup(struct reserved_mem *rmem)
{
tcon_rmem.rsv_mem_paddr = rmem->base;
tcon_rmem.rsv_mem_size = rmem->size;
rmem->ops = &tcon_buf_ops;
LCDPR("tcon: Reserved memory: created buf at 0x%lx, size %ld MiB\n",
(unsigned long)rmem->base, (unsigned long)rmem->size / SZ_1M);
return 0;
}
RESERVEDMEM_OF_DECLARE(buf, "amlogic, lcd_tcon-memory", tcon_buf_setup);