blob: 967f5eff93aa3f0eece37b46d39207775cff8ce6 [file]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2021 Amlogic, Inc. All rights reserved.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/types.h>
#include <asm/delay.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/amlogic/media/vout/peripheral_lcd.h>
#include "peripheral_lcd_drv.h"
extern struct per_gpio_s plcd_gpio[PER_GPIO_NUM_MAX];
static unsigned char *table_init_on_dft;
static unsigned char *table_init_off_dft;
unsigned int rgb565_color_data[8] = {
0xffff, 0xf800, 0x7e0, 0x001f, 0x7ff, 0xf81f, 0xffe0, 0x0000};
unsigned int rgb666_color_data[8] = {
0x3ffff, 0x3f000, 0x00fc0, 0x0003f, 0x00fff, 0x3f03f, 0x3ffc0, 0x00000};
unsigned int rgb888_color_data[8] = {
0xffffff, 0xff0000, 0x00ff00, 0x0000ff, 0x00ffff, 0xff00ff, 0xffff00, 0x000000};
inline void plcd_usleep(unsigned int us)
{
if (!us)
return;
else if (us < 20)
udelay(us);
else if (us < 20000)
usleep_range(us, us + 10);
else
msleep_interruptible(us / 1000);
}
void plcd_gpio_probe(unsigned int index)
{
const char *str;
int ret;
if (index >= PER_GPIO_NUM_MAX) {
LCDERR("gpio index %d, exit\n", index);
return;
}
if (plcd_gpio[index].probe_flag) {
if (per_lcd_debug_flag)
LCDPR("gpio %s[%d] is already registered\n", plcd_gpio[index].name, index);
return;
}
/* get gpio name */
ret = of_property_read_string_index(plcd_drv->dev->of_node,
"per_lcd_gpio_names", index, &str);
if (ret) {
LCDERR("failed to get lcd_per_gpio_names: %d\n", index);
str = "unknown";
}
strcpy(plcd_gpio[index].name, str);
/* init gpio flag */
plcd_gpio[index].probe_flag = 1;
plcd_gpio[index].register_flag = 0;
}
int plcd_gpio_regist(int index, int init_value)
{
int value;
if (index >= PER_GPIO_NUM_MAX) {
LCDERR("%s: gpio index %d, exit\n", __func__, index);
return -1;
}
if (plcd_gpio[index].probe_flag == 0) {
LCDERR("%s: gpio [%d] is not probed, exit\n", __func__, index);
return -1;
}
if (plcd_gpio[index].register_flag) {
if (per_lcd_debug_flag) {
LCDPR("%s: gpio %s[%d] is already registered\n",
__func__, plcd_gpio[index].name, index);
}
return 0;
}
switch (init_value) {
case PER_GPIO_OUTPUT_LOW:
value = GPIOD_OUT_LOW;
break;
case PER_GPIO_OUTPUT_HIGH:
value = GPIOD_OUT_HIGH;
break;
case PER_GPIO_INPUT:
default:
value = GPIOD_IN;
break;
}
/* request gpio */
plcd_gpio[index].gpio = devm_gpiod_get_index(plcd_drv->dev, "per_lcd", index, value);
if (IS_ERR(plcd_gpio[index].gpio)) {
LCDERR("register gpio %s[%d]: %p, err: %d\n", plcd_gpio[index].name, index,
plcd_gpio[index].gpio, IS_ERR(plcd_gpio[index].gpio));
return -1;
}
plcd_gpio[index].register_flag = 1;
if (per_lcd_debug_flag) {
LCDPR("register gpio %s[%d]: %p, init value: %d\n",
plcd_gpio[index].name, index, plcd_gpio[index].gpio, init_value);
}
return 0;
}
void plcd_gpio_set(int index, int value)
{
if (per_lcd_debug_flag)
LCDPR("%s: idx:val= [%d, %d]\n", __func__, index, value);
if (index >= PER_GPIO_NUM_MAX) {
LCDERR("gpio index %d, exit\n", index);
return;
}
if (plcd_gpio[index].probe_flag == 0) {
LCDERR("%s: gpio [%d] is not probed\n", __func__, index);
return;
}
if (plcd_gpio[index].register_flag == 1) {
if (per_lcd_debug_flag)
LCDPR("no need regist\n");
} else {
plcd_gpio_regist(index, value);
return;
}
if (IS_ERR_OR_NULL(plcd_gpio[index].gpio)) {
LCDERR("gpio %s[%d]: %p, err: %ld\n", plcd_gpio[index].name, index,
plcd_gpio[index].gpio, PTR_ERR(plcd_gpio[index].gpio));
return;
}
switch (value) {
case PER_GPIO_OUTPUT_LOW:
case PER_GPIO_OUTPUT_HIGH:
gpiod_direction_output(plcd_gpio[index].gpio, value);
break;
case PER_GPIO_INPUT:
default:
gpiod_direction_input(plcd_gpio[index].gpio);
break;
}
if (per_lcd_debug_flag)
LCDPR("set gpio %s[%d] value: %d\n", plcd_gpio[index].name, index, value);
}
int plcd_gpio_set_irq(int index)
{
int irq_pin;
if (index >= PER_GPIO_NUM_MAX) {
LCDERR("gpio index %d, exit\n", index);
return -1;
}
plcd_gpio_regist(index, 2);
irq_pin = desc_to_gpio(plcd_gpio[index].gpio);
plcd_drv->irq_num = gpio_to_irq(irq_pin);
if (!plcd_drv->irq_num) {
LCDERR("gpio to irq failed\n");
return -1;
}
return 0;
}
int plcd_gpio_get(int index)
{
if (index >= PER_GPIO_NUM_MAX) {
LCDERR("gpio index %d, exit\n", index);
return -1;
}
if (plcd_gpio[index].probe_flag == 0) {
LCDERR("%s: gpio [%d] not probed, exit\n", __func__, index);
return -1;
}
if (plcd_gpio[index].register_flag == 0) {
LCDERR("%s: gpio %s[%d] not registered\n", __func__, plcd_gpio[index].name, index);
return -1;
}
if (IS_ERR_OR_NULL(plcd_gpio[index].gpio)) {
LCDERR("gpio %s[%d]: %p, err: %ld\n", plcd_gpio[index].name, index,
plcd_gpio[index].gpio, PTR_ERR(plcd_gpio[index].gpio));
return -1;
}
return gpiod_get_value(plcd_gpio[index].gpio);
}
static int plcd_init_table_dynamic_load(struct device_node *of_node,
struct per_lcd_config_s *pconf, int flag)
{
unsigned char cmd_size, type = 0;
int i = 0, j, val, max_len, step = 0, ret = 0;
unsigned char *table;
char propname[9];
if (flag) {
table = table_init_on_dft;
max_len = PER_INIT_ON_MAX;
sprintf(propname, "init_on");
} else {
table = table_init_off_dft;
max_len = PER_INIT_OFF_MAX;
sprintf(propname, "init_off");
}
if (!table) {
LCDERR("%s: init_table is null\n", __func__);
return -1;
}
while ((i + 1) < max_len) {
/* type */
ret = of_property_read_u32_index(of_node, propname, i, &val);
if (ret) {
LCDERR("%s: get %s type failed, step %d\n", pconf->name, propname, step);
table[i] = PER_LCD_CMD_TYPE_END;
table[i + 1] = 0;
return -1;
}
table[i] = (unsigned char)val;
type = table[i];
/* cmd_size */
ret = of_property_read_u32_index(of_node, propname, (i + 1), &val);
if (ret) {
LCDERR("%s:get %s cmd_size fail, step %d\n", pconf->name, propname, step);
table[i] = PER_LCD_CMD_TYPE_END;
table[i + 1] = 0;
return -1;
}
table[i + 1] = (unsigned char)val;
cmd_size = table[i + 1];
if (type == PER_LCD_CMD_TYPE_END)
break;
if (cmd_size == 0)
goto init_table_dynamic_dts_next;
if ((i + 2 + cmd_size) > max_len) {
LCDERR("%s: %s cmd_size too much, step %d\n", pconf->name, propname, step);
table[i] = PER_LCD_CMD_TYPE_END;
table[i + 1] = 0;
return -1;
}
/* data */
for (j = 0; j < cmd_size; j++) {
ret = of_property_read_u32_index(of_node, propname, (i + 2 + j), &val);
if (ret) {
LCDERR("%s: get %s failed, step %d\n", pconf->name, propname, step);
table[i] = PER_LCD_CMD_TYPE_END;
table[i + 1] = 0;
return -1;
}
table[i + 2 + j] = (unsigned char)val;
}
init_table_dynamic_dts_next:
i += (cmd_size + 2);
step++;
}
if (flag)
pconf->init_on_cnt = i + 2;
else
pconf->init_off_cnt = i + 2;
return 0;
}
static int plcd_init_table_fixed_load(struct device_node *of_node,
struct per_lcd_config_s *pconf, int flag)
{
unsigned char cmd_size;
int i = 0, j, val, max_len, step = 0, ret = 0;
unsigned char *table;
char propname[9];
if (flag) {
table = table_init_on_dft;
max_len = PER_INIT_ON_MAX;
sprintf(propname, "init_on");
} else {
table = table_init_off_dft;
max_len = PER_INIT_OFF_MAX;
sprintf(propname, "init_off");
}
if (!table) {
LCDERR("%s: init_table is null\n", __func__);
return -1;
}
cmd_size = pconf->cmd_size;
while (i < max_len) {
/* group detect */
if ((i + cmd_size) > max_len) {
LCDERR("%s: %s cmd_size too much, step %d\n", pconf->name, propname, step);
table[i] = PER_LCD_CMD_TYPE_END;
return -1;
}
for (j = 0; j < cmd_size; j++) {
ret = of_property_read_u32_index(of_node, propname, (i + j), &val);
if (ret) {
LCDERR("%s: get %s failed, step %d\n", pconf->name, propname, step);
table[i] = PER_LCD_CMD_TYPE_END;
return -1;
}
table[i + j] = (unsigned char)val;
}
if (table[i] == PER_LCD_CMD_TYPE_END)
break;
i += cmd_size;
step++;
}
if (flag)
pconf->init_on_cnt = i + cmd_size;
else
pconf->init_off_cnt = i + cmd_size;
return 0;
}
static int plcd_init_table_dft_malloc(void)
{
table_init_on_dft = kcalloc(PER_INIT_ON_MAX, sizeof(unsigned char), GFP_KERNEL);
if (!table_init_on_dft) {
LCDERR("failed to alloc init_on table\n");
return -1;
}
table_init_off_dft = kcalloc(PER_INIT_OFF_MAX, sizeof(unsigned char), GFP_KERNEL);
if (!table_init_off_dft) {
LCDERR("failed to alloc init_off table\n");
kfree(table_init_on_dft);
return -1;
}
table_init_on_dft[0] = PER_LCD_CMD_TYPE_END;
table_init_on_dft[1] = 0;
table_init_off_dft[0] = PER_LCD_CMD_TYPE_END;
table_init_off_dft[1] = 0;
return 0;
}
int plcd_init_table_save(struct per_lcd_config_s *pconf)
{
if (pconf->init_on_cnt > 0) {
pconf->init_on = kcalloc(pconf->init_on_cnt, sizeof(unsigned char), GFP_KERNEL);
if (!pconf->init_on) {
LCDERR("failed to alloc init_on table\n");
return -1;
}
memcpy(pconf->init_on, table_init_on_dft,
pconf->init_on_cnt * sizeof(unsigned char));
}
if (pconf->init_off_cnt > 0) {
pconf->init_off = kcalloc(pconf->init_off_cnt, sizeof(unsigned char), GFP_KERNEL);
if (!pconf->init_off) {
LCDERR("failed to alloc init_off table\n");
kfree(pconf->init_on);
return -1;
}
memcpy(pconf->init_off, table_init_off_dft,
pconf->init_off_cnt * sizeof(unsigned char));
}
return 0;
}
int plcd_get_detail_config_dts(void)
{
struct device_node *np = plcd_drv->dev->of_node;
struct device_node *child;
char per_lcd_propname[20];
unsigned int index, para[20], val;
const char *str;
int ret, i;
/* get device config */
index = plcd_drv->dev_index;
struct per_lcd_config_s *pcfg = plcd_drv->pcfg;
sprintf(per_lcd_propname, "per_lcd_%d", index);
LCDPR("load: %s\n", per_lcd_propname);
child = of_get_child_by_name(np, per_lcd_propname);
if (!child) {
LCDERR("child device_node is null\n");
return -1;
}
ret = of_property_read_string(child, "peripheral_lcd_dev_name", &str);
if (ret) {
LCDERR("failed to get peripheral_lcd_dev_name\n");
return -1;
}
strncpy(pcfg->name, str, sizeof(pcfg->name));
pcfg->name[sizeof(pcfg->name) - 1] = '\0';
ret = of_property_read_u32_array(child, "resolution", para, 2);
if (ret) {
LCDERR("failed to get peripheral_lcd resolution\n");
return -1;
}
pcfg->h = (unsigned short)para[0];
pcfg->v = (unsigned short)para[1];
LCDPR("get peripheral_lcd h = %d, v = %d\n", pcfg->h, pcfg->v);
ret = of_property_read_u32_array(child, "per_lcd_attr", para, 6);
if (ret)
LCDERR("failed to get per_lcd_attr\n");
pcfg->cfmt = ret ? CFMT_RGB888 : (unsigned char)para[0];
LCDPR("get peripheral_lcd color_format = %d\n", pcfg->cfmt);
ret = of_property_read_u32(child, "type", para);
if (ret) {
LCDERR("failed to get type\n");
return -1;
}
pcfg->type = (unsigned char)para[0];
LCDPR("type: %d\n", pcfg->type);
if (pcfg->type >= PLCD_TYPE_MAX) {
LCDERR("type num is out of support\n");
return -1;
}
ret = plcd_init_table_dft_malloc();
if (ret)
return -1;
switch (pcfg->type) {
case PLCD_TYPE_SPI:
case PLCD_TYPE_QSPI:
/* get spi config */
pcfg->spi_cfg.spi_info = kcalloc(1, sizeof(struct spi_board_info), GFP_KERNEL);
ret = of_property_read_u32(child, "spi_bus_num", &val);
if (ret) {
LCDERR("failed to get spi_bus_num\n");
return -1;
}
pcfg->spi_cfg.spi_info->bus_num = val;
if (per_lcd_debug_flag)
LCDPR("spi bus_num: %d\n", pcfg->spi_cfg.spi_info->bus_num);
ret = of_property_read_u32(child, "spi_chip_select", &val);
if (ret)
LCDERR("failed to get spi_chip_select\n");
pcfg->spi_cfg.spi_info->chip_select = ret ? 0 : val;
if (per_lcd_debug_flag)
LCDPR("spi chip_select: %d\n", pcfg->spi_cfg.spi_info->chip_select);
ret = of_property_read_u32(child, "spi_max_frequency", &val);
if (ret)
LCDERR("failed to get spi_max_frequency\n");
pcfg->spi_cfg.spi_info->max_speed_hz = ret ? 1000000 : val;
if (per_lcd_debug_flag)
LCDPR("spi max_speed_hz: %d\n", pcfg->spi_cfg.spi_info->max_speed_hz);
ret = of_property_read_u32(child, "spi_mode", &val);
if (ret)
LCDERR("failed to get spi_mode\n");
pcfg->spi_cfg.spi_info->mode = ret ? 0 : val;
if (per_lcd_debug_flag)
LCDPR("spi mode: %d\n", pcfg->spi_cfg.spi_info->mode);
ret = of_property_read_u32_array(child, "spi_cs_delay", para, 2);
pcfg->spi_cfg.cs_hold_delay = ret ? 0 : para[0];
pcfg->spi_cfg.cs_clk_delay = ret ? 0 : para[1];
break;
case PLCD_TYPE_MCU_8080:
ret = of_property_read_u32(child, "max_gpio_num", para);
if (ret) {
LCDERR("failed to get max_gpio_num\n");
return -1;
}
pcfg->i8080_cfg.max_gpio_num = para[0];
for (i = 0; i < pcfg->i8080_cfg.max_gpio_num; i++)
plcd_gpio_probe(i);
if (per_lcd_debug_flag)
LCDPR("max_gpio_num: %d\n", pcfg->i8080_cfg.max_gpio_num);
ret = of_property_read_u32_array(child, "mcu_attr", para, 13);
pcfg->i8080_cfg.reset_index = ret ? 0 : para[0];
pcfg->i8080_cfg.nCS_index = ret ? 0 : para[1];
pcfg->i8080_cfg.nRD_index = ret ? 0 : para[2];
pcfg->i8080_cfg.nWR_index = ret ? 0 : para[3];
pcfg->i8080_cfg.nRS_index = ret ? 0 : para[4];
pcfg->i8080_cfg.data0_index = ret ? 0 : para[5];
pcfg->i8080_cfg.data1_index = ret ? 0 : para[6];
pcfg->i8080_cfg.data2_index = ret ? 0 : para[7];
pcfg->i8080_cfg.data3_index = ret ? 0 : para[8];
pcfg->i8080_cfg.data4_index = ret ? 0 : para[9];
pcfg->i8080_cfg.data5_index = ret ? 0 : para[10];
pcfg->i8080_cfg.data6_index = ret ? 0 : para[11];
pcfg->i8080_cfg.data7_index = ret ? 0 : para[12];
break;
case PLCD_TYPE_MAX:
default:
break;
}
/* get init_cmd */
ret = of_property_read_u32(child, "cmd_size", &val);
if (ret) {
LCDPR("no cmd_size\n");
pcfg->cmd_size = 0;
goto per_lcd_get_config_init_table_err;
} else {
pcfg->cmd_size = (unsigned char)val;
}
if (per_lcd_debug_flag)
LCDPR("%s: cmd_size = %d\n", pcfg->name, pcfg->cmd_size);
if (pcfg->cmd_size == PER_LCD_CMD_SIZE_DYNAMIC) {
ret = plcd_init_table_dynamic_load(child, pcfg, 1);
if (ret)
goto per_lcd_get_config_init_table_err;
plcd_init_table_dynamic_load(child, pcfg, 0);
} else {
ret = plcd_init_table_fixed_load(child, pcfg, 1);
if (ret)
goto per_lcd_get_config_init_table_err;
plcd_init_table_fixed_load(child, pcfg, 0);
}
plcd_drv->init_flag = 1;
ret = plcd_init_table_save(pcfg);
if (ret)
goto per_lcd_get_config_init_table_err;
kfree(table_init_on_dft);
kfree(table_init_off_dft);
return 0;
per_lcd_get_config_init_table_err:
kfree(table_init_on_dft);
kfree(table_init_off_dft);
return -1;
}