blob: 21d54b4550508102f9e8e69a9dac8647fb51d241 [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>
#include "vxr7200.h"
enum power_state {
POWER_STATE_POWERKEY = 0,
POWER_STATE_ENABLE = 1,
};
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;
struct i2c_client *i2c_client;
struct regulator *vddio;
struct regulator *lab;
struct regulator *ibb;
bool power_on;
bool enable;
bool usb_plugged;
enum power_state state;
};
static bool dsi_way;
static struct vxr7200 *pdata;
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("%s i2c read failed\n", __func__);
return -EIO;
}
return 0;
}
static void turn_gpio(struct vxr7200 *pdata, int gpio, bool on)
{
pr_debug("%s vxr7200_turn_gpio gpio:%d, on:%d\n", __func__, gpio, on);
if (on) {
gpio_direction_output(gpio, 0);
gpio_set_value(gpio, 1);
pr_debug("%s vxr7200 gpio:%d set to high\n", __func__, gpio);
} else {
gpio_direction_output(gpio, 1);
gpio_set_value(gpio, 0);
pr_debug("%s vxr7200 gpio:%d set to low\n", __func__, gpio);
}
}
static void vxr7200_set_gpios(struct vxr7200 *pdata, bool turn_on)
{
pr_debug("%s, turn_on:%d\n", __func__, turn_on);
if (pdata) {
if (pdata->state == POWER_STATE_POWERKEY) {
turn_gpio(pdata, pdata->vxr_3v3_en, turn_on);
turn_gpio(pdata, pdata->display_res1, turn_on);
turn_gpio(pdata, pdata->display_1v8_en, turn_on);
turn_gpio(pdata, pdata->mipi_switch_1v8_en, turn_on);
if (!pdata->enable && turn_on)
return;
}
turn_gpio(pdata, pdata->led_5v_en, turn_on);
turn_gpio(pdata, pdata->led_drive_en1, turn_on);
turn_gpio(pdata, pdata->led_drive_en2, turn_on);
}
return;
}
static ssize_t vxr7200_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
struct i2c_client *client = to_i2c_client(dev);
struct vxr7200 *pdata = i2c_get_clientdata(client);
if (pdata->enable)
ret = scnprintf(buf, PAGE_SIZE, "display on\n");
else
ret = scnprintf(buf, PAGE_SIZE, "display off\n");
return ret;
}
static ssize_t vxr7200_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct vxr7200 *pdata = i2c_get_clientdata(client);
long val;
int ret;
ret = kstrtol(buf, 0, &val);
pr_debug("%s, count:%d val:%lx, buf:%s\n",
__func__, (int)count, val, buf);
if (val == 0x1) {
pr_debug("%s Turning on Display\n", __func__);
pdata->enable = true;
pdata->state = POWER_STATE_ENABLE;
vxr7200_set_gpios(pdata, true);
} else if (val == 0x0) {
pr_debug("%s Turning off Display\n",
__func__);
pdata->state = POWER_STATE_ENABLE;
pdata->enable = false;
vxr7200_set_gpios(pdata, false);
} else
pr_err("%s Invalid value. Valid vals(1-on,0-off)\n",
__func__);
return count;
}
static DEVICE_ATTR_RW(vxr7200_enable);
static struct attribute *vxr7200_fs_attrs[] = {
&dev_attr_vxr7200_enable.attr,
NULL
};
static struct attribute_group vxr7200_fs_attr_group = {
.attrs = vxr7200_fs_attrs,
};
void vxr7200_usb_event(bool cable_plug)
{
pr_debug("%s, cable_plug:%d\n", __func__, cable_plug);
if (pdata) {
pdata->state = POWER_STATE_POWERKEY;
if (cable_plug) {
pdata->usb_plugged = true;
vxr7200_set_gpios(pdata, true);
} else {
pdata->usb_plugged = false;
vxr7200_set_gpios(pdata, false);
}
}
}
EXPORT_SYMBOL(vxr7200_usb_event);
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("vxr7200_3v3_en gpio not specified\n");
rc = -EINVAL;
}
if (gpio_request(pdata->vxr_3v3_en, "vxr_3v3_en")) {
pr_err("vxr7200_3v3_en request gpio failed\n");
rc = -EINVAL;
goto gpio1Fail;
}
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("vxr7200_led_5v_en gpio not specified\n");
rc = -EINVAL;
}
if (gpio_request(pdata->led_5v_en, "led_5v_en")) {
pr_err("vxr7200_led_5v_en request gpio failed\n");
rc = -EINVAL;
goto gpio2Fail;
}
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("vxr7200_led_drive_en1 gpio not specified\n");
rc = -EINVAL;
}
if (gpio_request(pdata->led_drive_en1, "led_drive_en1")) {
pr_err("vxr7200_led_drive_en1 request gpio failed\n");
rc = -EINVAL;
goto gpio3Fail;
}
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("vxr7200_led_drive_en2 gpio not specified\n");
rc = -EINVAL;
}
if (gpio_request(pdata->led_drive_en2, "led_drive_en2")) {
pr_err("vxr7200_led_drive_en2 request gpio failed\n");
rc = -EINVAL;
goto gpio4Fail;
}
pdata->display_1v8_en =
of_get_named_gpio(np, "qcom,1p8-en-gpio", 0);
if (!gpio_is_valid(pdata->display_1v8_en)) {
pr_err("vxr7200_display_1v8_en gpio not specified\n");
rc = -EINVAL;
}
if (gpio_request(pdata->display_1v8_en, "display_1v8_en")) {
pr_err("vxr7200_display_1v8_en request gpio failed\n");
rc = -EINVAL;
goto gpio5Fail;
}
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("vxr7200_mipi_switch_1v8_en gpio not specified\n");
rc = -EINVAL;
}
if (gpio_request(pdata->mipi_switch_1v8_en, "mipi_switch_1v8_en")) {
pr_err("vxr7200_mipi_switch_1v8_en request gpio failed\n");
rc = -EINVAL;
goto gpio6Fail;
}
pdata->display_res1 =
of_get_named_gpio(np, "qcom,platform-reset-gpio", 0);
if (!gpio_is_valid(pdata->display_res1)) {
pr_err("vxr7200_display_res1 gpio not specified\n");
rc = -EINVAL;
}
if (gpio_request(pdata->display_res1, "display_res1")) {
pr_err("vxr7200_display_res1 request gpio failed\n");
rc = -EINVAL;
goto gpio7Fail;
}
return rc;
gpio7Fail:
gpio_free(pdata->display_res1);
gpio6Fail:
gpio_free(pdata->mipi_switch_1v8_en);
gpio5Fail:
gpio_free(pdata->display_1v8_en);
gpio4Fail:
gpio_free(pdata->led_drive_en2);
gpio3Fail:
gpio_free(pdata->led_drive_en1);
gpio2Fail:
gpio_free(pdata->led_5v_en);
gpio1Fail:
gpio_free(pdata->vxr_3v3_en);
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;
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->state = POWER_STATE_POWERKEY;
pdata->enable = true;
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;
rc = sysfs_create_group(&pdata->dev->kobj, &vxr7200_fs_attr_group);
if (rc) {
pr_err("%s unable to register vxr7200 sysfs nodes\n",
__func__);
goto err_dt_parse;
}
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
return rc;
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) {
sysfs_remove_group(&pdata->dev->kobj, &vxr7200_fs_attr_group);
devm_kfree(&client->dev, pdata);
}
return 0;
}
static void vxr7200_shutdown(struct i2c_client *client)
{
dev_info(&(client->dev), "shutdown");
}
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");
pdata->state = POWER_STATE_POWERKEY;
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");
pdata->state = POWER_STATE_POWERKEY;
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,
.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");