blob: 7388adeba56c54eed323a1bcd4c2975b707b8489 [file] [log] [blame]
/*
* Copyright (c) 2020, The Linux Foundation. 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 version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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/device.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/kernel.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include <linux/rwlock.h>
#include <linux/leds.h>
struct vxr7200 {
struct device *dev;
struct device_node *host_node;
u8 i2c_addr;
int irq;
u32 vxr_3v3_en;
u32 led_5v_en;
u32 led_drive_en1;
u32 led_drive_en2;
u32 display_1v8_en;
u32 mipi_switch_1v8_en;
u32 display_res1;
bool gpioInit;
struct i2c_client *i2c_client;
struct regulator *vddio;
struct regulator *lab;
struct regulator *ibb;
bool power_on;
};
static bool dsi_way;
static int vxr7200_read(struct vxr7200 *pdata, u8 *reg, u8 *buf, u32 size)
{
struct i2c_client *client = pdata->i2c_client;
struct i2c_msg msg[2] = {
{
.addr = client->addr,
.flags = 0,
.len = 4,
.buf = reg,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = size,
.buf = buf,
}
};
if (i2c_transfer(client->adapter, msg, 2) != 2) {
pr_err("i2c read failed\n");
return -EIO;
}
return 0;
}
static int turnGpio(struct vxr7200 *pdata, int gpio, char *name, bool on)
{
int ret = -1;
pr_info("%s vxr7200 gpio:%d, name:%s, on:%d\n", __func__, gpio,
name, on);
if (!pdata->gpioInit) {
ret = gpio_request(gpio, name);
if (ret) {
pr_err("vxr7200 %s gpio request failed\n", name);
goto error;
}
}
if (on) {
ret = gpio_direction_output(gpio, 0);
if (ret) {
pr_err("vxr7200 gpio direction failed\n");
goto error;
}
gpio_set_value(gpio, 1);
msleep(20);
pr_debug("%s vxr7200 gpio:%d set to high\n", __func__, gpio);
} else {
ret = gpio_direction_output(gpio, 1);
if (ret) {
pr_err("vxr7200 gpio direction failed\n");
goto error;
}
gpio_set_value(gpio, 0);
msleep(20);
pr_debug("%s vxr7200 gpio:%d set to low\n", __func__, gpio);
}
return 0;
error:
return -EINVAL;
}
static void vxr7200_set_gpios(struct vxr7200 *pdata, bool turnOn)
{
int rc;
pr_debug("%s, turnOn:%d\n", __func__, turnOn);
if (pdata) {
rc = turnGpio(pdata, pdata->vxr_3v3_en, "vxr_3v3_en", turnOn);
if (rc)
goto gpio1Fail;
rc = turnGpio(pdata, pdata->display_res1,
"display_res1", turnOn);
if (rc)
goto gpio2Fail;
rc = turnGpio(pdata, pdata->display_1v8_en,
"disp_1v8_en", turnOn);
if (rc)
goto gpio3Fail;
rc = turnGpio(pdata, pdata->mipi_switch_1v8_en,
"mipi_switch_1v8_en", turnOn);
if (rc)
goto gpio4Fail;
rc = turnGpio(pdata, pdata->led_5v_en, "led_5v_en", turnOn);
if (rc)
goto gpio5Fail;
rc = turnGpio(pdata, pdata->led_drive_en1,
"led_drive_en1", turnOn);
if (rc)
goto gpio6Fail;
rc = turnGpio(pdata, pdata->led_drive_en2,
"led_drive_en2", turnOn);
if (rc)
goto gpio7Fail;
}
return;
gpio7Fail:
gpio_free(pdata->led_drive_en2);
gpio6Fail:
gpio_free(pdata->led_drive_en1);
gpio5Fail:
gpio_free(pdata->led_5v_en);
gpio4Fail:
gpio_free(pdata->mipi_switch_1v8_en);
gpio3Fail:
gpio_free(pdata->display_1v8_en);
gpio2Fail:
gpio_free(pdata->display_res1);
gpio1Fail:
gpio_free(pdata->vxr_3v3_en);
}
static int vxr7200_parse_dt(struct device *dev,
struct vxr7200 *pdata)
{
struct device_node *np = dev->of_node;
int rc = 0;
pdata->vxr_3v3_en =
of_get_named_gpio(np, "qcom,vxr_3v3_en", 0);
if (!gpio_is_valid(pdata->vxr_3v3_en)) {
pr_err("vxr_3v3_en gpio not specified\n");
rc = -EINVAL;
}
pdata->led_5v_en =
of_get_named_gpio(np, "qcom,led-5v-en-gpio", 0);
if (!gpio_is_valid(pdata->led_5v_en)) {
pr_err("led_5v_en gpio not specified\n");
rc = -EINVAL;
}
pdata->led_drive_en1 =
of_get_named_gpio(np, "qcom,led-driver-en1-gpio", 0);
if (!gpio_is_valid(pdata->led_drive_en1)) {
pr_err("led_drive_en1 gpio not specified\n");
rc = -EINVAL;
}
pdata->led_drive_en2 =
of_get_named_gpio(np, "qcom,led-driver-en2-gpio", 0);
if (!gpio_is_valid(pdata->led_drive_en2)) {
pr_err("led_drive_en2 gpio not specified\n");
rc = -EINVAL;
}
pdata->display_1v8_en =
of_get_named_gpio(np, "qcom,1p8-en-gpio", 0);
if (!gpio_is_valid(pdata->display_1v8_en)) {
pr_err("display_1v8_en gpio not specified\n");
rc = -EINVAL;
}
pdata->mipi_switch_1v8_en =
of_get_named_gpio(np, "qcom,switch-power-gpio", 0);
if (!gpio_is_valid(pdata->mipi_switch_1v8_en)) {
pr_err("mipi_switch_1v8_en gpio not specified\n");
rc = -EINVAL;
}
pdata->display_res1 =
of_get_named_gpio(np, "qcom,platform-reset-gpio", 0);
if (!gpio_is_valid(pdata->display_res1)) {
pr_err("display_res1 gpio not specified\n");
rc = -EINVAL;
}
if (!rc)
vxr7200_set_gpios(pdata, true);
if (!pdata->gpioInit)
pdata->gpioInit = true;
return rc;
}
static void vxr7200_display_pwr_enable_vregs(struct vxr7200 *pdata)
{
int rc = 0;
pdata->vddio = devm_regulator_get(pdata->dev, "pm660_l11");
rc = PTR_RET(pdata->vddio);
if (rc) {
pr_err("Failed to get pm660_l11 regulator %s\n", __func__);
goto vddio_fail;
}
rc = regulator_set_load(pdata->vddio, 62000);
if (rc < 0) {
pr_err("Load setting failed for vddio %s\n", __func__);
goto vddio_fail;
}
rc = regulator_enable(pdata->vddio);
msleep(20);
if (rc) {
pr_err("enable failed for vddio, rc=%d %s\n", rc, __func__);
goto vddio_fail;
}
pdata->lab = devm_regulator_get(pdata->dev, "lcdb_ldo");
rc = PTR_RET(pdata->lab);
if (rc) {
pr_err("Failed to get lcdb_ldo_vreg regulator %s\n", __func__);
goto lab_fail;
}
rc = regulator_set_load(pdata->lab, 100000);
if (rc < 0) {
pr_err("Load Setting failed for lab %s\n", __func__);
goto lab_fail;
}
rc = regulator_enable(pdata->lab);
if (rc) {
pr_err("enable failed for lab, rc=%d %s\n", rc, __func__);
goto lab_fail;
}
msleep(20);
pdata->ibb = devm_regulator_get(pdata->dev, "lcdb_ncp");
rc = PTR_RET(pdata->ibb);
if (rc) {
pr_err("Failed to get lcdb_ncp_vreg regulator %s\n", __func__);
goto ibb_fail;
}
rc = regulator_set_load(pdata->ibb, 100000);
if (rc < 0) {
pr_err("Load Setting failed for ibb %s\n", __func__);
goto ibb_fail;
}
rc = regulator_enable(pdata->ibb);
if (rc) {
pr_err("enable failed for ibb, rc=%d %s\n", rc, __func__);
goto ibb_fail;
}
msleep(20);
return;
ibb_fail:
devm_regulator_put(pdata->ibb);
(void)regulator_set_load(pdata->ibb, 100);
(void)regulator_set_voltage(pdata->ibb, 0, 6000000);
lab_fail:
(void)regulator_set_voltage(pdata->lab, 0, 6000000);
(void)regulator_set_load(pdata->lab, 100);
devm_regulator_put(pdata->lab);
vddio_fail:
(void)regulator_set_load(pdata->vddio, 100);
(void)regulator_set_voltage(pdata->vddio, 0, 1800000);
devm_regulator_put(pdata->vddio);
}
static int vxr7200_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int rc;
struct vxr7200 *pdata;
u8 reg[4] = {0x00, 0x20, 0x01, 0x60};
u8 buf[4] = {0x00, 0x0, 0x0, 0x0};
if (!client || !client->dev.of_node) {
pr_err("%s invalid input\n", __func__);
return -EINVAL;
}
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
pr_err("%s device doesn't support I2C\n", __func__);
return -ENODEV;
}
if (dsi_way)
return -EINVAL;
pdata = devm_kzalloc(&client->dev,
sizeof(struct vxr7200), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->gpioInit = false;
rc = vxr7200_parse_dt(&client->dev, pdata);
if (rc) {
pr_err("%s failed to parse device tree\n", __func__);
goto err_dt_parse;
}
pdata->dev = &client->dev;
pdata->i2c_client = client;
vxr7200_display_pwr_enable_vregs(pdata);
i2c_set_clientdata(client, pdata);
dev_set_drvdata(&client->dev, pdata);
//vxr7200_write(pdata, 0x0A, 0x02);//Enable 4-lane DP
vxr7200_read(pdata, reg, buf, 4);//Enable 4-lane DP
err_dt_parse:
devm_kfree(&client->dev, pdata);
return rc;
}
static int vxr7200_remove(struct i2c_client *client)
{
struct vxr7200 *pdata = i2c_get_clientdata(client);
if (pdata)
devm_kfree(&client->dev, pdata);
return 0;
}
static void vxr7200_shutdown(struct i2c_client *client)
{
dev_info(&(client->dev), "shutdown");
}
static int vxr7200_pm_freeze(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct vxr7200 *pdata = i2c_get_clientdata(client);
dev_info(dev, "freeze");
vxr7200_set_gpios(pdata, false);
return 0;
}
static int vxr7200_pm_restore(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct vxr7200 *pdata = i2c_get_clientdata(client);
dev_info(dev, "restore");
vxr7200_set_gpios(pdata, true);
return 0;
}
static int vxr7200_pm_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct vxr7200 *pdata = i2c_get_clientdata(client);
dev_info(dev, "suspend");
vxr7200_set_gpios(pdata, false);
return 0;
}
static int vxr7200_pm_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct vxr7200 *pdata = i2c_get_clientdata(client);
dev_info(dev, "resume");
vxr7200_set_gpios(pdata, true);
return 0;
}
static const struct dev_pm_ops vxr7200_dev_pm_ops = {
.suspend = vxr7200_pm_suspend,
.resume = vxr7200_pm_resume,
.freeze = vxr7200_pm_freeze,
.restore = vxr7200_pm_restore,
.thaw = vxr7200_pm_restore,
.poweroff = vxr7200_pm_suspend,
};
static const struct i2c_device_id vxr7200_id_table[] = {
{"vxr7200", 0},
{}
};
static struct i2c_driver vxr7200_i2c_driver = {
.probe = vxr7200_probe,
.remove = vxr7200_remove,
.shutdown = vxr7200_shutdown,
.driver = {
.name = "vxr7200",
.owner = THIS_MODULE,
.pm = &vxr7200_dev_pm_ops,
},
.id_table = vxr7200_id_table,
};
static int __init vxr7200_init(void)
{
char *cmdline;
cmdline = strnstr(boot_command_line,
"msm_drm.dsi_display0=dsi_sim_vid_display",
strlen(boot_command_line));
if (cmdline) {
pr_debug("%s DSI SIM mode, going to dp init cmdline:%s\n",
__func__, cmdline);
dsi_way = false;
} else {
pr_debug("%s DSI WAY, going to dsi init cmdline:%s\n",
__func__, cmdline);
dsi_way = true;
}
return 0;
}
device_initcall(vxr7200_init);
module_i2c_driver(vxr7200_i2c_driver);
MODULE_DEVICE_TABLE(i2c, vxr7200_id_table);
MODULE_DESCRIPTION("VXR7200 DP2DSI Bridge");