drm/vc4: hdmi: Fix hotplug extcon uevent to works
[platform/kernel/linux-rpi.git] / drivers / gpu / drm / vc4 / vc4_hdmi.c
index c52e4d6..5a20e7b 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/clk.h>
 #include <linux/component.h>
 #include <linux/gpio/consumer.h>
+#include <linux/extcon-provider.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
@@ -477,6 +478,15 @@ static int vc4_hdmi_connector_detect_ctx(struct drm_connector *connector,
        vc4_hdmi_handle_hotplug(vc4_hdmi, ctx, status);
        pm_runtime_put(&vc4_hdmi->pdev->dev);
 
+#ifdef CONFIG_EXTCON
+       if (status != vc4_hdmi->status) {
+               extcon_set_state_sync(vc4_hdmi->edev, EXTCON_DISP_HDMI,
+                                     (status == connector_status_connected ?
+                                     true : false));
+               vc4_hdmi->status = status;
+       }
+#endif
+
        return status;
 }
 
@@ -507,7 +517,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
        ret = drm_add_edid_modes(connector, edid);
        kfree(edid);
 
-       if (!vc4->hvs->vc5_hdmi_enable_scrambling) {
+       if (!vc4->hvs->vc5_hdmi_enable_hdmi_20) {
                struct drm_device *drm = connector->dev;
                const struct drm_display_mode *mode;
 
@@ -1959,6 +1969,7 @@ vc4_hdmi_sink_supports_format_bpc(const struct vc4_hdmi *vc4_hdmi,
 
 static enum drm_mode_status
 vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi,
+                            const struct drm_display_mode *mode,
                             unsigned long long clock)
 {
        const struct drm_connector *connector = &vc4_hdmi->connector;
@@ -1968,7 +1979,13 @@ vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi,
        if (clock > vc4_hdmi->variant->max_pixel_clock)
                return MODE_CLOCK_HIGH;
 
-       if (!vc4->hvs->vc5_hdmi_enable_scrambling && clock > HDMI_14_MAX_TMDS_CLK)
+       if (!vc4->hvs->vc5_hdmi_enable_hdmi_20 && clock > HDMI_14_MAX_TMDS_CLK)
+               return MODE_CLOCK_HIGH;
+
+       /* 4096x2160@60 is not reliable without overclocking core */
+       if (!vc4->hvs->vc5_hdmi_enable_4096by2160 &&
+           mode->hdisplay > 3840 && mode->vdisplay >= 2160 &&
+           drm_mode_vrefresh(mode) >= 50)
                return MODE_CLOCK_HIGH;
 
        if (info->max_tmds_clock && clock > (info->max_tmds_clock * 1000))
@@ -2002,7 +2019,7 @@ vc4_hdmi_encoder_compute_clock(const struct vc4_hdmi *vc4_hdmi,
        unsigned long long clock;
 
        clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt);
-       if (vc4_hdmi_encoder_clock_valid(vc4_hdmi, clock) != MODE_OK)
+       if (vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode, clock) != MODE_OK)
                return -EINVAL;
 
        vc4_state->pixel_rate = clock;
@@ -2101,7 +2118,6 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
        struct drm_connector *connector = &vc4_hdmi->connector;
        struct drm_connector_state *old_conn_state = drm_atomic_get_old_connector_state(conn_state->state, connector);
        struct vc4_hdmi_connector_state *old_vc4_state = conn_state_to_vc4_hdmi_conn_state(old_conn_state);
-       struct vc4_dev *vc4 = to_vc4_dev(connector->dev);
        unsigned long long pixel_rate = mode->clock * 1000;
        unsigned long long tmds_rate;
        int ret;
@@ -2126,12 +2142,6 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
                        return -EINVAL;
        }
 
-       /* 4096x2160@60 is not reliable without overclocking core */
-       if (mode->hdisplay > 3840 && mode->vdisplay >= 2160 &&
-           drm_mode_vrefresh(mode) >= 50 &&
-           !vc4->hvs->vc5_hdmi_enable_4096by2160)
-               return -EINVAL;
-
        /*
         * The 1440p@60 pixel rate is in the same range than the first
         * WiFi channel (between 2.4GHz and 2.422GHz with 22MHz
@@ -2163,8 +2173,6 @@ vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder,
                            const struct drm_display_mode *mode)
 {
        struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
-       const struct drm_connector *connector = &vc4_hdmi->connector;
-       struct vc4_dev *vc4 = to_vc4_dev(connector->dev);
 
        if (vc4_hdmi->variant->unsupported_odd_h_timings &&
            !(mode->flags & DRM_MODE_FLAG_DBLCLK) &&
@@ -2172,12 +2180,7 @@ vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder,
             (mode->hsync_end % 2) || (mode->htotal % 2)))
                return MODE_H_ILLEGAL;
 
-       if (mode->hdisplay > 3840 && mode->vdisplay >= 2160 &&
-           drm_mode_vrefresh(mode) >= 50 &&
-           !vc4->hvs->vc5_hdmi_enable_4096by2160)
-               return MODE_CLOCK_HIGH;
-
-       return vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode->clock * 1000);
+       return vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode, mode->clock * 1000);
 }
 
 static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
@@ -3593,6 +3596,13 @@ static void vc4_hdmi_put_ddc_device(void *ptr)
        put_device(&vc4_hdmi->ddc->dev);
 }
 
+#ifdef CONFIG_EXTCON
+static const unsigned int vc4_hdmi_extcon_cable[] = {
+       EXTCON_DISP_HDMI,
+       EXTCON_NONE,
+};
+#endif
+
 static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 {
        const struct vc4_hdmi_variant *variant = of_device_get_match_data(dev);
@@ -3655,6 +3665,23 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
        if (ret)
                return ret;
 
+#ifdef CONFIG_EXTCON
+       vc4_hdmi->status = connector_status_disconnected;
+
+       /* Initialize extcon device */
+       vc4_hdmi->edev = devm_extcon_dev_allocate(dev, vc4_hdmi_extcon_cable);
+       if (IS_ERR(vc4_hdmi->edev)) {
+               dev_err(dev, "failed to allocate memory for extcon\n");
+               return PTR_ERR(vc4_hdmi->edev);
+       }
+
+       ret = devm_extcon_dev_register(dev, vc4_hdmi->edev);
+       if (ret) {
+               dev_err(dev, "failed to register extcon device\n");
+               return ret;
+       }
+#endif
+
        /* Only use the GPIO HPD pin if present in the DT, otherwise
         * we'll use the HDMI core's register.
         */