// 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 #include #include #include #include #include #include #include #include #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");