blob: 86298442c02b048d5c9f81feb9b56d8930171d5b [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/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/compat.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"
#include <linux/amlogic/gki_module.h>
#define EXT_CDEV_NAME "lcd_ext"
struct ext_cdev_s {
dev_t devno;
struct class *class;
};
static struct ext_cdev_s *ext_cdev;
/* for driver global resource init:
* 0: none
* n: initialized cnt
*/
static unsigned char ext_global_init_flag;
static unsigned int ext_drv_init_state;
static int lcd_ext_dev_cnt[LCD_MAX_DRV];
static int lcd_ext_index_lut[LCD_MAX_DRV][LCD_EXTERN_DEV_MAX];
static struct lcd_extern_driver_s *ext_driver[LCD_MAX_DRV];
struct lcd_extern_driver_s *lcd_extern_get_driver(int drv_index)
{
if (drv_index >= LCD_MAX_DRV)
return NULL;
return ext_driver[drv_index];
}
struct lcd_extern_dev_s *lcd_extern_get_dev(struct lcd_extern_driver_s *edrv, int dev_index)
{
int i = 0;
if (!edrv)
return NULL;
if (dev_index >= LCD_EXTERN_INDEX_INVALID)
return NULL;
for (i = 0; i < lcd_ext_dev_cnt[edrv->index]; i++) {
if (edrv->dev[i] && edrv->dev[i]->dev_index == dev_index)
return edrv->dev[i];
}
EXTERR("[%d]: invalid dev_index: %d\n", edrv->index, dev_index);
return NULL;
}
int lcd_extern_dev_index_add(int drv_index, int dev_index)
{
int dev_cnt, i;
if (drv_index >= LCD_MAX_DRV) {
EXTERR("%s: invalid drv_index: %d\n", __func__, drv_index);
return -1;
}
dev_cnt = lcd_ext_dev_cnt[drv_index];
if (dev_cnt >= LCD_EXTERN_DEV_MAX) {
EXTERR("[%d]: %s: out off dev_cnt support\n", drv_index, __func__);
return -1;
}
for (i = 0; i < LCD_EXTERN_DEV_MAX; i++) {
if (lcd_ext_index_lut[drv_index][i] == dev_index) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: dev_index %d already exist\n",
drv_index, __func__, dev_index);
}
return 0;
}
}
lcd_ext_index_lut[drv_index][dev_cnt] = dev_index;
lcd_ext_dev_cnt[drv_index]++;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: dev_index: %d, dev_cnt: %d\n",
drv_index, __func__, dev_index,
lcd_ext_dev_cnt[drv_index]);
}
return 0;
}
int lcd_extern_dev_index_remove(int drv_index, int dev_index)
{
int find, i;
if (drv_index >= LCD_MAX_DRV) {
EXTERR("%s: invalid drv_index: %d\n", __func__, dev_index);
return -1;
}
if (lcd_ext_dev_cnt[drv_index] == 0)
return -1;
find = 0xff;
for (i = 0; i < LCD_EXTERN_DEV_MAX; i++) {
if (lcd_ext_index_lut[drv_index][i] == dev_index)
find = i;
}
if (find == 0xff)
return 0;
lcd_ext_index_lut[drv_index][find] = LCD_EXTERN_INDEX_INVALID;
for (i = (find + 1); i < LCD_EXTERN_DEV_MAX; i++) {
if (lcd_ext_index_lut[drv_index][i] == LCD_EXTERN_INDEX_INVALID)
break;
lcd_ext_index_lut[drv_index][i - 1] = lcd_ext_index_lut[drv_index][i];
lcd_ext_index_lut[drv_index][i] = LCD_EXTERN_INDEX_INVALID;
}
if (lcd_ext_dev_cnt[drv_index])
lcd_ext_dev_cnt[drv_index]--;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
EXTPR("[%d]: %s: dev_index: %d\n", drv_index, __func__, dev_index);
return 0;
}
int lcd_extern_init(void)
{
int i, j;
for (i = 0; i < LCD_MAX_DRV; i++) {
for (j = 0; j < LCD_EXTERN_DEV_MAX; j++)
lcd_ext_index_lut[i][j] = LCD_EXTERN_INDEX_INVALID;
lcd_ext_dev_cnt[i] = 0;
}
return 0;
}
static void lcd_extern_gpio_probe(struct lcd_extern_driver_s *edrv, unsigned char index)
{
struct lcd_ext_gpio_s *ext_gpio;
const char *str;
int ret;
if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
EXTERR("[%d]: %s: invalid gpio index %d, exit\n",
edrv->index, __func__, index);
return;
}
ext_gpio = &edrv->gpio[index];
if (ext_gpio->probe_flag) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: gpio %s[%d] is already probed\n",
edrv->index, __func__, ext_gpio->name, index);
}
return;
}
/* get gpio name */
ret = of_property_read_string_index(edrv->sub_dev->of_node,
"extern_gpio_names", index, &str);
if (ret) {
EXTERR("[%d]: %s: failed to get extern_gpio_names: %d\n",
edrv->index, __func__, 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(struct lcd_extern_driver_s *edrv, int index)
{
struct lcd_ext_gpio_s *ext_gpio;
if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
EXTERR("[%d]: %s: invalid gpio index %d, exit\n",
edrv->index, __func__, index);
return;
}
ext_gpio = &edrv->gpio[index];
if (ext_gpio->probe_flag == 0) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: gpio [%d] is not probed, exit\n",
edrv->index, __func__, index);
}
return;
}
if (ext_gpio->register_flag == 0) {
EXTPR("[%d]: %s: gpio %s[%d] is already unregistered\n",
edrv->index, __func__, ext_gpio->name, index);
return;
}
if (IS_ERR(ext_gpio->gpio)) {
EXTERR("[%d]: %s: gpio %s[%d]: %p, err: %d\n",
edrv->index, __func__, ext_gpio->name, index,
ext_gpio->gpio, IS_ERR(ext_gpio->gpio));
ext_gpio->gpio = NULL;
return;
}
/* release gpio */
devm_gpiod_put(edrv->sub_dev, ext_gpio->gpio);
ext_gpio->register_flag = 0;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: release gpio %s[%d]\n",
edrv->index, __func__, ext_gpio->name, index);
}
}
static int lcd_extern_gpio_register(struct lcd_extern_driver_s *edrv,
unsigned char index, int init_value)
{
struct lcd_ext_gpio_s *ext_gpio;
int value;
if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
EXTERR("[%d]: %s: invalid gpio [%d], exit\n",
edrv->index, __func__, index);
return -1;
}
ext_gpio = &edrv->gpio[index];
if (ext_gpio->probe_flag == 0) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: gpio [%d] is not probed, exit\n",
edrv->index, __func__, index);
}
return -1;
}
if (ext_gpio->register_flag) {
EXTPR("[%d]: %s: gpio %s[%d] is already registered\n",
edrv->index, __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(edrv->sub_dev, "extern", index, value);
if (IS_ERR(ext_gpio->gpio)) {
EXTERR("[%d]: %s: gpio %s[%d]: %p, err: %d\n",
edrv->index, __func__, 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 & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: gpio %s[%d]: %p, init value: %d\n",
edrv->index, __func__, ext_gpio->name, index,
ext_gpio->gpio, init_value);
}
return 0;
}
void lcd_extern_gpio_set(struct lcd_extern_driver_s *edrv, unsigned char index, int value)
{
struct lcd_ext_gpio_s *ext_gpio;
if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
EXTERR("[%d]: %s: invalid gpio [%d], exit\n",
edrv->index, __func__, index);
return;
}
ext_gpio = &edrv->gpio[index];
if (ext_gpio->probe_flag == 0) {
EXTPR("[%d]: %s: gpio [%d] is not probed, exit\n",
edrv->index, __func__, index);
return;
}
if (ext_gpio->register_flag == 0) {
lcd_extern_gpio_register(edrv, index, value);
return;
}
if (IS_ERR_OR_NULL(ext_gpio->gpio)) {
EXTERR("[%d]: %s: gpio %s[%d]: %p, err: %ld\n",
edrv->index, __func__, 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 & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: set gpio %s[%d] value: %d\n",
edrv->index, __func__, ext_gpio->name, index, value);
}
}
unsigned int lcd_extern_gpio_get(struct lcd_extern_driver_s *edrv, unsigned char index)
{
struct lcd_ext_gpio_s *ext_gpio;
if (index >= LCD_EXTERN_GPIO_NUM_MAX) {
EXTERR("[%d]: %s: invalid gpio [%d], exit\n",
edrv->index, __func__, index);
return -1;
}
ext_gpio = &edrv->gpio[index];
if (ext_gpio->probe_flag == 0) {
EXTPR("[%d]: %s: gpio [%d] is not probed, exit\n",
edrv->index, __func__, index);
return -1;
}
if (ext_gpio->register_flag == 0) {
EXTERR("[%d]: %s: gpio %s[%d] is not registered, exit\n",
edrv->index, __func__, ext_gpio->name, index);
return -1;
}
if (IS_ERR_OR_NULL(ext_gpio->gpio)) {
EXTERR("[%d]: %s: gpio %s[%d]: %p, err: %ld\n",
edrv->index, __func__, 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(struct lcd_extern_driver_s *edrv, int status)
{
int index = 0xff;
if (edrv->pinmux_valid == 0) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
EXTPR("[%d]: %s: pinmux invalid, bypass\n",
edrv->index, __func__);
return;
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
EXTPR("[%d]: %s: %d\n", edrv->index, __func__, status);
index = (status) ? 0 : 1;
if (edrv->pinmux_flag == index) {
EXTPR("[%d]: %s: pinmux %s is already selected\n",
edrv->index, __func__, lcd_extern_pinmux_str[index]);
return;
}
/* request pinmux */
edrv->pin = devm_pinctrl_get_select(edrv->sub_dev, lcd_extern_pinmux_str[index]);
if (IS_ERR(edrv->pin)) {
EXTERR("[%d]: %s: set pinmux %s error\n",
edrv->index, __func__, lcd_extern_pinmux_str[index]);
} else {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: set pinmux %s ok\n",
edrv->index, __func__, lcd_extern_pinmux_str[index]);
}
}
edrv->pinmux_flag = index;
}
#ifdef CONFIG_OF
struct device_node *aml_lcd_extern_get_dts_child(struct lcd_extern_driver_s *edrv, int index)
{
char propname[15];
struct device_node *child;
sprintf(propname, "extern_%d", index);
child = of_get_child_by_name(edrv->sub_dev->of_node, propname);
return child;
}
static int lcd_extern_init_table_dynamic_dts(struct lcd_extern_driver_s *edrv,
struct device_node *np,
struct lcd_extern_config_s *econf,
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) {
max_len = LCD_EXTERN_INIT_ON_MAX;
sprintf(propname, "init_on");
} else {
max_len = LCD_EXTERN_INIT_OFF_MAX;
sprintf(propname, "init_off");
}
table = kcalloc(max_len, sizeof(unsigned char), GFP_KERNEL);
if (!table)
return -1;
table[0] = LCD_EXT_CMD_TYPE_END;
table[1] = 0;
switch (econf->type) {
case LCD_EXTERN_I2C:
case LCD_EXTERN_SPI:
while ((i + 1) < max_len) {
/* type */
ret = of_property_read_u32_index(np, propname, i, &val);
if (ret) {
EXTERR("%s: get %s type failed, step %d\n",
econf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_dts_err;
}
table[i] = (unsigned char)val;
type = table[i];
/* cmd_size */
ret = of_property_read_u32_index(np, propname, (i + 1), &val);
if (ret) {
EXTERR("%s: get %s cmd_size failed, step %d\n",
econf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_dts_err;
}
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",
econf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_dts_err;
}
/* data */
for (j = 0; j < cmd_size; j++) {
ret = of_property_read_u32_index(np, propname,
(i + 2 + j), &val);
if (ret) {
EXTERR("%s: get %s data failed, step %d\n",
econf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_dts_err;
}
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(edrv, index);
}
init_table_dynamic_i2c_spi_dts_next:
i += (cmd_size + 2);
step++;
}
if (flag)
econf->table_init_on_cnt = i + 2;
else
econf->table_init_off_cnt = i + 2;
break;
case LCD_EXTERN_MIPI:
while ((i + 1) < max_len) {
/* type */
ret = of_property_read_u32_index(np, propname, i, &val);
if (ret) {
EXTERR("%s: get %s type failed, step %d\n",
econf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_dts_err;
}
table[i] = (unsigned char)val;
type = table[i];
/* cmd_size */
ret = of_property_read_u32_index(np, propname, (i + 1), &val);
if (ret) {
EXTERR("%s: get %s cmd_size failed, step %d\n",
econf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_dts_err;
}
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",
econf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_dts_err;
}
for (j = 0; j < cmd_size; j++) {
ret = of_property_read_u32_index(np, propname,
(i + 2 + j), &val);
if (ret) {
EXTERR("%s: get %s failed, step %d\n",
econf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_dts_err;
}
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(edrv, index);
}
init_table_dynamic_mipi_dts_next:
i += (cmd_size + 2);
step++;
}
if (flag)
econf->table_init_on_cnt = i + 2;
else
econf->table_init_off_cnt = i + 2;
break;
default:
goto init_table_dynamic_dts_end;
}
if (flag) {
econf->table_init_on = kcalloc(econf->table_init_on_cnt,
sizeof(unsigned char), GFP_KERNEL);
if (!econf->table_init_on)
goto init_table_dynamic_dts_err;
memcpy(econf->table_init_on, table, econf->table_init_on_cnt);
} else {
econf->table_init_off = kcalloc(econf->table_init_off_cnt,
sizeof(unsigned char), GFP_KERNEL);
if (!econf->table_init_off)
goto init_table_dynamic_dts_err;
memcpy(econf->table_init_off, table, econf->table_init_off_cnt);
}
init_table_dynamic_dts_end:
kfree(table);
return 0;
init_table_dynamic_dts_err:
kfree(table);
return -1;
}
static int lcd_extern_init_table_fixed_dts(struct lcd_extern_driver_s *edrv,
struct device_node *np,
struct lcd_extern_config_s *econf,
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 = econf->cmd_size;
if (flag) {
max_len = LCD_EXTERN_INIT_ON_MAX;
sprintf(propname, "init_on");
} else {
max_len = LCD_EXTERN_INIT_OFF_MAX;
sprintf(propname, "init_off");
}
table = kcalloc(max_len, sizeof(unsigned char), GFP_KERNEL);
if (!table)
return -1;
table[0] = LCD_EXT_CMD_TYPE_END;
table[1] = 0;
while (i < max_len) { /* group detect */
if ((i + cmd_size) > max_len) {
EXTERR("%s: %s cmd_size out of support, step %d\n",
econf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
goto init_table_fixed_dts_err;
}
for (j = 0; j < cmd_size; j++) {
ret = of_property_read_u32_index(np, propname, (i + j), &val);
if (ret) {
EXTERR("%s: get %s failed, step %d\n",
econf->name, propname, step);
table[i] = LCD_EXT_CMD_TYPE_END;
goto init_table_fixed_dts_err;
}
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(edrv, index);
}
i += cmd_size;
step++;
}
if (flag) {
econf->table_init_on_cnt = i + cmd_size;
econf->table_init_on = kcalloc(econf->table_init_on_cnt,
sizeof(unsigned char), GFP_KERNEL);
if (!econf->table_init_on)
goto init_table_fixed_dts_err;
memcpy(econf->table_init_on, table, econf->table_init_on_cnt);
} else {
econf->table_init_off_cnt = i + cmd_size;
econf->table_init_off = kcalloc(econf->table_init_off_cnt,
sizeof(unsigned char), GFP_KERNEL);
if (!econf->table_init_off)
goto init_table_fixed_dts_err;
memcpy(econf->table_init_off, table, econf->table_init_off_cnt);
}
kfree(table);
return 0;
init_table_fixed_dts_err:
kfree(table);
return -1;
}
static int lcd_extern_get_config_dts(struct device_node *np,
struct lcd_extern_driver_s *edrv,
struct lcd_extern_dev_s *edev)
{
struct lcd_extern_config_s *econf;
char snode[15];
struct device_node *child;
const char *str;
unsigned int val;
int ret;
sprintf(snode, "extern_%d", edev->dev_index);
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
EXTPR("[%d]: %s: %s\n", edrv->index, __func__, snode);
child = of_get_child_by_name(np, snode);
if (!child) {
EXTERR("[%d]: failed to get %s\n", edrv->index, snode);
return -1;
}
econf = &edev->config;
ret = of_property_read_u32(child, "index", &val);
if (ret) {
EXTERR("get index failed, exit\n");
return -1;
}
econf->index = (unsigned char)val;
if (econf->index == LCD_EXTERN_INDEX_INVALID) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
EXTPR("[%d]: dev_index %d is invalid\n", edrv->index, econf->index);
return -1;
}
ret = of_property_read_string(child, "status", &str);
if (ret) {
EXTERR("[%d]: get index %d status failed\n", edrv->index, econf->index);
return -1;
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
EXTPR("[%d]: index %d status = %s\n", edrv->index, econf->index, str);
if (strncmp(str, "okay", 2) == 0)
econf->status = 1;
else
return -1;
ret = of_property_read_string(child, "extern_name", &str);
if (ret) {
EXTERR("[%d]: get extern_name failed\n", edrv->index);
strncpy(econf->name, "none", (LCD_EXTERN_NAME_LEN_MAX - 1));
} else {
strncpy(econf->name, str, (LCD_EXTERN_NAME_LEN_MAX - 1));
}
EXTPR("[%d]: load config: %s[%d]\n", edrv->index, econf->name, econf->index);
ret = of_property_read_u32(child, "type", &econf->type);
if (ret) {
econf->type = LCD_EXTERN_MAX;
EXTERR("[%d]: %s: get type failed, exit\n", edrv->index, econf->name);
return -1;
}
switch (econf->type) {
case LCD_EXTERN_I2C:
ret = of_property_read_u32(child, "i2c_address", &val);
if (ret) {
EXTERR("[%d]: %s: get i2c_address failed, exit\n",
edrv->index, econf->name);
econf->i2c_addr = LCD_EXT_I2C_ADDR_INVALID;
return -1;
}
econf->i2c_addr = (unsigned char)val;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: i2c_address = 0x%02x\n",
edrv->index, econf->name, econf->i2c_addr);
}
ret = of_property_read_u32(child, "i2c_address2", &val);
if (ret) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: no i2c_address2 exist\n",
edrv->index, econf->name);
}
econf->i2c_addr2 = LCD_EXT_I2C_ADDR_INVALID;
} else {
econf->i2c_addr2 = (unsigned char)val;
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: i2c_address2 = 0x%02x\n",
edrv->index, econf->name, econf->i2c_addr2);
}
ret = of_property_read_u32(child, "i2c_address3", &val);
if (ret) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: no i2c_address3 exist\n",
edrv->index, econf->name);
}
econf->i2c_addr3 = LCD_EXT_I2C_ADDR_INVALID;
} else {
econf->i2c_addr3 = (unsigned char)val;
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: i2c_address3 = 0x%02x\n",
edrv->index, econf->name, econf->i2c_addr3);
}
ret = of_property_read_u32(child, "i2c_address4", &val);
if (ret) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: no i2c_address4 exist\n",
edrv->index, econf->name);
}
econf->i2c_addr4 = LCD_EXT_I2C_ADDR_INVALID;
} else {
econf->i2c_addr4 = (unsigned char)val;
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: i2c_address4 = 0x%02x\n",
edrv->index, econf->name, econf->i2c_addr4);
}
ret = of_property_read_u32(child, "cmd_size", &val);
if (ret) {
EXTPR("[%d]: %s: no cmd_size\n", edrv->index, econf->name);
econf->cmd_size = 0;
} else {
econf->cmd_size = (unsigned char)val;
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: cmd_size = %d\n",
edrv->index, econf->name, econf->cmd_size);
}
if (econf->cmd_size == 0)
break;
if (econf->cmd_size == LCD_EXT_CMD_SIZE_DYNAMIC) {
ret = lcd_extern_init_table_dynamic_dts(edrv, child, econf, 1);
if (ret)
break;
ret = lcd_extern_init_table_dynamic_dts(edrv, child, econf, 0);
} else {
ret = lcd_extern_init_table_fixed_dts(edrv, child, econf, 1);
if (ret)
break;
ret = lcd_extern_init_table_fixed_dts(edrv, child, econf, 0);
}
if (ret == 0)
econf->table_init_loaded = 1;
break;
case LCD_EXTERN_SPI:
ret = of_property_read_u32(child, "gpio_spi_cs", &val);
if (ret) {
EXTERR("[%d]: %s: get gpio_spi_cs failed, exit\n",
edrv->index, econf->name);
econf->spi_gpio_cs = LCD_EXT_GPIO_INVALID;
return -1;
}
econf->spi_gpio_cs = val;
lcd_extern_gpio_probe(edrv, val);
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: spi_gpio_cs: %d\n",
edrv->index, econf->name, econf->spi_gpio_cs);
}
ret = of_property_read_u32(child, "gpio_spi_clk", &val);
if (ret) {
EXTERR("[%d]: %s: get gpio_spi_clk failed, exit\n",
edrv->index, econf->name);
econf->spi_gpio_clk = LCD_EXT_GPIO_INVALID;
return -1;
}
econf->spi_gpio_clk = val;
lcd_extern_gpio_probe(edrv, val);
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: spi_gpio_clk: %d\n",
edrv->index, econf->name, econf->spi_gpio_clk);
}
ret = of_property_read_u32(child, "gpio_spi_data", &val);
if (ret) {
EXTERR("[%d]: %s: get gpio_spi_data failed, exit\n",
edrv->index, econf->name);
econf->spi_gpio_data = LCD_EXT_GPIO_INVALID;
return -1;
}
econf->spi_gpio_data = val;
lcd_extern_gpio_probe(edrv, val);
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: spi_gpio_data: %d\n",
edrv->index, econf->name, econf->spi_gpio_data);
}
ret = of_property_read_u32(child, "spi_clk_freq", &val);
if (ret) {
EXTERR("[%d]: %s: get spi_clk_freq failed, default to %dKHz\n",
edrv->index, econf->name, LCD_EXT_SPI_CLK_FREQ_DFT);
econf->spi_clk_freq = LCD_EXT_SPI_CLK_FREQ_DFT;
} else {
econf->spi_clk_freq = val;
}
ret = of_property_read_u32(child, "spi_clk_pol", &val);
if (ret) {
EXTERR("[%d]: %s: get spi_clk_pol failed, default to 1\n",
edrv->index, econf->name);
econf->spi_clk_pol = 1;
} else {
econf->spi_clk_pol = (unsigned char)val;
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: spi_clk_freq: %dKHz, spi_clk_pol: %d\n",
edrv->index, econf->name, econf->spi_clk_freq,
econf->spi_clk_pol);
}
ret = of_property_read_u32(child, "cmd_size", &val);
if (ret) {
EXTPR("[%d]: %s: no cmd_size\n", edrv->index, econf->name);
econf->cmd_size = 0;
} else {
econf->cmd_size = (unsigned char)val;
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: cmd_size: %d\n",
edrv->index, econf->name, econf->cmd_size);
}
if (econf->cmd_size == 0)
break;
if (econf->cmd_size == LCD_EXT_CMD_SIZE_DYNAMIC) {
ret = lcd_extern_init_table_dynamic_dts(edrv, child, econf, 1);
if (ret)
break;
ret = lcd_extern_init_table_dynamic_dts(edrv, child, econf, 0);
} else {
ret = lcd_extern_init_table_fixed_dts(edrv, child, econf, 1);
if (ret)
break;
ret = lcd_extern_init_table_fixed_dts(edrv, child, econf, 0);
}
if (ret == 0)
econf->table_init_loaded = 1;
break;
case LCD_EXTERN_MIPI:
ret = of_property_read_u32(child, "cmd_size", &val);
if (ret) {
EXTPR("[%d]: %s: no cmd_size\n", edrv->index, econf->name);
econf->cmd_size = 0;
} else {
econf->cmd_size = (unsigned char)val;
}
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
EXTPR("[%d]: %s: cmd_size = %d\n",
edrv->index, econf->name, econf->cmd_size);
}
if (econf->cmd_size != LCD_EXT_CMD_SIZE_DYNAMIC)
break;
ret = lcd_extern_init_table_dynamic_dts(edrv, child, econf, 1);
if (ret)
break;
ret = lcd_extern_init_table_dynamic_dts(edrv, child, econf, 0);
if (ret == 0)
econf->table_init_loaded = 1;
break;
default:
break;
}
return 0;
}
#endif
static int lcd_extern_init_dynamic_ukey(struct lcd_extern_driver_s *edrv,
struct lcd_extern_config_s *econf,
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) {
max_len = LCD_EXTERN_INIT_ON_MAX;
sprintf(propname, "init_on");
buf = p;
} else {
max_len = LCD_EXTERN_INIT_OFF_MAX;
sprintf(propname, "init_off");
buf = p + econf->table_init_on_cnt;
}
table = kcalloc(max_len, sizeof(unsigned char), GFP_KERNEL);
if (!table)
return -1;
table[0] = LCD_EXT_CMD_TYPE_END;
table[1] = 0;
switch (econf->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", econf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_ukey_err;
}
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", econf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_ukey_err;
}
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", econf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_ukey_err;
}
/* step3: data */
len += cmd_size;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
EXTERR("%s: get %s failed\n", econf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_ukey_err;
}
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(edrv, index);
}
init_table_dynamic_i2c_spi_ukey_next:
i += (cmd_size + 2);
}
if (flag)
econf->table_init_on_cnt = i + 2;
else
econf->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", econf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_ukey_err;
}
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", econf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_ukey_err;
}
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", econf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_ukey_err;
}
/* data */
len += cmd_size;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
EXTERR("%s: get %s failed\n", econf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
table[i + 1] = 0;
goto init_table_dynamic_ukey_err;
}
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(edrv, index);
}
init_table_dynamic_mipi_ukey_next:
i += (cmd_size + 2);
}
if (flag)
econf->table_init_on_cnt = i + 2;
else
econf->table_init_off_cnt = i + 2;
break;
default:
goto init_table_dynamic_ukey_end;
}
if (flag) {
econf->table_init_on = kcalloc(econf->table_init_on_cnt,
sizeof(unsigned char), GFP_KERNEL);
if (!econf->table_init_on)
goto init_table_dynamic_ukey_err;
memcpy(econf->table_init_on, table, econf->table_init_on_cnt);
} else {
econf->table_init_off = kcalloc(econf->table_init_off_cnt,
sizeof(unsigned char), GFP_KERNEL);
if (!econf->table_init_off)
goto init_table_dynamic_ukey_err;
memcpy(econf->table_init_off, table, econf->table_init_off_cnt);
}
init_table_dynamic_ukey_end:
kfree(table);
return 0;
init_table_dynamic_ukey_err:
kfree(table);
return -1;
}
static int lcd_extern_init_fixed_ukey(struct lcd_extern_driver_s *edrv,
struct lcd_extern_config_s *econf,
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 = econf->cmd_size;
if (flag) {
max_len = LCD_EXTERN_INIT_ON_MAX;
sprintf(propname, "init_on");
buf = p;
} else {
max_len = LCD_EXTERN_INIT_OFF_MAX;
sprintf(propname, "init_off");
buf = p + econf->table_init_on_cnt;
}
table = kcalloc(max_len, sizeof(unsigned char), GFP_KERNEL);
if (!table)
return -1;
table[0] = LCD_EXT_CMD_TYPE_END;
table[1] = 0;
while (i < max_len) {
if ((i + cmd_size) > max_len) {
EXTERR("%s: %s cmd_size out of max\n", econf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
goto init_table_fixed_ukey_err;
}
len += cmd_size;
ret = lcd_unifykey_len_check(key_len, len);
if (ret) {
EXTERR("%s: get %s failed\n", econf->name, propname);
table[i] = LCD_EXT_CMD_TYPE_END;
for (j = 1; j < cmd_size; j++)
table[i + j] = 0;
goto init_table_fixed_ukey_err;
}
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 + 2];
if (index < LCD_EXTERN_GPIO_NUM_MAX)
lcd_extern_gpio_probe(edrv, index);
}
i += cmd_size;
}
if (flag) {
econf->table_init_on_cnt = i + cmd_size;
econf->table_init_on = kcalloc(econf->table_init_on_cnt,
sizeof(unsigned char), GFP_KERNEL);
if (!econf->table_init_on)
goto init_table_fixed_ukey_err;
memcpy(econf->table_init_on, table, econf->table_init_on_cnt);
} else {
econf->table_init_off_cnt = i + cmd_size;
econf->table_init_off = kcalloc(econf->table_init_off_cnt,
sizeof(unsigned char), GFP_KERNEL);
if (!econf->table_init_off)
goto init_table_fixed_ukey_err;
memcpy(econf->table_init_off, table, econf->table_init_off_cnt);
}
kfree(table);
return 0;
init_table_fixed_ukey_err:
kfree(table);
return -1;
}
static int lcd_extern_get_config_unifykey(struct lcd_extern_driver_s *edrv,
struct lcd_extern_dev_s *edev, char *skey)
{
struct lcd_extern_config_s *econf;
struct aml_lcd_unifykey_header_s ext_header;
unsigned char *para, *p;
int key_len, len;
const char *str;
int ret;
if (!edev)
return -1;
econf = &edev->config;
key_len = LCD_UKEY_LCD_EXT_SIZE;
para = kcalloc(key_len, sizeof(unsigned char), GFP_KERNEL);
if (!para)
return -1;
ret = lcd_unifykey_get(skey, para, &key_len);
if (ret)
goto lcd_extern_get_config_unifykey_error;
/* 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");
goto lcd_extern_get_config_unifykey_error;
}
/* header: 10byte */
lcd_unifykey_header_check(para, &ext_header);
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
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(econf->name, str, (LCD_EXTERN_NAME_LEN_MAX - 1));
econf->index = *(p + LCD_UKEY_EXT_INDEX);
econf->type = *(p + LCD_UKEY_EXT_TYPE);
econf->status = *(p + LCD_UKEY_EXT_STATUS);
if (econf->index == LCD_EXTERN_INDEX_INVALID) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
EXTPR("index %d is invalid\n", econf->index);
goto lcd_extern_get_config_unifykey_error;
}
/* type: 10byte */
switch (econf->type) {
case LCD_EXTERN_I2C:
if (*(p + LCD_UKEY_EXT_TYPE_VAL_0))
econf->i2c_addr = *(p + LCD_UKEY_EXT_TYPE_VAL_0);
else
econf->i2c_addr = LCD_EXT_I2C_ADDR_INVALID;
if (*(p + LCD_UKEY_EXT_TYPE_VAL_1))
econf->i2c_addr2 = *(p + LCD_UKEY_EXT_TYPE_VAL_1);
else
econf->i2c_addr2 = LCD_EXT_I2C_ADDR_INVALID;
if (*(p + LCD_UKEY_EXT_TYPE_VAL_4))
econf->i2c_addr3 = *(p + LCD_UKEY_EXT_TYPE_VAL_4);
else
econf->i2c_addr3 = LCD_EXT_I2C_ADDR_INVALID;
if (*(p + LCD_UKEY_EXT_TYPE_VAL_5))
econf->i2c_addr4 = *(p + LCD_UKEY_EXT_TYPE_VAL_5);
else
econf->i2c_addr4 = LCD_EXT_I2C_ADDR_INVALID;
econf->cmd_size = *(p + LCD_UKEY_EXT_TYPE_VAL_3);
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
EXTPR("%s: cmd_size = %d\n", econf->name, econf->cmd_size);
/* init */
if (econf->cmd_size == 0)
break;
if (econf->cmd_size == LCD_EXT_CMD_SIZE_DYNAMIC) {
ret = lcd_extern_init_dynamic_ukey(edrv, econf, p, key_len, len, 1);
if (ret)
break;
ret = lcd_extern_init_dynamic_ukey(edrv, econf, p, key_len, len, 0);
} else {
ret = lcd_extern_init_fixed_ukey(edrv, econf, p, key_len, len, 1);
if (ret)
break;
ret = lcd_extern_init_fixed_ukey(edrv, econf, p, key_len, len, 0);
}
if (ret == 0)
econf->table_init_loaded = 1;
break;
case LCD_EXTERN_SPI:
econf->spi_gpio_cs = *(p + LCD_UKEY_EXT_TYPE_VAL_0);
lcd_extern_gpio_probe(edrv, econf->spi_gpio_cs);
econf->spi_gpio_clk = *(p + LCD_UKEY_EXT_TYPE_VAL_1);
lcd_extern_gpio_probe(edrv, econf->spi_gpio_clk);
econf->spi_gpio_data = *(p + LCD_UKEY_EXT_TYPE_VAL_2);
lcd_extern_gpio_probe(edrv, econf->spi_gpio_data);
econf->spi_clk_freq = (*(p + LCD_UKEY_EXT_TYPE_VAL_3) |
((*(p + LCD_UKEY_EXT_TYPE_VAL_4)) << 8));
econf->spi_clk_pol = *(p + LCD_UKEY_EXT_TYPE_VAL_5);
econf->cmd_size = *(p + LCD_UKEY_EXT_TYPE_VAL_6);
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
EXTPR("%s: cmd_size = %d\n", econf->name, econf->cmd_size);
/* init */
if (econf->cmd_size == 0)
break;
if (econf->cmd_size == LCD_EXT_CMD_SIZE_DYNAMIC) {
ret = lcd_extern_init_dynamic_ukey(edrv, econf, p, key_len, len, 1);
if (ret)
break;
ret = lcd_extern_init_dynamic_ukey(edrv, econf, p, key_len, len, 0);
} else {
ret = lcd_extern_init_fixed_ukey(edrv, econf, p, key_len, len, 1);
if (ret)
break;
ret = lcd_extern_init_fixed_ukey(edrv, econf, p, key_len, len, 0);
}
if (ret == 0)
econf->table_init_loaded = 1;
break;
case LCD_EXTERN_MIPI:
econf->cmd_size = *(p + LCD_UKEY_EXT_TYPE_VAL_0);
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
EXTPR("%s: cmd_size = %d\n", econf->name, econf->cmd_size);
if (econf->cmd_size != LCD_EXT_CMD_SIZE_DYNAMIC)
break;
ret = lcd_extern_init_dynamic_ukey(edrv, econf, p, key_len, len, 1);
if (ret)
break;
ret = lcd_extern_init_dynamic_ukey(edrv, econf, p, key_len, len, 0);
if (ret == 0)
econf->table_init_loaded = 1;
break;
default:
break;
}
kfree(para);
return 0;
lcd_extern_get_config_unifykey_error:
kfree(para);
return 0;
}
static struct lcd_extern_dev_s *lcd_extern_dev_malloc(int dev_index)
{
struct lcd_extern_dev_s *edev;
edev = kzalloc(sizeof(*edev), GFP_KERNEL);
if (!edev)
return NULL;
edev->dev_index = dev_index;
edev->config.index = LCD_EXTERN_INDEX_INVALID;
edev->config.type = LCD_EXTERN_MAX;
edev->config.i2c_addr = LCD_EXT_I2C_ADDR_INVALID;
edev->config.i2c_addr2 = LCD_EXT_I2C_ADDR_INVALID;
edev->config.i2c_addr3 = LCD_EXT_I2C_ADDR_INVALID;
edev->config.i2c_addr4 = LCD_EXT_I2C_ADDR_INVALID;
edev->config.spi_gpio_cs = LCD_EXT_GPIO_INVALID;
edev->config.spi_gpio_clk = LCD_EXT_GPIO_INVALID;
edev->config.spi_gpio_data = LCD_EXT_GPIO_INVALID;
edev->config.spi_clk_pol = 1;
return edev;
}
static void lcd_extern_dev_free(struct lcd_extern_dev_s *edev)
{
if (!edev)
return;
kfree(edev->config.table_init_on);
edev->config.table_init_on = NULL;
kfree(edev->config.table_init_off);
edev->config.table_init_off = NULL;
kfree(edev);
}
static int lcd_extern_add_dev(struct lcd_extern_driver_s *edrv,
struct lcd_extern_dev_s *edev)
{
int ret = -1;
if (edev->config.status == 0) {
EXTERR("[%d]: %s: %s[%d] status is disabled\n",
edrv->index, __func__,
edev->config.name, edev->dev_index);
return -1;
}
if (strcmp(edev->config.name, "ext_default") == 0) {
if (edev->config.type == LCD_EXTERN_MIPI) {
if (edev->config.cmd_size != LCD_EXT_CMD_SIZE_DYNAMIC) {
EXTERR("[%d]: %s: %s(%d): cmd_size %d is invalid\n",
edrv->index, __func__,
edev->config.name,
edev->dev_index,
edev->config.cmd_size);
} else {
ret = lcd_extern_mipi_default_probe(edrv, edev);
}
} else {
if (edev->config.cmd_size < 2) {
EXTERR("[%d]: %s: %s(%d): cmd_size %d is invalid\n",
edrv->index, __func__,
edev->config.name,
edev->dev_index,
edev->config.cmd_size);
} else {
ret = lcd_extern_default_probe(edrv, edev);
}
}
} else if (strcmp(edev->config.name, "i2c_CS602") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_CS602
ret = lcd_extern_i2c_CS602_probe(edrv, edev);
#endif
} else if (strcmp(edev->config.name, "i2c_ANX6862_7911") == 0) {
#ifdef CONFIG_AMLOGIC_LCD_EXTERN_I2C_ANX6862_7911
ret = lcd_extern_i2c_ANX6862_7911_probe(edrv, edev);
#endif
} else {
EXTERR("[%d]: %s: invalid dev: %s(%d)\n",
edrv->index, __func__,
edev->config.name, edev->dev_index);
}
if (ret) {
EXTERR("[%d]: %s: %s(%d) failed\n",
edrv->index, __func__,
edev->config.name, edev->dev_index);
return -1;
}
EXTPR("[%d]: %s: %s(%d) ok\n",
edrv->index, __func__,
edev->config.name, edev->dev_index);
return 0;
}
static void lcd_extern_dev_probe_work(struct work_struct *work)
{
struct lcd_extern_driver_s *edrv;
bool is_init;
char skey[15];
int index, i, dev_index;
int ret;
edrv = container_of(work, struct lcd_extern_driver_s, dev_probe_work);
index = edrv->index;
if (edrv->index == 0)
sprintf(skey, "lcd_extern");
else
sprintf(skey, "lcd%d_extern", edrv->index);
is_init = lcd_unifykey_init_get();
i = 0;
while (!is_init) {
if (i++ >= LCD_UNIFYKEY_WAIT_TIMEOUT)
break;
lcd_delay_ms(LCD_UNIFYKEY_RETRY_INTERVAL);
is_init = lcd_unifykey_init_get();
}
if (!is_init) {
EXTERR("[%d]: key_init_flag=%d\n", edrv->index, is_init);
goto lcd_extern_dev_probe_work_err;
}
ret = lcd_unifykey_check(skey);
if (ret)
goto lcd_extern_dev_probe_work_err;
dev_index = 0;
edrv->dev_cnt = 1;
edrv->dev[dev_index] = lcd_extern_dev_malloc(dev_index);
EXTPR("[%d]: %s: from unifykey\n", edrv->index, __func__);
ret = lcd_extern_get_config_unifykey(edrv, edrv->dev[dev_index], skey);
if (ret) {
lcd_extern_dev_free(edrv->dev[dev_index]);
edrv->dev[dev_index] = NULL;
goto lcd_extern_dev_probe_work_err;
}
ret = lcd_extern_add_dev(edrv, edrv->dev[dev_index]);
if (ret) {
lcd_extern_dev_free(edrv->dev[dev_index]);
edrv->dev[dev_index] = NULL;
goto lcd_extern_dev_probe_work_err;
}
return;
lcd_extern_dev_probe_work_err:
/* free drvdata */
platform_set_drvdata(edrv->pdev, NULL);
/* free drv */
kfree(edrv);
ext_driver[index] = NULL;
EXTPR("[%d]: %s: failed\n", index, __func__);
}
static int lcd_extern_config_load(struct lcd_extern_driver_s *edrv)
{
struct device_node *np;
unsigned int para[5];
const char *str;
int dev_index, i;
int ret;
if (!edrv->pdev->dev.of_node) {
EXTERR("[%d]: %s: dev of_node is null\n", edrv->index, __func__);
return -1;
}
np = edrv->pdev->dev.of_node;
ret = of_property_read_string(np, "i2c_bus", &str);
if (ret)
edrv->i2c_bus = LCD_EXT_I2C_BUS_MAX;
else
edrv->i2c_bus = aml_lcd_i2c_bus_get_str(str);
ret = of_property_read_string(np, "pinctrl-names", &str);
if (ret)
edrv->pinmux_valid = 0;
else
edrv->pinmux_valid = 1;
edrv->pinmux_flag = 0xff;
ret = of_property_read_u32(np, "key_valid", &para[0]);
if (ret) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
EXTPR("failed to get key_valid\n");
edrv->key_valid = 0;
} else {
edrv->key_valid = (unsigned char)para[0];
}
EXTPR("[%d]: key_valid: %d\n", edrv->index, edrv->key_valid);
if (edrv->key_valid) {
edrv->config_load = 1;
lcd_queue_work(&edrv->dev_probe_work);
} else {
edrv->config_load = 0;
EXTPR("[%d]: %s from dts\n", edrv->index, __func__);
for (i = 0; i < edrv->dev_cnt; i++) {
dev_index = lcd_ext_index_lut[edrv->index][i];
if (dev_index == LCD_EXTERN_INDEX_INVALID) {
EXTPR("[%d]: %s: invalid dev_index\n", edrv->index, __func__);
continue;
}
edrv->dev[dev_index] = lcd_extern_dev_malloc(dev_index);
if (!edrv->dev[dev_index])
continue;
ret = lcd_extern_get_config_dts(np, edrv, edrv->dev[dev_index]);
if (ret) {
lcd_extern_dev_free(edrv->dev[dev_index]);
edrv->dev[dev_index] = NULL;
continue;
}
ret = lcd_extern_add_dev(edrv, edrv->dev[dev_index]);
if (ret) {
lcd_extern_dev_free(edrv->dev[dev_index]);
edrv->dev[dev_index] = NULL;
}
}
}
return 0;
}
/* *********************************************************
* debug function
* *********************************************************
*/
static int lcd_extern_init_dynamic_print(char *buf, struct lcd_extern_config_s *econf, int flag)
{
int i, j, max_len;
unsigned char type, size;
unsigned char *table;
int len = 0;
if (flag) {
len = sprintf(buf, "power on:\n");
table = econf->table_init_on;
max_len = econf->table_init_on_cnt;
} else {
len = sprintf(buf, "power off:\n");
table = econf->table_init_off;
max_len = econf->table_init_off_cnt;
}
if (!table) {
len += sprintf(buf + len, "init_table %d is NULL\n", flag);
return len;
}
i = 0;
switch (econf->type) {
case LCD_EXTERN_I2C:
case LCD_EXTERN_SPI:
while ((i + 1) < max_len) {
type = table[i];
size = table[i + 1];
if (type == LCD_EXT_CMD_TYPE_END) {
len += sprintf(buf + len, " 0x%02x,%d,\n", type, size);
break;
}
len += sprintf(buf + len, " 0x%02x,%d,", type, size);
if (size == 0)
goto init_table_dynamic_print_i2c_spi_next;
if (i + 2 + size > max_len) {
len += sprintf(buf + len, "size out of support\n");
break;
}
if (type == LCD_EXT_CMD_TYPE_GPIO ||
type == LCD_EXT_CMD_TYPE_DELAY) {
for (j = 0; j < size; j++)
len += sprintf(buf + len, "%d,", table[i + 2 + j]);
} else if ((type == LCD_EXT_CMD_TYPE_CMD) ||
(type == LCD_EXT_CMD_TYPE_CMD2) ||
(type == LCD_EXT_CMD_TYPE_CMD3) ||
(type == LCD_EXT_CMD_TYPE_CMD4) ||
(type == LCD_EXT_CMD_TYPE_CMD_BIN) ||
(type == LCD_EXT_CMD_TYPE_CMD2_BIN) ||
(type == LCD_EXT_CMD_TYPE_CMD3_BIN) ||
(type == LCD_EXT_CMD_TYPE_CMD4_BIN) ||
(type == LCD_EXT_CMD_TYPE_CMD_BIN_DATA) ||
(type == LCD_EXT_CMD_TYPE_CMD2_BIN_DATA) ||
(type == LCD_EXT_CMD_TYPE_CMD3_BIN_DATA) ||
(type == LCD_EXT_CMD_TYPE_CMD4_BIN_DATA)) {
for (j = 0; j < size; j++)
len += sprintf(buf + len, "0x%02x,", table[i + 2 + j]);
} else if ((type == LCD_EXT_CMD_TYPE_CMD_DELAY) ||
(type == LCD_EXT_CMD_TYPE_CMD2_DELAY)) {
for (j = 0; j < (size - 1); j++)
len += sprintf(buf + len, "0x%02x,", table[i + 2 + j]);
len += sprintf(buf + len, "%d,", table[i + size + 1]);
} else {
for (j = 0; j < size; j++)
len += sprintf(buf + len, "0x%02x,", table[i + 2 + j]);
}
init_table_dynamic_print_i2c_spi_next:
len += sprintf(buf + len, "\n");
i += (size + 2);
}
break;
case LCD_EXTERN_MIPI:
while ((i + 1) < max_len) {
type = table[i];
size = table[i + 1];
if (type == LCD_EXT_CMD_TYPE_END) {
if (size == 0xff) {
len += sprintf(buf + len, " 0x%02x,0x%02x,\n",
type, size);
break;
}
if (size == 0) {
len += sprintf(buf + len, " 0x%02x,%d,\n", type, size);
break;
}
size = 0;
}
len += sprintf(buf + len, " 0x%02x,%d,", type, size);
if (size == 0)
goto init_table_dynamic_print_mipi_next;
if (i + 2 + size > max_len) {
len += sprintf(buf + len, "size out of support\n");
break;
}
if (type == LCD_EXT_CMD_TYPE_GPIO ||
type == LCD_EXT_CMD_TYPE_DELAY) {
for (j = 0; j < size; j++)
len += sprintf(buf + len, "%d,", table[i + 2 + j]);
} else if ((type & 0xf) == 0x0) {
len += sprintf(buf + len, " init_%s wrong data_type: 0x%02x\n",
flag ? "on" : "off", type);
break;
} else {
size = table[i + DSI_CMD_SIZE_INDEX];
len += sprintf(buf + len, " 0x%02x,%d,", type, size);
for (j = 0; j < size; j++)
len += sprintf(buf + len, "0x%02x,", table[i + 2 + j]);
}
init_table_dynamic_print_mipi_next:
len += sprintf(buf + len, "\n");
i += (size + 2);
}
break;
default:
break;
}
return len;
}
static int lcd_extern_init_fixed_print(char *buf, struct lcd_extern_config_s *econf, int flag)
{
int i, j, max_len;
unsigned char cmd_size;
unsigned char *table;
int len = 0;
cmd_size = econf->cmd_size;
if (flag) {
len = sprintf(buf, "power on:\n");
table = econf->table_init_on;
max_len = econf->table_init_on_cnt;
} else {
len = sprintf(buf, "power off:\n");
table = econf->table_init_off;
max_len = econf->table_init_off_cnt;
}
if (!table) {
len += sprintf(buf + len, "init_table %d is NULL\n", flag);
return len;
}
i = 0;
while ((i + cmd_size) <= max_len) {
len += sprintf(buf + len, " ");
for (j = 0; j < cmd_size; j++)
len += sprintf(buf + len, " 0x%02x", table[i + j]);
len += sprintf(buf + len, "\n");
if (table[i] == LCD_EXT_CMD_TYPE_END)
break;
i += cmd_size;
}
return len;
}
static ssize_t lcd_extern_info_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lcd_extern_driver_s *edrv = dev_get_drvdata(dev);
struct lcd_extern_dev_s *edev;
ssize_t len = 0;
int i = 0;
len = sprintf(buf, "lcd extern driver[%d] info:\n", edrv->index);
for (i = 0; i < edrv->dev_cnt; i++) {
edev = edrv->dev[i];
if (!edev)
continue;
len += sprintf(buf + len, "dev[%d]: %s\n",
edev->dev_index, edev->config.name);
len += sprintf(buf + len, "status: %d\n", edev->config.status);
switch (edev->config.type) {
case LCD_EXTERN_I2C:
len += sprintf(buf + len,
"type: i2c(%d)\n"
"i2c_addr: 0x%02x\n"
"i2c_addr2: 0x%02x\n"
"i2c_addr3: 0x%02x\n"
"i2c_addr4: 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",
edev->config.type,
edev->config.i2c_addr, edev->config.i2c_addr2,
edev->config.i2c_addr3, edev->config.i2c_addr4,
edrv->i2c_bus,
edev->config.table_init_loaded, edev->config.cmd_size,
edev->config.table_init_on_cnt,
edev->config.table_init_off_cnt);
if (edev->config.cmd_size == 0)
break;
if (edev->config.cmd_size == LCD_EXT_CMD_SIZE_DYNAMIC) {
len += lcd_extern_init_dynamic_print(buf + len, &edev->config, 1);
len += lcd_extern_init_dynamic_print(buf + len, &edev->config, 0);
} else {
len += lcd_extern_init_fixed_print(buf + len, &edev->config, 1);
len += lcd_extern_init_fixed_print(buf + len, &edev->config, 0);
}
break;
case LCD_EXTERN_SPI:
len += sprintf(buf + len,
"type: spi(%d)\n"
"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",
edev->config.type,
edev->config.spi_gpio_cs, edev->config.spi_gpio_clk,
edev->config.spi_gpio_data, edev->config.spi_clk_freq,
edev->config.spi_delay_us, edev->config.spi_clk_pol,
edev->config.table_init_loaded, edev->config.cmd_size,
edev->config.table_init_on_cnt,
edev->config.table_init_off_cnt);
if (edev->config.cmd_size == 0)
break;
if (edev->config.cmd_size == LCD_EXT_CMD_SIZE_DYNAMIC) {
len += lcd_extern_init_dynamic_print(buf + len, &edev->config, 1);
len += lcd_extern_init_dynamic_print(buf + len, &edev->config, 0);
} else {
len += lcd_extern_init_fixed_print(buf + len, &edev->config, 1);
len += lcd_extern_init_fixed_print(buf + len, &edev->config, 0);
}
break;
case LCD_EXTERN_MIPI:
len += sprintf(buf + len,
"type: mipi(%d)\n"
"table_loaded: %d\n"
"cmd_size: %d\n"
"table_init_on_cnt: %d\n"
"table_init_off_cnt: %d\n",
edev->config.type,
edev->config.table_init_loaded,
edev->config.cmd_size,
edev->config.table_init_on_cnt,
edev->config.table_init_off_cnt);
if (edev->config.cmd_size != LCD_EXT_CMD_SIZE_DYNAMIC)
break;
len += lcd_extern_init_dynamic_print(buf + len, &edev->config, 1);
len += lcd_extern_init_dynamic_print(buf + len, &edev->config, 0);
break;
default:
len += sprintf(buf + len, "invalid extern_type\n");
break;
}
if (edrv->pinmux_valid) {
len += sprintf(buf + len,
"pinmux_flag: %d\n"
"pinmux_pointer: 0x%p\n",
edrv->pinmux_flag, edrv->pin);
}
}
return len;
}
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 device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", lcd_extern_debug_usage_str);
}
static ssize_t lcd_extern_debug_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct lcd_extern_driver_s *edrv = dev_get_drvdata(dev);
struct lcd_extern_dev_s *edev;
unsigned int ret, j;
unsigned int val[3];
unsigned char reg, value;
unsigned char reg_buf[2];
unsigned int index = LCD_EXTERN_INDEX_INVALID;
switch (buf[0]) {
case 't':
ret = sscanf(buf, "test %d %d", &index, &val[0]);
if (ret == 2) {
edev = lcd_extern_get_dev(edrv, index);
if (!edev)
return -EINVAL;
if (val[0]) {
if (edev->power_on)
edev->power_on(edrv, edev);
} else {
if (edev->power_off)
edev->power_off(edrv, edev);
}
} else {
pr_info("invalid data\n");
return -EINVAL;
}
break;
case 'r':
ret = sscanf(buf, "r %d %d %x", &index, &val[0], &val[1]);
if (ret == 3) {
edev = lcd_extern_get_dev(edrv, index);
if (!edev)
return -EINVAL;
edev->addr_sel = (unsigned char)val[0];
reg = (unsigned char)val[1];
if (edev->reg_read) {
edev->reg_read(edrv, edev, 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 %d %x %d", &index, &val[0], &val[1], &val[2]);
if (ret == 4) {
edev = lcd_extern_get_dev(edrv, index);
if (!edev)
return -EINVAL;
edev->addr_sel = (unsigned char)val[0];
reg = (unsigned char)val[1];
if (edev->reg_read) {
pr_info("reg dump:\n");
for (j = 0; j < val[2]; j++) {
edev->reg_read(edrv, edev, reg + j, &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 %d %x %x", &index, &val[0],
&val[1], &val[2]);
if (ret == 4) {
edev = lcd_extern_get_dev(edrv, index);
if (!edev)
return -EINVAL;
edev->addr_sel = (unsigned char)val[0];
reg = (unsigned char)val[1];
value = (unsigned char)val[2];
if (edev->reg_write) {
reg_buf[0] = (unsigned char)val[1];
reg_buf[1] = (unsigned char)val[2];
edev->reg_write(edrv, edev, reg_buf, 2);
if (edev->reg_read) {
edev->reg_read(edrv, edev, 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 device_attribute lcd_extern_debug_attrs[] = {
__ATTR(info, 0444, lcd_extern_info_show, NULL),
__ATTR(debug, 0644, lcd_extern_debug_show, lcd_extern_debug_store),
};
static int lcd_extern_debug_file_creat(struct lcd_extern_driver_s *edrv)
{
int i;
for (i = 0; i < ARRAY_SIZE(lcd_extern_debug_attrs); i++) {
if (device_create_file(edrv->sub_dev, &lcd_extern_debug_attrs[i])) {
EXTERR("[%d]: create debug attribute %s fail\n",
edrv->index, lcd_extern_debug_attrs[i].attr.name);
}
}
return 0;
}
static int lcd_extern_debug_file_remove(struct lcd_extern_driver_s *edrv)
{
int i;
for (i = 0; i < ARRAY_SIZE(lcd_extern_debug_attrs); i++)
device_remove_file(edrv->sub_dev, &lcd_extern_debug_attrs[i]);
return 0;
}
/* ************************************************************* */
static int ext_io_open(struct inode *inode, struct file *file)
{
struct lcd_extern_driver_s *edrv;
edrv = container_of(inode->i_cdev, struct lcd_extern_driver_s, cdev);
file->private_data = edrv;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
EXTPR("%s\n", __func__);
return 0;
}
static int ext_io_release(struct inode *inode, struct file *file)
{
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
EXTPR("%s\n", __func__);
file->private_data = NULL;
return 0;
}
static long ext_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
return 0;
}
#ifdef CONFIG_COMPAT
static long ext_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
unsigned long ret;
arg = (unsigned long)compat_ptr(arg);
ret = ext_ioctl(file, cmd, arg);
return ret;
}
#endif
static const struct file_operations ext_fops = {
.owner = THIS_MODULE,
.open = ext_io_open,
.release = ext_io_release,
.unlocked_ioctl = ext_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ext_compat_ioctl,
#endif
};
static int ext_cdev_add(struct lcd_extern_driver_s *edrv, struct device *parent)
{
dev_t devno;
int ret = 0;
if (!edrv) {
EXTERR("%s: edrv is null\n", __func__);
return -1;
}
if (!ext_cdev) {
ret = 1;
goto ext_cdev_add_failed;
}
devno = MKDEV(MAJOR(ext_cdev->devno), edrv->index);
cdev_init(&edrv->cdev, &ext_fops);
edrv->cdev.owner = THIS_MODULE;
ret = cdev_add(&edrv->cdev, devno, 1);
if (ret) {
ret = 2;
goto ext_cdev_add_failed;
}
edrv->sub_dev = device_create(ext_cdev->class, parent,
devno, NULL, "ext%d", edrv->index);
if (IS_ERR_OR_NULL(edrv->sub_dev)) {
ret = 3;
goto ext_cdev_add_failed1;
}
dev_set_drvdata(edrv->sub_dev, edrv);
edrv->sub_dev->of_node = parent->of_node;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
EXTPR("[%d]: %s OK\n", edrv->index, __func__);
return 0;
ext_cdev_add_failed1:
cdev_del(&edrv->cdev);
ext_cdev_add_failed:
EXTERR("[%d]: %s: failed: %d\n", edrv->index, __func__, ret);
return -1;
}
static void ext_cdev_remove(struct lcd_extern_driver_s *edrv)
{
dev_t devno;
if (!ext_cdev || !edrv)
return;
devno = MKDEV(MAJOR(ext_cdev->devno), edrv->index);
device_destroy(ext_cdev->class, devno);
cdev_del(&edrv->cdev);
}
static int ext_global_init_once(void)
{
int ret;
if (ext_global_init_flag) {
ext_global_init_flag++;
return 0;
}
ext_global_init_flag++;
ext_cdev = kzalloc(sizeof(*ext_cdev), GFP_KERNEL);
if (!ext_cdev)
return -1;
ret = alloc_chrdev_region(&ext_cdev->devno, 0,
LCD_MAX_DRV, EXT_CDEV_NAME);
if (ret) {
ret = 1;
goto ext_global_init_once_err;
}
ext_cdev->class = class_create(THIS_MODULE, "lcd_ext");
if (IS_ERR_OR_NULL(ext_cdev->class)) {
ret = 2;
goto ext_global_init_once_err_1;
}
return 0;
ext_global_init_once_err_1:
unregister_chrdev_region(ext_cdev->devno, LCD_MAX_DRV);
ext_global_init_once_err:
kfree(ext_cdev);
ext_cdev = NULL;
EXTERR("%s: failed: %d\n", __func__, ret);
return -1;
}
static void ext_global_remove_once(void)
{
if (ext_global_init_flag > 1) {
ext_global_init_flag--;
return;
}
ext_global_init_flag--;
if (!ext_cdev)
return;
class_destroy(ext_cdev->class);
unregister_chrdev_region(ext_cdev->devno, LCD_MAX_DRV);
kfree(ext_cdev);
ext_cdev = NULL;
}
/* **************************************** */
static int aml_lcd_extern_probe(struct platform_device *pdev)
{
struct lcd_extern_driver_s *edrv;
int index = 0;
int ret;
ext_global_init_once();
if (!pdev->dev.of_node)
return -1;
ret = of_property_read_u32(pdev->dev.of_node, "index", &index);
if (ret) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
EXTPR("%s: no index exist, default to 0\n", __func__);
index = 0;
}
if (index >= LCD_MAX_DRV) {
EXTERR("%s: invalid index %d\n", __func__, index);
return -1;
}
if (ext_drv_init_state & (1 << index)) {
EXTERR("%s: index %d driver already registered\n",
__func__, index);
return -1;
}
ext_drv_init_state |= (1 << index);
edrv = kzalloc(sizeof(*edrv), GFP_KERNEL);
if (!edrv)
return -ENOMEM;
edrv->index = index;
ext_driver[index] = edrv;
/* set drvdata */
platform_set_drvdata(pdev, edrv);
ext_cdev_add(edrv, &pdev->dev);
edrv->pdev = pdev;
edrv->dev_cnt = lcd_ext_dev_cnt[index];
INIT_WORK(&edrv->dev_probe_work, lcd_extern_dev_probe_work);
ret = lcd_extern_config_load(edrv);
if (ret)
goto lcd_extern_probe_err;
lcd_extern_debug_file_creat(edrv);
EXTPR("[%d]: probe OK, init_state:0x%x\n", index, ext_drv_init_state);
return 0;
lcd_extern_probe_err:
/* free drvdata */
platform_set_drvdata(pdev, NULL);
/* free drv */
kfree(edrv);
ext_driver[index] = NULL;
ext_drv_init_state &= ~(1 << index);
EXTPR("[%d]: %s: failed\n", index, __func__);
return -1;
}
static int aml_lcd_extern_remove(struct platform_device *pdev)
{
struct lcd_extern_driver_s *edrv = platform_get_drvdata(pdev);
int index, i;
if (!edrv)
return 0;
index = edrv->index;
lcd_extern_debug_file_remove(edrv);
ext_cdev_remove(edrv);
platform_set_drvdata(pdev, NULL);
for (i = 0; i < edrv->dev_cnt; i++)
lcd_extern_dev_free(edrv->dev[i]);
kfree(edrv);
ext_driver[index] = NULL;
ext_drv_init_state &= ~(1 << index);
ext_global_remove_once();
EXTPR("[%d]: %s, init_state:0x%x\n",
index, __func__, ext_drv_init_state);
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
},
};
int __init aml_lcd_extern_init(void)
{
int ret;
ret = platform_driver_register(&aml_lcd_extern_driver);
if (ret) {
EXTERR("driver register failed\n");
return -ENODEV;
}
return ret;
}
void __exit aml_lcd_extern_exit(void)
{
platform_driver_unregister(&aml_lcd_extern_driver);
}
//MODULE_AUTHOR("AMLOGIC");
//MODULE_DESCRIPTION("LCD extern driver");
//MODULE_LICENSE("GPL");