]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/exynos/exynos_hdmi.c
drm/exynos: Use devm_* APIs in exynos_hdmi.c
[karo-tx-linux.git] / drivers / gpu / drm / exynos / exynos_hdmi.c
index a6aea6f3ea1ab842d8c6d26a5ecb7e606c335f0d..9f017748e730aa50c209c9496a79d580c48cdee4 100644 (file)
@@ -14,9 +14,9 @@
  *
  */
 
-#include "drmP.h"
-#include "drm_edid.h"
-#include "drm_crtc_helper.h"
+#include <drm/drmP.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_crtc_helper.h>
 
 #include "regs-hdmi.h"
 
@@ -32,6 +32,9 @@
 #include <linux/pm_runtime.h>
 #include <linux/clk.h>
 #include <linux/regulator/consumer.h>
+#include <linux/io.h>
+#include <linux/of_gpio.h>
+#include <plat/gpio-cfg.h>
 
 #include <drm/exynos_drm.h>
 
 
 #include "exynos_hdmi.h"
 
+#include <linux/gpio.h>
+#include <media/s5p_hdmi.h>
+
 #define MAX_WIDTH              1920
 #define MAX_HEIGHT             1080
 #define get_hdmi_context(dev)  platform_get_drvdata(to_platform_device(dev))
 
+enum hdmi_type {
+       HDMI_TYPE13,
+       HDMI_TYPE14,
+};
+
 struct hdmi_resources {
        struct clk                      *hdmi;
        struct clk                      *sclk_hdmi;
@@ -59,13 +70,13 @@ struct hdmi_context {
        struct drm_device               *drm_dev;
        bool                            hpd;
        bool                            powered;
-       bool                            is_v13;
        bool                            dvi_mode;
        struct mutex                    hdmi_mutex;
 
        void __iomem                    *regs;
-       unsigned int                    external_irq;
-       unsigned int                    internal_irq;
+       void                            *parent_ctx;
+       int                             external_irq;
+       int                             internal_irq;
 
        struct i2c_client               *ddc_port;
        struct i2c_client               *hdmiphy_port;
@@ -74,10 +85,10 @@ struct hdmi_context {
        int cur_conf;
 
        struct hdmi_resources           res;
-       void                            *parent_ctx;
 
-       void                            (*cfg_hpd)(bool external);
-       int                             (*get_hpd)(void);
+       int                             hpd_gpio;
+
+       enum hdmi_type                  type;
 };
 
 /* HDMI Version 1.3 */
@@ -1209,7 +1220,7 @@ static void hdmi_v14_regs_dump(struct hdmi_context *hdata, char *prefix)
 
 static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
 {
-       if (hdata->is_v13)
+       if (hdata->type == HDMI_TYPE13)
                hdmi_v13_regs_dump(hdata, prefix);
        else
                hdmi_v14_regs_dump(hdata, prefix);
@@ -1250,7 +1261,7 @@ static int hdmi_v14_conf_index(struct drm_display_mode *mode)
 static int hdmi_conf_index(struct hdmi_context *hdata,
                           struct drm_display_mode *mode)
 {
-       if (hdata->is_v13)
+       if (hdata->type == HDMI_TYPE13)
                return hdmi_v13_conf_index(mode);
 
        return hdmi_v14_conf_index(mode);
@@ -1282,6 +1293,7 @@ static int hdmi_get_edid(void *ctx, struct drm_connector *connector,
                DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
                        (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
                        raw_edid->width_cm, raw_edid->height_cm);
+               kfree(raw_edid);
        } else {
                return -ENODEV;
        }
@@ -1346,7 +1358,7 @@ static int hdmi_check_timing(void *ctx, void *timing)
                        check_timing->yres, check_timing->refresh,
                        check_timing->vmode);
 
-       if (hdata->is_v13)
+       if (hdata->type == HDMI_TYPE13)
                return hdmi_v13_check_timing(check_timing);
        else
                return hdmi_v14_check_timing(check_timing);
@@ -1412,7 +1424,7 @@ static void hdmi_reg_acr(struct hdmi_context *hdata, u8 *acr)
        hdmi_reg_writeb(hdata, HDMI_ACR_CTS1, acr[2]);
        hdmi_reg_writeb(hdata, HDMI_ACR_CTS2, acr[1]);
 
-       if (hdata->is_v13)
+       if (hdata->type == HDMI_TYPE13)
                hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 4);
        else
                hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
@@ -1516,7 +1528,7 @@ static void hdmi_conf_reset(struct hdmi_context *hdata)
 {
        u32 reg;
 
-       if (hdata->is_v13)
+       if (hdata->type == HDMI_TYPE13)
                reg = HDMI_V13_CORE_RSTOUT;
        else
                reg = HDMI_CORE_RSTOUT;
@@ -1530,12 +1542,9 @@ static void hdmi_conf_reset(struct hdmi_context *hdata)
 
 static void hdmi_conf_init(struct hdmi_context *hdata)
 {
-       /* enable HPD interrupts */
+       /* disable HPD interrupts */
        hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL |
                HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
-       mdelay(10);
-       hdmi_reg_writemask(hdata, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL |
-               HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
 
        /* choose HDMI mode */
        hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
@@ -1551,7 +1560,7 @@ static void hdmi_conf_init(struct hdmi_context *hdata)
                                HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);
        }
 
-       if (hdata->is_v13) {
+       if (hdata->type == HDMI_TYPE13) {
                /* choose bluescreen (fecal) color */
                hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
                hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
@@ -1833,7 +1842,7 @@ static void hdmi_v14_timing_apply(struct hdmi_context *hdata)
 
 static void hdmi_timing_apply(struct hdmi_context *hdata)
 {
-       if (hdata->is_v13)
+       if (hdata->type == HDMI_TYPE13)
                hdmi_v13_timing_apply(hdata);
        else
                hdmi_v14_timing_apply(hdata);
@@ -1855,7 +1864,7 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
        if (hdata->hdmiphy_port)
                i2c_master_send(hdata->hdmiphy_port, buffer, 2);
 
-       if (hdata->is_v13)
+       if (hdata->type == HDMI_TYPE13)
                reg = HDMI_V13_PHY_RSTOUT;
        else
                reg = HDMI_PHY_RSTOUT;
@@ -1882,7 +1891,7 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
        }
 
        /* pixel clock */
-       if (hdata->is_v13)
+       if (hdata->type == HDMI_TYPE13)
                hdmiphy_data = hdmi_v13_confs[hdata->cur_conf].hdmiphy_data;
        else
                hdmiphy_data = hdmi_confs[hdata->cur_conf].hdmiphy_data;
@@ -1950,7 +1959,7 @@ static void hdmi_mode_fixup(void *ctx, struct drm_connector *connector,
 
        drm_mode_set_crtcinfo(adjusted_mode, 0);
 
-       if (hdata->is_v13)
+       if (hdata->type == HDMI_TYPE13)
                index = hdmi_v13_conf_index(adjusted_mode);
        else
                index = hdmi_v14_conf_index(adjusted_mode);
@@ -1964,15 +1973,24 @@ static void hdmi_mode_fixup(void *ctx, struct drm_connector *connector,
         * to adjusted_mode.
         */
        list_for_each_entry(m, &connector->modes, head) {
-               if (hdata->is_v13)
+               if (hdata->type == HDMI_TYPE13)
                        index = hdmi_v13_conf_index(m);
                else
                        index = hdmi_v14_conf_index(m);
 
                if (index >= 0) {
+                       struct drm_mode_object base;
+                       struct list_head head;
+
                        DRM_INFO("desired mode doesn't exist so\n");
                        DRM_INFO("use the most suitable mode among modes.\n");
+
+                       /* preserve display mode header while copying. */
+                       head = adjusted_mode->head;
+                       base = adjusted_mode->base;
                        memcpy(adjusted_mode, m, sizeof(*m));
+                       adjusted_mode->head = head;
+                       adjusted_mode->base = base;
                        break;
                }
        }
@@ -2024,8 +2042,6 @@ static void hdmi_poweron(struct hdmi_context *hdata)
 
        hdata->powered = true;
 
-       if (hdata->cfg_hpd)
-               hdata->cfg_hpd(true);
        mutex_unlock(&hdata->hdmi_mutex);
 
        pm_runtime_get_sync(hdata->dev);
@@ -2061,8 +2077,6 @@ static void hdmi_poweroff(struct hdmi_context *hdata)
        pm_runtime_put_sync(hdata->dev);
 
        mutex_lock(&hdata->hdmi_mutex);
-       if (hdata->cfg_hpd)
-               hdata->cfg_hpd(false);
 
        hdata->powered = false;
 
@@ -2110,17 +2124,13 @@ static irqreturn_t hdmi_external_irq_thread(int irq, void *arg)
        struct exynos_drm_hdmi_context *ctx = arg;
        struct hdmi_context *hdata = ctx->ctx;
 
-       if (!hdata->get_hpd)
-               goto out;
-
        mutex_lock(&hdata->hdmi_mutex);
-       hdata->hpd = hdata->get_hpd();
+       hdata->hpd = gpio_get_value(hdata->hpd_gpio);
        mutex_unlock(&hdata->hdmi_mutex);
 
        if (ctx->drm_dev)
                drm_helper_hpd_irq_event(ctx->drm_dev);
 
-out:
        return IRQ_HANDLED;
 }
 
@@ -2143,18 +2153,9 @@ static irqreturn_t hdmi_internal_irq_thread(int irq, void *arg)
                        HDMI_INTC_FLAG_HPD_PLUG);
        }
 
-       mutex_lock(&hdata->hdmi_mutex);
-       hdata->hpd = hdmi_reg_read(hdata, HDMI_HPD_STATUS);
-       if (hdata->powered && hdata->hpd) {
-               mutex_unlock(&hdata->hdmi_mutex);
-               goto out;
-       }
-       mutex_unlock(&hdata->hdmi_mutex);
-
        if (ctx->drm_dev)
                drm_helper_hpd_irq_event(ctx->drm_dev);
 
-out:
        return IRQ_HANDLED;
 }
 
@@ -2175,27 +2176,27 @@ static int __devinit hdmi_resources_init(struct hdmi_context *hdata)
        memset(res, 0, sizeof(*res));
 
        /* get clocks, power */
-       res->hdmi = clk_get(dev, "hdmi");
+       res->hdmi = devm_clk_get(dev, "hdmi");
        if (IS_ERR_OR_NULL(res->hdmi)) {
                DRM_ERROR("failed to get clock 'hdmi'\n");
                goto fail;
        }
-       res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
+       res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
        if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
                DRM_ERROR("failed to get clock 'sclk_hdmi'\n");
                goto fail;
        }
-       res->sclk_pixel = clk_get(dev, "sclk_pixel");
+       res->sclk_pixel = devm_clk_get(dev, "sclk_pixel");
        if (IS_ERR_OR_NULL(res->sclk_pixel)) {
                DRM_ERROR("failed to get clock 'sclk_pixel'\n");
                goto fail;
        }
-       res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy");
+       res->sclk_hdmiphy = devm_clk_get(dev, "sclk_hdmiphy");
        if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) {
                DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n");
                goto fail;
        }
-       res->hdmiphy = clk_get(dev, "hdmiphy");
+       res->hdmiphy = devm_clk_get(dev, "hdmiphy");
        if (IS_ERR_OR_NULL(res->hdmiphy)) {
                DRM_ERROR("failed to get clock 'hdmiphy'\n");
                goto fail;
@@ -2203,7 +2204,7 @@ static int __devinit hdmi_resources_init(struct hdmi_context *hdata)
 
        clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
 
-       res->regul_bulk = kzalloc(ARRAY_SIZE(supply) *
+       res->regul_bulk = devm_kzalloc(dev, ARRAY_SIZE(supply) *
                sizeof(res->regul_bulk[0]), GFP_KERNEL);
        if (!res->regul_bulk) {
                DRM_ERROR("failed to get memory for regulators\n");
@@ -2213,7 +2214,7 @@ static int __devinit hdmi_resources_init(struct hdmi_context *hdata)
                res->regul_bulk[i].supply = supply[i];
                res->regul_bulk[i].consumer = NULL;
        }
-       ret = regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk);
+       ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk);
        if (ret) {
                DRM_ERROR("failed to get regulators\n");
                goto fail;
@@ -2226,28 +2227,6 @@ fail:
        return -ENODEV;
 }
 
-static int hdmi_resources_cleanup(struct hdmi_context *hdata)
-{
-       struct hdmi_resources *res = &hdata->res;
-
-       regulator_bulk_free(res->regul_count, res->regul_bulk);
-       /* kfree is NULL-safe */
-       kfree(res->regul_bulk);
-       if (!IS_ERR_OR_NULL(res->hdmiphy))
-               clk_put(res->hdmiphy);
-       if (!IS_ERR_OR_NULL(res->sclk_hdmiphy))
-               clk_put(res->sclk_hdmiphy);
-       if (!IS_ERR_OR_NULL(res->sclk_pixel))
-               clk_put(res->sclk_pixel);
-       if (!IS_ERR_OR_NULL(res->sclk_hdmi))
-               clk_put(res->sclk_hdmi);
-       if (!IS_ERR_OR_NULL(res->hdmi))
-               clk_put(res->hdmi);
-       memset(res, 0, sizeof(*res));
-
-       return 0;
-}
-
 static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy;
 
 void hdmi_attach_ddc_client(struct i2c_client *ddc)
@@ -2262,18 +2241,89 @@ void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy)
                hdmi_hdmiphy = hdmiphy;
 }
 
+#ifdef CONFIG_OF
+static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
+                                       (struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct s5p_hdmi_platform_data *pd;
+       enum of_gpio_flags flags;
+       u32 value;
+
+       pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+       if (!pd) {
+               DRM_ERROR("memory allocation for pdata failed\n");
+               goto err_data;
+       }
+
+       if (!of_find_property(np, "hpd-gpio", &value)) {
+               DRM_ERROR("no hpd gpio property found\n");
+               goto err_data;
+       }
+
+       pd->hpd_gpio = of_get_named_gpio_flags(np, "hpd-gpio", 0, &flags);
+
+       return pd;
+
+err_data:
+       return NULL;
+}
+#else
+static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
+                                       (struct device *dev)
+{
+       return NULL;
+}
+#endif
+
+static struct platform_device_id hdmi_driver_types[] = {
+       {
+               .name           = "s5pv210-hdmi",
+               .driver_data    = HDMI_TYPE13,
+       }, {
+               .name           = "exynos4-hdmi",
+               .driver_data    = HDMI_TYPE13,
+       }, {
+               .name           = "exynos4-hdmi14",
+               .driver_data    = HDMI_TYPE14,
+       }, {
+               .name           = "exynos5-hdmi",
+               .driver_data    = HDMI_TYPE14,
+       }, {
+               /* end node */
+       }
+};
+
+static struct of_device_id hdmi_match_types[] = {
+       {
+               .compatible = "samsung,exynos5-hdmi",
+               .data   = (void *)HDMI_TYPE14,
+       }, {
+               /* end node */
+       }
+};
+
 static int __devinit hdmi_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct exynos_drm_hdmi_context *drm_hdmi_ctx;
        struct hdmi_context *hdata;
-       struct exynos_drm_hdmi_pdata *pdata;
+       struct s5p_hdmi_platform_data *pdata;
        struct resource *res;
        int ret;
 
        DRM_DEBUG_KMS("[%d]\n", __LINE__);
 
-       pdata = pdev->dev.platform_data;
+       if (pdev->dev.of_node) {
+               pdata = drm_hdmi_dt_parse_pdata(dev);
+               if (IS_ERR(pdata)) {
+                       DRM_ERROR("failed to parse dt\n");
+                       return PTR_ERR(pdata);
+               }
+       } else {
+               pdata = pdev->dev.platform_data;
+       }
+
        if (!pdata) {
                DRM_ERROR("no platform data specified\n");
                return -EINVAL;
@@ -2300,31 +2350,48 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, drm_hdmi_ctx);
 
-       hdata->is_v13 = pdata->is_v13;
-       hdata->cfg_hpd = pdata->cfg_hpd;
-       hdata->get_hpd = pdata->get_hpd;
+       if (dev->of_node) {
+               const struct of_device_id *match;
+               match = of_match_node(of_match_ptr(hdmi_match_types),
+                                       pdev->dev.of_node);
+               hdata->type = (enum hdmi_type)match->data;
+       } else {
+               hdata->type = (enum hdmi_type)platform_get_device_id
+                                       (pdev)->driver_data;
+       }
+
+       hdata->hpd_gpio = pdata->hpd_gpio;
        hdata->dev = dev;
 
        ret = hdmi_resources_init(hdata);
+
        if (ret) {
-               ret = -EINVAL;
-               goto err_data;
+               DRM_ERROR("hdmi_resources_init failed\n");
+               return -EINVAL;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               DRM_ERROR("failed to find registers\n");
+               return -ENOENT;
+       }
 
        hdata->regs = devm_request_and_ioremap(&pdev->dev, res);
        if (!hdata->regs) {
                DRM_ERROR("failed to map registers\n");
-               ret = -ENXIO;
-               goto err_resource;
+               return -ENXIO;
+       }
+
+       ret = devm_gpio_request(&pdev->dev, hdata->hpd_gpio, "HPD");
+       if (ret) {
+               DRM_ERROR("failed to request HPD gpio\n");
+               return ret;
        }
 
        /* DDC i2c driver */
        if (i2c_add_driver(&ddc_driver)) {
                DRM_ERROR("failed to register ddc i2c driver\n");
-               ret = -ENOENT;
-               goto err_resource;
+               return -ENOENT;
        }
 
        hdata->ddc_port = hdmi_ddc;
@@ -2338,32 +2405,31 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
 
        hdata->hdmiphy_port = hdmi_hdmiphy;
 
-       hdata->external_irq = platform_get_irq_byname(pdev, "external_irq");
+       hdata->external_irq = gpio_to_irq(hdata->hpd_gpio);
        if (hdata->external_irq < 0) {
-               DRM_ERROR("failed to get platform irq\n");
+               DRM_ERROR("failed to get GPIO external irq\n");
                ret = hdata->external_irq;
                goto err_hdmiphy;
        }
 
-       hdata->internal_irq = platform_get_irq_byname(pdev, "internal_irq");
+       hdata->internal_irq = platform_get_irq(pdev, 0);
        if (hdata->internal_irq < 0) {
                DRM_ERROR("failed to get platform internal irq\n");
                ret = hdata->internal_irq;
                goto err_hdmiphy;
        }
 
+       hdata->hpd = gpio_get_value(hdata->hpd_gpio);
+
        ret = request_threaded_irq(hdata->external_irq, NULL,
                        hdmi_external_irq_thread, IRQF_TRIGGER_RISING |
                        IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                        "hdmi_external", drm_hdmi_ctx);
        if (ret) {
-               DRM_ERROR("failed to register hdmi internal interrupt\n");
+               DRM_ERROR("failed to register hdmi external interrupt\n");
                goto err_hdmiphy;
        }
 
-       if (hdata->cfg_hpd)
-               hdata->cfg_hpd(false);
-
        ret = request_threaded_irq(hdata->internal_irq, NULL,
                        hdmi_internal_irq_thread, IRQF_ONESHOT,
                        "hdmi_internal", drm_hdmi_ctx);
@@ -2372,6 +2438,9 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
                goto err_free_irq;
        }
 
+       /* Attach HDMI Driver to common hdmi. */
+       exynos_hdmi_drv_attach(drm_hdmi_ctx);
+
        /* register specific callbacks to common hdmi. */
        exynos_hdmi_ops_register(&hdmi_ops);
 
@@ -2385,9 +2454,6 @@ err_hdmiphy:
        i2c_del_driver(&hdmiphy_driver);
 err_ddc:
        i2c_del_driver(&ddc_driver);
-err_resource:
-       hdmi_resources_cleanup(hdata);
-err_data:
        return ret;
 }
 
@@ -2402,8 +2468,8 @@ static int __devexit hdmi_remove(struct platform_device *pdev)
        pm_runtime_disable(dev);
 
        free_irq(hdata->internal_irq, hdata);
+       free_irq(hdata->external_irq, hdata);
 
-       hdmi_resources_cleanup(hdata);
 
        /* hdmiphy i2c driver */
        i2c_del_driver(&hdmiphy_driver);
@@ -2447,9 +2513,11 @@ static SIMPLE_DEV_PM_OPS(hdmi_pm_ops, hdmi_suspend, hdmi_resume);
 struct platform_driver hdmi_driver = {
        .probe          = hdmi_probe,
        .remove         = __devexit_p(hdmi_remove),
+       .id_table = hdmi_driver_types,
        .driver         = {
-               .name   = "exynos4-hdmi",
+               .name   = "exynos-hdmi",
                .owner  = THIS_MODULE,
                .pm     = &hdmi_pm_ops,
+               .of_match_table = hdmi_match_types,
        },
 };