Merge tag 'nds32-for-linus-4.18' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / gpu / drm / bridge / sil-sii8620.c
index ff041be..a6e8f45 100644 (file)
@@ -14,6 +14,7 @@
 #include <drm/bridge/mhl.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_edid.h>
+#include <drm/drm_encoder.h>
 
 #include <linux/clk.h>
 #include <linux/delay.h>
@@ -72,9 +73,7 @@ struct sii8620 {
        struct regulator_bulk_data supplies[2];
        struct mutex lock; /* context lock, protects fields below */
        int error;
-       int pixel_clock;
        unsigned int use_packed_pixel:1;
-       int video_code;
        enum sii8620_mode mode;
        enum sii8620_sink_type sink_type;
        u8 cbus_status;
@@ -82,7 +81,9 @@ struct sii8620 {
        u8 xstat[MHL_XDS_SIZE];
        u8 devcap[MHL_DCAP_SIZE];
        u8 xdevcap[MHL_XDC_SIZE];
-       u8 avif[HDMI_INFOFRAME_SIZE(AVI)];
+       bool feature_complete;
+       bool devcap_read;
+       bool sink_detected;
        struct edid *edid;
        unsigned int gen2_write_burst:1;
        enum sii8620_mt_state mt_state;
@@ -479,7 +480,7 @@ static void sii8620_update_array(u8 *dst, u8 *src, int count)
        }
 }
 
-static void sii8620_sink_detected(struct sii8620 *ctx, int ret)
+static void sii8620_identify_sink(struct sii8620 *ctx)
 {
        static const char * const sink_str[] = {
                [SINK_NONE] = "NONE",
@@ -490,7 +491,7 @@ static void sii8620_sink_detected(struct sii8620 *ctx, int ret)
        char sink_name[20];
        struct device *dev = ctx->dev;
 
-       if (ret < 0)
+       if (!ctx->sink_detected || !ctx->devcap_read)
                return;
 
        sii8620_fetch_edid(ctx);
@@ -499,6 +500,7 @@ static void sii8620_sink_detected(struct sii8620 *ctx, int ret)
                sii8620_mhl_disconnected(ctx);
                return;
        }
+       sii8620_set_upstream_edid(ctx);
 
        if (drm_detect_hdmi_monitor(ctx->edid))
                ctx->sink_type = SINK_HDMI;
@@ -511,15 +513,6 @@ static void sii8620_sink_detected(struct sii8620 *ctx, int ret)
                 sink_str[ctx->sink_type], sink_name);
 }
 
-static void sii8620_edid_read(struct sii8620 *ctx, int ret)
-{
-       if (ret < 0)
-               return;
-
-       sii8620_set_upstream_edid(ctx);
-       sii8620_enable_hpd(ctx);
-}
-
 static void sii8620_mr_devcap(struct sii8620 *ctx)
 {
        u8 dcap[MHL_DCAP_SIZE];
@@ -535,6 +528,8 @@ static void sii8620_mr_devcap(struct sii8620 *ctx)
                 dcap[MHL_DCAP_ADOPTER_ID_H], dcap[MHL_DCAP_ADOPTER_ID_L],
                 dcap[MHL_DCAP_DEVICE_ID_H], dcap[MHL_DCAP_DEVICE_ID_L]);
        sii8620_update_array(ctx->devcap, dcap, MHL_DCAP_SIZE);
+       ctx->devcap_read = true;
+       sii8620_identify_sink(ctx);
 }
 
 static void sii8620_mr_xdevcap(struct sii8620 *ctx)
@@ -1027,23 +1022,23 @@ static void sii8620_set_format(struct sii8620 *ctx)
                                BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE_PACKED,
                                ctx->use_packed_pixel ? ~0 : 0);
        } else {
-               if (ctx->use_packed_pixel)
+               if (ctx->use_packed_pixel) {
                        sii8620_write_seq_static(ctx,
                                REG_VID_MODE, BIT_VID_MODE_M1080P,
                                REG_MHL_TOP_CTL, BIT_MHL_TOP_CTL_MHL_PP_SEL | 1,
                                REG_MHLTX_CTL6, 0x60
                        );
-               else
+               } else {
                        sii8620_write_seq_static(ctx,
                                REG_VID_MODE, 0,
                                REG_MHL_TOP_CTL, 1,
                                REG_MHLTX_CTL6, 0xa0
                        );
+               }
        }
 
        if (ctx->use_packed_pixel)
-               out_fmt = VAL_TPI_FORMAT(YCBCR422, FULL) |
-                       BIT_TPI_OUTPUT_CSCMODE709;
+               out_fmt = VAL_TPI_FORMAT(YCBCR422, FULL);
        else
                out_fmt = VAL_TPI_FORMAT(RGB, FULL);
 
@@ -1100,18 +1095,28 @@ static ssize_t mhl3_infoframe_pack(struct mhl3_infoframe *frame,
        return frm_len;
 }
 
-static void sii8620_set_infoframes(struct sii8620 *ctx)
+static void sii8620_set_infoframes(struct sii8620 *ctx,
+                                  struct drm_display_mode *mode)
 {
        struct mhl3_infoframe mhl_frm;
        union hdmi_infoframe frm;
        u8 buf[31];
        int ret;
 
+       ret = drm_hdmi_avi_infoframe_from_display_mode(&frm.avi,
+                                                      mode,
+                                                      true);
+       if (ctx->use_packed_pixel)
+               frm.avi.colorspace = HDMI_COLORSPACE_YUV422;
+
+       if (!ret)
+               ret = hdmi_avi_infoframe_pack(&frm.avi, buf, ARRAY_SIZE(buf));
+       if (ret > 0)
+               sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, buf + 3, ret - 3);
+
        if (!sii8620_is_mhl3(ctx) || !ctx->use_packed_pixel) {
                sii8620_write(ctx, REG_TPI_SC,
                        BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI);
-               sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, ctx->avif + 3,
-                       ARRAY_SIZE(ctx->avif) - 3);
                sii8620_write(ctx, REG_PKT_FILTER_0,
                        BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT |
                        BIT_PKT_FILTER_0_DROP_MPEG_PKT |
@@ -1120,16 +1125,6 @@ static void sii8620_set_infoframes(struct sii8620 *ctx)
                return;
        }
 
-       ret = hdmi_avi_infoframe_init(&frm.avi);
-       frm.avi.colorspace = HDMI_COLORSPACE_YUV422;
-       frm.avi.active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
-       frm.avi.picture_aspect = HDMI_PICTURE_ASPECT_16_9;
-       frm.avi.colorimetry = HDMI_COLORIMETRY_ITU_709;
-       frm.avi.video_code = ctx->video_code;
-       if (!ret)
-               ret = hdmi_avi_infoframe_pack(&frm.avi, buf, ARRAY_SIZE(buf));
-       if (ret > 0)
-               sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, buf + 3, ret - 3);
        sii8620_write(ctx, REG_PKT_FILTER_0,
                BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT |
                BIT_PKT_FILTER_0_DROP_MPEG_PKT |
@@ -1149,6 +1144,9 @@ static void sii8620_set_infoframes(struct sii8620 *ctx)
 
 static void sii8620_start_video(struct sii8620 *ctx)
 {
+       struct drm_display_mode *mode =
+               &ctx->bridge.encoder->crtc->state->adjusted_mode;
+
        if (!sii8620_is_mhl3(ctx))
                sii8620_stop_video(ctx);
 
@@ -1167,8 +1165,14 @@ static void sii8620_start_video(struct sii8620 *ctx)
        sii8620_set_format(ctx);
 
        if (!sii8620_is_mhl3(ctx)) {
-               sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE),
-                       MHL_DST_LM_CLK_MODE_NORMAL | MHL_DST_LM_PATH_ENABLED);
+               u8 link_mode = MHL_DST_LM_PATH_ENABLED;
+
+               if (ctx->use_packed_pixel)
+                       link_mode |= MHL_DST_LM_CLK_MODE_PACKED_PIXEL;
+               else
+                       link_mode |= MHL_DST_LM_CLK_MODE_NORMAL;
+
+               sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), link_mode);
                sii8620_set_auto_zone(ctx);
        } else {
                static const struct {
@@ -1185,7 +1189,7 @@ static void sii8620_start_video(struct sii8620 *ctx)
                          MHL_XDS_LINK_RATE_6_0_GBPS, 0x40 },
                };
                u8 p0_ctrl = BIT_M3_P0CTRL_MHL3_P0_PORT_EN;
-               int clk = ctx->pixel_clock * (ctx->use_packed_pixel ? 2 : 3);
+               int clk = mode->clock * (ctx->use_packed_pixel ? 2 : 3);
                int i;
 
                for (i = 0; i < ARRAY_SIZE(clk_spec) - 1; ++i)
@@ -1214,7 +1218,7 @@ static void sii8620_start_video(struct sii8620 *ctx)
                        clk_spec[i].link_rate);
        }
 
-       sii8620_set_infoframes(ctx);
+       sii8620_set_infoframes(ctx, mode);
 }
 
 static void sii8620_disable_hpd(struct sii8620 *ctx)
@@ -1506,6 +1510,16 @@ static void sii8620_set_mode(struct sii8620 *ctx, enum sii8620_mode mode)
        );
 }
 
+static void sii8620_hpd_unplugged(struct sii8620 *ctx)
+{
+       sii8620_disable_hpd(ctx);
+       ctx->sink_type = SINK_NONE;
+       ctx->sink_detected = false;
+       ctx->feature_complete = false;
+       kfree(ctx->edid);
+       ctx->edid = NULL;
+}
+
 static void sii8620_disconnect(struct sii8620 *ctx)
 {
        sii8620_disable_gen2_write_burst(ctx);
@@ -1533,7 +1547,7 @@ static void sii8620_disconnect(struct sii8620 *ctx)
                REG_MHL_DP_CTL6, 0x2A,
                REG_MHL_DP_CTL7, 0x03
        );
-       sii8620_disable_hpd(ctx);
+       sii8620_hpd_unplugged(ctx);
        sii8620_write_seq_static(ctx,
                REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE,
                REG_MHL_COC_CTL1, 0x07,
@@ -1581,10 +1595,8 @@ static void sii8620_disconnect(struct sii8620 *ctx)
        memset(ctx->xstat, 0, sizeof(ctx->xstat));
        memset(ctx->devcap, 0, sizeof(ctx->devcap));
        memset(ctx->xdevcap, 0, sizeof(ctx->xdevcap));
+       ctx->devcap_read = false;
        ctx->cbus_status = 0;
-       ctx->sink_type = SINK_NONE;
-       kfree(ctx->edid);
-       ctx->edid = NULL;
        sii8620_mt_cleanup(ctx);
 }
 
@@ -1671,17 +1683,18 @@ static void sii8620_status_dcap_ready(struct sii8620 *ctx)
 
 static void sii8620_status_changed_path(struct sii8620 *ctx)
 {
-       if (ctx->stat[MHL_DST_LINK_MODE] & MHL_DST_LM_PATH_ENABLED) {
-               sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE),
-                                     MHL_DST_LM_CLK_MODE_NORMAL
-                                     | MHL_DST_LM_PATH_ENABLED);
-               if (!sii8620_is_mhl3(ctx))
-                       sii8620_mt_read_devcap(ctx, false);
-               sii8620_mt_set_cont(ctx, sii8620_sink_detected);
-       } else {
-               sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE),
-                                     MHL_DST_LM_CLK_MODE_NORMAL);
-       }
+       u8 link_mode;
+
+       if (ctx->use_packed_pixel)
+               link_mode = MHL_DST_LM_CLK_MODE_PACKED_PIXEL;
+       else
+               link_mode = MHL_DST_LM_CLK_MODE_NORMAL;
+
+       if (ctx->stat[MHL_DST_LINK_MODE] & MHL_DST_LM_PATH_ENABLED)
+               link_mode |= MHL_DST_LM_PATH_ENABLED;
+
+       sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE),
+                             link_mode);
 }
 
 static void sii8620_msc_mr_write_stat(struct sii8620 *ctx)
@@ -1694,9 +1707,14 @@ static void sii8620_msc_mr_write_stat(struct sii8620 *ctx)
        sii8620_update_array(ctx->stat, st, MHL_DST_SIZE);
        sii8620_update_array(ctx->xstat, xst, MHL_XDS_SIZE);
 
-       if (ctx->stat[MHL_DST_CONNECTED_RDY] & MHL_DST_CONN_DCAP_RDY)
+       if (ctx->stat[MHL_DST_CONNECTED_RDY] & st[MHL_DST_CONNECTED_RDY] &
+           MHL_DST_CONN_DCAP_RDY) {
                sii8620_status_dcap_ready(ctx);
 
+               if (!sii8620_is_mhl3(ctx))
+                       sii8620_mt_read_devcap(ctx, false);
+       }
+
        if (st[MHL_DST_LINK_MODE] & MHL_DST_LM_PATH_ENABLED)
                sii8620_status_changed_path(ctx);
 }
@@ -1780,8 +1798,11 @@ static void sii8620_msc_mr_set_int(struct sii8620 *ctx)
        }
        if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_REQ)
                sii8620_send_features(ctx);
-       if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_COMPLETE)
-               sii8620_edid_read(ctx, 0);
+       if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_COMPLETE) {
+               ctx->feature_complete = true;
+               if (ctx->edid)
+                       sii8620_enable_hpd(ctx);
+       }
 }
 
 static struct sii8620_mt_msg *sii8620_msc_msg_first(struct sii8620 *ctx)
@@ -1856,6 +1877,15 @@ static void sii8620_irq_msc(struct sii8620 *ctx)
        if (stat & BIT_CBUS_MSC_MR_WRITE_STAT)
                sii8620_msc_mr_write_stat(ctx);
 
+       if (stat & BIT_CBUS_HPD_CHG) {
+               if (ctx->cbus_status & BIT_CBUS_STATUS_CBUS_HPD) {
+                       ctx->sink_detected = true;
+                       sii8620_identify_sink(ctx);
+               } else {
+                       sii8620_hpd_unplugged(ctx);
+               }
+       }
+
        if (stat & BIT_CBUS_MSC_MR_SET_INT)
                sii8620_msc_mr_set_int(ctx);
 
@@ -1967,11 +1997,11 @@ static void sii8620_irq_ddc(struct sii8620 *ctx)
 
        if (stat & BIT_DDC_CMD_DONE) {
                sii8620_write(ctx, REG_INTR3_MASK, 0);
-               if (sii8620_is_mhl3(ctx))
+               if (sii8620_is_mhl3(ctx) && !ctx->feature_complete)
                        sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE),
                                           MHL_INT_RC_FEAT_REQ);
                else
-                       sii8620_edid_read(ctx, 0);
+                       sii8620_enable_hpd(ctx);
        }
        sii8620_write(ctx, REG_INTR3, stat);
 }
@@ -2238,8 +2268,6 @@ static bool sii8620_mode_fixup(struct drm_bridge *bridge,
        mutex_lock(&ctx->lock);
 
        ctx->use_packed_pixel = sii8620_is_packing_required(ctx, adjusted_mode);
-       ctx->video_code = drm_match_cea_mode(adjusted_mode);
-       ctx->pixel_clock = adjusted_mode->clock;
 
        mutex_unlock(&ctx->lock);