Merge tag 'drm-intel-next-2021-01-04' of git://anongit.freedesktop.org/drm/drm-intel...
[sfrench/cifs-2.6.git] / drivers / gpu / drm / i915 / display / intel_hdmi.c
index 82674a8853c606e24f7b392786948fe1dcad4c6b..c5959590562baf499a9d936512faaea242872199 100644 (file)
@@ -518,10 +518,10 @@ static u32 vlv_infoframes_enabled(struct intel_encoder *encoder,
                      VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP);
 }
 
-static void hsw_write_infoframe(struct intel_encoder *encoder,
-                               const struct intel_crtc_state *crtc_state,
-                               unsigned int type,
-                               const void *frame, ssize_t len)
+void hsw_write_infoframe(struct intel_encoder *encoder,
+                        const struct intel_crtc_state *crtc_state,
+                        unsigned int type,
+                        const void *frame, ssize_t len)
 {
        const u32 *data = frame;
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
@@ -555,10 +555,9 @@ static void hsw_write_infoframe(struct intel_encoder *encoder,
        intel_de_posting_read(dev_priv, ctl_reg);
 }
 
-static void hsw_read_infoframe(struct intel_encoder *encoder,
-                              const struct intel_crtc_state *crtc_state,
-                              unsigned int type,
-                              void *frame, ssize_t len)
+void hsw_read_infoframe(struct intel_encoder *encoder,
+                       const struct intel_crtc_state *crtc_state,
+                       unsigned int type, void *frame, ssize_t len)
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
@@ -2950,21 +2949,12 @@ static void
 intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector)
 {
        struct drm_i915_private *dev_priv = to_i915(connector->dev);
-       struct intel_digital_port *dig_port =
-                               hdmi_to_dig_port(intel_hdmi);
 
        intel_attach_force_audio_property(connector);
        intel_attach_broadcast_rgb_property(connector);
        intel_attach_aspect_ratio_property(connector);
 
-       /*
-        * Attach Colorspace property for Non LSPCON based device
-        * ToDo: This needs to be extended for LSPCON implementation
-        * as well. Will be implemented separately.
-        */
-       if (!dig_port->lspcon.active)
-               intel_attach_colorspace_property(connector);
-
+       intel_attach_hdmi_colorspace_property(connector);
        drm_connector_attach_content_type_property(connector);
 
        if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv))
@@ -3438,3 +3428,236 @@ void intel_hdmi_init(struct drm_i915_private *dev_priv,
        dig_port->aux_ch = intel_bios_port_aux_ch(dev_priv, port);
        intel_hdmi_init_connector(dig_port, intel_connector);
 }
+
+/*
+ * intel_hdmi_dsc_get_slice_height - get the dsc slice_height
+ * @vactive: Vactive of a display mode
+ *
+ * @return: appropriate dsc slice height for a given mode.
+ */
+int intel_hdmi_dsc_get_slice_height(int vactive)
+{
+       int slice_height;
+
+       /*
+        * Slice Height determination : HDMI2.1 Section 7.7.5.2
+        * Select smallest slice height >=96, that results in a valid PPS and
+        * requires minimum padding lines required for final slice.
+        *
+        * Assumption : Vactive is even.
+        */
+       for (slice_height = 96; slice_height <= vactive; slice_height += 2)
+               if (vactive % slice_height == 0)
+                       return slice_height;
+
+       return 0;
+}
+
+/*
+ * intel_hdmi_dsc_get_num_slices - get no. of dsc slices based on dsc encoder
+ * and dsc decoder capabilities
+ *
+ * @crtc_state: intel crtc_state
+ * @src_max_slices: maximum slices supported by the DSC encoder
+ * @src_max_slice_width: maximum slice width supported by DSC encoder
+ * @hdmi_max_slices: maximum slices supported by sink DSC decoder
+ * @hdmi_throughput: maximum clock per slice (MHz) supported by HDMI sink
+ *
+ * @return: num of dsc slices that can be supported by the dsc encoder
+ * and decoder.
+ */
+int
+intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state,
+                             int src_max_slices, int src_max_slice_width,
+                             int hdmi_max_slices, int hdmi_throughput)
+{
+/* Pixel rates in KPixels/sec */
+#define HDMI_DSC_PEAK_PIXEL_RATE               2720000
+/*
+ * Rates at which the source and sink are required to process pixels in each
+ * slice, can be two levels: either atleast 340000KHz or atleast 40000KHz.
+ */
+#define HDMI_DSC_MAX_ENC_THROUGHPUT_0          340000
+#define HDMI_DSC_MAX_ENC_THROUGHPUT_1          400000
+
+/* Spec limits the slice width to 2720 pixels */
+#define MAX_HDMI_SLICE_WIDTH                   2720
+       int kslice_adjust;
+       int adjusted_clk_khz;
+       int min_slices;
+       int target_slices;
+       int max_throughput; /* max clock freq. in khz per slice */
+       int max_slice_width;
+       int slice_width;
+       int pixel_clock = crtc_state->hw.adjusted_mode.crtc_clock;
+
+       if (!hdmi_throughput)
+               return 0;
+
+       /*
+        * Slice Width determination : HDMI2.1 Section 7.7.5.1
+        * kslice_adjust factor for 4:2:0, and 4:2:2 formats is 0.5, where as
+        * for 4:4:4 is 1.0. Multiplying these factors by 10 and later
+        * dividing adjusted clock value by 10.
+        */
+       if (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR444 ||
+           crtc_state->output_format == INTEL_OUTPUT_FORMAT_RGB)
+               kslice_adjust = 10;
+       else
+               kslice_adjust = 5;
+
+       /*
+        * As per spec, the rate at which the source and the sink process
+        * the pixels per slice are at two levels: atleast 340Mhz or 400Mhz.
+        * This depends upon the pixel clock rate and output formats
+        * (kslice adjust).
+        * If pixel clock * kslice adjust >= 2720MHz slices can be processed
+        * at max 340MHz, otherwise they can be processed at max 400MHz.
+        */
+
+       adjusted_clk_khz = DIV_ROUND_UP(kslice_adjust * pixel_clock, 10);
+
+       if (adjusted_clk_khz <= HDMI_DSC_PEAK_PIXEL_RATE)
+               max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_0;
+       else
+               max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_1;
+
+       /*
+        * Taking into account the sink's capability for maximum
+        * clock per slice (in MHz) as read from HF-VSDB.
+        */
+       max_throughput = min(max_throughput, hdmi_throughput * 1000);
+
+       min_slices = DIV_ROUND_UP(adjusted_clk_khz, max_throughput);
+       max_slice_width = min(MAX_HDMI_SLICE_WIDTH, src_max_slice_width);
+
+       /*
+        * Keep on increasing the num of slices/line, starting from min_slices
+        * per line till we get such a number, for which the slice_width is
+        * just less than max_slice_width. The slices/line selected should be
+        * less than or equal to the max horizontal slices that the combination
+        * of PCON encoder and HDMI decoder can support.
+        */
+       slice_width = max_slice_width;
+
+       do {
+               if (min_slices <= 1 && src_max_slices >= 1 && hdmi_max_slices >= 1)
+                       target_slices = 1;
+               else if (min_slices <= 2 && src_max_slices >= 2 && hdmi_max_slices >= 2)
+                       target_slices = 2;
+               else if (min_slices <= 4 && src_max_slices >= 4 && hdmi_max_slices >= 4)
+                       target_slices = 4;
+               else if (min_slices <= 8 && src_max_slices >= 8 && hdmi_max_slices >= 8)
+                       target_slices = 8;
+               else if (min_slices <= 12 && src_max_slices >= 12 && hdmi_max_slices >= 12)
+                       target_slices = 12;
+               else if (min_slices <= 16 && src_max_slices >= 16 && hdmi_max_slices >= 16)
+                       target_slices = 16;
+               else
+                       return 0;
+
+               slice_width = DIV_ROUND_UP(crtc_state->hw.adjusted_mode.hdisplay, target_slices);
+               if (slice_width >= max_slice_width)
+                       min_slices = target_slices + 1;
+       } while (slice_width >= max_slice_width);
+
+       return target_slices;
+}
+
+/*
+ * intel_hdmi_dsc_get_bpp - get the appropriate compressed bits_per_pixel based on
+ * source and sink capabilities.
+ *
+ * @src_fraction_bpp: fractional bpp supported by the source
+ * @slice_width: dsc slice width supported by the source and sink
+ * @num_slices: num of slices supported by the source and sink
+ * @output_format: video output format
+ * @hdmi_all_bpp: sink supports decoding of 1/16th bpp setting
+ * @hdmi_max_chunk_bytes: max bytes in a line of chunks supported by sink
+ *
+ * @return: compressed bits_per_pixel in step of 1/16 of bits_per_pixel
+ */
+int
+intel_hdmi_dsc_get_bpp(int src_fractional_bpp, int slice_width, int num_slices,
+                      int output_format, bool hdmi_all_bpp,
+                      int hdmi_max_chunk_bytes)
+{
+       int max_dsc_bpp, min_dsc_bpp;
+       int target_bytes;
+       bool bpp_found = false;
+       int bpp_decrement_x16;
+       int bpp_target;
+       int bpp_target_x16;
+
+       /*
+        * Get min bpp and max bpp as per Table 7.23, in HDMI2.1 spec
+        * Start with the max bpp and keep on decrementing with
+        * fractional bpp, if supported by PCON DSC encoder
+        *
+        * for each bpp we check if no of bytes can be supported by HDMI sink
+        */
+
+       /* Assuming: bpc as 8*/
+       if (output_format == INTEL_OUTPUT_FORMAT_YCBCR420) {
+               min_dsc_bpp = 6;
+               max_dsc_bpp = 3 * 4; /* 3*bpc/2 */
+       } else if (output_format == INTEL_OUTPUT_FORMAT_YCBCR444 ||
+                  output_format == INTEL_OUTPUT_FORMAT_RGB) {
+               min_dsc_bpp = 8;
+               max_dsc_bpp = 3 * 8; /* 3*bpc */
+       } else {
+               /* Assuming 4:2:2 encoding */
+               min_dsc_bpp = 7;
+               max_dsc_bpp = 2 * 8; /* 2*bpc */
+       }
+
+       /*
+        * Taking into account if all dsc_all_bpp supported by HDMI2.1 sink
+        * Section 7.7.34 : Source shall not enable compressed Video
+        * Transport with bpp_target settings above 12 bpp unless
+        * DSC_all_bpp is set to 1.
+        */
+       if (!hdmi_all_bpp)
+               max_dsc_bpp = min(max_dsc_bpp, 12);
+
+       /*
+        * The Sink has a limit of compressed data in bytes for a scanline,
+        * as described in max_chunk_bytes field in HFVSDB block of edid.
+        * The no. of bytes depend on the target bits per pixel that the
+        * source configures. So we start with the max_bpp and calculate
+        * the target_chunk_bytes. We keep on decrementing the target_bpp,
+        * till we get the target_chunk_bytes just less than what the sink's
+        * max_chunk_bytes, or else till we reach the min_dsc_bpp.
+        *
+        * The decrement is according to the fractional support from PCON DSC
+        * encoder. For fractional BPP we use bpp_target as a multiple of 16.
+        *
+        * bpp_target_x16 = bpp_target * 16
+        * So we need to decrement by {1, 2, 4, 8, 16} for fractional bpps
+        * {1/16, 1/8, 1/4, 1/2, 1} respectively.
+        */
+
+       bpp_target = max_dsc_bpp;
+
+       /* src does not support fractional bpp implies decrement by 16 for bppx16 */
+       if (!src_fractional_bpp)
+               src_fractional_bpp = 1;
+       bpp_decrement_x16 = DIV_ROUND_UP(16, src_fractional_bpp);
+       bpp_target_x16 = (bpp_target * 16) - bpp_decrement_x16;
+
+       while (bpp_target_x16 > (min_dsc_bpp * 16)) {
+               int bpp;
+
+               bpp = DIV_ROUND_UP(bpp_target_x16, 16);
+               target_bytes = DIV_ROUND_UP((num_slices * slice_width * bpp), 8);
+               if (target_bytes <= hdmi_max_chunk_bytes) {
+                       bpp_found = true;
+                       break;
+               }
+               bpp_target_x16 -= bpp_decrement_x16;
+       }
+       if (bpp_found)
+               return bpp_target_x16;
+
+       return 0;
+}