drm/exynos: Debounce gpio hotplug interrupts
[cascardo/linux.git] / drivers / gpu / drm / exynos / exynos_hdmi.c
index 91516ec..153edeb 100644 (file)
@@ -33,6 +33,8 @@
 #include <linux/clk.h>
 #include <linux/regulator/consumer.h>
 #include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_i2c.h>
 #include <linux/of_gpio.h>
 #include <plat/gpio-cfg.h>
 
@@ -45,6 +47,8 @@
 
 #define get_hdmi_context(dev)  platform_get_drvdata(to_platform_device(dev))
 
+#define HOTPLUG_DEBOUNCE_MS            1100
+
 struct hdmi_resources {
        struct clk                      *hdmi;
        struct clk                      *sclk_hdmi;
@@ -131,19 +135,18 @@ struct hdmi_context {
        unsigned int                    default_win;
        unsigned int                    default_bpp;
        bool                            hpd_handle;
-       bool                            enabled;
        bool                            has_hdmi_sink;
        bool                            has_hdmi_audio;
        bool                            is_soc_exynos5;
        bool                            is_hdmi_powered_on;
+       bool                            video_enabled;
 
        struct resource                 *regs_res;
        void __iomem                    *regs;
        unsigned int                    external_irq;
        unsigned int                    internal_irq;
        unsigned int                    curr_irq;
-       struct workqueue_struct         *wq;
-       struct work_struct              hotplug_work;
+       struct timer_list               hotplug_timer;
 
        struct i2c_client               *ddc_port;
        struct i2c_client               *hdmiphy_port;
@@ -860,10 +863,10 @@ static int hdmi_v13_conf_index(struct drm_display_mode *mode)
        int i;
 
        for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i)
-               if (hdmi_v13_confs[i].width == mode->hdisplay &&
-                               hdmi_v13_confs[i].height == mode->vdisplay &&
-                               hdmi_v13_confs[i].vrefresh == mode->vrefresh &&
-                               hdmi_v13_confs[i].interlace ==
+               if (hdmi_v13_confs[i].width == mode->crtc_hdisplay &&
+                       hdmi_v13_confs[i].height == mode->crtc_vdisplay &&
+                       hdmi_v13_confs[i].vrefresh == mode->vrefresh &&
+                       hdmi_v13_confs[i].interlace ==
                                ((mode->flags & DRM_MODE_FLAG_INTERLACE) ?
                                 true : false))
                        return i;
@@ -887,34 +890,30 @@ static bool hdmi_is_connected(void *ctx)
        return true;
 }
 
-static int hdmi_get_edid(void *ctx, struct drm_connector *connector,
-                               u8 *edid, int len)
+static struct edid *hdmi_get_edid(void *ctx, struct drm_connector *connector)
 {
-       struct edid *raw_edid;
        struct hdmi_context *hdata = ctx;
+       struct edid *edid;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", DRM_BASE_ID(connector),
+                       drm_get_connector_name(connector));
 
        if (!hdata->ddc_port)
-               return -ENODEV;
+               return ERR_PTR(-ENODEV);
+
+       edid = drm_get_edid(connector, hdata->ddc_port->adapter);
+       if (!edid)
+               return ERR_PTR(-ENODEV);
 
-       raw_edid = drm_get_edid(connector, hdata->ddc_port->adapter);
-       if (raw_edid) {
-       /* TODO : Need to call this in exynos_drm_connector.c, do a drm_get_edid
+       /*
+        * TODO : Need to call this in exynos_drm_connector.c, do a drm_get_edid
         * to get the edid and then call drm_detect_hdmi_monitor.
         */
-               hdata->has_hdmi_sink = drm_detect_hdmi_monitor(raw_edid);
-               hdata->has_hdmi_audio = drm_detect_monitor_audio(raw_edid);
-               memcpy(edid, raw_edid, min((1 + raw_edid->extensions)
-                                       * EDID_LENGTH, len));
-               DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
-                       (hdata->has_hdmi_sink ? "hdmi monitor" : "dvi monitor"),
-                       raw_edid->width_cm, raw_edid->height_cm);
-       } else {
-               return -ENODEV;
-       }
+       hdata->has_hdmi_sink = drm_detect_hdmi_monitor(edid);
+       hdata->has_hdmi_audio = drm_detect_monitor_audio(edid);
+       DRM_DEBUG_KMS("%s monitor\n", hdata->has_hdmi_sink ? "hdmi" : "dvi");
 
-       return 0;
+       return edid;
 }
 
 static int hdmi_v13_check_timing(struct fb_videomode *check_timing)
@@ -987,7 +986,9 @@ void hdmi_reg_infoframe(struct hdmi_context *hdata,
        u32 aspect_ratio;
        u32 mod;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+       /* TODO: stringify HDMI_PACKET_TYPE */
+       DRM_DEBUG_KMS("type: %d ver: %d len: %d\n", infoframe->type,
+                       infoframe->ver, infoframe->len);
        mod = hdmi_reg_read(hdata, HDMI_MODE_SEL);
        if (!hdata->has_hdmi_sink) {
                hdmi_reg_writeb(hdata, HDMI_VSI_CON,
@@ -1044,8 +1045,6 @@ static int hdmi_check_timing(void *ctx, void *timing)
        struct hdmi_context *hdata = ctx;
        struct fb_videomode *check_timing = timing;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
        DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres,
                        check_timing->yres, check_timing->refresh,
                        check_timing->vmode);
@@ -1238,6 +1237,33 @@ static void hdmi_conf_reset(struct hdmi_context *hdata)
        hdata->hpd_handle = true;
 }
 
+static void hdmi_enable_video(struct hdmi_context *hdata)
+{
+       if (hdata->is_v13)
+               return;
+
+       hdata->video_enabled = true;
+       hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
+}
+
+static void hdmi_disable_video(struct hdmi_context *hdata)
+{
+       if (hdata->is_v13)
+               return;
+
+       /* Set the blue screen color to black */
+       hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_R_0, 0);
+       hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_R_1, 0);
+       hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_G_0, 0);
+       hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_G_1, 0);
+       hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_B_0, 0);
+       hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_B_1, 0);
+
+       /* Enable the "blue screen", which effectively disconnects the mixer */
+       hdata->video_enabled = false;
+       hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_BLUE_SCR_EN);
+}
+
 static void hdmi_conf_init(struct hdmi_context *hdata)
 {
        struct hdmi_infoframe infoframe;
@@ -1254,8 +1280,11 @@ static void hdmi_conf_init(struct hdmi_context *hdata)
        /* choose HDMI mode */
        hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
                HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
-       /* disable bluescreen */
-       hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
+
+       if (hdata->video_enabled)
+               hdmi_enable_video(hdata);
+       else
+               hdmi_disable_video(hdata);
 
        if (!hdata->has_hdmi_sink) {
                /* choose DVI mode */
@@ -1599,6 +1628,8 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
        int ret;
        int i;
 
+       DRM_DEBUG_KMS("\n");
+
        if (!hdata->hdmiphy_port) {
                DRM_ERROR("hdmiphy is not attached\n");
                return;
@@ -1644,7 +1675,7 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
 
 static void hdmi_conf_apply(struct hdmi_context *hdata)
 {
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+       DRM_DEBUG_KMS("\n");
 
        hdmiphy_conf_reset(hdata);
        hdmiphy_conf_apply(hdata);
@@ -1667,6 +1698,8 @@ static void hdmi_mode_copy(struct drm_display_mode *dst,
 {
        struct drm_mode_object base;
 
+       DRM_DEBUG_KMS("[MODE:%d:%s]\n", DRM_BASE_ID(src), src->name);
+
        /* following information should be preserved,
         * required for releasing the drm_display_mode node,
         * duplicated to recieve adjustment info. */
@@ -1684,37 +1717,43 @@ static void hdmi_mode_fixup(void *ctx, struct drm_connector *connector,
                                struct drm_display_mode *mode,
                                struct drm_display_mode *adjusted_mode)
 {
-       struct drm_display_mode *m;
+       struct drm_display_mode *t, *con_mode;
        struct hdmi_context *hdata = ctx;
        int index;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] [MODE:%d:%s]\n",
+                       DRM_BASE_ID(connector),
+                       drm_get_connector_name(connector),
+                       DRM_BASE_ID(mode), mode->name);
 
-       drm_mode_set_crtcinfo(adjusted_mode, 0);
-
-       if (hdata->is_v13)
-               index = hdmi_v13_conf_index(adjusted_mode);
-       else
-               index = find_hdmiphy_conf(adjusted_mode->clock * 1000);
-
-       /* just return if user desired mode exists. */
-       if (index >= 0)
-               return;
+       /*
+        * Match the incoming mode to a mode in the connector list and copy it
+        * over. This is important since this might be an adjusted mode from the
+        * mixer and those have differing crtc_* values.
+        */
+       list_for_each_entry_safe(con_mode, t, &connector->modes, head) {
+               if (mode->hdisplay == con_mode->hdisplay &&
+                   mode->vdisplay == con_mode->vdisplay &&
+                   mode->clock == con_mode->clock) {
+                       hdmi_mode_copy(adjusted_mode, con_mode);
+                       return;
+               }
+       }
 
        /*
-        * otherwise, find the most suitable mode among modes and change it
-        * to adjusted_mode.
+        * We didn't find a mode which matched the desired resolution, so just
+        * find something with the same clock.
         */
-       list_for_each_entry(m, &connector->modes, head) {
+       list_for_each_entry_safe(con_mode, t, &connector->modes, head) {
                if (hdata->is_v13)
-                       index = hdmi_v13_conf_index(m);
+                       index = hdmi_v13_conf_index(con_mode);
                else
-                       index = find_hdmiphy_conf(m->clock * 1000);
+                       index = find_hdmiphy_conf(con_mode->clock * 1000);
 
                if (index >= 0) {
                        DRM_INFO("desired mode doesn't exist so\n");
                        DRM_INFO("use the most suitable mode among modes.\n");
-                       hdmi_mode_copy(adjusted_mode, m);
+                       hdmi_mode_copy(adjusted_mode, con_mode);
                        break;
                }
        }
@@ -1734,12 +1773,14 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
        struct hdmi_core_regs *core = &hdata->mode_conf.core;
        struct hdmi_tg_regs *tg = &hdata->mode_conf.tg;
 
+       DRM_DEBUG_KMS("[MODE:%d:%s]\n", DRM_BASE_ID(m), m->name);
+
        hdata->mode_conf.vic = drm_match_cea_mode(m);
 
        hdata->mode_conf.pixel_clock = m->clock * 1000;
-       hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);
-       hdmi_set_reg(core->v_line, 2, m->vtotal);
-       hdmi_set_reg(core->h_line, 2, m->htotal);
+       hdmi_set_reg(core->h_blank, 2, m->crtc_htotal - m->crtc_hdisplay);
+       hdmi_set_reg(core->v_line, 2, m->crtc_vtotal);
+       hdmi_set_reg(core->h_line, 2, m->crtc_htotal);
        hdmi_set_reg(core->hsync_pol, 1,
                        (m->flags & DRM_MODE_FLAG_NHSYNC)  ? 1 : 0);
        hdmi_set_reg(core->vsync_pol, 1,
@@ -1757,49 +1798,60 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
        if (m->flags & DRM_MODE_FLAG_INTERLACE) {
                /* Interlaced Mode */
                hdmi_set_reg(core->v_sync_line_bef_2, 2,
-                       (m->vsync_end - m->vdisplay) / 2);
+                       (m->crtc_vsync_end - m->crtc_vdisplay) / 2);
                hdmi_set_reg(core->v_sync_line_bef_1, 2,
-                       (m->vsync_start - m->vdisplay) / 2);
-               hdmi_set_reg(core->v2_blank, 2, m->vtotal / 2);
-               hdmi_set_reg(core->v1_blank, 2, (m->vtotal - m->vdisplay) / 2);
+                       (m->crtc_vsync_start - m->crtc_vdisplay) / 2);
+               hdmi_set_reg(core->v2_blank, 2, m->crtc_vtotal / 2);
+               hdmi_set_reg(core->v1_blank, 2,
+                               (m->crtc_vtotal - m->crtc_vdisplay) / 2);
                hdmi_set_reg(core->v_blank_f0, 2,
-                       (m->vtotal + ((m->vsync_end - m->vsync_start) * 4) + 5) / 2);
-               hdmi_set_reg(core->v_blank_f1, 2, m->vtotal);
-               hdmi_set_reg(core->v_sync_line_aft_2, 2, (m->vtotal / 2) + 7);
-               hdmi_set_reg(core->v_sync_line_aft_1, 2, (m->vtotal / 2) + 2);
+                       (m->crtc_vtotal +
+                       ((m->crtc_vsync_end - m->crtc_vsync_start) * 4) + 5)
+                       / 2);
+               hdmi_set_reg(core->v_blank_f1, 2, m->crtc_vtotal);
+               hdmi_set_reg(core->v_sync_line_aft_2, 2,
+                       (m->crtc_vtotal / 2) + 7);
+               hdmi_set_reg(core->v_sync_line_aft_1, 2,
+                       (m->crtc_vtotal / 2) + 2);
                hdmi_set_reg(core->v_sync_line_aft_pxl_2, 2,
-                       (m->htotal / 2) + (m->hsync_start - m->hdisplay));
+                       (m->crtc_htotal / 2) +
+                       (m->crtc_hsync_start - m->crtc_hdisplay));
                hdmi_set_reg(core->v_sync_line_aft_pxl_1, 2,
-                       (m->htotal / 2) + (m->hsync_start - m->hdisplay));
-               hdmi_set_reg(tg->vact_st, 2, (m->vtotal - m->vdisplay) / 2);
-               hdmi_set_reg(tg->vact_sz, 2, m->vdisplay / 2);
+                       (m->crtc_htotal / 2) +
+                       (m->crtc_hsync_start - m->crtc_hdisplay));
+               hdmi_set_reg(tg->vact_st, 2,
+                       (m->crtc_vtotal - m->crtc_vdisplay) / 2);
+               hdmi_set_reg(tg->vact_sz, 2, m->crtc_vdisplay / 2);
                hdmi_set_reg(tg->vact_st2, 2, 0x249);/* Reset value + 1*/
                hdmi_set_reg(tg->vact_st3, 2, 0x0);
                hdmi_set_reg(tg->vact_st4, 2, 0x0);
        } else {
                /* Progressive Mode */
                hdmi_set_reg(core->v_sync_line_bef_2, 2,
-                       m->vsync_end - m->vdisplay);
+                       m->crtc_vsync_end - m->crtc_vdisplay);
                hdmi_set_reg(core->v_sync_line_bef_1, 2,
-                       m->vsync_start - m->vdisplay);
-               hdmi_set_reg(core->v2_blank, 2, m->vtotal);
-               hdmi_set_reg(core->v1_blank, 2, m->vtotal - m->vdisplay);
+                       m->crtc_vsync_start - m->crtc_vdisplay);
+               hdmi_set_reg(core->v2_blank, 2, m->crtc_vtotal);
+               hdmi_set_reg(core->v1_blank, 2,
+                       m->crtc_vtotal - m->crtc_vdisplay);
                hdmi_set_reg(core->v_blank_f0, 2, 0xffff);
                hdmi_set_reg(core->v_blank_f1, 2, 0xffff);
                hdmi_set_reg(core->v_sync_line_aft_2, 2, 0xffff);
                hdmi_set_reg(core->v_sync_line_aft_1, 2, 0xffff);
                hdmi_set_reg(core->v_sync_line_aft_pxl_2, 2, 0xffff);
                hdmi_set_reg(core->v_sync_line_aft_pxl_1, 2, 0xffff);
-               hdmi_set_reg(tg->vact_st, 2, m->vtotal - m->vdisplay);
-               hdmi_set_reg(tg->vact_sz, 2, m->vdisplay);
+               hdmi_set_reg(tg->vact_st, 2, m->crtc_vtotal - m->crtc_vdisplay);
+               hdmi_set_reg(tg->vact_sz, 2, m->crtc_vdisplay);
                hdmi_set_reg(tg->vact_st2, 2, 0x248); /* Reset value */
                hdmi_set_reg(tg->vact_st3, 2, 0x47b); /* Reset value */
                hdmi_set_reg(tg->vact_st4, 2, 0x6ae); /* Reset value */
        }
 
        /* Following values & calculations are same irrespective of mode type */
-       hdmi_set_reg(core->h_sync_start, 2, m->hsync_start - m->hdisplay - 2);
-       hdmi_set_reg(core->h_sync_end, 2, m->hsync_end - m->hdisplay - 2);
+       hdmi_set_reg(core->h_sync_start, 2,
+               m->crtc_hsync_start - m->crtc_hdisplay - 2);
+       hdmi_set_reg(core->h_sync_end, 2,
+               m->crtc_hsync_end - m->crtc_hdisplay - 2);
        hdmi_set_reg(core->vact_space_1, 2, 0xffff);
        hdmi_set_reg(core->vact_space_2, 2, 0xffff);
        hdmi_set_reg(core->vact_space_3, 2, 0xffff);
@@ -1821,10 +1873,10 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
 
        /* Timing generator registers */
        hdmi_set_reg(tg->cmd, 1, 0x0);
-       hdmi_set_reg(tg->h_fsz, 2, m->htotal);
-       hdmi_set_reg(tg->hact_st, 2, m->htotal - m->hdisplay);
-       hdmi_set_reg(tg->hact_sz, 2, m->hdisplay);
-       hdmi_set_reg(tg->v_fsz, 2, m->vtotal);
+       hdmi_set_reg(tg->h_fsz, 2, m->crtc_htotal);
+       hdmi_set_reg(tg->hact_st, 2, m->crtc_htotal - m->crtc_hdisplay);
+       hdmi_set_reg(tg->hact_sz, 2, m->crtc_hdisplay);
+       hdmi_set_reg(tg->v_fsz, 2, m->crtc_vtotal);
        hdmi_set_reg(tg->vsync, 2, 0x1);
        hdmi_set_reg(tg->vsync2, 2, 0x233); /* Reset value */
        hdmi_set_reg(tg->field_chg, 2, 0x233); /* Reset value */
@@ -1834,26 +1886,22 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
        hdmi_set_reg(tg->field_bot_hdmi, 2, 0x233); /* Reset value */
        hdmi_set_reg(tg->tg_3d, 1, 0x0);
 
+       /* Workaround 4 implementation for 1440x900 resolution support */
        if (hdata->is_soc_exynos5) {
-               /* Workaround 4 implementation for 1440x900 resolution support */
-               if (m->hdisplay == 1440 && m->vdisplay == 900 && m->clock == 106500) {
-                       hdmi_set_reg(tg->hact_st, 2, m->htotal - m->hdisplay - 0xe0);
-                       hdmi_set_reg(tg->hact_sz, 2, m->hdisplay + 0xe0);
-               }
-
-               /* Workaround 3 implementation for 800x600 resolution support */
-               if (m->hdisplay == 800 && m->vdisplay == 600 && m->clock == 40000) {
-                       hdmi_set_reg(tg->hact_st, 2, m->htotal - m->hdisplay - 0x20);
-                       hdmi_set_reg(tg->hact_sz, 2, m->hdisplay + 0x20);
+               if (m->crtc_hdisplay == 1440 && m->crtc_vdisplay == 900 &&
+                   m->clock == 106500) {
+                       hdmi_set_reg(tg->hact_st, 2,
+                               m->crtc_htotal - m->crtc_hdisplay - 0xe0);
+                       hdmi_set_reg(tg->hact_sz, 2, m->crtc_hdisplay + 0xe0);
                }
        }
 }
 
-static void hdmi_mode_set(void *ctx, void *mode)
+static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode)
 {
        struct hdmi_context *hdata = ctx;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+       DRM_DEBUG_KMS("[MODE:%d:%s]\n", DRM_BASE_ID(mode), mode->name);
 
        if (hdata->is_v13)
                hdata->cur_conf = hdmi_v13_conf_index(mode);
@@ -1861,23 +1909,6 @@ static void hdmi_mode_set(void *ctx, void *mode)
                hdmi_v14_mode_set(hdata, mode);
 }
 
-static void hdmi_commit(void *ctx)
-{
-       struct hdmi_context *hdata = ctx;
-
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-       if (!hdata->is_hdmi_powered_on)
-               return;
-
-       hdmi_conf_apply(hdata);
-       hdata->enabled = true;
-}
-
-static void hdmi_apply(void *ctx)
-{
-       hdmi_commit(ctx);
-}
-
 static int hdmiphy_update_bits(struct i2c_client *client, u8 *reg_cache,
                               u8 reg, u8 mask, u8 val)
 {
@@ -1901,7 +1932,7 @@ static int hdmiphy_s_power(struct i2c_client *client, bool on)
        u8 buffer[2];
        int ret;
 
-       DRM_DEBUG_KMS("%s: hdmiphy is %s\n", __func__, on ? "on" : "off");
+       DRM_DEBUG_KMS("%s\n", on ? "on" : "off");
 
        /* Cache all 32 registers to make the code below faster */
        buffer[0] = 0x0;
@@ -1986,7 +2017,7 @@ static void hdmi_resource_poweron(struct hdmi_context *hdata)
        hdmi_conf_init(hdata);
        if (!hdata->is_soc_exynos5)
                hdmi_audio_init(hdata);
-       hdmi_commit(hdata);
+       hdmi_conf_apply(hdata);
 }
 
 static void hdmi_resource_poweroff(struct hdmi_context *hdata)
@@ -2014,20 +2045,23 @@ static void hdmi_resource_poweroff(struct hdmi_context *hdata)
        regulator_bulk_disable(res->regul_count, res->regul_bulk);
 }
 
-static int hdmi_power(void *ctx, int mode)
+static int hdmi_dpms(void *ctx, int mode)
 {
        struct hdmi_context *hdata = ctx;
 
+       DRM_DEBUG_KMS("[DPMS:%s]\n", drm_get_dpms_name(mode));
+
        switch (mode) {
        case DRM_MODE_DPMS_ON:
                if (!hdata->is_hdmi_powered_on)
                        hdmi_resource_poweron(hdata);
+               hdmi_enable_video(hdata);
                break;
        case DRM_MODE_DPMS_STANDBY:
-               break;
-       case DRM_MODE_DPMS_SUSPEND:
+               hdmi_disable_video(hdata);
                break;
        case DRM_MODE_DPMS_OFF:
+       case DRM_MODE_DPMS_SUSPEND:
                if (hdata->is_hdmi_powered_on)
                        hdmi_resource_poweroff(hdata);
                break;
@@ -2043,6 +2077,8 @@ static int hdmi_subdrv_probe(void *ctx, struct drm_device *drm_dev)
 {
        struct hdmi_context *hdata = ctx;
 
+       DRM_DEBUG_KMS("[DEV:%s]\n", drm_dev->devname);
+
        hdata->drm_dev = drm_dev;
 
        return 0;
@@ -2054,22 +2090,19 @@ static struct exynos_panel_ops hdmi_ops = {
        .is_connected   = hdmi_is_connected,
        .get_edid       = hdmi_get_edid,
        .check_timing   = hdmi_check_timing,
-       .power          = hdmi_power,
+       .dpms           = hdmi_dpms,
 
        /* manager */
        .mode_fixup     = hdmi_mode_fixup,
        .mode_set       = hdmi_mode_set,
-       .commit         = hdmi_commit,
-       .apply          = hdmi_apply,
 };
 
 /*
  * Handle hotplug events outside the interrupt handler proper.
  */
-static void hdmi_hotplug_func(struct work_struct *work)
+static void hdmi_hotplug_timer_func(unsigned long data)
 {
-       struct hdmi_context *hdata =
-               container_of(work, struct hdmi_context, hotplug_work);
+       struct hdmi_context *hdata = (struct hdmi_context *)data;
 
        drm_helper_hpd_irq_event(hdata->drm_dev);
 }
@@ -2077,7 +2110,8 @@ static void hdmi_hotplug_func(struct work_struct *work)
 static irqreturn_t hdmi_irq_handler(int irq, void *arg)
 {
        struct hdmi_context *hdata = arg;
-       u32 intc_flag;
+       u32 intc_flag, time_ms = HOTPLUG_DEBOUNCE_MS;
+
        if (hdata->is_hdmi_powered_on) {
                intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG);
                /* clearing flags for HPD plug/unplug */
@@ -2093,10 +2127,17 @@ static irqreturn_t hdmi_irq_handler(int irq, void *arg)
                        hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
                                HDMI_INTC_FLAG_HPD_PLUG);
                }
+
+               /*
+                * No need to debounce if we're powered on, the hardware
+                * has already done it for us.
+                */
+               time_ms = 0;
        }
 
        if (hdata->drm_dev && hdata->hpd_handle)
-               queue_work(hdata->wq, &hdata->hotplug_work);
+               mod_timer(&hdata->hotplug_timer,
+                               jiffies + msecs_to_jiffies(time_ms));
 
        return IRQ_HANDLED;
 }
@@ -2201,20 +2242,6 @@ static int hdmi_resources_cleanup(struct hdmi_context *hdata)
        return 0;
 }
 
-static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy;
-
-void hdmi_attach_ddc_client(struct i2c_client *ddc)
-{
-       if (ddc)
-               hdmi_ddc = ddc;
-}
-
-void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy)
-{
-       if (hdmiphy)
-               hdmi_hdmiphy = hdmiphy;
-}
-
 struct platform_device *hdmi_audio_device;
 
 int hdmi_register_audio_device(struct platform_device *pdev)
@@ -2223,6 +2250,8 @@ int hdmi_register_audio_device(struct platform_device *pdev)
        struct platform_device *audio_dev;
        int ret;
 
+       DRM_DEBUG_KMS("[PDEV:%s]\n", pdev->name);
+
        audio_dev = platform_device_alloc("exynos-hdmi-audio", -1);
        if (!audio_dev) {
                DRM_ERROR("hdmi audio device allocation failed.\n");
@@ -2258,6 +2287,7 @@ err:
 
 void hdmi_unregister_audio_device(void)
 {
+       DRM_DEBUG_KMS("\n");
        platform_device_unregister(hdmi_audio_device);
 }
 
@@ -2267,10 +2297,11 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
        struct hdmi_context *hdata;
        struct exynos_drm_hdmi_pdata *pdata;
        struct resource *res;
+       struct device_node *ddc_node, *phy_node;
        int ret;
        enum of_gpio_flags flags;
 
-       DRM_DEBUG_KMS("[%d]\n", __LINE__);
+       DRM_DEBUG_KMS("[PDEV:%s]\n", pdev->name);
 
        pdata = pdev->dev.platform_data;
        if (!pdata) {
@@ -2323,22 +2354,32 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
        }
 
        /* DDC i2c driver */
-       if (i2c_add_driver(&ddc_driver)) {
-               DRM_ERROR("failed to register ddc i2c driver\n");
-               ret = -ENOENT;
+       ddc_node = of_find_node_by_name(NULL, "exynos_ddc");
+       if (!ddc_node) {
+               DRM_ERROR("Failed to find ddc node in device tree\n");
+               ret = -ENODEV;
+               goto err_iomap;
+       }
+       hdata->ddc_port = of_find_i2c_device_by_node(ddc_node);
+       if (!hdata->ddc_port) {
+               DRM_ERROR("Failed to get ddc i2c client by node\n");
+               ret = -ENODEV;
                goto err_iomap;
        }
-
-       hdata->ddc_port = hdmi_ddc;
 
        /* hdmiphy i2c driver */
-       if (i2c_add_driver(&hdmiphy_driver)) {
-               DRM_ERROR("failed to register hdmiphy i2c driver\n");
-               ret = -ENOENT;
+       phy_node = of_find_node_by_name(NULL, "exynos_hdmiphy");
+       if (!phy_node) {
+               DRM_ERROR("Failed to find hdmiphy node in device tree\n");
+               ret = -ENODEV;
+               goto err_ddc;
+       }
+       hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
+       if (!hdata->hdmiphy_port) {
+               DRM_ERROR("Failed to get hdmi phy i2c client from node\n");
+               ret = -ENODEV;
                goto err_ddc;
        }
-
-       hdata->hdmiphy_port = hdmi_hdmiphy;
 
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (res == NULL) {
@@ -2360,21 +2401,11 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
 
        hdata->external_irq = gpio_to_irq(hdata->hpd_gpio);
 
-       /* create workqueue and hotplug work */
-       hdata->wq = alloc_workqueue("exynos-drm-hdmi",
-                       WQ_UNBOUND | WQ_NON_REENTRANT, 1);
-       if (hdata->wq == NULL) {
-               DRM_ERROR("Failed to create workqueue.\n");
-               ret = -ENOMEM;
-               goto err_hdmiphy;
-       }
-       INIT_WORK(&hdata->hotplug_work, hdmi_hotplug_func);
-
        ret = request_irq(hdata->internal_irq, hdmi_irq_handler,
                        IRQF_SHARED, "int_hdmi", hdata);
        if (ret) {
                DRM_ERROR("request int interrupt failed.\n");
-               goto err_workqueue;
+               goto err_hdmiphy;
        }
        disable_irq(hdata->internal_irq);
 
@@ -2387,6 +2418,9 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
        }
        disable_irq(hdata->external_irq);
 
+       setup_timer(&hdata->hotplug_timer, hdmi_hotplug_timer_func,
+                       (unsigned long)hdata);
+
        if (of_device_is_compatible(dev->of_node,
                "samsung,exynos5-hdmi")) {
                ret = hdmi_register_audio_device(pdev);
@@ -2412,12 +2446,10 @@ err_ext_irq:
        free_irq(hdata->external_irq, hdata);
 err_int_irq:
        free_irq(hdata->internal_irq, hdata);
- err_workqueue:
-       destroy_workqueue(hdata->wq);
 err_hdmiphy:
-       i2c_del_driver(&hdmiphy_driver);
+       put_device(&hdata->hdmiphy_port->dev);
 err_ddc:
-       i2c_del_driver(&ddc_driver);
+       put_device(&hdata->ddc_port->dev);
 err_iomap:
        iounmap(hdata->regs);
 err_req_region:
@@ -2435,7 +2467,7 @@ static int __devexit hdmi_remove(struct platform_device *pdev)
        struct hdmi_context *hdata = platform_get_drvdata(pdev);
        struct hdmi_resources *res = &hdata->res;
 
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+       DRM_DEBUG_KMS("[PDEV:%s]\n", pdev->name);
 
        hdmi_resource_poweroff(hdata);
 
@@ -2445,8 +2477,7 @@ static int __devexit hdmi_remove(struct platform_device *pdev)
        free_irq(hdata->internal_irq, hdata);
        free_irq(hdata->external_irq, hdata);
 
-       cancel_work_sync(&hdata->hotplug_work);
-       destroy_workqueue(hdata->wq);
+       del_timer(&hdata->hotplug_timer);
 
        clk_disable(res->hdmi);
        clk_disable(res->sclk_hdmi);
@@ -2457,10 +2488,8 @@ static int __devexit hdmi_remove(struct platform_device *pdev)
        release_mem_region(hdata->regs_res->start,
                        resource_size(hdata->regs_res));
 
-       /* hdmiphy i2c driver */
-       i2c_del_driver(&hdmiphy_driver);
-       /* DDC i2c driver */
-       i2c_del_driver(&ddc_driver);
+       put_device(&hdata->hdmiphy_port->dev);
+       put_device(&hdata->ddc_port->dev);
 
        kfree(hdata);