From: Sean Paul Date: Sat, 4 Aug 2012 16:12:33 +0000 (-0700) Subject: auxdisplay/ptn3460: Add driver for NXP PTN3460 X-Git-Url: http://git.cascardo.eti.br/?a=commitdiff_plain;h=57353d9950d514cf2930b636f28192d632079ea6;p=cascardo%2Flinux.git auxdisplay/ptn3460: Add driver for NXP PTN3460 Add a new driver to handle the NXP PTN3460 DP/LVDS bridge chip. The driver currently gets its platform data from device tree. In its current state, it only supports power up/down using PD_N & RST_N pins. BUG=chrome-os-partner:10829 BUG=chrome-os-partner:11158 BUG=chrome-os-partner:12046 TEST=Tested on snow, screen came up on power-on and resume Change-Id: Ic9c7884edfe514ae5ab646b172c4c47352d46276 Signed-off-by: Sean Paul Reviewed-on: https://gerrit.chromium.org/gerrit/29239 Reviewed-by: Olof Johansson --- diff --git a/Documentation/devicetree/bindings/video/ptn3460.txt b/Documentation/devicetree/bindings/video/ptn3460.txt new file mode 100644 index 000000000000..2465a6075ca5 --- /dev/null +++ b/Documentation/devicetree/bindings/video/ptn3460.txt @@ -0,0 +1,15 @@ +ptn3460-bridge bindings + +Required properties: + - compatible: "nxp,ptn3460" + - reg: i2c address of the bridge + - powerdown-gpio: OF device-tree gpio specification + - reset-gpio: OF device-tree gpio specification + +Example: + ptn3460-bridge@20 { + compatible = "nxp,ptn3460"; + reg = <0x20>; + powerdown-gpio = <&gpy2 5 1 0 0>; + reset-gpio = <&gpx1 5 1 0 0>; + }; diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index c07e725ea93d..480ef1f789ab 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -119,4 +119,9 @@ config CFAG12864B_RATE If you compile this as a module, you can still override this value using the module parameters. +config PTN3460 + tristate "PTN3460 DP/LVDS bridge" + ---help--- + Adds the driver for the NXP PTN3460 DP/LVDS bridge chip. + endif # AUXDISPLAY diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile index 8a8936a468b9..eb9ce7ab45a6 100644 --- a/drivers/auxdisplay/Makefile +++ b/drivers/auxdisplay/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_KS0108) += ks0108.o obj-$(CONFIG_CFAG12864B) += cfag12864b.o cfag12864bfb.o +obj-$(CONFIG_PTN3460) += ptn3460.o diff --git a/drivers/auxdisplay/ptn3460.c b/drivers/auxdisplay/ptn3460.c new file mode 100644 index 000000000000..5e5b2b76765b --- /dev/null +++ b/drivers/auxdisplay/ptn3460.c @@ -0,0 +1,155 @@ +/* + * NXP PTN3460 DP/LVDS bridge driver + * + * Copyright (C) 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include +#include +#include +#include + +struct ptn3460_platform_data { + u8 addr; + int gpio_pd_n; + int gpio_rst_n; + int free_me; +}; + +static int ptn3460_init_platform_data_from_dt(struct device *dev) +{ + struct ptn3460_platform_data *pd; + + dev->platform_data = devm_kzalloc(dev, + sizeof(struct ptn3460_platform_data), + GFP_KERNEL); + if (!dev->platform_data) { + dev_err(dev, "Can't allocate platform data\n"); + return -ENOMEM; + } + pd = dev->platform_data; + + /* Fill platform data with device tree data */ + pd->gpio_pd_n = of_get_named_gpio(dev->of_node, "powerdown-gpio", 0); + pd->gpio_rst_n = of_get_named_gpio(dev->of_node, "reset-gpio", 0); + pd->free_me = 1; /* Mark this to be freed later */ + + return 0; + +} + +static int ptn3460_power_up(struct ptn3460_platform_data *pd) +{ + if (pd->gpio_pd_n > 0) + gpio_set_value(pd->gpio_pd_n, 1); + + if (pd->gpio_rst_n > 0) { + gpio_set_value(pd->gpio_rst_n, 0); + udelay(10); + gpio_set_value(pd->gpio_rst_n, 1); + } + return 0; +} + +static int ptn3460_power_down(struct ptn3460_platform_data *pd) +{ + if (pd->gpio_rst_n > 0) + gpio_set_value(pd->gpio_rst_n, 1); + + if (pd->gpio_pd_n > 0) + gpio_set_value(pd->gpio_pd_n, 0); + + return 0; +} + +int ptn3460_suspend(struct device *dev) +{ + return ptn3460_power_down(dev->platform_data); +} + +int ptn3460_resume(struct device *dev) +{ + return ptn3460_power_up(dev->platform_data); +} + +int ptn3460_probe(struct i2c_client *client, const struct i2c_device_id *device) +{ + struct device *dev = &client->dev; + struct ptn3460_platform_data *pd; + int ret; + + if (!dev->platform_data) { + ret = ptn3460_init_platform_data_from_dt(dev); + if (ret) + return ret; + } + pd = dev->platform_data; + + if (pd->gpio_pd_n > 0) { + ret = gpio_request_one(pd->gpio_pd_n, GPIOF_OUT_INIT_HIGH, + "PTN3460_PD_N"); + if (ret) + goto err_pd; + } + if (pd->gpio_rst_n > 0) { + /* + * Request the reset pin low to avoid the bridge being + * initialized prematurely + */ + ret = gpio_request_one(pd->gpio_rst_n, GPIOF_OUT_INIT_LOW, + "PTN3460_RST_N"); + if (ret) + goto err_pd; + } + + ret = ptn3460_power_up(pd); + if (ret) + goto err_pd; + + return 0; + +err_pd: + if (pd->free_me) + devm_kfree(dev, pd); + return ret; +} + +int ptn3460_remove(struct i2c_client *client) +{ + struct ptn3460_platform_data *pd = client->dev.platform_data; + if (pd && pd->free_me) + devm_kfree(&client->dev, pd); + return 0; +} + +static const struct i2c_device_id ptn3460_ids[] = { + { "ptn3460", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, ptn3460_ids); + +static SIMPLE_DEV_PM_OPS(ptn3460_pm_ops, ptn3460_suspend, ptn3460_resume); + +static struct i2c_driver ptn3460_driver = { + .id_table = ptn3460_ids, + .probe = ptn3460_probe, + .remove = ptn3460_remove, + .driver = { + .name = "ptn3460", + .owner = THIS_MODULE, + .pm = &ptn3460_pm_ops, + }, +}; +module_i2c_driver(ptn3460_driver);