blob: a6e491ed6cf1275e6581ea90a0cf1a8349b12950 [file] [log] [blame]
/*
* drivers/amlogic/media/vout/lcd/lcd_extern/lcd_extern.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/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/amlogic/i2c-amlogic.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
#include <linux/amlogic/media/vout/lcd/lcd_notify.h>
#include "lcd_extern.h"
static struct device *lcd_extern_dev;
static struct aml_lcd_extern_driver_s *lcd_ext_driver;
static unsigned char lcd_ext_config_load;
static unsigned char *table_init_on_dft;
static unsigned char *table_init_off_dft;
struct lcd_ext_gpio_s {
char name[15];
struct gpio_desc *gpio;
int probe_flag;
int register_flag;
};
static struct lcd_ext_gpio_s lcd_extern_gpio[LCD_EXTERN_GPIO_NUM_MAX] = {
{.probe_flag = 0, .register_flag = 0,},
{.probe_flag = 0, .register_flag = 0,},
{.probe_flag = 0, .register_flag = 0,},
{.probe_flag = 0, .register_flag = 0,},
{.probe_flag = 0, .register_flag = 0,},
{.probe_flag = 0, .register_flag = 0,},
};
static struct lcd_extern_config_s lcd_extern_config = {
.index = LCD_EXTERN_INDEX_INVALID,
.name = "invalid",
.type = LCD_EXTERN_MAX,
.status = 0,
.pinmux_valid = 0,
.pinmux_gpio_off = 2,
.key_valid = 0,
.addr_sel = 0,
.i2c_addr = LCD_EXT_I2C_ADDR_INVALID,
.i2c_addr2 = LCD_EXT_I2C_ADDR_INVALID,
.i2c_bus = LCD_EXT_I2C_BUS_INVALID,
.i2c_sck_gpio = LCD_EXT_GPIO_INVALID,
.i2c_sda_gpio = LCD_EXT_GPIO_INVALID,
.spi_gpio_cs = LCD_EXT_GPIO_INVALID,
.spi_gpio_clk = LCD_EXT_GPIO_INVALID,
.spi_gpio_data = LCD_EXT_GPIO_INVALID,
.spi_clk_freq = 0,
.spi_delay_us = 0,
.spi_clk_pol = 1,
.cmd_size = 0,
.table_init_loaded = 0, /* internal use */
.table_init_on_cnt = 0,
.table_init_off_cnt = 0,
.table_init_on = NULL,
.table_init_off = NULL,
};
struct aml_lcd_extern_driver_s *aml_lcd_extern_get_driver(int index)
{
if (lcd_ext_driver == NULL) {
EXTERR("invalid driver\n");
return NULL;
}
if (index >= LCD_EXTERN_INDEX_INVALID) {
EXTERR("invalid driver index: %d\n", index);
return NULL;
}
if (lcd_ext_driver->config->index == index)
return lcd_ext_driver;
EXTERR("invalid driver index: %d\n", index);
return NULL;
}
#ifdef CONFIG_OF
void lcd_extern_gpio_probe(unsigned char index)
{
struct lcd_ext_gpio_s *ext_gpio;
const char *str;
int ret;
if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
EXTERR("gpio index %d, exit\n", index);
return;
}
ext_gpio = &lcd_extern_gpio[index];
if (ext_gpio->probe_flag) {
if (lcd_debug_print_flag) {
EXTPR("gpio %s[%d] is already registered\n",
ext_gpio->name, index);
}
return;
}
/* get gpio name */
ret = of_property_read_string_index(lcd_extern_dev->of_node,
"extern_gpio_names", index, &str);
if (ret) {
EXTERR("failed to get extern_gpio_names: %d\n", index);
str = "unknown";
}
strcpy(ext_gpio->name, str);
/* init gpio flag */
ext_gpio->probe_flag = 1;
ext_gpio->register_flag = 0;
}
void lcd_extern_gpio_unregister(int index)
{
struct lcd_ext_gpio_s *ext_gpio;
if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
EXTERR("gpio index %d, exit\n", index);
return;
}
ext_gpio = &lcd_extern_gpio[index];
if (ext_gpio->probe_flag == 0) {
if (lcd_debug_print_flag) {
EXTPR("gpio %s[%d] is already registered\n",
ext_gpio->name, index);
}
return;
}
if (ext_gpio->register_flag) {
EXTPR("%s: gpio %s[%d] is already registered\n",
__func__, ext_gpio->name, index);
return;
}
if (IS_ERR(ext_gpio->gpio)) {
EXTERR("register gpio %s[%d]: %p, err: %d\n",
ext_gpio->name, index, ext_gpio->gpio,
IS_ERR(ext_gpio->gpio));
ext_gpio->gpio = NULL;
return;
}
/* release gpio */
devm_gpiod_put(lcd_extern_dev, ext_gpio->gpio);
ext_gpio->probe_flag = 0;
ext_gpio->register_flag = 0;
if (lcd_debug_print_flag)
EXTPR("release gpio %s[%d]\n", ext_gpio->name, index);
}
static int lcd_extern_gpio_register(unsigned char index, int init_value)
{
struct lcd_ext_gpio_s *ext_gpio;
int value;
if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
EXTERR("gpio index %d, exit\n", index);
return -1;
}
ext_gpio = &lcd_extern_gpio[index];
if (ext_gpio->probe_flag == 0) {
EXTERR("%s: gpio [%d] is not probed, exit\n", __func__, index);
return -1;
}
if (ext_gpio->register_flag) {
EXTPR("%s: gpio %s[%d] is already registered\n",
__func__, ext_gpio->name, index);
return 0;
}
switch (init_value) {
case LCD_GPIO_OUTPUT_LOW:
value = GPIOD_OUT_LOW;
break;
case LCD_GPIO_OUTPUT_HIGH:
value = GPIOD_OUT_HIGH;
break;
case LCD_GPIO_INPUT:
default:
value = GPIOD_IN;
break;
}
/* request gpio */
ext_gpio->gpio = devm_gpiod_get_index(
lcd_extern_dev, "extern", index, value);
if (IS_ERR(ext_gpio->gpio)) {
EXTERR("register gpio %s[%d]: %p, err: %d\n",
ext_gpio->name, index, ext_gpio->gpio,
IS_ERR(ext_gpio->gpio));
ext_gpio->gpio = NULL;
return -1;
}
ext_gpio->register_flag = 1;
if (lcd_debug_print_flag) {
EXTPR("register gpio %s[%d]: %p, init value: %d\n",
ext_gpio->name, index, ext_gpio->gpio, init_value);
}
return 0;
}
#endif
void lcd_extern_gpio_set(unsigned char index, int value)
{
struct lcd_ext_gpio_s *ext_gpio;
if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
EXTERR("gpio index %d, exit\n", index);
return;
}
ext_gpio = &lcd_extern_gpio[index];
if (ext_gpio->probe_flag == 0) {
EXTERR("%s: gpio [%d] is not probed, exit\n", __func__, index);
return;
}
if (ext_gpio->register_flag == 0) {
lcd_extern_gpio_register(index, value);
return;
}
if (IS_ERR_OR_NULL(ext_gpio->gpio)) {
EXTERR("gpio %s[%d]: %p, err: %ld\n",
ext_gpio->name, index, ext_gpio->gpio,
PTR_ERR(ext_gpio->gpio));
return;
}
switch (value) {
case LCD_GPIO_OUTPUT_LOW:
case LCD_GPIO_OUTPUT_HIGH:
gpiod_direction_output(ext_gpio->gpio, value);
break;
case LCD_GPIO_INPUT:
default:
gpiod_direction_input(ext_gpio->gpio);
break;
}
if (lcd_debug_print_flag) {
EXTPR("set gpio %s[%d] value: %d\n",
ext_gpio->name, index, value);
}
}
unsigned int lcd_extern_gpio_get(unsigned char index)
{
struct lcd_ext_gpio_s *ext_gpio;
if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
EXTERR("gpio index %d, exit\n", index);
return -1;
}
ext_gpio = &lcd_extern_gpio[index];
if (ext_gpio->probe_flag == 0) {
EXTERR("%s: gpio [%d] is not probed, exit\n", __func__, index);
return -1;
}
if (ext_gpio->register_flag == 0) {
EXTERR("%s: gpio %s[%d] is not registered\n",
__func__, ext_gpio->name, index);
return -1;
}
if (IS_ERR_OR_NULL(ext_gpio->gpio)) {
EXTERR("gpio %s[%d]: %p, err: %ld\n",
ext_gpio->name, index, ext_gpio->gpio,
PTR_ERR(ext_gpio->gpio));
return -1;
}
return gpiod_get_value(ext_gpio->gpio);
}
#define LCD_EXTERN_PINMUX_MAX 3
static char *lcd_extern_pinmux_str[LCD_EXTERN_PINMUX_MAX] = {
"extern_on", /* 0 */
"extern_off", /* 1 */
"none",
};
void lcd_extern_pinmux_set(int status)
{
int index = 0xff;
if (lcd_ext_driver == NULL)
return;
if (lcd_ext_driver->config->pinmux_valid == 0) {
if (lcd_debug_print_flag)
EXTPR("%s: pinmux invalid, bypass\n", __func__);
return;
}
if (lcd_debug_print_flag)
EXTPR("%s: %d\n", __func__, status);
index = (status) ? 0 : 1;
if (lcd_ext_driver->pinmux_flag == index) {
EXTPR("%s(%d) pinmux %s is already selected\n",
lcd_ext_driver->config->name,
lcd_ext_driver->config->index,
lcd_extern_pinmux_str[index]);
return;
}
/* request pinmux */
lcd_ext_driver->pin = devm_pinctrl_get_select(lcd_extern_dev,
lcd_extern_pinmux_str[index]);
if (IS_ERR(lcd_ext_driver->pin)) {
EXTERR("set %s(%d) pinmux %s error\n",
lcd_ext_driver->config->name,
lcd_ext_driver->config->index,
lcd_extern_pinmux_str[index]);
} else {
if (lcd_debug_print_flag) {
EXTPR("set %s(%d) pinmux %s ok\n",
lcd_ext_driver->config->name,
lcd_ext_driver->config->index,
lcd_extern_pinmux_str[index]);
}
}
lcd_ext_driver->pinmux_flag = index;
}
#ifdef CONFIG_OF
static unsigned char lcd_extern_get_i2c_bus_str(const char *str)
{
unsigned char i2c_bus;
if (strncmp(str, "i2c_bus_ao", 10) == 0)
i2c_bus = LCD_EXT_I2C_BUS_4;
else if (strncmp(str, "i2c_bus_a", 9) == 0)
i2c_bus = LCD_EXT_I2C_BUS_0;
else if (strncmp(str, "i2c_bus_b", 9) == 0)
i2c_bus = LCD_EXT_I2C_BUS_1;
else if (strncmp(str, "i2c_bus_c", 9) == 0)
i2c_bus = LCD_EXT_I2C_BUS_2;
else if (strncmp(str, "i2c_bus_d", 9) == 0)
i2c_bus = LCD_EXT_I2C_BUS_3;
else if (strncmp(str, "i2c_bus_0", 9) == 0)
i2c_bus = LCD_EXT_I2C_BUS_0;
else if (strncmp(str, "i2c_bus_1", 9) == 0)
i2c_bus = LCD_EXT_I2C_BUS_1;
else if (strncmp(str, "i2c_bus_2", 9) == 0)
i2c_bus = LCD_EXT_I2C_BUS_2;
else if (strncmp(str, "i2c_bus_3", 9) == 0)
i2c_bus = LCD_EXT_I2C_BUS_3;
else if (strncmp(str, "i2c_bus_4", 9) == 0)
i2c_bus = LCD_EXT_I2C_BUS_4;
else {
i2c_bus = LCD_EXT_I2C_BUS_INVALID;
EXTERR("invalid i2c_bus: %s\n", str);
}
return i2c_bus;
}
struct device_node *aml_lcd_extern_get_dts_child(int index)
{
char propname[30];
struct device_node *child;
sprintf(propname, "extern_%d", index);
child = of_get_child_by_name(lcd_extern_dev->of_node, propname);
return child;
}
static int lcd_extern_init_table_dynamic_size_load_dts(
struct device_node *of_node,
struct lcd_extern_config_s *extconf, int flag)
{
unsigned char cmd_size, index, type;
int i = 0, j, val, max_len, step = 0, ret = 0;
unsigned char *table;
char propname[20];
if (flag) {
table = table_init_on_dft;
max_len = LCD_EXTERN_INIT_ON_MAX;
sprintf(propname, "init_on");
} else {
table = table_init_off_dft;
max_len = LCD_EXTERN_INIT_OFF_MAX;
sprintf(propname, "init_off");
}
if (table == NULL) {
EXTERR("%s: init_table is null\n", __func__);
return -1;
}
switch (extconf->type) {
case LCD_EXTERN_I2C:
case LCD_EXTERN_SPI:
while ((i + 1) < max_len) {
/* type */
ret = of_property_read_u32_index(
of_node, propname, i, &val);
if (ret) {
EXTERR("%s: get %s type failed, step %d\n",
extconf->name, propname, step);
table[i] = LCD_EXT_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) {
EXTERR("%s: get %s cmd_size failed, step %d\n",
extconf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i+1] = 0;
return -1;
}
table[i+1] = (unsigned char)val;
cmd_size = table[i+1];
if (type == LCD_EXT_CMD_TYPE_END)
break;
if (cmd_size == 0)
goto init_table_dynamic_i2c_spi_dts_next;
if ((i + 2 + cmd_size) > max_len) {
EXTERR(
"%s: %s cmd_size out of support, step %d\n",
extconf->name, propname, step);
table[i] = LCD_EXT_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) {
EXTERR(
"%s: get %s data failed, step %d\n",
extconf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i+1] = 0;
return -1;
}
table[i+2+j] = (unsigned char)val;
}
if (type == LCD_EXT_CMD_TYPE_GPIO) {
/* gpio probe */
index = table[i+2];
if (index < LCD_EXTERN_GPIO_NUM_MAX)
lcd_extern_gpio_probe(index);
}
init_table_dynamic_i2c_spi_dts_next:
i += (cmd_size + 2);
step++;
}
if (flag)
extconf->table_init_on_cnt = i + 2;
else
extconf->table_init_off_cnt = i + 2;
break;
case LCD_EXTERN_MIPI:
while ((i + 1) < max_len) {
/* type */
ret = of_property_read_u32_index(
of_node, propname, i, &val);
if (ret) {
EXTERR("%s: get %s type failed, step %d\n",
extconf->name, propname, step);
table[i] = LCD_EXT_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) {
EXTERR("%s: get %s cmd_size failed, step %d\n",
extconf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i+1] = 0;
return -1;
}
table[i+1] = (unsigned char)val;
cmd_size = table[i+1];
if (type == LCD_EXT_CMD_TYPE_END) {
if ((cmd_size == 0xff) || (cmd_size == 0))
break;
cmd_size = 0;
}
if (cmd_size == 0)
goto init_table_dynamic_mipi_dts_next;
if ((i + 2 + cmd_size) > max_len) {
EXTERR(
"%s: %s cmd_size out of support, step %d\n",
extconf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i+1] = 0;
return -1;
}
for (j = 0; j < cmd_size; j++) {
ret = of_property_read_u32_index(
of_node, propname, (i+2+j), &val);
if (ret) {
EXTERR("%s: get %s failed, step %d\n",
extconf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i+1] = 0;
return -1;
}
table[i+2+j] = (unsigned char)val;
}
if (type == LCD_EXT_CMD_TYPE_GPIO) {
/* gpio probe */
index = table[i+2];
if (index < LCD_EXTERN_GPIO_NUM_MAX)
lcd_extern_gpio_probe(index);
}
init_table_dynamic_mipi_dts_next:
i += (cmd_size + 2);
step++;
}
if (flag)
extconf->table_init_on_cnt = i + 2;
else
extconf->table_init_off_cnt = i + 2;
break;
default:
break;
}
return 0;
}
static int lcd_extern_init_table_fixed_size_load_dts(
struct device_node *of_node,
struct lcd_extern_config_s *extconf, int flag)
{
unsigned char cmd_size, index;
int i = 0, j, val, max_len, step = 0, ret = 0;
unsigned char *table;
char propname[20];
cmd_size = extconf->cmd_size;
if (flag) {
table = table_init_on_dft;
max_len = LCD_EXTERN_INIT_ON_MAX;
sprintf(propname, "init_on");
} else {
table = table_init_off_dft;
max_len = LCD_EXTERN_INIT_OFF_MAX;
sprintf(propname, "init_off");
}
if (table == NULL) {
EXTERR("%s: init_table is null\n", __func__);
return -1;
}
while (i < max_len) { /* group detect */
if ((i + cmd_size) > max_len) {
EXTERR("%s: %s cmd_size out of support, step %d\n",
extconf->name, propname, step);
table[i] = LCD_EXT_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) {
EXTERR("%s: get %s failed, step %d\n",
extconf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
return -1;
}
table[i+j] = (unsigned char)val;
}
if (table[i] == LCD_EXT_CMD_TYPE_END)
break;
if (table[i] == LCD_EXT_CMD_TYPE_GPIO) {
/* gpio probe */
index = table[i+1];
if (index < LCD_EXTERN_GPIO_NUM_MAX)
lcd_extern_gpio_probe(index);
}
i += cmd_size;
step++;
}
if (flag)
extconf->table_init_on_cnt = i + cmd_size;
else
extconf->table_init_off_cnt = i + cmd_size;
return 0;
}
static int lcd_extern_get_config_dts(struct device_node *of_node,
struct lcd_extern_config_s *extconf)
{
unsigned int ext_index = LCD_EXTERN_INDEX_INVALID;
char ext_propname[20];
struct device_node *child;
const char *str;
int val;
int ret;
extconf->index = LCD_EXTERN_INDEX_INVALID;
extconf->type = LCD_EXTERN_MAX;
extconf->status = 0;
extconf->table_init_loaded = 0;
aml_lcd_notifier_call_chain(LCD_EVENT_EXTERN_SEL, &ext_index);
if (ext_index == LCD_EXTERN_INDEX_INVALID) {
EXTPR("%s: invalid index\n", __func__);
return -1;
}
sprintf(ext_propname, "extern_%d", ext_index);
if (lcd_debug_print_flag)
EXTPR("%s: %s\n", __func__, ext_propname);
child = of_get_child_by_name(of_node, ext_propname);
if (child == NULL) {
EXTERR("failed to get %s\n", ext_propname);
return -1;
}
ret = of_property_read_u32(child, "index", &val);
if (ret) {
EXTERR("get index failed, exit\n");
return -1;
}
extconf->index = (unsigned char)val;
if (extconf->index == LCD_EXTERN_INDEX_INVALID) {
if (lcd_debug_print_flag)
EXTPR("index %d is invalid\n", extconf->index);
return -1;
}
ret = of_property_read_string(child, "status", &str);
if (ret) {
EXTERR("get index %d status failed\n", extconf->index);
return -1;
}
if (lcd_debug_print_flag)
EXTPR("index %d status = %s\n", extconf->index, str);
if (strncmp(str, "okay", 2) == 0)
extconf->status = 1;
else
return -1;
ret = of_property_read_string(child, "extern_name", &str);
if (ret) {
EXTERR("get extern_name failed\n");
strncpy(extconf->name, "none", LCD_EXTERN_NAME_LEN_MAX);
} else {
strncpy(extconf->name, str, LCD_EXTERN_NAME_LEN_MAX);
}
/* ensure string ending */
extconf->name[LCD_EXTERN_NAME_LEN_MAX-1] = '\0';
EXTPR("load config: %s[%d]\n", extconf->name, extconf->index);
ret = of_property_read_u32(child, "type", &extconf->type);
if (ret) {
extconf->type = LCD_EXTERN_MAX;
EXTERR("%s: get type failed, exit\n", extconf->name);
return -1;
}
switch (extconf->type) {
case LCD_EXTERN_I2C:
if (extconf->i2c_bus == LCD_EXT_I2C_BUS_INVALID)
EXTERR("%s: get i2c_bus failed\n", extconf->name);
if (lcd_debug_print_flag) {
EXTPR("%s: i2c_bus = %d\n",
extconf->name, extconf->i2c_bus);
}
ret = of_property_read_u32(child, "i2c_address", &val);
if (ret) {
EXTERR("%s: get i2c_address failed, exit\n",
extconf->name);
extconf->i2c_addr = LCD_EXT_I2C_ADDR_INVALID;
return -1;
}
extconf->i2c_addr = (unsigned char)val;
if (lcd_debug_print_flag) {
EXTPR("%s: i2c_address = 0x%02x\n",
extconf->name, extconf->i2c_addr);
}
ret = of_property_read_u32(child, "i2c_address2", &val);
if (ret) {
ret = of_property_read_u32(child,
"i2c_second_address", &val);
if (ret) {
EXTPR("%s: no i2c_address2 exist\n",
extconf->name);
extconf->i2c_addr2 = LCD_EXT_I2C_ADDR_INVALID;
} else {
extconf->i2c_addr2 = (unsigned char)val;
}
} else {
extconf->i2c_addr2 = (unsigned char)val;
}
if (lcd_debug_print_flag) {
EXTPR("%s: i2c_address2 = 0x%02x\n",
extconf->name, extconf->i2c_addr2);
}
ret = of_property_read_u32(child, "cmd_size", &val);
if (ret) {
EXTPR("%s: no cmd_size\n", extconf->name);
extconf->cmd_size = 0;
} else {
extconf->cmd_size = (unsigned char)val;
}
if (lcd_debug_print_flag) {
EXTPR("%s: cmd_size = %d\n",
extconf->name, extconf->cmd_size);
}
if (extconf->cmd_size == 0)
break;
if (extconf->cmd_size == LCD_EXT_CMD_SIZE_DYNAMIC) {
ret = lcd_extern_init_table_dynamic_size_load_dts(
child, extconf, 1);
if (ret)
break;
ret = lcd_extern_init_table_dynamic_size_load_dts(
child, extconf, 0);
} else {
ret = lcd_extern_init_table_fixed_size_load_dts(
child, extconf, 1);
if (ret)
break;
ret = lcd_extern_init_table_fixed_size_load_dts(
child, extconf, 0);
}
if (ret == 0)
extconf->table_init_loaded = 1;
break;
case LCD_EXTERN_SPI:
ret = of_property_read_u32(child, "gpio_spi_cs", &val);
if (ret) {
EXTERR("%s: get gpio_spi_cs failed, exit\n",
extconf->name);
extconf->spi_gpio_cs = LCD_EXT_GPIO_INVALID;
return -1;
}
extconf->spi_gpio_cs = val;
lcd_extern_gpio_probe(val);
if (lcd_debug_print_flag) {
EXTPR("%s: spi_gpio_cs: %d\n",
extconf->name, extconf->spi_gpio_cs);
}
ret = of_property_read_u32(child, "gpio_spi_clk", &val);
if (ret) {
EXTERR("%s: get gpio_spi_clk failed, exit\n",
extconf->name);
extconf->spi_gpio_clk = LCD_EXT_GPIO_INVALID;
return -1;
}
extconf->spi_gpio_clk = val;
lcd_extern_gpio_probe(val);
if (lcd_debug_print_flag) {
EXTPR("%s: spi_gpio_clk: %d\n",
extconf->name, extconf->spi_gpio_clk);
}
ret = of_property_read_u32(child, "gpio_spi_data", &val);
if (ret) {
EXTERR("%s: get gpio_spi_data failed, exit\n",
extconf->name);
extconf->spi_gpio_data = LCD_EXT_GPIO_INVALID;
return -1;
}
extconf->spi_gpio_data = val;
lcd_extern_gpio_probe(val);
if (lcd_debug_print_flag) {
EXTPR("%s: spi_gpio_data: %d\n",
extconf->name, extconf->spi_gpio_data);
}
ret = of_property_read_u32(child, "spi_clk_freq", &val);
if (ret) {
EXTERR(
"%s: get spi_clk_freq failed, default to %dKHz\n",
extconf->name, LCD_EXT_SPI_CLK_FREQ_DFT);
extconf->spi_clk_freq = LCD_EXT_SPI_CLK_FREQ_DFT;
} else {
extconf->spi_clk_freq = val;
}
ret = of_property_read_u32(child, "spi_clk_pol", &val);
if (ret) {
EXTERR("%s: get spi_clk_pol failed, default to 1\n",
extconf->name);
extconf->spi_clk_pol = 1;
} else {
extconf->spi_clk_pol = (unsigned char)val;
}
if (lcd_debug_print_flag) {
EXTPR("%s: spi_clk_freq: %dKHz, spi_clk_pol: %d\n",
extconf->name, extconf->spi_clk_freq,
extconf->spi_clk_pol);
}
ret = of_property_read_u32(child, "cmd_size", &val);
if (ret) {
EXTPR("%s: no cmd_size\n", extconf->name);
extconf->cmd_size = 0;
} else {
extconf->cmd_size = (unsigned char)val;
}
if (lcd_debug_print_flag) {
EXTPR("%s: cmd_size: %d\n",
extconf->name, extconf->cmd_size);
}
if (extconf->cmd_size == 0)
break;
if (extconf->cmd_size == LCD_EXT_CMD_SIZE_DYNAMIC) {
ret = lcd_extern_init_table_dynamic_size_load_dts(
child, extconf, 1);
if (ret)
break;
ret = lcd_extern_init_table_dynamic_size_load_dts(
child, extconf, 0);
} else {
ret = lcd_extern_init_table_fixed_size_load_dts(
child, extconf, 1);
if (ret)
break;
ret = lcd_extern_init_table_fixed_size_load_dts(
child, extconf, 0);
}
if (ret == 0)
extconf->table_init_loaded = 1;
break;
case LCD_EXTERN_MIPI:
ret = of_property_read_u32(child, "cmd_size", &val);
if (ret) {
EXTPR("%s: no cmd_size\n", extconf->name);
extconf->cmd_size = 0;
} else {
extconf->cmd_size = (unsigned char)val;
}
if (lcd_debug_print_flag) {
EXTPR("%s: cmd_size = %d\n",
extconf->name, extconf->cmd_size);
}
if (extconf->cmd_size != LCD_EXT_CMD_SIZE_DYNAMIC)
break;
ret = lcd_extern_init_table_dynamic_size_load_dts(
child, extconf, 1);
if (ret)
break;
ret = lcd_extern_init_table_dynamic_size_load_dts(
child, extconf, 0);
if (ret == 0)
extconf->table_init_loaded = 1;
break;
default:
break;
}
return 0;
}
#endif
static int lcd_extern_init_table_dynamic_size_load_unifykey(
struct lcd_extern_config_s *extconf, unsigned char *p,
int key_len, int len, int flag)
{
unsigned char cmd_size = 0;
unsigned char index;
int i = 0, j, max_len, ret = 0;
unsigned char *table, *buf;
char propname[20];
if (flag) {
table = table_init_on_dft;
max_len = LCD_EXTERN_INIT_ON_MAX;
sprintf(propname, "init_on");
buf = p;
} else {
table = table_init_off_dft;
max_len = LCD_EXTERN_INIT_OFF_MAX;
sprintf(propname, "init_off");
buf = p + extconf->table_init_on_cnt;
}
if (table == NULL) {
EXTERR("%s: init_table is null\n", __func__);
return -1;
}
switch (extconf->type) {
case LCD_EXTERN_I2C:
case LCD_EXTERN_SPI:
while ((i + 1) < max_len) {
/* type */
len += 1;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
EXTERR("%s: get %s failed\n",
extconf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i+1] = 0;
return -1;
}
table[i] = *(buf + LCD_UKEY_EXT_INIT + i);
/* cmd_size */
len += 1;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
EXTERR("%s: get %s failed\n",
extconf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i+1] = 0;
return -1;
}
table[i+1] = *(buf + LCD_UKEY_EXT_INIT + i + 1);
cmd_size = table[i+1];
if (table[i] == LCD_EXT_CMD_TYPE_END)
break;
if (cmd_size == 0)
goto init_table_dynamic_i2c_spi_ukey_next;
if ((i + 2 + cmd_size) > max_len) {
EXTERR("%s: %s cmd_size out of support\n",
extconf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i+1] = 0;
return -1;
}
/* step3: data */
len += cmd_size;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
EXTERR("%s: get %s failed\n",
extconf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i+1] = 0;
return -1;
}
for (j = 0; j < cmd_size; j++) {
table[i+2+j] =
*(buf + LCD_UKEY_EXT_INIT + i + 2 + j);
}
if (table[i] == LCD_EXT_CMD_TYPE_GPIO) {
/* gpio probe */
index = table[i+1];
if (index < LCD_EXTERN_GPIO_NUM_MAX)
lcd_extern_gpio_probe(index);
}
init_table_dynamic_i2c_spi_ukey_next:
i += (cmd_size + 2);
}
if (flag)
extconf->table_init_on_cnt = i + 2;
else
extconf->table_init_off_cnt = i + 2;
break;
case LCD_EXTERN_MIPI:
while ((i + 1) < max_len) {
/* type */
len += 1;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
EXTERR("%s: get %s failed\n",
extconf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i+1] = 0;
return -1;
}
table[i] = *(buf + LCD_UKEY_EXT_INIT + i);
/* cmd_size */
len += 1;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
EXTERR("%s: get %s failed\n",
extconf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i+1] = 0;
return -1;
}
table[i+1] = *(buf + LCD_UKEY_EXT_INIT + i + 1);
cmd_size = table[i+1];
if (table[i] == LCD_EXT_CMD_TYPE_END) {
if ((cmd_size == 0xff) || (cmd_size == 0))
break;
cmd_size = 0;
}
if (cmd_size == 0)
goto init_table_dynamic_mipi_ukey_next;
if ((i + 2 + cmd_size) > max_len) {
EXTERR("%s: %s cmd_size out of max\n",
extconf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i+1] = 0;
return -1;
}
/* data */
len += cmd_size;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
EXTERR("%s: get %s failed\n",
extconf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i+1] = 0;
return -1;
}
for (j = 0; j < cmd_size; j++) {
table[i+2+j] =
*(buf + LCD_UKEY_EXT_INIT + i + 2 + j);
}
if (table[i] == LCD_EXT_CMD_TYPE_GPIO) {
/* gpio probe */
index = table[i+2];
if (index < LCD_EXTERN_GPIO_NUM_MAX)
lcd_extern_gpio_probe(index);
}
init_table_dynamic_mipi_ukey_next:
i += (cmd_size + 2);
}
if (flag)
extconf->table_init_on_cnt = i + 2;
else
extconf->table_init_off_cnt = i + 2;
break;
default:
break;
}
return 0;
}
static int lcd_extern_init_table_fixed_size_load_unifykey(
struct lcd_extern_config_s *extconf, unsigned char *p,
int key_len, int len, int flag)
{
unsigned char cmd_size;
unsigned char index;
int i = 0, j, max_len, ret = 0;
unsigned char *table, *buf;
char propname[20];
cmd_size = extconf->cmd_size;
if (flag) {
table = table_init_on_dft;
max_len = LCD_EXTERN_INIT_ON_MAX;
sprintf(propname, "init_on");
buf = p;
} else {
table = table_init_off_dft;
max_len = LCD_EXTERN_INIT_OFF_MAX;
sprintf(propname, "init_off");
buf = p + extconf->table_init_on_cnt;
}
if (table == NULL) {
EXTERR("%s: init_table is null\n", __func__);
return -1;
}
while (i < max_len) {
if ((i + cmd_size) > max_len) {
EXTERR("%s: %s cmd_size out of max\n",
extconf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
return -1;
}
len += cmd_size;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
EXTERR("%s: get %s failed\n",
extconf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
for (j = 1; j < cmd_size; j++)
table[i+j] = 0;
return -1;
}
for (j = 0; j < cmd_size; j++)
table[i+j] = *(buf + LCD_UKEY_EXT_INIT + i + j);
if (table[i] == LCD_EXT_CMD_TYPE_END)
break;
if (table[i] == LCD_EXT_CMD_TYPE_GPIO) {
/* gpio probe */
index = table[i+1];
if (index < LCD_EXTERN_GPIO_NUM_MAX)
lcd_extern_gpio_probe(index);
}
i += cmd_size;
}
if (flag)
extconf->table_init_on_cnt = i + cmd_size;
else
extconf->table_init_off_cnt = i + cmd_size;
return 0;
}
static int lcd_extern_get_config_unifykey(struct lcd_extern_config_s *extconf)
{
unsigned char *para, *p;
int key_len, len;
const char *str;
struct aml_lcd_unifykey_header_s ext_header;
int ret;
extconf->index = LCD_EXTERN_INDEX_INVALID;
extconf->type = LCD_EXTERN_MAX;
extconf->table_init_loaded = 0;
para = kmalloc((sizeof(unsigned char) * LCD_UKEY_LCD_EXT_SIZE),
GFP_KERNEL);
if (!para) {
EXTERR("%s: Not enough memory\n", __func__);
return -1;
}
key_len = LCD_UKEY_LCD_EXT_SIZE;
memset(para, 0, (sizeof(unsigned char) * key_len));
ret = lcd_unifykey_get("lcd_extern", para, &key_len);
if (ret) {
kfree(para);
return -1;
}
/* check lcd_extern unifykey length */
len = 10 + 33 + 10;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
EXTERR("unifykey length is not correct\n");
kfree(para);
return -1;
}
/* header: 10byte */
lcd_unifykey_header_check(para, &ext_header);
if (lcd_debug_print_flag) {
EXTPR("unifykey header:\n");
EXTPR("crc32 = 0x%08x\n", ext_header.crc32);
EXTPR("data_len = %d\n", ext_header.data_len);
EXTPR("version = 0x%04x\n", ext_header.version);
EXTPR("reserved = 0x%04x\n", ext_header.reserved);
}
/* basic: 33byte */
p = para;
str = (const char *)(p + LCD_UKEY_HEAD_SIZE);
strncpy(extconf->name, str, LCD_EXTERN_NAME_LEN_MAX);
/* ensure string ending */
extconf->name[LCD_EXTERN_NAME_LEN_MAX-1] = '\0';
extconf->index = *(p + LCD_UKEY_EXT_INDEX);
extconf->type = *(p + LCD_UKEY_EXT_TYPE);
extconf->status = *(p + LCD_UKEY_EXT_STATUS);
if (extconf->index == LCD_EXTERN_INDEX_INVALID) {
if (lcd_debug_print_flag)
EXTPR("index %d is invalid\n", extconf->index);
kfree(para);
return -1;
}
/* type: 10byte */
switch (extconf->type) {
case LCD_EXTERN_I2C:
extconf->i2c_addr = *(p + LCD_UKEY_EXT_TYPE_VAL_0);
extconf->i2c_addr2 = *(p + LCD_UKEY_EXT_TYPE_VAL_1);
/*extconf->i2c_bus = *(p + LCD_UKEY_EXT_TYPE_VAL_2);*/
if (extconf->i2c_bus == LCD_EXT_I2C_BUS_MAX)
EXTERR("%s: get i2c_bus failed\n", extconf->name);
extconf->cmd_size = *(p + LCD_UKEY_EXT_TYPE_VAL_3);
if (lcd_debug_print_flag) {
EXTPR("%s: cmd_size = %d\n",
extconf->name, extconf->cmd_size);
}
/* init */
if (extconf->cmd_size == 0)
break;
if (extconf->cmd_size == LCD_EXT_CMD_SIZE_DYNAMIC) {
ret = lcd_extern_init_table_dynamic_size_load_unifykey(
extconf, p, key_len, len, 1);
if (ret)
break;
ret = lcd_extern_init_table_dynamic_size_load_unifykey(
extconf, p, key_len, len, 0);
} else {
ret = lcd_extern_init_table_fixed_size_load_unifykey(
extconf, p, key_len, len, 1);
if (ret)
break;
ret = lcd_extern_init_table_fixed_size_load_unifykey(
extconf, p, key_len, len, 0);
}
if (ret == 0)
extconf->table_init_loaded = 1;
break;
case LCD_EXTERN_SPI:
extconf->spi_gpio_cs = *(p + LCD_UKEY_EXT_TYPE_VAL_0);
lcd_extern_gpio_probe(extconf->spi_gpio_cs);
extconf->spi_gpio_clk = *(p + LCD_UKEY_EXT_TYPE_VAL_1);
lcd_extern_gpio_probe(extconf->spi_gpio_clk);
extconf->spi_gpio_data = *(p + LCD_UKEY_EXT_TYPE_VAL_2);
lcd_extern_gpio_probe(extconf->spi_gpio_data);
extconf->spi_clk_freq = (*(p + LCD_UKEY_EXT_TYPE_VAL_3) |
((*(p + LCD_UKEY_EXT_TYPE_VAL_4)) << 8));
extconf->spi_clk_pol = *(p + LCD_UKEY_EXT_TYPE_VAL_5);
extconf->cmd_size = *(p + LCD_UKEY_EXT_TYPE_VAL_6);
if (lcd_debug_print_flag) {
EXTPR("%s: cmd_size = %d\n",
extconf->name, extconf->cmd_size);
}
/* init */
if (extconf->cmd_size == 0)
break;
if (extconf->cmd_size == LCD_EXT_CMD_SIZE_DYNAMIC) {
ret = lcd_extern_init_table_dynamic_size_load_unifykey(
extconf, p, key_len, len, 1);
if (ret)
break;
ret = lcd_extern_init_table_dynamic_size_load_unifykey(
extconf, p, key_len, len, 0);
} else {
ret = lcd_extern_init_table_fixed_size_load_unifykey(
extconf, p, key_len, len, 1);
if (ret)
break;
ret = lcd_extern_init_table_fixed_size_load_unifykey(
extconf, p, key_len, len, 0);
}
if (ret == 0)
extconf->table_init_loaded = 1;
break;
case LCD_EXTERN_MIPI:
extconf->cmd_size = *(p + LCD_UKEY_EXT_TYPE_VAL_0);
if (lcd_debug_print_flag) {
EXTPR("%s: cmd_size = %d\n",
extconf->name, extconf->cmd_size);
}
if (extconf->cmd_size != LCD_EXT_CMD_SIZE_DYNAMIC)
break;
ret = lcd_extern_init_table_dynamic_size_load_unifykey(
extconf, p, key_len, len, 1);
if (ret)
break;
ret = lcd_extern_init_table_dynamic_size_load_unifykey(
extconf, p, key_len, len, 0);
if (ret == 0)
extconf->table_init_loaded = 1;
break;
default:
break;
}
kfree(para);
return 0;
}
static int lcd_extern_table_init_dft_malloc(void)
{
table_init_on_dft = kcalloc(LCD_EXTERN_INIT_ON_MAX,
sizeof(unsigned char), GFP_KERNEL);
if (table_init_on_dft == NULL) {
EXTERR("failed to alloc init_on_dft table\n");
return -1;
}
table_init_off_dft = kcalloc(LCD_EXTERN_INIT_OFF_MAX,
sizeof(unsigned char), GFP_KERNEL);
if (table_init_off_dft == NULL) {
EXTERR("failed to alloc init_off_dft table\n");
kfree(table_init_on_dft);
return -1;
}
table_init_on_dft[0] = LCD_EXT_CMD_TYPE_END;
table_init_on_dft[1] = 0;
table_init_off_dft[0] = LCD_EXT_CMD_TYPE_END;
table_init_off_dft[1] = 0;
return 0;
}
static int lcd_extern_table_init_save(struct lcd_extern_config_s *extconf)
{
if (extconf->table_init_on_cnt > 0) {
extconf->table_init_on = kcalloc(extconf->table_init_on_cnt,
sizeof(unsigned char), GFP_KERNEL);
if (extconf->table_init_on == NULL) {
EXTERR("failed to alloc init_on table\n");
return -1;
}
memcpy(extconf->table_init_on, table_init_on_dft,
extconf->table_init_on_cnt*sizeof(unsigned char));
}
if (extconf->table_init_off_cnt > 0) {
extconf->table_init_off = kcalloc(extconf->table_init_off_cnt,
sizeof(unsigned char), GFP_KERNEL);
if (extconf->table_init_off == NULL) {
EXTERR("failed to alloc init_off table\n");
kfree(extconf->table_init_on);
return -1;
}
memcpy(extconf->table_init_off, table_init_off_dft,
extconf->table_init_off_cnt*sizeof(unsigned char));
}
return 0;
}
static int lcd_extern_get_config(void)
{
unsigned int extern_para[5];
const char *str;
unsigned int load_id = 0;
int ret;
if (lcd_extern_dev->of_node == NULL) {
EXTERR("no lcd_extern of_node exist\n");
return -1;
}
ret = lcd_extern_table_init_dft_malloc();
if (ret)
return -1;
ret = of_property_read_string(lcd_extern_dev->of_node, "i2c_bus", &str);
if (ret)
lcd_extern_config.i2c_bus = LCD_EXT_I2C_BUS_MAX;
else
lcd_extern_config.i2c_bus = lcd_extern_get_i2c_bus_str(str);
ret = of_property_read_u32_array(lcd_extern_dev->of_node,
"i2c_gpio", &extern_para[0], 2);
if (ret) {
lcd_extern_config.i2c_sck_gpio = LCD_EXT_GPIO_INVALID;
lcd_extern_config.i2c_sda_gpio = LCD_EXT_GPIO_INVALID;
} else {
lcd_extern_config.i2c_sck_gpio = (unsigned char)extern_para[0];
lcd_extern_config.i2c_sda_gpio = (unsigned char)extern_para[1];
}
ret = of_property_read_string(lcd_extern_dev->of_node,
"pinctrl-names", &str);
if (ret)
lcd_extern_config.pinmux_valid = 0;
else
lcd_extern_config.pinmux_valid = 1;
ret = of_property_read_u32(lcd_extern_dev->of_node,
"pinctrl_gpio_off", &extern_para[0]);
if (ret) {
lcd_extern_config.pinmux_gpio_off = 2;
} else {
lcd_extern_config.pinmux_gpio_off =
(unsigned char)extern_para[0];
}
ret = of_property_read_u32(lcd_extern_dev->of_node,
"key_valid", &extern_para[0]);
if (ret) {
if (lcd_debug_print_flag)
EXTPR("failed to get key_valid\n");
lcd_extern_config.key_valid = 0;
} else {
lcd_extern_config.key_valid = (unsigned char)extern_para[0];
}
EXTPR("key_valid: %d\n", lcd_extern_config.key_valid);
if (lcd_extern_config.key_valid) {
ret = lcd_unifykey_check("lcd_extern");
if (ret < 0)
load_id = 0;
else
load_id = 1;
}
if (load_id) {
EXTPR("%s from unifykey\n", __func__);
lcd_ext_config_load = 1;
ret = lcd_extern_get_config_unifykey(&lcd_extern_config);
} else {
#ifdef CONFIG_OF
EXTPR("%s from dts\n", __func__);
lcd_ext_config_load = 0;
ret = lcd_extern_get_config_dts(lcd_extern_dev->of_node,
&lcd_extern_config);
#endif
}
if (ret)
goto lcd_extern_get_config_err;
if (lcd_extern_config.table_init_loaded > 0) {
ret = lcd_extern_table_init_save(&lcd_extern_config);
if (ret)
goto lcd_extern_get_config_err;
}
kfree(table_init_on_dft);
kfree(table_init_off_dft);
return 0;
lcd_extern_get_config_err:
kfree(table_init_on_dft);
kfree(table_init_off_dft);
return -1;
}
static int lcd_extern_add_i2c(struct aml_lcd_extern_driver_s *ext_drv)
{
int ret = -1;
if (strcmp(ext_drv->config->name, "ext_default") == 0) {
if (ext_drv->config->cmd_size < 2) {
EXTERR("%s: cmd_size %d is invalid\n",
ext_drv->config->name,
ext_drv->config->cmd_size);
ret = -1;
} else {
ret = aml_lcd_extern_default_probe(ext_drv);
}
} else if (strcmp(ext_drv->config->name, "i2c_T5800Q") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_T5800Q
ret = aml_lcd_extern_i2c_T5800Q_probe(ext_drv);
#endif
} else if (strcmp(ext_drv->config->name, "i2c_tc101") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_TC101
ret = aml_lcd_extern_i2c_tc101_probe(ext_drv);
#endif
} else if (strcmp(ext_drv->config->name, "i2c_anx6345") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_ANX6345
ret = aml_lcd_extern_i2c_anx6345_probe(ext_drv);
#endif
} else if (strcmp(ext_drv->config->name, "i2c_DLPC3439") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_DLPC3439
ret = aml_lcd_extern_i2c_DLPC3439_probe(ext_drv);
#endif
} else if (strcmp(ext_drv->config->name, "i2c_ANX6862_7911") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_ANX6862_7911
ret = aml_lcd_extern_i2c_ANX6862_7911_probe(ext_drv);
#endif
} else {
EXTERR("invalid driver name: %s\n", ext_drv->config->name);
ret = -1;
}
return ret;
}
static int lcd_extern_add_spi(struct aml_lcd_extern_driver_s *ext_drv)
{
int ret = 0;
if (strcmp(ext_drv->config->name, "ext_default") == 0) {
if (ext_drv->config->cmd_size < 2) {
EXTERR("%s: cmd_size %d is invalid\n",
ext_drv->config->name,
ext_drv->config->cmd_size);
ret = -1;
} else {
ret = aml_lcd_extern_default_probe(ext_drv);
}
} else if (strcmp(ext_drv->config->name, "spi_LD070WS2") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_SPI_LD070WS2
ret = aml_lcd_extern_spi_LD070WS2_probe(ext_drv);
#endif
} else {
EXTERR("invalid driver name: %s\n", ext_drv->config->name);
ret = -1;
}
return ret;
}
static int lcd_extern_add_mipi(struct aml_lcd_extern_driver_s *ext_drv)
{
int ret = 0;
if ((strcmp(ext_drv->config->name, "mipi_default") == 0) ||
(strcmp(ext_drv->config->name, "ext_default") == 0)) {
if (ext_drv->config->cmd_size != LCD_EXT_CMD_SIZE_DYNAMIC) {
EXTERR("%s: cmd_size %d is invalid\n",
ext_drv->config->name,
ext_drv->config->cmd_size);
ret = -1;
} else {
ret = aml_lcd_extern_mipi_default_probe(ext_drv);
}
} else if (strcmp(ext_drv->config->name, "mipi_N070ICN") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_N070ICN
ret = aml_lcd_extern_mipi_N070ICN_probe(ext_drv);
#endif
} else if (strcmp(ext_drv->config->name, "mipi_KD080D13") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_KD080D13
ret = aml_lcd_extern_mipi_KD080D13_probe(ext_drv);
#endif
} else if (strcmp(ext_drv->config->name, "mipi_TV070WSM") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_TV070WSM
ret = aml_lcd_extern_mipi_TV070WSM_probe(ext_drv);
#endif
} else if (strcmp(ext_drv->config->name, "mipi_ST7701") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_ST7701
ret = aml_lcd_extern_mipi_st7701_probe(ext_drv);
#endif
} else if (strcmp(ext_drv->config->name, "mipi_P070ACB") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_P070ACB
ret = aml_lcd_extern_mipi_p070acb_probe(ext_drv);
#endif
} else if (strcmp(ext_drv->config->name, "mipi_P070ACB_FT") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_P070ACB_FT
ret = aml_lcd_extern_mipi_p070acb_ft_probe(ext_drv);
#endif
} else if (strcmp(ext_drv->config->name, "mipi_TV070WSM_FT") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_TV070WSM_FT
ret = aml_lcd_extern_mipi_TV070WSM_ft_probe(ext_drv);
#endif
} else if (strcmp(ext_drv->config->name, "mipi_TL050FHV02CT") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_TL050FHV02CT
ret = aml_lcd_extern_mipi_tl050fhv02ct_probe(ext_drv);
#endif
} else if (strcmp(ext_drv->config->name, "mipi_G101B158") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_MIPI_G101B158
ret = aml_lcd_extern_mipi_g101b158_probe(ext_drv);
#endif
} else {
EXTERR("invalid driver name: %s\n", ext_drv->config->name);
ret = -1;
}
return ret;
}
static int lcd_extern_add_invalid(struct aml_lcd_extern_driver_s *ext_drv)
{
return -1;
}
static int lcd_extern_add_driver(struct lcd_extern_config_s *extconf)
{
struct aml_lcd_extern_driver_s *ext_drv;
int ret = 0;
if (extconf->status == 0) {
EXTERR("driver %s[%d] status is disabled\n",
extconf->name, extconf->index);
return -1;
}
lcd_ext_driver =
kzalloc(sizeof(struct aml_lcd_extern_driver_s), GFP_KERNEL);
if (lcd_ext_driver == NULL) {
EXTERR("failed to alloc driver %s[%d], not enough memory\n",
extconf->name, extconf->index);
return -1;
}
ext_drv = lcd_ext_driver;
ext_drv->config = extconf;
ext_drv->pinmux_flag = 0xff;
ext_drv->reg_read = NULL;
ext_drv->reg_write = NULL;
ext_drv->power_on = NULL;
ext_drv->power_off = NULL;
switch (ext_drv->config->type) {
case LCD_EXTERN_I2C:
ret = lcd_extern_add_i2c(ext_drv);
break;
case LCD_EXTERN_SPI:
ret = lcd_extern_add_spi(ext_drv);
break;
case LCD_EXTERN_MIPI:
ret = lcd_extern_add_mipi(ext_drv);
break;
default:
ret = lcd_extern_add_invalid(ext_drv);
EXTERR("don't support type %d\n", ext_drv->config->type);
break;
}
if (ret) {
EXTERR("add driver failed\n");
kfree(lcd_ext_driver->config->table_init_on);
kfree(lcd_ext_driver->config->table_init_off);
lcd_ext_driver->config->table_init_on = NULL;
lcd_ext_driver->config->table_init_off = NULL;
kfree(lcd_ext_driver);
lcd_ext_driver = NULL;
return -1;
}
EXTPR("add driver %s(%d)\n",
ext_drv->config->name, ext_drv->config->index);
return 0;
}
/* *********************************************************
* debug function
* *********************************************************
*/
#define EXT_LEN_MAX 500
static void lcd_extern_init_table_dynamic_size_print(
struct lcd_extern_config_s *econf, int flag)
{
int i, j, k, max_len;
unsigned char cmd_size;
char *str;
unsigned char *table;
str = kcalloc(EXT_LEN_MAX, sizeof(char), GFP_KERNEL);
if (str == NULL) {
EXTERR("%s: str malloc error\n", __func__);
return;
}
if (flag) {
pr_info("power on:\n");
table = econf->table_init_on;
max_len = econf->table_init_on_cnt;
} else {
pr_info("power off:\n");
table = econf->table_init_off;
max_len = econf->table_init_off_cnt;
}
if (table == NULL) {
EXTERR("init_table %d is NULL\n", flag);
kfree(str);
return;
}
i = 0;
switch (econf->type) {
case LCD_EXTERN_I2C:
case LCD_EXTERN_SPI:
while ((i + 1) < max_len) {
if (table[i] == LCD_EXT_CMD_TYPE_END) {
pr_info(" 0x%02x,%d,\n",
table[i], table[i+1]);
break;
}
cmd_size = table[i+1];
k = snprintf(str, EXT_LEN_MAX, " 0x%02x,%d,",
table[i], cmd_size);
if (cmd_size == 0)
goto init_table_dynamic_print_i2c_spi_next;
if (i + 2 + cmd_size > max_len) {
pr_info("cmd_size out of support\n");
break;
}
if ((table[i] == LCD_EXT_CMD_TYPE_GPIO) ||
(table[i] == LCD_EXT_CMD_TYPE_DELAY)) {
for (j = 0; j < cmd_size; j++) {
k += snprintf(str+k, EXT_LEN_MAX,
"%d,", table[i+2+j]);
}
} else if ((table[i] == LCD_EXT_CMD_TYPE_CMD) ||
(table[i] == LCD_EXT_CMD_TYPE_CMD2)) {
for (j = 0; j < cmd_size; j++) {
k += snprintf(str+k, EXT_LEN_MAX,
"0x%02x,", table[i+2+j]);
}
} else if ((table[i] == LCD_EXT_CMD_TYPE_CMD_DELAY) ||
(table[i] == LCD_EXT_CMD_TYPE_CMD2_DELAY)) {
for (j = 0; j < (cmd_size - 1); j++) {
k += snprintf(str+k, EXT_LEN_MAX,
"0x%02x,", table[i+2+j]);
}
snprintf(str+k, EXT_LEN_MAX,
"%d,", table[i+cmd_size+1]);
} else {
for (j = 0; j < cmd_size; j++) {
k += snprintf(str+k, EXT_LEN_MAX,
"0x%02x,", table[i+2+j]);
}
}
init_table_dynamic_print_i2c_spi_next:
pr_info("%s\n", str);
i += (cmd_size + 2);
}
break;
case LCD_EXTERN_MIPI:
while ((i + 1) < max_len) {
cmd_size = table[i+1];
if (table[i] == LCD_EXT_CMD_TYPE_END) {
if (cmd_size == 0xff) {
pr_info(" 0x%02x,0x%02x,\n",
table[i], table[i+1]);
break;
}
if (cmd_size == 0) {
pr_info(" 0x%02x,%d,\n",
table[i], table[i+1]);
break;
}
cmd_size = 0;
}
k = snprintf(str, EXT_LEN_MAX, " 0x%02x,%d,",
table[i], cmd_size);
if (cmd_size == 0)
goto init_table_dynamic_print_mipi_next;
if (i + 2 + cmd_size > max_len) {
pr_info("cmd_size out of support\n");
break;
}
if ((table[i] == LCD_EXT_CMD_TYPE_GPIO) ||
(table[i] == LCD_EXT_CMD_TYPE_DELAY)) {
for (j = 0; j < cmd_size; j++) {
k += snprintf(str+k, EXT_LEN_MAX,
"%d,", table[i+2+j]);
}
} else if ((table[i] & 0xf) == 0x0) {
pr_info(" init_%s wrong data_type: 0x%02x\n",
flag ? "on" : "off", table[i]);
break;
} else {
cmd_size = table[i+DSI_CMD_SIZE_INDEX];
k = snprintf(str, EXT_LEN_MAX, " 0x%02x,%d,",
table[i], cmd_size);
for (j = 0; j < cmd_size; j++) {
k += snprintf(str+k, EXT_LEN_MAX,
"0x%02x,",
table[i+2+j]);
}
}
init_table_dynamic_print_mipi_next:
pr_info("%s\n", str);
i += (cmd_size + 2);
}
break;
default:
break;
}
kfree(str);
}
static void lcd_extern_init_table_fixed_size_print(
struct lcd_extern_config_s *econf, int flag)
{
int i, j, k, max_len;
unsigned char cmd_size;
char *str;
unsigned char *table;
str = kcalloc(EXT_LEN_MAX, sizeof(char), GFP_KERNEL);
if (str == NULL) {
EXTERR("%s: str malloc error\n", __func__);
return;
}
cmd_size = econf->cmd_size;
if (flag) {
pr_info("power on:\n");
table = econf->table_init_on;
max_len = econf->table_init_on_cnt;
} else {
pr_info("power off:\n");
table = econf->table_init_off;
max_len = econf->table_init_off_cnt;
}
if (table == NULL) {
EXTERR("init_table %d is NULL\n", flag);
kfree(str);
return;
}
i = 0;
while ((i + cmd_size) <= max_len) {
k = snprintf(str, EXT_LEN_MAX, " ");
for (j = 0; j < cmd_size; j++) {
k += snprintf(str+k, EXT_LEN_MAX, " 0x%02x",
table[i+j]);
}
pr_info("%s\n", str);
if (table[i] == LCD_EXT_CMD_TYPE_END)
break;
i += cmd_size;
}
kfree(str);
}
static ssize_t lcd_extern_info_show(struct class *class,
struct class_attribute *attr, char *buf)
{
struct lcd_extern_config_s *econf;
if (lcd_ext_driver == NULL)
return sprintf(buf, "lcd extern driver is NULL\n");
econf = lcd_ext_driver->config;
pr_info("lcd extern driver %s(%d) info:\n", econf->name, econf->index);
pr_info("status: %d\n", econf->status);
switch (econf->type) {
case LCD_EXTERN_I2C:
pr_info("type: i2c(%d)\n", econf->type);
pr_info("i2c_addr: 0x%02x\n"
"i2c_addr2: 0x%02x\n"
"i2c_bus: %d\n"
"table_loaded: %d\n"
"cmd_size: %d\n"
"table_init_on_cnt: %d\n"
"table_init_off_cnt: %d\n",
econf->i2c_addr, econf->i2c_addr2, econf->i2c_bus,
econf->table_init_loaded, econf->cmd_size,
econf->table_init_on_cnt,
econf->table_init_off_cnt);
if (econf->cmd_size == 0)
break;
if (econf->cmd_size == LCD_EXT_CMD_SIZE_DYNAMIC) {
lcd_extern_init_table_dynamic_size_print(econf, 1);
lcd_extern_init_table_dynamic_size_print(econf, 0);
} else {
lcd_extern_init_table_fixed_size_print(econf, 1);
lcd_extern_init_table_fixed_size_print(econf, 0);
}
break;
case LCD_EXTERN_SPI:
pr_info("type: spi(%d)\n", econf->type);
pr_info("spi_gpio_cs: %d\n"
"spi_gpio_clk: %d\n"
"spi_gpio_data: %d\n"
"spi_clk_freq: %dKHz\n"
"spi_delay_us: %d\n"
"spi_clk_pol: %d\n"
"table_loaded: %d\n"
"cmd_size: %d\n"
"table_init_on_cnt: %d\n"
"table_init_off_cnt: %d\n",
econf->spi_gpio_cs, econf->spi_gpio_clk,
econf->spi_gpio_data, econf->spi_clk_freq,
econf->spi_delay_us, econf->spi_clk_pol,
econf->table_init_loaded, econf->cmd_size,
econf->table_init_on_cnt,
econf->table_init_off_cnt);
if (econf->cmd_size == 0)
break;
if (econf->cmd_size == LCD_EXT_CMD_SIZE_DYNAMIC) {
lcd_extern_init_table_dynamic_size_print(econf, 1);
lcd_extern_init_table_dynamic_size_print(econf, 0);
} else {
lcd_extern_init_table_fixed_size_print(econf, 1);
lcd_extern_init_table_fixed_size_print(econf, 0);
}
break;
case LCD_EXTERN_MIPI:
pr_info("type: mipi(%d)\n", econf->type);
pr_info("table_loaded: %d\n"
"cmd_size: %d\n"
"table_init_on_cnt: %d\n"
"table_init_off_cnt: %d\n",
econf->table_init_loaded,
econf->cmd_size,
econf->table_init_on_cnt,
econf->table_init_off_cnt);
if (econf->cmd_size != LCD_EXT_CMD_SIZE_DYNAMIC)
break;
lcd_extern_init_table_dynamic_size_print(econf, 1);
lcd_extern_init_table_dynamic_size_print(econf, 0);
break;
default:
pr_info("not support extern_type\n");
break;
}
if (econf->pinmux_valid) {
pr_info("pinmux_flag: %d\n", lcd_ext_driver->pinmux_flag);
pr_info("pinmux_pointer: 0x%p\n", lcd_ext_driver->pin);
}
return sprintf(buf, "\n");
}
static ssize_t lcd_extern_key_valid_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", lcd_extern_config.key_valid);
}
static ssize_t lcd_extern_config_load_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", lcd_ext_config_load);
}
static const char *lcd_extern_debug_usage_str = {
"Usage:\n"
" echo test <on/off> > debug ; test power on/off for extern device\n"
" <on/off>: 1 for power on, 0 for power off\n"
" echo r <addr_sel> <reg> > debug ; read reg for extern device\n"
" echo d <addr_sel> <reg> <cnt> > debug ; dump regs for extern device\n"
" echo w <addr_sel> <reg> <value> > debug ; write reg for extern device\n"
};
static ssize_t lcd_extern_debug_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", lcd_extern_debug_usage_str);
}
static ssize_t lcd_extern_debug_store(struct class *class,
struct class_attribute *attr, const char *buf, size_t count)
{
unsigned int ret;
unsigned int val[3], i;
unsigned char reg, value;
if (lcd_ext_driver == NULL) {
pr_info("lcd_extern_driver is null\n");
return count;
}
switch (buf[0]) {
case 't':
ret = sscanf(buf, "test %d", &val[0]);
if (ret == 1) {
if (val[0]) {
if (lcd_ext_driver->power_on)
lcd_ext_driver->power_on();
} else {
if (lcd_ext_driver->power_off)
lcd_ext_driver->power_off();
}
} else {
pr_info("invalid data\n");
return -EINVAL;
}
break;
case 'r':
ret = sscanf(buf, "r %d %x", &val[0], &val[1]);
if (ret == 2) {
lcd_ext_driver->config->addr_sel =
(unsigned char)val[0];
reg = (unsigned char)val[1];
if (lcd_ext_driver->reg_read) {
lcd_ext_driver->reg_read(reg, &value);
pr_info("reg read: 0x%02x = 0x%02x\n",
reg, value);
}
} else {
pr_info("invalid data\n");
return -EINVAL;
}
break;
case 'd':
ret = sscanf(buf, "d %d %x %d", &val[0], &val[1], &val[2]);
if (ret == 3) {
lcd_ext_driver->config->addr_sel =
(unsigned char)val[0];
reg = (unsigned char)val[1];
if (lcd_ext_driver->reg_read) {
pr_info("reg dump:\n");
for (i = 0; i < val[2]; i++) {
lcd_ext_driver->reg_read(reg+i, &value);
pr_info(" 0x%02x = 0x%02x\n",
reg, value);
}
}
} else {
pr_info("invalid data\n");
return -EINVAL;
}
break;
case 'w':
ret = sscanf(buf, "w %d %x %x", &val[0], &val[1], &val[2]);
if (ret == 2) {
lcd_ext_driver->config->addr_sel =
(unsigned char)val[0];
reg = (unsigned char)val[1];
value = (unsigned char)val[2];
if (lcd_ext_driver->reg_write) {
lcd_ext_driver->reg_write(reg, value);
if (lcd_ext_driver->reg_read) {
lcd_ext_driver->reg_read(reg, &value);
pr_info(
"reg write 0x%02x = 0x%02x, readback: 0x%02x\n",
reg, val[2], value);
} else {
pr_info("reg write 0x%02x = 0x%02x\n",
reg, value);
}
}
} else {
pr_info("invalid data\n");
return -EINVAL;
}
break;
default:
pr_info("invalid data\n");
break;
}
return count;
}
static struct class_attribute lcd_extern_class_attrs[] = {
__ATTR(info, 0644,
lcd_extern_info_show, NULL),
__ATTR(key_valid, 0444,
lcd_extern_key_valid_show, NULL),
__ATTR(config_load, 0444,
lcd_extern_config_load_show, NULL),
__ATTR(debug, 0644,
lcd_extern_debug_show, lcd_extern_debug_store),
};
static struct class *debug_class;
static int creat_lcd_extern_class(void)
{
int i;
debug_class = class_create(THIS_MODULE, "lcd_ext");
if (IS_ERR(debug_class)) {
EXTERR("create debug class failed\n");
return -1;
}
for (i = 0; i < ARRAY_SIZE(lcd_extern_class_attrs); i++) {
if (class_create_file(debug_class,
&lcd_extern_class_attrs[i])) {
EXTERR("create debug attribute %s failed\n",
lcd_extern_class_attrs[i].attr.name);
}
}
return 0;
}
static int remove_lcd_extern_class(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(lcd_extern_class_attrs); i++)
class_remove_file(debug_class, &lcd_extern_class_attrs[i]);
class_destroy(debug_class);
debug_class = NULL;
return 0;
}
/* ********************************************************* */
static int aml_lcd_extern_probe(struct platform_device *pdev)
{
int ret;
lcd_extern_dev = &pdev->dev;
ret = lcd_extern_get_config();
if (ret == 0)
lcd_extern_add_driver(&lcd_extern_config);
creat_lcd_extern_class();
EXTPR("%s ok\n", __func__);
return 0;
}
static int aml_lcd_extern_remove(struct platform_device *pdev)
{
remove_lcd_extern_class();
kfree(lcd_ext_driver->config->table_init_on);
kfree(lcd_ext_driver->config->table_init_off);
lcd_ext_driver->config->table_init_on = NULL;
lcd_ext_driver->config->table_init_off = NULL;
kfree(lcd_ext_driver);
lcd_ext_driver = NULL;
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id aml_lcd_extern_dt_match[] = {
{
.compatible = "amlogic, lcd_extern",
},
{},
};
#endif
static struct platform_driver aml_lcd_extern_driver = {
.probe = aml_lcd_extern_probe,
.remove = aml_lcd_extern_remove,
.driver = {
.name = "lcd_extern",
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = aml_lcd_extern_dt_match,
#endif
},
};
static int __init aml_lcd_extern_init(void)
{
int ret;
if (lcd_debug_print_flag)
EXTPR("%s\n", __func__);
ret = platform_driver_register(&aml_lcd_extern_driver);
if (ret) {
EXTERR("driver register failed\n");
return -ENODEV;
}
return ret;
}
static void __exit aml_lcd_extern_exit(void)
{
platform_driver_unregister(&aml_lcd_extern_driver);
}
late_initcall(aml_lcd_extern_init);
module_exit(aml_lcd_extern_exit);
MODULE_AUTHOR("AMLOGIC");
MODULE_DESCRIPTION("LCD extern driver");
MODULE_LICENSE("GPL");