1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2025-09-26 19:04:54 +00:00
Files
a155-U-u1/kernel-5.10/drivers/gpu/drm/panel/mtk_drm_gateic_rt4831a.c
physwizz 99537be4e2 first
2024-03-11 06:53:12 +11:00

500 lines
13 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#ifdef CONFIG_LEDS_MTK_PWM
#include <leds-mtk-pwm.h>
#define CONFIG_LEDS_BRIGHTNESS_CHANGED
#elif defined(CONFIG_LEDS_MTK_I2C)
#include <leds-mtk-i2c.h>
#define CONFIG_LEDS_BRIGHTNESS_CHANGED
#endif
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include "mtk_drm_gateic.h"
#define ADDR_BACKLIGHT_CONFIG1 (0x02)
#define ADDR_BACKLIGHT_CONFIG2 (0x03)
#define ADDR_BACKLIGHT_BRIGHTNESS_LSB (0x04)
#define ADDR_BACKLIGHT_BRIGHTNESS_MSB (0x05)
#define ADDR_BACKLIGHT_AUTO_FREQ_LOW (0x06)
#define ADDR_BACKLIGHT_AUTO_FREQ_HIGH (0x07)
#define ADDR_BACKLIGHT_ENABLE (0x08)
#define ADDR_BACKLIGHT_OPTION1 (0x10)
#define ADDR_BACKLIGHT_OPTION2 (0x11)
#define ADDR_BACKLIGHT_SMOOTH (0x14)
#define ADDR_BIAS_CONFIG1 (0x09)
#define ADDR_BIAS_CONFIG2 (0x0A)
#define ADDR_BIAS_CONFIG3 (0x0B)
#define ADDR_BIAS_LCM (0x0C)
#define ADDR_BIAS_VPOS (0x0D)
#define ADDR_BIAS_VNEG (0x0E)
#define BRIGHTNESS_LOW_OFFSET (0)
#define BRIGHTNESS_HIGH_OFFSET (3)
#define BRIGHTNESS_LOW_MASK (0x07)
#define BRIGHTNESS_HIGH_MASK (0x7F8)
#define RT4831A_VOL_UNIT (50) //50mV
#define RT4831A_VOL_MIN_LEVEL (4000) //4000mV
#define RT4831A_VOL_MAX_LEVEL (6500) //6500mV
#define RT4831A_VOL_REG_VALUE(level) ((level - RT4831A_VOL_MIN_LEVEL) / RT4831A_VOL_UNIT)
struct mtk_gateic_data ctx_rt4831a;
static int rt4831a_push_i2c_data(unsigned char *table, unsigned int size,
unsigned int unit)
{
int ret = 0, i = 0;
if (IS_ERR_OR_NULL(table) ||
size == 0 || unit < 2) {
DDPMSG("%s, invalid table, size:%u, unit:%u\n",
__func__, size, unit);
return -EINVAL;
}
for (i = 0; i < size; i++) {
if (unit > 2)
ret = mtk_panel_i2c_write_multiple_bytes(*(table + i * unit),
table + i * unit + 1, unit - 1);
else if (unit == 2)
ret = mtk_panel_i2c_write_bytes(*(table + i * 2),
*(table + i * 2 + 1));
if (ret < 0) {
DDPMSG("%s, failed of i2c write, i:%u addr:0x%x, unit:0x%x",
__func__, i, *(table + i * unit), unit);
break;
}
}
return ret;
}
static int rt4831a_update_backlight_table(unsigned int level, unsigned char *table,
unsigned int size, unsigned int unit)
{
int i = 0;
if (IS_ERR_OR_NULL(table) ||
size == 0 || unit < 2) {
DDPMSG("%s, invalid table, size:%u, unit:%u\n",
__func__, size, unit);
return -EINVAL;
}
for (i = 0; i < size; i++) {
if (*(table + i * unit) == ADDR_BACKLIGHT_BRIGHTNESS_LSB)
*(table + i * unit + 1) = (level & BRIGHTNESS_LOW_MASK) >>
BRIGHTNESS_LOW_OFFSET;
else if (*(table + i * unit) == ADDR_BACKLIGHT_BRIGHTNESS_MSB)
*(table + i * unit + 1) = (level & BRIGHTNESS_HIGH_MASK) >>
BRIGHTNESS_HIGH_OFFSET;
}
return 0;
}
static int rt4831a_set_backlight(unsigned int level)
{
int ret = 0;
unsigned char table[2][2] = {
{ADDR_BACKLIGHT_BRIGHTNESS_LSB, 0},
{ADDR_BACKLIGHT_BRIGHTNESS_MSB, 0}
};
unsigned int unit = ARRAY_SIZE(table[0]);
unsigned int size = sizeof(table) / unit;
if (atomic_read(&ctx_rt4831a.init) != 1) {
DDPPR_ERR("%s gate ic is not initialized\n", __func__);
return -1;
}
if (ctx_rt4831a.backlight_mode != BL_I2C_MODE) {
DDPPR_ERR("%s do not support I2C mode\n", __func__);
return -1;
}
rt4831a_update_backlight_table(level, &table[0][0], size, unit);
ret = rt4831a_push_i2c_data(&table[0][0], size, unit);
if (ret < 0)
DDPMSG("%s: ERROR %d!! i2c write data fail 0x%0x, 0x%0x !!\n",
__func__, ret, table[0][1], table[1][1]);
ctx_rt4831a.backlight_level = level;
return 0; //for evb case, i2c ops are always failed
}
static int rt4831a_enable_backlight(void)
{
int ret = 0;
unsigned char table[5][2] = {
{ADDR_BACKLIGHT_CONFIG2, 0x9D},
{ADDR_BACKLIGHT_OPTION1, 0x06},
{ADDR_BACKLIGHT_OPTION2, 0xB7},
{ADDR_BACKLIGHT_ENABLE, 0x15},
{ADDR_BACKLIGHT_SMOOTH, 0x03}
};
unsigned int unit = ARRAY_SIZE(table[0]);
unsigned int size = sizeof(table) / unit;
//DDPMSG("%s+\n", __func__);
switch (ctx_rt4831a.backlight_mode) {
case BL_PWM_MODE:
ret = mtk_panel_i2c_write_bytes(ADDR_BACKLIGHT_CONFIG1, 0x6B);
break;
case BL_I2C_MODE:
ret = mtk_panel_i2c_write_bytes(ADDR_BACKLIGHT_CONFIG1, 0x68);
if (atomic_read(&ctx_rt4831a.backlight_status) == 0)
rt4831a_set_backlight(0);
break;
default:
ret = -EINVAL;
break;
}
if (ret < 0) {
DDPMSG("%s, failed to enable backlight, mode:%u",
__func__, ctx_rt4831a.backlight_mode);
return ret;
}
ret = rt4831a_push_i2c_data(&table[0][0], size, unit);
if (ret < 0)
DDPMSG("%s, failed to push backlight table, mode:%u, ret:%d",
__func__, ctx_rt4831a.backlight_mode, ret);
//DDPMSG("%s--, %d\n", __func__, ret);
return 0; //for evb case, i2c ops are always failed
}
#ifdef CONFIG_LEDS_BRIGHTNESS_CHANGED
int rt4831a_backlight_event(struct notifier_block *nb, unsigned long event,
void *v)
{
struct led_conf_info *led_conf;
DDPMSG("%s+\n", __func__);
led_conf = (struct led_conf_info *)v;
switch (event) {
case 1:
if (led_conf->cdev.brightness > 0)
atomic_set(&ctx_rt4831a.backlight_status, 1);
else
atomic_set(&ctx_rt4831a.backlight_status, 0);
break;
default:
break;
}
return NOTIFY_DONE;
}
static struct notifier_block rt4831a_leds_init_notifier = {
.notifier_call = rt4831a_backlight_event,
};
#endif
static int rt4831a_reset(int on)
{
ctx_rt4831a.reset_gpio =
devm_gpiod_get(ctx_rt4831a.dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(ctx_rt4831a.reset_gpio)) {
dev_err(ctx_rt4831a.dev, "%s: cannot get reset_gpio %ld\n",
__func__, PTR_ERR(ctx_rt4831a.reset_gpio));
return PTR_ERR(ctx_rt4831a.reset_gpio);
}
gpiod_set_value(ctx_rt4831a.reset_gpio, on);
DDPINFO("%s, gpio:0x%x, on:%d\n",
__func__, ctx_rt4831a.reset_gpio, on);
devm_gpiod_put(ctx_rt4831a.dev, ctx_rt4831a.reset_gpio);
return 0;
}
static int rt4831a_power_on(void)
{
int ret = 0;
unsigned char table[8][2] = {
{ADDR_BIAS_CONFIG2, 0x11},
{ADDR_BIAS_CONFIG3, 0x00},
/*default voltage of 5.4v*/
{ADDR_BIAS_LCM, 0x24},
{ADDR_BIAS_VPOS, 0x1c},
{ADDR_BIAS_VNEG, 0x1c},
/*set dsv FPWM mode*/
{0xF0, 0x69},
{0xB1, 0x6c},
/* bias enable*/
{ADDR_BIAS_CONFIG1, 0x9e},
};
unsigned int unit = ARRAY_SIZE(table[0]);
unsigned int size = sizeof(table) / unit;
if (atomic_read(&ctx_rt4831a.init) != 1) {
DDPPR_ERR("%s gate ic is not initialized\n", __func__);
return -1;
}
if (atomic_read(&ctx_rt4831a.ref) > 0) {
atomic_inc(&ctx_rt4831a.ref);
DDPPR_ERR("%s gate ic (%u) already power on\n",
__func__, atomic_read(&ctx_rt4831a.ref));
return 0;
}
DDPMSG("%s++ size:%u, unit:%u, backlight status:%d\n", __func__,
size, unit, atomic_read(&ctx_rt4831a.backlight_status));
ctx_rt4831a.bias_pos_gpio = devm_gpiod_get(ctx_rt4831a.dev,
"pm-enable", GPIOD_OUT_HIGH);
if (IS_ERR(ctx_rt4831a.bias_pos_gpio)) {
dev_err(ctx_rt4831a.dev, "%s: cannot get pm-enable:%ld\n",
__func__, PTR_ERR(ctx_rt4831a.bias_pos_gpio));
return PTR_ERR(ctx_rt4831a.bias_pos_gpio);
}
gpiod_set_value(ctx_rt4831a.bias_pos_gpio, 1);
devm_gpiod_put(ctx_rt4831a.dev, ctx_rt4831a.bias_pos_gpio);
atomic_inc(&ctx_rt4831a.ref);
ret = rt4831a_enable_backlight();
if (ret < 0)
DDPMSG("%s, failed to enable backlight, ret:%d",
__func__, ret);
ret = rt4831a_push_i2c_data(&table[0][0], size, unit);
if (ret < 0)
DDPMSG("%s, failed to push power on table, ret:%d",
__func__, ret);
//DDPMSG("%s--\n", __func__);
return 0; //for evb case, i2c ops are always failed
}
static int rt4831a_power_off(void)
{
int ret = 0;
if (atomic_read(&ctx_rt4831a.init) != 1) {
DDPPR_ERR("%s gate ic is not initialized\n", __func__);
return -1;
}
if (atomic_read(&ctx_rt4831a.ref) == 0) {
DDPPR_ERR("%s gate ic (%u) already power off\n",
__func__, atomic_read(&ctx_rt4831a.ref));
return 0;
}
DDPMSG("%s++\n", __func__);
atomic_dec(&ctx_rt4831a.ref);
if (atomic_read(&ctx_rt4831a.ref) > 0) {
DDPMSG("%s, %d, do nothing, there are other users, %u\n",
__func__, __LINE__, atomic_read(&ctx_rt4831a.ref));
return 0;
}
if (atomic_read(&ctx_rt4831a.backlight_status) == 0) {
ret = mtk_panel_i2c_write_bytes(ADDR_BIAS_CONFIG1, 0x18);
if (ret < 0)
DDPMSG("%s, %d, failed of i2c write\n", __func__, __LINE__);
ctx_rt4831a.bias_pos_gpio = devm_gpiod_get(ctx_rt4831a.dev,
"pm-enable", GPIOD_OUT_HIGH);
if (IS_ERR(ctx_rt4831a.bias_pos_gpio)) {
dev_err(ctx_rt4831a.dev, "%s: cannot get pm-enable:%ld\n",
__func__, PTR_ERR(ctx_rt4831a.bias_pos_gpio));
return PTR_ERR(ctx_rt4831a.bias_pos_gpio);
}
gpiod_set_value(ctx_rt4831a.bias_pos_gpio, 0);
devm_gpiod_put(ctx_rt4831a.dev, ctx_rt4831a.bias_pos_gpio);
}
DDPMSG("%s--\n", __func__);
return 0; //for evb case, i2c ops are always failed
}
static int rt4831a_set_voltage(unsigned int level)
{
int ret = 0, i = 0;
unsigned char level_id = 0;
unsigned char table[3][2] = {
{ADDR_BIAS_LCM, 0},
{ADDR_BIAS_VPOS, 0},
{ADDR_BIAS_VNEG, 0}
};
unsigned int unit = ARRAY_SIZE(table[0]);
unsigned int size = sizeof(table) / unit;
if (atomic_read(&ctx_rt4831a.init) != 1) {
DDPPR_ERR("%s gate ic is not initialized\n", __func__);
return -1;
}
if (atomic_read(&ctx_rt4831a.ref) == 0) {
DDPPR_ERR("%s gate ic (%u) is power off\n",
__func__, atomic_read(&ctx_rt4831a.ref));
return -2;
}
if (level < RT4831A_VOL_MIN_LEVEL || level > RT4831A_VOL_MAX_LEVEL) {
DDPPR_ERR("%s invalid voltage level:%d\n", __func__, level);
return -3;
}
level_id = (unsigned char)RT4831A_VOL_REG_VALUE(level);
DDPMSG("%s++ level:%d, id:%u\n", __func__, level, level_id);
for (i = 0; i < size; i++)
table[i][1] = level_id;
ret = rt4831a_push_i2c_data(&table[0][0], size, unit);
if (ret < 0)
DDPMSG("%s, failed to push voltage table,level:%u-%u, ret:%d",
__func__, level, RT4831A_VOL_REG_VALUE(level), ret);
return 0; //for evb case, i2c ops are always failed
}
static int rt4831a_match_lcm_list(const char *lcm_name)
{
if (atomic_read(&ctx_rt4831a.init) != 1) {
DDPPR_ERR("%s gate ic is not initialized\n", __func__);
return 0;
}
if (mtk_gateic_match_lcm_list(lcm_name, ctx_rt4831a.lcm_list,
ctx_rt4831a.lcm_count, "rt4831a") == true) {
return 1;
}
return 0;
}
static struct mtk_gateic_funcs rt4831a_ops = {
.reset = rt4831a_reset,
.power_on = rt4831a_power_on,
.power_off = rt4831a_power_off,
.set_voltage = rt4831a_set_voltage,
.enable_backlight = rt4831a_enable_backlight,
.set_backlight = rt4831a_set_backlight,
.match_lcm_list = rt4831a_match_lcm_list,
};
static int rt4831a_drv_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int ret = 0, len = 0;
if (atomic_read(&ctx_rt4831a.init) == 1)
return 0;
DDPMSG("%s++\n", __func__);
ctx_rt4831a.dev = dev;
len = of_property_count_strings(dev->of_node, "panel-list");
if (len > 0) {
//DDPMSG("%s, %d, len:%d\n", __func__, __LINE__, len);
ctx_rt4831a.lcm_list = kcalloc(len, sizeof(char *), GFP_KERNEL);
if (IS_ERR_OR_NULL(ctx_rt4831a.lcm_list)) {
DDPPR_ERR("%s, %d, failed to allocate lcm list, len:%d\n",
__func__, __LINE__, len);
return -ENOMEM;
}
len = of_property_read_string_array(dev->of_node, "panel-list",
ctx_rt4831a.lcm_list, len);
if (len < 0) {
DDPPR_ERR("%s, %d, failed to get panel-list, len:%d\n",
__func__, __LINE__, len);
ret = -EINVAL;
goto error;
}
ctx_rt4831a.lcm_count = (unsigned int)len;
} else {
DDPPR_ERR("%s, %d, failed to get lcm_pinctrl_names, %d\n",
__func__, __LINE__, len);
return -EFAULT;
}
ctx_rt4831a.reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(ctx_rt4831a.reset_gpio)) {
dev_err(dev, "%s: cannot get reset-gpios %ld\n",
__func__, PTR_ERR(ctx_rt4831a.reset_gpio));
ret = PTR_ERR(ctx_rt4831a.reset_gpio);
goto error;
}
devm_gpiod_put(dev, ctx_rt4831a.reset_gpio);
ctx_rt4831a.bias_pos_gpio = devm_gpiod_get(dev, "pm-enable", GPIOD_OUT_HIGH);
if (IS_ERR(ctx_rt4831a.bias_pos_gpio)) {
dev_err(dev, "%s: cannot get pm-enable:%ld\n",
__func__, PTR_ERR(ctx_rt4831a.bias_pos_gpio));
ret = PTR_ERR(ctx_rt4831a.bias_pos_gpio);
goto error;
}
devm_gpiod_put(dev, ctx_rt4831a.bias_pos_gpio);
ret = of_property_read_u32(dev->of_node, "backlight_mode", &ctx_rt4831a.backlight_mode);
if (ret != 0) {
dev_err(dev, "%s: failed to get backlight mode, %d\n",
__func__, ret);
ctx_rt4831a.backlight_mode = BL_PWM_MODE;
}
ctx_rt4831a.backlight_level = 2047;
#ifdef CONFIG_LEDS_BRIGHTNESS_CHANGED
mtk_leds_register_notifier(&rt4831a_leds_init_notifier);
#endif
atomic_set(&ctx_rt4831a.ref, 1);
atomic_set(&ctx_rt4831a.init, 1);
ret = mtk_drm_gateic_register(&rt4831a_ops, MTK_LCM_FUNC_DSI);
DDPMSG("%s--, %d, backlight mode:%u\n", __func__, ret, ctx_rt4831a.backlight_mode);
return ret;
error:
if (ctx_rt4831a.lcm_list != NULL)
kfree(ctx_rt4831a.lcm_list);
ctx_rt4831a.lcm_count = 0;
return ret;
}
static int rt4831a_drv_remove(struct platform_device *pdev)
{
DDPMSG("%s\n", __func__);
#ifdef CONFIG_LEDS_BRIGHTNESS_CHANGED
mtk_leds_unregister_notifier(&rt4831a_leds_init_notifier);
#endif
return 0;
}
static const struct of_device_id rt4831a_of_match[] = {
{ .compatible = "mediatek,mtk-drm-gateic-drv-rt4831a", },
{ }
};
MODULE_DEVICE_TABLE(of, rt4831a_of_match);
struct platform_driver mtk_gateic_rt4831a_driver = {
.probe = rt4831a_drv_probe,
.remove = rt4831a_drv_remove,
.driver = {
.name = "mtk-drm-gateic-drv-rt4831a",
.owner = THIS_MODULE,
.of_match_table = rt4831a_of_match,
},
};
MODULE_AUTHOR("Cui Zhang <cui.zhang@mediatek.com>");
MODULE_DESCRIPTION("mediatek, drm GATE IC driver of rt4831a");
MODULE_LICENSE("GPL v2");