kernel_samsung_a53x/drivers/gpu/drm/samsung/panel/panel_blic.c

776 lines
16 KiB
C
Raw Normal View History

2024-06-15 16:02:09 -03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include "panel_drv.h"
#include "panel_blic.h"
#include "panel_i2c.h"
#include "panel_debug.h"
#define BLIC_DT_NODE_NAME "blic"
static int panel_blic_do_seq(struct panel_blic_dev *blic, enum panel_blic_seq index);
static struct panel_blic_ops panel_blic_ops = {
.do_seq = panel_blic_do_seq,
.execute_power_ctrl = panel_blic_power_control_execute,
};
__visible_for_testing struct panel_device *to_panel_drv(struct panel_blic_dev *blic_dev)
{
struct panel_device *panel;
if (!blic_dev)
return NULL;
panel = container_of(blic_dev, struct panel_device, blic_dev[blic_dev->id]);
return panel;
}
__visible_for_testing struct blic_data *panel_blic_find_blic_data(struct panel_blic_dev *blic)
{
struct panel_device *panel;
struct panel_info *panel_data;
int i = 0;
if (!blic) {
panel_err("blic is null.\n");
return NULL;
}
panel = to_panel_drv(blic);
if (!panel) {
panel_err("panel is null.\n");
return NULL;
}
panel_data = &panel->panel_data;
for (i = 0; i < panel_data->nr_blic_data_tbl; i++) {
if (!strcmp(panel_data->blic_data_tbl[i]->name, blic->name))
return panel_data->blic_data_tbl[i];
}
return NULL;
}
static int panel_blic_do_seq(struct panel_blic_dev *blic, enum panel_blic_seq index)
{
struct panel_device *panel;
struct blic_data *blic_data;
struct seqinfo *tbl;
int ret = 0;
if (!blic || index >= MAX_PANEL_BLIC_SEQ || index < 0) {
panel_err("invalid arguments.\n");
return -EINVAL;
}
panel = to_panel_drv(blic);
if (!panel) {
panel_err("panel is null.\n");
return -EINVAL;
}
if (panel_bypass_is_on(panel)) {
panel_warn("panel no use\n");
return 0;
}
blic_data = panel_blic_find_blic_data(blic);
if (!blic_data) {
panel_err("blic_data is null.\n");
return -EINVAL;
}
tbl = blic_data->seqtbl;
mutex_lock(&panel->op_lock);
if (unlikely(index < 0 || index >= MAX_PANEL_BLIC_SEQ)) {
panel_err("invalid parameter (index %d)\n", index);
ret = -EINVAL;
goto do_exit;
}
/*
* @ i2c_dev_selected
*
* There can be many i2c drivers,
* "panel_do_seqtbl" don't know who should do among them.
* So, leave a selected i2c_dev pointer for hint.
* .i2c_dev_selected should be protected in panel->op_lock.
*/
#ifdef CONFIG_MCD_PANEL_I2C
if (blic->i2c_dev)
panel->i2c_dev_selected = blic->i2c_dev;
#endif
panel_info("%s:start\n", tbl[index].name);
ret = panel_do_seqtbl(panel, &tbl[index]);
if (unlikely(ret < 0)) {
panel_err("failed to excute seqtbl:%s\n", tbl->name);
ret = -EIO;
goto do_exit;
}
do_exit:
mutex_unlock(&panel->op_lock);
panel_info("%s:end\n", tbl[index].name);
return ret;
}
bool panel_blic_power_control_exists(struct panel_blic_dev *blic, const char *name)
{
struct panel_power_ctrl *pctrl;
struct panel_device *panel;
if (!blic || !name) {
panel_err("invalid arg");
return false;
}
if (!blic->np) {
panel_err("of node is empty");
return false;
}
if (!blic->np->name) {
panel_err("np name is empty");
return false;
}
panel = to_panel_drv(blic);
pctrl = panel_power_control_find_sequence(panel, blic->np->name, name);
if (IS_ERR_OR_NULL(pctrl)) {
if (PTR_ERR(pctrl) == -ENODATA)
panel_dbg("not found %s\n", name);
else
panel_err("error occurred when find %s, %ld\n", name, PTR_ERR(pctrl));
return false;
}
return true;
}
int panel_blic_power_control_execute(struct panel_blic_dev *blic, const char *name)
{
struct panel_power_ctrl *pctrl;
struct panel_device *panel;
if (!blic || !name) {
panel_err("invalid arg\n");
return -EINVAL;
}
if (!blic->np) {
panel_err("of node is empty");
return -ENODEV;
}
if (!blic->np->name) {
panel_err("np name is empty");
return -ENODEV;
}
panel = to_panel_drv(blic);
pctrl = panel_power_control_find_sequence(panel, blic->np->name, name);
if (IS_ERR_OR_NULL(pctrl)) {
if (PTR_ERR(pctrl) == -ENODATA) {
panel_dbg("%s not found\n", name);
return -ENODATA;
}
panel_dbg("error occurred when find %s, %ld\n", name, PTR_ERR(pctrl));
return PTR_ERR(pctrl);
}
return panel_power_ctrl_helper_execute(pctrl);
}
/* enable/disable regulator */
int panel_blic_regulator_enable(struct regulator_dev *rdev)
{
struct panel_blic_dev *blic;
int ret = 0;
panel_info("\n");
if (!rdev) {
panel_err("rdev is null.\n");
return -EINVAL;
}
blic = rdev->reg_data;
if (!blic) {
panel_err("blic is null.\n");
return -EINVAL;
}
if (blic->enable) {
panel_info("already enabled\n");
return 0;
}
if (!blic->ops) {
panel_info("blic ops is null\n");
return -EINVAL;
}
if (!blic->ops->do_seq) {
panel_info("do_seq is null\n");
return -EINVAL;
}
if (!blic->ops->execute_power_ctrl) {
panel_info("execute_power_ctrl is null\n");
return -EINVAL;
}
if (panel_blic_power_control_exists(blic, "panel_blic_pre_on"))
ret |= blic->ops->execute_power_ctrl(blic, "panel_blic_pre_on");
ret |= blic->ops->do_seq(blic, PANEL_BLIC_I2C_ON_SEQ);
if (panel_blic_power_control_exists(blic, "panel_blic_post_on"))
ret |= blic->ops->execute_power_ctrl(blic, "panel_blic_post_on");
if (ret) {
panel_err("failed.(%d)\n", ret);
return -EINVAL;
}
blic->enable = 1;
return 0;
}
__visible_for_testing int panel_blic_regulator_disable(struct regulator_dev *rdev)
{
struct panel_blic_dev *blic;
int ret = 0;
if (!rdev) {
panel_err("rdev is null.\n");
return -EINVAL;
}
blic = rdev->reg_data;
if (!blic) {
panel_err("blic is null.\n");
return -EINVAL;
}
panel_info("\n");
if (!blic->ops) {
panel_info("blic ops is null\n");
return -EINVAL;
}
if (!blic->ops->do_seq) {
panel_info("do_seq is null\n");
return -EINVAL;
}
if (!blic->ops->execute_power_ctrl) {
panel_info("execute_power_ctrl is null\n");
return -EINVAL;
}
if (!blic->enable) {
panel_info("already disabled\n");
return 0;
}
if (panel_blic_power_control_exists(blic, "panel_blic_pre_off"))
ret |= blic->ops->execute_power_ctrl(blic, "panel_blic_pre_off");
ret |= blic->ops->do_seq(blic, PANEL_BLIC_I2C_OFF_SEQ);
if (panel_blic_power_control_exists(blic, "panel_blic_post_off"))
ret |= blic->ops->execute_power_ctrl(blic, "panel_blic_post_off");
if (ret) {
panel_err("failed.(%d)\n", ret);
return -EINVAL;
}
blic->enable = 0;
return 0;
}
static int panel_blic_is_enabled_default(struct regulator_dev *rdev)
{
struct panel_blic_dev *blic;
if (!rdev) {
panel_err("rdev is null.\n");
return -EINVAL;
}
blic = rdev->reg_data;
if (!blic) {
panel_err("blic is null.\n");
return -EINVAL;
}
return blic->enable;
}
/* get/set regulator voltage */
static int panel_blic_set_voltage_default(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector)
{
if (!rdev) {
panel_err("rdev is null.\n");
return -EINVAL;
}
panel_info("PANEL:INFO:%s\n", __func__);
return 0;
}
static int panel_blic_get_voltage_default(struct regulator_dev *rdev)
{
struct panel_blic_dev *blic;
if (!rdev) {
panel_err("rdev is null.\n");
return -EINVAL;
}
blic = rdev->reg_data;
panel_info("PANEL:INFO:%s\n", __func__);
return blic->enable ? rdev->constraints->max_uV : 0;
}
static struct regulator_ops ddi_blic_regulator_ops = {
.is_enabled = panel_blic_is_enabled_default,
.enable = panel_blic_regulator_enable,
.disable = panel_blic_regulator_disable,
.set_voltage = panel_blic_set_voltage_default,
.get_voltage = panel_blic_get_voltage_default,
};
static struct regulator_desc ddi_blic_regulator_desc = {
.name = "ddi-blic",
.id = 0,
.ops = &ddi_blic_regulator_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
};
static struct regulator_desc ddi_blic_2_regulator_desc = {
.name = "ddi-blic-2",
.id = 0,
.ops = &ddi_blic_regulator_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
};
static struct regulator_desc *panel_blic_regulator_list[] = {
&ddi_blic_regulator_desc,
&ddi_blic_2_regulator_desc,
};
__visible_for_testing struct regulator_desc *panel_blic_find_regulator_desc(const char *name)
{
int i = 0;
if (!name) {
panel_err("name is null.\n");
return NULL;
}
for (i = 0; i < ARRAY_SIZE(panel_blic_regulator_list); i++) {
if (!strcmp(name, panel_blic_regulator_list[i]->name))
return panel_blic_regulator_list[i];
}
return NULL;
}
#if !defined(CONFIG_UML)
static int panel_blic_regulator_register(struct panel_blic_dev *blic_dev)
{
struct regulator_config config = {0, };
struct regulator_dev *rdev;
struct regulator_desc *desc;
if (!blic_dev) {
panel_err("blic_dev is null.\n");
return -EINVAL;
}
desc = panel_blic_find_regulator_desc(blic_dev->regulator_desc_name);
if (!desc) {
panel_err("failed to find regulator desc.(%s)\n", blic_dev->regulator_desc_name);
return -EINVAL;
}
config.driver_data = blic_dev;
config.of_node = blic_dev->regulator_node;
config.dev = blic_dev->dev;
config.init_data = of_get_regulator_init_data(blic_dev->dev, blic_dev->regulator_node, desc);
if (blic_dev->regulator_boot_on) {
panel_info("%s (regulator-boot-on)\n", desc->name);
blic_dev->enable = 1;
}
rdev = devm_regulator_register(blic_dev->dev, desc, &config);
if (IS_ERR(rdev)) {
panel_err("rdev is err. (%s) %d.\n", desc->name, rdev);
return -EINVAL;
}
panel_info("(%s) is registered\n", desc->name);
return 0;
}
#else
static inline int panel_blic_regulator_register(struct panel_blic_dev *blic_dev)
{
return 0;
}
#endif
#ifdef CONFIG_MCD_PANEL_I2C
__visible_for_testing struct panel_i2c_dev *panel_blic_find_i2c_drv(struct panel_blic_dev *blic_dev, struct panel_device *panel)
{
u8 i2c_read_buf[2] = { 0, };
int i = 0;
if (!blic_dev) {
panel_err("blic_dev is null.\n");
return NULL;
}
if (!panel) {
panel_err("panel is null.\n");
return NULL;
}
/*
* Find I2C for BLIC
* 1. Check reg (slave addr).
* 2. Check if it is used.
* 3. Read IC ID register.
*/
i2c_read_buf[0] = blic_dev->i2c_match[0];
for (i = 0; i < panel->nr_i2c_dev; i++) {
if (panel->i2c_dev[i].reg != blic_dev->i2c_reg)
continue;
if (panel->i2c_dev[i].use)
continue;
panel->i2c_dev[i].ops->rx(&panel->i2c_dev[i], i2c_read_buf, 2);
if ((i2c_read_buf[1] & blic_dev->i2c_match[1]) == blic_dev->i2c_match[2]) {
panel_info("panel_i2c found. blic id:(0x%02X) mask:(0x%02X) id&mask:(0x%02X) val:(0x%02X)\n",
i2c_read_buf[1], blic_dev->i2c_match[1],
(i2c_read_buf[1]&blic_dev->i2c_match[1]), blic_dev->i2c_match[2]);
return &panel->i2c_dev[i];
}
}
return NULL;
}
#endif
static int of_get_panel_blic(struct panel_blic_dev *blic_dev, struct device_node *np)
{
struct device_node *gpio_np, *gpios_np, *power_np, *seq_np;
struct property *pp;
struct panel_device *panel;
struct panel_blic_dev *temp;
struct panel_gpio gpio;
struct panel_gpio *p_gpio;
struct panel_power_ctrl *p_seq;
int id = 0;
int ret = 0;
if (!blic_dev) {
panel_err("blic_dev is null.\n");
return -EINVAL;
}
if (!np) {
panel_err("np is null.\n");
return -EINVAL;
}
panel = to_panel_drv(blic_dev);
if (!panel) {
panel_err("panel is null.\n");
return -EINVAL;
}
if (panel->nr_blic_dev >= PANEL_BLIC_MAX) {
pr_err("%s i2c_dev array is full. (%d)\n", __func__, panel->nr_blic_dev);
return -EINVAL;
}
temp = kzalloc(sizeof(*temp), GFP_KERNEL);
id = panel->nr_blic_dev;
temp->id = id;
temp->np = np;
temp->ops = &panel_blic_ops;
/* name */
ret = of_property_read_string(np, "name", &temp->name);
if (ret < 0) {
panel_err("name is null.\n");
ret = -EINVAL;
goto err;
}
/* i2c info */
ret |= of_property_read_u32_array(np, "i2c,match", temp->i2c_match, 3);
ret |= of_property_read_u32(np, "i2c,reg", &temp->i2c_reg);
if (ret < 0) {
panel_err("i2c is null.\n");
ret = -EINVAL;
goto err;
}
/* regulator info */
temp->regulator_node = of_parse_phandle(temp->np, "regulator", 0);
if (!temp->regulator_node) {
panel_err("i2c is null.\n");
ret = -EINVAL;
goto err;
}
temp->regulator_boot_on = of_property_read_bool(temp->regulator_node, "regulator-boot-on");
ret = of_property_read_string(temp->regulator_node, "regulator-name", &temp->regulator_name);
if (ret < 0) {
panel_err("regulator name is null.\n");
ret = -EINVAL;
goto err;
}
ret = of_property_read_string(temp->regulator_node, "regulator-desc-name", &temp->regulator_desc_name);
if (ret < 0) {
panel_err("regulator desc name is null.\n");
ret = -EINVAL;
goto err;
}
/* gpio info */
gpios_np = of_get_child_by_name(np, "gpios");
if (gpios_np) {
for_each_child_of_node(gpios_np, gpio_np) {
memset(&gpio, 0, sizeof(gpio));
if (of_get_panel_gpio(gpio_np, &gpio)) {
panel_err("failed to get gpio %s\n", gpio_np->name);
break;
}
p_gpio = panel_gpio_list_add(panel, &gpio);
if (!p_gpio) {
panel_err("failed to add gpio list %s\n", gpio.name);
break;
}
}
of_node_put(gpios_np);
} else
panel_err("'gpios' node not found\n");
/* power ctrl info */
power_np = of_get_child_by_name(np, "power_ctrl");
if (!power_np) {
panel_err("'power_ctrl' node not found\n");
ret = -EINVAL;
goto exit;
}
seq_np = of_get_child_by_name(power_np, "sequences");
if (!seq_np) {
panel_err("'sequences' node not found\n");
ret = -EINVAL;
goto exit_power;
}
for_each_property_of_node(seq_np, pp) {
if (!strcmp(pp->name, "name") || !strcmp(pp->name, "phandle"))
continue;
p_seq = kzalloc(sizeof(struct panel_power_ctrl), GFP_KERNEL);
if (!p_seq) {
ret = -ENOMEM;
goto exit_power;
}
p_seq->dev_name = np->name;
p_seq->name = pp->name;
ret = of_get_panel_power_ctrl(panel, seq_np, pp->name, p_seq);
if (ret < 0) {
panel_err("failed to get power_ctrl %s\n", pp->name);
break;
}
list_add_tail(&p_seq->head, &panel->power_ctrl_list);
panel_info("power_ctrl '%s' initialized\n", p_seq->name);
}
of_node_put(seq_np);
exit_power:
of_node_put(power_np);
exit:
#ifdef CONFIG_MCD_PANEL_I2C
if (temp->i2c_reg) {
temp->i2c_dev = panel_blic_find_i2c_drv(temp, panel);
if (!temp->i2c_dev) {
/*
* In most cases, it is not error.
* ex. Already i2c driver is occupied by a dualization device.
* The device doesn't need to probe anymore.
*/
panel_warn("cancel.(%s).\n", temp->name);
kfree(temp);
return ret;
}
temp->i2c_dev->use = true;
temp->dev = &temp->i2c_dev->client->dev;
}
#endif
panel->nr_blic_dev++;
memcpy(&blic_dev[id], temp, sizeof(struct panel_blic_dev));
kfree(temp);
blic_dev[id].id = id;
panel_info("(%s) is registered(nr: %d).\n", blic_dev[id].name, panel->nr_blic_dev);
return ret;
err:
kfree(temp);
return ret;
}
static int panel_blic_get_blic(struct panel_blic_dev *blic_dev, struct device_node *np)
{
struct device_node *blic_np;
int ret = 0;
int id = 0;
if (!blic_dev) {
panel_err("blic_dev is null.\n");
return -EINVAL;
}
if (!np) {
panel_err("blic node is null.\n");
return -EINVAL;
}
for_each_child_of_node(np, blic_np) {
if (of_get_panel_blic(blic_dev, blic_np)) {
panel_err("failed to get panel_blic %s\n", blic_np->name);
ret = -EINVAL;
break;
}
id++;
}
return ret;
}
__visible_for_testing int panel_blic_get_blic_regulator(struct panel_blic_dev *blic_dev)
{
struct panel_device *panel;
int ret = 0;
int i = 0;
if (!blic_dev) {
panel_err("blic_dev is null.\n");
return -EINVAL;
}
panel = to_panel_drv(blic_dev);
if (!panel) {
panel_err("panel is null.\n");
return -EINVAL;
}
for (i = 0; i < panel->nr_blic_dev; i++) {
ret = panel_blic_regulator_register(&blic_dev[i]);
if (ret)
panel_err("failed to register blic regulator(%d)(%s)\n",
i, blic_dev[i].regulator_name);
}
return ret;
}
int panel_blic_probe(struct panel_device *panel)
{
struct panel_blic_dev *blic_dev;
struct device_node *np;
int ret = 0;
if (!panel) {
panel_err("panel is null.\n");
return -EINVAL;
}
np = of_parse_phandle(panel->dev->of_node, BLIC_DT_NODE_NAME, 0);
if (!np) {
panel_err("failed to get blic DT node.\n");
return ret;
}
blic_dev = panel->blic_dev;
ret = panel_blic_get_blic(blic_dev, np);
if (ret) {
panel_err("failed to get blic.\n");
return ret;
}
ret = panel_blic_get_blic_regulator(blic_dev);
if (ret) {
panel_err("failed to get blic regulator.\n");
return ret;
}
return ret;
}
MODULE_DESCRIPTION("blic driver for panel");
MODULE_LICENSE("GPL");