Merge tag 'sound-6.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
[linux-2.6-block.git] / drivers / gpu / drm / vc4 / vc4_hdmi.c
index 3e3bd88745b7cc264048d9c5fa371a5582ba5b84..592c3b5d03e6e1f296dace9bf27b0a39c8a42dd9 100644 (file)
 #define VC5_HDMI_VERTB_VSPO_SHIFT              16
 #define VC5_HDMI_VERTB_VSPO_MASK               VC4_MASK(29, 16)
 
+#define VC4_HDMI_MISC_CONTROL_PIXEL_REP_SHIFT  0
+#define VC4_HDMI_MISC_CONTROL_PIXEL_REP_MASK   VC4_MASK(3, 0)
+#define VC5_HDMI_MISC_CONTROL_PIXEL_REP_SHIFT  0
+#define VC5_HDMI_MISC_CONTROL_PIXEL_REP_MASK   VC4_MASK(3, 0)
+
 #define VC5_HDMI_SCRAMBLER_CTL_ENABLE          BIT(0)
 
 #define VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_SHIFT     8
@@ -145,6 +150,12 @@ static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
 
        drm_print_regset32(&p, &vc4_hdmi->hdmi_regset);
        drm_print_regset32(&p, &vc4_hdmi->hd_regset);
+       drm_print_regset32(&p, &vc4_hdmi->cec_regset);
+       drm_print_regset32(&p, &vc4_hdmi->csc_regset);
+       drm_print_regset32(&p, &vc4_hdmi->dvp_regset);
+       drm_print_regset32(&p, &vc4_hdmi->phy_regset);
+       drm_print_regset32(&p, &vc4_hdmi->ram_regset);
+       drm_print_regset32(&p, &vc4_hdmi->rm_regset);
 
        return 0;
 }
@@ -418,6 +429,7 @@ static int vc4_hdmi_connector_init(struct drm_device *dev,
 
        connector->interlace_allowed = 1;
        connector->doublescan_allowed = 0;
+       connector->stereo_allowed = 1;
 
        if (vc4_hdmi->variant->supports_hdr)
                drm_connector_attach_hdr_output_metadata_property(connector);
@@ -455,9 +467,11 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
        const struct vc4_hdmi_register *ram_packet_start =
                &vc4_hdmi->variant->registers[HDMI_RAM_PACKET_START];
        u32 packet_reg = ram_packet_start->offset + VC4_HDMI_PACKET_STRIDE * packet_id;
+       u32 packet_reg_next = ram_packet_start->offset +
+               VC4_HDMI_PACKET_STRIDE * (packet_id + 1);
        void __iomem *base = __vc4_hdmi_get_field_base(vc4_hdmi,
                                                       ram_packet_start->reg);
-       uint8_t buffer[VC4_HDMI_PACKET_STRIDE];
+       uint8_t buffer[VC4_HDMI_PACKET_STRIDE] = {};
        unsigned long flags;
        ssize_t len, i;
        int ret;
@@ -493,6 +507,13 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
                packet_reg += 4;
        }
 
+       /*
+        * clear remainder of packet ram as it's included in the
+        * infoframe and triggers a checksum error on hdmi analyser
+        */
+       for (; packet_reg < packet_reg_next; packet_reg += 4)
+               writel(0, base + packet_reg);
+
        HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
                   HDMI_READ(HDMI_RAM_PACKET_CONFIG) | BIT(packet_id));
 
@@ -584,7 +605,9 @@ static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder)
        union hdmi_infoframe frame;
 
        memcpy(&frame.audio, audio, sizeof(*audio));
-       vc4_hdmi_write_infoframe(encoder, &frame);
+
+       if (vc4_hdmi->packet_ram_enabled)
+               vc4_hdmi_write_infoframe(encoder, &frame);
 }
 
 static void vc4_hdmi_set_hdr_infoframe(struct drm_encoder *encoder)
@@ -724,6 +747,8 @@ static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder,
 
        mutex_lock(&vc4_hdmi->mutex);
 
+       vc4_hdmi->packet_ram_enabled = false;
+
        spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
        HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0);
@@ -771,15 +796,6 @@ static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder,
        mutex_unlock(&vc4_hdmi->mutex);
 }
 
-static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
-{
-       struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
-
-       mutex_lock(&vc4_hdmi->mutex);
-       vc4_hdmi->output_enabled = false;
-       mutex_unlock(&vc4_hdmi->mutex);
-}
-
 static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
                               struct drm_connector_state *state,
                               const struct drm_display_mode *mode)
@@ -970,14 +986,15 @@ static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
                                   VC4_HDMI_VERTA_VFP) |
                     VC4_SET_FIELD(mode->crtc_vdisplay, VC4_HDMI_VERTA_VAL));
        u32 vertb = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) |
-                    VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
+                    VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end +
+                                  interlaced,
                                   VC4_HDMI_VERTB_VBP));
        u32 vertb_even = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) |
                          VC4_SET_FIELD(mode->crtc_vtotal -
-                                       mode->crtc_vsync_end -
-                                       interlaced,
+                                       mode->crtc_vsync_end,
                                        VC4_HDMI_VERTB_VBP));
        unsigned long flags;
+       u32 reg;
 
        spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
@@ -1004,6 +1021,11 @@ static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
        HDMI_WRITE(HDMI_VERTB0, vertb_even);
        HDMI_WRITE(HDMI_VERTB1, vertb);
 
+       reg = HDMI_READ(HDMI_MISC_CONTROL);
+       reg &= ~VC4_HDMI_MISC_CONTROL_PIXEL_REP_MASK;
+       reg |= VC4_SET_FIELD(pixel_rep - 1, VC4_HDMI_MISC_CONTROL_PIXEL_REP);
+       HDMI_WRITE(HDMI_MISC_CONTROL, reg);
+
        spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
 }
 
@@ -1022,13 +1044,13 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
                     VC4_SET_FIELD(mode->crtc_vsync_start - mode->crtc_vdisplay,
                                   VC5_HDMI_VERTA_VFP) |
                     VC4_SET_FIELD(mode->crtc_vdisplay, VC5_HDMI_VERTA_VAL));
-       u32 vertb = (VC4_SET_FIELD(0, VC5_HDMI_VERTB_VSPO) |
+       u32 vertb = (VC4_SET_FIELD(mode->htotal >> (2 - pixel_rep),
+                                  VC5_HDMI_VERTB_VSPO) |
                     VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
                                   VC4_HDMI_VERTB_VBP));
        u32 vertb_even = (VC4_SET_FIELD(0, VC5_HDMI_VERTB_VSPO) |
                          VC4_SET_FIELD(mode->crtc_vtotal -
-                                       mode->crtc_vsync_end -
-                                       interlaced,
+                                       mode->crtc_vsync_end - interlaced,
                                        VC4_HDMI_VERTB_VBP));
        unsigned long flags;
        unsigned char gcp;
@@ -1102,6 +1124,11 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
        reg |= gcp_en ? VC5_HDMI_GCP_CONFIG_GCP_ENABLE : 0;
        HDMI_WRITE(HDMI_GCP_CONFIG, reg);
 
+       reg = HDMI_READ(HDMI_MISC_CONTROL);
+       reg &= ~VC5_HDMI_MISC_CONTROL_PIXEL_REP_MASK;
+       reg |= VC4_SET_FIELD(pixel_rep - 1, VC5_HDMI_MISC_CONTROL_PIXEL_REP);
+       HDMI_WRITE(HDMI_MISC_CONTROL, reg);
+
        HDMI_WRITE(HDMI_CLOCK_STOP, 0);
 
        spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
@@ -1330,14 +1357,12 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
 
                WARN_ON(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) &
                          VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE));
-               HDMI_WRITE(HDMI_SCHEDULER_CONTROL,
-                          HDMI_READ(HDMI_SCHEDULER_CONTROL) |
-                          VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT);
 
                HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
                           VC4_HDMI_RAM_PACKET_ENABLE);
 
                spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+               vc4_hdmi->packet_ram_enabled = true;
 
                vc4_hdmi_set_infoframes(encoder);
        }
@@ -1348,15 +1373,6 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
        mutex_unlock(&vc4_hdmi->mutex);
 }
 
-static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
-{
-       struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
-
-       mutex_lock(&vc4_hdmi->mutex);
-       vc4_hdmi->output_enabled = true;
-       mutex_unlock(&vc4_hdmi->mutex);
-}
-
 static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder,
                                             struct drm_crtc_state *crtc_state,
                                             struct drm_connector_state *conn_state)
@@ -1481,7 +1497,7 @@ vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode,
                                    unsigned int bpc,
                                    enum vc4_hdmi_output_format fmt)
 {
-       unsigned long long clock = mode->clock * 1000;
+       unsigned long long clock = mode->clock * 1000ULL;
 
        if (mode->flags & DRM_MODE_FLAG_DBLCLK)
                clock = clock * 2;
@@ -1597,18 +1613,37 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
                                         struct drm_crtc_state *crtc_state,
                                         struct drm_connector_state *conn_state)
 {
+       struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(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_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state);
        struct drm_display_mode *mode = &crtc_state->adjusted_mode;
-       struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
        unsigned long long tmds_char_rate = mode->clock * 1000;
        unsigned long long tmds_bit_rate;
        int ret;
 
-       if (vc4_hdmi->variant->unsupported_odd_h_timings &&
-           !(mode->flags & DRM_MODE_FLAG_DBLCLK) &&
-           ((mode->hdisplay % 2) || (mode->hsync_start % 2) ||
-            (mode->hsync_end % 2) || (mode->htotal % 2)))
-               return -EINVAL;
+       if (vc4_hdmi->variant->unsupported_odd_h_timings) {
+               if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
+                       /* Only try to fixup DBLCLK modes to get 480i and 576i
+                        * working.
+                        * A generic solution for all modes with odd horizontal
+                        * timing values seems impossible based on trying to
+                        * solve it for 1366x768 monitors.
+                        */
+                       if ((mode->hsync_start - mode->hdisplay) & 1)
+                               mode->hsync_start--;
+                       if ((mode->hsync_end - mode->hsync_start) & 1)
+                               mode->hsync_end--;
+               }
+
+               /* Now check whether we still have odd values remaining */
+               if ((mode->hdisplay % 2) || (mode->hsync_start % 2) ||
+                   (mode->hsync_end % 2) || (mode->htotal % 2))
+                       return -EINVAL;
+       }
 
        /*
         * The 1440p@60 pixel rate is in the same range than the first
@@ -1628,6 +1663,11 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
        if (ret)
                return ret;
 
+       /* vc4_hdmi_encoder_compute_config may have changed output_bpc and/or output_format */
+       if (vc4_state->output_bpc != old_vc4_state->output_bpc ||
+           vc4_state->output_format != old_vc4_state->output_format)
+               crtc_state->mode_changed = true;
+
        return 0;
 }
 
@@ -1650,8 +1690,6 @@ static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
        .atomic_check = vc4_hdmi_encoder_atomic_check,
        .atomic_mode_set = vc4_hdmi_encoder_atomic_mode_set,
        .mode_valid = vc4_hdmi_encoder_mode_valid,
-       .disable = vc4_hdmi_encoder_disable,
-       .enable = vc4_hdmi_encoder_enable,
 };
 
 static u32 vc4_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask)
@@ -1748,19 +1786,15 @@ static inline struct vc4_hdmi *dai_to_hdmi(struct snd_soc_dai *dai)
 
 static bool vc4_hdmi_audio_can_stream(struct vc4_hdmi *vc4_hdmi)
 {
-       lockdep_assert_held(&vc4_hdmi->mutex);
+       struct drm_display_info *display = &vc4_hdmi->connector.display_info;
 
-       /*
-        * If the controller is disabled, prevent any ALSA output.
-        */
-       if (!vc4_hdmi->output_enabled)
-               return false;
+       lockdep_assert_held(&vc4_hdmi->mutex);
 
        /*
         * If the encoder is currently in DVI mode, treat the codec DAI
         * as missing.
         */
-       if (!(HDMI_READ(HDMI_RAM_PACKET_CONFIG) & VC4_HDMI_RAM_PACKET_ENABLE))
+       if (!display->is_hdmi)
                return false;
 
        return true;
@@ -1941,10 +1975,10 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data,
 
        /* Set the MAI threshold */
        HDMI_WRITE(HDMI_MAI_THR,
-                  VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICHIGH) |
-                  VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICLOW) |
-                  VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQHIGH) |
-                  VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQLOW));
+                  VC4_SET_FIELD(0x08, VC4_HD_MAI_THR_PANICHIGH) |
+                  VC4_SET_FIELD(0x08, VC4_HD_MAI_THR_PANICLOW) |
+                  VC4_SET_FIELD(0x06, VC4_HD_MAI_THR_DREQHIGH) |
+                  VC4_SET_FIELD(0x08, VC4_HD_MAI_THR_DREQLOW));
 
        HDMI_WRITE(HDMI_MAI_CONFIG,
                   VC4_HDMI_MAI_CONFIG_BIT_REVERSE |
@@ -2036,12 +2070,12 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
        struct device *dev = &vc4_hdmi->pdev->dev;
        struct platform_device *codec_pdev;
        const __be32 *addr;
-       int index;
+       int index, len;
        int ret;
 
-       if (!of_find_property(dev->of_node, "dmas", NULL)) {
+       if (!of_find_property(dev->of_node, "dmas", &len) || !len) {
                dev_warn(dev,
-                        "'dmas' DT property is missing, no HDMI audio\n");
+                        "'dmas' DT property is missing or empty, no HDMI audio\n");
                return 0;
        }
 
@@ -2522,8 +2556,6 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
        struct cec_connector_info conn_info;
        struct platform_device *pdev = vc4_hdmi->pdev;
        struct device *dev = &pdev->dev;
-       unsigned long flags;
-       u32 value;
        int ret;
 
        if (!of_find_property(dev->of_node, "interrupts", NULL)) {
@@ -2542,15 +2574,6 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
        cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector);
        cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info);
 
-       spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
-       value = HDMI_READ(HDMI_CEC_CNTRL_1);
-       /* Set the logical address to Unregistered */
-       value |= VC4_HDMI_CEC_ADDR_MASK;
-       HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
-       spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
-
-       vc4_hdmi_cec_update_clk_div(vc4_hdmi);
-
        if (vc4_hdmi->variant->external_irq_controller) {
                ret = request_threaded_irq(platform_get_irq_byname(pdev, "cec-rx"),
                                           vc4_cec_irq_handler_rx_bare,
@@ -2566,10 +2589,6 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
                if (ret)
                        goto err_remove_cec_rx_handler;
        } else {
-               spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
-               HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff);
-               spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
-
                ret = request_threaded_irq(platform_get_irq(pdev, 0),
                                           vc4_cec_irq_handler,
                                           vc4_cec_irq_handler_thread, 0,
@@ -2620,7 +2639,6 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
 }
 
 static void vc4_hdmi_cec_exit(struct vc4_hdmi *vc4_hdmi) {};
-
 #endif
 
 static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi,
@@ -2705,6 +2723,7 @@ static int vc5_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi)
        struct platform_device *pdev = vc4_hdmi->pdev;
        struct device *dev = &pdev->dev;
        struct resource *res;
+       int ret;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi");
        if (!res)
@@ -2801,6 +2820,38 @@ static int vc5_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi)
                return PTR_ERR(vc4_hdmi->reset);
        }
 
+       ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hdmi_regset, VC4_HDMI);
+       if (ret)
+               return ret;
+
+       ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hd_regset, VC4_HD);
+       if (ret)
+               return ret;
+
+       ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->cec_regset, VC5_CEC);
+       if (ret)
+               return ret;
+
+       ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->csc_regset, VC5_CSC);
+       if (ret)
+               return ret;
+
+       ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->dvp_regset, VC5_DVP);
+       if (ret)
+               return ret;
+
+       ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->phy_regset, VC5_PHY);
+       if (ret)
+               return ret;
+
+       ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->ram_regset, VC5_RAM);
+       if (ret)
+               return ret;
+
+       ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->rm_regset, VC5_RM);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
@@ -2816,12 +2867,34 @@ static int __maybe_unused vc4_hdmi_runtime_suspend(struct device *dev)
 static int vc4_hdmi_runtime_resume(struct device *dev)
 {
        struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
+       unsigned long __maybe_unused flags;
+       u32 __maybe_unused value;
        int ret;
 
        ret = clk_prepare_enable(vc4_hdmi->hsm_clock);
        if (ret)
                return ret;
 
+       if (vc4_hdmi->variant->reset)
+               vc4_hdmi->variant->reset(vc4_hdmi);
+
+#ifdef CONFIG_DRM_VC4_HDMI_CEC
+       spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+       value = HDMI_READ(HDMI_CEC_CNTRL_1);
+       /* Set the logical address to Unregistered */
+       value |= VC4_HDMI_CEC_ADDR_MASK;
+       HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
+       spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+       vc4_hdmi_cec_update_clk_div(vc4_hdmi);
+
+       if (!vc4_hdmi->variant->external_irq_controller) {
+               spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+               HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff);
+               spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+       }
+#endif
+
        return 0;
 }
 
@@ -2911,9 +2984,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
        pm_runtime_set_active(dev);
        pm_runtime_enable(dev);
 
-       if (vc4_hdmi->variant->reset)
-               vc4_hdmi->variant->reset(vc4_hdmi);
-
        if ((of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi0") ||
             of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi1")) &&
            HDMI_READ(HDMI_VID_CTL) & VC4_HD_VID_CTL_ENABLE) {