CHROMIUM: drm/exynos: Remove panel_ops->commit
[cascardo/linux.git] / drivers / gpu / drm / exynos / exynos_dp_core.c
index c409939..73d32c8 100644 (file)
  * option) any later version.
  */
 
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/clk.h>
+#include <linux/gpio.h>
 #include <linux/io.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 
 #include <video/exynos_dp.h>
 #include "exynos_drm_drv.h"
+#include "exynos_drm_display.h"
 
 #include <plat/cpu.h>
+#include <plat/gpio-cfg.h>
 
 #include "exynos_dp_core.h"
 
@@ -49,6 +55,9 @@ static int exynos_dp_detect_hpd(struct exynos_dp_device *dp)
 {
        int timeout_loop = 0;
 
+       if (gpio_is_valid(dp->hpd_gpio))
+               return !gpio_get_value(dp->hpd_gpio);
+
        while (exynos_dp_get_plug_in_status(dp) != 0) {
                timeout_loop++;
                if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
@@ -495,6 +504,9 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
                return ret;
 
        lane_count = dp->link_train.lane_count;
+       if (lane_count == 0)
+               return 0;
+
        adjust_request = link_status + 4;
 
        if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) {
@@ -813,7 +825,6 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp)
 {
        int retval = 0;
        int timeout_loop = 0;
-       int done_count = 0;
 
        exynos_dp_config_video_slave_mode(dp);
 
@@ -826,14 +837,14 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp)
 
        for (;;) {
                timeout_loop++;
-               if (exynos_dp_is_slave_video_stream_clock_on(dp) == 0)
+               if (!exynos_dp_is_slave_video_stream_clock_on(dp))
                        break;
                if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
-                       dev_err(dp->dev, "Timeout of video streamclk ok\n");
+                       dev_err(dp->dev, "Wait for stream clock timed out\n");
                        return -ETIMEDOUT;
                }
 
-               mdelay(100);
+               usleep_range(1000, 5000);
        }
 
        /* Set to use the register calculated M/N video */
@@ -855,19 +866,15 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp)
 
        for (;;) {
                timeout_loop++;
-               if (exynos_dp_is_video_stream_on(dp) == 0) {
-                       done_count++;
-                       if (done_count > 10)
-                               break;
-               } else if (done_count) {
-                       done_count = 0;
-               }
+               if (!exynos_dp_is_video_stream_on(dp))
+                       break;
+
                if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
-                       dev_err(dp->dev, "Timeout of video streamclk ok\n");
+                       dev_err(dp->dev, "Wait for video stream timed out\n");
                        return -ETIMEDOUT;
                }
 
-               mdelay(100);
+               usleep_range(1000, 5000);
        }
 
        if (retval != 0)
@@ -909,12 +916,9 @@ static irqreturn_t exynos_dp_irq_handler(int irq, void *arg)
        irq_type = exynos_dp_get_irq_type(dp);
        switch (irq_type) {
        case DP_IRQ_TYPE_HP_CABLE_IN:
-               dev_dbg(dp->dev, "Received irq - cable in\n");
-               schedule_work(&dp->hotplug_work);
-               exynos_dp_clear_hotplug_interrupts(dp);
-               break;
        case DP_IRQ_TYPE_HP_CABLE_OUT:
-               dev_dbg(dp->dev, "Received irq - cable out\n");
+               dev_dbg(dp->dev, "Received irq - type=%d\n", irq_type);
+               schedule_work(&dp->hotplug_work);
                exynos_dp_clear_hotplug_interrupts(dp);
                break;
        case DP_IRQ_TYPE_HP_CHANGE:
@@ -936,15 +940,15 @@ static irqreturn_t exynos_dp_irq_handler(int irq, void *arg)
 static void exynos_dp_hotplug(struct work_struct *work)
 {
        struct exynos_dp_device *dp;
-       int ret;
 
        dp = container_of(work, struct exynos_dp_device, hotplug_work);
 
-       ret = exynos_dp_detect_hpd(dp);
-       if (ret) {
-               /* Cable has been disconnected, we're done */
-               return;
-       }
+       drm_helper_hpd_irq_event(dp->drm_dev);
+}
+
+static void exynos_dp_train_link(struct exynos_dp_device *dp)
+{
+       int ret;
 
        ret = exynos_dp_handle_edid(dp);
        if (ret) {
@@ -954,7 +958,7 @@ static void exynos_dp_hotplug(struct work_struct *work)
 
        if (dp->training_type == SW_LINK_TRAINING)
                ret = exynos_dp_set_link_train(dp, dp->video_info->lane_count,
-                                               dp->video_info->link_rate);
+                       dp->video_info->link_rate);
        else
                ret = exynos_dp_set_hw_link_train(dp,
                        dp->video_info->lane_count, dp->video_info->link_rate);
@@ -971,9 +975,121 @@ static void exynos_dp_hotplug(struct work_struct *work)
        exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate);
 
        exynos_dp_init_video(dp);
+}
+
+static int exynos_dp_power_off(struct exynos_dp_device *dp)
+{
+       if (!dp->enabled)
+               return 0;
+
+       dp->enabled = false;
+       exynos_dp_disable_hpd(dp);
+
+       if (work_pending(&dp->hotplug_work))
+               flush_work_sync(&dp->hotplug_work);
+
+       if (dp->phy_ops.phy_exit)
+               dp->phy_ops.phy_exit();
+
+       clk_disable(dp->clock);
+       return 0;
+}
+
+static int exynos_dp_power_on(struct exynos_dp_device *dp)
+{
+       if (dp->enabled)
+               return 0;
+
+       if (dp->phy_ops.phy_init)
+               dp->phy_ops.phy_init();
+
+       clk_enable(dp->clock);
+
+       exynos_dp_init_dp(dp);
+
+       /*
+        * DP controller is reset and needs HPD interrupt to trigger
+        * re-configuration. If we don't have valid IRQ, this is never
+        * going to happen. Let's reconfigure it here in this case.
+        */
+       if (dp->irq < 0 && !exynos_dp_detect_hpd(dp))
+               schedule_work(&dp->hotplug_work);
+
+       exynos_dp_train_link(dp);
        exynos_dp_config_video(dp);
+
+       dp->enabled = true;
+       return 0;
 }
 
+static int exynos_dp_dpms(void *ctx, int mode)
+{
+       struct exynos_dp_device *dp = ctx;
+
+       switch (mode) {
+       case DRM_MODE_DPMS_ON:
+               return exynos_dp_power_on(dp);
+
+       case DRM_MODE_DPMS_STANDBY:
+       case DRM_MODE_DPMS_SUSPEND:
+       case DRM_MODE_DPMS_OFF:
+               return exynos_dp_power_off(dp);
+
+       default:
+               DRM_ERROR("Unknown dpms mode %d\n", mode);
+       }
+       return -EINVAL;
+}
+
+static int exynos_dp_check_timing(void *ctx, void *timing)
+{
+       /*
+        * TODO(seanpaul): The datasheet isn't terribly descriptive about the
+        * limitations we have here. It's not vitally important to implement
+        * this right now, but should be implemented once we use EDID to mode
+        * set.
+        */
+       return 0;
+}
+
+static bool exynos_dp_is_connected(void *ctx)
+{
+       struct exynos_dp_device *dp = ctx;
+
+       if (dp->force_connected)
+               return true;
+       else
+               return !exynos_dp_detect_hpd(dp);
+}
+
+static int exynos_dp_subdrv_probe(void *ctx, struct drm_device *drm_dev)
+{
+       struct exynos_dp_device *dp = ctx;
+       int ret;
+
+       dp->drm_dev = drm_dev;
+
+       if (dp->irq >= 0) {
+               ret = request_irq(dp->irq, exynos_dp_irq_handler, dp->irq_flags,
+                               "exynos-dp", dp);
+               if (ret) {
+                       dev_err(dp->dev, "failed to request irq\n");
+                       return ret;
+               }
+       }
+
+       exynos_dp_dpms(dp, DRM_MODE_DPMS_ON);
+
+       return 0;
+}
+
+static struct exynos_panel_ops dp_panel_ops = {
+       .subdrv_probe = exynos_dp_subdrv_probe,
+       .is_connected = exynos_dp_is_connected,
+       .check_timing = exynos_dp_check_timing,
+       .dpms = exynos_dp_dpms,
+};
+
 static int __devinit exynos_dp_probe(struct platform_device *pdev)
 {
        struct resource *res;
@@ -1029,35 +1145,50 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev)
                goto err_req_region;
        }
 
-       dp->irq = platform_get_irq(pdev, 0);
-       if (dp->irq == -ENXIO) {
-               dev_err(&pdev->dev, "failed to get irq\n");
-               ret = -ENODEV;
-               goto err_ioremap;
+       if (gpio_is_valid(pdata->hpd_gpio)) {
+               dp->hpd_gpio = pdata->hpd_gpio;
+               ret = gpio_request_one(dp->hpd_gpio, GPIOF_IN, "dp_hpd");
+               if (ret)
+                       goto err_ioremap;
+#ifdef CONFIG_S5P_GPIO_INT
+               ret = s5p_register_gpio_interrupt(dp->hpd_gpio);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "cannot register/get GPIO irq\n");
+                       goto err_gpio;
+               }
+               s3c_gpio_cfgpin(dp->hpd_gpio, S3C_GPIO_SFN(0xf));
+#endif
+               dp->irq = gpio_to_irq(dp->hpd_gpio);
+               dp->irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+       } else {
+               dp->hpd_gpio = -ENODEV;
+               dp->irq = platform_get_irq(pdev, 0);
+               dp->irq_flags = 0;
        }
 
+       dp->enabled = false;
        dp->training_type = pdata->training_type;
        dp->video_info = pdata->video_info;
-       if (pdata->phy_init)
-               pdata->phy_init();
-
-       INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug);
-
-       ret = request_irq(dp->irq, exynos_dp_irq_handler, 0,
-                       "exynos-dp", dp);
-       if (ret) {
-               dev_err(&pdev->dev, "failed to request irq\n");
-               goto err_ioremap;
+       dp->force_connected = pdata->force_connected;
+       if (pdata->phy_init) {
+               dp->phy_ops.phy_init = pdata->phy_init;
+               dp->phy_ops.phy_init();
        }
+       if (pdata->phy_exit)
+               dp->phy_ops.phy_exit = pdata->phy_exit;
 
-       disable_irq(dp->irq);
+       INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug);
 
        platform_set_drvdata(pdev, dp);
 
-       exynos_fimd_dp_attach(dp->dev);
+       exynos_display_attach_panel(EXYNOS_DRM_DISPLAY_TYPE_FIMD, &dp_panel_ops,
+                       dp);
 
        return 0;
 
+err_gpio:
+       if (gpio_is_valid(dp->hpd_gpio))
+               gpio_free(dp->hpd_gpio);
 err_ioremap:
        iounmap(dp->reg_base);
 err_req_region:
@@ -1072,14 +1203,13 @@ err_dp:
 
 static int __devexit exynos_dp_remove(struct platform_device *pdev)
 {
-       struct exynos_dp_platdata *pdata = pdev->dev.platform_data;
        struct exynos_dp_device *dp = platform_get_drvdata(pdev);
 
-       if (work_pending(&dp->hotplug_work))
-               flush_work_sync(&dp->hotplug_work);
+       /* power_off will take care of flushing the hotplug_work */
+       exynos_dp_dpms(dp, DRM_MODE_DPMS_OFF);
 
-       if (pdata && pdata->phy_exit)
-               pdata->phy_exit();
+       if (gpio_is_valid(dp->hpd_gpio))
+               gpio_free(dp->hpd_gpio);
 
        free_irq(dp->irq, dp);
        iounmap(dp->reg_base);
@@ -1094,55 +1224,6 @@ static int __devexit exynos_dp_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-int exynos_dp_suspend(struct device *dev)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct exynos_dp_platdata *pdata = pdev->dev.platform_data;
-       struct exynos_dp_device *dp = platform_get_drvdata(pdev);
-
-       disable_irq(dp->irq);
-
-       if (work_pending(&dp->hotplug_work))
-               flush_work_sync(&dp->hotplug_work);
-
-       if (pdata && pdata->phy_exit)
-               pdata->phy_exit();
-
-       clk_disable(dp->clock);
-
-       return 0;
-}
-
-int exynos_dp_resume(struct device *dev)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct exynos_dp_platdata *pdata = pdev->dev.platform_data;
-       struct exynos_dp_device *dp = platform_get_drvdata(pdev);
-
-       if (pdata && pdata->phy_init)
-               pdata->phy_init();
-
-       clk_enable(dp->clock);
-
-       exynos_dp_init_dp(dp);
-
-       enable_irq(dp->irq);
-
-       return 0;
-}
-#else
-int exynos_dp_suspend(struct device *dev)
-{
-       return 0;
-}
-
-int exynos_dp_resume(struct device *dev)
-{
-       return 0;
-}
-#endif
-
 struct platform_driver dp_driver = {
        .probe          = exynos_dp_probe,
        .remove         = __devexit_p(exynos_dp_remove),
@@ -1151,5 +1232,3 @@ struct platform_driver dp_driver = {
                .owner  = THIS_MODULE,
        },
 };
-
-