mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
bl: add bl_extern driver
PD#148374: bl: add bl_extern driver Change-Id: I8019ab4c2d55c37e14f7b266166408fd5a5a58e3 Signed-off-by: Weiming Liu <weiming.liu@amlogic.com>
This commit is contained in:
@@ -14034,3 +14034,11 @@ M: Alex Deng <alex.deng@amlogic.com>
|
||||
F: arch/arm64/boot/dts/amlogic/axg_s400_v03.dts
|
||||
F: arch/arm64/boot/dts/amlogic/axg_s420_v03.dts
|
||||
|
||||
AMLOGIC BL_EXTERN driver
|
||||
M: Weiming Liu <weiming.liu@amlogic.com>
|
||||
F: drivers/amlogic/media/vout/backlight/bl_extern/bl_extern.dts
|
||||
F: drivers/amlogic/media/vout/backlight/bl_extern/bl_extern.h
|
||||
F: drivers/amlogic/media/vout/backlight/bl_extern/mipi_lt070me05.c
|
||||
F: drivers/amlogic/media/vout/backlight/bl_extern/aml_bl_extern.dts
|
||||
F: drivers/amlogic/media/vout/backlight/bl_extern/mipi_LT070ME05.c
|
||||
F: drivers/amlogic/media/vout/backlight/bl_extern/pmu_aml1218.c
|
||||
@@ -138,7 +138,8 @@
|
||||
/* level_attr: max, min, mid, mid_mapping */
|
||||
bl_level_attr = <255 10 128 102>;
|
||||
|
||||
bl_ctrl_method = <1>; /* 1=pwm, 2=pwm_combo, 3=extern */
|
||||
/* 1=pwm, 2=pwm_combo, 3=ldim, 4=extern */
|
||||
bl_ctrl_method = <1>;
|
||||
/* power_attr:
|
||||
* en_gpio_index, on_value, off_value,
|
||||
* on_delay(ms), off_delay(ms)
|
||||
@@ -159,6 +160,22 @@
|
||||
*/
|
||||
bl_pwm_power = <0 0 10 10>;
|
||||
};
|
||||
backlight_1{
|
||||
index = <1>;
|
||||
bl_name = "bl_extern";
|
||||
bl_level_default_uboot_kernel = <100 100>;
|
||||
/* level_attr: max, min, mid, mid_mapping */
|
||||
bl_level_attr = <255 10 128 102>;
|
||||
|
||||
/* 1=pwm, 2=pwm_combo, 3=ldim, 4=extern */
|
||||
bl_ctrl_method = <4>;
|
||||
/* power_attr:
|
||||
* en_gpio_index, on_value, off_value,
|
||||
* on_delay(ms), off_delay(ms)
|
||||
*/
|
||||
bl_power_attr = <1 1 0 200 200>;
|
||||
bl_extern_index = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
bl_pwm_conf:bl_pwm_conf{
|
||||
@@ -167,5 +184,33 @@
|
||||
pwms = <&pwm_ab MESON_PWM_1 30040 0>;
|
||||
};
|
||||
};
|
||||
|
||||
bl_extern{
|
||||
compatible = "amlogic, bl_extern";
|
||||
dev_name = "bl_extern";
|
||||
status = "disabled";
|
||||
|
||||
extern_0{
|
||||
index = <0>;
|
||||
extern_name = "i2c_lp8556";
|
||||
|
||||
/*bl_extern_driver type: 0=i2c, 1=spi, 2=mipi*/
|
||||
type = <0>;
|
||||
i2c_address = <0x2c>; /** 7bit i2c address */
|
||||
i2c_bus = "i2c_bus_c";
|
||||
dim_max_min = <255 10>;
|
||||
};
|
||||
|
||||
extern_1{
|
||||
index = <1>;
|
||||
extern_name = "mipi_lt070me05";
|
||||
|
||||
/*bl_extern_driver type: 0=i2c, 1=spi, 2=mipi*/
|
||||
type = <2>;
|
||||
dim_max_min = <255 10>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
};/* end of panel */
|
||||
|
||||
|
||||
@@ -235,6 +235,9 @@ CONFIG_AMLOGIC_LCD_TABLET=y
|
||||
CONFIG_AMLOGIC_LCD_EXTERN=y
|
||||
CONFIG_AMLOGIC_LCD_EXTERN_MIPI_KD080D13=y
|
||||
CONFIG_AMLOGIC_BACKLIGHT=y
|
||||
CONFIG_AMLOGIC_BL_EXTERN=y
|
||||
CONFIG_AMLOGIC_BL_EXTERN_I2C_LP8556=y
|
||||
CONFIG_AMLOGIC_BL_EXTERN_MIPI_LT070ME05=y
|
||||
CONFIG_AMLOGIC_VOUT_SERVE=y
|
||||
CONFIG_AMLOGIC_MEDIA_FB=y
|
||||
CONFIG_AMLOGIC_MEDIA_FB_OSD_SYNC_FENCE=y
|
||||
|
||||
@@ -996,7 +996,7 @@ static void aml_bl_set_level(unsigned int level)
|
||||
bl_set_level_ldim(level);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_AML_BL_TABLET_EXTERN
|
||||
#ifdef CONFIG_AMLOGIC_BL_EXTERN
|
||||
case BL_CTRL_EXTERN:
|
||||
bl_set_level_extern(level);
|
||||
break;
|
||||
@@ -1524,8 +1524,18 @@ static int aml_bl_config_load_from_dts(struct bl_config_s *bconf,
|
||||
case BL_CTRL_LOCAL_DIMING:
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_AMLOGIC_BL_EXTERN
|
||||
case BL_CTRL_EXTERN:
|
||||
/* get bl_extern_index from dts */
|
||||
ret = of_property_read_u32(child, "bl_extern_index", &bl_para[0]);
|
||||
if (ret)
|
||||
BLERR("failed to get bl_extern_index\n");
|
||||
else {
|
||||
bconf->bl_extern_index = bl_para[0];
|
||||
BLPR("get bl_extern_index = %d\n", bconf->bl_extern_index);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1908,6 +1918,12 @@ static int aml_bl_config_load(struct bl_config_s *bconf,
|
||||
aml_ldim_probe(pdev);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_AMLOGIC_BL_EXTERN
|
||||
case BL_CTRL_EXTERN:
|
||||
aml_bl_extern_device_load(bconf->bl_extern_index);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -2139,6 +2155,10 @@ static ssize_t bl_status_read(struct class *class,
|
||||
#ifdef CONFIG_AMLOGIC_LOCAL_DIMMING
|
||||
struct aml_ldim_driver_s *ldim_drv = aml_ldim_get_driver();
|
||||
#endif
|
||||
#ifdef CONFIG_AMLOGIC_BL_EXTERN
|
||||
struct aml_bl_extern_driver_s *bl_extern = aml_bl_extern_get_driver();
|
||||
#endif
|
||||
|
||||
ssize_t len = 0;
|
||||
|
||||
len = sprintf(buf, "read backlight status:\n"
|
||||
@@ -2223,8 +2243,12 @@ static ssize_t bl_status_read(struct class *class,
|
||||
ldim_drv->config_print();
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_AMLOGIC_BL_EXTERN
|
||||
case BL_CTRL_EXTERN:
|
||||
if (bl_extern->config_print)
|
||||
bl_extern->config_print();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
len += sprintf(buf+len, "wrong backlight control method\n");
|
||||
break;
|
||||
|
||||
@@ -8,17 +8,6 @@ config AMLOGIC_BL_EXTERN
|
||||
such as i2c or mipi initial
|
||||
According to choose the corresponding bl_extern inside index bl_extern driver
|
||||
|
||||
config AMLOGIC_BL_EXTERN_PMU_AML1218
|
||||
bool "Amlogic backlight pmu aml1218"
|
||||
depends on AMLOGIC_BL_EXTERN
|
||||
default n
|
||||
help
|
||||
pmu aml1218 backlight controller support.
|
||||
Once the backlight power on, according to the timing requirements,
|
||||
through the aml1218 to write data to the backlight,
|
||||
make its initialization
|
||||
|
||||
|
||||
config AMLOGIC_BL_EXTERN_I2C_LP8556
|
||||
bool "Amlogic backlight i2c lp8556"
|
||||
depends on AMLOGIC_BL_EXTERN
|
||||
@@ -30,7 +19,7 @@ config AMLOGIC_BL_EXTERN_I2C_LP8556
|
||||
make its initialization
|
||||
|
||||
config AMLOGIC_BL_EXTERN_MIPI_LT070ME05
|
||||
bool "Amlogic backlight mipi LT070ME05"
|
||||
bool "Amlogic backlight mipi lt070me05"
|
||||
depends on AMLOGIC_BL_EXTERN
|
||||
default n
|
||||
help
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
obj-$(CONFIG_AMLOGIC_BL_EXTERN) += bl_extern.o
|
||||
obj-$(CONFIG_AMLOGIC_BL_EXTERN_I2C_LP8556) += i2c_lp8556.o
|
||||
obj-$(CONFIG_AMLOGIC_BL_EXTERN_PMU_AML1218) += pmu_aml1218.o
|
||||
obj-$(CONFIG_AMLOGIC_BL_EXTERN_MIPI_LT070ME05) += mipi_LT070ME05.o
|
||||
obj-$(CONFIG_AMLOGIC_BL_EXTERN_MIPI_LT070ME05) += mipi_lt070me05.o
|
||||
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* drivers/amlogic/media/vout/backlight/bl_extern/aml_bl_extern.dts
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
bl_extern_pmu_aml1218{
|
||||
compatible = "amlogic, bl_pmu_aml1218";
|
||||
dev_name ="bl_pmu_aml1218";
|
||||
status = "disabled"; /** "disabled" or "okay" */
|
||||
GPIODV_28-gpios = <&gpio GPIODV_28 0>;
|
||||
gpio_enable_on_off = "GPIODV_28","1","0";
|
||||
/* gpio("n"for none),
|
||||
* on/off(1=output high, 0=output low, 2=input)
|
||||
*/
|
||||
type = <2>; /** bl_extern_driver type: 0=i2c, 1=spi, 2=other */
|
||||
dim_max_min = <0x1 0x1b>;
|
||||
};
|
||||
|
||||
|
||||
bl_extern_i2c_lp8556{
|
||||
compatible = "amlogic, bl_i2c_lp8556";
|
||||
dev_name ="bl_i2c_lp8556";
|
||||
status = "disabled"; /** "disabled" or "okay" */
|
||||
GPIODV_28-gpios = <&gpio GPIODV_28 0>;
|
||||
gpio_enable_on_off = "GPIODV_28","1","0";
|
||||
/* gpio("n"for none),
|
||||
* on/off(1=output high, 0=output low, 2=input)
|
||||
*/
|
||||
type = <0>; /** bl_extern_driver type: 0=i2c, 1=spi, 2=other */
|
||||
i2c_address = <0x2c>; /** 7bit i2c address */
|
||||
i2c_bus = "i2c_bus_b";
|
||||
dim_max_min = <255 10>;
|
||||
};
|
||||
|
||||
|
||||
bl_extern_mipi_LT070ME05{
|
||||
compatible = "amlogic, bl_mipi_LT070ME05";
|
||||
dev_name ="bl_mipi_LT070ME056";
|
||||
status = "disabled"; /** "disabled" or "okay" */
|
||||
GPIODV_28-gpios = <&gpio GPIODV_28 0>;
|
||||
gpio_enable_on_off = "GPIODV_28","1","0";
|
||||
/* gpio("n"for none),
|
||||
* on/off(1=output high, 0=output low, 2=input)
|
||||
*/
|
||||
type = <2>; /** bl_extern_driver type: 0=i2c, 1=spi, 2=other */
|
||||
dim_max_min = <255 10>;
|
||||
};
|
||||
@@ -26,209 +26,373 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/amlogic/media/vout/lcd/aml_bl_extern.h>
|
||||
#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
|
||||
#include <linux/amlogic/media/vout/lcd/lcd_unifykey.h>
|
||||
#include <linux/amlogic/media/vout/lcd/aml_bl.h>
|
||||
#include "bl_extern.h"
|
||||
|
||||
#ifdef BL_EXT_DEBUG_INFO
|
||||
#define DBG_PRINT(...) pr_info(__VA_ARGS__)
|
||||
#else
|
||||
#define DBG_PRINT(...)
|
||||
#endif
|
||||
unsigned int bl_extern_brightness;
|
||||
static struct aml_bl_extern_driver_s bl_extern_driver;
|
||||
|
||||
static struct aml_bl_extern_driver_t bl_ext_driver = {
|
||||
.type = BL_EXTERN_MAX,
|
||||
.name = NULL,
|
||||
.power_on = NULL,
|
||||
.power_off = NULL,
|
||||
.set_level = NULL,
|
||||
|
||||
};
|
||||
|
||||
struct aml_bl_extern_driver_t *aml_bl_extern_get_driver(void)
|
||||
static int bl_extern_set_level(unsigned int level)
|
||||
{
|
||||
return &bl_ext_driver;
|
||||
}
|
||||
bl_extern_brightness = level & 0xff;
|
||||
|
||||
int bl_extern_driver_check(void)
|
||||
{
|
||||
struct aml_bl_extern_driver_t *bl_ext;
|
||||
if (bl_extern_driver.device_bri_update)
|
||||
bl_extern_driver.device_bri_update(level);
|
||||
|
||||
bl_ext = aml_bl_extern_get_driver();
|
||||
if (bl_ext) {
|
||||
if (bl_ext->type < BL_EXTERN_MAX) {
|
||||
pr_err("[warning]: bl_extern has already exist (%s)\n",
|
||||
bl_ext->name);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
pr_err("get bl_extern_driver failed\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_bl_extern_dt_data(struct device dev, struct bl_extern_config_t *pdata)
|
||||
static int bl_extern_power_on(void)
|
||||
{
|
||||
int ret;
|
||||
struct device_node *of_node = dev.of_node;
|
||||
u32 bl_para[2];
|
||||
const char *str;
|
||||
struct gpio_desc *bl_extern_gpio;
|
||||
int ret = 0;
|
||||
|
||||
ret = of_property_read_string(of_node, "dev_name",
|
||||
(const char **)&pdata->name);
|
||||
if (ret) {
|
||||
pdata->name = "aml_bl_extern";
|
||||
pr_err("warning: get dev_name failed\n");
|
||||
BLEX("%s\n", __func__);
|
||||
|
||||
if (bl_extern_driver.device_power_on)
|
||||
bl_extern_driver.device_power_on();
|
||||
|
||||
/* restore bl level */
|
||||
bl_extern_set_level(bl_extern_brightness);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static int bl_extern_power_off(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
BLEX("%s\n", __func__);
|
||||
|
||||
if (bl_extern_driver.device_power_off)
|
||||
bl_extern_driver.device_power_off();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct aml_bl_extern_driver_s bl_extern_driver = {
|
||||
.power_on = bl_extern_power_on,
|
||||
.power_off = bl_extern_power_off,
|
||||
.set_level = bl_extern_set_level,
|
||||
.config_print = NULL,
|
||||
.device_power_on = NULL,
|
||||
.device_power_off = NULL,
|
||||
.device_bri_update = NULL,
|
||||
.config = {
|
||||
.index = BL_EXTERN_INDEX_INVALID,
|
||||
.name = "none",
|
||||
.type = BL_EXTERN_MAX,
|
||||
.i2c_addr = 0xff,
|
||||
.i2c_bus = BL_EXTERN_I2C_BUS_MAX,
|
||||
.dim_min = 10,
|
||||
.dim_max = 255,
|
||||
},
|
||||
};
|
||||
|
||||
struct aml_bl_extern_driver_s *aml_bl_extern_get_driver(void)
|
||||
{
|
||||
return &bl_extern_driver;
|
||||
}
|
||||
|
||||
static unsigned char bl_extern_get_i2c_bus_str(const char *str)
|
||||
{
|
||||
unsigned char i2c_bus;
|
||||
|
||||
if (strncmp(str, "i2c_bus_ao", 10) == 0)
|
||||
i2c_bus = AML_I2C_MASTER_AO;
|
||||
else if (strncmp(str, "i2c_bus_a", 9) == 0)
|
||||
i2c_bus = AML_I2C_MASTER_A;
|
||||
else if (strncmp(str, "i2c_bus_b", 9) == 0)
|
||||
i2c_bus = AML_I2C_MASTER_B;
|
||||
else if (strncmp(str, "i2c_bus_c", 9) == 0)
|
||||
i2c_bus = AML_I2C_MASTER_C;
|
||||
else if (strncmp(str, "i2c_bus_d", 9) == 0)
|
||||
i2c_bus = AML_I2C_MASTER_D;
|
||||
else {
|
||||
i2c_bus = AML_I2C_MASTER_A;
|
||||
BLEXERR("invalid i2c_bus: %s\n", str);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(of_node, "type", &pdata->type);
|
||||
if (ret) {
|
||||
pdata->type = BL_EXTERN_MAX;
|
||||
pr_err("%s warning: get type failed, exit\n", pdata->name);
|
||||
return -1;
|
||||
}
|
||||
pdata->gpio_used = 0;
|
||||
ret = of_property_read_string_index(of_node,
|
||||
"gpio_enable_on_off", 0, &str);
|
||||
if (ret) {
|
||||
pr_warn("%s warning: get gpio_enable failed\n", pdata->name);
|
||||
} else {
|
||||
if (strncmp(str, "G", 1) == 0) {
|
||||
pdata->gpio_used = 1;
|
||||
bl_extern_gpio = gpiod_get(&dev, str);
|
||||
if (bl_extern_gpio) {
|
||||
pr_warn("%s warning:failed to alloc gpio (%s)\n",
|
||||
pdata->name, str);
|
||||
}
|
||||
pdata->gpio = bl_extern_gpio;
|
||||
}
|
||||
DBG_PRINT("%s: gpio_enable %s\n",
|
||||
pdata->name, ((pdata->gpio_used) ? str:"none"));
|
||||
}
|
||||
ret = of_property_read_string_index(of_node,
|
||||
"gpio_enable_on_off", 1, &str);
|
||||
if (ret) {
|
||||
pr_warn("%s warning: get gpio_enable_on failed\n", pdata->name);
|
||||
} else {
|
||||
if (strncmp(str, "2", 1) == 0)
|
||||
pdata->gpio_on = LCD_POWER_GPIO_INPUT;
|
||||
else if (strncmp(str, "0", 1) == 0)
|
||||
pdata->gpio_on = LCD_POWER_GPIO_OUTPUT_LOW;
|
||||
else
|
||||
pdata->gpio_on = LCD_POWER_GPIO_OUTPUT_HIGH;
|
||||
}
|
||||
ret = of_property_read_string_index(of_node,
|
||||
"gpio_enable_on_off", 2, &str);
|
||||
if (ret) {
|
||||
pr_warn("%s warning:get gpio_enable_off failed\n", pdata->name);
|
||||
} else {
|
||||
if (strncmp(str, "2", 1) == 0)
|
||||
pdata->gpio_off = LCD_POWER_GPIO_INPUT;
|
||||
else if (strncmp(str, "1", 1) == 0)
|
||||
pdata->gpio_off = LCD_POWER_GPIO_OUTPUT_HIGH;
|
||||
else
|
||||
pdata->gpio_off = LCD_POWER_GPIO_OUTPUT_LOW;
|
||||
}
|
||||
DBG_PRINT("%s: gpio_on = %d,", pdata->name, pdata->gpio_on);
|
||||
DBG_PRINT("gpio_off = %d\n", pdata->gpio_off);
|
||||
switch (pdata->type) {
|
||||
return i2c_bus;
|
||||
}
|
||||
|
||||
static void bl_extern_config_print(void)
|
||||
{
|
||||
struct aml_bl_extern_driver_s *bl_extern = aml_bl_extern_get_driver();
|
||||
|
||||
BLEX("%s:\n", __func__);
|
||||
switch (bl_extern->config.type) {
|
||||
case BL_EXTERN_I2C:
|
||||
ret = of_property_read_u32(of_node, "i2c_address",
|
||||
&pdata->i2c_addr);
|
||||
if (ret) {
|
||||
pr_warn("%s warning: get i2c_address failed\n",
|
||||
pdata->name);
|
||||
pdata->i2c_addr = 0;
|
||||
}
|
||||
DBG_PRINT("%s: i2c_address=", pdata->name);
|
||||
DBG_PRINT("0x%02x\n", pdata->i2c_addr);
|
||||
ret = of_property_read_string(of_node, "i2c_bus", &str);
|
||||
if (ret) {
|
||||
pr_warn("%s warning: get i2c_bus failed,", pdata->name);
|
||||
pr_warn("use default i2c bus\n");
|
||||
pdata->i2c_bus = AML_I2C_MASTER_A;
|
||||
} else {
|
||||
if (strncmp(str, "i2c_bus_a", 9) == 0)
|
||||
pdata->i2c_bus = AML_I2C_MASTER_A;
|
||||
else if (strncmp(str, "i2c_bus_b", 9) == 0)
|
||||
pdata->i2c_bus = AML_I2C_MASTER_B;
|
||||
else if (strncmp(str, "i2c_bus_c", 9) == 0)
|
||||
pdata->i2c_bus = AML_I2C_MASTER_C;
|
||||
else if (strncmp(str, "i2c_bus_d", 9) == 0)
|
||||
pdata->i2c_bus = AML_I2C_MASTER_D;
|
||||
else if (strncmp(str, "i2c_bus_ao", 10) == 0)
|
||||
pdata->i2c_bus = AML_I2C_MASTER_AO;
|
||||
else
|
||||
pdata->i2c_bus = AML_I2C_MASTER_A;
|
||||
}
|
||||
DBG_PRINT("%s: i2c_bus=%s[%d]\n", pdata->name,
|
||||
str, pdata->i2c_bus);
|
||||
pr_info("index: %d\n"
|
||||
"name: %s\n"
|
||||
"type: i2c(%d)\n"
|
||||
"i2c_addr: 0x%02x\n"
|
||||
"i2c_bus: %d\n"
|
||||
"dim_min: %d\n"
|
||||
"dim_max: %d\n",
|
||||
bl_extern->config.index,
|
||||
bl_extern->config.name,
|
||||
bl_extern->config.type,
|
||||
bl_extern->config.i2c_addr,
|
||||
bl_extern->config.i2c_bus,
|
||||
bl_extern->config.dim_min,
|
||||
bl_extern->config.dim_max);
|
||||
break;
|
||||
case BL_EXTERN_SPI:
|
||||
ret = of_property_read_string(of_node, "gpio_spi_cs", &str);
|
||||
if (ret) {
|
||||
pr_warn("%s warning: get spi gpio_spi_cs failed\n",
|
||||
pdata->name);
|
||||
pdata->spi_cs = NULL;
|
||||
} else {
|
||||
bl_extern_gpio = gpiod_get(&dev, str);
|
||||
if (bl_extern_gpio) {
|
||||
pdata->spi_cs = bl_extern_gpio;
|
||||
DBG_PRINT("spi_cs gpio = %s\n", str);
|
||||
} else {
|
||||
pr_warn("%s warning:failed to alloc gpio (%s)\n",
|
||||
pdata->name, str);
|
||||
pdata->spi_cs = NULL;
|
||||
}
|
||||
}
|
||||
ret = of_property_read_string(of_node, "gpio_spi_clk", &str);
|
||||
if (ret) {
|
||||
pr_warn("%s warning: get spi gpio_spi_clk failed\n",
|
||||
pdata->name);
|
||||
pdata->spi_clk = NULL;
|
||||
} else {
|
||||
bl_extern_gpio = gpiod_get(&dev, str);
|
||||
if (bl_extern_gpio) {
|
||||
pdata->spi_clk = bl_extern_gpio;
|
||||
DBG_PRINT("spi_cs gpio = %s\n", str);
|
||||
} else {
|
||||
pdata->spi_clk = NULL;
|
||||
pr_warn("%s warning:failed to alloc gpio (%s)\n",
|
||||
pdata->name, str);
|
||||
}
|
||||
}
|
||||
ret = of_property_read_string(of_node, "gpio_spi_data", &str);
|
||||
if (ret) {
|
||||
pr_warn("%s warning: get spi gpio_spi_data failed\n",
|
||||
pdata->name);
|
||||
pdata->spi_data = NULL;
|
||||
} else {
|
||||
bl_extern_gpio = gpiod_get(&dev, str);
|
||||
if (bl_extern_gpio) {
|
||||
pdata->spi_data = bl_extern_gpio;
|
||||
DBG_PRINT("spi_cs gpio = %s\n", str);
|
||||
} else {
|
||||
pdata->spi_data = NULL;
|
||||
pr_warn("%s warning:failed to alloc gpio (%s)\n",
|
||||
pdata->name, str);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BL_EXTERN_OTHER:
|
||||
case BL_EXTERN_MIPI:
|
||||
pr_info("index: %d\n"
|
||||
"name: %s\n"
|
||||
"type: mipi(%d)\n"
|
||||
"dim_min: %d\n"
|
||||
"dim_max: %d\n",
|
||||
bl_extern->config.index,
|
||||
bl_extern->config.name,
|
||||
bl_extern->config.type,
|
||||
bl_extern->config.dim_min,
|
||||
bl_extern->config.dim_max);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ret = of_property_read_u32_array(of_node, "dim_max_min",
|
||||
&bl_para[0], 2);
|
||||
if (ret) {
|
||||
pr_warn("%s warning: get dim_max_min failed\n", pdata->name);
|
||||
pdata->dim_max = 0;
|
||||
pdata->dim_min = 0;
|
||||
} else {
|
||||
pdata->dim_max = bl_para[0];
|
||||
pdata->dim_min = bl_para[1];
|
||||
}
|
||||
|
||||
static int bl_extern_config_from_dts(struct device_node *np, int index)
|
||||
{
|
||||
char be_propname[20];
|
||||
struct device_node *child;
|
||||
const char *str;
|
||||
unsigned int temp[5], val;
|
||||
int ret = 0;
|
||||
struct aml_bl_extern_driver_s *bl_extern = aml_bl_extern_get_driver();
|
||||
|
||||
/* get device config */
|
||||
sprintf(be_propname, "extern_%d", index);
|
||||
BLEX("load: %s\n", be_propname);
|
||||
child = of_get_child_by_name(np, be_propname);
|
||||
if (child == NULL) {
|
||||
BLEXERR("failed to get %s\n", be_propname);
|
||||
return -1;
|
||||
}
|
||||
ret = of_property_read_u32_array(child, "index", &temp[0], 1);
|
||||
if (ret)
|
||||
BLEXERR("failed to get index, exit\n");
|
||||
else {
|
||||
if (temp[0] == index)
|
||||
bl_extern->config.index = temp[0];
|
||||
else {
|
||||
BLEXERR("index not match, exit\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ret = of_property_read_string(child, "extern_name", &str);
|
||||
if (ret) {
|
||||
BLEXERR("failed to get bl_extern_name\n");
|
||||
str = "bl_extern_name";
|
||||
}
|
||||
strcpy(bl_extern->config.name, str);
|
||||
|
||||
ret = of_property_read_u32(child, "type", &val);
|
||||
if (ret) {
|
||||
BLEXERR("failed to get type\n");
|
||||
} else {
|
||||
bl_extern->config.type = val;
|
||||
BLEX("type: %d\n", bl_extern->config.type);
|
||||
}
|
||||
if (bl_extern->config.type >= BL_EXTERN_MAX) {
|
||||
BLEXERR("type num is out of support\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(child, "dim_max_min", &temp[0], 2);
|
||||
if (ret) {
|
||||
BLEXERR("failed to get dim_max_min\n");
|
||||
bl_extern->config.dim_max = 255;
|
||||
bl_extern->config.dim_min = 10;
|
||||
} else {
|
||||
bl_extern->config.dim_max = temp[0];
|
||||
bl_extern->config.dim_min = temp[1];
|
||||
}
|
||||
|
||||
switch (bl_extern->config.type) {
|
||||
case BL_EXTERN_I2C:
|
||||
ret = of_property_read_u32(child, "i2c_address", &val);
|
||||
if (ret) {
|
||||
BLEXERR("failed to get i2c_address\n");
|
||||
} else {
|
||||
bl_extern->config.i2c_addr = (unsigned char)val;
|
||||
}
|
||||
if (lcd_debug_print_flag) {
|
||||
BLEX("%s i2c_address=0x%02x\n",
|
||||
bl_extern->config.name,
|
||||
bl_extern->config.i2c_addr);
|
||||
}
|
||||
|
||||
ret = of_property_read_string(child, "i2c_bus", &str);
|
||||
if (ret) {
|
||||
BLEXERR("failed to get i2c_bus\n");
|
||||
} else {
|
||||
bl_extern->config.i2c_bus =
|
||||
bl_extern_get_i2c_bus_str(str);
|
||||
}
|
||||
if (lcd_debug_print_flag) {
|
||||
BLEX("%s i2c_bus=%s[%d]\n",
|
||||
bl_extern->config.name, str,
|
||||
bl_extern->config.i2c_bus);
|
||||
}
|
||||
break;
|
||||
case BL_EXTERN_SPI:
|
||||
break;
|
||||
case BL_EXTERN_MIPI:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
DBG_PRINT("%s dim_min = %d, dim_max = %d\n", pdata->name,
|
||||
pdata->dim_min, pdata->dim_max);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bl_extern_add_driver(void)
|
||||
{
|
||||
int ret = 0;
|
||||
struct bl_extern_config_s *extconf = &bl_extern_driver.config;
|
||||
|
||||
if (strcmp(extconf->name, "i2c_lp8556") == 0) {
|
||||
#ifdef CONFIG_AMLOGIC_BL_EXTERN_I2C_LP8556
|
||||
ret = i2c_lp8556_probe();
|
||||
#endif
|
||||
goto bl_extern_add_driver_next;
|
||||
} else if (strcmp(extconf->name, "mipi_lt070me05") == 0) {
|
||||
#ifdef CONFIG_AMLOGIC_BL_EXTERN_MIPI_LT070ME05
|
||||
ret = mipi_lt070me05_probe();
|
||||
#endif
|
||||
goto bl_extern_add_driver_next;
|
||||
} else {
|
||||
BLEXERR("invalid device name: %s\n", extconf->name);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
bl_extern_add_driver_next:
|
||||
if (ret) {
|
||||
BLEXERR("add device driver failed %s(%d)\n",
|
||||
extconf->name, extconf->index);
|
||||
} else {
|
||||
BLEX("add device driver %s(%d)\n",
|
||||
extconf->name, extconf->index);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bl_extern_remove_driver(void)
|
||||
{
|
||||
int ret = 0;
|
||||
struct bl_extern_config_s *extconf = &bl_extern_driver.config;
|
||||
|
||||
if (strcmp(extconf->name, "i2c_lp8556") == 0) {
|
||||
#ifdef CONFIG_AMLOGIC_BL_EXTERN_I2C_LP8556
|
||||
ret = i2c_lp8556_remove();
|
||||
#endif
|
||||
goto bl_extern_remove_driver_next;
|
||||
} else if (strcmp(extconf->name, "mipi_lt070me05") == 0) {
|
||||
#ifdef CONFIG_AMLOGIC_BL_EXTERN_MIPI_LT070ME05
|
||||
ret = mipi_lt070me05_remove();
|
||||
#endif
|
||||
goto bl_extern_remove_driver_next;
|
||||
} else {
|
||||
BLEXERR("invalid device name: %s\n", extconf->name);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
bl_extern_remove_driver_next:
|
||||
if (ret) {
|
||||
BLEXERR("remove device driver failed %s(%d)\n",
|
||||
extconf->name, extconf->index);
|
||||
} else {
|
||||
BLEX("remove device driver %s(%d)\n",
|
||||
extconf->name, extconf->index);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int aml_bl_extern_device_load(int index)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
bl_extern_config_from_dts(bl_extern_driver.dev->of_node,
|
||||
index);
|
||||
bl_extern_add_driver();
|
||||
bl_extern_driver.config_print = bl_extern_config_print;
|
||||
BLEX("%s OK\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int aml_bl_extern_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
bl_extern_driver.dev = &pdev->dev;
|
||||
BLEX("%s OK\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aml_bl_extern_remove(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
bl_extern_remove_driver();
|
||||
|
||||
BLEX("%s OK\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id aml_bl_extern_dt_match[] = {
|
||||
{
|
||||
.compatible = "amlogic, bl_extern",
|
||||
},
|
||||
{},
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct platform_driver aml_bl_extern_driver = {
|
||||
.probe = aml_bl_extern_probe,
|
||||
.remove = aml_bl_extern_remove,
|
||||
.driver = {
|
||||
.name = "bl_extern",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_OF
|
||||
.of_match_table = aml_bl_extern_dt_match,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
static int __init aml_bl_extern_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (lcd_debug_print_flag)
|
||||
BLEX("%s\n", __func__);
|
||||
|
||||
ret = platform_driver_register(&aml_bl_extern_driver);
|
||||
if (ret) {
|
||||
BLEXERR("driver register failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit aml_bl_extern_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&aml_bl_extern_driver);
|
||||
}
|
||||
|
||||
module_init(aml_bl_extern_init);
|
||||
module_exit(aml_bl_extern_exit);
|
||||
|
||||
MODULE_AUTHOR("AMLOGIC");
|
||||
MODULE_DESCRIPTION("bl extern driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
40
drivers/amlogic/media/vout/backlight/bl_extern/bl_extern.dts
Normal file
40
drivers/amlogic/media/vout/backlight/bl_extern/bl_extern.dts
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* drivers/amlogic/media/vout/backlight/bl_extern/bl_extern.dts
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
bl_extern{
|
||||
compatible = "amlogic, bl_extern";
|
||||
dev_name = "bl_extern";
|
||||
status = "okay";
|
||||
|
||||
extern_0{
|
||||
index = <0>;
|
||||
extern_name = "i2c_lp8556";
|
||||
type = <1>; /** bl_extern_driver type: 1=i2c, 2=spi, 3=mipi */
|
||||
i2c_address = <0x2c>; /** 7bit i2c address */
|
||||
i2c_bus = "i2c_bus_d";
|
||||
dim_max_min = <255 10>;
|
||||
};
|
||||
|
||||
extern_1{
|
||||
index = <1>;
|
||||
extern_name = "mipi_lt070me05";
|
||||
type = <3>; /** bl_extern_driver type: 1=i2c, 2=spi, 3=mipi */
|
||||
dim_max_min = <255 10>;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
43
drivers/amlogic/media/vout/backlight/bl_extern/bl_extern.h
Normal file
43
drivers/amlogic/media/vout/backlight/bl_extern/bl_extern.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* drivers/amlogic/media/vout/backlight/bl_extern/bl_extern.h
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _BL_EXTERN_H_
|
||||
#define _BL_EXTERN_H_
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/amlogic/aml_gpio_consumer.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
|
||||
|
||||
#define BLEX(fmt, args...) pr_info("bl extern: "fmt"", ## args)
|
||||
#define BLEXERR(fmt, args...) pr_err("bl extern: error: "fmt"", ## args)
|
||||
|
||||
#define BL_EXTERN_DRIVER "bl_extern"
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_BL_EXTERN_I2C_LP8556
|
||||
extern int i2c_lp8556_probe(void);
|
||||
extern int i2c_lp8556_remove(void);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_BL_EXTERN_MIPI_LT070ME05
|
||||
extern int mipi_lt070me05_probe(void);
|
||||
extern int mipi_lt070me05_remove(void);
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,53 +16,49 @@
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/amlogic/i2c-amlogic.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/amlogic/media/vout/lcd/aml_bl_extern.h>
|
||||
|
||||
static struct bl_extern_config_t *bl_ext_config;
|
||||
|
||||
static struct i2c_client *aml_lp8556_i2c_client;
|
||||
#include <linux/amlogic/media/vout/lcd/aml_bl.h>
|
||||
#include "bl_extern.h"
|
||||
|
||||
|
||||
#ifdef BL_EXT_DEBUG_INFO
|
||||
#define DBG_PRINT(...) pr_info(__VA_ARGS__)
|
||||
#else
|
||||
#define DBG_PRINT(...)
|
||||
#endif
|
||||
#define BL_EXTERN_INDEX 0
|
||||
#define BL_EXTERN_NAME "i2c_lp8556"
|
||||
#define BL_EXTERN_TYPE BL_EXTERN_I2C
|
||||
|
||||
#define BL_EXTERN_NAME "bl_i2c_lp8556"
|
||||
static unsigned int bl_status = 1;
|
||||
static unsigned int bl_level;
|
||||
#define BL_EXTERN_CMD_SIZE 4
|
||||
|
||||
static unsigned char i2c_init_table[][2] = {
|
||||
{0xa1, 0x76}, /* hight bit(8~11)(0~0X66e set backlight) */
|
||||
{0xa0, 0x66}, /* low bit(0~7) 20mA */
|
||||
{0x16, 0x1F}, /* 5channel LED enable 0x1F */
|
||||
{0xa9, 0xA0}, /* VBOOST_MAX 25V */
|
||||
{0x9e, 0x12},
|
||||
{0xa2, 0x23},
|
||||
{0x01, 0x05}, /* 0x03 pwm+I2c set brightness,0x5 I2c set brightness */
|
||||
{0xff, 0xff}, /* ending flag */
|
||||
static unsigned int bl_status;
|
||||
static struct i2c_client *i2c_lp8556_client;
|
||||
|
||||
static unsigned char init_on_table[] = {
|
||||
0x00, 0xa1, 0x76, 0x00,//hight bit(8~11)(0~0X66e set backlight)
|
||||
0x00, 0xa0, 0x66, 0x00,//low bit(0~7) 20mA
|
||||
0x00, 0x16, 0x1F, 0x00,// 5channel LED enable 0x1F
|
||||
0x00, 0xa9, 0xA0, 0x00,//VBOOST_MAX 25V
|
||||
0x00, 0x9e, 0x12, 0x00,
|
||||
0x00, 0xa2, 0x23, 0x00,
|
||||
/*0x03 pwm+I2c set brightness,0x5 I2c set brightness*/
|
||||
0x00, 0x01, 0x05, 0x00,
|
||||
0xff, 0x00, 0x00, 0x00, //ending
|
||||
};
|
||||
|
||||
static int aml_i2c_write(struct i2c_client *i2client,
|
||||
unsigned char *buff, unsigned int len)
|
||||
static unsigned char init_off_table[] = {
|
||||
0xff, 0x00, 0x00, 0x00, //ending
|
||||
};
|
||||
|
||||
static int i2c_lp8556_write(struct i2c_client *i2client,
|
||||
unsigned char *buff, unsigned int len)
|
||||
{
|
||||
int res = 0;
|
||||
int ret = 0;
|
||||
struct i2c_msg msg[] = {
|
||||
{
|
||||
.addr = i2client->addr,
|
||||
@@ -72,61 +68,93 @@ static int aml_i2c_write(struct i2c_client *i2client,
|
||||
}
|
||||
};
|
||||
|
||||
res = i2c_transfer(i2client->adapter, msg, 1);
|
||||
if (res < 0)
|
||||
pr_err("%s: i2c transfer failed [addr 0x%02x]\n",
|
||||
__func__, i2client->addr);
|
||||
ret = i2c_transfer(i2client->adapter, msg, 1);
|
||||
if (ret < 0)
|
||||
BLEXERR("i2c write failed [addr 0x%02x]\n", i2client->addr);
|
||||
|
||||
return res;
|
||||
return ret;
|
||||
}
|
||||
#if 0
|
||||
static int aml_i2c_read(struct i2c_client *i2client,
|
||||
unsigned char *buff, unsigned int len)
|
||||
|
||||
static int i2c_lp8556_power_cmd(unsigned char *init_table)
|
||||
{
|
||||
int res = 0;
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = i2client->addr,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
.buf = buff,
|
||||
},
|
||||
{
|
||||
.addr = i2client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = len,
|
||||
.buf = buff,
|
||||
}
|
||||
};
|
||||
res = i2c_transfer(i2client->adapter, msgs, 2);
|
||||
if (res < 0)
|
||||
pr_err("%s: i2c transfer failed [addr 0x%02x]\n",
|
||||
__func__, i2client->addr);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
int i = 0, len;
|
||||
int ret = 0;
|
||||
struct aml_bl_extern_driver_s *bl_extern = aml_bl_extern_get_driver();
|
||||
|
||||
static int bl_extern_set_level(unsigned int level)
|
||||
len = BL_EXTERN_CMD_SIZE;
|
||||
while (i <= BL_EXTERN_INIT_TABLE_MAX) {
|
||||
if (init_table[i] == BL_EXTERN_INIT_END) {
|
||||
break;
|
||||
} else if (init_table[i] == BL_EXTERN_INIT_NONE) {
|
||||
/* do nothing, only for delay */
|
||||
} else if (init_table[i] == BL_EXTERN_INIT_CMD) {
|
||||
ret = i2c_lp8556_write(i2c_lp8556_client,
|
||||
&init_table[i+1], (len-2));
|
||||
} else {
|
||||
BLEXERR("%s(%d: %s): power_type %d is invalid\n",
|
||||
__func__, bl_extern->config.index,
|
||||
bl_extern->config.name, bl_extern->config.type);
|
||||
}
|
||||
if (init_table[i+len-1] > 0)
|
||||
mdelay(init_table[i+len-1]);
|
||||
i += len;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_lp8556_power_ctrl(int flag)
|
||||
{
|
||||
int ret = 0;
|
||||
struct aml_bl_extern_driver_s *bl_extern = aml_bl_extern_get_driver();
|
||||
|
||||
if (flag)
|
||||
ret = i2c_lp8556_power_cmd(init_on_table);
|
||||
else
|
||||
ret = i2c_lp8556_power_cmd(init_off_table);
|
||||
|
||||
BLEX("%s(%d: %s): %d\n",
|
||||
__func__, bl_extern->config.index,
|
||||
bl_extern->config.name, flag);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_lp8556_power_on(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
bl_status = 1;
|
||||
ret = i2c_lp8556_power_ctrl(1);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int i2c_lp8556_power_off(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
bl_status = 0;
|
||||
ret = i2c_lp8556_power_ctrl(0);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int i2c_lp8556_set_level(unsigned int level)
|
||||
{
|
||||
unsigned char tData[3];
|
||||
int ret = 0;
|
||||
struct aml_bl_drv_s *bl_drv = aml_bl_get_driver();
|
||||
struct aml_bl_extern_driver_s *bl_extern = aml_bl_extern_get_driver();
|
||||
unsigned int level_max, level_min;
|
||||
unsigned int dim_max, dim_min;
|
||||
|
||||
bl_level = level;
|
||||
|
||||
if (bl_ext_config == NULL) {
|
||||
pr_err("no %s driver\n", BL_EXTERN_NAME);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bl_drv == NULL)
|
||||
return -1;
|
||||
level_max = bl_drv->bconf->level_max;
|
||||
level_min = bl_drv->bconf->level_min;
|
||||
dim_max = bl_ext_config->dim_max;
|
||||
dim_min = bl_ext_config->dim_min;
|
||||
dim_max = bl_extern->config.dim_max;
|
||||
dim_min = bl_extern->config.dim_min;
|
||||
level = dim_min - ((level - level_min) * (dim_min - dim_max)) /
|
||||
(level_max - level_min);
|
||||
level &= 0xff;
|
||||
@@ -134,237 +162,119 @@ static int bl_extern_set_level(unsigned int level)
|
||||
if (bl_status) {
|
||||
tData[0] = 0x0;
|
||||
tData[1] = level;
|
||||
ret = aml_i2c_write(aml_lp8556_i2c_client, tData, 2);
|
||||
ret = i2c_lp8556_write(i2c_lp8556_client, tData, 2);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bl_extern_power_on(void)
|
||||
static int i2c_lp8556_update(void)
|
||||
{
|
||||
unsigned char tData[3];
|
||||
int i = 0, ending_flag = 0;
|
||||
int ret = 0;
|
||||
struct aml_bl_extern_driver_s *bl_extern = aml_bl_extern_get_driver();
|
||||
|
||||
if (bl_ext_config->gpio) {
|
||||
if (bl_ext_config->gpio_on == 2) {
|
||||
bl_gpio_input(bl_ext_config->gpio);
|
||||
} else {
|
||||
bl_gpio_output(bl_ext_config->gpio,
|
||||
bl_ext_config->gpio_on);
|
||||
}
|
||||
if (bl_extern == NULL) {
|
||||
BLEXERR("%s driver is null\n", BL_EXTERN_NAME);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (ending_flag == 0) {
|
||||
if (i2c_init_table[i][0] == 0xff) {
|
||||
if (i2c_init_table[i][1] == 0xff)
|
||||
ending_flag = 1;
|
||||
else
|
||||
mdelay(i2c_init_table[i][1]);
|
||||
} else {
|
||||
tData[0] = i2c_init_table[i][0];
|
||||
tData[1] = i2c_init_table[i][1];
|
||||
ret = aml_i2c_write(aml_lp8556_i2c_client, tData, 2);
|
||||
}
|
||||
i++;
|
||||
if (bl_extern->config.type == BL_EXTERN_MAX) {
|
||||
bl_extern->config.index = BL_EXTERN_INDEX;
|
||||
bl_extern->config.type = BL_EXTERN_TYPE;
|
||||
strcpy(bl_extern->config.name, BL_EXTERN_NAME);
|
||||
}
|
||||
bl_status = 1;
|
||||
bl_extern_set_level(bl_level);
|
||||
BLPR("%s\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bl_extern_power_off(void)
|
||||
{
|
||||
bl_status = 0;
|
||||
if (bl_ext_config->gpio) {
|
||||
if (bl_ext_config->gpio_off == 2) {
|
||||
bl_gpio_input(bl_ext_config->gpio);
|
||||
} else {
|
||||
bl_gpio_output(bl_ext_config->gpio,
|
||||
bl_ext_config->gpio_off);
|
||||
}
|
||||
}
|
||||
BLPR("%s\n", __func__);
|
||||
bl_extern->device_power_on = i2c_lp8556_power_on;
|
||||
bl_extern->device_power_off = i2c_lp8556_power_off;
|
||||
bl_extern->device_bri_update = i2c_lp8556_set_level;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_bl_extern_config(struct device dev,
|
||||
struct bl_extern_config_t *bl_ext_cfg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct aml_bl_extern_driver_t *bl_ext;
|
||||
|
||||
ret = get_bl_extern_dt_data(dev, bl_ext_cfg);
|
||||
if (ret) {
|
||||
BLPR("error %s: failed to get dt data\n", BL_EXTERN_NAME);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (bl_ext_cfg->dim_min > 0xff)
|
||||
bl_ext_cfg->dim_min = 0xff;
|
||||
if (bl_ext_cfg->dim_max > 0xff)
|
||||
bl_ext_cfg->dim_max = 0xff;
|
||||
|
||||
bl_ext = aml_bl_extern_get_driver();
|
||||
if (bl_ext) {
|
||||
bl_ext->type = bl_ext_cfg->type;
|
||||
bl_ext->name = bl_ext_cfg->name;
|
||||
bl_ext->power_on = bl_extern_power_on;
|
||||
bl_ext->power_off = bl_extern_power_off;
|
||||
bl_ext->set_level = bl_extern_set_level;
|
||||
} else {
|
||||
BLPR("error %s get bl_extern_driver failed\n",
|
||||
bl_ext_cfg->name);
|
||||
ret = -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aml_lp8556_i2c_probe(struct i2c_client *client,
|
||||
static int bl_extern_i2c_lp8556_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
BLPR("error %s: functionality check failed\n",
|
||||
__func__);
|
||||
else
|
||||
aml_lp8556_i2c_client = client;
|
||||
BLPR("%s OK\n", __func__);
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
BLEXERR("%s: functionality check failed\n", __func__);
|
||||
else
|
||||
i2c_lp8556_client = client;
|
||||
|
||||
BLEX("%s OK\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aml_lp8556_i2c_remove(struct i2c_client *client)
|
||||
static int bl_extern_i2c_lp8556_remove(struct i2c_client *client)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id aml_lp8556_i2c_id[] = {
|
||||
static const struct i2c_device_id bl_extern_i2c_lp8556_id[] = {
|
||||
{BL_EXTERN_NAME, 0},
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct i2c_driver aml_lp8556_i2c_driver = {
|
||||
.probe = aml_lp8556_i2c_probe,
|
||||
.remove = aml_lp8556_i2c_remove,
|
||||
.id_table = aml_lp8556_i2c_id,
|
||||
static struct i2c_driver i2c_lp8556_driver = {
|
||||
.probe = bl_extern_i2c_lp8556_probe,
|
||||
.remove = bl_extern_i2c_lp8556_remove,
|
||||
.id_table = bl_extern_i2c_lp8556_id,
|
||||
.driver = {
|
||||
.name = BL_EXTERN_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int aml_lp8556_probe(struct platform_device *pdev)
|
||||
int i2c_lp8556_probe(void)
|
||||
{
|
||||
int ret = 0;
|
||||
struct i2c_board_info i2c_info;
|
||||
struct i2c_adapter *adapter;
|
||||
struct i2c_client *i2c_client;
|
||||
int ret = 0;
|
||||
|
||||
if (bl_extern_driver_check())
|
||||
return -1;
|
||||
if (bl_ext_config == NULL)
|
||||
bl_ext_config = kzalloc(sizeof(*bl_ext_config), GFP_KERNEL);
|
||||
if (bl_ext_config == NULL) {
|
||||
BLPR("error %s probe: failed to alloc data\n", BL_EXTERN_NAME);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pdev->dev.platform_data = bl_ext_config;
|
||||
ret = get_bl_extern_config(pdev->dev, bl_ext_config);
|
||||
if (ret)
|
||||
goto bl_extern_probe_failed;
|
||||
struct aml_bl_extern_driver_s *bl_extern = aml_bl_extern_get_driver();
|
||||
|
||||
memset(&i2c_info, 0, sizeof(i2c_info));
|
||||
adapter = i2c_get_adapter(bl_ext_config->i2c_bus);
|
||||
adapter = i2c_get_adapter(bl_extern->config.i2c_bus);
|
||||
if (!adapter) {
|
||||
BLPR("error %s£ºfailed get i2c adapter\n", BL_EXTERN_NAME);
|
||||
goto bl_extern_probe_failed;
|
||||
BLEXERR("%s failed to get i2c adapter\n",
|
||||
bl_extern->config.name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(i2c_info.type, bl_ext_config->name, I2C_NAME_SIZE);
|
||||
i2c_info.addr = bl_ext_config->i2c_addr;
|
||||
i2c_info.platform_data = bl_ext_config;
|
||||
strncpy(i2c_info.type, bl_extern->config.name, I2C_NAME_SIZE);
|
||||
i2c_info.addr = bl_extern->config.i2c_addr;
|
||||
i2c_info.platform_data = &bl_extern->config;
|
||||
i2c_info.flags = 0;
|
||||
if (i2c_info.addr > 0x7f)
|
||||
i2c_info.flags = 0x10;
|
||||
if (i2c_info.addr > 0x7f) {
|
||||
BLEXERR("%s invalid i2c address: 0x%02x\n",
|
||||
bl_extern->config.name, bl_extern->config.i2c_addr);
|
||||
return -1;
|
||||
}
|
||||
i2c_client = i2c_new_device(adapter, &i2c_info);
|
||||
if (!i2c_client) {
|
||||
BLPR("error %s :failed new i2c device\n", BL_EXTERN_NAME);
|
||||
goto bl_extern_probe_failed;
|
||||
} else{
|
||||
DBG_PRINT("error %s: new i2c device succeed\n",
|
||||
((struct bl_extern_data_t *)(bl_ext_config))->name);
|
||||
}
|
||||
|
||||
if (!aml_lp8556_i2c_client) {
|
||||
ret = i2c_add_driver(&aml_lp8556_i2c_driver);
|
||||
if (ret) {
|
||||
BLPR("error %s probe: add i2c_driver failed\n",
|
||||
BL_EXTERN_NAME);
|
||||
goto bl_extern_probe_failed;
|
||||
BLEXERR("%s failed to new i2c device\n",
|
||||
bl_extern->config.name);
|
||||
} else {
|
||||
if (lcd_debug_print_flag) {
|
||||
BLEX("%s new i2c device succeed\n",
|
||||
bl_extern->config.name);
|
||||
}
|
||||
}
|
||||
BLPR("%s ok\n", __func__);
|
||||
|
||||
if (!i2c_lp8556_client) {
|
||||
ret = i2c_add_driver(&i2c_lp8556_driver);
|
||||
if (ret) {
|
||||
BLEXERR("%s add i2c_driver failed\n",
|
||||
bl_extern->config.name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ret = i2c_lp8556_update();
|
||||
|
||||
BLEX("%s: %d\n", __func__, ret);
|
||||
return ret;
|
||||
|
||||
bl_extern_probe_failed:
|
||||
kfree(bl_ext_config);
|
||||
bl_ext_config = NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int aml_lp8556_remove(struct platform_device *pdev)
|
||||
int i2c_lp8556_remove(void)
|
||||
{
|
||||
kfree(pdev->dev.platform_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USE_OF
|
||||
static const struct of_device_id aml_lp8556_dt_match[] = {
|
||||
{
|
||||
.compatible = "amlogic, bl_i2c_lp8556",
|
||||
},
|
||||
{},
|
||||
};
|
||||
#else
|
||||
#define aml_lp8556_dt_match NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver aml_lp8556_driver = {
|
||||
.probe = aml_lp8556_probe,
|
||||
.remove = aml_lp8556_remove,
|
||||
.driver = {
|
||||
.name = BL_EXTERN_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_USE_OF
|
||||
.of_match_table = aml_lp8556_dt_match,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
static int __init aml_lp8556_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
BLPR("%s\n", __func__);
|
||||
ret = platform_driver_register(&aml_lp8556_driver);
|
||||
if (ret) {
|
||||
BLPR("error %s failed to register bl extern driver\n",
|
||||
__func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit aml_lp8556_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&aml_lp8556_driver);
|
||||
}
|
||||
|
||||
module_init(aml_lp8556_init);
|
||||
module_exit(aml_lp8556_exit);
|
||||
|
||||
MODULE_AUTHOR("AMLOGIC");
|
||||
MODULE_DESCRIPTION("BL Extern driver for LP8556");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
/*
|
||||
* drivers/amlogic/media/vout/backlight/bl_extern/mipi_LT070ME05.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/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/amlogic/media/vout/lcd/aml_bl_extern.h>
|
||||
|
||||
#ifdef CONFIG_LCD_IF_MIPI_VALID
|
||||
static struct bl_extern_config_t *bl_ext_config;
|
||||
|
||||
#ifdef BL_EXT_DEBUG_INFO
|
||||
#define DBG_PRINT(...) pr_info(__VA_ARGS__)
|
||||
#else
|
||||
#define DBG_PRINT(...)
|
||||
#endif
|
||||
|
||||
#define BL_EXTERN_NAME "bl_mipi_LT070ME05"
|
||||
static unsigned int bl_status = 1;
|
||||
static unsigned int bl_level;
|
||||
|
||||
/******************** mipi command ********************
|
||||
* format: data_type, num, data....
|
||||
* special: data_type=0xff, num<0xff means delay ms, num=0xff means ending.
|
||||
*/
|
||||
static int bl_extern_set_level(unsigned int level)
|
||||
{
|
||||
unsigned char payload[] = {0x15, 2, 0x51, 0xe6, 0xff, 0xff};
|
||||
|
||||
bl_level = level;
|
||||
|
||||
if (bl_ext_config == NULL) {
|
||||
pr_err("no %s driver\n", BL_EXTERN_NAME);
|
||||
return -1;
|
||||
}
|
||||
get_bl_ext_level(bl_ext_config);
|
||||
level = bl_ext_config->dim_min -
|
||||
((level - bl_ext_config->level_min) *
|
||||
(bl_ext_config->dim_min - bl_ext_config->dim_max)) /
|
||||
(bl_ext_config->level_max - bl_ext_config->level_min);
|
||||
level &= 0xff;
|
||||
|
||||
if (bl_status) {
|
||||
payload[3] = level;
|
||||
dsi_write_cmd(payload);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bl_extern_power_on(void)
|
||||
{
|
||||
if (bl_ext_config->gpio_used > 0) {
|
||||
if (bl_ext_config->gpio_on == 2) {
|
||||
bl_extern_gpio_input(bl_ext_config->gpio);
|
||||
} else {
|
||||
bl_extern_gpio_output(bl_ext_config->gpio,
|
||||
bl_ext_config->gpio_on);
|
||||
}
|
||||
}
|
||||
bl_status = 1;
|
||||
bl_extern_set_level(bl_level);
|
||||
pr_info("%s\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bl_extern_power_off(void)
|
||||
{
|
||||
if (bl_ext_config->gpio_used > 0) {
|
||||
if (bl_ext_config->gpio_off == 2) {
|
||||
bl_extern_gpio_input(bl_ext_config->gpio);
|
||||
} else {
|
||||
bl_extern_gpio_output(bl_ext_config->gpio,
|
||||
bl_ext_config->gpio_off);
|
||||
}
|
||||
}
|
||||
pr_info("%s\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_bl_extern_config(struct device dev,
|
||||
struct bl_extern_config_t *bl_ext_cfg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct aml_bl_extern_driver_t *bl_ext;
|
||||
|
||||
ret = get_bl_extern_dt_data(dev, bl_ext_cfg);
|
||||
if (ret) {
|
||||
pr_err("[error] %s: failed to get dt data\n", BL_EXTERN_NAME);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (bl_ext_cfg->dim_min > 0xff)
|
||||
bl_ext_cfg->dim_min = 0xff;
|
||||
if (bl_ext_cfg->dim_max > 0xff)
|
||||
bl_ext_cfg->dim_max = 0xff;
|
||||
|
||||
bl_ext = aml_bl_extern_get_driver();
|
||||
if (bl_ext) {
|
||||
bl_ext->type = bl_ext_cfg->type;
|
||||
bl_ext->name = bl_ext_cfg->name;
|
||||
bl_ext->power_on = bl_extern_power_on;
|
||||
bl_ext->power_off = bl_extern_power_off;
|
||||
bl_ext->set_level = bl_extern_set_level;
|
||||
} else {
|
||||
pr_err("[error] %s get bl_extern_driver failed\n",
|
||||
bl_ext_cfg->name);
|
||||
ret = -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aml_LT070ME05_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (bl_extern_driver_check())
|
||||
return -1;
|
||||
if (bl_ext_config == NULL)
|
||||
bl_ext_config = kzalloc(sizeof(*bl_ext_config), GFP_KERNEL);
|
||||
if (bl_ext_config == NULL) {
|
||||
pr_err("[error] %s probe: failed to alloc data\n",
|
||||
BL_EXTERN_NAME);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pdev->dev.platform_data = bl_ext_config;
|
||||
ret = get_bl_extern_config(pdev->dev, bl_ext_config);
|
||||
if (ret)
|
||||
goto bl_extern_probe_failed;
|
||||
|
||||
pr_info("%s ok\n", __func__);
|
||||
return ret;
|
||||
|
||||
bl_extern_probe_failed:
|
||||
kfree(bl_ext_config);
|
||||
bl_ext_config = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int aml_LT070ME05_remove(struct platform_device *pdev)
|
||||
{
|
||||
kfree(pdev->dev.platform_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USE_OF
|
||||
static const struct of_device_id aml_LT070ME05_dt_match[] = {
|
||||
{
|
||||
.compatible = "amlogic, bl_mipi_LT070ME05",
|
||||
},
|
||||
{},
|
||||
};
|
||||
#else
|
||||
#define aml_LT070ME05_dt_match NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver aml_LT070ME05_driver = {
|
||||
.probe = aml_LT070ME05_probe,
|
||||
.remove = aml_LT070ME05_remove,
|
||||
.driver = {
|
||||
.name = BL_EXTERN_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_USE_OF
|
||||
.of_match_table = aml_LT070ME05_dt_match,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
static int __init aml_LT070ME05_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
DBG_PRINT("%s\n", __func__);
|
||||
|
||||
ret = platform_driver_register(&aml_LT070ME05_driver);
|
||||
if (ret) {
|
||||
pr_err("[error] %s failed to register bl extern driver\n",
|
||||
__func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit aml_LT070ME05_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&aml_LT070ME05_driver);
|
||||
}
|
||||
|
||||
rootfs_initcall(aml_LT070ME05_init);
|
||||
module_exit(aml_LT070ME05_exit);
|
||||
|
||||
MODULE_AUTHOR("AMLOGIC");
|
||||
MODULE_DESCRIPTION("BL Extern driver for LT070ME05");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#endif
|
||||
123
drivers/amlogic/media/vout/backlight/bl_extern/mipi_lt070me05.c
Normal file
123
drivers/amlogic/media/vout/backlight/bl_extern/mipi_lt070me05.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* drivers/amlogic/media/vout/backlight/bl_extern/mipi_lt070me05.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/aml_bl_extern.h>
|
||||
#include <linux/amlogic/media/vout/lcd/aml_bl.h>
|
||||
#include "bl_extern.h"
|
||||
|
||||
|
||||
#define BL_EXTERN_INDEX 1
|
||||
#define BL_EXTERN_NAME "mipi_lt070me05"
|
||||
#define BL_EXTERN_TYPE BL_EXTERN_MIPI
|
||||
|
||||
|
||||
static unsigned int bl_status;
|
||||
|
||||
static int mipi_lt070me05_power_on(void)
|
||||
{
|
||||
bl_status = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mipi_lt070me05_power_off(void)
|
||||
{
|
||||
bl_status = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************** mipi command ********************
|
||||
*format: data_type, num, data....
|
||||
*special: data_type=0xff, num<0xff means delay ms, num=0xff means ending.
|
||||
*/
|
||||
static int mipi_lt070me05_set_level(unsigned int level)
|
||||
{
|
||||
unsigned char payload[] = {0x15, 2, 0x51, 0xe6, 0xff, 0xff};
|
||||
struct aml_bl_drv_s *bl_drv = aml_bl_get_driver();
|
||||
struct aml_bl_extern_driver_s *bl_extern = aml_bl_extern_get_driver();
|
||||
unsigned int level_max, level_min;
|
||||
unsigned int dim_max, dim_min;
|
||||
|
||||
if (bl_drv == NULL)
|
||||
return -1;
|
||||
level_max = bl_drv->bconf->level_max;
|
||||
level_min = bl_drv->bconf->level_min;
|
||||
dim_max = bl_extern->config.dim_max;
|
||||
dim_min = bl_extern->config.dim_min;
|
||||
level = dim_min - ((level - level_min) * (dim_min - dim_max)) /
|
||||
(level_max - level_min);
|
||||
level &= 0xff;
|
||||
|
||||
if (bl_status) {
|
||||
payload[3] = level;
|
||||
#ifdef CONFIG_AMLOGIC_LCD_TABLET
|
||||
dsi_write_cmd(&payload[0]);
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mipi_lt070me05_update(void)
|
||||
{
|
||||
struct aml_bl_extern_driver_s *bl_extern = aml_bl_extern_get_driver();
|
||||
|
||||
if (bl_extern == NULL) {
|
||||
BLEXERR("%s driver is null\n", BL_EXTERN_NAME);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bl_extern->config.type == BL_EXTERN_MAX) {
|
||||
bl_extern->config.index = BL_EXTERN_INDEX;
|
||||
bl_extern->config.type = BL_EXTERN_TYPE;
|
||||
strcpy(bl_extern->config.name, BL_EXTERN_NAME);
|
||||
}
|
||||
|
||||
bl_extern->device_power_on = mipi_lt070me05_power_on;
|
||||
bl_extern->device_power_off = mipi_lt070me05_power_off;
|
||||
bl_extern->device_bri_update = mipi_lt070me05_set_level;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mipi_lt070me05_probe(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = mipi_lt070me05_update();
|
||||
|
||||
BLEX("%s: %d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int mipi_lt070me05_remove(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,328 +0,0 @@
|
||||
/*
|
||||
* drivers/amlogic/media/vout/backlight/bl_extern/pmu_aml1218.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/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/amlogic/i2c-amlogic.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/amlogic/media/vout/lcd/aml_bl_extern.h>
|
||||
#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
|
||||
#include <linux/amlogic/aml_pmu_common.h>
|
||||
#endif
|
||||
|
||||
static struct bl_extern_config_t *bl_ext_config;
|
||||
|
||||
|
||||
#ifdef BL_EXT_DEBUG_INFO
|
||||
#define DBG_PRINT(...) pr_info(__VA_ARGS__)
|
||||
#else
|
||||
#define DBG_PRINT(...)
|
||||
#endif
|
||||
|
||||
#define BL_EXTERN_NAME "bl_pmu_aml1218"
|
||||
|
||||
static int bl_extern_set_level(unsigned int level)
|
||||
{
|
||||
#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
|
||||
struct aml_pmu_driver *pmu_driver;
|
||||
unsigned char temp;
|
||||
#endif
|
||||
int ret = 0;
|
||||
|
||||
if (bl_ext_config == NULL) {
|
||||
pr_err("no %s driver\n", BL_EXTERN_NAME);
|
||||
return -1;
|
||||
}
|
||||
get_bl_ext_level(bl_ext_config);
|
||||
level = bl_ext_config->dim_min -
|
||||
((level - bl_ext_config->level_min) *
|
||||
(bl_ext_config->dim_min - bl_ext_config->dim_max)) /
|
||||
(bl_ext_config->level_max - bl_ext_config->level_min);
|
||||
level &= 0x1f;
|
||||
#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
|
||||
pmu_driver = aml_pmu_get_driver();
|
||||
if (pmu_driver == NULL) {
|
||||
pr_err("no pmu driver\n");
|
||||
} else {
|
||||
if ((pmu_driver->pmu_reg_write) && (pmu_driver->pmu_reg_read)) {
|
||||
ret = pmu_driver->pmu_reg_read(0x005f, &temp);
|
||||
temp &= ~(0x3f << 2);
|
||||
temp |= (level << 2);
|
||||
ret = pmu_driver->pmu_reg_write(0x005f, temp);
|
||||
} else {
|
||||
pr_err("no pmu_reg_read/write\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bl_extern_power_on(void)
|
||||
{
|
||||
#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
|
||||
struct aml_pmu_driver *pmu_driver;
|
||||
unsigned char temp;
|
||||
#endif
|
||||
int ret = 0;
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
|
||||
pmu_driver = aml_pmu_get_driver();
|
||||
if (pmu_driver == NULL) {
|
||||
pr_err("no pmu driver\n");
|
||||
} else {
|
||||
if ((pmu_driver->pmu_reg_write) && (pmu_driver->pmu_reg_read)) {
|
||||
ret = pmu_driver->pmu_reg_read(0x005e, &temp);
|
||||
temp |= (1 << 7);
|
||||
ret = pmu_driver->pmu_reg_write(0x005e, temp);
|
||||
} else {
|
||||
pr_err("no pmu_reg_read/write\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (bl_ext_config->gpio_used > 0) {
|
||||
if (bl_ext_config->gpio_on == 2)
|
||||
bl_extern_gpio_input(bl_ext_config->gpio);
|
||||
else
|
||||
bl_extern_gpio_output(bl_ext_config->gpio,
|
||||
bl_ext_config->gpio_on);
|
||||
}
|
||||
pr_info("%s\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bl_extern_power_off(void)
|
||||
{
|
||||
#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
|
||||
struct aml_pmu_driver *pmu_driver;
|
||||
unsigned char temp;
|
||||
#endif
|
||||
int ret = 0;
|
||||
|
||||
if (bl_ext_config->gpio_used > 0) {
|
||||
if (bl_ext_config->gpio_off == 2)
|
||||
bl_extern_gpio_input(bl_ext_config->gpio);
|
||||
else
|
||||
bl_extern_gpio_output(bl_ext_config->gpio,
|
||||
bl_ext_config->gpio_off);
|
||||
}
|
||||
#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
|
||||
pmu_driver = aml_pmu_get_driver();
|
||||
if (pmu_driver == NULL) {
|
||||
pr_err("no pmu driver\n");
|
||||
} else {
|
||||
if ((pmu_driver->pmu_reg_write) && (pmu_driver->pmu_reg_read)) {
|
||||
ret = pmu_driver->pmu_reg_read(0x005e, &temp);
|
||||
temp &= ~(1 << 7);
|
||||
ret = pmu_driver->pmu_reg_write(0x005e, temp);
|
||||
} else {
|
||||
pr_err("no pmu_reg_read/write\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
pr_info("%s\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t bl_extern_debug(struct class *class,
|
||||
struct class_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
|
||||
struct aml_pmu_driver *pmu_driver;
|
||||
unsigned char temp;
|
||||
unsigned int t[2];
|
||||
#endif
|
||||
int ret = 0;
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_BOARD_HAS_PMU
|
||||
pmu_driver = aml_pmu_get_driver();
|
||||
if (pmu_driver == NULL) {
|
||||
pr_err("no pmu driver\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (buf[0]) {
|
||||
case 'r':
|
||||
ret = sscanf(buf, "r %x", &t[0]);
|
||||
if ((pmu_driver->pmu_reg_write) && (pmu_driver->pmu_reg_read)) {
|
||||
ret = pmu_driver->pmu_reg_read(t[0], &temp);
|
||||
pr_info("read pmu reg: 0x%x=0x%x\n", t[0], temp);
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
ret = sscanf(buf, "w %x %x", &t[0], &t[1]);
|
||||
if ((pmu_driver->pmu_reg_write) && (pmu_driver->pmu_reg_read)) {
|
||||
ret = pmu_driver->pmu_reg_write(t[0], t[1]);
|
||||
ret = pmu_driver->pmu_reg_read(t[0], &temp);
|
||||
pr_info("write pmu reg 0x%x: 0x%x, readback: 0x%x\n",
|
||||
t[0], t[1], temp);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_err("wrong format of command.\n");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (ret != 1 || ret != 2)
|
||||
return -EINVAL;
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct class_attribute bl_extern_debug_class_attrs[] = {
|
||||
__ATTR(debug, 0644, NULL, bl_extern_debug),
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static struct class bl_extern_debug_class = {
|
||||
.name = "bl_ext",
|
||||
.class_attrs = bl_extern_debug_class_attrs,
|
||||
};
|
||||
|
||||
static int get_bl_extern_config(struct device dev,
|
||||
struct bl_extern_config_t *bl_ext_cfg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct aml_bl_extern_driver_t *bl_ext;
|
||||
|
||||
ret = get_bl_extern_dt_data(dev, bl_ext_cfg);
|
||||
if (ret) {
|
||||
pr_err("[error] %s: failed to get dt data\n", BL_EXTERN_NAME);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (bl_ext_cfg->dim_min > 0x1f)
|
||||
bl_ext_cfg->dim_min = 0x1f;
|
||||
if (bl_ext_cfg->dim_max > 0x1f)
|
||||
bl_ext_cfg->dim_max = 0x1f;
|
||||
|
||||
bl_ext = aml_bl_extern_get_driver();
|
||||
if (bl_ext) {
|
||||
bl_ext->type = bl_ext_cfg->type;
|
||||
bl_ext->name = bl_ext_cfg->name;
|
||||
bl_ext->power_on = bl_extern_power_on;
|
||||
bl_ext->power_off = bl_extern_power_off;
|
||||
bl_ext->set_level = bl_extern_set_level;
|
||||
} else {
|
||||
pr_err("[error] %s get bl_extern_driver failed\n",
|
||||
bl_ext_cfg->name);
|
||||
ret = -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aml_aml1218_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (bl_extern_driver_check())
|
||||
return -1;
|
||||
if (bl_ext_config == NULL)
|
||||
bl_ext_config = kzalloc(sizeof(*bl_ext_config), GFP_KERNEL);
|
||||
if (bl_ext_config == NULL) {
|
||||
pr_err("[error] %s probe: failed to alloc data\n",
|
||||
BL_EXTERN_NAME);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pdev->dev.platform_data = bl_ext_config;
|
||||
|
||||
ret = get_bl_extern_config(pdev->dev, bl_ext_config);
|
||||
if (ret)
|
||||
goto bl_extern_probe_failed;
|
||||
|
||||
ret = class_register(&bl_extern_debug_class);
|
||||
if (ret)
|
||||
pr_err("class register bl_extern_debug_class fail!\n");
|
||||
|
||||
pr_err("%s ok\n", __func__);
|
||||
return ret;
|
||||
|
||||
bl_extern_probe_failed:
|
||||
kfree(bl_ext_config);
|
||||
bl_ext_config = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int aml_aml1218_remove(struct platform_device *pdev)
|
||||
{
|
||||
kfree(pdev->dev.platform_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USE_OF
|
||||
static const struct of_device_id aml_aml1218_dt_match[] = {
|
||||
{
|
||||
.compatible = "amlogic, bl_pmu_aml1218",
|
||||
},
|
||||
{},
|
||||
};
|
||||
#else
|
||||
#define aml_aml1218_dt_match NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver aml_aml1218_driver = {
|
||||
.probe = aml_aml1218_probe,
|
||||
.remove = aml_aml1218_remove,
|
||||
.driver = {
|
||||
.name = BL_EXTERN_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_USE_OF
|
||||
.of_match_table = aml_aml1218_dt_match,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
static int __init aml_aml1218_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
DBG_PRINT("%s\n", __func__);
|
||||
|
||||
ret = platform_driver_register(&aml_aml1218_driver);
|
||||
if (ret) {
|
||||
pr_err("[error] %s failed to register bl extern driver\n",
|
||||
__func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit aml_aml1218_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&aml_aml1218_driver);
|
||||
}
|
||||
|
||||
rootfs_initcall(aml_aml1218_init);
|
||||
module_exit(aml_aml1218_exit);
|
||||
|
||||
MODULE_AUTHOR("AMLOGIC");
|
||||
MODULE_DESCRIPTION("BL Extern driver for aml1218");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -20,6 +20,8 @@
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/amlogic/aml_gpio_consumer.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/amlogic/pwm_meson.h>
|
||||
|
||||
#define BLPR(fmt, args...) pr_info("bl: "fmt"", ## args)
|
||||
#define BLERR(fmt, args...) pr_err("bl error: "fmt"", ## args)
|
||||
@@ -147,6 +149,7 @@ struct bl_config_s {
|
||||
struct bl_gpio_s bl_gpio[BL_GPIO_NUM_MAX];
|
||||
struct pinctrl *pin;
|
||||
unsigned int pinmux_flag;
|
||||
unsigned int bl_extern_index;
|
||||
};
|
||||
|
||||
#define BL_INDEX_DEFAULT 0
|
||||
|
||||
@@ -15,62 +15,70 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __AMLOGIC_BL_EXTERN_H_
|
||||
#define __AMLOGIC_BL_EXTERN_H_
|
||||
|
||||
#include <linux/amlogic/aml_gpio_consumer.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#ifndef _INC_AML_BL_EXTERN_H_
|
||||
#define _INC_AML_BL_EXTERN_H_
|
||||
|
||||
enum bl_extern_type_e {
|
||||
BL_EXTERN_I2C = 0,
|
||||
BL_EXTERN_SPI,
|
||||
BL_EXTERN_OTHER,
|
||||
BL_EXTERN_MIPI,
|
||||
BL_EXTERN_MAX,
|
||||
};
|
||||
|
||||
struct bl_extern_config_s {
|
||||
const char *name;
|
||||
enum bl_extern_type_e type;
|
||||
struct gpio_desc *gpio;
|
||||
unsigned char gpio_on;
|
||||
unsigned char gpio_off;
|
||||
enum bl_extern_i2c_bus_e {
|
||||
BL_EXTERN_I2C_BUS_AO = 0,
|
||||
BL_EXTERN_I2C_BUS_A,
|
||||
BL_EXTERN_I2C_BUS_B,
|
||||
BL_EXTERN_I2C_BUS_C,
|
||||
BL_EXTERN_I2C_BUS_D,
|
||||
BL_EXTERN_I2C_BUS_MAX,
|
||||
};
|
||||
#define BL_EXTERN_I2C_BUS_INVALID 0xff
|
||||
|
||||
int i2c_addr;
|
||||
int i2c_bus;
|
||||
struct gpio_desc *spi_cs;
|
||||
struct gpio_desc *spi_clk;
|
||||
struct gpio_desc *spi_data;
|
||||
#define BL_EXTERN_SPI_CLK_FREQ_DFT 10000 /* default 10k */
|
||||
|
||||
#define BL_EXTERN_INIT_TABLE_MAX 500
|
||||
|
||||
#define BL_EXTERN_INIT_CMD 0x00
|
||||
#define BL_EXTERN_INIT_NONE 0xf0
|
||||
#define BL_EXTERN_INIT_END 0xff
|
||||
|
||||
#define BL_EXTERN_DYNAMIC_LEN 0xff
|
||||
|
||||
#define BL_EXTERN_GPIO_NUM_MAX 6
|
||||
#define BL_EXTERN_INDEX_INVALID 0xff
|
||||
#define BL_EXTERN_NAME_LEN_MAX 30
|
||||
struct bl_extern_config_s {
|
||||
unsigned char index;
|
||||
char name[BL_EXTERN_NAME_LEN_MAX];
|
||||
enum bl_extern_type_e type;
|
||||
unsigned char i2c_addr;
|
||||
unsigned char i2c_bus;
|
||||
unsigned int dim_min;
|
||||
unsigned int dim_max;
|
||||
/* unsigned int level_min; */
|
||||
/* unsigned int level_max; */
|
||||
};
|
||||
|
||||
/*******global API******/
|
||||
struct aml_bl_extern_driver_t {
|
||||
const char *name;
|
||||
enum bl_extern_type_e type;
|
||||
/* global API */
|
||||
#define BL_EXT_DRIVER_MAX 10
|
||||
struct aml_bl_extern_driver_s {
|
||||
int (*power_on)(void);
|
||||
int (*power_off)(void);
|
||||
int (*set_level)(unsigned int level);
|
||||
void (*config_print)(void);
|
||||
int (*device_power_on)(void);
|
||||
int (*device_power_off)(void);
|
||||
int (*device_bri_update)(unsigned int level);
|
||||
struct bl_extern_config_s config;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
#define BL_EXTERN_DRIVER "bl_extern"
|
||||
extern struct aml_bl_extern_driver_s *aml_bl_extern_get_driver(void);
|
||||
extern int aml_bl_extern_device_load(int index);
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_LCD_TABLET
|
||||
extern int dsi_write_cmd(unsigned char *payload);
|
||||
#endif
|
||||
|
||||
#define bl_extern_gpio_free(gpio) gpiod_free(gpio, BL_EXTERN_DRIVER)
|
||||
#define bl_extern_gpio_input(gpio) gpiod_direction_input(gpio)
|
||||
#define bl_extern_gpio_output(gpio, val) gpiod_direction_output(gpio, val)
|
||||
#define bl_extern_gpio_get_value(gpio) gpiod_get_value(gpio)
|
||||
#define bl_extern_gpio_set_value(gpio, val) gpiod_set_value(gpio, val)
|
||||
|
||||
|
||||
extern struct aml_bl_extern_driver_t *aml_bl_extern_get_driver(void);
|
||||
extern int bl_extern_driver_check(void);
|
||||
extern int get_bl_extern_dt_data(struct device dev,
|
||||
struct bl_extern_config_s *pdata);
|
||||
|
||||
extern void get_bl_ext_level(struct bl_extern_config_s *bl_ext_cfg);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user