diff mbox series

[v2,12/13] drm/i915: Add helper functions for calculating DSC parameters for HDMI2.1

Message ID 20201101100657.12087-13-ankit.k.nautiyal@intel.com (mailing list archive)
State New, archived
Headers show
Series Add support for DP-HDMI2.1 PCON | expand

Commit Message

Ankit Nautiyal Nov. 1, 2020, 10:06 a.m. UTC
The DP-HDMI2.1 PCON spec provides way for a source to set PPS
parameters: slice height, slice width and bits_per_pixel, based on
the HDMI2.1 sink capabilities. The DSC encoder of the PCON will
respect these parameters, while preparing the 128 byte PPS.

This patch adds helper functions to calculate these PPS paremeters as
per the HDMI2.1 specification.

Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
---
 drivers/gpu/drm/i915/display/intel_hdmi.c | 181 ++++++++++++++++++++++
 drivers/gpu/drm/i915/display/intel_hdmi.h |   7 +
 2 files changed, 188 insertions(+)

Comments

Uma Shankar Nov. 25, 2020, 8:28 p.m. UTC | #1
> -----Original Message-----
> From: Nautiyal, Ankit K <ankit.k.nautiyal@intel.com>
> Sent: Sunday, November 1, 2020 3:37 PM
> To: intel-gfx@lists.freedesktop.org
> Cc: dri-devel@lists.freedesktop.org; Shankar, Uma <uma.shankar@intel.com>;
> Kulkarni, Vandita <vandita.kulkarni@intel.com>; ville.syrjala@linux.intel.com;
> Sharma, Swati2 <swati2.sharma@intel.com>
> Subject: [PATCH v2 12/13] drm/i915: Add helper functions for calculating DSC
> parameters for HDMI2.1
> 
> The DP-HDMI2.1 PCON spec provides way for a source to set PPS
> parameters: slice height, slice width and bits_per_pixel, based on the HDMI2.1
> sink capabilities. The DSC encoder of the PCON will respect these parameters,
> while preparing the 128 byte PPS.
> 
> This patch adds helper functions to calculate these PPS paremeters as per the
> HDMI2.1 specification.
> 
> Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
> ---
>  drivers/gpu/drm/i915/display/intel_hdmi.c | 181 ++++++++++++++++++++++
>  drivers/gpu/drm/i915/display/intel_hdmi.h |   7 +
>  2 files changed, 188 insertions(+)
> 
> diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c
> b/drivers/gpu/drm/i915/display/intel_hdmi.c
> index f90838bc74fb..6b9b9ea7f9b0 100644
> --- a/drivers/gpu/drm/i915/display/intel_hdmi.c
> +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c
> @@ -3438,3 +3438,184 @@ 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);  }
> +
> +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;
> +}
> +
> +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) {

It would be good to add a comment describing each of the function parameters, this
will make following code easier.

> +/* Pixel rates in KPixels/sec */
> +#define HDMI_DSC_PEAK_PIXEL_RATE		2720000
> +#define HDMI_DSC_MAX_ENC_THROUGHPUT_0		340000
> +#define HDMI_DSC_MAX_ENC_THROUGHPUT_1		400000

It would be good to add a comment to describe what actually this represent.

> +#define MAX_HDMI_SLICE_WIDTH			2720

How this limit is defined, may be mention of the spec details.

> +	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 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_YCBCR420)
> +		kslice_adjust = 5;
> +	else
> +		kslice_adjust = 10;

May be add a note for 422 formats, or fail in that case, not sure if value of 1 holds
good for 422 as well. If it's 1, then ignore the comment.

> +
> +	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;

A comment describing slight details will be good.

> +	/*
> +	 * 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);
> +
> +	if (!max_throughput)
> +		return 0;

hdmi_throughput is already checked for 0, so this is not needed.

> +	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;
> +}
> +
> +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)

Describe the parameters here as well.

> +{
> +	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 {
> +		min_dsc_bpp = 8;
> +		max_dsc_bpp = 3 * 8; /* 3*bpc */
> +	}

May be add a note for 422 formats, or fail in that case. I guess 8 and 24 will work
for RGB and 422 will have different values. Please check and fix this.

> +
> +	/*
> +	 * 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;
> +	if (src_fractional_bpp)

I think you missed a ! here

> +		return 0;
> +	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 = num_slices * slice_width * DIV_ROUND_UP(bpp,
> 8);
> +		if (target_bytes <= hdmi_max_chunk_bytes) {
> +			bpp_found = true;
> +			break;
> +		}
> +		bpp_target_x16 -= bpp_decrement_x16;

Not sure on the overall calculation on this part, but if this works then I am good. Please confirm the
same with actual bpp values computed and validate.

> +	}
> +	if (bpp_found)
> +		return bpp_target_x16;

I think we should divide by 16 and return. Isn't it ?

> +
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h
> b/drivers/gpu/drm/i915/display/intel_hdmi.h
> index 15eb0ccde76e..fa1a9b030850 100644
> --- a/drivers/gpu/drm/i915/display/intel_hdmi.h
> +++ b/drivers/gpu/drm/i915/display/intel_hdmi.h
> @@ -50,5 +50,12 @@ bool intel_hdmi_limited_color_range(const struct
> intel_crtc_state *crtc_state,
>  				    const struct drm_connector_state
> *conn_state);  bool intel_hdmi_deep_color_possible(const struct intel_crtc_state
> *crtc_state, int bpc,
>  				    bool has_hdmi_sink, bool ycbcr420_output);
> +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 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); int
> +intel_hdmi_dsc_get_slice_height(int vactive);
> 
>  #endif /* __INTEL_HDMI_H__ */
> --
> 2.17.1
Ankit Nautiyal Dec. 2, 2020, 2:13 p.m. UTC | #2
Hi Uma,

Thanks for the comments and spotting the errors. I agree to most of the 
comments and will address them in the next version.

Please find my responses inline:

On 11/26/2020 1:58 AM, Shankar, Uma wrote:
>
>> -----Original Message-----
>> From: Nautiyal, Ankit K <ankit.k.nautiyal@intel.com>
>> Sent: Sunday, November 1, 2020 3:37 PM
>> To: intel-gfx@lists.freedesktop.org
>> Cc: dri-devel@lists.freedesktop.org; Shankar, Uma <uma.shankar@intel.com>;
>> Kulkarni, Vandita <vandita.kulkarni@intel.com>; ville.syrjala@linux.intel.com;
>> Sharma, Swati2 <swati2.sharma@intel.com>
>> Subject: [PATCH v2 12/13] drm/i915: Add helper functions for calculating DSC
>> parameters for HDMI2.1
>>
>> The DP-HDMI2.1 PCON spec provides way for a source to set PPS
>> parameters: slice height, slice width and bits_per_pixel, based on the HDMI2.1
>> sink capabilities. The DSC encoder of the PCON will respect these parameters,
>> while preparing the 128 byte PPS.
>>
>> This patch adds helper functions to calculate these PPS paremeters as per the
>> HDMI2.1 specification.
>>
>> Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com>
>> ---
>>   drivers/gpu/drm/i915/display/intel_hdmi.c | 181 ++++++++++++++++++++++
>>   drivers/gpu/drm/i915/display/intel_hdmi.h |   7 +
>>   2 files changed, 188 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c
>> b/drivers/gpu/drm/i915/display/intel_hdmi.c
>> index f90838bc74fb..6b9b9ea7f9b0 100644
>> --- a/drivers/gpu/drm/i915/display/intel_hdmi.c
>> +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c
>> @@ -3438,3 +3438,184 @@ 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);  }
>> +
>> +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;
>> +}
>> +
>> +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) {
> It would be good to add a comment describing each of the function parameters, this
> will make following code easier.

Alright, I will add documentation here.


>
>> +/* Pixel rates in KPixels/sec */
>> +#define HDMI_DSC_PEAK_PIXEL_RATE		2720000
>> +#define HDMI_DSC_MAX_ENC_THROUGHPUT_0		340000
>> +#define HDMI_DSC_MAX_ENC_THROUGHPUT_1		400000
> It would be good to add a comment to describe what actually this represent.
Agreed will have a comment to describe these macros.
>
>> +#define MAX_HDMI_SLICE_WIDTH			2720
> How this limit is defined, may be mention of the spec details.
Agreed, will provide details in next patch version.
>
>> +	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 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_YCBCR420)
>> +		kslice_adjust = 5;
>> +	else
>> +		kslice_adjust = 10;
> May be add a note for 422 formats, or fail in that case, not sure if value of 1 holds
> good for 422 as well. If it's 1, then ignore the comment.

You are right, for 422 format correct value is 0.5.

I will set the value of 1 for 444 formats and the else case for 422 and 
420 will be then 0.5

>
>> +
>> +	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;
> A comment describing slight details will be good.
Will add the comments in this part in next version.
>
>> +	/*
>> +	 * 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);
>> +
>> +	if (!max_throughput)
>> +		return 0;
> hdmi_throughput is already checked for 0, so this is not needed.

Agreed, this is redundant, will get rid of this condition.


>> +	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;
>> +}
>> +
>> +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)
> Describe the parameters here as well.
Will add the documentation for the function in next version.
>
>> +{
>> +	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 {
>> +		min_dsc_bpp = 8;
>> +		max_dsc_bpp = 3 * 8; /* 3*bpc */
>> +	}
> May be add a note for 422 formats, or fail in that case. I guess 8 and 24 will work
> for RGB and 422 will have different values. Please check and fix this.

Alright, will have a separate condition for 444 format and 420 format.

The 422 format will be then in else case.

>
>> +
>> +	/*
>> +	 * 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;
>> +	if (src_fractional_bpp)
> I think you missed a ! here


Thanks for catching that. Will fix in the next version.

>
>> +		return 0;
>> +	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 = num_slices * slice_width * DIV_ROUND_UP(bpp,
>> 8);
>> +		if (target_bytes <= hdmi_max_chunk_bytes) {
>> +			bpp_found = true;
>> +			break;
>> +		}
>> +		bpp_target_x16 -= bpp_decrement_x16;
> Not sure on the overall calculation on this part, but if this works then I am good. Please confirm the
> same with actual bpp values computed and validate.

The value for bpp was seen to be around 10-12bpp for with TGL platform 
with for 8k resolutions with one of the DSC1.2 panel for 8k modes.

>> +	}
>> +	if (bpp_found)
>> +		return bpp_target_x16;
> I think we should divide by 16 and return. Isn't it ?

The values expected to be written in PPS parameter register are x16 so I 
am directly returning this value.

Thanks & Regards,

Ankit


>> +
>> +	return 0;
>> +}
>> diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h
>> b/drivers/gpu/drm/i915/display/intel_hdmi.h
>> index 15eb0ccde76e..fa1a9b030850 100644
>> --- a/drivers/gpu/drm/i915/display/intel_hdmi.h
>> +++ b/drivers/gpu/drm/i915/display/intel_hdmi.h
>> @@ -50,5 +50,12 @@ bool intel_hdmi_limited_color_range(const struct
>> intel_crtc_state *crtc_state,
>>   				    const struct drm_connector_state
>> *conn_state);  bool intel_hdmi_deep_color_possible(const struct intel_crtc_state
>> *crtc_state, int bpc,
>>   				    bool has_hdmi_sink, bool ycbcr420_output);
>> +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 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); int
>> +intel_hdmi_dsc_get_slice_height(int vactive);
>>
>>   #endif /* __INTEL_HDMI_H__ */
>> --
>> 2.17.1
diff mbox series

Patch

diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c
index f90838bc74fb..6b9b9ea7f9b0 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.c
@@ -3438,3 +3438,184 @@  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);
 }
+
+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;
+}
+
+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
+#define HDMI_DSC_MAX_ENC_THROUGHPUT_0		340000
+#define HDMI_DSC_MAX_ENC_THROUGHPUT_1		400000
+#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 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_YCBCR420)
+		kslice_adjust = 5;
+	else
+		kslice_adjust = 10;
+
+	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);
+
+	if (!max_throughput)
+		return 0;
+
+	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;
+}
+
+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 {
+		min_dsc_bpp = 8;
+		max_dsc_bpp = 3 * 8; /* 3*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;
+	if (src_fractional_bpp)
+		return 0;
+	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 = num_slices * slice_width * DIV_ROUND_UP(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;
+}
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h
index 15eb0ccde76e..fa1a9b030850 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.h
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.h
@@ -50,5 +50,12 @@  bool intel_hdmi_limited_color_range(const struct intel_crtc_state *crtc_state,
 				    const struct drm_connector_state *conn_state);
 bool intel_hdmi_deep_color_possible(const struct intel_crtc_state *crtc_state, int bpc,
 				    bool has_hdmi_sink, bool ycbcr420_output);
+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 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);
+int intel_hdmi_dsc_get_slice_height(int vactive);
 
 #endif /* __INTEL_HDMI_H__ */