auxdisplay/ptn3460: Add driver for NXP PTN3460
authorSean Paul <seanpaul@chromium.org>
Sat, 4 Aug 2012 16:12:33 +0000 (09:12 -0700)
committerGerrit <chrome-bot@google.com>
Tue, 7 Aug 2012 16:08:43 +0000 (09:08 -0700)
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 <seanpaul@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/29239
Reviewed-by: Olof Johansson <olofj@chromium.org>
Documentation/devicetree/bindings/video/ptn3460.txt [new file with mode: 0644]
drivers/auxdisplay/Kconfig
drivers/auxdisplay/Makefile
drivers/auxdisplay/ptn3460.c [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/video/ptn3460.txt b/Documentation/devicetree/bindings/video/ptn3460.txt
new file mode 100644 (file)
index 0000000..2465a60
--- /dev/null
@@ -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>;
+       };
index c07e725..480ef1f 100644 (file)
@@ -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
index 8a8936a..eb9ce7a 100644 (file)
@@ -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 (file)
index 0000000..5e5b2b7
--- /dev/null
@@ -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 <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+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);