OMAPDSS: TPO-TD03MTEA1: add vdd regulator
[pandora-kernel.git] / drivers / video / omap2 / displays / panel-tpo-td043mtea1.c
index 2462b9e..0c6aca1 100644 (file)
                        TPO_R03_EN_PRE_CHARGE | TPO_R03_SOFTWARE_CTL)
 
 static const u16 tpo_td043_def_gamma[12] = {
-       106, 200, 289, 375, 460, 543, 625, 705, 785, 864, 942, 1020
+       105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023
 };
 
 struct tpo_td043_device {
        struct spi_device *spi;
        struct regulator *vcc_reg;
+       struct regulator *vdd_reg;
+       int nreset_gpio;
        u16 gamma[12];
        u32 mode;
        u32 hmirror:1;
        u32 vmirror:1;
+       u32 powered_on:1;
+       u32 spi_suspended:1;
+       u32 power_on_resume:1;
 };
 
 static int tpo_td043_write(struct spi_device *spi, u8 addr, u8 data)
@@ -256,37 +261,30 @@ static const struct omap_video_timings tpo_td043_timings = {
 
        .pixel_clock    = 36000,
 
+       /* note:
+        * hbp+hsw must be 215
+        * if vbp+vsw < 32, the panel tears at the bottom
+        * if vbp+vsw > 35, it wraps from the top */
        .hsw            = 1,
-       .hfp            = 68,
+       .hfp            = 150,
        .hbp            = 214,
 
        .vsw            = 1,
-       .vfp            = 39,
+       .vfp            = 0,
        .vbp            = 34,
 };
 
-static int tpo_td043_power_on(struct omap_dss_device *dssdev)
+static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043)
 {
-       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
-       int nreset_gpio = dssdev->reset_gpio;
-       int r;
+       int nreset_gpio = tpo_td043->nreset_gpio;
 
-       if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+       if (tpo_td043->powered_on)
                return 0;
 
-       r = omapdss_dpi_display_enable(dssdev);
-       if (r)
-               goto err0;
-
-       if (dssdev->platform_enable) {
-               r = dssdev->platform_enable(dssdev);
-               if (r)
-                       goto err1;
-       }
-
        regulator_enable(tpo_td043->vcc_reg);
+       regulator_enable(tpo_td043->vdd_reg);
 
-       /* wait for power up */
+       /* wait for regulator to stabilize */
        msleep(160);
 
        if (gpio_is_valid(nreset_gpio))
@@ -301,19 +299,15 @@ static int tpo_td043_power_on(struct omap_dss_device *dssdev)
                        tpo_td043->vmirror);
        tpo_td043_write_gamma(tpo_td043->spi, tpo_td043->gamma);
 
+       tpo_td043->powered_on = 1;
        return 0;
-err1:
-       omapdss_dpi_display_disable(dssdev);
-err0:
-       return r;
 }
 
-static void tpo_td043_power_off(struct omap_dss_device *dssdev)
+static void tpo_td043_power_off(struct tpo_td043_device *tpo_td043)
 {
-       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
-       int nreset_gpio = dssdev->reset_gpio;
+       int nreset_gpio = tpo_td043->nreset_gpio;
 
-       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+       if (!tpo_td043->powered_on)
                return;
 
        tpo_td043_write(tpo_td043->spi, 3,
@@ -327,56 +321,97 @@ static void tpo_td043_power_off(struct omap_dss_device *dssdev)
 
        tpo_td043_write(tpo_td043->spi, 3, TPO_R03_VAL_STANDBY);
 
+       regulator_disable(tpo_td043->vdd_reg);
        regulator_disable(tpo_td043->vcc_reg);
 
+       tpo_td043->powered_on = 0;
+}
+
+static int tpo_td043_enable_dss(struct omap_dss_device *dssdev)
+{
+       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
+       int r;
+
+       if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+               return 0;
+
+       r = omapdss_dpi_display_enable(dssdev);
+       if (r)
+               goto err0;
+
+       if (dssdev->platform_enable) {
+               r = dssdev->platform_enable(dssdev);
+               if (r)
+                       goto err1;
+       }
+
+       /* 
+        * If we are resuming from system suspend, SPI clocks might not be
+        * enabled yet, so we'll program the LCD from SPI PM resume callback.
+        */
+       if (!tpo_td043->spi_suspended) {
+               r = tpo_td043_power_on(tpo_td043);
+               if (r)
+                       goto err1;
+       }
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       return 0;
+err1:
+       omapdss_dpi_display_disable(dssdev);
+err0:
+       return r;
+}
+
+static void tpo_td043_disable_dss(struct omap_dss_device *dssdev)
+{
+       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+               return;
+
        if (dssdev->platform_disable)
                dssdev->platform_disable(dssdev);
 
        omapdss_dpi_display_disable(dssdev);
+
+       if (!tpo_td043->spi_suspended)
+               tpo_td043_power_off(tpo_td043);
 }
 
 static int tpo_td043_enable(struct omap_dss_device *dssdev)
 {
-       int ret;
-
        dev_dbg(&dssdev->dev, "enable\n");
 
-       ret = tpo_td043_power_on(dssdev);
-       if (ret)
-               return ret;
-
-       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-       return 0;
+       return tpo_td043_enable_dss(dssdev);
 }
 
 static void tpo_td043_disable(struct omap_dss_device *dssdev)
 {
        dev_dbg(&dssdev->dev, "disable\n");
 
-       tpo_td043_power_off(dssdev);
+       tpo_td043_disable_dss(dssdev);
 
        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 }
 
 static int tpo_td043_suspend(struct omap_dss_device *dssdev)
 {
-       tpo_td043_power_off(dssdev);
+       dev_dbg(&dssdev->dev, "suspend\n");
+
+       tpo_td043_disable_dss(dssdev);
+
        dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+
        return 0;
 }
 
 static int tpo_td043_resume(struct omap_dss_device *dssdev)
 {
-       int r = 0;
-
-       r = tpo_td043_power_on(dssdev);
-       if (r)
-               return r;
+       dev_dbg(&dssdev->dev, "resume\n");
 
-       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-       return 0;
+       return tpo_td043_enable_dss(dssdev);
 }
 
 static int tpo_td043_probe(struct omap_dss_device *dssdev)
@@ -407,6 +442,13 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev)
                goto fail_regulator;
        }
 
+       tpo_td043->vdd_reg = regulator_get(&dssdev->dev, "vdd");
+       if (IS_ERR(tpo_td043->vdd_reg)) {
+               dev_err(&dssdev->dev, "failed to get LCD VDD regulator\n");
+               ret = PTR_ERR(tpo_td043->vdd_reg);
+               goto fail_vdd_regulator;
+       }
+
        if (gpio_is_valid(nreset_gpio)) {
                ret = gpio_request(nreset_gpio, "lcd reset");
                if (ret < 0) {
@@ -430,6 +472,8 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev)
 fail_gpio_direction:
        gpio_free(nreset_gpio);
 fail_gpio_req:
+       regulator_put(tpo_td043->vdd_reg);
+fail_vdd_regulator:
        regulator_put(tpo_td043->vcc_reg);
 fail_regulator:
        kfree(tpo_td043);
@@ -444,11 +488,24 @@ static void tpo_td043_remove(struct omap_dss_device *dssdev)
        dev_dbg(&dssdev->dev, "remove\n");
 
        sysfs_remove_group(&dssdev->dev.kobj, &tpo_td043_attr_group);
+       regulator_put(tpo_td043->vdd_reg);
        regulator_put(tpo_td043->vcc_reg);
        if (gpio_is_valid(nreset_gpio))
                gpio_free(nreset_gpio);
 }
 
+static void tpo_td043_set_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       dpi_set_timings(dssdev, timings);
+}
+
+static int tpo_td043_check_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       return dpi_check_timings(dssdev, timings);
+}
+
 static struct omap_dss_driver tpo_td043_driver = {
        .probe          = tpo_td043_probe,
        .remove         = tpo_td043_remove,
@@ -460,6 +517,9 @@ static struct omap_dss_driver tpo_td043_driver = {
        .set_mirror     = tpo_td043_set_hmirror,
        .get_mirror     = tpo_td043_get_hmirror,
 
+       .set_timings    = tpo_td043_set_timings,
+       .check_timings  = tpo_td043_check_timings,
+
        .driver         = {
                .name   = "tpo_td043mtea1_panel",
                .owner  = THIS_MODULE,
@@ -491,6 +551,7 @@ static int tpo_td043_spi_probe(struct spi_device *spi)
                return -ENOMEM;
 
        tpo_td043->spi = spi;
+       tpo_td043->nreset_gpio = dssdev->reset_gpio;
        dev_set_drvdata(&spi->dev, tpo_td043);
        dev_set_drvdata(&dssdev->dev, tpo_td043);
 
@@ -509,11 +570,47 @@ static int __devexit tpo_td043_spi_remove(struct spi_device *spi)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int tpo_td043_spi_suspend(struct device *dev)
+{
+       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "tpo_td043_spi_suspend, tpo %p\n", tpo_td043);
+
+       tpo_td043->power_on_resume = tpo_td043->powered_on;
+       tpo_td043_power_off(tpo_td043);
+       tpo_td043->spi_suspended = 1;
+
+       return 0;
+}
+
+static int tpo_td043_spi_resume(struct device *dev)
+{
+       struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
+       int ret;
+
+       dev_dbg(dev, "tpo_td043_spi_resume\n");
+
+       if (tpo_td043->power_on_resume) {
+               ret = tpo_td043_power_on(tpo_td043);
+               if (ret)
+                       return ret;
+       }
+       tpo_td043->spi_suspended = 0;
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm,
+       tpo_td043_spi_suspend, tpo_td043_spi_resume);
+
 static struct spi_driver tpo_td043_spi_driver = {
        .driver = {
                .name   = "tpo_td043mtea1_panel_spi",
                .bus    = &spi_bus_type,
                .owner  = THIS_MODULE,
+               .pm     = &tpo_td043_spi_pm,
        },
        .probe  = tpo_td043_spi_probe,
        .remove = __devexit_p(tpo_td043_spi_remove),