drm/exynos: Implement drm_connector in hdmi directly
authorSean Paul <seanpaul@chromium.org>
Thu, 30 Jan 2014 21:19:29 +0000 (16:19 -0500)
committerInki Dae <daeinki@gmail.com>
Sun, 23 Mar 2014 15:36:36 +0000 (00:36 +0900)
This patch implements drm_connector in the hdmi driver directly, instead
of using exynos_drm_connector.

Signed-off-by: Sean Paul <seanpaul@chromium.org>
Signed-off-by: Inki Dae <inki.dae@samsung.com>
drivers/gpu/drm/exynos/exynos_hdmi.c

index 1a06a54a901f8790b5e3805d6e51500ac45affac..0097032cd350b35a4c6781b908d3d15c434913bb 100644 (file)
@@ -45,9 +45,8 @@
 #include <linux/gpio.h>
 #include <media/s5p_hdmi.h>
 
-#define MAX_WIDTH              1920
-#define MAX_HEIGHT             1080
 #define get_hdmi_display(dev)  platform_get_drvdata(to_platform_device(dev))
+#define ctx_from_connector(c)  container_of(c, struct hdmi_context, connector)
 
 /* AVI header and aspect ratio */
 #define HDMI_AVI_VERSION               0x02
@@ -172,6 +171,8 @@ struct hdmi_conf_regs {
 struct hdmi_context {
        struct device                   *dev;
        struct drm_device               *drm_dev;
+       struct drm_connector            connector;
+       struct drm_encoder              *encoder;
        bool                            hpd;
        bool                            powered;
        bool                            dvi_mode;
@@ -790,42 +791,46 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,
        }
 }
 
-static int hdmi_initialize(struct exynos_drm_display *display,
-                       struct drm_device *drm_dev)
+static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
+                               bool force)
 {
-       struct hdmi_context *hdata = display->ctx;
+       struct hdmi_context *hdata = ctx_from_connector(connector);
 
-       hdata->drm_dev = drm_dev;
-
-       return 0;
+       return hdata->hpd ? connector_status_connected :
+                       connector_status_disconnected;
 }
 
-static bool hdmi_is_connected(struct exynos_drm_display *display)
+static void hdmi_connector_destroy(struct drm_connector *connector)
 {
-       struct hdmi_context *hdata = display->ctx;
-
-       return hdata->hpd;
 }
 
-static struct edid *hdmi_get_edid(struct exynos_drm_display *display,
-                       struct drm_connector *connector)
+static struct drm_connector_funcs hdmi_connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .detect = hdmi_detect,
+       .destroy = hdmi_connector_destroy,
+};
+
+static int hdmi_get_modes(struct drm_connector *connector)
 {
-       struct edid *raw_edid;
-       struct hdmi_context *hdata = display->ctx;
+       struct hdmi_context *hdata = ctx_from_connector(connector);
+       struct edid *edid;
 
        if (!hdata->ddc_port)
-               return ERR_PTR(-ENODEV);
+               return -ENODEV;
 
-       raw_edid = drm_get_edid(connector, hdata->ddc_port->adapter);
-       if (!raw_edid)
-               return ERR_PTR(-ENODEV);
+       edid = drm_get_edid(connector, hdata->ddc_port->adapter);
+       if (!edid)
+               return -ENODEV;
 
-       hdata->dvi_mode = !drm_detect_hdmi_monitor(raw_edid);
+       hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);
        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);
+               edid->width_cm, edid->height_cm);
+
+       drm_mode_connector_update_edid_property(connector, edid);
 
-       return raw_edid;
+       return drm_add_edid_modes(connector, edid);
 }
 
 static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
@@ -850,10 +855,10 @@ static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
        return -EINVAL;
 }
 
-static int hdmi_check_mode(struct exynos_drm_display *display,
+static int hdmi_mode_valid(struct drm_connector *connector,
                        struct drm_display_mode *mode)
 {
-       struct hdmi_context *hdata = display->ctx;
+       struct hdmi_context *hdata = ctx_from_connector(connector);
        int ret;
 
        DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
@@ -863,11 +868,60 @@ static int hdmi_check_mode(struct exynos_drm_display *display,
 
        ret = mixer_check_mode(mode);
        if (ret)
-               return ret;
+               return MODE_BAD;
 
        ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);
        if (ret < 0)
+               return MODE_BAD;
+
+       return MODE_OK;
+}
+
+static struct drm_encoder *hdmi_best_encoder(struct drm_connector *connector)
+{
+       struct hdmi_context *hdata = ctx_from_connector(connector);
+
+       return hdata->encoder;
+}
+
+static struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
+       .get_modes = hdmi_get_modes,
+       .mode_valid = hdmi_mode_valid,
+       .best_encoder = hdmi_best_encoder,
+};
+
+static int hdmi_create_connector(struct exynos_drm_display *display,
+                       struct drm_encoder *encoder)
+{
+       struct hdmi_context *hdata = display->ctx;
+       struct drm_connector *connector = &hdata->connector;
+       int ret;
+
+       hdata->encoder = encoder;
+       connector->interlace_allowed = true;
+       connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+       ret = drm_connector_init(hdata->drm_dev, connector,
+                       &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
+       if (ret) {
+               DRM_ERROR("Failed to initialize connector with drm\n");
                return ret;
+       }
+
+       drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
+       drm_sysfs_connector_add(connector);
+       drm_mode_connector_attach_encoder(connector, encoder);
+
+       return 0;
+}
+
+static int hdmi_initialize(struct exynos_drm_display *display,
+                       struct drm_device *drm_dev)
+{
+       struct hdmi_context *hdata = display->ctx;
+
+       hdata->drm_dev = drm_dev;
+
        return 0;
 }
 
@@ -883,10 +937,10 @@ static void hdmi_mode_fixup(struct exynos_drm_display *display,
 
        drm_mode_set_crtcinfo(adjusted_mode, 0);
 
-       mode_ok = hdmi_check_mode(display, adjusted_mode);
+       mode_ok = hdmi_mode_valid(connector, adjusted_mode);
 
        /* just return if user desired mode exists. */
-       if (mode_ok == 0)
+       if (mode_ok == MODE_OK)
                return;
 
        /*
@@ -894,9 +948,9 @@ static void hdmi_mode_fixup(struct exynos_drm_display *display,
         * to adjusted_mode.
         */
        list_for_each_entry(m, &connector->modes, head) {
-               mode_ok = hdmi_check_mode(display, m);
+               mode_ok = hdmi_mode_valid(connector, m);
 
-               if (mode_ok == 0) {
+               if (mode_ok == MODE_OK) {
                        DRM_INFO("desired mode doesn't exist so\n");
                        DRM_INFO("use the most suitable mode among modes.\n");
 
@@ -1753,13 +1807,6 @@ static void hdmi_mode_set(struct exynos_drm_display *display,
                hdmi_v14_mode_set(hdata, mode);
 }
 
-static void hdmi_get_max_resol(struct exynos_drm_display *display,
-                       unsigned int *width, unsigned int *height)
-{
-       *width = MAX_WIDTH;
-       *height = MAX_HEIGHT;
-}
-
 static void hdmi_commit(struct exynos_drm_display *display)
 {
        struct hdmi_context *hdata = display->ctx;
@@ -1854,10 +1901,7 @@ static void hdmi_dpms(struct exynos_drm_display *display, int mode)
 
 static struct exynos_drm_display_ops hdmi_display_ops = {
        .initialize     = hdmi_initialize,
-       .is_connected   = hdmi_is_connected,
-       .get_max_resol  = hdmi_get_max_resol,
-       .get_edid       = hdmi_get_edid,
-       .check_mode     = hdmi_check_mode,
+       .create_connector = hdmi_create_connector,
        .mode_fixup     = hdmi_mode_fixup,
        .mode_set       = hdmi_mode_set,
        .dpms           = hdmi_dpms,