blob: ad28c236c3a6a1dd0ccf0828a8a84a637fe0ed2e [file] [log] [blame]
/*
* drivers/amlogic/media/vout/lcd/lcd_tcon.c
*
* Copyright (C) 2017 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 <linux/init.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/slab.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>
#include "lcd_common.h"
#include "lcd_reg.h"
#include "lcd_tcon.h"
/*#include "tcon_ceds.h"*/
#define TCON_INTR_MASKN_VAL 0x0 /* default mask all */
static struct tcon_rmem_s tcon_rmem = {
.flag = 0,
.mem_vaddr = NULL,
.mem_paddr = 0,
.mem_size = 0,
};
static struct lcd_tcon_data_s *lcd_tcon_data;
static struct delayed_work lcd_tcon_delayed_work;
static int lcd_tcon_valid_check(void)
{
if (lcd_tcon_data == NULL) {
LCDERR("invalid tcon data\n");
return -1;
}
if (lcd_tcon_data->tcon_valid == 0) {
LCDERR("invalid tcon\n");
return -1;
}
return 0;
}
unsigned int lcd_tcon_reg_read(unsigned int addr)
{
unsigned int val;
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return 0;
if (addr < TCON_TOP_BASE) {
if (lcd_tcon_data->core_reg_width == 8)
val = lcd_tcon_read_byte(addr);
else
val = lcd_tcon_read(addr);
} else {
val = lcd_tcon_read(addr);
}
return val;
}
void lcd_tcon_reg_write(unsigned int addr, unsigned int val)
{
unsigned char temp;
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return;
if (addr < TCON_TOP_BASE) {
if (lcd_tcon_data->core_reg_width == 8) {
temp = (unsigned char)val;
lcd_tcon_write_byte(addr, temp);
} else {
lcd_tcon_write(addr, val);
}
} else {
lcd_tcon_write(addr, val);
}
}
static void lcd_tcon_od_check(unsigned char *table)
{
unsigned int reg, bit;
if (lcd_tcon_data->reg_core_od == REG_LCD_TCON_MAX)
return;
reg = lcd_tcon_data->reg_core_od;
bit = lcd_tcon_data->bit_od_en;
if (((table[reg] >> bit) & 1) == 0)
return;
if (tcon_rmem.flag == 0) {
table[reg] &= ~(1 << bit);
LCDPR("%s: invalid fb, disable od function\n", __func__);
}
}
void lcd_tcon_core_reg_update(void)
{
unsigned char *table;
unsigned int len, temp;
int i, ret;
ret = lcd_tcon_valid_check();
if (ret)
return;
len = lcd_tcon_data->reg_table_len;
table = lcd_tcon_data->reg_table;
if (table == NULL) {
LCDERR("%s: table is NULL\n", __func__);
return;
}
lcd_tcon_od_check(table);
if (lcd_tcon_data->core_reg_width == 8) {
for (i = 0; i < len; i++) {
lcd_tcon_write_byte((i + TCON_CORE_REG_START),
table[i]);
}
} else {
for (i = 0; i < len; i++)
lcd_tcon_write((i + TCON_CORE_REG_START), table[i]);
}
LCDPR("tcon core regs update\n");
if (lcd_tcon_data->reg_core_od != REG_LCD_TCON_MAX) {
i = lcd_tcon_data->reg_core_od;
if (lcd_tcon_data->core_reg_width == 8)
temp = lcd_tcon_read_byte(i + TCON_CORE_REG_START);
else
temp = lcd_tcon_read(i + TCON_CORE_REG_START);
LCDPR("%s: tcon od reg readback: 0x%04x = 0x%04x\n",
__func__, i, temp);
}
}
static int lcd_tcon_top_set_tl1(struct lcd_config_s *pconf)
{
unsigned int axi_reg[3] = {
TCON_AXI_OFST0, TCON_AXI_OFST1, TCON_AXI_OFST2
};
unsigned int addr[3] = {0, 0, 0};
unsigned int size[3] = {4162560, 4162560, 1960440};
int i;
LCDPR("lcd tcon top set\n");
if (tcon_rmem.flag) {
addr[0] = tcon_rmem.mem_paddr;
addr[1] = addr[0] + size[0];
addr[2] = addr[1] + size[1];
for (i = 0; i < 3; i++) {
lcd_tcon_write(axi_reg[i], addr[i]);
LCDPR("set tcon axi_mem_paddr[%d]: 0x%08x\n",
i, addr[i]);
}
} else {
LCDERR("%s: invalid axi_mem\n", __func__);
}
lcd_tcon_write(TCON_CLK_CTRL, 0x001f);
if (pconf->lcd_basic.lcd_type == LCD_P2P) {
switch (pconf->lcd_control.p2p_config->p2p_type) {
case P2P_CHPI:
lcd_tcon_write(TCON_TOP_CTRL, 0x8199);
break;
default:
lcd_tcon_write(TCON_TOP_CTRL, 0x8999);
break;
}
} else {
lcd_tcon_write(TCON_TOP_CTRL, 0x8999);
}
lcd_tcon_write(TCON_PLLLOCK_CNTL, 0x0037);
lcd_tcon_write(TCON_RST_CTRL, 0x003f);
lcd_tcon_write(TCON_RST_CTRL, 0x0000);
lcd_tcon_write(TCON_DDRIF_CTRL0, 0x33fff000);
lcd_tcon_write(TCON_DDRIF_CTRL1, 0x300300);
return 0;
}
static void lcd_tcon_chpi_bbc_init_tl1(int delay)
{
unsigned int data32;
udelay(delay);
lcd_hiu_setb(HHI_DIF_CSI_PHY_CNTL1, 1, 3, 1);
lcd_hiu_setb(HHI_DIF_CSI_PHY_CNTL1, 1, 19, 1);
lcd_hiu_setb(HHI_DIF_CSI_PHY_CNTL2, 1, 3, 1);
lcd_hiu_setb(HHI_DIF_CSI_PHY_CNTL2, 1, 19, 1);
lcd_hiu_setb(HHI_DIF_CSI_PHY_CNTL3, 1, 3, 1);
lcd_hiu_setb(HHI_DIF_CSI_PHY_CNTL3, 1, 19, 1);
lcd_hiu_setb(HHI_DIF_CSI_PHY_CNTL4, 1, 3, 1);
lcd_hiu_setb(HHI_DIF_CSI_PHY_CNTL4, 1, 19, 1);
lcd_hiu_setb(HHI_DIF_CSI_PHY_CNTL6, 1, 3, 1);
lcd_hiu_setb(HHI_DIF_CSI_PHY_CNTL6, 1, 19, 1);
lcd_hiu_setb(HHI_DIF_CSI_PHY_CNTL7, 1, 3, 1);
lcd_hiu_setb(HHI_DIF_CSI_PHY_CNTL7, 1, 19, 1);
LCDPR("%s: delay: %dus\n", __func__, delay);
data32 = 0x06020602;
lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL14, 0xff2027ef);
lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL15, 0);
lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL16, 0x80000000);
lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL8, 0);
lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL1, data32);
lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL9, 0);
lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL2, data32);
lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL10, 0);
lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL3, data32);
lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL11, 0);
lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL4, data32);
lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL12, 0);
lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL6, data32);
lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL13, 0);
lcd_hiu_write(HHI_DIF_CSI_PHY_CNTL7, data32);
}
static int lcd_tcon_enable_tl1(struct lcd_config_s *pconf)
{
unsigned int n = 10;
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return -1;
/* step 1: tcon top */
lcd_tcon_top_set_tl1(pconf);
/* step 2: tcon_core_reg_update */
lcd_tcon_core_reg_update();
if (pconf->lcd_basic.lcd_type == LCD_P2P) {
switch (pconf->lcd_control.p2p_config->p2p_type) {
case P2P_CHPI:
lcd_tcon_chpi_bbc_init_tl1(n);
break;
default:
break;
}
}
/* step 3: tcon_top_output_set */
lcd_tcon_write(TCON_OUT_CH_SEL0, 0x76543210);
lcd_tcon_write(TCON_OUT_CH_SEL1, 0xba98);
/* step 4: tcon_intr_mask */
lcd_tcon_write(TCON_INTR_MASKN, TCON_INTR_MASKN_VAL);
return 0;
}
static irqreturn_t lcd_tcon_isr(int irq, void *dev_id)
{
struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
unsigned int temp;
if ((lcd_drv->lcd_status & LCD_STATUS_IF_ON) == 0)
return IRQ_HANDLED;
temp = lcd_tcon_read(TCON_INTR_RO);
if (temp & 0x2) {
LCDPR("%s: tcon sw_reset triggered\n", __func__);
lcd_tcon_core_reg_update();
}
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 *lcd_drv)
{
unsigned int tcon_irq = 0;
if (!lcd_drv->res_tcon_irq) {
LCDERR("res_tcon_irq is null\n");
return;
}
tcon_irq = lcd_drv->res_tcon_irq->start;
if (lcd_debug_print_flag)
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)
LCDPR("request lcd_tcon irq successful\n");
}
lcd_tcon_write(TCON_INTR_MASKN, TCON_INTR_MASKN_VAL);
}
static int lcd_tcon_config(struct aml_lcd_drv_s *lcd_drv)
{
int key_len, reg_len, ret;
#if 1
/* get reg table from unifykey */
reg_len = lcd_tcon_data->reg_table_len;
if (lcd_tcon_data->reg_table == NULL) {
lcd_tcon_data->reg_table =
kcalloc(reg_len, sizeof(unsigned char), GFP_KERNEL);
if (!lcd_tcon_data->reg_table) {
LCDERR("%s: Not enough memory\n", __func__);
return -1;
}
}
key_len = reg_len;
ret = lcd_unifykey_get_no_header("lcd_tcon",
lcd_tcon_data->reg_table, &key_len);
if (ret) {
kfree(lcd_tcon_data->reg_table);
lcd_tcon_data->reg_table = NULL;
LCDERR("%s: !!!!!!!!tcon unifykey load error!!!!!!!!\n",
__func__);
return -1;
}
if (key_len != reg_len) {
kfree(lcd_tcon_data->reg_table);
lcd_tcon_data->reg_table = NULL;
LCDERR("%s: !!!!!!!!tcon unifykey load length error!!!!!!!!\n",
__func__);
return -1;
}
LCDPR("tcon: load unifykey len: %d\n", key_len);
#else
reg_len = lcd_tcon_data->reg_table_len;
lcd_tcon_data->reg_table = uhd_tcon_setting_ceds_h10;
key_len = sizeof(uhd_tcon_setting_ceds_h10)/sizeof(unsigned char);
if (key_len != reg_len) {
lcd_tcon_data->reg_table = NULL;
LCDERR("%s: !!!!!!!!tcon unifykey load length error!!!!!!!!\n",
__func__);
return -1;
}
LCDPR("tcon: load default table len: %d\n", key_len);
#endif
lcd_tcon_intr_init(lcd_drv);
return 0;
}
static void lcd_tcon_config_delayed(struct work_struct *work)
{
struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver();
int key_init_flag = 0;
int i = 0;
key_init_flag = key_unify_get_init_flag();
while (key_init_flag == 0) {
if (i++ >= LCD_UNIFYKEY_WAIT_TIMEOUT)
break;
msleep(LCD_UNIFYKEY_RETRY_INTERVAL);
key_init_flag = key_unify_get_init_flag();
}
LCDPR("tcon: key_init_flag=%d, i=%d\n", key_init_flag, i);
if (key_init_flag)
lcd_tcon_config(lcd_drv);
}
/* **********************************
* tcon function api
* **********************************
*/
#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 (lcd_tcon_data->reg_table == NULL) {
LCDERR("%s: reg_table is null\n", __func__);
return;
}
buf = kcalloc(PR_BUF_MAX, sizeof(char), GFP_KERNEL);
if (buf == NULL) {
LCDERR("%s: buf malloc error\n", __func__);
return;
}
LCDPR("%s:\n", __func__);
cnt = lcd_tcon_data->reg_table_len;
for (i = 0; i < cnt; i += 16) {
n = snprintf(buf, PR_BUF_MAX, "0x%04x: ", i);
for (j = 0; j < 16; j++) {
if ((i + j) >= cnt)
break;
n += snprintf(buf+n, PR_BUF_MAX, " 0x%02x",
lcd_tcon_data->reg_table[i+j]);
}
buf[n] = '\0';
pr_info("%s\n", buf);
}
kfree(buf);
}
void lcd_tcon_reg_readback_print(void)
{
int i, j, n, cnt;
char *buf;
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return;
buf = kcalloc(PR_BUF_MAX, sizeof(char), GFP_KERNEL);
if (buf == NULL) {
LCDERR("%s: buf malloc error\n", __func__);
return;
}
LCDPR("%s:\n", __func__);
cnt = lcd_tcon_data->reg_table_len;
for (i = 0; i < cnt; i += 16) {
n = snprintf(buf, PR_BUF_MAX, "0x%04x: ", i);
for (j = 0; j < 16; j++) {
if ((i + j) >= cnt)
break;
if (lcd_tcon_data->core_reg_width == 8) {
n += snprintf(buf+n, PR_BUF_MAX, " 0x%02x",
lcd_tcon_read_byte(i+j));
} else {
n += snprintf(buf+n, PR_BUF_MAX, " 0x%02x",
lcd_tcon_read(i+j));
}
}
buf[n] = '\0';
pr_info("%s\n", buf);
}
kfree(buf);
}
int lcd_tcon_info_print(char *buf, int offset)
{
int len = 0, n, ret;
ret = lcd_tcon_valid_check();
if (ret)
return len;
n = lcd_debug_info_len(len + offset);
len += snprintf((buf+len), n,
"tcon info:\n"
"core_reg_width: %d\n"
"reg_table_len: %d\n"
"axi_mem paddr: 0x%lx\n"
"axi_mem size: 0x%x\n\n",
lcd_tcon_data->core_reg_width,
lcd_tcon_data->reg_table_len,
(unsigned long)tcon_rmem.mem_paddr,
tcon_rmem.mem_size);
return len;
}
int lcd_tcon_reg_table_size_get(void)
{
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return -1;
return lcd_tcon_data->reg_table_len;
}
unsigned char *lcd_tcon_reg_table_get(void)
{
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return NULL;
return lcd_tcon_data->reg_table;
}
int lcd_tcon_core_reg_get(unsigned char *buf, unsigned int size)
{
int i, ret;
ret = lcd_tcon_valid_check();
if (ret)
return -1;
if (size > lcd_tcon_data->reg_table_len) {
LCDERR("%s: size is not enough\n", __func__);
return -1;
}
if (lcd_tcon_data->core_reg_width == 8) {
for (i = 0; i < size; i++)
buf[i] = lcd_tcon_read_byte(i + TCON_CORE_REG_START);
} else {
for (i = 0; i < size; i++)
buf[i] = lcd_tcon_read(i + TCON_CORE_REG_START);
}
return 0;
}
int lcd_tcon_od_set(int flag)
{
unsigned int reg, bit, temp;
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return -1;
if (lcd_tcon_data->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;
}
}
reg = lcd_tcon_data->reg_core_od;
bit = lcd_tcon_data->bit_od_en;
if (lcd_tcon_data->core_reg_width == 8)
temp = lcd_tcon_read_byte(reg + TCON_CORE_REG_START);
else
temp = lcd_tcon_read(reg + TCON_CORE_REG_START);
if (flag)
temp |= (1 << bit);
else
temp &= ~(1 << bit);
temp &= 0xff;
if (lcd_tcon_data->core_reg_width == 8)
lcd_tcon_write_byte((reg + TCON_CORE_REG_START), temp);
else
lcd_tcon_write((reg + TCON_CORE_REG_START), temp);
msleep(100);
LCDPR("%s: %d\n", __func__, flag);
return 0;
}
int lcd_tcon_od_get(void)
{
unsigned int reg, bit, temp;
int ret = 0;
ret = lcd_tcon_valid_check();
if (ret)
return 0;
if (lcd_tcon_data->reg_core_od == REG_LCD_TCON_MAX) {
LCDERR("%s: invalid od reg\n", __func__);
return 0;
}
reg = lcd_tcon_data->reg_core_od;
bit = lcd_tcon_data->bit_od_en;
if (lcd_tcon_data->core_reg_width == 8)
temp = lcd_tcon_read_byte(reg + TCON_CORE_REG_START);
else
temp = lcd_tcon_read(reg + TCON_CORE_REG_START);
ret = ((temp >> bit) & 1);
return ret;
}
int lcd_tcon_enable(struct lcd_config_s *pconf)
{
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return -1;
if (lcd_tcon_data->tcon_enable)
lcd_tcon_data->tcon_enable(pconf);
return 0;
}
#define TCON_CTRL_TIMING_OFFSET 12
void lcd_tcon_disable(void)
{
unsigned int reg, i, cnt, offset, bit;
int ret;
ret = lcd_tcon_valid_check();
if (ret)
return;
LCDPR("%s\n", __func__);
/* disable tcon intr */
lcd_tcon_write(TCON_INTR_MASKN, 0);
/* disable over_drive */
if (lcd_tcon_data->reg_core_od != REG_LCD_TCON_MAX) {
reg = lcd_tcon_data->reg_core_od + TCON_CORE_REG_START;
bit = lcd_tcon_data->bit_od_en;
if (lcd_tcon_data->core_reg_width == 8)
lcd_tcon_setb_byte(reg, 0, bit, 1);
else
lcd_tcon_setb(reg, 0, bit, 1);
msleep(100);
}
/* disable all ctrl signal */
if (lcd_tcon_data->reg_core_ctrl_timing_base == REG_LCD_TCON_MAX)
goto lcd_tcon_disable_next;
reg = lcd_tcon_data->reg_core_ctrl_timing_base + TCON_CORE_REG_START;
offset = lcd_tcon_data->ctrl_timing_offset;
cnt = lcd_tcon_data->ctrl_timing_cnt;
for (i = 0; i < cnt; i++) {
if (lcd_tcon_data->core_reg_width == 8)
lcd_tcon_setb_byte((reg + (i * offset)), 1, 3, 1);
else
lcd_tcon_setb((reg + (i * offset)), 1, 3, 1);
}
/* disable top */
lcd_tcon_disable_next:
if (lcd_tcon_data->reg_top_ctrl != REG_LCD_TCON_MAX) {
reg = lcd_tcon_data->reg_top_ctrl;
bit = lcd_tcon_data->bit_en;
lcd_tcon_setb(reg, 0, bit, 1);
}
}
/* **********************************
* tcon match data
* **********************************
*/
static struct lcd_tcon_data_s tcon_data_tl1 = {
.tcon_valid = 0,
.core_reg_width = LCD_TCON_CORE_REG_WIDTH_TL1,
.reg_table_len = LCD_TCON_TABLE_LEN_TL1,
.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_core_ctrl_timing_base = REG_LCD_TCON_MAX,
.ctrl_timing_offset = CTRL_TIMING_OFFSET_TL1,
.ctrl_timing_cnt = CTRL_TIMING_CNT_TL1,
.axi_mem_size = 0xc00000,
.reg_table = NULL,
.tcon_enable = lcd_tcon_enable_tl1,
};
int lcd_tcon_probe(struct aml_lcd_drv_s *lcd_drv)
{
struct cma *cma;
unsigned int mem_size;
int key_init_flag = 0;
int ret = 0;
lcd_tcon_data = NULL;
switch (lcd_drv->data->chip_type) {
case LCD_CHIP_TL1:
switch (lcd_drv->lcd_config->lcd_basic.lcd_type) {
case LCD_MLVDS:
case LCD_P2P:
lcd_tcon_data = &tcon_data_tl1;
lcd_tcon_data->tcon_valid = 1;
break;
default:
break;
}
break;
default:
break;
}
if (lcd_tcon_data == NULL)
return 0;
/* init reserved memory */
ret = of_reserved_mem_device_init(lcd_drv->dev);
if (ret) {
LCDERR("tcon: init reserved memory failed\n");
} else {
if ((void *)tcon_rmem.mem_paddr == NULL) {
#ifdef CONFIG_CMA
cma = dev_get_cma_area(lcd_drv->dev);
if (cma) {
tcon_rmem.mem_paddr = cma_get_base(cma);
LCDPR("tcon axi_mem base:0x%lx, size:0x%lx\n",
(unsigned long)tcon_rmem.mem_paddr,
cma_get_size(cma));
mem_size = lcd_tcon_data->axi_mem_size;
tcon_rmem.mem_vaddr = dma_alloc_from_contiguous(
lcd_drv->dev,
(mem_size >> PAGE_SHIFT),
0);
if (tcon_rmem.mem_vaddr == NULL) {
LCDERR("tcon axi_mem alloc failed\n");
} else {
LCDPR("tcon axi_mem dma_alloc=0x%x\n",
mem_size);
tcon_rmem.mem_size = mem_size;
tcon_rmem.flag = 2; /* cma memory */
}
} else {
LCDERR("tcon: NO CMA\n");
}
#else
LCDERR("tcon axi_mem alloc failed\n");
#endif
} else {
tcon_rmem.flag = 1; /* reserved memory */
mem_size = tcon_rmem.mem_size;
LCDPR("tcon axi_mem base:0x%lx, size:0x%x\n",
(unsigned long)tcon_rmem.mem_paddr, mem_size);
}
}
INIT_DELAYED_WORK(&lcd_tcon_delayed_work, lcd_tcon_config_delayed);
key_init_flag = key_unify_get_init_flag();
if (key_init_flag) {
ret = lcd_tcon_config(lcd_drv);
} else {
if (lcd_drv->workqueue) {
queue_delayed_work(lcd_drv->workqueue,
&lcd_tcon_delayed_work,
msecs_to_jiffies(2000));
} else {
schedule_delayed_work(&lcd_tcon_delayed_work,
msecs_to_jiffies(2000));
}
}
return ret;
}
int lcd_tcon_remove(struct aml_lcd_drv_s *lcd_drv)
{
if (tcon_rmem.flag == 2) {
LCDPR("tcon free memory: base:0x%lx, size:0x%x\n",
(unsigned long)tcon_rmem.mem_paddr,
tcon_rmem.mem_size);
#ifdef CONFIG_CMA
dma_release_from_contiguous(lcd_drv->dev,
tcon_rmem.mem_vaddr,
tcon_rmem.mem_size >> PAGE_SHIFT);
#endif
}
if (lcd_tcon_data) {
/* lcd_tcon_data == NULL; */
lcd_tcon_data->tcon_valid = 0;
}
return 0;
}
static int __init tcon_fb_device_init(struct reserved_mem *rmem,
struct device *dev)
{
return 0;
}
static const struct reserved_mem_ops tcon_fb_ops = {
.device_init = tcon_fb_device_init,
};
static int __init tcon_fb_setup(struct reserved_mem *rmem)
{
tcon_rmem.mem_paddr = rmem->base;
tcon_rmem.mem_size = rmem->size;
rmem->ops = &tcon_fb_ops;
LCDPR("tcon: Reserved memory: created fb at 0x%lx, size %ld MiB\n",
(unsigned long)rmem->base, (unsigned long)rmem->size / SZ_1M);
return 0;
}
RESERVEDMEM_OF_DECLARE(fb, "amlogic, lcd_tcon-memory", tcon_fb_setup);