Last active
May 10, 2026 03:00
-
-
Save xzn/028a18f78f6fa6f08a8a0d6a3d29c232 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| commit 7097b510060d52631504c066faaf684bea8689ad | |
| Author: JS Deck <jsdeckerido@gmail.com> | |
| Date: Sat May 9 15:29:32 2026 -0300 | |
| drm/msm: displayport dsc support and misc fixes | |
| ported from https://lore.kernel.org/lkml/1674498274-6010-1-git-send-email-quic_khsieh@quicinc.com/ | |
| diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile | |
| index 8b94c5f1cb68..2f6abcf4f429 100644 | |
| --- a/drivers/gpu/drm/msm/Makefile | |
| +++ b/drivers/gpu/drm/msm/Makefile | |
| @@ -81,6 +81,7 @@ msm-display-$(CONFIG_DRM_MSM_DPU) += \ | |
| disp/dpu1/dpu_hw_cwb.o \ | |
| disp/dpu1/dpu_hw_dsc.o \ | |
| disp/dpu1/dpu_hw_dsc_1_2.o \ | |
| + disp/dpu1/dpu_dsc_helper.o \ | |
| disp/dpu1/dpu_hw_interrupts.o \ | |
| disp/dpu1/dpu_hw_intf.o \ | |
| disp/dpu1/dpu_hw_lm.o \ | |
| @@ -138,6 +139,7 @@ msm-$(CONFIG_DRM_MSM_KMS_FBDEV) += msm_fbdev.o | |
| msm-display-$(CONFIG_DRM_MSM_DP)+= dp/dp_aux.o \ | |
| dp/dp_ctrl.o \ | |
| + dp/dp_tu_calc.o \ | |
| dp/dp_debug.o \ | |
| dp/dp_display.o \ | |
| dp/dp_drm.o \ | |
| diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | |
| index 0f4921b1a892..49db6571eb54 100644 | |
| --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | |
| +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | |
| @@ -1367,7 +1367,6 @@ static int dpu_crtc_reassign_planes(struct drm_crtc *crtc, struct drm_crtc_state | |
| } | |
| #define MAX_CHANNELS_PER_CRTC PIPES_PER_PLANE | |
| -#define MAX_HDISPLAY_SPLIT 1080 | |
| static struct msm_display_topology dpu_crtc_get_topology( | |
| struct drm_crtc *crtc, | |
| @@ -1377,10 +1376,13 @@ static struct msm_display_topology dpu_crtc_get_topology( | |
| struct drm_display_mode *mode = &crtc_state->adjusted_mode; | |
| struct msm_display_topology topology = {0}; | |
| struct drm_encoder *drm_enc; | |
| + int enc_count = 0; | |
| - drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask) | |
| + drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask) { | |
| dpu_encoder_update_topology(drm_enc, &topology, crtc_state->state, | |
| &crtc_state->adjusted_mode); | |
| + WARN(enc_count++, "Only one encoder per crtc is supported, topology may be erroneous.\n"); | |
| + } | |
| topology.cwb_enabled = drm_crtc_in_clone_mode(crtc_state); | |
| @@ -1402,16 +1404,15 @@ static struct msm_display_topology dpu_crtc_get_topology( | |
| * enabled. This is because in cases where CWB is enabled, num_intf will | |
| * count both the WB and real-time phys encoders. | |
| * | |
| - * For non-DSC CWB usecases, have the num_lm be decided by the | |
| - * (mode->hdisplay > MAX_HDISPLAY_SPLIT) check. | |
| + * For non-DSC CWB usecases, have the num_lm be the minimum allowed. | |
| */ | |
| if (topology.num_intf == 2 && !topology.cwb_enabled) | |
| topology.num_lm = 2; | |
| - else if (topology.num_dsc == 2) | |
| - topology.num_lm = 2; | |
| + else if (topology.num_dsc) | |
| + topology.num_lm = topology.num_dsc; | |
| else if (dpu_kms->catalog->caps->has_3d_merge) | |
| - topology.num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1; | |
| + topology.num_lm = (mode->hdisplay > dpu_kms->catalog->caps->max_mixer_width) ? 2 : 1; | |
| else | |
| topology.num_lm = 1; | |
| @@ -1442,6 +1443,7 @@ static int dpu_crtc_assign_resources(struct drm_crtc *crtc, | |
| return PTR_ERR(global_state); | |
| dpu_rm_release(global_state, crtc); | |
| + cstate->num_mixers = 0; | |
| if (!crtc_state->enable) | |
| return 0; | |
| @@ -1449,8 +1451,11 @@ static int dpu_crtc_assign_resources(struct drm_crtc *crtc, | |
| topology = dpu_crtc_get_topology(crtc, dpu_kms, crtc_state); | |
| ret = dpu_rm_reserve(&dpu_kms->rm, global_state, | |
| crtc_state->crtc, &topology); | |
| - if (ret) | |
| + if (ret) { | |
| + /* Release on failure to avoid persistent bork */ | |
| + dpu_rm_release(global_state, crtc); | |
| return ret; | |
| + } | |
| cstate = to_dpu_crtc_state(crtc_state); | |
| diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h | |
| index 94392b9b9245..2b5416b56f5d 100644 | |
| --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h | |
| +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h | |
| @@ -220,9 +220,6 @@ struct dpu_crtc_state { | |
| u32 num_mixers; | |
| struct dpu_crtc_mixer mixers[CRTC_DUAL_MIXERS]; | |
| - u32 num_ctls; | |
| - struct dpu_hw_ctl *hw_ctls[CRTC_DUAL_MIXERS]; | |
| - | |
| enum dpu_crtc_crc_source crc_source; | |
| int crc_frame_skip_count; | |
| }; | |
| diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c | |
| new file mode 100644 | |
| index 000000000000..2ab71073d748 | |
| --- /dev/null | |
| +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.c | |
| @@ -0,0 +1,183 @@ | |
| +// SPDX-License-Identifier: GPL-2.0-only | |
| +/* | |
| + * Copyright (c) 2012-2023 The Linux Foundation. All rights reserved. | |
| + * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved | |
| + */ | |
| + | |
| +#include "dpu_kms.h" | |
| +#include "dpu_hw_dsc.h" | |
| +#include "dpu_dsc_helper.h" | |
| + | |
| +#include <drm/display/drm_dsc_helper.h> | |
| + | |
| +int msm_populate_dsc_params(struct device *dev, struct drm_dsc_config *dsc) | |
| +{ | |
| + int ret; | |
| + | |
| + if (dsc->bits_per_pixel & 0xf) { | |
| + DRM_DEV_ERROR(dev, "DSI does not support fractional bits_per_pixel\n"); | |
| + return -EINVAL; | |
| + } | |
| + | |
| + switch (dsc->bits_per_component) { | |
| + case 8: | |
| + case 10: | |
| + case 12: | |
| + /* | |
| + * Only 8, 10, and 12 bpc are supported for DSC 1.1 block. | |
| + * If additional bpc values need to be supported, update | |
| + * this quard with the appropriate DSC version verification. | |
| + */ | |
| + break; | |
| + default: | |
| + DRM_DEV_ERROR(dev, | |
| + "Unsupported bits_per_component value: %d\n", | |
| + dsc->bits_per_component); | |
| + return -EOPNOTSUPP; | |
| + } | |
| + | |
| + dsc->simple_422 = 0; | |
| + dsc->convert_rgb = 1; | |
| + dsc->vbr_enable = 0; | |
| + | |
| + drm_dsc_set_const_params(dsc); | |
| + drm_dsc_set_rc_buf_thresh(dsc); | |
| + | |
| + /* DPU supports only pre-SCR panels */ | |
| + ret = drm_dsc_setup_rc_params(dsc, DRM_DSC_1_1_PRE_SCR); | |
| + if (ret) { | |
| + DRM_DEV_ERROR(dev, "could not find DSC RC parameters\n"); | |
| + return ret; | |
| + } | |
| + | |
| + dsc->initial_scale_value = drm_dsc_initial_scale_value(dsc); | |
| + dsc->line_buf_depth = dsc->bits_per_component + 1; | |
| + | |
| + return drm_dsc_compute_rc_parameters(dsc); | |
| +} | |
| + | |
| +void dpu_dsc_initial_line_calc(struct msm_display_dsc_info *dsc_info, int enc_ip_width, | |
| + int dsc_cmn_mode) | |
| +{ | |
| + int max_ssm_delay, max_se_size, max_muxword_size; | |
| + int compress_bpp_group, obuf_latency, input_ssm_out_latency; | |
| + int base_hs_latency, chunk_bits, ob_data_width; | |
| + int output_rate_extra_budget_bits, multi_hs_extra_budget_bits; | |
| + int multi_hs_extra_latency, mux_word_size; | |
| + int ob_data_width_4comps, ob_data_width_3comps; | |
| + int output_rate_ratio_complement, container_slice_width; | |
| + int rtl_num_components, multi_hs_c, multi_hs_d; | |
| + | |
| + int bpc = dsc_info->drm_dsc.bits_per_component; | |
| + int bpp = DSC_BPP(dsc_info->drm_dsc); | |
| + bool native_422 = dsc_info->drm_dsc.native_422; | |
| + bool native_420 = dsc_info->drm_dsc.native_420; | |
| + | |
| + /* Hardent core config */ | |
| + int multiplex_mode_enable = 0, split_panel_enable = 0; | |
| + int rtl_max_bpc = 10, rtl_output_data_width = 64; | |
| + int pipeline_latency = 28; | |
| + | |
| + if (dsc_cmn_mode & DSC_MODE_MULTIPLEX) | |
| + multiplex_mode_enable = 1; | |
| + if (dsc_cmn_mode & DSC_MODE_SPLIT_PANEL) | |
| + split_panel_enable = 1; | |
| + container_slice_width = (native_422 ? | |
| + dsc_info->drm_dsc.slice_width / 2 : dsc_info->drm_dsc.slice_width); | |
| + max_muxword_size = (rtl_max_bpc >= 12) ? 64 : 48; | |
| + max_se_size = 4 * (rtl_max_bpc + 1); | |
| + max_ssm_delay = max_se_size + max_muxword_size - 1; | |
| + mux_word_size = (bpc >= 12) ? 64 : 48; | |
| + compress_bpp_group = native_422 ? (2 * bpp) : bpp; | |
| + input_ssm_out_latency = pipeline_latency + 3 * (max_ssm_delay + 2) | |
| + * dsc_info->num_active_ss_per_enc; | |
| + rtl_num_components = (native_420 || native_422) ? 4 : 3; | |
| + ob_data_width_4comps = (rtl_output_data_width >= (2 * | |
| + max_muxword_size)) ? | |
| + rtl_output_data_width : | |
| + (2 * rtl_output_data_width); | |
| + ob_data_width_3comps = (rtl_output_data_width >= max_muxword_size) ? | |
| + rtl_output_data_width : 2 * rtl_output_data_width; | |
| + ob_data_width = (rtl_num_components == 4) ? | |
| + ob_data_width_4comps : ob_data_width_3comps; | |
| + obuf_latency = DIV_ROUND_UP((9 * ob_data_width + mux_word_size), | |
| + compress_bpp_group) + 1; | |
| + base_hs_latency = dsc_info->drm_dsc.initial_xmit_delay + | |
| + input_ssm_out_latency + obuf_latency; | |
| + chunk_bits = 8 * dsc_info->drm_dsc.slice_chunk_size; | |
| + output_rate_ratio_complement = ob_data_width - compress_bpp_group; | |
| + output_rate_extra_budget_bits = | |
| + (output_rate_ratio_complement * chunk_bits) >> | |
| + ((ob_data_width == 128) ? 7 : 6); | |
| + multi_hs_c = split_panel_enable * multiplex_mode_enable; | |
| + multi_hs_d = (dsc_info->num_active_ss_per_enc > 1) * (ob_data_width > compress_bpp_group); | |
| + multi_hs_extra_budget_bits = multi_hs_c ? | |
| + chunk_bits : (multi_hs_d ? chunk_bits : | |
| + output_rate_extra_budget_bits); | |
| + multi_hs_extra_latency = DIV_ROUND_UP(multi_hs_extra_budget_bits, | |
| + compress_bpp_group); | |
| + dsc_info->initial_lines = DIV_ROUND_UP((base_hs_latency + | |
| + multi_hs_extra_latency), | |
| + container_slice_width); | |
| +} | |
| + | |
| +int dpu_dsc_populate_dsc_private_params(struct msm_display_dsc_info *dsc_info, | |
| + int intf_width) | |
| +{ | |
| + int mod_offset; | |
| + int slice_per_pkt, slice_per_intf; | |
| + int bytes_in_slice, total_bytes_per_intf; | |
| + u16 bpp; | |
| + | |
| + if (!dsc_info || !dsc_info->drm_dsc.slice_width || | |
| + !dsc_info->drm_dsc.slice_height || | |
| + intf_width < dsc_info->drm_dsc.slice_width) { | |
| + DPU_ERROR("invalid input, intf_width=%d slice_width=%d\n", | |
| + intf_width, dsc_info ? dsc_info->drm_dsc.slice_width : | |
| + -1); | |
| + return -EINVAL; | |
| + } | |
| + | |
| + mod_offset = dsc_info->drm_dsc.slice_width % 3; | |
| + | |
| + | |
| + switch (mod_offset) { | |
| + case 0: | |
| + dsc_info->slice_last_group_size = 2; | |
| + break; | |
| + case 1: | |
| + dsc_info->slice_last_group_size = 0; | |
| + break; | |
| + case 2: | |
| + dsc_info->slice_last_group_size = 1; | |
| + break; | |
| + default: | |
| + break; | |
| + } | |
| + | |
| + dsc_info->det_thresh_flatness = | |
| + 2 << (dsc_info->drm_dsc.bits_per_component - 8); | |
| + | |
| + slice_per_pkt = dsc_info->slice_per_pkt; | |
| + slice_per_intf = DIV_ROUND_UP(intf_width, | |
| + dsc_info->drm_dsc.slice_width); | |
| + | |
| + /* | |
| + * If slice_per_pkt is greater than slice_per_intf then default to 1. | |
| + * This can happen during partial update. | |
| + */ | |
| + if (slice_per_pkt > slice_per_intf) | |
| + slice_per_pkt = 1; | |
| + | |
| + bpp = DSC_BPP(dsc_info->drm_dsc); | |
| + bytes_in_slice = DIV_ROUND_UP(dsc_info->drm_dsc.slice_width * bpp, 8); | |
| + total_bytes_per_intf = bytes_in_slice * slice_per_intf; | |
| + | |
| + dsc_info->eol_byte_num = total_bytes_per_intf % 3; | |
| + dsc_info->pclk_per_line = DIV_ROUND_UP(total_bytes_per_intf, 3); | |
| + dsc_info->bytes_in_slice = bytes_in_slice; | |
| + dsc_info->bytes_per_pkt = bytes_in_slice * slice_per_pkt; | |
| + dsc_info->pkt_per_line = slice_per_intf / slice_per_pkt; | |
| + | |
| + return 0; | |
| +} | |
| diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h | |
| new file mode 100644 | |
| index 000000000000..a6385d2464d5 | |
| --- /dev/null | |
| +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_dsc_helper.h | |
| @@ -0,0 +1,22 @@ | |
| +// SPDX-License-Identifier: GPL-2.0-only | |
| +/* | |
| + * Copyright (c) 2020 - 2023 The Linux Foundation. All rights reserved. | |
| + * Copyright (c) 2023. Qualcomm Innovation Center, Inc. All rights reserved | |
| + */ | |
| + | |
| +#ifndef __DPU_DSC_HELPER_H__ | |
| +#define __DPU_DSC_HELPER_H__ | |
| + | |
| +#include "msm_drv.h" | |
| + | |
| +#define DSC_1_1_PPS_PARAMETER_SET_ELEMENTS 88 | |
| + | |
| +int msm_populate_dsc_params(struct device *dev, struct drm_dsc_config *dsc); | |
| + | |
| +int dpu_dsc_populate_dsc_private_params(struct msm_display_dsc_info *dsc_info, | |
| + int intf_width); | |
| + | |
| +void dpu_dsc_initial_line_calc(struct msm_display_dsc_info *dsc_info, int enc_ip_width, | |
| + int dsc_cmn_mode); | |
| + | |
| +#endif /* __DPU_DSC_HELPER_H__ */ | |
| diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | |
| index eba1d52211f6..906406846d02 100644 | |
| --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | |
| +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | |
| @@ -17,6 +17,7 @@ | |
| #include <drm/drm_file.h> | |
| #include <drm/drm_probe_helper.h> | |
| #include <drm/drm_framebuffer.h> | |
| +#include <drm/drm_bridge.h> | |
| #include "msm_drv.h" | |
| #include "dpu_kms.h" | |
| @@ -31,6 +32,7 @@ | |
| #include "dpu_hw_cdm.h" | |
| #include "dpu_formats.h" | |
| #include "dpu_encoder_phys.h" | |
| +#include "dpu_dsc_helper.h" | |
| #include "dpu_crtc.h" | |
| #include "dpu_trace.h" | |
| #include "dpu_core_irq.h" | |
| @@ -183,6 +185,7 @@ struct dpu_encoder_virt { | |
| struct dpu_encoder_phys *cur_master; | |
| struct dpu_encoder_phys *cur_slave; | |
| struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; | |
| + struct dpu_hw_mixer *hw_mixer[MAX_CHANNELS_PER_ENC]; | |
| struct dpu_hw_cwb *hw_cwb[MAX_CWB_PER_ENC]; | |
| struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; | |
| @@ -207,14 +210,13 @@ struct dpu_encoder_virt { | |
| struct mutex rc_lock; | |
| enum dpu_enc_rc_states rc_state; | |
| struct delayed_work delayed_off_work; | |
| - struct msm_display_topology topology; | |
| u32 idle_timeout; | |
| bool wide_bus_en; | |
| - /* DSC configuration */ | |
| - struct drm_dsc_config *dsc; | |
| + /* DSC compression configuration */ | |
| + struct msm_compression_info *comp_info; | |
| }; | |
| #define to_dpu_encoder_virt(x) container_of(x, struct dpu_encoder_virt, base) | |
| @@ -291,18 +293,6 @@ bool dpu_encoder_is_widebus_enabled(const struct drm_encoder *drm_enc) | |
| return false; | |
| } | |
| -/** | |
| - * dpu_encoder_is_dsc_enabled - indicate whether dsc is enabled | |
| - * for the encoder. | |
| - * @drm_enc: Pointer to previously created drm encoder structure | |
| - */ | |
| -bool dpu_encoder_is_dsc_enabled(const struct drm_encoder *drm_enc) | |
| -{ | |
| - const struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc); | |
| - | |
| - return dpu_enc->dsc ? true : false; | |
| -} | |
| - | |
| /** | |
| * dpu_encoder_get_crc_values_cnt - get number of physical encoders contained | |
| * in virtual encoder that can collect CRC values | |
| @@ -641,7 +631,7 @@ bool dpu_encoder_use_dsc_merge(struct drm_encoder *drm_enc) | |
| * used for this encoder. | |
| * @drm_enc: Pointer to encoder structure | |
| */ | |
| -struct drm_dsc_config *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc) | |
| +struct msm_compression_info *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc) | |
| { | |
| struct msm_drm_private *priv = drm_enc->dev->dev_private; | |
| struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc); | |
| @@ -649,6 +639,22 @@ struct drm_dsc_config *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc) | |
| if (dpu_enc->disp_info.intf_type == INTF_DSI) | |
| return msm_dsi_get_dsc_config(priv->kms->dsi[index]); | |
| + else if (dpu_enc->disp_info.intf_type == INTF_DP) | |
| + return msm_dp_get_dsc_config(priv->kms->dp[index]); | |
| + | |
| + return NULL; | |
| +} | |
| + | |
| +static bool dpu_encoder_dsc_enabled_for_mode(struct drm_encoder *drm_enc, const struct drm_display_mode *adj_mode) | |
| +{ | |
| + struct msm_drm_private *priv = drm_enc->dev->dev_private; | |
| + struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc); | |
| + int index = dpu_enc->disp_info.h_tile_instance[0]; | |
| + | |
| + if (dpu_enc->disp_info.intf_type == INTF_DSI) | |
| + return msm_dsi_dsc_enabled_for_mode(priv->kms->dsi[index], adj_mode); | |
| + else if (dpu_enc->disp_info.intf_type == INTF_DP) | |
| + return msm_dp_dsc_enabled_for_mode(priv->kms->dp[index], adj_mode); | |
| return NULL; | |
| } | |
| @@ -665,7 +671,6 @@ void dpu_encoder_update_topology(struct drm_encoder *drm_enc, | |
| struct drm_connector *connector; | |
| struct drm_connector_state *conn_state; | |
| struct drm_framebuffer *fb; | |
| - struct drm_dsc_config *dsc; | |
| int i; | |
| @@ -673,10 +678,8 @@ void dpu_encoder_update_topology(struct drm_encoder *drm_enc, | |
| if (dpu_enc->phys_encs[i]) | |
| topology->num_intf++; | |
| - dsc = dpu_encoder_get_dsc_config(drm_enc); | |
| - | |
| /* We only support 2 DSC mode (with 2 LM and 1 INTF) */ | |
| - if (dsc) { | |
| + if (dpu_encoder_dsc_enabled_for_mode(drm_enc, adj_mode)) { | |
| /* | |
| * Use 2 DSC encoders, 2 layer mixers and 1 or 2 interfaces | |
| * when Display Stream Compression (DSC) is enabled, | |
| @@ -684,12 +687,15 @@ void dpu_encoder_update_topology(struct drm_encoder *drm_enc, | |
| * This is power-optimal and can drive up to (including) 4k | |
| * screens. | |
| */ | |
| - WARN(topology->num_intf > 2, | |
| - "DSC topology cannot support more than 2 interfaces\n"); | |
| - if (topology->num_intf >= 2 || dpu_kms->catalog->dsc_count >= 2) | |
| + if ((topology->num_intf == 2 || adj_mode->hdisplay > dpu_kms->catalog->caps->max_mixer_width) && | |
| + dpu_kms->catalog->dsc_count >= 2) | |
| topology->num_dsc = 2; | |
| - else | |
| + else if ((topology->num_intf == 1 && adj_mode->hdisplay <= dpu_kms->catalog->caps->max_mixer_width) | |
| + && dpu_kms->catalog->dsc_count) | |
| topology->num_dsc = 1; | |
| + else | |
| + pr_warn("Unsupported DSC topology for configuration num_intf=%d hdisplay=%d dsc_count=%d\n", | |
| + topology->num_intf, adj_mode->hdisplay, dpu_kms->catalog->dsc_count); | |
| } | |
| connector = drm_atomic_get_new_connector_for_encoder(state, drm_enc); | |
| @@ -1149,6 +1155,43 @@ void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc, | |
| } | |
| } | |
| +static void _dpu_encoder_populate_encoder_phys(struct drm_encoder *drm_enc, | |
| + struct dpu_encoder_virt *dpu_enc) | |
| +{ | |
| + struct msm_compression_info *comp_info; | |
| + struct msm_display_dsc_info *dsc_info; | |
| + int i; | |
| + | |
| + for (i = 0; i < dpu_enc->num_phys_encs; i++) { | |
| + struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i]; | |
| + | |
| + if (!phys) | |
| + continue; | |
| + | |
| + phys->dsc_extra_pclk_cycle_cnt = 0; | |
| + phys->dsc_extra_disp_width = 0; | |
| + phys->dce_bytes_per_line = 0; | |
| + | |
| + if (!dpu_enc->comp_info) { | |
| + phys->comp_type = MSM_DISPLAY_COMPRESSION_NONE; | |
| + phys->comp_ratio = 1; | |
| + } else { | |
| + comp_info = dpu_enc->comp_info; | |
| + dsc_info = &comp_info->msm_dsc_info; | |
| + | |
| + phys->comp_type = comp_info->comp_type; | |
| + phys->comp_ratio = comp_info->comp_ratio; | |
| + | |
| + if (phys->comp_type == MSM_DISPLAY_COMPRESSION_DSC) { | |
| + phys->dsc_extra_pclk_cycle_cnt = dsc_info->pclk_per_line; | |
| + phys->dsc_extra_disp_width = dsc_info->extra_width; | |
| + phys->dce_bytes_per_line = | |
| + dsc_info->bytes_per_pkt * dsc_info->pkt_per_line; | |
| + } | |
| + } | |
| + } | |
| +} | |
| + | |
| static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc, | |
| struct drm_crtc_state *crtc_state, | |
| struct drm_connector_state *conn_state) | |
| @@ -1159,9 +1202,10 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc, | |
| struct dpu_global_state *global_state; | |
| struct dpu_hw_blk *hw_pp[MAX_CHANNELS_PER_ENC]; | |
| struct dpu_hw_blk *hw_ctl[MAX_CHANNELS_PER_ENC]; | |
| + struct dpu_hw_blk *hw_lm[MAX_CHANNELS_PER_ENC]; | |
| struct dpu_hw_blk *hw_dsc[MAX_CHANNELS_PER_ENC]; | |
| struct dpu_hw_blk *hw_cwb[MAX_CHANNELS_PER_ENC]; | |
| - int num_ctl, num_pp, num_dsc, num_pp_per_intf; | |
| + int num_ctl, num_pp, num_dsc, num_pp_per_intf, num_lm; | |
| int num_cwb = 0; | |
| bool is_cwb_encoder; | |
| unsigned int dsc_mask = 0; | |
| @@ -1220,12 +1264,23 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc, | |
| dpu_enc->hw_pp[i] = i < num_pp ? to_dpu_hw_pingpong(hw_pp[i]) | |
| : NULL; | |
| + num_lm = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, | |
| + drm_enc->crtc, DPU_HW_BLK_LM, hw_lm, ARRAY_SIZE(hw_lm)); | |
| + | |
| + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) | |
| + dpu_enc->hw_mixer[i] = i < num_lm ? to_dpu_hw_mixer(hw_lm[i]) | |
| + : NULL; | |
| + | |
| num_dsc = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, | |
| drm_enc->crtc, DPU_HW_BLK_DSC, | |
| hw_dsc, ARRAY_SIZE(hw_dsc)); | |
| - for (i = 0; i < num_dsc; i++) { | |
| - dpu_enc->hw_dsc[i] = to_dpu_hw_dsc(hw_dsc[i]); | |
| - dsc_mask |= BIT(dpu_enc->hw_dsc[i]->idx - DSC_0); | |
| + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { | |
| + if (i < num_dsc) { | |
| + dpu_enc->hw_dsc[i] = to_dpu_hw_dsc(hw_dsc[i]); | |
| + dsc_mask |= BIT(dpu_enc->hw_dsc[i]->idx - DSC_0); | |
| + } else { | |
| + dpu_enc->hw_dsc[i] = NULL; | |
| + } | |
| } | |
| dpu_enc->dsc_mask = dsc_mask; | |
| @@ -1342,7 +1397,9 @@ static void dpu_encoder_virt_atomic_enable(struct drm_encoder *drm_enc, | |
| struct drm_display_mode *cur_mode = NULL; | |
| dpu_enc = to_dpu_encoder_virt(drm_enc); | |
| - dpu_enc->dsc = dpu_encoder_get_dsc_config(drm_enc); | |
| + | |
| + dpu_enc->comp_info = dpu_encoder_get_dsc_config(drm_enc); | |
| + _dpu_encoder_populate_encoder_phys(drm_enc, dpu_enc); | |
| atomic_set(&dpu_enc->frame_done_timeout_cnt, 0); | |
| @@ -1661,6 +1718,10 @@ static void _dpu_encoder_trigger_flush(struct drm_encoder *drm_enc, | |
| if (extra_flush_bits && ctl->ops.update_pending_flush) | |
| ctl->ops.update_pending_flush(ctl, extra_flush_bits); | |
| + if (ctl->ops.update_pending_flush_periph && dpu_encoder_needs_periph_flush(phys)) { | |
| + ctl->ops.update_pending_flush_periph(ctl, phys->hw_intf->idx); | |
| + } | |
| + | |
| ctl->ops.trigger_flush(ctl); | |
| if (ctl->ops.get_pending_flush) | |
| @@ -1963,31 +2024,6 @@ int dpu_encoder_vsync_time(struct drm_encoder *drm_enc, ktime_t *wakeup_time) | |
| return 0; | |
| } | |
| -static u32 | |
| -dpu_encoder_dsc_initial_line_calc(struct drm_dsc_config *dsc, | |
| - u32 enc_ip_width) | |
| -{ | |
| - int ssm_delay, total_pixels, soft_slice_per_enc; | |
| - | |
| - soft_slice_per_enc = enc_ip_width / dsc->slice_width; | |
| - | |
| - /* | |
| - * minimum number of initial line pixels is a sum of: | |
| - * 1. sub-stream multiplexer delay (83 groups for 8bpc, | |
| - * 91 for 10 bpc) * 3 | |
| - * 2. for two soft slice cases, add extra sub-stream multiplexer * 3 | |
| - * 3. the initial xmit delay | |
| - * 4. total pipeline delay through the "lock step" of encoder (47) | |
| - * 5. 6 additional pixels as the output of the rate buffer is | |
| - * 48 bits wide | |
| - */ | |
| - ssm_delay = ((dsc->bits_per_component < 10) ? 84 : 92); | |
| - total_pixels = ssm_delay * 3 + dsc->initial_xmit_delay + 47; | |
| - if (soft_slice_per_enc > 1) | |
| - total_pixels += (ssm_delay * 3); | |
| - return DIV_ROUND_UP(total_pixels, dsc->slice_width); | |
| -} | |
| - | |
| static void dpu_encoder_dsc_pipe_cfg(struct dpu_hw_ctl *ctl, | |
| struct dpu_hw_dsc *hw_dsc, | |
| struct dpu_hw_pingpong *hw_pp, | |
| @@ -2015,7 +2051,7 @@ static void dpu_encoder_dsc_pipe_cfg(struct dpu_hw_ctl *ctl, | |
| } | |
| static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc, | |
| - struct drm_dsc_config *dsc) | |
| + struct msm_display_dsc_info *dsc_info) | |
| { | |
| struct dpu_encoder_phys *enc_master = dpu_enc->cur_master; | |
| struct dpu_hw_ctl *ctl = enc_master->hw_ctl; | |
| @@ -2023,10 +2059,10 @@ static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc, | |
| struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; | |
| int this_frame_slices; | |
| int intf_ip_w, enc_ip_w; | |
| - int dsc_common_mode; | |
| - int pic_width; | |
| - u32 initial_lines; | |
| - int num_dsc = 0; | |
| + int dsc_common_mode = 0; | |
| + int dsc_pic_width; | |
| + int num_dsc, num_intf; | |
| + bool dsc_merge; | |
| int i; | |
| for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { | |
| @@ -2035,29 +2071,106 @@ static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc, | |
| if (!hw_pp[i] || !hw_dsc[i]) | |
| break; | |
| - | |
| num_dsc++; | |
| } | |
| - pic_width = dsc->pic_width; | |
| + for (i = 0; i < MAX_PHYS_ENCODERS_PER_VIRTUAL; i++) | |
| + if (dpu_enc->phys_encs[i]) | |
| + num_intf++; | |
| - dsc_common_mode = 0; | |
| + dsc_merge = num_dsc > num_intf; | |
| + | |
| + /* | |
| + * If this encoder is driving more than one DSC encoder, they | |
| + * operate in tandem, same pic dimension needs to be used by | |
| + * each of them.(pp-split is assumed to be not supported) | |
| + * | |
| + * If encoder is driving more than 2 DSCs, each DSC pair will operate | |
| + * on half of the picture in tandem. | |
| + */ | |
| + dsc_pic_width = dsc_info->drm_dsc.pic_width; | |
| + this_frame_slices = dsc_pic_width / dsc_info->drm_dsc.slice_width; | |
| + intf_ip_w = this_frame_slices * dsc_info->drm_dsc.slice_width; | |
| + enc_ip_w = intf_ip_w; | |
| + | |
| + intf_ip_w /= num_intf; | |
| if (num_dsc > 1) | |
| dsc_common_mode |= DSC_MODE_SPLIT_PANEL; | |
| - if (dpu_encoder_use_dsc_merge(enc_master->parent)) | |
| + if (dsc_merge) { | |
| dsc_common_mode |= DSC_MODE_MULTIPLEX; | |
| + /* | |
| + * in dsc merge case: when using 2 encoders for the same | |
| + * stream, no. of slices need to be same on both the | |
| + * encoders. | |
| + */ | |
| + enc_ip_w = intf_ip_w / 2; | |
| + } | |
| + | |
| if (enc_master->intf_mode == INTF_MODE_VIDEO) | |
| dsc_common_mode |= DSC_MODE_VIDEO; | |
| - this_frame_slices = pic_width / dsc->slice_width; | |
| - intf_ip_w = this_frame_slices * dsc->slice_width; | |
| + dsc_info->num_active_ss_per_enc = dsc_info->drm_dsc.slice_count; | |
| + | |
| + if (dsc_common_mode & DSC_MODE_MULTIPLEX) | |
| + dsc_info->num_active_ss_per_enc = dsc_info->drm_dsc.slice_count >> 1; | |
| + | |
| + dpu_dsc_populate_dsc_private_params(dsc_info, intf_ip_w); | |
| - enc_ip_w = intf_ip_w / num_dsc; | |
| - initial_lines = dpu_encoder_dsc_initial_line_calc(dsc, enc_ip_w); | |
| + dpu_dsc_initial_line_calc(dsc_info, enc_ip_w, dsc_common_mode); | |
| + /* | |
| + * dsc merge case: when using 2 encoders for the same stream, | |
| + * no. of slices need to be same on both the encoders. | |
| + */ | |
| for (i = 0; i < num_dsc; i++) | |
| dpu_encoder_dsc_pipe_cfg(ctl, hw_dsc[i], hw_pp[i], | |
| - dsc, dsc_common_mode, initial_lines); | |
| + &dsc_info->drm_dsc, dsc_common_mode, dsc_info->initial_lines); | |
| +} | |
| + | |
| +static void dpu_encoder_dsc_pipe_clr(struct dpu_hw_ctl *ctl, | |
| + struct dpu_hw_dsc *hw_dsc, | |
| + struct dpu_hw_pingpong *hw_pp) | |
| +{ | |
| + if (hw_dsc->ops.dsc_disable) | |
| + hw_dsc->ops.dsc_disable(hw_dsc); | |
| + | |
| + if (hw_pp->ops.disable_dsc) | |
| + hw_pp->ops.disable_dsc(hw_pp); | |
| + | |
| + if (hw_dsc->ops.dsc_bind_pingpong_blk) | |
| + hw_dsc->ops.dsc_bind_pingpong_blk(hw_dsc, PINGPONG_NONE); | |
| + | |
| + if (ctl->ops.update_pending_flush_dsc) | |
| + ctl->ops.update_pending_flush_dsc(ctl, hw_dsc->idx); | |
| +} | |
| + | |
| +static void dpu_encoder_unprep_dsc(struct dpu_encoder_virt *dpu_enc) | |
| +{ | |
| + /* coding only for 2LM, 2enc, 1 dsc config */ | |
| + struct dpu_encoder_phys *enc_master = dpu_enc->cur_master; | |
| + struct dpu_hw_ctl *ctl = enc_master->hw_ctl; | |
| + struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; | |
| + struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; | |
| + int i; | |
| + | |
| + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { | |
| + hw_pp[i] = dpu_enc->hw_pp[i]; | |
| + hw_dsc[i] = dpu_enc->hw_dsc[i]; | |
| + | |
| + if (hw_dsc[i] && hw_pp[i]) | |
| + dpu_encoder_dsc_pipe_clr(ctl, hw_dsc[i], hw_pp[i]); | |
| + } | |
| +} | |
| + | |
| +void dpu_encoder_dsc_cleanup(struct dpu_encoder_phys *phys_enc) | |
| +{ | |
| + struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(phys_enc->parent); | |
| + | |
| + if (dpu_enc->comp_info) { | |
| + if (phys_enc->comp_type == MSM_DISPLAY_COMPRESSION_DSC) | |
| + dpu_encoder_unprep_dsc(dpu_enc); | |
| + dpu_enc->comp_info = NULL; | |
| + } | |
| } | |
| /** | |
| @@ -2071,7 +2184,9 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc) | |
| { | |
| struct dpu_encoder_virt *dpu_enc; | |
| struct dpu_encoder_phys *phys; | |
| + struct msm_compression_info *comp_info; | |
| bool needs_hw_reset = false; | |
| + bool needs_phy_enable = false; | |
| unsigned int i; | |
| dpu_enc = to_dpu_encoder_virt(drm_enc); | |
| @@ -2086,6 +2201,8 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc) | |
| phys->ops.prepare_for_kickoff(phys); | |
| if (phys->enable_state == DPU_ENC_ERR_NEEDS_HW_RESET) | |
| needs_hw_reset = true; | |
| + if (phys->enable_state == DPU_ENC_ENABLING) | |
| + needs_phy_enable = true; | |
| } | |
| DPU_ATRACE_END("enc_prepare_for_kickoff"); | |
| @@ -2099,8 +2216,10 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc) | |
| } | |
| } | |
| - if (dpu_enc->dsc) | |
| - dpu_encoder_prep_dsc(dpu_enc, dpu_enc->dsc); | |
| + comp_info = dpu_enc->comp_info; | |
| + if (comp_info && phys->comp_type == MSM_DISPLAY_COMPRESSION_DSC && | |
| + (needs_phy_enable || dpu_enc->disp_info.is_cmd_mode)) | |
| + dpu_encoder_prep_dsc(dpu_enc, &comp_info->msm_dsc_info); | |
| } | |
| /** | |
| @@ -2176,79 +2295,43 @@ void dpu_encoder_kickoff(struct drm_encoder *drm_enc) | |
| DPU_ATRACE_END("encoder_kickoff"); | |
| } | |
| -static void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc) | |
| +void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc) | |
| { | |
| - int i, num_lm; | |
| - struct dpu_global_state *global_state; | |
| - struct dpu_hw_blk *hw_lm[2]; | |
| - struct dpu_hw_mixer *hw_mixer[2]; | |
| struct dpu_hw_ctl *ctl = phys_enc->hw_ctl; | |
| + int i; | |
| + struct dpu_encoder_virt *dpu_enc; | |
| - /* reset all mixers for this encoder */ | |
| - if (ctl->ops.clear_all_blendstages) | |
| - ctl->ops.clear_all_blendstages(ctl); | |
| - | |
| - global_state = dpu_kms_get_existing_global_state(phys_enc->dpu_kms); | |
| - | |
| - num_lm = dpu_rm_get_assigned_resources(&phys_enc->dpu_kms->rm, global_state, | |
| - phys_enc->parent->crtc, DPU_HW_BLK_LM, hw_lm, ARRAY_SIZE(hw_lm)); | |
| - | |
| - for (i = 0; i < num_lm; i++) { | |
| - hw_mixer[i] = to_dpu_hw_mixer(hw_lm[i]); | |
| - if (ctl->ops.update_pending_flush_mixer) | |
| - ctl->ops.update_pending_flush_mixer(ctl, hw_mixer[i]->idx); | |
| - | |
| - /* clear all blendstages */ | |
| - if (ctl->ops.setup_blendstage) | |
| - ctl->ops.setup_blendstage(ctl, hw_mixer[i]->idx, NULL); | |
| + dpu_enc = to_dpu_encoder_virt(phys_enc->parent); | |
| - if (hw_mixer[i]->ops.clear_all_blendstages) | |
| - hw_mixer[i]->ops.clear_all_blendstages(hw_mixer[i]); | |
| + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { | |
| + if (dpu_enc->hw_mixer[i]) { | |
| + if (ctl->ops.update_pending_flush_mixer) | |
| + ctl->ops.update_pending_flush_mixer(ctl, dpu_enc->hw_mixer[i]->idx); | |
| - if (ctl->ops.set_active_lms) | |
| - ctl->ops.set_active_lms(ctl, NULL); | |
| + /* clear all blendstages */ | |
| + if (ctl->ops.setup_blendstage) | |
| + ctl->ops.setup_blendstage(ctl, dpu_enc->hw_mixer[i]->idx, NULL); | |
| - if (ctl->ops.set_active_fetch_pipes) | |
| - ctl->ops.set_active_fetch_pipes(ctl, NULL); | |
| + if (dpu_enc->hw_mixer[i]->ops.clear_all_blendstages) | |
| + dpu_enc->hw_mixer[i]->ops.clear_all_blendstages(dpu_enc->hw_mixer[i]); | |
| - if (ctl->ops.set_active_pipes) | |
| - ctl->ops.set_active_pipes(ctl, NULL); | |
| + if (dpu_enc->hw_mixer[i]->ops.setup_blendstage) | |
| + dpu_enc->hw_mixer[i]->ops.setup_blendstage(dpu_enc->hw_mixer[i], dpu_enc->hw_mixer[i]->idx, NULL); | |
| + } | |
| } | |
| -} | |
| - | |
| -static void dpu_encoder_dsc_pipe_clr(struct dpu_hw_ctl *ctl, | |
| - struct dpu_hw_dsc *hw_dsc, | |
| - struct dpu_hw_pingpong *hw_pp) | |
| -{ | |
| - if (hw_dsc->ops.dsc_disable) | |
| - hw_dsc->ops.dsc_disable(hw_dsc); | |
| - if (hw_pp->ops.disable_dsc) | |
| - hw_pp->ops.disable_dsc(hw_pp); | |
| - | |
| - if (hw_dsc->ops.dsc_bind_pingpong_blk) | |
| - hw_dsc->ops.dsc_bind_pingpong_blk(hw_dsc, PINGPONG_NONE); | |
| + /* reset all mixers for this encoder */ | |
| + if (ctl->ops.clear_all_blendstages) | |
| + ctl->ops.clear_all_blendstages(ctl); | |
| - if (ctl->ops.update_pending_flush_dsc) | |
| - ctl->ops.update_pending_flush_dsc(ctl, hw_dsc->idx); | |
| -} | |
| + if (ctl->ops.set_active_lms) | |
| + ctl->ops.set_active_lms(ctl, NULL); | |
| -static void dpu_encoder_unprep_dsc(struct dpu_encoder_virt *dpu_enc) | |
| -{ | |
| - /* coding only for 2LM, 2enc, 1 dsc config */ | |
| - struct dpu_encoder_phys *enc_master = dpu_enc->cur_master; | |
| - struct dpu_hw_ctl *ctl = enc_master->hw_ctl; | |
| - struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; | |
| - struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; | |
| - int i; | |
| + if (ctl->ops.set_active_fetch_pipes) | |
| + ctl->ops.set_active_fetch_pipes(ctl, NULL); | |
| - for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { | |
| - hw_pp[i] = dpu_enc->hw_pp[i]; | |
| - hw_dsc[i] = dpu_enc->hw_dsc[i]; | |
| - | |
| - if (hw_pp[i] && hw_dsc[i]) | |
| - dpu_encoder_dsc_pipe_clr(ctl, hw_dsc[i], hw_pp[i]); | |
| - } | |
| + if (ctl->ops.set_active_pipes) | |
| + ctl->ops.set_active_pipes(ctl, NULL); | |
| } | |
| /** | |
| @@ -2319,10 +2402,7 @@ void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc) | |
| phys_enc->hw_cdm->idx); | |
| } | |
| - if (dpu_enc->dsc) { | |
| - dpu_encoder_unprep_dsc(dpu_enc); | |
| - dpu_enc->dsc = NULL; | |
| - } | |
| + dpu_encoder_dsc_cleanup(phys_enc); | |
| intf_cfg.stream_sel = 0; /* Don't care value for video mode */ | |
| intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc); | |
| @@ -2355,8 +2435,8 @@ void dpu_encoder_helper_phys_setup_cwb(struct dpu_encoder_phys *phys_enc, | |
| struct dpu_kms *dpu_kms; | |
| struct dpu_global_state *global_state; | |
| - struct dpu_hw_blk *rt_pp_list[MAX_CHANNELS_PER_ENC]; | |
| - int num_pp; | |
| + struct dpu_hw_blk *rt_pp_list[MAX_CHANNELS_PER_ENC] = {0}; | |
| + int num_pp = 0; | |
| if (!phys_enc->hw_wb) | |
| return; | |
| @@ -2369,16 +2449,18 @@ void dpu_encoder_helper_phys_setup_cwb(struct dpu_encoder_phys *phys_enc, | |
| return; | |
| } | |
| - dpu_kms = phys_enc->dpu_kms; | |
| - global_state = dpu_kms_get_existing_global_state(dpu_kms); | |
| - num_pp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, | |
| - phys_enc->parent->crtc, | |
| - DPU_HW_BLK_PINGPONG, rt_pp_list, | |
| - ARRAY_SIZE(rt_pp_list)); | |
| + if (enable) { | |
| + dpu_kms = phys_enc->dpu_kms; | |
| + global_state = dpu_kms_get_existing_global_state(dpu_kms); | |
| + num_pp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, | |
| + phys_enc->parent->crtc, | |
| + DPU_HW_BLK_PINGPONG, rt_pp_list, | |
| + ARRAY_SIZE(rt_pp_list)); | |
| - if (num_pp == 0 || num_pp > MAX_CHANNELS_PER_ENC) { | |
| - DPU_DEBUG_ENC(dpu_enc, "invalid num_pp %d\n", num_pp); | |
| - return; | |
| + if (num_pp == 0 || num_pp > MAX_CHANNELS_PER_ENC) { | |
| + DPU_DEBUG_ENC(dpu_enc, "invalid num_pp %d\n", num_pp); | |
| + return; | |
| + } | |
| } | |
| /* | |
| @@ -2393,9 +2475,9 @@ void dpu_encoder_helper_phys_setup_cwb(struct dpu_encoder_phys *phys_enc, | |
| continue; | |
| if (enable) { | |
| - struct dpu_hw_pingpong *hw_pp = | |
| - to_dpu_hw_pingpong(rt_pp_list[i]); | |
| - cwb_cfg.pp_idx = hw_pp->idx; | |
| + struct dpu_hw_pingpong *hw_pp = i < num_pp ? | |
| + to_dpu_hw_pingpong(rt_pp_list[i]) : NULL; | |
| + cwb_cfg.pp_idx = hw_pp ? hw_pp->idx : PINGPONG_NONE; | |
| } else { | |
| cwb_cfg.pp_idx = PINGPONG_NONE; | |
| } | |
| @@ -2911,6 +2993,31 @@ unsigned int dpu_encoder_helper_get_cwb_mask(struct dpu_encoder_phys *phys_enc) | |
| return dpu_enc->cwb_mask; | |
| } | |
| +enum dpu_3d_blend_mode dpu_encoder_helper_get_3d_blend_mode( | |
| + struct dpu_encoder_phys *phys_enc) | |
| +{ | |
| + struct drm_encoder *encoder = phys_enc->parent; | |
| + struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(encoder); | |
| + int i, num_mixers = 0; | |
| + | |
| + if (!phys_enc || phys_enc->enable_state == DPU_ENC_DISABLING) | |
| + return BLEND_3D_NONE; | |
| + | |
| + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { | |
| + if (!dpu_enc->hw_mixer[i]) | |
| + break; | |
| + num_mixers++; | |
| + } | |
| + | |
| + /* Use merge_3d unless DSC MERGE topology is used */ | |
| + if (phys_enc->split_role == ENC_ROLE_SOLO && | |
| + num_mixers == CRTC_DUAL_MIXERS && | |
| + !dpu_encoder_use_dsc_merge(phys_enc->parent)) | |
| + return BLEND_3D_H_ROW_INT; | |
| + | |
| + return BLEND_3D_NONE; | |
| +} | |
| + | |
| /** | |
| * dpu_encoder_helper_get_dsc - get DSC blocks mask for the DPU encoder | |
| * This helper function is used by physical encoder to get DSC blocks mask | |
| diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | |
| index ca1ca2e51d7e..9f8b7436c332 100644 | |
| --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | |
| +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | |
| @@ -72,8 +72,6 @@ int dpu_encoder_get_vsync_count(struct drm_encoder *drm_enc); | |
| bool dpu_encoder_is_widebus_enabled(const struct drm_encoder *drm_enc); | |
| -bool dpu_encoder_is_dsc_enabled(const struct drm_encoder *drm_enc); | |
| - | |
| int dpu_encoder_get_crc_values_cnt(const struct drm_encoder *drm_enc); | |
| void dpu_encoder_setup_misr(const struct drm_encoder *drm_encoder); | |
| diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | |
| index 61b22d949454..522f6e87967d 100644 | |
| --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | |
| +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | |
| @@ -197,6 +197,11 @@ struct dpu_encoder_phys { | |
| atomic_t pending_kickoff_cnt; | |
| wait_queue_head_t pending_kickoff_wq; | |
| unsigned int irq[INTR_IDX_MAX]; | |
| + enum msm_display_compression_type comp_type; | |
| + u32 comp_ratio; | |
| + u32 dsc_extra_pclk_cycle_cnt; | |
| + u32 dsc_extra_disp_width; | |
| + u32 dce_bytes_per_line; | |
| bool has_intf_te; | |
| }; | |
| @@ -290,30 +295,12 @@ struct dpu_encoder_phys *dpu_encoder_phys_wb_init(struct drm_device *dev, | |
| void dpu_encoder_helper_trigger_start(struct dpu_encoder_phys *phys_enc); | |
| -static inline enum dpu_3d_blend_mode dpu_encoder_helper_get_3d_blend_mode( | |
| - struct dpu_encoder_phys *phys_enc) | |
| -{ | |
| - struct dpu_crtc_state *dpu_cstate; | |
| - | |
| - if (!phys_enc || phys_enc->enable_state == DPU_ENC_DISABLING) | |
| - return BLEND_3D_NONE; | |
| - | |
| - dpu_cstate = to_dpu_crtc_state(phys_enc->parent->crtc->state); | |
| - | |
| - /* Use merge_3d unless DSC MERGE topology is used */ | |
| - if (phys_enc->split_role == ENC_ROLE_SOLO && | |
| - dpu_cstate->num_mixers == CRTC_DUAL_MIXERS && | |
| - !dpu_encoder_use_dsc_merge(phys_enc->parent)) | |
| - return BLEND_3D_H_ROW_INT; | |
| - | |
| - return BLEND_3D_NONE; | |
| -} | |
| - | |
| +enum dpu_3d_blend_mode dpu_encoder_helper_get_3d_blend_mode(struct dpu_encoder_phys *phys_enc); | |
| unsigned int dpu_encoder_helper_get_cwb_mask(struct dpu_encoder_phys *phys_enc); | |
| unsigned int dpu_encoder_helper_get_dsc(struct dpu_encoder_phys *phys_enc); | |
| -struct drm_dsc_config *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc); | |
| +struct msm_compression_info *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc); | |
| u32 dpu_encoder_get_drm_fmt(struct dpu_encoder_phys *phys_enc); | |
| @@ -331,6 +318,10 @@ int dpu_encoder_helper_wait_for_irq(struct dpu_encoder_phys *phys_enc, | |
| void (*func)(void *arg), | |
| struct dpu_encoder_wait_info *wait_info); | |
| +void dpu_encoder_dsc_cleanup(struct dpu_encoder_phys *phys_enc); | |
| + | |
| +void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc); | |
| + | |
| void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc); | |
| void dpu_encoder_helper_phys_setup_cwb(struct dpu_encoder_phys *phys_enc, | |
| diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c | |
| index 93db1484f606..b97df3ba1295 100644 | |
| --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c | |
| +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c | |
| @@ -532,13 +532,18 @@ static void dpu_encoder_phys_cmd_disable(struct dpu_encoder_phys *phys_enc) | |
| { | |
| struct dpu_encoder_phys_cmd *cmd_enc = | |
| to_dpu_encoder_phys_cmd(phys_enc); | |
| - struct dpu_hw_ctl *ctl; | |
| + struct dpu_hw_ctl *ctl = phys_enc->hw_ctl; | |
| + struct dpu_hw_intf_cfg intf_cfg = { 0 }; | |
| if (phys_enc->enable_state == DPU_ENC_DISABLED) { | |
| DPU_ERROR_CMDENC(cmd_enc, "already disabled\n"); | |
| return; | |
| } | |
| + ctl->ops.reset(ctl); | |
| + | |
| + dpu_encoder_helper_reset_mixers(phys_enc); | |
| + | |
| if (phys_enc->has_intf_te) { | |
| DRM_DEBUG_KMS("id:%u intf:%d state:%d\n", DRMID(phys_enc->parent), | |
| phys_enc->hw_intf->idx - INTF_0, | |
| @@ -565,10 +570,26 @@ static void dpu_encoder_phys_cmd_disable(struct dpu_encoder_phys *phys_enc) | |
| phys_enc->hw_intf, | |
| PINGPONG_NONE); | |
| - ctl = phys_enc->hw_ctl; | |
| ctl->ops.update_pending_flush_intf(ctl, phys_enc->hw_intf->idx); | |
| } | |
| + dpu_encoder_dsc_cleanup(phys_enc); | |
| + | |
| + if (ctl->ops.reset_intf_cfg) { | |
| + intf_cfg.intf = phys_enc->hw_intf->idx; | |
| + if (phys_enc->split_role == ENC_ROLE_MASTER) | |
| + intf_cfg.intf_master = phys_enc->hw_intf->idx; | |
| + intf_cfg.intf_mode_sel = DPU_CTL_MODE_SEL_CMD; | |
| + intf_cfg.stream_sel = cmd_enc->stream_sel; | |
| + intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc); | |
| + intf_cfg.dsc = dpu_encoder_helper_get_dsc(phys_enc); | |
| + ctl->ops.reset_intf_cfg(ctl, &intf_cfg); | |
| + } | |
| + | |
| + ctl->ops.trigger_flush(ctl); | |
| + ctl->ops.trigger_start(ctl); | |
| + ctl->ops.clear_pending_flush(ctl); | |
| + | |
| phys_enc->enable_state = DPU_ENC_DISABLED; | |
| } | |
| diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | |
| index 0ba777bda253..335cd63de798 100644 | |
| --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | |
| +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c | |
| @@ -45,6 +45,7 @@ static void drm_mode_to_intf_timing_params( | |
| const struct drm_display_mode *mode, | |
| struct dpu_hw_intf_timing_params *timing) | |
| { | |
| + bool comp_ratio_gt_1 = false; | |
| memset(timing, 0, sizeof(*timing)); | |
| if ((mode->htotal < mode->hsync_end) | |
| @@ -95,7 +96,11 @@ static void drm_mode_to_intf_timing_params( | |
| } | |
| timing->wide_bus_en = dpu_encoder_is_widebus_enabled(phys_enc->parent); | |
| - timing->compression_en = dpu_encoder_is_dsc_enabled(phys_enc->parent); | |
| + timing->compression_en = phys_enc->comp_type == MSM_DISPLAY_COMPRESSION_DSC; | |
| + if (timing->compression_en) { | |
| + timing->dce_bytes_per_line = phys_enc->dce_bytes_per_line; | |
| + comp_ratio_gt_1 = phys_enc->comp_ratio > 1; | |
| + } | |
| /* | |
| * For DP/EDP, Shift timings to align it to bottom right. | |
| @@ -110,31 +115,38 @@ static void drm_mode_to_intf_timing_params( | |
| } | |
| /* | |
| - * for DP, divide the horizonal parameters by 2 when | |
| + * for DP, divide the horizontal parameters by 2 when | |
| * widebus is enabled | |
| */ | |
| - if (phys_enc->hw_intf->cap->type == INTF_DP && timing->wide_bus_en) { | |
| + if (phys_enc->hw_intf->cap->type == INTF_DP && | |
| + (timing->wide_bus_en || comp_ratio_gt_1)) { | |
| timing->width = timing->width >> 1; | |
| timing->xres = timing->xres >> 1; | |
| timing->h_back_porch = timing->h_back_porch >> 1; | |
| timing->h_front_porch = timing->h_front_porch >> 1; | |
| timing->hsync_pulse_width = timing->hsync_pulse_width >> 1; | |
| + | |
| + if (comp_ratio_gt_1) { | |
| + timing->extra_dto_cycles = | |
| + phys_enc->dsc_extra_pclk_cycle_cnt; | |
| + timing->width += phys_enc->dsc_extra_disp_width; | |
| + timing->h_back_porch += | |
| + phys_enc->dsc_extra_disp_width; | |
| + } | |
| } | |
| /* | |
| - * for DSI, if compression is enabled, then divide the horizonal active | |
| + * for DSI, if compression is enabled, then divide the horizontal active | |
| * timing parameters by compression ratio. bits of 3 components(R/G/B) | |
| * is compressed into bits of 1 pixel. | |
| */ | |
| if (phys_enc->hw_intf->cap->type != INTF_DP && timing->compression_en) { | |
| - struct drm_dsc_config *dsc = | |
| - dpu_encoder_get_dsc_config(phys_enc->parent); | |
| - /* | |
| - * TODO: replace drm_dsc_get_bpp_int with logic to handle | |
| - * fractional part if there is fraction | |
| - */ | |
| - timing->width = timing->width * drm_dsc_get_bpp_int(dsc) / | |
| - (dsc->bits_per_component * 3); | |
| + struct msm_compression_info *comp_info = | |
| + dpu_encoder_get_dsc_config(phys_enc->parent); | |
| + struct drm_dsc_config *dsc = &comp_info->msm_dsc_info.drm_dsc; | |
| + | |
| + timing->width = (timing->width * dsc->bits_per_pixel / | |
| + (dsc->bits_per_component * 3)) >> 4; | |
| timing->xres = timing->width; | |
| } | |
| } | |
| diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c | |
| index b3395e9c34a1..04f17c610faa 100644 | |
| --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c | |
| +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c | |
| @@ -91,9 +91,9 @@ static void dpu_hw_dsc_config_1_2(struct dpu_hw_dsc *hw_dsc, | |
| { | |
| struct dpu_hw_blk_reg_map *hw; | |
| const struct dpu_dsc_sub_blks *sblk; | |
| + struct msm_display_dsc_info *dsc_info; | |
| u32 data = 0; | |
| u32 det_thresh_flatness; | |
| - u32 num_active_slice_per_enc; | |
| u32 bpp; | |
| if (!hw_dsc || !dsc) | |
| @@ -101,6 +101,8 @@ static void dpu_hw_dsc_config_1_2(struct dpu_hw_dsc *hw_dsc, | |
| hw = &hw_dsc->hw; | |
| + dsc_info = to_msm_dsc_info(dsc); | |
| + | |
| sblk = hw_dsc->caps->sblk; | |
| if (mode & DSC_MODE_SPLIT_PANEL) | |
| @@ -109,11 +111,7 @@ static void dpu_hw_dsc_config_1_2(struct dpu_hw_dsc *hw_dsc, | |
| if (mode & DSC_MODE_MULTIPLEX) | |
| data |= BIT(1); | |
| - num_active_slice_per_enc = dsc->slice_count; | |
| - if (mode & DSC_MODE_MULTIPLEX) | |
| - num_active_slice_per_enc = dsc->slice_count / 2; | |
| - | |
| - data |= (num_active_slice_per_enc & 0x3) << 7; | |
| + data |= (dsc_info->num_active_ss_per_enc & 0x3) << 7; | |
| DPU_REG_WRITE(hw, DSC_CMN_MAIN_CNF, data); | |
| @@ -122,7 +120,7 @@ static void dpu_hw_dsc_config_1_2(struct dpu_hw_dsc *hw_dsc, | |
| if (mode & DSC_MODE_VIDEO) | |
| data |= BIT(9); | |
| - data |= (_dsc_calc_output_buf_max_addr(hw_dsc, num_active_slice_per_enc) << 18); | |
| + data |= (_dsc_calc_output_buf_max_addr(hw_dsc, dsc_info->num_active_ss_per_enc) << 18); | |
| DPU_REG_WRITE(hw, sblk->enc.base + ENC_DF_CTRL, data); | |
| diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | |
| index 7e620f590984..b3604c7248dc 100644 | |
| --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | |
| +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c | |
| @@ -122,7 +122,7 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *intf, | |
| /* read interface_cfg */ | |
| intf_cfg = DPU_REG_READ(c, INTF_CONFIG); | |
| - if (intf->cap->type == INTF_DP) | |
| + if (intf->cap->type == INTF_EDP || intf->cap->type == INTF_DP) | |
| dp_intf = true; | |
| hsync_period = p->hsync_pulse_width + p->h_back_porch + p->width + | |
| @@ -170,19 +170,24 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *intf, | |
| if (p->wide_bus_en) | |
| intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN; | |
| - data_width = p->width; | |
| - | |
| /* | |
| * If widebus is enabled, data is valid for only half the active window | |
| * since the data rate is doubled in this mode. But for the compression | |
| * mode in DP case, the p->width is already adjusted in | |
| * drm_mode_to_intf_timing_params() | |
| */ | |
| - if (p->wide_bus_en && !dp_intf) | |
| + if (p->compression_en) { | |
| + if (p->wide_bus_en) | |
| + data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 6); | |
| + else | |
| + data_width = DIV_ROUND_UP(p->dce_bytes_per_line, 3); | |
| + } else if (!dp_intf && p->wide_bus_en) { | |
| data_width = p->width >> 1; | |
| + } else { | |
| + data_width = p->width; | |
| + } | |
| - /* TODO: handle DSC+DP case, we only handle DSC+DSI case so far */ | |
| - if (p->compression_en && !dp_intf && | |
| + if (p->compression_en && | |
| intf->mdss_ver->core_major_ver >= 7) | |
| intf_cfg2 |= INTF_CFG2_DCE_DATA_COMPRESS; | |
| @@ -205,6 +210,13 @@ static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *intf, | |
| display_hctl = active_hctl; | |
| intf_cfg |= INTF_CFG_ACTIVE_H_EN | INTF_CFG_ACTIVE_V_EN; | |
| + | |
| + if (p->compression_en) { | |
| + active_data_hctl = (hsync_start_x + p->extra_dto_cycles) << 16; | |
| + active_data_hctl += hsync_start_x; | |
| + | |
| + display_data_hctl = active_data_hctl; | |
| + } | |
| } | |
| den_polarity = 0; | |
| @@ -578,9 +590,13 @@ static void dpu_hw_intf_program_intf_cmd_cfg(struct dpu_hw_intf *intf, | |
| if (cmd_mode_cfg->data_compress) | |
| intf_cfg2 |= INTF_CFG2_DCE_DATA_COMPRESS; | |
| + else | |
| + intf_cfg2 &= ~INTF_CFG2_DCE_DATA_COMPRESS; | |
| if (cmd_mode_cfg->wide_bus_en) | |
| intf_cfg2 |= INTF_CFG2_DATABUS_WIDEN; | |
| + else | |
| + intf_cfg2 &= ~INTF_CFG2_DATABUS_WIDEN; | |
| DPU_REG_WRITE(&intf->hw, INTF_CONFIG2, intf_cfg2); | |
| } | |
| diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | |
| index f6ef2c21b66d..5423c171b65d 100644 | |
| --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | |
| +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h | |
| @@ -35,6 +35,8 @@ struct dpu_hw_intf_timing_params { | |
| bool wide_bus_en; | |
| bool compression_en; | |
| + u32 extra_dto_cycles; /* for DP only */ | |
| + u32 dce_bytes_per_line; | |
| }; | |
| struct dpu_hw_intf_prog_fetch { | |
| diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | |
| index 61d7e65469b3..7b95debe6eea 100644 | |
| --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | |
| +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | |
| @@ -663,6 +663,7 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev, | |
| info.num_of_h_tiles = 1; | |
| info.h_tile_instance[0] = i; | |
| info.intf_type = INTF_DP; | |
| + info.is_cmd_mode = 0; /* dp always video mode */ | |
| encoder = dpu_encoder_init(dev, DRM_MODE_ENCODER_TMDS, &info); | |
| if (IS_ERR(encoder)) { | |
| @@ -877,9 +878,11 @@ static int _dpu_kms_drm_obj_init(struct dpu_kms *dpu_kms) | |
| } | |
| } | |
| - /* All CRTCs are compatible with all encoders */ | |
| - drm_for_each_encoder(encoder, dev) | |
| - encoder->possible_crtcs = (1 << dev->mode_config.num_crtc) - 1; | |
| + num_encoders = 0; | |
| + drm_for_each_encoder(encoder, dev) { | |
| + encoder->possible_crtcs = 1 << num_encoders; | |
| + num_encoders++; | |
| + } | |
| return 0; | |
| } | |
| diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | |
| index 7e77d88f8959..498d0336ce70 100644 | |
| --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | |
| +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | |
| @@ -353,13 +353,15 @@ static bool _dpu_rm_check_lm_and_get_connected_blks(struct dpu_rm *rm, | |
| static int _dpu_rm_reserve_lms(struct dpu_rm *rm, | |
| struct dpu_global_state *global_state, | |
| uint32_t crtc_id, | |
| - struct msm_display_topology *topology) | |
| + struct msm_display_topology *topology, | |
| + bool pp_even_odd[MAX_BLOCKS]) | |
| { | |
| int lm_idx[MAX_BLOCKS]; | |
| int pp_idx[MAX_BLOCKS]; | |
| int dspp_idx[MAX_BLOCKS] = {0}; | |
| int i, lm_count = 0; | |
| + const bool dsc_merge = topology->num_dsc > topology->num_intf; | |
| if (!topology->num_lm) { | |
| DPU_ERROR("zero LMs in topology\n"); | |
| @@ -377,7 +379,6 @@ static int _dpu_rm_reserve_lms(struct dpu_rm *rm, | |
| * primary mixer if failed to find its peer. | |
| */ | |
| lm_count &= ~1; | |
| - lm_idx[lm_count] = i; | |
| if (!_dpu_rm_check_lm_and_get_connected_blks(rm, global_state, | |
| crtc_id, i, &pp_idx[lm_count], | |
| @@ -385,6 +386,10 @@ static int _dpu_rm_reserve_lms(struct dpu_rm *rm, | |
| continue; | |
| } | |
| + lm_idx[lm_count] = i; | |
| + pp_even_odd[lm_count] = pp_idx[lm_count] & 1; | |
| + if (dsc_merge && pp_even_odd[lm_count]) | |
| + continue; | |
| ++lm_count; | |
| /* Valid primary mixer found, find matching peers */ | |
| @@ -406,6 +411,7 @@ static int _dpu_rm_reserve_lms(struct dpu_rm *rm, | |
| } | |
| lm_idx[lm_count] = j; | |
| + pp_even_odd[lm_count] = pp_idx[lm_count] & 1; | |
| ++lm_count; | |
| } | |
| } | |
| @@ -519,15 +525,19 @@ static int _dpu_rm_pingpong_dsc_check(int dsc_idx, int pp_idx) | |
| static int _dpu_rm_dsc_alloc(struct dpu_rm *rm, | |
| struct dpu_global_state *global_state, | |
| uint32_t crtc_id, | |
| - const struct msm_display_topology *top) | |
| + const struct msm_display_topology *top, | |
| + bool pp_even_odd[MAX_BLOCKS]) | |
| { | |
| int num_dsc = 0; | |
| int pp_idx = 0; | |
| - int dsc_idx; | |
| + int dsc_idx, dsc_idx_even = 0, dsc_idx_odd = 1, idx_even_odd; | |
| int ret; | |
| - for (dsc_idx = 0; dsc_idx < ARRAY_SIZE(rm->dsc_blks) && | |
| - num_dsc < top->num_dsc; dsc_idx++) { | |
| + for (; | |
| + (idx_even_odd = (pp_even_odd[num_dsc & ~1] ^ num_dsc) & 1), | |
| + (dsc_idx = idx_even_odd ? dsc_idx_odd : dsc_idx_even), | |
| + dsc_idx < ARRAY_SIZE(rm->dsc_blks) && | |
| + num_dsc < top->num_dsc;) { | |
| if (!rm->dsc_blks[dsc_idx]) | |
| continue; | |
| @@ -545,6 +555,11 @@ static int _dpu_rm_dsc_alloc(struct dpu_rm *rm, | |
| global_state->dsc_to_crtc_id[dsc_idx] = crtc_id; | |
| num_dsc++; | |
| pp_idx++; | |
| + | |
| + if (idx_even_odd) | |
| + dsc_idx_odd = dsc_idx + 2; | |
| + else | |
| + dsc_idx_even = dsc_idx + 2; | |
| } | |
| if (num_dsc < top->num_dsc) { | |
| @@ -615,7 +630,8 @@ static int _dpu_rm_dsc_alloc_pair(struct dpu_rm *rm, | |
| static int _dpu_rm_reserve_dsc(struct dpu_rm *rm, | |
| struct dpu_global_state *global_state, | |
| uint32_t crtc_id, | |
| - const struct msm_display_topology *top) | |
| + const struct msm_display_topology *top, | |
| + bool pp_even_odd[MAX_BLOCKS]) | |
| { | |
| if (!top->num_dsc || !top->num_intf) | |
| return 0; | |
| @@ -634,7 +650,7 @@ static int _dpu_rm_reserve_dsc(struct dpu_rm *rm, | |
| if (top->num_dsc > top->num_intf) /* merge mode */ | |
| return _dpu_rm_dsc_alloc_pair(rm, global_state, crtc_id, top); | |
| else | |
| - return _dpu_rm_dsc_alloc(rm, global_state, crtc_id, top); | |
| + return _dpu_rm_dsc_alloc(rm, global_state, crtc_id, top, pp_even_odd); | |
| return 0; | |
| } | |
| @@ -672,8 +688,9 @@ static int _dpu_rm_make_reservation( | |
| struct msm_display_topology *topology) | |
| { | |
| int ret; | |
| + bool pp_even_odd[MAX_BLOCKS]; | |
| - ret = _dpu_rm_reserve_lms(rm, global_state, crtc_id, topology); | |
| + ret = _dpu_rm_reserve_lms(rm, global_state, crtc_id, topology, pp_even_odd); | |
| if (ret) { | |
| DPU_ERROR("unable to find appropriate mixers\n"); | |
| return ret; | |
| @@ -693,9 +710,11 @@ static int _dpu_rm_make_reservation( | |
| return ret; | |
| } | |
| - ret = _dpu_rm_reserve_dsc(rm, global_state, crtc_id, topology); | |
| - if (ret) | |
| + ret = _dpu_rm_reserve_dsc(rm, global_state, crtc_id, topology, pp_even_odd); | |
| + if (ret) { | |
| + DPU_ERROR("unable to find appropriate DSC\n"); | |
| return ret; | |
| + } | |
| if (topology->num_cdm > 0) { | |
| ret = _dpu_rm_reserve_cdm(rm, global_state, crtc_id, topology->num_cdm); | |
| diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c | |
| index ef298c7d3e5e..cbb6d653fbec 100644 | |
| --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c | |
| +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c | |
| @@ -18,12 +18,13 @@ | |
| #include <drm/display/drm_dp_helper.h> | |
| #include <drm/drm_device.h> | |
| -#include <drm/drm_fixed.h> | |
| #include <drm/drm_print.h> | |
| #include "dp_reg.h" | |
| #include "dp_ctrl.h" | |
| +#include "dp_panel.h" | |
| #include "dp_link.h" | |
| +#include "dp_tu_calc.h" | |
| #define POLLING_SLEEP_US 1000 | |
| #define POLLING_TIMEOUT_US 10000 | |
| @@ -80,33 +81,9 @@ enum { | |
| DP_TRAINING_2, | |
| }; | |
| -struct msm_dp_tu_calc_input { | |
| - u64 lclk; /* 162, 270, 540 and 810 */ | |
| - u64 pclk_khz; /* in KHz */ | |
| - u64 hactive; /* active h-width */ | |
| - u64 hporch; /* bp + fp + pulse */ | |
| - int nlanes; /* no.of.lanes */ | |
| - int bpp; /* bits */ | |
| - int pixel_enc; /* 444, 420, 422 */ | |
| - int dsc_en; /* dsc on/off */ | |
| - int async_en; /* async mode */ | |
| - int fec_en; /* fec */ | |
| - int compress_ratio; /* 2:1 = 200, 3:1 = 300, 3.75:1 = 375 */ | |
| - int num_of_dsc_slices; /* number of slices per line */ | |
| -}; | |
| - | |
| -struct msm_dp_vc_tu_mapping_table { | |
| - u32 vic; | |
| - u8 lanes; | |
| - u8 lrate; /* DP_LINK_RATE -> 162(6), 270(10), 540(20), 810 (30) */ | |
| - u8 bpp; | |
| - u8 valid_boundary_link; | |
| - u16 delay_start_link; | |
| - bool boundary_moderation_en; | |
| - u8 valid_lower_boundary_link; | |
| - u8 upper_boundary_count; | |
| - u8 lower_boundary_count; | |
| - u8 tu_size_minus1; | |
| +enum msm_dp_flush_bit { | |
| + DP_PPS_FLUSH, | |
| + DP_DHDR_FLUSH, | |
| }; | |
| struct msm_dp_ctrl_private { | |
| @@ -140,6 +117,7 @@ struct msm_dp_ctrl_private { | |
| bool core_clks_on; | |
| bool link_clks_on; | |
| bool stream_clks_on; | |
| + struct msm_dp_dsc_cfg_data dsc_data; | |
| }; | |
| static inline u32 msm_dp_read_ahb(const struct msm_dp_ctrl_private *ctrl, u32 offset) | |
| @@ -307,6 +285,37 @@ static void msm_dp_ctrl_psr_mainlink_disable(struct msm_dp_ctrl_private *ctrl) | |
| msm_dp_write_link(ctrl, REG_DP_MAINLINK_CTRL, val); | |
| } | |
| +static void msm_dp_ctrl_mainlink_levels(struct msm_dp_ctrl_private *ctrl) | |
| +{ | |
| + u32 mainlink_levels, safe_to_exit_level = 14; | |
| + u8 lane_cnt = ctrl->link->link_params.num_lanes; | |
| + | |
| + switch (lane_cnt) { | |
| + case 1: | |
| + safe_to_exit_level = 14; | |
| + break; | |
| + case 2: | |
| + safe_to_exit_level = 8; | |
| + break; | |
| + case 4: | |
| + safe_to_exit_level = 5; | |
| + break; | |
| + default: | |
| + drm_dbg_dp(ctrl->drm_dev, "setting the default safe_to_exit_level=%u\n", | |
| + safe_to_exit_level); | |
| + break; | |
| + } | |
| + | |
| + mainlink_levels = msm_dp_read_link(ctrl, REG_DP_MAINLINK_LEVELS); | |
| + mainlink_levels &= 0xFE0; | |
| + mainlink_levels |= safe_to_exit_level; | |
| + | |
| + drm_dbg_dp(ctrl->drm_dev, "mainlink_level=0x%x, safe_to_exit_level=0x%x\n", | |
| + mainlink_levels, safe_to_exit_level); | |
| + | |
| + msm_dp_write_link(ctrl, REG_DP_MAINLINK_LEVELS, mainlink_levels); | |
| +} | |
| + | |
| static void msm_dp_ctrl_mainlink_enable(struct msm_dp_ctrl_private *ctrl) | |
| { | |
| u32 mainlink_ctrl; | |
| @@ -403,8 +412,15 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl) | |
| if (drm_dp_alternate_scrambler_reset_cap(dpcd)) | |
| config |= DP_CONFIGURATION_CTRL_ASSR; | |
| - tbd = msm_dp_link_get_test_bits_depth(ctrl->link, | |
| - ctrl->panel->msm_dp_mode.bpp); | |
| + /* | |
| + * since dsc encoder output byte stream to dp controller, | |
| + * 8 bits bpc should be used as long as dsc enabled | |
| + */ | |
| + if (DP_DSC_PANEL_EN(ctrl->panel)) | |
| + tbd = DP_TEST_BIT_DEPTH_8 >> DP_TEST_BIT_DEPTH_SHIFT; | |
| + else | |
| + tbd = msm_dp_link_get_test_bits_depth(ctrl->link, | |
| + ctrl->panel->msm_dp_mode.bpp); | |
| config |= tbd << DP_CONFIGURATION_CTRL_BPC_SHIFT; | |
| @@ -429,6 +445,48 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl) | |
| msm_dp_write_link(ctrl, REG_DP_CONFIGURATION_CTRL, config); | |
| } | |
| +static void msm_dp_ctrl_fec_config(struct msm_dp_ctrl_private *ctrl, bool enable) | |
| +{ | |
| + u32 reg; | |
| + | |
| + reg = msm_dp_read_link(ctrl, REG_DP_MAINLINK_CTRL); | |
| + | |
| + /* | |
| + * fec_en = BIT(12) | |
| + * fec_seq_mode = BIT(22) | |
| + * sde_flush = BIT(23) | BIT(24) | |
| + * fb_boundary_sel = BIT(25) | |
| + */ | |
| + if (enable) | |
| + reg |= BIT(12) | BIT(22) | BIT(23) | BIT(24) | BIT(25); | |
| + else | |
| + reg &= ~BIT(12); | |
| + | |
| + msm_dp_write_link(ctrl, REG_DP_MAINLINK_CTRL, reg); | |
| + /* make sure mainlink configuration is updated with fec sequence */ | |
| + wmb(); | |
| +} | |
| + | |
| +static void msm_dp_ctrl_config_dsc_dto(struct msm_dp_ctrl_private *ctrl) | |
| +{ | |
| + struct msm_dp_dsc_cfg_data *dsc_data = &ctrl->dsc_data; | |
| + u32 reg; | |
| + | |
| + msm_dp_panel_config_dsc_dto(ctrl->panel, dsc_data); | |
| + | |
| + reg = 0; | |
| + if (dsc_data->dsc_en) { | |
| + reg = BIT(0); | |
| + reg |= (dsc_data->eol_byte_num << 3); | |
| + reg |= (dsc_data->slice_per_pkt << 5); | |
| + reg |= (dsc_data->bytes_per_pkt << 16); | |
| + reg |= (dsc_data->be_in_lane << 10); | |
| + } | |
| + msm_dp_write_link(ctrl, DP_COMPRESSION_MODE_CTRL, reg); | |
| + | |
| + drm_dbg_dp(ctrl->drm_dev, "compression:0x%x\n", reg); | |
| +} | |
| + | |
| static void msm_dp_ctrl_lane_mapping(struct msm_dp_ctrl_private *ctrl) | |
| { | |
| u32 *lane_map = ctrl->link->lane_map; | |
| @@ -443,17 +501,24 @@ static void msm_dp_ctrl_lane_mapping(struct msm_dp_ctrl_private *ctrl) | |
| ln_mapping); | |
| } | |
| +static void msm_dp_ctrl_sdp_update(struct msm_dp_ctrl_private *ctrl) | |
| +{ | |
| + msm_dp_write_link(ctrl, MMSS_DP_SDP_CFG3, 0x01); | |
| + msm_dp_write_link(ctrl, MMSS_DP_SDP_CFG3, 0x00); | |
| +} | |
| + | |
| static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl) | |
| { | |
| u32 colorimetry_cfg, test_bits_depth, misc_val; | |
| msm_dp_ctrl_lane_mapping(ctrl); | |
| + msm_dp_ctrl_mainlink_levels(ctrl); | |
| msm_dp_setup_peripheral_flush(ctrl); | |
| msm_dp_ctrl_config_ctrl(ctrl); | |
| test_bits_depth = msm_dp_link_get_test_bits_depth(ctrl->link, ctrl->panel->msm_dp_mode.bpp); | |
| - colorimetry_cfg = msm_dp_link_get_colorimetry_config(ctrl->link); | |
| + colorimetry_cfg = msm_dp_panel_get_colorimetry_config(ctrl->panel); | |
| misc_val = msm_dp_read_link(ctrl, REG_DP_MISC1_MISC0); | |
| @@ -470,778 +535,16 @@ static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl | |
| msm_dp_panel_timing_cfg(ctrl->panel, ctrl->msm_dp_ctrl.wide_bus_en); | |
| } | |
| -/* | |
| - * The structure and few functions present below are IP/Hardware | |
| - * specific implementation. Most of the implementation will not | |
| - * have coding comments | |
| - */ | |
| -struct tu_algo_data { | |
| - s64 lclk_fp; | |
| - s64 pclk_fp; | |
| - s64 lwidth; | |
| - s64 lwidth_fp; | |
| - s64 hbp_relative_to_pclk; | |
| - s64 hbp_relative_to_pclk_fp; | |
| - int nlanes; | |
| - int bpp; | |
| - int pixelEnc; | |
| - int dsc_en; | |
| - int async_en; | |
| - int bpc; | |
| - | |
| - uint delay_start_link_extra_pixclk; | |
| - int extra_buffer_margin; | |
| - s64 ratio_fp; | |
| - s64 original_ratio_fp; | |
| - | |
| - s64 err_fp; | |
| - s64 n_err_fp; | |
| - s64 n_n_err_fp; | |
| - int tu_size; | |
| - int tu_size_desired; | |
| - int tu_size_minus1; | |
| - | |
| - int valid_boundary_link; | |
| - s64 resulting_valid_fp; | |
| - s64 total_valid_fp; | |
| - s64 effective_valid_fp; | |
| - s64 effective_valid_recorded_fp; | |
| - int n_tus; | |
| - int n_tus_per_lane; | |
| - int paired_tus; | |
| - int remainder_tus; | |
| - int remainder_tus_upper; | |
| - int remainder_tus_lower; | |
| - int extra_bytes; | |
| - int filler_size; | |
| - int delay_start_link; | |
| - | |
| - int extra_pclk_cycles; | |
| - int extra_pclk_cycles_in_link_clk; | |
| - s64 ratio_by_tu_fp; | |
| - s64 average_valid2_fp; | |
| - int new_valid_boundary_link; | |
| - int remainder_symbols_exist; | |
| - int n_symbols; | |
| - s64 n_remainder_symbols_per_lane_fp; | |
| - s64 last_partial_tu_fp; | |
| - s64 TU_ratio_err_fp; | |
| - | |
| - int n_tus_incl_last_incomplete_tu; | |
| - int extra_pclk_cycles_tmp; | |
| - int extra_pclk_cycles_in_link_clk_tmp; | |
| - int extra_required_bytes_new_tmp; | |
| - int filler_size_tmp; | |
| - int lower_filler_size_tmp; | |
| - int delay_start_link_tmp; | |
| - | |
| - bool boundary_moderation_en; | |
| - int boundary_mod_lower_err; | |
| - int upper_boundary_count; | |
| - int lower_boundary_count; | |
| - int i_upper_boundary_count; | |
| - int i_lower_boundary_count; | |
| - int valid_lower_boundary_link; | |
| - int even_distribution_BF; | |
| - int even_distribution_legacy; | |
| - int even_distribution; | |
| - int min_hblank_violated; | |
| - s64 delay_start_time_fp; | |
| - s64 hbp_time_fp; | |
| - s64 hactive_time_fp; | |
| - s64 diff_abs_fp; | |
| - | |
| - s64 ratio; | |
| -}; | |
| - | |
| -static int _tu_param_compare(s64 a, s64 b) | |
| -{ | |
| - u32 a_sign; | |
| - u32 b_sign; | |
| - s64 a_temp, b_temp, minus_1; | |
| - | |
| - if (a == b) | |
| - return 0; | |
| - | |
| - minus_1 = drm_fixp_from_fraction(-1, 1); | |
| - | |
| - a_sign = (a >> 32) & 0x80000000 ? 1 : 0; | |
| - | |
| - b_sign = (b >> 32) & 0x80000000 ? 1 : 0; | |
| - | |
| - if (a_sign > b_sign) | |
| - return 2; | |
| - else if (b_sign > a_sign) | |
| - return 1; | |
| - | |
| - if (!a_sign && !b_sign) { /* positive */ | |
| - if (a > b) | |
| - return 1; | |
| - else | |
| - return 2; | |
| - } else { /* negative */ | |
| - a_temp = drm_fixp_mul(a, minus_1); | |
| - b_temp = drm_fixp_mul(b, minus_1); | |
| - | |
| - if (a_temp > b_temp) | |
| - return 2; | |
| - else | |
| - return 1; | |
| - } | |
| -} | |
| - | |
| -static void msm_dp_panel_update_tu_timings(struct msm_dp_tu_calc_input *in, | |
| - struct tu_algo_data *tu) | |
| -{ | |
| - int nlanes = in->nlanes; | |
| - int dsc_num_slices = in->num_of_dsc_slices; | |
| - int dsc_num_bytes = 0; | |
| - int numerator; | |
| - s64 pclk_dsc_fp; | |
| - s64 dwidth_dsc_fp; | |
| - s64 hbp_dsc_fp; | |
| - | |
| - int tot_num_eoc_symbols = 0; | |
| - int tot_num_hor_bytes = 0; | |
| - int tot_num_dummy_bytes = 0; | |
| - int dwidth_dsc_bytes = 0; | |
| - int eoc_bytes = 0; | |
| - | |
| - s64 temp1_fp, temp2_fp, temp3_fp; | |
| - | |
| - tu->lclk_fp = drm_fixp_from_fraction(in->lclk, 1); | |
| - tu->pclk_fp = drm_fixp_from_fraction(in->pclk_khz, 1000); | |
| - tu->lwidth = in->hactive; | |
| - tu->hbp_relative_to_pclk = in->hporch; | |
| - tu->nlanes = in->nlanes; | |
| - tu->bpp = in->bpp; | |
| - tu->pixelEnc = in->pixel_enc; | |
| - tu->dsc_en = in->dsc_en; | |
| - tu->async_en = in->async_en; | |
| - tu->lwidth_fp = drm_fixp_from_fraction(in->hactive, 1); | |
| - tu->hbp_relative_to_pclk_fp = drm_fixp_from_fraction(in->hporch, 1); | |
| - | |
| - if (tu->pixelEnc == 420) { | |
| - temp1_fp = drm_fixp_from_fraction(2, 1); | |
| - tu->pclk_fp = drm_fixp_div(tu->pclk_fp, temp1_fp); | |
| - tu->lwidth_fp = drm_fixp_div(tu->lwidth_fp, temp1_fp); | |
| - tu->hbp_relative_to_pclk_fp = | |
| - drm_fixp_div(tu->hbp_relative_to_pclk_fp, 2); | |
| - } | |
| - | |
| - if (tu->pixelEnc == 422) { | |
| - switch (tu->bpp) { | |
| - case 24: | |
| - tu->bpp = 16; | |
| - tu->bpc = 8; | |
| - break; | |
| - case 30: | |
| - tu->bpp = 20; | |
| - tu->bpc = 10; | |
| - break; | |
| - default: | |
| - tu->bpp = 16; | |
| - tu->bpc = 8; | |
| - break; | |
| - } | |
| - } else { | |
| - tu->bpc = tu->bpp/3; | |
| - } | |
| - | |
| - if (!in->dsc_en) | |
| - goto fec_check; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(in->compress_ratio, 100); | |
| - temp2_fp = drm_fixp_from_fraction(in->bpp, 1); | |
| - temp3_fp = drm_fixp_div(temp2_fp, temp1_fp); | |
| - temp2_fp = drm_fixp_mul(tu->lwidth_fp, temp3_fp); | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(8, 1); | |
| - temp3_fp = drm_fixp_div(temp2_fp, temp1_fp); | |
| - | |
| - numerator = drm_fixp2int(temp3_fp); | |
| - | |
| - dsc_num_bytes = numerator / dsc_num_slices; | |
| - eoc_bytes = dsc_num_bytes % nlanes; | |
| - tot_num_eoc_symbols = nlanes * dsc_num_slices; | |
| - tot_num_hor_bytes = dsc_num_bytes * dsc_num_slices; | |
| - tot_num_dummy_bytes = (nlanes - eoc_bytes) * dsc_num_slices; | |
| - | |
| - if (dsc_num_bytes == 0) | |
| - pr_info("incorrect no of bytes per slice=%d\n", dsc_num_bytes); | |
| - | |
| - dwidth_dsc_bytes = (tot_num_hor_bytes + | |
| - tot_num_eoc_symbols + | |
| - (eoc_bytes == 0 ? 0 : tot_num_dummy_bytes)); | |
| - | |
| - dwidth_dsc_fp = drm_fixp_from_fraction(dwidth_dsc_bytes, 3); | |
| - | |
| - temp2_fp = drm_fixp_mul(tu->pclk_fp, dwidth_dsc_fp); | |
| - temp1_fp = drm_fixp_div(temp2_fp, tu->lwidth_fp); | |
| - pclk_dsc_fp = temp1_fp; | |
| - | |
| - temp1_fp = drm_fixp_div(pclk_dsc_fp, tu->pclk_fp); | |
| - temp2_fp = drm_fixp_mul(tu->hbp_relative_to_pclk_fp, temp1_fp); | |
| - hbp_dsc_fp = temp2_fp; | |
| - | |
| - /* output */ | |
| - tu->pclk_fp = pclk_dsc_fp; | |
| - tu->lwidth_fp = dwidth_dsc_fp; | |
| - tu->hbp_relative_to_pclk_fp = hbp_dsc_fp; | |
| - | |
| -fec_check: | |
| - if (in->fec_en) { | |
| - temp1_fp = drm_fixp_from_fraction(976, 1000); /* 0.976 */ | |
| - tu->lclk_fp = drm_fixp_mul(tu->lclk_fp, temp1_fp); | |
| - } | |
| -} | |
| - | |
| -static void _tu_valid_boundary_calc(struct tu_algo_data *tu) | |
| -{ | |
| - s64 temp1_fp, temp2_fp, temp, temp1, temp2; | |
| - int compare_result_1, compare_result_2, compare_result_3; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1); | |
| - temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp); | |
| - | |
| - tu->new_valid_boundary_link = drm_fixp2int_ceil(temp2_fp); | |
| - | |
| - temp = (tu->i_upper_boundary_count * | |
| - tu->new_valid_boundary_link + | |
| - tu->i_lower_boundary_count * | |
| - (tu->new_valid_boundary_link-1)); | |
| - tu->average_valid2_fp = drm_fixp_from_fraction(temp, | |
| - (tu->i_upper_boundary_count + | |
| - tu->i_lower_boundary_count)); | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->bpp, 8); | |
| - temp2_fp = tu->lwidth_fp; | |
| - temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp); | |
| - temp2_fp = drm_fixp_div(temp1_fp, tu->average_valid2_fp); | |
| - tu->n_tus = drm_fixp2int(temp2_fp); | |
| - if ((temp2_fp & 0xFFFFFFFF) > 0xFFFFF000) | |
| - tu->n_tus += 1; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->n_tus, 1); | |
| - temp2_fp = drm_fixp_mul(temp1_fp, tu->average_valid2_fp); | |
| - temp1_fp = drm_fixp_from_fraction(tu->n_symbols, 1); | |
| - temp2_fp = temp1_fp - temp2_fp; | |
| - temp1_fp = drm_fixp_from_fraction(tu->nlanes, 1); | |
| - temp2_fp = drm_fixp_div(temp2_fp, temp1_fp); | |
| - tu->n_remainder_symbols_per_lane_fp = temp2_fp; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1); | |
| - tu->last_partial_tu_fp = | |
| - drm_fixp_div(tu->n_remainder_symbols_per_lane_fp, | |
| - temp1_fp); | |
| - | |
| - if (tu->n_remainder_symbols_per_lane_fp != 0) | |
| - tu->remainder_symbols_exist = 1; | |
| - else | |
| - tu->remainder_symbols_exist = 0; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->n_tus, tu->nlanes); | |
| - tu->n_tus_per_lane = drm_fixp2int(temp1_fp); | |
| - | |
| - tu->paired_tus = (int)((tu->n_tus_per_lane) / | |
| - (tu->i_upper_boundary_count + | |
| - tu->i_lower_boundary_count)); | |
| - | |
| - tu->remainder_tus = tu->n_tus_per_lane - tu->paired_tus * | |
| - (tu->i_upper_boundary_count + | |
| - tu->i_lower_boundary_count); | |
| - | |
| - if ((tu->remainder_tus - tu->i_upper_boundary_count) > 0) { | |
| - tu->remainder_tus_upper = tu->i_upper_boundary_count; | |
| - tu->remainder_tus_lower = tu->remainder_tus - | |
| - tu->i_upper_boundary_count; | |
| - } else { | |
| - tu->remainder_tus_upper = tu->remainder_tus; | |
| - tu->remainder_tus_lower = 0; | |
| - } | |
| - | |
| - temp = tu->paired_tus * (tu->i_upper_boundary_count * | |
| - tu->new_valid_boundary_link + | |
| - tu->i_lower_boundary_count * | |
| - (tu->new_valid_boundary_link - 1)) + | |
| - (tu->remainder_tus_upper * | |
| - tu->new_valid_boundary_link) + | |
| - (tu->remainder_tus_lower * | |
| - (tu->new_valid_boundary_link - 1)); | |
| - tu->total_valid_fp = drm_fixp_from_fraction(temp, 1); | |
| - | |
| - if (tu->remainder_symbols_exist) { | |
| - temp1_fp = tu->total_valid_fp + | |
| - tu->n_remainder_symbols_per_lane_fp; | |
| - temp2_fp = drm_fixp_from_fraction(tu->n_tus_per_lane, 1); | |
| - temp2_fp = temp2_fp + tu->last_partial_tu_fp; | |
| - temp1_fp = drm_fixp_div(temp1_fp, temp2_fp); | |
| - } else { | |
| - temp2_fp = drm_fixp_from_fraction(tu->n_tus_per_lane, 1); | |
| - temp1_fp = drm_fixp_div(tu->total_valid_fp, temp2_fp); | |
| - } | |
| - tu->effective_valid_fp = temp1_fp; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1); | |
| - temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp); | |
| - tu->n_n_err_fp = tu->effective_valid_fp - temp2_fp; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1); | |
| - temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp); | |
| - tu->n_err_fp = tu->average_valid2_fp - temp2_fp; | |
| - | |
| - tu->even_distribution = tu->n_tus % tu->nlanes == 0 ? 1 : 0; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->bpp, 8); | |
| - temp2_fp = tu->lwidth_fp; | |
| - temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp); | |
| - temp2_fp = drm_fixp_div(temp1_fp, tu->average_valid2_fp); | |
| - | |
| - if (temp2_fp) | |
| - tu->n_tus_incl_last_incomplete_tu = drm_fixp2int_ceil(temp2_fp); | |
| - else | |
| - tu->n_tus_incl_last_incomplete_tu = 0; | |
| - | |
| - temp1 = 0; | |
| - temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1); | |
| - temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp); | |
| - temp1_fp = tu->average_valid2_fp - temp2_fp; | |
| - temp2_fp = drm_fixp_from_fraction(tu->n_tus_incl_last_incomplete_tu, 1); | |
| - temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp); | |
| - | |
| - if (temp1_fp) | |
| - temp1 = drm_fixp2int_ceil(temp1_fp); | |
| - | |
| - temp = tu->i_upper_boundary_count * tu->nlanes; | |
| - temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1); | |
| - temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp); | |
| - temp1_fp = drm_fixp_from_fraction(tu->new_valid_boundary_link, 1); | |
| - temp2_fp = temp1_fp - temp2_fp; | |
| - temp1_fp = drm_fixp_from_fraction(temp, 1); | |
| - temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp); | |
| - | |
| - if (temp2_fp) | |
| - temp2 = drm_fixp2int_ceil(temp2_fp); | |
| - else | |
| - temp2 = 0; | |
| - tu->extra_required_bytes_new_tmp = (int)(temp1 + temp2); | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(8, tu->bpp); | |
| - temp2_fp = drm_fixp_from_fraction( | |
| - tu->extra_required_bytes_new_tmp, 1); | |
| - temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp); | |
| - | |
| - if (temp1_fp) | |
| - tu->extra_pclk_cycles_tmp = drm_fixp2int_ceil(temp1_fp); | |
| - else | |
| - tu->extra_pclk_cycles_tmp = 0; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->extra_pclk_cycles_tmp, 1); | |
| - temp2_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp); | |
| - temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp); | |
| - | |
| - if (temp1_fp) | |
| - tu->extra_pclk_cycles_in_link_clk_tmp = | |
| - drm_fixp2int_ceil(temp1_fp); | |
| - else | |
| - tu->extra_pclk_cycles_in_link_clk_tmp = 0; | |
| - | |
| - tu->filler_size_tmp = tu->tu_size - tu->new_valid_boundary_link; | |
| - | |
| - tu->lower_filler_size_tmp = tu->filler_size_tmp + 1; | |
| - | |
| - tu->delay_start_link_tmp = tu->extra_pclk_cycles_in_link_clk_tmp + | |
| - tu->lower_filler_size_tmp + | |
| - tu->extra_buffer_margin; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->delay_start_link_tmp, 1); | |
| - tu->delay_start_time_fp = drm_fixp_div(temp1_fp, tu->lclk_fp); | |
| - | |
| - compare_result_1 = _tu_param_compare(tu->n_n_err_fp, tu->diff_abs_fp); | |
| - if (compare_result_1 == 2) | |
| - compare_result_1 = 1; | |
| - else | |
| - compare_result_1 = 0; | |
| - | |
| - compare_result_2 = _tu_param_compare(tu->n_n_err_fp, tu->err_fp); | |
| - if (compare_result_2 == 2) | |
| - compare_result_2 = 1; | |
| - else | |
| - compare_result_2 = 0; | |
| - | |
| - compare_result_3 = _tu_param_compare(tu->hbp_time_fp, | |
| - tu->delay_start_time_fp); | |
| - if (compare_result_3 == 2) | |
| - compare_result_3 = 0; | |
| - else | |
| - compare_result_3 = 1; | |
| - | |
| - if (((tu->even_distribution == 1) || | |
| - ((tu->even_distribution_BF == 0) && | |
| - (tu->even_distribution_legacy == 0))) && | |
| - tu->n_err_fp >= 0 && tu->n_n_err_fp >= 0 && | |
| - compare_result_2 && | |
| - (compare_result_1 || (tu->min_hblank_violated == 1)) && | |
| - (tu->new_valid_boundary_link - 1) > 0 && | |
| - compare_result_3 && | |
| - (tu->delay_start_link_tmp <= 1023)) { | |
| - tu->upper_boundary_count = tu->i_upper_boundary_count; | |
| - tu->lower_boundary_count = tu->i_lower_boundary_count; | |
| - tu->err_fp = tu->n_n_err_fp; | |
| - tu->boundary_moderation_en = true; | |
| - tu->tu_size_desired = tu->tu_size; | |
| - tu->valid_boundary_link = tu->new_valid_boundary_link; | |
| - tu->effective_valid_recorded_fp = tu->effective_valid_fp; | |
| - tu->even_distribution_BF = 1; | |
| - tu->delay_start_link = tu->delay_start_link_tmp; | |
| - } else if (tu->boundary_mod_lower_err == 0) { | |
| - compare_result_1 = _tu_param_compare(tu->n_n_err_fp, | |
| - tu->diff_abs_fp); | |
| - if (compare_result_1 == 2) | |
| - tu->boundary_mod_lower_err = 1; | |
| - } | |
| -} | |
| - | |
| -static void _dp_ctrl_calc_tu(struct msm_dp_ctrl_private *ctrl, | |
| - struct msm_dp_tu_calc_input *in, | |
| - struct msm_dp_vc_tu_mapping_table *tu_table) | |
| -{ | |
| - struct tu_algo_data *tu; | |
| - int compare_result_1, compare_result_2; | |
| - u64 temp = 0; | |
| - s64 temp_fp = 0, temp1_fp = 0, temp2_fp = 0; | |
| - | |
| - s64 LCLK_FAST_SKEW_fp = drm_fixp_from_fraction(6, 10000); /* 0.0006 */ | |
| - s64 const_p49_fp = drm_fixp_from_fraction(49, 100); /* 0.49 */ | |
| - s64 const_p56_fp = drm_fixp_from_fraction(56, 100); /* 0.56 */ | |
| - s64 RATIO_SCALE_fp = drm_fixp_from_fraction(1001, 1000); | |
| - | |
| - u8 DP_BRUTE_FORCE = 1; | |
| - s64 BRUTE_FORCE_THRESHOLD_fp = drm_fixp_from_fraction(1, 10); /* 0.1 */ | |
| - uint EXTRA_PIXCLK_CYCLE_DELAY = 4; | |
| - uint HBLANK_MARGIN = 4; | |
| - | |
| - tu = kzalloc_obj(*tu); | |
| - if (!tu) | |
| - return; | |
| - | |
| - msm_dp_panel_update_tu_timings(in, tu); | |
| - | |
| - tu->err_fp = drm_fixp_from_fraction(1000, 1); /* 1000 */ | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(4, 1); | |
| - temp2_fp = drm_fixp_mul(temp1_fp, tu->lclk_fp); | |
| - temp_fp = drm_fixp_div(temp2_fp, tu->pclk_fp); | |
| - tu->extra_buffer_margin = drm_fixp2int_ceil(temp_fp); | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->bpp, 8); | |
| - temp2_fp = drm_fixp_mul(tu->pclk_fp, temp1_fp); | |
| - temp1_fp = drm_fixp_from_fraction(tu->nlanes, 1); | |
| - temp2_fp = drm_fixp_div(temp2_fp, temp1_fp); | |
| - tu->ratio_fp = drm_fixp_div(temp2_fp, tu->lclk_fp); | |
| - | |
| - tu->original_ratio_fp = tu->ratio_fp; | |
| - tu->boundary_moderation_en = false; | |
| - tu->upper_boundary_count = 0; | |
| - tu->lower_boundary_count = 0; | |
| - tu->i_upper_boundary_count = 0; | |
| - tu->i_lower_boundary_count = 0; | |
| - tu->valid_lower_boundary_link = 0; | |
| - tu->even_distribution_BF = 0; | |
| - tu->even_distribution_legacy = 0; | |
| - tu->even_distribution = 0; | |
| - tu->delay_start_time_fp = 0; | |
| - | |
| - tu->err_fp = drm_fixp_from_fraction(1000, 1); | |
| - tu->n_err_fp = 0; | |
| - tu->n_n_err_fp = 0; | |
| - | |
| - tu->ratio = drm_fixp2int(tu->ratio_fp); | |
| - temp1_fp = drm_fixp_from_fraction(tu->nlanes, 1); | |
| - div64_u64_rem(tu->lwidth_fp, temp1_fp, &temp2_fp); | |
| - if (temp2_fp != 0 && | |
| - !tu->ratio && tu->dsc_en == 0) { | |
| - tu->ratio_fp = drm_fixp_mul(tu->ratio_fp, RATIO_SCALE_fp); | |
| - tu->ratio = drm_fixp2int(tu->ratio_fp); | |
| - if (tu->ratio) | |
| - tu->ratio_fp = drm_fixp_from_fraction(1, 1); | |
| - } | |
| - | |
| - if (tu->ratio > 1) | |
| - tu->ratio = 1; | |
| - | |
| - if (tu->ratio == 1) | |
| - goto tu_size_calc; | |
| - | |
| - compare_result_1 = _tu_param_compare(tu->ratio_fp, const_p49_fp); | |
| - if (!compare_result_1 || compare_result_1 == 1) | |
| - compare_result_1 = 1; | |
| - else | |
| - compare_result_1 = 0; | |
| - | |
| - compare_result_2 = _tu_param_compare(tu->ratio_fp, const_p56_fp); | |
| - if (!compare_result_2 || compare_result_2 == 2) | |
| - compare_result_2 = 1; | |
| - else | |
| - compare_result_2 = 0; | |
| - | |
| - if (tu->dsc_en && compare_result_1 && compare_result_2) { | |
| - HBLANK_MARGIN += 4; | |
| - drm_dbg_dp(ctrl->drm_dev, | |
| - "increase HBLANK_MARGIN to %d\n", HBLANK_MARGIN); | |
| - } | |
| - | |
| -tu_size_calc: | |
| - for (tu->tu_size = 32; tu->tu_size <= 64; tu->tu_size++) { | |
| - temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1); | |
| - temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp); | |
| - temp = drm_fixp2int_ceil(temp2_fp); | |
| - temp1_fp = drm_fixp_from_fraction(temp, 1); | |
| - tu->n_err_fp = temp1_fp - temp2_fp; | |
| - | |
| - if (tu->n_err_fp < tu->err_fp) { | |
| - tu->err_fp = tu->n_err_fp; | |
| - tu->tu_size_desired = tu->tu_size; | |
| - } | |
| - } | |
| - | |
| - tu->tu_size_minus1 = tu->tu_size_desired - 1; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->tu_size_desired, 1); | |
| - temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp); | |
| - tu->valid_boundary_link = drm_fixp2int_ceil(temp2_fp); | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->bpp, 8); | |
| - temp2_fp = tu->lwidth_fp; | |
| - temp2_fp = drm_fixp_mul(temp2_fp, temp1_fp); | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->valid_boundary_link, 1); | |
| - temp2_fp = drm_fixp_div(temp2_fp, temp1_fp); | |
| - tu->n_tus = drm_fixp2int(temp2_fp); | |
| - if ((temp2_fp & 0xFFFFFFFF) > 0xFFFFF000) | |
| - tu->n_tus += 1; | |
| - | |
| - tu->even_distribution_legacy = tu->n_tus % tu->nlanes == 0 ? 1 : 0; | |
| - | |
| - drm_dbg_dp(ctrl->drm_dev, | |
| - "n_sym = %d, num_of_tus = %d\n", | |
| - tu->valid_boundary_link, tu->n_tus); | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->tu_size_desired, 1); | |
| - temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp); | |
| - temp1_fp = drm_fixp_from_fraction(tu->valid_boundary_link, 1); | |
| - temp2_fp = temp1_fp - temp2_fp; | |
| - temp1_fp = drm_fixp_from_fraction(tu->n_tus + 1, 1); | |
| - temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp); | |
| - | |
| - temp = drm_fixp2int(temp2_fp); | |
| - if (temp && temp2_fp) | |
| - tu->extra_bytes = drm_fixp2int_ceil(temp2_fp); | |
| - else | |
| - tu->extra_bytes = 0; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->extra_bytes, 1); | |
| - temp2_fp = drm_fixp_from_fraction(8, tu->bpp); | |
| - temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp); | |
| - | |
| - if (temp && temp1_fp) | |
| - tu->extra_pclk_cycles = drm_fixp2int_ceil(temp1_fp); | |
| - else | |
| - tu->extra_pclk_cycles = drm_fixp2int(temp1_fp); | |
| - | |
| - temp1_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp); | |
| - temp2_fp = drm_fixp_from_fraction(tu->extra_pclk_cycles, 1); | |
| - temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp); | |
| - | |
| - if (temp1_fp) | |
| - tu->extra_pclk_cycles_in_link_clk = drm_fixp2int_ceil(temp1_fp); | |
| - else | |
| - tu->extra_pclk_cycles_in_link_clk = drm_fixp2int(temp1_fp); | |
| - | |
| - tu->filler_size = tu->tu_size_desired - tu->valid_boundary_link; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->tu_size_desired, 1); | |
| - tu->ratio_by_tu_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp); | |
| - | |
| - tu->delay_start_link = tu->extra_pclk_cycles_in_link_clk + | |
| - tu->filler_size + tu->extra_buffer_margin; | |
| - | |
| - tu->resulting_valid_fp = | |
| - drm_fixp_from_fraction(tu->valid_boundary_link, 1); | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->tu_size_desired, 1); | |
| - temp2_fp = drm_fixp_div(tu->resulting_valid_fp, temp1_fp); | |
| - tu->TU_ratio_err_fp = temp2_fp - tu->original_ratio_fp; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(HBLANK_MARGIN, 1); | |
| - temp1_fp = tu->hbp_relative_to_pclk_fp - temp1_fp; | |
| - tu->hbp_time_fp = drm_fixp_div(temp1_fp, tu->pclk_fp); | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->delay_start_link, 1); | |
| - tu->delay_start_time_fp = drm_fixp_div(temp1_fp, tu->lclk_fp); | |
| - | |
| - compare_result_1 = _tu_param_compare(tu->hbp_time_fp, | |
| - tu->delay_start_time_fp); | |
| - if (compare_result_1 == 2) /* if (hbp_time_fp < delay_start_time_fp) */ | |
| - tu->min_hblank_violated = 1; | |
| - | |
| - tu->hactive_time_fp = drm_fixp_div(tu->lwidth_fp, tu->pclk_fp); | |
| - | |
| - compare_result_2 = _tu_param_compare(tu->hactive_time_fp, | |
| - tu->delay_start_time_fp); | |
| - if (compare_result_2 == 2) | |
| - tu->min_hblank_violated = 1; | |
| - | |
| - tu->delay_start_time_fp = 0; | |
| - | |
| - /* brute force */ | |
| - | |
| - tu->delay_start_link_extra_pixclk = EXTRA_PIXCLK_CYCLE_DELAY; | |
| - tu->diff_abs_fp = tu->resulting_valid_fp - tu->ratio_by_tu_fp; | |
| - | |
| - temp = drm_fixp2int(tu->diff_abs_fp); | |
| - if (!temp && tu->diff_abs_fp <= 0xffff) | |
| - tu->diff_abs_fp = 0; | |
| - | |
| - /* if(diff_abs < 0) diff_abs *= -1 */ | |
| - if (tu->diff_abs_fp < 0) | |
| - tu->diff_abs_fp = drm_fixp_mul(tu->diff_abs_fp, -1); | |
| - | |
| - tu->boundary_mod_lower_err = 0; | |
| - if ((tu->diff_abs_fp != 0 && | |
| - ((tu->diff_abs_fp > BRUTE_FORCE_THRESHOLD_fp) || | |
| - (tu->even_distribution_legacy == 0) || | |
| - (DP_BRUTE_FORCE == 1))) || | |
| - (tu->min_hblank_violated == 1)) { | |
| - do { | |
| - tu->err_fp = drm_fixp_from_fraction(1000, 1); | |
| - | |
| - temp1_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp); | |
| - temp2_fp = drm_fixp_from_fraction( | |
| - tu->delay_start_link_extra_pixclk, 1); | |
| - temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp); | |
| - | |
| - if (temp1_fp) | |
| - tu->extra_buffer_margin = | |
| - drm_fixp2int_ceil(temp1_fp); | |
| - else | |
| - tu->extra_buffer_margin = 0; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->bpp, 8); | |
| - temp1_fp = drm_fixp_mul(tu->lwidth_fp, temp1_fp); | |
| - | |
| - if (temp1_fp) | |
| - tu->n_symbols = drm_fixp2int_ceil(temp1_fp); | |
| - else | |
| - tu->n_symbols = 0; | |
| - | |
| - for (tu->tu_size = 32; tu->tu_size <= 64; tu->tu_size++) { | |
| - for (tu->i_upper_boundary_count = 1; | |
| - tu->i_upper_boundary_count <= 15; | |
| - tu->i_upper_boundary_count++) { | |
| - for (tu->i_lower_boundary_count = 1; | |
| - tu->i_lower_boundary_count <= 15; | |
| - tu->i_lower_boundary_count++) { | |
| - _tu_valid_boundary_calc(tu); | |
| - } | |
| - } | |
| - } | |
| - tu->delay_start_link_extra_pixclk--; | |
| - } while (tu->boundary_moderation_en != true && | |
| - tu->boundary_mod_lower_err == 1 && | |
| - tu->delay_start_link_extra_pixclk != 0); | |
| - | |
| - if (tu->boundary_moderation_en == true) { | |
| - temp1_fp = drm_fixp_from_fraction( | |
| - (tu->upper_boundary_count * | |
| - tu->valid_boundary_link + | |
| - tu->lower_boundary_count * | |
| - (tu->valid_boundary_link - 1)), 1); | |
| - temp2_fp = drm_fixp_from_fraction( | |
| - (tu->upper_boundary_count + | |
| - tu->lower_boundary_count), 1); | |
| - tu->resulting_valid_fp = | |
| - drm_fixp_div(temp1_fp, temp2_fp); | |
| - | |
| - temp1_fp = drm_fixp_from_fraction( | |
| - tu->tu_size_desired, 1); | |
| - tu->ratio_by_tu_fp = | |
| - drm_fixp_mul(tu->original_ratio_fp, temp1_fp); | |
| - | |
| - tu->valid_lower_boundary_link = | |
| - tu->valid_boundary_link - 1; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->bpp, 8); | |
| - temp1_fp = drm_fixp_mul(tu->lwidth_fp, temp1_fp); | |
| - temp2_fp = drm_fixp_div(temp1_fp, | |
| - tu->resulting_valid_fp); | |
| - tu->n_tus = drm_fixp2int(temp2_fp); | |
| - | |
| - tu->tu_size_minus1 = tu->tu_size_desired - 1; | |
| - tu->even_distribution_BF = 1; | |
| - | |
| - temp1_fp = | |
| - drm_fixp_from_fraction(tu->tu_size_desired, 1); | |
| - temp2_fp = | |
| - drm_fixp_div(tu->resulting_valid_fp, temp1_fp); | |
| - tu->TU_ratio_err_fp = temp2_fp - tu->original_ratio_fp; | |
| - } | |
| - } | |
| - | |
| - temp2_fp = drm_fixp_mul(LCLK_FAST_SKEW_fp, tu->lwidth_fp); | |
| - | |
| - if (temp2_fp) | |
| - temp = drm_fixp2int_ceil(temp2_fp); | |
| - else | |
| - temp = 0; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->nlanes, 1); | |
| - temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp); | |
| - temp1_fp = drm_fixp_from_fraction(tu->bpp, 8); | |
| - temp2_fp = drm_fixp_div(temp1_fp, temp2_fp); | |
| - temp1_fp = drm_fixp_from_fraction(temp, 1); | |
| - temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp); | |
| - temp = drm_fixp2int(temp2_fp); | |
| - | |
| - if (tu->async_en) | |
| - tu->delay_start_link += (int)temp; | |
| - | |
| - temp1_fp = drm_fixp_from_fraction(tu->delay_start_link, 1); | |
| - tu->delay_start_time_fp = drm_fixp_div(temp1_fp, tu->lclk_fp); | |
| - | |
| - /* OUTPUTS */ | |
| - tu_table->valid_boundary_link = tu->valid_boundary_link; | |
| - tu_table->delay_start_link = tu->delay_start_link; | |
| - tu_table->boundary_moderation_en = tu->boundary_moderation_en; | |
| - tu_table->valid_lower_boundary_link = tu->valid_lower_boundary_link; | |
| - tu_table->upper_boundary_count = tu->upper_boundary_count; | |
| - tu_table->lower_boundary_count = tu->lower_boundary_count; | |
| - tu_table->tu_size_minus1 = tu->tu_size_minus1; | |
| - | |
| - drm_dbg_dp(ctrl->drm_dev, "TU: valid_boundary_link: %d\n", | |
| - tu_table->valid_boundary_link); | |
| - drm_dbg_dp(ctrl->drm_dev, "TU: delay_start_link: %d\n", | |
| - tu_table->delay_start_link); | |
| - drm_dbg_dp(ctrl->drm_dev, "TU: boundary_moderation_en: %d\n", | |
| - tu_table->boundary_moderation_en); | |
| - drm_dbg_dp(ctrl->drm_dev, "TU: valid_lower_boundary_link: %d\n", | |
| - tu_table->valid_lower_boundary_link); | |
| - drm_dbg_dp(ctrl->drm_dev, "TU: upper_boundary_count: %d\n", | |
| - tu_table->upper_boundary_count); | |
| - drm_dbg_dp(ctrl->drm_dev, "TU: lower_boundary_count: %d\n", | |
| - tu_table->lower_boundary_count); | |
| - drm_dbg_dp(ctrl->drm_dev, "TU: tu_size_minus1: %d\n", | |
| - tu_table->tu_size_minus1); | |
| - | |
| - kfree(tu); | |
| -} | |
| - | |
| static void msm_dp_ctrl_calc_tu_parameters(struct msm_dp_ctrl_private *ctrl, | |
| struct msm_dp_vc_tu_mapping_table *tu_table) | |
| { | |
| struct msm_dp_tu_calc_input in; | |
| struct drm_display_mode *drm_mode; | |
| + struct msm_dp_panel_info *timing; | |
| + struct msm_compression_info *comp_info; | |
| + timing = &ctrl->panel->msm_dp_mode.timing; | |
| + comp_info = &timing->comp_info; | |
| drm_mode = &ctrl->panel->msm_dp_mode.drm_mode; | |
| in.lclk = ctrl->link->link_params.rate / 1000; | |
| @@ -1251,13 +554,22 @@ static void msm_dp_ctrl_calc_tu_parameters(struct msm_dp_ctrl_private *ctrl, | |
| in.nlanes = ctrl->link->link_params.num_lanes; | |
| in.bpp = ctrl->panel->msm_dp_mode.bpp; | |
| in.pixel_enc = ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420 ? 420 : 444; | |
| - in.dsc_en = 0; | |
| + in.dsc_en = DP_DSC_PANEL_EN(ctrl->panel); | |
| in.async_en = 0; | |
| - in.fec_en = 0; | |
| + in.fec_en = ctrl->panel->fec_en; | |
| in.num_of_dsc_slices = 0; | |
| in.compress_ratio = 100; | |
| - _dp_ctrl_calc_tu(ctrl, &in, tu_table); | |
| + /* | |
| + * TODO: only one dsc slice supported for now | |
| + */ | |
| + if (in.dsc_en) { | |
| + in.num_of_dsc_slices = 1; | |
| + in.compress_ratio = mult_frac(100, comp_info->src_bpp, | |
| + comp_info->tgt_bpp); | |
| + } | |
| + | |
| + msm_dp_ctrl_calc_tu(ctrl->drm_dev, &in, tu_table); | |
| } | |
| static void msm_dp_ctrl_setup_tr_unit(struct msm_dp_ctrl_private *ctrl) | |
| @@ -1675,12 +987,73 @@ static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl, | |
| return ret; | |
| } | |
| +static void msm_dp_ctrl_sink_fec_enable(struct msm_dp_ctrl_private *ctrl) | |
| +{ | |
| + int rlen; | |
| + | |
| + rlen = drm_dp_dpcd_writeb(ctrl->aux, DP_FEC_CONFIGURATION, 0x07); | |
| + if (rlen < 1) | |
| + DRM_ERROR("failed to enable sink fec\n"); | |
| +} | |
| + | |
| +static void msm_dp_ctrl_host_fec_start(struct msm_dp_ctrl_private *ctrl) | |
| +{ | |
| + u8 fec_sts = 0; | |
| + int i, max_retries = 3; | |
| + bool fec_en = ctrl->panel->fec_en; | |
| + bool fec_en_detected; | |
| + | |
| + /* Need to try to enable multiple times due to BS symbols collisions */ | |
| + for (i = 0; i < max_retries; i++) { | |
| + msm_dp_ctrl_fec_config(ctrl, fec_en); | |
| + | |
| + /* wait for controller to start fec sequence */ | |
| + usleep_range(900, 1000); | |
| + | |
| + /* read back FEC status and check if it is enabled */ | |
| + drm_dp_dpcd_readb(ctrl->aux, DP_FEC_STATUS, &fec_sts); | |
| + fec_en_detected = !!(fec_sts & DP_FEC_DECODE_EN_DETECTED); | |
| + | |
| + if (fec_en_detected == fec_en) | |
| + break; | |
| + } | |
| + | |
| + drm_dbg_dp(ctrl->drm_dev, "retries %d, fec_en_detected %d\n", | |
| + i, fec_en_detected); | |
| + | |
| + if (fec_en_detected != fec_en) | |
| + DRM_ERROR("failed to set sink fec\n"); | |
| +} | |
| + | |
| +static void msm_dp_ctrl_host_fec_stop(struct msm_dp_ctrl_private *ctrl) | |
| +{ | |
| + msm_dp_ctrl_fec_config(ctrl, false); | |
| +} | |
| + | |
| +static void msm_dp_ctrl_sink_dsc_enable(struct msm_dp_ctrl_private *ctrl) | |
| +{ | |
| + int rlen; | |
| + u32 dsc_enable; | |
| + u8 xx = 0; | |
| + | |
| + dsc_enable = DP_DSC_PANEL_EN(ctrl->panel) ? 1 : 0; | |
| + rlen = drm_dp_dpcd_writeb(ctrl->aux, DP_DSC_ENABLE, dsc_enable); | |
| + if (rlen < 1) | |
| + DRM_ERROR("failed to set sink dsc\n"); | |
| + | |
| + | |
| + dsc_enable = 0; | |
| + drm_dp_dpcd_readb(ctrl->aux, DP_DSC_ENABLE, &xx); | |
| + | |
| +} | |
| + | |
| static int msm_dp_ctrl_setup_main_link(struct msm_dp_ctrl_private *ctrl, | |
| int *training_step) | |
| { | |
| int ret = 0; | |
| msm_dp_ctrl_mainlink_enable(ctrl); | |
| + msm_dp_ctrl_mainlink_levels(ctrl); | |
| if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) | |
| return ret; | |
| @@ -1691,6 +1064,9 @@ static int msm_dp_ctrl_setup_main_link(struct msm_dp_ctrl_private *ctrl, | |
| * a link training pattern, we have to first do soft reset. | |
| */ | |
| + if (ctrl->panel->fec_en) | |
| + msm_dp_ctrl_sink_fec_enable(ctrl); | |
| + | |
| ret = msm_dp_ctrl_link_train(ctrl, training_step); | |
| return ret; | |
| @@ -2472,6 +1848,51 @@ static void msm_dp_ctrl_config_msa(struct msm_dp_ctrl_private *ctrl, | |
| msm_dp_write_link(ctrl, REG_DP_SOFTWARE_NVID, nvid); | |
| } | |
| +static void msm_dp_ctrl_dsc_commit_pps(struct msm_dp_ctrl_private *ctrl) | |
| +{ | |
| + struct msm_dp_dsc_cfg_data *dsc_data = &ctrl->dsc_data; | |
| + int i; | |
| + | |
| + msm_dp_write_link(ctrl, DP_PPS_HB_0_3, 0x7F1000); | |
| + msm_dp_write_link(ctrl, DP_PPS_PB_0_3, 0xA22300); | |
| + | |
| + for (i = 0; i < dsc_data->parity_word_len; i++) | |
| + msm_dp_write_link(ctrl, DP_PPS_PB_4_7 + (i << 2), | |
| + dsc_data->parity_word[i]); | |
| + | |
| + for (i = 0; i < dsc_data->pps_word_len; i++) | |
| + msm_dp_write_link(ctrl, DP_PPS_PPS_0_3 + (i << 2), | |
| + dsc_data->pps_word[i]); | |
| +} | |
| + | |
| +static void msm_dp_ctrl_dp_flush(struct msm_dp_ctrl_private *ctrl, | |
| + enum msm_dp_flush_bit flush_bit) | |
| +{ | |
| + u32 dp_flush; | |
| + struct msm_dp_dsc_cfg_data *dsc_data = &ctrl->dsc_data; | |
| + | |
| + dp_flush = msm_dp_read_link(ctrl, MMSS_DP_FLUSH); | |
| + | |
| + dsc_data->continuous_pps = true; | |
| + | |
| + if ((flush_bit == DP_PPS_FLUSH) && dsc_data->continuous_pps) | |
| + dp_flush &= ~BIT(2); | |
| + | |
| + dp_flush |= BIT(flush_bit); | |
| + msm_dp_write_link(ctrl, MMSS_DP_FLUSH, dp_flush); | |
| + | |
| + /* | |
| + * TODO: no dp_config_sdp_update() required? | |
| + */ | |
| + msm_dp_ctrl_sdp_update(ctrl); | |
| +} | |
| + | |
| +static void msm_dp_ctrl_pps_flush(struct msm_dp_ctrl_private *ctrl) | |
| +{ | |
| + msm_dp_ctrl_dp_flush(ctrl, DP_PPS_FLUSH); | |
| + drm_dbg_dp(ctrl->drm_dev, "pps flush\n"); | |
| +} | |
| + | |
| int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train) | |
| { | |
| int ret = 0; | |
| @@ -2535,6 +1956,8 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train | |
| */ | |
| reinit_completion(&ctrl->video_comp); | |
| + msm_dp_panel_dsc_config(ctrl->panel, &ctrl->dsc_data, true); | |
| + | |
| msm_dp_ctrl_configure_source_params(ctrl); | |
| msm_dp_ctrl_config_msa(ctrl, | |
| @@ -2542,10 +1965,14 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train | |
| pixel_rate_orig, | |
| ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420); | |
| - msm_dp_panel_clear_dsc_dto(ctrl->panel); | |
| + msm_dp_ctrl_config_dsc_dto(ctrl); | |
| + msm_dp_ctrl_dsc_commit_pps(ctrl); | |
| + msm_dp_ctrl_pps_flush(ctrl); | |
| msm_dp_ctrl_setup_tr_unit(ctrl); | |
| + msm_dp_panel_override_ack_dto(ctrl->panel, true); | |
| + | |
| msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO); | |
| ret = msm_dp_ctrl_wait4video_ready(ctrl); | |
| @@ -2556,6 +1983,10 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train | |
| drm_dbg_dp(ctrl->drm_dev, | |
| "mainlink %s\n", mainlink_ready ? "READY" : "NOT READY"); | |
| + /* wait for link training completion before fec config as per spec */ | |
| + msm_dp_ctrl_host_fec_start(ctrl); | |
| + msm_dp_ctrl_sink_dsc_enable(ctrl); | |
| + | |
| end: | |
| return ret; | |
| } | |
| @@ -2569,6 +2000,7 @@ void msm_dp_ctrl_off_link_stream(struct msm_dp_ctrl *msm_dp_ctrl) | |
| phy = ctrl->phy; | |
| msm_dp_panel_disable_vsc_sdp(ctrl->panel); | |
| + msm_dp_panel_dsc_config(ctrl->panel, &ctrl->dsc_data, false); | |
| /* set dongle to D3 (power off) mode */ | |
| msm_dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, true); | |
| @@ -2625,6 +2057,12 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl) | |
| msm_dp_panel_disable_vsc_sdp(ctrl->panel); | |
| + msm_dp_ctrl_host_fec_stop(ctrl); | |
| + msm_dp_panel_dsc_config(ctrl->panel, &ctrl->dsc_data, false); | |
| + msm_dp_ctrl_config_dsc_dto(ctrl); | |
| + | |
| + msm_dp_panel_override_ack_dto(ctrl->panel, false); | |
| + | |
| msm_dp_ctrl_mainlink_disable(ctrl); | |
| msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl); | |
| diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c | |
| index 476848bf8cd1..6a8a36259236 100644 | |
| --- a/drivers/gpu/drm/msm/dp/dp_display.c | |
| +++ b/drivers/gpu/drm/msm/dp/dp_display.c | |
| @@ -91,7 +91,7 @@ struct msm_dp_display_private { | |
| struct msm_dp_panel *panel; | |
| struct msm_dp_ctrl *ctrl; | |
| - struct msm_dp_display_mode msm_dp_mode; | |
| + struct msm_dp_display_mode *msm_dp_mode; | |
| struct msm_dp msm_dp_display; | |
| /* wait for audio signaling */ | |
| @@ -799,6 +799,8 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp) | |
| dp->panel = NULL; | |
| goto error_link; | |
| } | |
| + /* both msm_dp_display and msm_dp_panel shared same msm_dp_mode */ | |
| + dp->msm_dp_mode = &dp->panel->msm_dp_mode; | |
| dp->ctrl = msm_dp_ctrl_get(dev, dp->link, dp->panel, dp->aux, | |
| phy, dp->ahb_base, dp->link_base); | |
| @@ -827,20 +829,6 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp) | |
| return rc; | |
| } | |
| -static int msm_dp_display_set_mode(struct msm_dp *msm_dp_display, | |
| - struct msm_dp_display_mode *mode) | |
| -{ | |
| - struct msm_dp_display_private *dp; | |
| - | |
| - dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display); | |
| - | |
| - drm_mode_copy(&dp->panel->msm_dp_mode.drm_mode, &mode->drm_mode); | |
| - dp->panel->msm_dp_mode.bpp = mode->bpp; | |
| - dp->panel->msm_dp_mode.out_fmt_is_yuv_420 = mode->out_fmt_is_yuv_420; | |
| - msm_dp_panel_init_panel_info(dp->panel); | |
| - return 0; | |
| -} | |
| - | |
| static int msm_dp_display_enable(struct msm_dp_display_private *dp, bool force_link_train) | |
| { | |
| int rc = 0; | |
| @@ -921,27 +909,17 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp) | |
| return 0; | |
| } | |
| -/** | |
| - * msm_dp_bridge_mode_valid - callback to determine if specified mode is valid | |
| - * @bridge: Pointer to drm bridge structure | |
| - * @info: display info | |
| - * @mode: Pointer to drm mode structure | |
| - * Returns: Validity status for specified mode | |
| - */ | |
| -enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge, | |
| - const struct drm_display_info *info, | |
| - const struct drm_display_mode *mode) | |
| +static enum drm_mode_status _dp_bridge_mode_valid(struct msm_dp *dp, | |
| + const struct drm_display_mode *mode, | |
| + enum msm_display_compression_type *comp_type) | |
| { | |
| const u32 num_components = 3, default_bpp = 24; | |
| struct msm_dp_display_private *msm_dp_display; | |
| struct msm_dp_link_info *link_info; | |
| u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0; | |
| - struct msm_dp *dp; | |
| int mode_pclk_khz = mode->clock; | |
| - dp = to_dp_bridge(bridge)->msm_dp_display; | |
| - | |
| - if (!dp || !mode_pclk_khz || !dp->connector) { | |
| + if (!dp || !mode_pclk_khz || !dp->connector || !comp_type) { | |
| DRM_ERROR("invalid params\n"); | |
| return -EINVAL; | |
| } | |
| @@ -950,8 +928,8 @@ enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge, | |
| link_info = &msm_dp_display->panel->link_info; | |
| if ((drm_mode_is_420_only(&dp->connector->display_info, mode) && | |
| - msm_dp_display->panel->vsc_sdp_supported) || | |
| - msm_dp_wide_bus_available(dp)) | |
| + msm_dp_display->panel->vsc_sdp_supported) || | |
| + msm_dp_wide_bus_available(dp)) | |
| mode_pclk_khz /= 2; | |
| if (mode_pclk_khz > DP_MAX_PIXEL_CLK_KHZ) | |
| @@ -962,9 +940,11 @@ enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge, | |
| mode_bpp = default_bpp; | |
| mode_bpp = msm_dp_panel_get_mode_bpp(msm_dp_display->panel, | |
| - mode_bpp, mode_pclk_khz); | |
| + mode_bpp, mode->clock, comp_type); | |
| - mode_rate_khz = mode_pclk_khz * mode_bpp; | |
| + if (*comp_type == MSM_DISPLAY_COMPRESSION_DSC) | |
| + mode_bpp /= DP_DSC_COMP_RATIO; | |
| + mode_rate_khz = mode->clock * mode_bpp; | |
| supported_rate_khz = link_info->num_lanes * link_info->rate * 8; | |
| if (mode_rate_khz > supported_rate_khz) | |
| @@ -973,6 +953,50 @@ enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge, | |
| return MODE_OK; | |
| } | |
| +/** | |
| + * msm_dp_bridge_mode_valid - callback to determine if specified mode is valid | |
| + * @bridge: Pointer to drm bridge structure | |
| + * @info: display info | |
| + * @mode: Pointer to drm mode structure | |
| + * Returns: Validity status for specified mode | |
| + */ | |
| +enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge, | |
| + const struct drm_display_info *info, | |
| + const struct drm_display_mode *mode) | |
| +{ | |
| + struct msm_dp *dp = to_dp_bridge(bridge)->msm_dp_display; | |
| + enum msm_display_compression_type comp_type = MSM_DISPLAY_COMPRESSION_NONE; | |
| + | |
| + return _dp_bridge_mode_valid(dp, mode, &comp_type); | |
| +} | |
| + | |
| +struct msm_compression_info *msm_dp_get_dsc_config(struct msm_dp *dp) | |
| +{ | |
| + struct msm_dp_display_private *msm_dp_display; | |
| + struct msm_compression_info *comp_info; | |
| + | |
| + if (!dp) { | |
| + DRM_ERROR("invalid params\n"); | |
| + return 0; | |
| + } | |
| + | |
| + msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); | |
| + if (msm_dp_display->panel->dsc_en) { | |
| + comp_info = &msm_dp_display->panel->msm_dp_mode.timing.comp_info; | |
| + if (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC) | |
| + return comp_info; | |
| + } | |
| + | |
| + return NULL; | |
| +} | |
| + | |
| +bool msm_dp_dsc_enabled_for_mode(struct msm_dp *dp, const struct drm_display_mode *adj_mode) | |
| +{ | |
| + enum msm_display_compression_type comp_type = MSM_DISPLAY_COMPRESSION_NONE; | |
| + return _dp_bridge_mode_valid(dp, adj_mode, &comp_type) == MODE_OK && | |
| + comp_type == MSM_DISPLAY_COMPRESSION_DSC; | |
| +} | |
| + | |
| int msm_dp_display_get_modes(struct msm_dp *dp) | |
| { | |
| struct msm_dp_display_private *msm_dp_display; | |
| @@ -1529,7 +1553,21 @@ bool msm_dp_is_yuv_420_enabled(const struct msm_dp *msm_dp_display, | |
| bool msm_dp_needs_periph_flush(const struct msm_dp *msm_dp_display, | |
| const struct drm_display_mode *mode) | |
| { | |
| - return msm_dp_is_yuv_420_enabled(msm_dp_display, mode); | |
| + struct msm_dp_display_private *dp; | |
| + struct msm_compression_info *comp_info; | |
| + | |
| + if (!msm_dp_display) { | |
| + DRM_ERROR("invalid params\n"); | |
| + return 0; | |
| + } | |
| + | |
| + dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display); | |
| + comp_info = &dp->panel->msm_dp_mode.timing.comp_info; | |
| + | |
| + return | |
| + (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC && | |
| + comp_info->comp_ratio) || | |
| + msm_dp_is_yuv_420_enabled(msm_dp_display, mode); | |
| } | |
| bool msm_dp_wide_bus_available(const struct msm_dp *msm_dp_display) | |
| @@ -1538,7 +1576,7 @@ bool msm_dp_wide_bus_available(const struct msm_dp *msm_dp_display) | |
| dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display); | |
| - if (dp->msm_dp_mode.out_fmt_is_yuv_420) | |
| + if (dp->msm_dp_mode->out_fmt_is_yuv_420) | |
| return false; | |
| return dp->wide_bus_supported; | |
| @@ -1600,7 +1638,7 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, | |
| bool force_link_train = false; | |
| msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); | |
| - if (!msm_dp_display->msm_dp_mode.drm_mode.clock) { | |
| + if (!msm_dp_display->msm_dp_mode->drm_mode.clock) { | |
| DRM_ERROR("invalid params\n"); | |
| return; | |
| } | |
| @@ -1620,16 +1658,6 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, | |
| mutex_unlock(&msm_dp_display->event_mutex); | |
| return; | |
| } | |
| - | |
| - rc = msm_dp_display_set_mode(dp, &msm_dp_display->msm_dp_mode); | |
| - if (rc) { | |
| - DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc); | |
| - mutex_unlock(&msm_dp_display->event_mutex); | |
| - return; | |
| - } | |
| - | |
| - hpd_state = msm_dp_display->hpd_state; | |
| - | |
| if (hpd_state == ST_DISPLAY_OFF) { | |
| msm_dp_display_host_phy_init(msm_dp_display); | |
| force_link_train = true; | |
| @@ -1688,6 +1716,10 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge, | |
| if (hpd_state == ST_DISCONNECT_PENDING) { | |
| /* completed disconnection */ | |
| msm_dp_display->hpd_state = ST_DISCONNECTED; | |
| + if (msm_dp_display->panel->dsc_en) { | |
| + msm_dp_display->msm_dp_mode->timing.comp_info.comp_type = MSM_DISPLAY_COMPRESSION_NONE; | |
| + msm_dp_display->panel->dsc_en = false; | |
| + } | |
| } else { | |
| msm_dp_display->hpd_state = ST_DISPLAY_OFF; | |
| } | |
| @@ -1710,31 +1742,38 @@ void msm_dp_bridge_mode_set(struct drm_bridge *drm_bridge, | |
| msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); | |
| msm_dp_panel = msm_dp_display->panel; | |
| - memset(&msm_dp_display->msm_dp_mode, 0x0, sizeof(struct msm_dp_display_mode)); | |
| + memset(msm_dp_display->msm_dp_mode, 0x0, sizeof(struct msm_dp_display_mode)); | |
| if (msm_dp_display_check_video_test(dp)) | |
| - msm_dp_display->msm_dp_mode.bpp = msm_dp_display_get_test_bpp(dp); | |
| + msm_dp_display->msm_dp_mode->bpp = msm_dp_display_get_test_bpp(dp); | |
| else /* Default num_components per pixel = 3 */ | |
| - msm_dp_display->msm_dp_mode.bpp = dp->connector->display_info.bpc * 3; | |
| + msm_dp_display->msm_dp_mode->bpp = dp->connector->display_info.bpc * 3; | |
| - if (!msm_dp_display->msm_dp_mode.bpp) | |
| - msm_dp_display->msm_dp_mode.bpp = 24; /* Default bpp */ | |
| + if (!msm_dp_display->msm_dp_mode->bpp) | |
| + msm_dp_display->msm_dp_mode->bpp = 24; /* Default bpp */ | |
| - drm_mode_copy(&msm_dp_display->msm_dp_mode.drm_mode, adjusted_mode); | |
| + drm_mode_copy(&msm_dp_display->msm_dp_mode->drm_mode, adjusted_mode); | |
| - msm_dp_display->msm_dp_mode.v_active_low = | |
| - !!(msm_dp_display->msm_dp_mode.drm_mode.flags & DRM_MODE_FLAG_NVSYNC); | |
| + msm_dp_display->msm_dp_mode->v_active_low = | |
| + !!(msm_dp_display->msm_dp_mode->drm_mode.flags & DRM_MODE_FLAG_NVSYNC); | |
| - msm_dp_display->msm_dp_mode.h_active_low = | |
| - !!(msm_dp_display->msm_dp_mode.drm_mode.flags & DRM_MODE_FLAG_NHSYNC); | |
| + msm_dp_display->msm_dp_mode->h_active_low = | |
| + !!(msm_dp_display->msm_dp_mode->drm_mode.flags & DRM_MODE_FLAG_NHSYNC); | |
| - msm_dp_display->msm_dp_mode.out_fmt_is_yuv_420 = | |
| - drm_mode_is_420_only(&dp->connector->display_info, adjusted_mode) && | |
| - msm_dp_panel->vsc_sdp_supported; | |
| + msm_dp_panel_init_panel_info(msm_dp_display->panel); | |
| + msm_dp_display->msm_dp_mode->out_fmt_is_yuv_420 = false; | |
| + if (drm_mode_is_420_only(&msm_dp_panel->connector->display_info, | |
| + &msm_dp_display->msm_dp_mode->drm_mode)) { | |
| + if (msm_dp_panel->vsc_sdp_supported) | |
| + msm_dp_display->msm_dp_mode->out_fmt_is_yuv_420 = true; | |
| + else | |
| + drm_dbg_dp(msm_dp_display->drm_dev, "YCBCR420 was not supported"); | |
| + } | |
| /* populate wide_bus_support to different layers */ | |
| msm_dp_display->ctrl->wide_bus_en = | |
| - msm_dp_display->msm_dp_mode.out_fmt_is_yuv_420 ? false : msm_dp_display->wide_bus_supported; | |
| + msm_dp_display->msm_dp_mode->out_fmt_is_yuv_420 ? false : msm_dp_display->wide_bus_supported; | |
| + msm_dp_panel->wide_bus_en = msm_dp_display->ctrl->wide_bus_en; | |
| } | |
| void msm_dp_bridge_hpd_enable(struct drm_bridge *bridge) | |
| diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c | |
| index 891211b23202..28bbf9693657 100644 | |
| --- a/drivers/gpu/drm/msm/dp/dp_panel.c | |
| +++ b/drivers/gpu/drm/msm/dp/dp_panel.c | |
| @@ -6,16 +6,21 @@ | |
| #include "dp_panel.h" | |
| #include "dp_reg.h" | |
| #include "dp_utils.h" | |
| +#include "dpu_dsc_helper.h" | |
| +#include <drm/drm_fixed.h> | |
| #include <drm/drm_connector.h> | |
| #include <drm/drm_edid.h> | |
| #include <drm/drm_of.h> | |
| #include <drm/drm_print.h> | |
| +#include <drm/display/drm_dsc_helper.h> | |
| #include <linux/io.h> | |
| #include <linux/types.h> | |
| #include <asm/byteorder.h> | |
| +/* set to 0 to automatically disable dsc if bandwidth allows */ | |
| +#define DP_DSC_EN_ALWAYS 0 | |
| #define DP_INTF_CONFIG_DATABUS_WIDEN BIT(4) | |
| struct msm_dp_panel_private { | |
| @@ -211,25 +216,149 @@ static int msm_dp_panel_read_dpcd(struct msm_dp_panel *msm_dp_panel) | |
| return rc; | |
| } | |
| -static u32 msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel, | |
| - u32 mode_edid_bpp, u32 mode_pclk_khz) | |
| +static u32 msm_dp_panel_get_supported_bpp_no_comp(u32 link_bitrate, u32 mode_edid_bpp, u32 mode_pclk_khz) | |
| { | |
| - const struct msm_dp_link_info *link_info; | |
| - const u32 max_supported_bpp = 30, min_supported_bpp = 18; | |
| - u32 bpp, data_rate_khz; | |
| + const u32 max_supported_bpp = 30; | |
| + u32 min_supported_bpp = 18; | |
| + u32 bpp = 0, mode_bitrate; | |
| bpp = min(mode_edid_bpp, max_supported_bpp); | |
| + for (; bpp > min_supported_bpp; bpp -= 6) { | |
| + mode_bitrate = mode_pclk_khz * bpp; | |
| + if (mode_bitrate <= link_bitrate) | |
| + break; | |
| + } | |
| + | |
| + if (bpp < min_supported_bpp) { | |
| + DRM_DEBUG("bpp %d is below minimum supported bpp %d\n", bpp, | |
| + min_supported_bpp); | |
| + bpp = min_supported_bpp; | |
| + } | |
| + | |
| + return bpp; | |
| +} | |
| + | |
| +static u32 msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel, | |
| + u32 mode_edid_bpp, u32 mode_pclk_khz, enum msm_display_compression_type *comp_type) | |
| +{ | |
| + const struct msm_dp_link_info *link_info; | |
| + const u32 max_supported_bpp = 30; | |
| + u32 min_supported_bpp = 24; | |
| + u32 bpp = 0, link_bitrate = 0, mode_bitrate; | |
| + s64 rate_fp = 0; | |
| + | |
| link_info = &msm_dp_panel->link_info; | |
| - data_rate_khz = link_info->num_lanes * link_info->rate * 8; | |
| + rate_fp = drm_int2fixp(link_info->num_lanes * link_info->rate * 8); | |
| + if (msm_dp_panel->fec_en) | |
| + rate_fp = drm_fixp_div(rate_fp, msm_dp_panel->fec_overhead_fp); | |
| + | |
| + link_bitrate = drm_fixp2int(rate_fp); | |
| - do { | |
| - if (mode_pclk_khz * bpp <= data_rate_khz) | |
| + *comp_type = MSM_DISPLAY_COMPRESSION_NONE; | |
| + | |
| + if (DP_DSC_EN_ALWAYS) { | |
| + if (!msm_dp_panel->dsc_en) | |
| + return msm_dp_panel_get_supported_bpp_no_comp(link_bitrate, mode_edid_bpp, mode_pclk_khz); | |
| + } else { | |
| + bpp = msm_dp_panel_get_supported_bpp_no_comp(link_bitrate, mode_edid_bpp, mode_pclk_khz); | |
| + if (bpp >= min_supported_bpp || !msm_dp_panel->dsc_en) | |
| return bpp; | |
| - bpp -= 6; | |
| - } while (bpp > min_supported_bpp); | |
| + } | |
| + | |
| + bpp = min(mode_edid_bpp, max_supported_bpp); | |
| - return min_supported_bpp; | |
| + for (; bpp > min_supported_bpp; bpp -= 6) { | |
| + if (bpp == 30 && !(msm_dp_panel->sink_dsc_caps.color_depth & DP_DSC_10_BPC)) | |
| + continue; | |
| + else if (bpp == 24 && !(msm_dp_panel->sink_dsc_caps.color_depth & DP_DSC_8_BPC)) | |
| + continue; | |
| + | |
| + mode_bitrate = mode_pclk_khz * bpp / 3; | |
| + | |
| + if (mode_bitrate <= link_bitrate) | |
| + break; | |
| + } | |
| + | |
| + if (bpp < min_supported_bpp) { | |
| + DRM_ERROR("bpp %d is below minimum supported bpp with dsc enabled %d\n", bpp, | |
| + min_supported_bpp); | |
| + bpp = min_supported_bpp; | |
| + } | |
| + | |
| + *comp_type = MSM_DISPLAY_COMPRESSION_DSC; | |
| + return bpp; | |
| +} | |
| + | |
| +static void msm_dp_panel_decode_dsc_dpcd(struct msm_dp_panel *msm_dp_panel) | |
| +{ | |
| + if (msm_dp_panel->dsc_dpcd[0]) { | |
| + msm_dp_panel->sink_dsc_caps.dsc_capable = true; | |
| + msm_dp_panel->sink_dsc_caps.version = msm_dp_panel->dsc_dpcd[1]; | |
| + msm_dp_panel->sink_dsc_caps.block_pred_en = | |
| + msm_dp_panel->dsc_dpcd[6] ? true : false; | |
| + msm_dp_panel->sink_dsc_caps.color_depth = | |
| + msm_dp_panel->dsc_dpcd[10]; | |
| + | |
| + if (msm_dp_panel->sink_dsc_caps.version >= 0x11) | |
| + msm_dp_panel->dsc_en = true; | |
| + } else { | |
| + msm_dp_panel->sink_dsc_caps.dsc_capable = false; | |
| + msm_dp_panel->dsc_en = false; | |
| + } | |
| +} | |
| + | |
| +static void msm_dp_panel_read_sink_dsc_caps(struct msm_dp_panel *msm_dp_panel) | |
| +{ | |
| + int rlen; | |
| + struct msm_dp_panel_private *panel; | |
| + int dpcd_rev; | |
| + | |
| + if (!msm_dp_panel) { | |
| + DRM_ERROR("invalid input\n"); | |
| + return; | |
| + } | |
| + | |
| + dpcd_rev = msm_dp_panel->dpcd[DP_DPCD_REV]; | |
| + | |
| + panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); | |
| + if (dpcd_rev >= 0x14) { | |
| + rlen = drm_dp_dpcd_read(panel->aux, DP_DSC_SUPPORT, | |
| + msm_dp_panel->dsc_dpcd, (DP_DSC_RECEIVER_CAP_SIZE)); | |
| + if (rlen < (DP_DSC_RECEIVER_CAP_SIZE)) { | |
| + drm_dbg_dp(panel->drm_dev, "dsc dpcd read failed, rlen=%d\n", rlen); | |
| + return; | |
| + } | |
| + | |
| + msm_dp_panel_decode_dsc_dpcd(msm_dp_panel); | |
| + } | |
| +} | |
| + | |
| +static void msm_dp_panel_read_sink_fec_caps(struct msm_dp_panel *msm_dp_panel) | |
| +{ | |
| + int rlen; | |
| + struct msm_dp_panel_private *panel; | |
| + s64 fec_overhead_fp = drm_fixp_from_fraction(1, 1); | |
| + | |
| + if (!msm_dp_panel) { | |
| + DRM_ERROR("invalid input\n"); | |
| + return; | |
| + } | |
| + | |
| + panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); | |
| + rlen = drm_dp_dpcd_readb(panel->aux, DP_FEC_CAPABILITY, | |
| + &msm_dp_panel->fec_dpcd); | |
| + if (rlen < 1) { | |
| + DRM_ERROR("fec capability read failed, rlen=%d\n", rlen); | |
| + return; | |
| + } | |
| + | |
| + msm_dp_panel->fec_en = msm_dp_panel->fec_dpcd & DP_FEC_CAPABLE; | |
| + | |
| + if (msm_dp_panel->fec_en) | |
| + fec_overhead_fp = drm_fixp_from_fraction(100000, 97582); | |
| + | |
| + msm_dp_panel->fec_overhead_fp = fec_overhead_fp; | |
| } | |
| int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, | |
| @@ -240,7 +369,7 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, | |
| struct msm_dp_panel_private *panel; | |
| if (!msm_dp_panel || !connector) { | |
| - DRM_ERROR("invalid input\n"); | |
| + drm_dbg_dp(panel->drm_dev, "invalid input\n"); | |
| return -EINVAL; | |
| } | |
| @@ -248,7 +377,7 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, | |
| rc = msm_dp_panel_read_dpcd(msm_dp_panel); | |
| if (rc) { | |
| - DRM_ERROR("read dpcd failed %d\n", rc); | |
| + drm_dbg_dp(panel->drm_dev, "read dpcd failed %d\n", rc); | |
| return rc; | |
| } | |
| @@ -256,7 +385,7 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, | |
| if (!is_link_rate_valid(bw_code) || | |
| !is_lane_count_valid(msm_dp_panel->link_info.num_lanes) || | |
| (bw_code > msm_dp_panel->max_bw_code)) { | |
| - DRM_ERROR("Illegal link rate=%d lane=%d\n", msm_dp_panel->link_info.rate, | |
| + drm_dbg_dp(panel->drm_dev, "Illegal link rate=%d lane=%d\n", msm_dp_panel->link_info.rate, | |
| msm_dp_panel->link_info.num_lanes); | |
| return -EINVAL; | |
| } | |
| @@ -281,7 +410,7 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, | |
| drm_edid_connector_update(connector, msm_dp_panel->drm_edid); | |
| if (!msm_dp_panel->drm_edid) { | |
| - DRM_ERROR("panel edid read failed\n"); | |
| + drm_dbg_dp(panel->drm_dev, "panel edid read failed\n"); | |
| /* check edid read fail is due to unplug */ | |
| if (!msm_dp_aux_is_link_connected(panel->aux)) { | |
| rc = -ETIMEDOUT; | |
| @@ -289,12 +418,19 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, | |
| } | |
| } | |
| + msm_dp_panel_read_sink_fec_caps(msm_dp_panel); | |
| + | |
| + if (msm_dp_panel->fec_en) | |
| + msm_dp_panel_read_sink_dsc_caps(msm_dp_panel); | |
| + | |
| + drm_dbg_dp(panel->drm_dev, "fec_en=%d dsc_en=%d\n", msm_dp_panel->fec_en, msm_dp_panel->dsc_en); | |
| + | |
| end: | |
| return rc; | |
| } | |
| u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel, | |
| - u32 mode_edid_bpp, u32 mode_pclk_khz) | |
| + u32 mode_edid_bpp, u32 mode_pclk_khz, enum msm_display_compression_type *comp_type) | |
| { | |
| struct msm_dp_panel_private *panel; | |
| u32 bpp; | |
| @@ -311,7 +447,7 @@ u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel, | |
| panel->link->test_video.test_bit_depth); | |
| else | |
| bpp = msm_dp_panel_get_supported_bpp(msm_dp_panel, mode_edid_bpp, | |
| - mode_pclk_khz); | |
| + mode_pclk_khz, comp_type); | |
| return bpp; | |
| } | |
| @@ -466,14 +602,6 @@ void msm_dp_panel_tpg_config(struct msm_dp_panel *msm_dp_panel, bool enable) | |
| msm_dp_panel_tpg_enable(msm_dp_panel, &panel->msm_dp_panel.msm_dp_mode.drm_mode); | |
| } | |
| -void msm_dp_panel_clear_dsc_dto(struct msm_dp_panel *msm_dp_panel) | |
| -{ | |
| - struct msm_dp_panel_private *panel = | |
| - container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); | |
| - | |
| - msm_dp_write_p0(panel, MMSS_DP_DSC_DTO, 0x0); | |
| -} | |
| - | |
| static void msm_dp_panel_send_vsc_sdp(struct msm_dp_panel_private *panel, struct dp_sdp *vsc_sdp) | |
| { | |
| u32 header[2]; | |
| @@ -679,15 +807,613 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en) | |
| return 0; | |
| } | |
| -int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel) | |
| +u8 msm_dp_panel_get_colorimetry_config(struct msm_dp_panel *msm_dp_panel) | |
| { | |
| - struct drm_display_mode *drm_mode; | |
| + u8 colorimetry; | |
| + u32 colorspace; | |
| + u32 cc; | |
| struct msm_dp_panel_private *panel; | |
| - drm_mode = &msm_dp_panel->msm_dp_mode.drm_mode; | |
| + panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); | |
| + | |
| + cc = msm_dp_link_get_colorimetry_config(panel->link); | |
| + /* | |
| + * If there is a non-zero value then compliance test-case | |
| + * is going on, otherwise we can honor the colorspace setting | |
| + */ | |
| + if (cc) | |
| + return cc; | |
| + | |
| + colorspace = msm_dp_panel->connector->state->colorspace; | |
| + switch (colorspace) { | |
| + case DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65: | |
| + case DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER: | |
| + colorimetry = 0x7; | |
| + break; | |
| + case DRM_MODE_COLORIMETRY_RGB_WIDE_FIXED: | |
| + colorimetry = 0x3; | |
| + break; | |
| + case DRM_MODE_COLORIMETRY_RGB_WIDE_FLOAT: | |
| + colorimetry = 0xb; | |
| + break; | |
| + case DRM_MODE_COLORIMETRY_OPRGB: | |
| + colorimetry = 0xc; | |
| + break; | |
| + default: | |
| + colorimetry = 0; | |
| + } | |
| + | |
| + return colorimetry; | |
| +} | |
| + | |
| +static inline int msm_fixp2int_ceil(s64 a) | |
| +{ | |
| + return (a ? drm_fixp2int_ceil(a) : 0); | |
| +} | |
| + | |
| +struct msm_dp_dsc_slices_per_line { | |
| + u32 min_ppr; | |
| + u32 max_ppr; | |
| + u8 num_slices; | |
| +}; | |
| + | |
| +struct msm_dp_dsc_peak_throughput { | |
| + u32 index; | |
| + u32 peak_throughput; | |
| +}; | |
| + | |
| +struct msm_dp_dsc_slice_caps_bit_map { | |
| + u32 num_slices; | |
| + u32 bit_index; | |
| +}; | |
| + | |
| +static const struct msm_dp_dsc_slices_per_line slice_per_line_tbl[] = { | |
| + {0, 340, 1 }, | |
| + {340, 680, 2 }, | |
| + {680, 1360, 4 }, | |
| + {1360, 3200, 8 }, | |
| + {3200, 4800, 12 }, | |
| + {4800, 6400, 16 }, | |
| + {6400, 8000, 20 }, | |
| + {8000, 9600, 24 } | |
| +}; | |
| + | |
| +static const struct msm_dp_dsc_peak_throughput peak_throughput_mode_0_tbl[] = { | |
| + {0, 0}, | |
| + {1, 340}, | |
| + {2, 400}, | |
| + {3, 450}, | |
| + {4, 500}, | |
| + {5, 550}, | |
| + {6, 600}, | |
| + {7, 650}, | |
| + {8, 700}, | |
| + {9, 750}, | |
| + {10, 800}, | |
| + {11, 850}, | |
| + {12, 900}, | |
| + {13, 950}, | |
| + {14, 1000}, | |
| +}; | |
| + | |
| +static const struct msm_dp_dsc_slice_caps_bit_map slice_caps_bit_map_tbl[] = { | |
| + {1, 0}, | |
| + {2, 1}, | |
| + {4, 3}, | |
| + {6, 4}, | |
| + {8, 5}, | |
| + {10, 6}, | |
| + {12, 7}, | |
| + {16, 0}, | |
| + {20, 1}, | |
| + {24, 2}, | |
| +}; | |
| + | |
| +static bool msm_dp_panel_check_slice_support(u32 num_slices, u32 raw_data_1, | |
| + u32 raw_data_2) | |
| +{ | |
| + const struct msm_dp_dsc_slice_caps_bit_map *bcap; | |
| + u32 raw_data; | |
| + int i; | |
| + | |
| + if (num_slices <= 12) | |
| + raw_data = raw_data_1; | |
| + else | |
| + raw_data = raw_data_2; | |
| + | |
| + for (i = 0; i < ARRAY_SIZE(slice_caps_bit_map_tbl); i++) { | |
| + bcap = &slice_caps_bit_map_tbl[i]; | |
| + | |
| + if (bcap->num_slices == num_slices) { | |
| + raw_data &= (1 << bcap->bit_index); | |
| + | |
| + if (raw_data) | |
| + return true; | |
| + else | |
| + return false; | |
| + } | |
| + } | |
| + | |
| + return false; | |
| +} | |
| + | |
| +static int msm_dp_panel_dsc_prepare_basic_params( | |
| + struct msm_compression_info *comp_info, | |
| + const struct msm_dp_display_mode *msm_dp_mode, | |
| + struct msm_dp_panel *msm_dp_panel) | |
| +{ | |
| + struct msm_dp_panel_private *panel; | |
| + struct drm_dsc_config *dsc; | |
| + int i; | |
| + const struct msm_dp_dsc_slices_per_line *rec; | |
| + const struct msm_dp_dsc_peak_throughput *tput; | |
| + u32 slice_width; | |
| + u32 ppr = msm_dp_mode->timing.pixel_clk_khz/1000; | |
| + u32 max_slice_width; | |
| + u32 ppr_max_index; | |
| + u32 peak_throughput; | |
| + u32 ppr_per_slice; | |
| + u32 slice_caps_1; | |
| + u32 slice_caps_2; | |
| + u32 dsc_version_major, dsc_version_minor; | |
| + bool dsc_version_supported = false; | |
| panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); | |
| + dsc_version_major = msm_dp_panel->sink_dsc_caps.version & 0xF; | |
| + dsc_version_minor = (msm_dp_panel->sink_dsc_caps.version >> 4) & 0xF; | |
| + dsc_version_supported = (dsc_version_major == 0x1 && | |
| + (dsc_version_minor == 0x1 || dsc_version_minor == 0x2)) | |
| + ? true : false; | |
| + | |
| + drm_dbg_dp(panel->drm_dev, "DSC version: %d.%d, dpcd value: %x\n", | |
| + dsc_version_major, dsc_version_minor, | |
| + msm_dp_panel->sink_dsc_caps.version); | |
| + | |
| + if (!dsc_version_supported) { | |
| + dsc_version_major = 1; | |
| + dsc_version_minor = 1; | |
| + DRM_ERROR("invalid sink DSC version, fallback to %d.%d\n", | |
| + dsc_version_major, dsc_version_minor); | |
| + } | |
| + | |
| + dsc = &comp_info->msm_dsc_info.drm_dsc; | |
| + dsc->dsc_version_major = dsc_version_major; | |
| + dsc->dsc_version_minor = dsc_version_minor; | |
| + comp_info->msm_dsc_info.scr_rev = 0x0; | |
| + | |
| + comp_info->msm_dsc_info.slice_per_pkt = 0; | |
| + for (i = 0; i < ARRAY_SIZE(slice_per_line_tbl); i++) { | |
| + rec = &slice_per_line_tbl[i]; | |
| + if ((ppr > rec->min_ppr) && (ppr <= rec->max_ppr)) { | |
| + comp_info->msm_dsc_info.slice_per_pkt = rec->num_slices; | |
| + i++; | |
| + break; | |
| + } | |
| + } | |
| + | |
| + if (comp_info->msm_dsc_info.slice_per_pkt == 0) | |
| + return -EINVAL; | |
| + | |
| + ppr_max_index = msm_dp_panel->dsc_dpcd[11] &= 0xf; | |
| + if (!ppr_max_index || ppr_max_index >= 15) { | |
| + drm_dbg_dp(panel->drm_dev, | |
| + "Throughput mode 0 not supported"); | |
| + return -EINVAL; | |
| + } | |
| + | |
| + tput = &peak_throughput_mode_0_tbl[ppr_max_index]; | |
| + peak_throughput = tput->peak_throughput; | |
| + | |
| + /* assuming we are using 2 dsc enc */ | |
| + max_slice_width = min(msm_dp_panel->dsc_dpcd[12] * 320, msm_dp_mode->timing.h_active / 2); | |
| + slice_width = (msm_dp_mode->timing.h_active / | |
| + comp_info->msm_dsc_info.slice_per_pkt); | |
| + | |
| + ppr_per_slice = ppr/comp_info->msm_dsc_info.slice_per_pkt; | |
| + | |
| + slice_caps_1 = msm_dp_panel->dsc_dpcd[4]; | |
| + slice_caps_2 = msm_dp_panel->dsc_dpcd[13] & 0x7; | |
| + | |
| + /* | |
| + * There are 3 conditions to check for sink support: | |
| + * 1. The slice width cannot exceed the maximum. | |
| + * 2. The ppr per slice cannot exceed the maximum. | |
| + * 3. The number of slices must be explicitly supported. | |
| + */ | |
| + while (slice_width > max_slice_width || | |
| + ppr_per_slice > peak_throughput || | |
| + !msm_dp_panel_check_slice_support( | |
| + comp_info->msm_dsc_info.slice_per_pkt, slice_caps_1, | |
| + slice_caps_2)) { | |
| + if (i == ARRAY_SIZE(slice_per_line_tbl)) | |
| + return -EINVAL; | |
| + | |
| + rec = &slice_per_line_tbl[i]; | |
| + comp_info->msm_dsc_info.slice_per_pkt = rec->num_slices; | |
| + slice_width = (msm_dp_mode->timing.h_active / | |
| + comp_info->msm_dsc_info.slice_per_pkt); | |
| + ppr_per_slice = ppr/comp_info->msm_dsc_info.slice_per_pkt; | |
| + i++; | |
| + } | |
| + | |
| + dsc->block_pred_enable = msm_dp_panel->sink_dsc_caps.block_pred_en; | |
| + | |
| + dsc->pic_width = msm_dp_mode->timing.h_active; | |
| + dsc->pic_height = msm_dp_mode->timing.v_active; | |
| + dsc->slice_width = slice_width; | |
| + | |
| + if (dsc->pic_height % 108 == 0) | |
| + dsc->slice_height = 108; | |
| + else if (dsc->pic_height % 16 == 0) | |
| + dsc->slice_height = 16; | |
| + else if (dsc->pic_height % 12 == 0) | |
| + dsc->slice_height = 12; | |
| + else | |
| + dsc->slice_height = 15; | |
| + | |
| + comp_info->comp_type = MSM_DISPLAY_COMPRESSION_DSC; | |
| + comp_info->comp_ratio = DP_DSC_COMP_RATIO; | |
| + comp_info->src_bpp = msm_dp_mode->timing.bpp; | |
| + comp_info->tgt_bpp = msm_dp_mode->timing.bpp / comp_info->comp_ratio; | |
| + | |
| + dsc->bits_per_component = (msm_dp_mode->timing.bpp / 3); | |
| + dsc->bits_per_pixel = comp_info->tgt_bpp << 4; | |
| + dsc->slice_count = DIV_ROUND_UP(msm_dp_mode->timing.h_active, slice_width); | |
| + | |
| + return 0; | |
| +} | |
| + | |
| +void msm_dp_panel_config_dsc_dto(struct msm_dp_panel *msm_dp_panel, struct msm_dp_dsc_cfg_data *dsc_data) | |
| +{ | |
| + struct msm_dp_panel_private *panel; | |
| + u32 reg; | |
| + | |
| + if (!msm_dp_panel || !dsc_data) { | |
| + DRM_ERROR("invalid input\n"); | |
| + return; | |
| + } | |
| + | |
| + panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); | |
| + | |
| + msm_dp_write_p0(panel, MMSS_DP_DSC_DTO_COUNT, dsc_data->dto_count); | |
| + reg = msm_dp_read_p0(panel, MMSS_DP_DSC_DTO); | |
| + if (dsc_data->dto_en) { | |
| + reg |= BIT(0); | |
| + reg |= BIT(3); | |
| + reg |= (dsc_data->dto_n << 8); | |
| + reg |= (dsc_data->dto_d << 16); | |
| + } | |
| + msm_dp_write_p0(panel, MMSS_DP_DSC_DTO, reg); | |
| +} | |
| + | |
| +void msm_dp_panel_override_ack_dto(struct msm_dp_panel *msm_dp_panel, bool not_ack) | |
| +{ | |
| + struct msm_dp_panel_private *panel; | |
| + u32 dsc_dto; | |
| + | |
| + if (!msm_dp_panel) { | |
| + DRM_ERROR("invalid input\n"); | |
| + return; | |
| + } | |
| + | |
| + panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); | |
| + | |
| + dsc_dto = msm_dp_read_p0(panel, MMSS_DP_DSC_DTO); | |
| + if (not_ack) | |
| + dsc_dto &= ~BIT(1); | |
| + else | |
| + dsc_dto = BIT(1); | |
| + | |
| + msm_dp_write_p0(panel, MMSS_DP_DSC_DTO, dsc_dto); | |
| +} | |
| + | |
| +static void msm_dp_panel_get_dto_params(u32 src_bpp, u32 tgt_bpp, u32 *num, u32 *denom) | |
| +{ | |
| + if ((tgt_bpp == 12) && (src_bpp == 24)) { | |
| + *num = 1; | |
| + *denom = 2; | |
| + } else if ((tgt_bpp == 15) && (src_bpp == 30)) { | |
| + *num = 5; | |
| + *denom = 8; | |
| + } else if ((tgt_bpp == 8) && ((src_bpp == 24) || (src_bpp == 30))) { | |
| + *num = 1; | |
| + *denom = 3; | |
| + } else if ((tgt_bpp == 10) && (src_bpp == 30)) { | |
| + *num = 5; | |
| + *denom = 12; | |
| + } else { | |
| + DRM_ERROR("dto params not found\n"); | |
| + *num = 0; | |
| + *denom = 1; | |
| + } | |
| +} | |
| + | |
| +static inline u8 msm_dp_ecc_get_g0_value(u8 data) | |
| +{ | |
| + u8 c[4]; | |
| + u8 g[4]; | |
| + u8 ret_data = 0; | |
| + u8 i; | |
| + | |
| + for (i = 0; i < 4; i++) | |
| + c[i] = (data >> i) & 0x01; | |
| + | |
| + g[0] = c[3]; | |
| + g[1] = c[0] ^ c[3]; | |
| + g[2] = c[1]; | |
| + g[3] = c[2]; | |
| + | |
| + for (i = 0; i < 4; i++) | |
| + ret_data = ((g[i] & 0x01) << i) | ret_data; | |
| + | |
| + return ret_data; | |
| +} | |
| + | |
| +static inline u8 msm_dp_ecc_get_g1_value(u8 data) | |
| +{ | |
| + u8 c[4]; | |
| + u8 g[4]; | |
| + u8 ret_data = 0; | |
| + u8 i; | |
| + | |
| + for (i = 0; i < 4; i++) | |
| + c[i] = (data >> i) & 0x01; | |
| + | |
| + g[0] = c[0] ^ c[3]; | |
| + g[1] = c[0] ^ c[1] ^ c[3]; | |
| + g[2] = c[1] ^ c[2]; | |
| + g[3] = c[2] ^ c[3]; | |
| + | |
| + for (i = 0; i < 4; i++) | |
| + ret_data = ((g[i] & 0x01) << i) | ret_data; | |
| + | |
| + return ret_data; | |
| +} | |
| + | |
| +static inline u8 msm_dp_header_get_parity(u32 data) | |
| +{ | |
| + u8 x0 = 0; | |
| + u8 x1 = 0; | |
| + u8 ci = 0; | |
| + u8 iData = 0; | |
| + u8 i = 0; | |
| + u8 parity_byte; | |
| + u8 num_byte = (data > 0xFF) ? 8 : 2; | |
| + | |
| + for (i = 0; i < num_byte; i++) { | |
| + iData = (data >> i*4) & 0xF; | |
| + | |
| + ci = iData ^ x1; | |
| + x1 = x0 ^ msm_dp_ecc_get_g1_value(ci); | |
| + x0 = msm_dp_ecc_get_g0_value(ci); | |
| + } | |
| + | |
| + parity_byte = x1 | (x0 << 4); | |
| + | |
| + return parity_byte; | |
| +} | |
| + | |
| +static void msm_dp_panel_dsc_prepare_pps_packet(struct msm_dp_panel_private *panel, struct msm_dp_dsc_cfg_data *dsc_data) | |
| +{ | |
| + u8 *pps, *parity; | |
| + u32 *pps_word, *parity_word; | |
| + int i, index_4; | |
| + | |
| + pps = dsc_data->pps; | |
| + pps_word = dsc_data->pps_word; | |
| + parity = dsc_data->parity; | |
| + parity_word = dsc_data->parity_word; | |
| + | |
| + memset(parity, 0, sizeof(dsc_data->parity)); | |
| + | |
| + dsc_data->pps_word_len = dsc_data->pps_len >> 2; | |
| + dsc_data->parity_len = dsc_data->pps_word_len; | |
| + dsc_data->parity_word_len = (dsc_data->parity_len >> 2) + 1; | |
| + | |
| + for (i = 0; i < dsc_data->pps_word_len; i++) { | |
| + index_4 = i << 2; | |
| + pps_word[i] = pps[index_4 + 0] << 0 | | |
| + pps[index_4 + 1] << 8 | | |
| + pps[index_4 + 2] << 16 | | |
| + pps[index_4 + 3] << 24; | |
| + | |
| + parity[i] = msm_dp_header_get_parity(pps_word[i]); | |
| + } | |
| + | |
| + for (i = 0; i < dsc_data->parity_word_len; i++) { | |
| + index_4 = i << 2; | |
| + parity_word[i] = parity[index_4 + 0] << 0 | | |
| + parity[index_4 + 1] << 8 | | |
| + parity[index_4 + 2] << 16 | | |
| + parity[index_4 + 3] << 24; | |
| + } | |
| +} | |
| + | |
| +void msm_dp_panel_dsc_config(struct msm_dp_panel *msm_dp_panel, struct msm_dp_dsc_cfg_data *dsc_data, bool enable) | |
| +{ | |
| + struct msm_dp_panel_private *panel; | |
| + struct msm_dp_panel_info *timing; | |
| + struct msm_compression_info *comp_info; | |
| + struct drm_dsc_picture_parameter_set *pps_payload; | |
| + struct drm_dsc_config *dsc; | |
| + | |
| + panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); | |
| + | |
| + timing = &msm_dp_panel->msm_dp_mode.timing; | |
| + comp_info = &timing->comp_info; | |
| + dsc = &comp_info->msm_dsc_info.drm_dsc; | |
| + | |
| + memset(dsc_data, 0, sizeof(*dsc_data)); | |
| + | |
| + if (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC && enable) { | |
| + pps_payload = (struct drm_dsc_picture_parameter_set *)dsc_data->pps; | |
| + drm_dsc_pps_payload_pack(pps_payload, dsc); | |
| + | |
| + dsc_data->pps_len = DSC_1_1_PPS_PARAMETER_SET_ELEMENTS; | |
| + msm_dp_panel_dsc_prepare_pps_packet(panel, dsc_data); | |
| + | |
| + dsc_data->slice_per_pkt = comp_info->msm_dsc_info.slice_per_pkt - 1; | |
| + dsc_data->bytes_per_pkt = comp_info->msm_dsc_info.bytes_per_pkt; | |
| + dsc_data->bytes_per_pkt /= comp_info->msm_dsc_info.slice_per_pkt; | |
| + dsc_data->eol_byte_num = comp_info->msm_dsc_info.eol_byte_num; | |
| + dsc_data->dto_count = comp_info->msm_dsc_info.pclk_per_line; | |
| + dsc_data->be_in_lane = 10; | |
| + dsc_data->dsc_en = true; | |
| + dsc_data->dto_en = true; | |
| + msm_dp_panel_get_dto_params(comp_info->src_bpp, comp_info->tgt_bpp, &dsc_data->dto_n, | |
| + &dsc_data->dto_d); | |
| + } | |
| +} | |
| + | |
| +static void msm_dp_panel_dsc_get_num_extra_pclk(struct msm_compression_info *comp_info) | |
| +{ | |
| + unsigned int dto_n = 0, dto_d = 0, remainder; | |
| + int ack_required, last_few_ack_required, accum_ack; | |
| + int last_few_pclk, last_few_pclk_required; | |
| + struct msm_display_dsc_info *dsc_info = &comp_info->msm_dsc_info; | |
| + int start, temp, line_width = dsc_info->drm_dsc.pic_width/2; | |
| + s64 temp1_fp, temp2_fp; | |
| + | |
| + msm_dp_panel_get_dto_params(comp_info->src_bpp, comp_info->tgt_bpp, &dto_n, &dto_d); | |
| + | |
| + ack_required = dsc_info->pclk_per_line; | |
| + | |
| + /* number of pclk cycles left outside of the complete DTO set */ | |
| + last_few_pclk = line_width % dto_d; | |
| + | |
| + /* number of pclk cycles outside of the complete dto */ | |
| + temp1_fp = drm_fixp_from_fraction(line_width, dto_d); | |
| + temp2_fp = drm_fixp_from_fraction(dto_n, 1); | |
| + temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp); | |
| + temp = drm_fixp2int(temp1_fp); | |
| + last_few_ack_required = ack_required - temp; | |
| + | |
| + /* | |
| + * check how many more pclk is needed to | |
| + * accommodate the last few ack required | |
| + */ | |
| + remainder = dto_n; | |
| + accum_ack = 0; | |
| + last_few_pclk_required = 0; | |
| + while (accum_ack < last_few_ack_required) { | |
| + last_few_pclk_required++; | |
| + | |
| + if (remainder >= dto_n) | |
| + start = remainder; | |
| + else | |
| + start = remainder + dto_d; | |
| + | |
| + remainder = start - dto_n; | |
| + if (remainder < dto_n) | |
| + accum_ack++; | |
| + } | |
| + | |
| + /* if fewer pclk than required */ | |
| + if (last_few_pclk < last_few_pclk_required) | |
| + dsc_info->extra_width = last_few_pclk_required - last_few_pclk; | |
| + else | |
| + dsc_info->extra_width = 0; | |
| +} | |
| + | |
| +static void msm_dp_panel_dsc_bw_overhead_calc(struct msm_dp_panel *msm_dp_panel, | |
| + struct msm_display_dsc_info *dsc_info, | |
| + struct msm_dp_display_mode *msm_dp_mode, u32 dsc_byte_cnt) | |
| +{ | |
| + int num_slices, tot_num_eoc_symbols; | |
| + int tot_num_hor_bytes, tot_num_dummy_bytes; | |
| + int dwidth_dsc_bytes, eoc_bytes; | |
| + u32 num_lanes; | |
| + struct msm_dp_panel_private *panel; | |
| + | |
| + panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); | |
| + | |
| + num_lanes = panel->link->link_params.num_lanes; | |
| + num_slices = dsc_info->slice_per_pkt; | |
| + | |
| + eoc_bytes = dsc_byte_cnt % num_lanes; | |
| + tot_num_eoc_symbols = num_lanes * num_slices; | |
| + tot_num_hor_bytes = dsc_byte_cnt * num_slices; | |
| + tot_num_dummy_bytes = (num_lanes - eoc_bytes) * num_slices; | |
| + | |
| + if (!eoc_bytes) | |
| + tot_num_dummy_bytes = 0; | |
| + | |
| + dwidth_dsc_bytes = tot_num_hor_bytes + tot_num_eoc_symbols + | |
| + tot_num_dummy_bytes; | |
| + | |
| + drm_dbg_dp(panel->drm_dev, "dwidth_dsc_bytes:%d, tot_num_hor_bytes:%d\n", | |
| + dwidth_dsc_bytes, tot_num_hor_bytes); | |
| + | |
| + msm_dp_mode->dsc_overhead_fp = drm_fixp_from_fraction(dwidth_dsc_bytes, | |
| + tot_num_hor_bytes); | |
| + | |
| + msm_dp_mode->timing.dsc_overhead_fp = msm_dp_mode->dsc_overhead_fp; | |
| +} | |
| + | |
| +static void msm_dp_panel_dsc_pclk_param_calc(struct msm_dp_panel *msm_dp_panel, | |
| + struct msm_compression_info *comp_info, | |
| + struct msm_dp_display_mode *msm_dp_mode) | |
| +{ | |
| + int comp_ratio = 100, intf_width; | |
| + int slice_per_pkt, slice_per_intf; | |
| + s64 temp1_fp, temp2_fp; | |
| + s64 numerator_fp, denominator_fp; | |
| + s64 dsc_byte_count_fp; | |
| + u32 dsc_byte_count, temp1, temp2; | |
| + struct msm_display_dsc_info *dsc_info = &comp_info->msm_dsc_info; | |
| + | |
| + intf_width = msm_dp_mode->timing.h_active; | |
| + if (!dsc_info || !dsc_info->drm_dsc.slice_width || !dsc_info->slice_per_pkt || | |
| + (intf_width < dsc_info->drm_dsc.slice_width)) | |
| + return; | |
| + | |
| + slice_per_pkt = dsc_info->slice_per_pkt; | |
| + slice_per_intf = DIV_ROUND_UP(intf_width, | |
| + dsc_info->drm_dsc.slice_width); | |
| + | |
| + comp_ratio = mult_frac(100, comp_info->src_bpp, comp_info->tgt_bpp); | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(comp_ratio, 100); | |
| + temp2_fp = drm_fixp_from_fraction(slice_per_pkt * 8, 1); | |
| + denominator_fp = drm_fixp_mul(temp1_fp, temp2_fp); | |
| + numerator_fp = drm_fixp_from_fraction( | |
| + intf_width * dsc_info->drm_dsc.bits_per_component * 3, 1); | |
| + dsc_byte_count_fp = drm_fixp_div(numerator_fp, denominator_fp); | |
| + dsc_byte_count = msm_fixp2int_ceil(dsc_byte_count_fp); | |
| + | |
| + temp1 = dsc_byte_count * slice_per_intf; | |
| + temp2 = temp1; | |
| + if (temp1 % 3 != 0) | |
| + temp1 += 3 - (temp1 % 3); | |
| + | |
| + dsc_info->eol_byte_num = temp1 - temp2; | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(slice_per_intf, 6); | |
| + temp2_fp = drm_fixp_mul(dsc_byte_count_fp, temp1_fp); | |
| + dsc_info->pclk_per_line = msm_fixp2int_ceil(temp2_fp); | |
| + | |
| + msm_dp_panel_dsc_get_num_extra_pclk(comp_info); | |
| + dsc_info->pclk_per_line--; | |
| + | |
| + msm_dp_panel_dsc_bw_overhead_calc(msm_dp_panel, dsc_info, msm_dp_mode, dsc_byte_count); | |
| +} | |
| + | |
| +void msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel) | |
| +{ | |
| + struct msm_dp_panel_private *panel; | |
| + const u32 default_bpp = 24; | |
| + struct drm_display_mode *drm_mode; | |
| + struct msm_dp_panel_info *timing; | |
| + struct msm_compression_info *comp_info; | |
| + struct msm_dp_display_mode *msm_dp_mode = &msm_dp_panel->msm_dp_mode; | |
| + int rc; | |
| + | |
| + panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); | |
| + | |
| + drm_mode = &msm_dp_mode->drm_mode; | |
| + timing = &msm_dp_mode->timing; | |
| + | |
| /* | |
| * print resolution info as this is a result | |
| * of user initiated action of cable connection | |
| @@ -709,13 +1435,75 @@ int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel) | |
| drm_mode->clock); | |
| drm_dbg_dp(panel->drm_dev, "bpp = %d\n", msm_dp_panel->msm_dp_mode.bpp); | |
| - msm_dp_panel->msm_dp_mode.bpp = msm_dp_panel_get_mode_bpp(msm_dp_panel, msm_dp_panel->msm_dp_mode.bpp, | |
| - msm_dp_panel->msm_dp_mode.drm_mode.clock); | |
| + timing->h_active = drm_mode->hdisplay; | |
| + timing->h_back_porch = drm_mode->htotal - drm_mode->hsync_end; | |
| + timing->h_sync_width = drm_mode->htotal - | |
| + (drm_mode->hsync_start + msm_dp_mode->timing.h_back_porch); | |
| + | |
| + timing->h_front_porch = drm_mode->hsync_start - | |
| + drm_mode->hdisplay; | |
| + timing->h_skew = drm_mode->hskew; | |
| + | |
| + timing->v_active = drm_mode->vdisplay; | |
| + timing->v_back_porch = drm_mode->vtotal - drm_mode->vsync_end; | |
| + timing->v_sync_width = drm_mode->vtotal - | |
| + (drm_mode->vsync_start + msm_dp_mode->timing.v_back_porch); | |
| + | |
| + timing->v_front_porch = drm_mode->vsync_start - drm_mode->vdisplay; | |
| + | |
| + timing->refresh_rate = drm_mode_vrefresh(drm_mode); | |
| + timing->pixel_clk_khz = drm_mode->clock; | |
| + | |
| + timing->v_active_low = | |
| + !!(drm_mode->flags & DRM_MODE_FLAG_NVSYNC); | |
| + | |
| + timing->h_active_low = | |
| + !!(drm_mode->flags & DRM_MODE_FLAG_NHSYNC); | |
| + | |
| + timing->bpp = msm_dp_panel->msm_dp_mode.bpp; | |
| + | |
| + timing->wide_bus_en = msm_dp_panel->wide_bus_en; | |
| + timing->dsc_overhead_fp = 0; | |
| + | |
| + comp_info = &timing->comp_info; | |
| + comp_info->src_bpp = default_bpp; | |
| + comp_info->tgt_bpp = default_bpp; | |
| + comp_info->comp_type = MSM_DISPLAY_COMPRESSION_NONE; | |
| + comp_info->comp_ratio = 1; | |
| + | |
| + timing->bpp = msm_dp_panel_get_mode_bpp(msm_dp_panel, | |
| + timing->bpp, timing->pixel_clk_khz, &comp_info->comp_type); | |
| + msm_dp_panel->msm_dp_mode.bpp = timing->bpp; | |
| drm_dbg_dp(panel->drm_dev, "updated bpp = %d\n", | |
| msm_dp_panel->msm_dp_mode.bpp); | |
| - return 0; | |
| + if (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC) { | |
| + if (msm_dp_panel_dsc_prepare_basic_params(comp_info, | |
| + msm_dp_mode, msm_dp_panel)) { | |
| + drm_dbg_dp(panel->drm_dev, | |
| + "prepare DSC basic params failed\n"); | |
| + return; | |
| + } | |
| + | |
| + rc = msm_populate_dsc_params(panel->dev, &comp_info->msm_dsc_info.drm_dsc); | |
| + if (rc) { | |
| + drm_dbg_dp(panel->drm_dev, | |
| + "failed populating dsc params \n"); | |
| + return; | |
| + } | |
| + | |
| + rc = dpu_dsc_populate_dsc_private_params(&comp_info->msm_dsc_info, | |
| + msm_dp_mode->timing.h_active); | |
| + if (rc) { | |
| + drm_dbg_dp(panel->drm_dev, | |
| + "failed populating other dsc params\n"); | |
| + return; | |
| + } | |
| + | |
| + msm_dp_panel_dsc_pclk_param_calc(msm_dp_panel, comp_info, msm_dp_mode); | |
| + } | |
| + msm_dp_mode->fec_overhead_fp = msm_dp_panel->fec_overhead_fp; | |
| } | |
| struct msm_dp_panel *msm_dp_panel_get(struct device *dev, struct drm_dp_aux *aux, | |
| diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h | |
| index 177c1328fd99..941da207b1f6 100644 | |
| --- a/drivers/gpu/drm/msm/dp/dp_panel.h | |
| +++ b/drivers/gpu/drm/msm/dp/dp_panel.h | |
| @@ -11,11 +11,35 @@ | |
| #include "dp_aux.h" | |
| #include "dp_link.h" | |
| +#include "../msm_drv.h" | |
| struct edid; | |
| +struct msm_dp_panel_info { | |
| + u32 h_active; | |
| + u32 v_active; | |
| + u32 h_back_porch; | |
| + u32 h_front_porch; | |
| + u32 h_sync_width; | |
| + u32 h_active_low; | |
| + u32 v_back_porch; | |
| + u32 v_front_porch; | |
| + u32 v_sync_width; | |
| + u32 v_active_low; | |
| + u32 h_skew; | |
| + u32 refresh_rate; | |
| + u32 pixel_clk_khz; | |
| + u32 bpp; | |
| + bool wide_bus_en; | |
| + struct msm_compression_info comp_info; | |
| + s64 dsc_overhead_fp; | |
| +}; | |
| + | |
| struct msm_dp_display_mode { | |
| struct drm_display_mode drm_mode; | |
| + struct msm_dp_panel_info timing; | |
| + s64 fec_overhead_fp; | |
| + s64 dsc_overhead_fp; | |
| u32 bpp; | |
| u32 h_active_low; | |
| u32 v_active_low; | |
| @@ -27,10 +51,19 @@ struct msm_dp_panel_psr { | |
| u8 capabilities; | |
| }; | |
| +struct msm_dp_dsc_caps { | |
| + bool dsc_capable; | |
| + u8 version; | |
| + bool block_pred_en; | |
| + u8 color_depth; | |
| +}; | |
| + | |
| struct msm_dp_panel { | |
| /* dpcd raw data */ | |
| u8 dpcd[DP_RECEIVER_CAP_SIZE]; | |
| u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; | |
| + u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]; | |
| + u8 fec_dpcd; | |
| struct msm_dp_link_info link_info; | |
| const struct drm_edid *drm_edid; | |
| @@ -42,25 +75,38 @@ struct msm_dp_panel { | |
| u32 hw_revision; | |
| u32 max_bw_code; | |
| + | |
| + struct msm_dp_dsc_caps sink_dsc_caps; | |
| + bool dsc_en; | |
| + bool fec_en; | |
| + bool wide_bus_en; | |
| + s64 fec_overhead_fp; | |
| }; | |
| -int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel); | |
| +#define DP_DSC_COMP_RATIO 3 | |
| + | |
| +#define DP_DSC_PANEL_EN(msm_dp_panel) ((msm_dp_panel)->msm_dp_mode.timing.comp_info.comp_type == MSM_DISPLAY_COMPRESSION_DSC) | |
| + | |
| +void msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel); | |
| int msm_dp_panel_deinit(struct msm_dp_panel *msm_dp_panel); | |
| int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en); | |
| int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, | |
| struct drm_connector *connector); | |
| u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel, u32 mode_max_bpp, | |
| - u32 mode_pclk_khz); | |
| + u32 mode_pclk_khz, enum msm_display_compression_type *comp_type); | |
| int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel, | |
| struct drm_connector *connector); | |
| void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel); | |
| void msm_dp_panel_tpg_config(struct msm_dp_panel *msm_dp_panel, bool enable); | |
| - | |
| -void msm_dp_panel_clear_dsc_dto(struct msm_dp_panel *msm_dp_panel); | |
| +u8 msm_dp_panel_get_colorimetry_config(struct msm_dp_panel *msm_dp_panel); | |
| +void msm_dp_panel_dsc_config(struct msm_dp_panel *msm_dp_panel, struct msm_dp_dsc_cfg_data *dsc_data, bool enable); | |
| void msm_dp_panel_enable_vsc_sdp(struct msm_dp_panel *msm_dp_panel, struct dp_sdp *vsc_sdp); | |
| void msm_dp_panel_disable_vsc_sdp(struct msm_dp_panel *msm_dp_panel); | |
| +void msm_dp_panel_config_dsc_dto(struct msm_dp_panel *msm_dp_panel, struct msm_dp_dsc_cfg_data *dsc_data); | |
| +void msm_dp_panel_override_ack_dto(struct msm_dp_panel *msm_dp_panel, bool not_ack); | |
| + | |
| /** | |
| * is_link_rate_valid() - validates the link rate | |
| * @bw_code: link rate requested by the sink | |
| diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h | |
| index 7c44d4e2cf13..8f61f1a1e74e 100644 | |
| --- a/drivers/gpu/drm/msm/dp/dp_reg.h | |
| +++ b/drivers/gpu/drm/msm/dp/dp_reg.h | |
| @@ -220,7 +220,38 @@ | |
| #define MMSS_DP_PSR_CRC_RG (0x00000154) | |
| #define MMSS_DP_PSR_CRC_B (0x00000158) | |
| -#define REG_DP_COMPRESSION_MODE_CTRL (0x00000180) | |
| +#define DP_COMPRESSION_MODE_CTRL (0x00000180) | |
| +#define DP_PPS_HB_0_3 (0x00000184) | |
| +#define DP_PPS_PB_0_3 (0x00000188) | |
| +#define DP_PPS_PB_4_7 (0x0000018C) | |
| +#define DP_PPS_PB_8_11 (0x00000190) | |
| +#define DP_PPS_PB_12_15 (0x00000194) | |
| +#define DP_PPS_PB_16_19 (0x00000198) | |
| +#define DP_PPS_PB_20_23 (0x0000019C) | |
| +#define DP_PPS_PB_24_27 (0x000001A0) | |
| +#define DP_PPS_PB_28_31 (0x000001A4) | |
| +#define DP_PPS_PPS_0_3 (0x000001A8) | |
| +#define DP_PPS_PPS_4_7 (0x000001AC) | |
| +#define DP_PPS_PPS_8_11 (0x000001B0) | |
| +#define DP_PPS_PPS_12_15 (0x000001B4) | |
| +#define DP_PPS_PPS_16_19 (0x000001B8) | |
| +#define DP_PPS_PPS_20_23 (0x000001BC) | |
| +#define DP_PPS_PPS_24_27 (0x000001C0) | |
| +#define DP_PPS_PPS_28_31 (0x000001C4) | |
| +#define DP_PPS_PPS_32_35 (0x000001C8) | |
| +#define DP_PPS_PPS_36_39 (0x000001CC) | |
| +#define DP_PPS_PPS_40_43 (0x000001D0) | |
| +#define DP_PPS_PPS_44_47 (0x000001D4) | |
| +#define DP_PPS_PPS_48_51 (0x000001D8) | |
| +#define DP_PPS_PPS_52_55 (0x000001DC) | |
| +#define DP_PPS_PPS_56_59 (0x000001E0) | |
| +#define DP_PPS_PPS_60_63 (0x000001E4) | |
| +#define DP_PPS_PPS_64_67 (0x000001E8) | |
| +#define DP_PPS_PPS_68_71 (0x000001EC) | |
| +#define DP_PPS_PPS_72_75 (0x000001F0) | |
| +#define DP_PPS_PPS_76_79 (0x000001F4) | |
| +#define DP_PPS_PPS_80_83 (0x000001F8) | |
| +#define DP_PPS_PPS_84_87 (0x000001FC) | |
| #define MMSS_DP_AUDIO_CFG (0x00000200) | |
| #define MMSS_DP_AUDIO_STATUS (0x00000204) | |
| @@ -239,7 +270,8 @@ | |
| #define MMSS_DP_AUDIO_STREAM_0 (0x00000240) | |
| #define MMSS_DP_AUDIO_STREAM_1 (0x00000244) | |
| -#define MMSS_DP_SDP_CFG3 (0x0000024c) | |
| +#define MMSS_DP_SDP_CFG3 (0x0000024C) | |
| +#define MMSS_DP_SDP_CFG4 (0x000004EC) | |
| #define UPDATE_SDP (0x00000001) | |
| #define MMSS_DP_EXTENSION_0 (0x00000250) | |
| @@ -268,6 +300,9 @@ | |
| #define MMSS_DP_AUDIO_INFOFRAME_1 (0x000002AC) | |
| #define MMSS_DP_AUDIO_INFOFRAME_2 (0x000002B0) | |
| +#define MMSS_DP_FLUSH (0x000002F8) | |
| +#define MMSS_DP1_FLUSH (0x000002FC) | |
| + | |
| #define MMSS_DP_GENERIC0_0 (0x00000300) | |
| #define MMSS_DP_GENERIC0_1 (0x00000304) | |
| #define MMSS_DP_GENERIC0_2 (0x00000308) | |
| @@ -326,6 +361,7 @@ | |
| #define MMSS_DP_TPG_MAIN_CONTROL (0x00000060) | |
| #define MMSS_DP_DSC_DTO (0x0000007C) | |
| +#define MMSS_DP_DSC_DTO_COUNT (0x00000084) | |
| #define DP_TPG_CHECKERED_RECT_PATTERN (0x00000100) | |
| #define MMSS_DP_TPG_VIDEO_CONFIG (0x00000064) | |
| diff --git a/drivers/gpu/drm/msm/dp/dp_tu_calc.c b/drivers/gpu/drm/msm/dp/dp_tu_calc.c | |
| new file mode 100644 | |
| index 000000000000..2aa607c03492 | |
| --- /dev/null | |
| +++ b/drivers/gpu/drm/msm/dp/dp_tu_calc.c | |
| @@ -0,0 +1,894 @@ | |
| +#include "dp_tu_calc.h" | |
| + | |
| +#include <drm/drm_fixed.h> | |
| +#include <drm/drm_print.h> | |
| + | |
| +/* | |
| + * The structure and few functions present below are IP/Hardware | |
| + * specific implementation. Most of the implementation will not | |
| + * have coding comments | |
| + */ | |
| +struct tu_algo_data { | |
| + s64 lclk_fp; | |
| + s64 orig_lclk_fp; | |
| + s64 pclk_fp; | |
| + s64 orig_pclk_fp; | |
| + s64 lwidth; | |
| + s64 lwidth_fp; | |
| + int orig_lwidth; | |
| + s64 hbp_relative_to_pclk; | |
| + s64 hbp_relative_to_pclk_fp; | |
| + int nlanes; | |
| + int orig_hbp; | |
| + int bpp; | |
| + int pixelEnc; | |
| + int dsc_en; | |
| + int async_en; | |
| + int fec_en; | |
| + int bpc; | |
| + | |
| + int rb2; | |
| + uint delay_start_link_extra_pixclk; | |
| + int extra_buffer_margin; | |
| + s64 ratio_fp; | |
| + s64 original_ratio_fp; | |
| + | |
| + s64 err_fp; | |
| + s64 n_err_fp; | |
| + s64 n_n_err_fp; | |
| + int tu_size; | |
| + int tu_size_desired; | |
| + int tu_size_minus1; | |
| + | |
| + int valid_boundary_link; | |
| + s64 resulting_valid_fp; | |
| + s64 total_valid_fp; | |
| + s64 effective_valid_fp; | |
| + s64 effective_valid_recorded_fp; | |
| + int n_tus; | |
| + int n_tus_per_lane; | |
| + int paired_tus; | |
| + int remainder_tus; | |
| + int remainder_tus_upper; | |
| + int remainder_tus_lower; | |
| + int extra_bytes; | |
| + int filler_size; | |
| + int delay_start_link; | |
| + | |
| + int extra_pclk_cycles; | |
| + int extra_pclk_cycles_in_link_clk; | |
| + s64 ratio_by_tu_fp; | |
| + s64 average_valid2_fp; | |
| + int new_valid_boundary_link; | |
| + int remainder_symbols_exist; | |
| + int n_symbols; | |
| + s64 n_remainder_symbols_per_lane_fp; | |
| + s64 last_partial_tu_fp; | |
| + s64 TU_ratio_err_fp; | |
| + | |
| + int n_tus_incl_last_incomplete_tu; | |
| + int extra_pclk_cycles_tmp; | |
| + int extra_pclk_cycles_in_link_clk_tmp; | |
| + int extra_required_bytes_new_tmp; | |
| + int filler_size_tmp; | |
| + int lower_filler_size_tmp; | |
| + int delay_start_link_tmp; | |
| + | |
| + bool boundary_moderation_en; | |
| + int boundary_mod_lower_err; | |
| + int upper_boundary_count; | |
| + int lower_boundary_count; | |
| + int i_upper_boundary_count; | |
| + int i_lower_boundary_count; | |
| + int valid_lower_boundary_link; | |
| + int even_distribution_BF; | |
| + int even_distribution_legacy; | |
| + int even_distribution; | |
| + | |
| + int hbp_delayStartCheck; | |
| + int pre_tu_hw_pipe_delay; | |
| + int post_tu_hw_pipe_delay; | |
| + int link_config_hactive_time; | |
| + int delay_start_link_lclk; | |
| + int tu_active_cycles; | |
| + s64 parity_symbols; | |
| + int resolution_line_time; | |
| + int last_partial_lclk; | |
| + | |
| + int min_hblank_violated; | |
| + s64 delay_start_time_fp; | |
| + s64 hbp_time_fp; | |
| + s64 hactive_time_fp; | |
| + s64 diff_abs_fp; | |
| + int second_loop_set; | |
| + s64 ratio; | |
| +}; | |
| + | |
| +static int _tu_param_compare(s64 a, s64 b) | |
| +{ | |
| + u32 a_sign, b_sign; | |
| + s64 a_temp, b_temp, minus_1; | |
| + | |
| + if (a == b) | |
| + return 0; | |
| + | |
| + minus_1 = drm_fixp_from_fraction(-1, 1); | |
| + | |
| + a_sign = (a >> 32) & 0x80000000 ? 1 : 0; | |
| + b_sign = (b >> 32) & 0x80000000 ? 1 : 0; | |
| + | |
| + if (a_sign > b_sign) | |
| + return 2; | |
| + else if (b_sign > a_sign) | |
| + return 1; | |
| + | |
| + if (!a_sign && !b_sign) { /* positive */ | |
| + if (a > b) | |
| + return 1; | |
| + else | |
| + return 2; | |
| + } else { /* negative */ | |
| + a_temp = drm_fixp_mul(a, minus_1); | |
| + b_temp = drm_fixp_mul(b, minus_1); | |
| + | |
| + if (a_temp > b_temp) | |
| + return 2; | |
| + else | |
| + return 1; | |
| + } | |
| +} | |
| + | |
| +static s64 msm_fixp_subtract(s64 a, s64 b) | |
| +{ | |
| + s64 minus_1 = drm_fixp_from_fraction(-1, 1); | |
| + | |
| + if (a >= b) | |
| + return a - b; | |
| + | |
| + return drm_fixp_mul(b - a, minus_1); | |
| +} | |
| + | |
| +static inline int msm_fixp2int_ceil(s64 a) | |
| +{ | |
| + return a ? drm_fixp2int_ceil(a) : 0; | |
| +} | |
| + | |
| +static void msm_dp_panel_update_tu_timings(struct msm_dp_tu_calc_input *in, | |
| + struct tu_algo_data *tu) | |
| +{ | |
| + int nlanes = in->nlanes; | |
| + int dsc_num_slices = in->num_of_dsc_slices; | |
| + int dsc_num_bytes = 0; | |
| + int numerator; | |
| + s64 pclk_dsc_fp; | |
| + s64 dwidth_dsc_fp; | |
| + s64 hbp_dsc_fp; | |
| + | |
| + int tot_num_eoc_symbols = 0; | |
| + int tot_num_hor_bytes = 0; | |
| + int tot_num_dummy_bytes = 0; | |
| + int dwidth_dsc_bytes = 0; | |
| + int eoc_bytes = 0; | |
| + | |
| + s64 temp1_fp, temp2_fp, temp3_fp; | |
| + | |
| + tu->lclk_fp = drm_fixp_from_fraction(in->lclk, 1); | |
| + tu->orig_lclk_fp = tu->lclk_fp; | |
| + tu->pclk_fp = drm_fixp_from_fraction(in->pclk_khz, 1000); | |
| + tu->orig_pclk_fp = tu->pclk_fp; | |
| + tu->lwidth = in->hactive; | |
| + tu->hbp_relative_to_pclk = in->hporch; | |
| + tu->nlanes = in->nlanes; | |
| + tu->bpp = in->bpp; | |
| + tu->pixelEnc = in->pixel_enc; | |
| + tu->dsc_en = in->dsc_en; | |
| + tu->fec_en = in->fec_en; | |
| + tu->async_en = in->async_en; | |
| + tu->lwidth_fp = drm_fixp_from_fraction(in->hactive, 1); | |
| + tu->orig_lwidth = in->hactive; | |
| + tu->hbp_relative_to_pclk_fp = drm_fixp_from_fraction(in->hporch, 1); | |
| + tu->orig_hbp = in->hporch; | |
| + tu->rb2 = (in->hporch < 160) ? 1 : 0; | |
| + | |
| + if (tu->pixelEnc == 420) { | |
| + temp1_fp = drm_fixp_from_fraction(2, 1); | |
| + tu->pclk_fp = drm_fixp_div(tu->pclk_fp, temp1_fp); | |
| + tu->lwidth_fp = drm_fixp_div(tu->lwidth_fp, temp1_fp); | |
| + tu->hbp_relative_to_pclk_fp = | |
| + drm_fixp_div(tu->hbp_relative_to_pclk_fp, 2); | |
| + } | |
| + | |
| + if (tu->pixelEnc == 422) { | |
| + switch (tu->bpp) { | |
| + case 24: | |
| + tu->bpp = 16; | |
| + tu->bpc = 8; | |
| + break; | |
| + case 30: | |
| + tu->bpp = 20; | |
| + tu->bpc = 10; | |
| + break; | |
| + default: | |
| + tu->bpp = 16; | |
| + tu->bpc = 8; | |
| + break; | |
| + } | |
| + } else { | |
| + tu->bpc = tu->bpp/3; | |
| + } | |
| + | |
| + if (!in->dsc_en) | |
| + goto fec_check; | |
| + | |
| + tu->bpp = 24; /* hardcode to 24 if DSC is enabled */ | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(in->compress_ratio, 100); | |
| + temp2_fp = drm_fixp_from_fraction(in->bpp, 1); | |
| + temp3_fp = drm_fixp_div(temp2_fp, temp1_fp); | |
| + temp2_fp = drm_fixp_mul(tu->lwidth_fp, temp3_fp); | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(8, 1); | |
| + temp3_fp = drm_fixp_div(temp2_fp, temp1_fp); | |
| + | |
| + numerator = drm_fixp2int(temp3_fp); | |
| + | |
| + dsc_num_bytes = numerator / dsc_num_slices; | |
| + eoc_bytes = dsc_num_bytes % nlanes; | |
| + tot_num_eoc_symbols = nlanes * dsc_num_slices; | |
| + tot_num_hor_bytes = dsc_num_bytes * dsc_num_slices; | |
| + tot_num_dummy_bytes = (nlanes - eoc_bytes) * dsc_num_slices; | |
| + | |
| + if (dsc_num_bytes == 0) | |
| + pr_info("incorrect no of bytes per slice=%d\n", dsc_num_bytes); | |
| + | |
| + dwidth_dsc_bytes = (tot_num_hor_bytes + | |
| + tot_num_eoc_symbols + | |
| + (eoc_bytes == 0 ? 0 : tot_num_dummy_bytes)); | |
| + | |
| + dwidth_dsc_fp = drm_fixp_from_fraction(dwidth_dsc_bytes, 3); | |
| + | |
| + temp2_fp = drm_fixp_mul(tu->pclk_fp, dwidth_dsc_fp); | |
| + temp1_fp = drm_fixp_div(temp2_fp, tu->lwidth_fp); | |
| + pclk_dsc_fp = temp1_fp; | |
| + | |
| + temp1_fp = drm_fixp_div(pclk_dsc_fp, tu->pclk_fp); | |
| + temp2_fp = drm_fixp_mul(tu->hbp_relative_to_pclk_fp, temp1_fp); | |
| + hbp_dsc_fp = temp2_fp; | |
| + | |
| + /* output */ | |
| + tu->pclk_fp = pclk_dsc_fp; | |
| + tu->lwidth_fp = dwidth_dsc_fp; | |
| + tu->hbp_relative_to_pclk_fp = hbp_dsc_fp; | |
| + | |
| +fec_check: | |
| + if (in->fec_en) { | |
| + temp1_fp = drm_fixp_from_fraction(976, 1000); /* 0.976 */ | |
| + tu->lclk_fp = drm_fixp_mul(tu->lclk_fp, temp1_fp); | |
| + } | |
| +} | |
| + | |
| +static void _tu_valid_boundary_calc(struct tu_algo_data *tu) | |
| +{ | |
| + s64 temp1_fp, temp2_fp, temp, temp1, temp2; | |
| + int compare_result_1, compare_result_2, compare_result_3; | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1); | |
| + temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp); | |
| + | |
| + tu->new_valid_boundary_link = msm_fixp2int_ceil(temp2_fp); | |
| + | |
| + temp = (tu->i_upper_boundary_count * | |
| + tu->new_valid_boundary_link + | |
| + tu->i_lower_boundary_count * | |
| + (tu->new_valid_boundary_link - 1)); | |
| + tu->average_valid2_fp = drm_fixp_from_fraction(temp, | |
| + (tu->i_upper_boundary_count + | |
| + tu->i_lower_boundary_count)); | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu->bpp, 8); | |
| + temp2_fp = tu->lwidth_fp; | |
| + temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp); | |
| + temp2_fp = drm_fixp_div(temp1_fp, tu->average_valid2_fp); | |
| + tu->n_tus = drm_fixp2int(temp2_fp); | |
| + if ((temp2_fp & 0xFFFFFFFF) > 0xFFFFF000) | |
| + tu->n_tus += 1; | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu->n_tus, 1); | |
| + temp2_fp = drm_fixp_mul(temp1_fp, tu->average_valid2_fp); | |
| + temp1_fp = drm_fixp_from_fraction(tu->n_symbols, 1); | |
| + temp2_fp = temp1_fp - temp2_fp; | |
| + temp1_fp = drm_fixp_from_fraction(tu->nlanes, 1); | |
| + temp2_fp = drm_fixp_div(temp2_fp, temp1_fp); | |
| + tu->n_remainder_symbols_per_lane_fp = temp2_fp; | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1); | |
| + tu->last_partial_tu_fp = | |
| + drm_fixp_div(tu->n_remainder_symbols_per_lane_fp, | |
| + temp1_fp); | |
| + | |
| + if (tu->n_remainder_symbols_per_lane_fp != 0) | |
| + tu->remainder_symbols_exist = 1; | |
| + else | |
| + tu->remainder_symbols_exist = 0; | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu->n_tus, tu->nlanes); | |
| + tu->n_tus_per_lane = drm_fixp2int(temp1_fp); | |
| + | |
| + tu->paired_tus = (int)((tu->n_tus_per_lane) / | |
| + (tu->i_upper_boundary_count + | |
| + tu->i_lower_boundary_count)); | |
| + | |
| + tu->remainder_tus = tu->n_tus_per_lane - tu->paired_tus * | |
| + (tu->i_upper_boundary_count + | |
| + tu->i_lower_boundary_count); | |
| + | |
| + if ((tu->remainder_tus - tu->i_upper_boundary_count) > 0) { | |
| + tu->remainder_tus_upper = tu->i_upper_boundary_count; | |
| + tu->remainder_tus_lower = tu->remainder_tus - | |
| + tu->i_upper_boundary_count; | |
| + } else { | |
| + tu->remainder_tus_upper = tu->remainder_tus; | |
| + tu->remainder_tus_lower = 0; | |
| + } | |
| + | |
| + temp = tu->paired_tus * (tu->i_upper_boundary_count * | |
| + tu->new_valid_boundary_link + | |
| + tu->i_lower_boundary_count * | |
| + (tu->new_valid_boundary_link - 1)) + | |
| + (tu->remainder_tus_upper * | |
| + tu->new_valid_boundary_link) + | |
| + (tu->remainder_tus_lower * | |
| + (tu->new_valid_boundary_link - 1)); | |
| + tu->total_valid_fp = drm_fixp_from_fraction(temp, 1); | |
| + | |
| + if (tu->remainder_symbols_exist) { | |
| + temp1_fp = tu->total_valid_fp + | |
| + tu->n_remainder_symbols_per_lane_fp; | |
| + temp2_fp = drm_fixp_from_fraction(tu->n_tus_per_lane, 1); | |
| + temp2_fp = temp2_fp + tu->last_partial_tu_fp; | |
| + temp1_fp = drm_fixp_div(temp1_fp, temp2_fp); | |
| + } else { | |
| + temp2_fp = drm_fixp_from_fraction(tu->n_tus_per_lane, 1); | |
| + temp1_fp = drm_fixp_div(tu->total_valid_fp, temp2_fp); | |
| + } | |
| + tu->effective_valid_fp = temp1_fp; | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1); | |
| + temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp); | |
| + tu->n_n_err_fp = msm_fixp_subtract(tu->effective_valid_fp, temp2_fp); | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1); | |
| + temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp); | |
| + tu->n_err_fp = msm_fixp_subtract(tu->average_valid2_fp, temp2_fp); | |
| + | |
| + tu->even_distribution = tu->n_tus % tu->nlanes == 0 ? 1 : 0; | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu->bpp, 8); | |
| + temp2_fp = tu->lwidth_fp; | |
| + temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp); | |
| + temp2_fp = drm_fixp_div(temp1_fp, tu->average_valid2_fp); | |
| + tu->n_tus_incl_last_incomplete_tu = msm_fixp2int_ceil(temp2_fp); | |
| + | |
| + temp1 = 0; | |
| + temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1); | |
| + temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp); | |
| + temp1_fp = tu->average_valid2_fp - temp2_fp; | |
| + temp2_fp = drm_fixp_from_fraction(tu->n_tus_incl_last_incomplete_tu, 1); | |
| + temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp); | |
| + temp1 = msm_fixp2int_ceil(temp1_fp); | |
| + | |
| + temp = tu->i_upper_boundary_count * tu->nlanes; | |
| + temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1); | |
| + temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp); | |
| + temp1_fp = drm_fixp_from_fraction(tu->new_valid_boundary_link, 1); | |
| + temp2_fp = temp1_fp - temp2_fp; | |
| + temp1_fp = drm_fixp_from_fraction(temp, 1); | |
| + temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp); | |
| + temp2 = msm_fixp2int_ceil(temp2_fp); | |
| + | |
| + tu->extra_required_bytes_new_tmp = (int)(temp1 + temp2); | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(8, tu->bpp); | |
| + temp2_fp = drm_fixp_from_fraction( | |
| + tu->extra_required_bytes_new_tmp, 1); | |
| + temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp); | |
| + tu->extra_pclk_cycles_tmp = msm_fixp2int_ceil(temp1_fp); | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu->extra_pclk_cycles_tmp, 1); | |
| + temp2_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp); | |
| + temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp); | |
| + tu->extra_pclk_cycles_in_link_clk_tmp = msm_fixp2int_ceil(temp1_fp); | |
| + | |
| + tu->filler_size_tmp = tu->tu_size - tu->new_valid_boundary_link; | |
| + | |
| + tu->lower_filler_size_tmp = tu->filler_size_tmp + 1; | |
| + | |
| + tu->delay_start_link_tmp = tu->extra_pclk_cycles_in_link_clk_tmp + | |
| + tu->lower_filler_size_tmp + | |
| + tu->extra_buffer_margin; | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu->delay_start_link_tmp, 1); | |
| + tu->delay_start_time_fp = drm_fixp_div(temp1_fp, tu->lclk_fp); | |
| + | |
| + if (tu->rb2) { | |
| + temp1_fp = drm_fixp_mul(tu->delay_start_time_fp, tu->lclk_fp); | |
| + tu->delay_start_link_lclk = msm_fixp2int_ceil(temp1_fp); | |
| + | |
| + if (tu->remainder_tus > tu->i_upper_boundary_count) { | |
| + temp = (tu->remainder_tus - tu->i_upper_boundary_count) * | |
| + (tu->new_valid_boundary_link - 1); | |
| + temp += (tu->i_upper_boundary_count * tu->new_valid_boundary_link); | |
| + temp *= tu->nlanes; | |
| + } else { | |
| + temp = tu->nlanes * tu->remainder_tus * tu->new_valid_boundary_link; | |
| + } | |
| + | |
| + temp1 = tu->i_lower_boundary_count * (tu->new_valid_boundary_link - 1); | |
| + temp1 += tu->i_upper_boundary_count * tu->new_valid_boundary_link; | |
| + temp1 *= tu->paired_tus * tu->nlanes; | |
| + temp1_fp = drm_fixp_from_fraction(tu->n_symbols - temp1 - temp, tu->nlanes); | |
| + tu->last_partial_lclk = msm_fixp2int_ceil(temp1_fp); | |
| + | |
| + tu->tu_active_cycles = (int)((tu->n_tus_per_lane * tu->tu_size) + | |
| + tu->last_partial_lclk); | |
| + tu->post_tu_hw_pipe_delay = 4 /*BS_on_the_link*/ + 1 /*BE_next_ren*/; | |
| + temp = tu->pre_tu_hw_pipe_delay + tu->delay_start_link_lclk + | |
| + tu->tu_active_cycles + tu->post_tu_hw_pipe_delay; | |
| + | |
| + if (tu->fec_en == 1) { | |
| + if (tu->nlanes == 1) { | |
| + temp1_fp = drm_fixp_from_fraction(temp, 500); | |
| + tu->parity_symbols = msm_fixp2int_ceil(temp1_fp) * 12 + 1; | |
| + } else { | |
| + temp1_fp = drm_fixp_from_fraction(temp, 250); | |
| + tu->parity_symbols = msm_fixp2int_ceil(temp1_fp) * 6 + 1; | |
| + } | |
| + } else { //no fec BW impact | |
| + tu->parity_symbols = 0; | |
| + } | |
| + | |
| + tu->link_config_hactive_time = temp + tu->parity_symbols; | |
| + | |
| + if (tu->resolution_line_time >= tu->link_config_hactive_time + 1 /*margin*/) | |
| + tu->hbp_delayStartCheck = 1; | |
| + else | |
| + tu->hbp_delayStartCheck = 0; | |
| + } else { | |
| + compare_result_3 = _tu_param_compare(tu->hbp_time_fp, tu->delay_start_time_fp); | |
| + if (compare_result_3 < 2) | |
| + tu->hbp_delayStartCheck = 1; | |
| + else | |
| + tu->hbp_delayStartCheck = 0; | |
| + } | |
| + | |
| + compare_result_1 = _tu_param_compare(tu->n_n_err_fp, tu->diff_abs_fp); | |
| + if (compare_result_1 == 2) | |
| + compare_result_1 = 1; | |
| + else | |
| + compare_result_1 = 0; | |
| + | |
| + compare_result_2 = _tu_param_compare(tu->n_n_err_fp, tu->err_fp); | |
| + if (compare_result_2 == 2) | |
| + compare_result_2 = 1; | |
| + else | |
| + compare_result_2 = 0; | |
| + | |
| + if (((tu->even_distribution == 1) || | |
| + ((tu->even_distribution_BF == 0) && | |
| + (tu->even_distribution_legacy == 0))) && | |
| + tu->n_err_fp >= 0 && tu->n_n_err_fp >= 0 && | |
| + compare_result_2 && | |
| + (compare_result_1 || (tu->min_hblank_violated == 1)) && | |
| + (tu->new_valid_boundary_link - 1) > 0 && | |
| + (tu->hbp_delayStartCheck == 1) && | |
| + (tu->delay_start_link_tmp <= 1023)) { | |
| + tu->upper_boundary_count = tu->i_upper_boundary_count; | |
| + tu->lower_boundary_count = tu->i_lower_boundary_count; | |
| + tu->err_fp = tu->n_n_err_fp; | |
| + tu->boundary_moderation_en = true; | |
| + tu->tu_size_desired = tu->tu_size; | |
| + tu->valid_boundary_link = tu->new_valid_boundary_link; | |
| + tu->effective_valid_recorded_fp = tu->effective_valid_fp; | |
| + tu->even_distribution_BF = 1; | |
| + tu->delay_start_link = tu->delay_start_link_tmp; | |
| + } else if (tu->boundary_mod_lower_err == 0) { | |
| + compare_result_1 = _tu_param_compare(tu->n_n_err_fp, | |
| + tu->diff_abs_fp); | |
| + if (compare_result_1 == 2) | |
| + tu->boundary_mod_lower_err = 1; | |
| + } | |
| +} | |
| + | |
| +static void _dp_calc_boundary(struct tu_algo_data *tu) | |
| +{ | |
| + s64 temp1_fp = 0, temp2_fp = 0; | |
| + | |
| + do { | |
| + tu->err_fp = drm_fixp_from_fraction(1000, 1); | |
| + | |
| + temp1_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp); | |
| + temp2_fp = drm_fixp_from_fraction( | |
| + tu->delay_start_link_extra_pixclk, 1); | |
| + temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp); | |
| + tu->extra_buffer_margin = msm_fixp2int_ceil(temp1_fp); | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu->bpp, 8); | |
| + temp1_fp = drm_fixp_mul(tu->lwidth_fp, temp1_fp); | |
| + tu->n_symbols = msm_fixp2int_ceil(temp1_fp); | |
| + | |
| + for (tu->tu_size = 32; tu->tu_size <= 64; tu->tu_size++) { | |
| + for (tu->i_upper_boundary_count = 1; | |
| + tu->i_upper_boundary_count <= 15; | |
| + tu->i_upper_boundary_count++) { | |
| + for (tu->i_lower_boundary_count = 1; | |
| + tu->i_lower_boundary_count <= 15; | |
| + tu->i_lower_boundary_count++) { | |
| + _tu_valid_boundary_calc(tu); | |
| + } | |
| + } | |
| + } | |
| + tu->delay_start_link_extra_pixclk--; | |
| + } while (!tu->boundary_moderation_en && | |
| + tu->boundary_mod_lower_err == 1 && | |
| + tu->delay_start_link_extra_pixclk != 0 && | |
| + ((tu->second_loop_set == 0 && tu->rb2 == 1) || tu->rb2 == 0)); | |
| +} | |
| + | |
| +static void _dp_calc_extra_bytes(struct tu_algo_data *tu) | |
| +{ | |
| + u64 temp = 0; | |
| + s64 temp1_fp = 0, temp2_fp = 0; | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu->tu_size_desired, 1); | |
| + temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp); | |
| + temp1_fp = drm_fixp_from_fraction(tu->valid_boundary_link, 1); | |
| + temp2_fp = temp1_fp - temp2_fp; | |
| + temp1_fp = drm_fixp_from_fraction(tu->n_tus + 1, 1); | |
| + temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp); | |
| + | |
| + temp = drm_fixp2int(temp2_fp); | |
| + if (temp) | |
| + tu->extra_bytes = msm_fixp2int_ceil(temp2_fp); | |
| + else | |
| + tu->extra_bytes = 0; | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu->extra_bytes, 1); | |
| + temp2_fp = drm_fixp_from_fraction(8, tu->bpp); | |
| + temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp); | |
| + tu->extra_pclk_cycles = msm_fixp2int_ceil(temp1_fp); | |
| + | |
| + temp1_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp); | |
| + temp2_fp = drm_fixp_from_fraction(tu->extra_pclk_cycles, 1); | |
| + temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp); | |
| + tu->extra_pclk_cycles_in_link_clk = msm_fixp2int_ceil(temp1_fp); | |
| +} | |
| + | |
| + | |
| +void msm_dp_ctrl_calc_tu(struct drm_device *drm_dev, | |
| + struct msm_dp_tu_calc_input *in, | |
| + struct msm_dp_vc_tu_mapping_table *tu_table) | |
| +{ | |
| + struct tu_algo_data tu; | |
| + int compare_result_1, compare_result_2; | |
| + u64 temp = 0, temp1; | |
| + s64 temp_fp = 0, temp1_fp = 0, temp2_fp = 0; | |
| + | |
| + s64 LCLK_FAST_SKEW_fp = drm_fixp_from_fraction(6, 10000); /* 0.0006 */ | |
| + s64 RATIO_SCALE_fp = drm_fixp_from_fraction(1001, 1000); | |
| + | |
| + u8 DP_BRUTE_FORCE = 1; | |
| + s64 BRUTE_FORCE_THRESHOLD_fp = drm_fixp_from_fraction(1, 10); /* 0.1 */ | |
| + uint EXTRA_PIXCLK_CYCLE_DELAY = 4; | |
| + s64 HBLANK_MARGIN = drm_fixp_from_fraction(4, 1); | |
| + s64 HBLANK_MARGIN_EXTRA = 0; | |
| + | |
| + | |
| + memset(&tu, 0, sizeof(tu)); | |
| + | |
| + msm_dp_panel_update_tu_timings(in, &tu); | |
| + | |
| + tu.err_fp = drm_fixp_from_fraction(1000, 1); /* 1000 */ | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(4, 1); | |
| + temp2_fp = drm_fixp_mul(temp1_fp, tu.lclk_fp); | |
| + temp_fp = drm_fixp_div(temp2_fp, tu.pclk_fp); | |
| + tu.extra_buffer_margin = msm_fixp2int_ceil(temp_fp); | |
| + | |
| + if (in->compress_ratio == 375 && tu.bpp == 30) | |
| + temp1_fp = drm_fixp_from_fraction(24, 8); | |
| + else | |
| + temp1_fp = drm_fixp_from_fraction(tu.bpp, 8); | |
| + | |
| + temp2_fp = drm_fixp_mul(tu.pclk_fp, temp1_fp); | |
| + temp1_fp = drm_fixp_from_fraction(tu.nlanes, 1); | |
| + temp2_fp = drm_fixp_div(temp2_fp, temp1_fp); | |
| + tu.ratio_fp = drm_fixp_div(temp2_fp, tu.lclk_fp); | |
| + | |
| + tu.original_ratio_fp = tu.ratio_fp; | |
| + tu.boundary_moderation_en = false; | |
| + tu.upper_boundary_count = 0; | |
| + tu.lower_boundary_count = 0; | |
| + tu.i_upper_boundary_count = 0; | |
| + tu.i_lower_boundary_count = 0; | |
| + tu.valid_lower_boundary_link = 0; | |
| + tu.even_distribution_BF = 0; | |
| + tu.even_distribution_legacy = 0; | |
| + tu.even_distribution = 0; | |
| + tu.hbp_delayStartCheck = 0; | |
| + tu.pre_tu_hw_pipe_delay = 0; | |
| + tu.post_tu_hw_pipe_delay = 0; | |
| + tu.link_config_hactive_time = 0; | |
| + tu.delay_start_link_lclk = 0; | |
| + tu.tu_active_cycles = 0; | |
| + tu.resolution_line_time = 0; | |
| + tu.last_partial_lclk = 0; | |
| + tu.delay_start_time_fp = 0; | |
| + tu.second_loop_set = 0; | |
| + | |
| + tu.err_fp = drm_fixp_from_fraction(1000, 1); | |
| + tu.n_err_fp = 0; | |
| + tu.n_n_err_fp = 0; | |
| + | |
| + temp = drm_fixp2int(tu.lwidth_fp); | |
| + if ((((u32)temp % tu.nlanes) != 0) && (_tu_param_compare(tu.ratio_fp, DRM_FIXED_ONE) == 2) | |
| + && (tu.dsc_en == 0)) { | |
| + tu.ratio_fp = drm_fixp_mul(tu.ratio_fp, RATIO_SCALE_fp); | |
| + if (_tu_param_compare(tu.ratio_fp, DRM_FIXED_ONE) == 1) | |
| + tu.ratio_fp = DRM_FIXED_ONE; | |
| + } | |
| + | |
| + if (_tu_param_compare(tu.ratio_fp, DRM_FIXED_ONE) == 1) | |
| + tu.ratio_fp = DRM_FIXED_ONE; | |
| + | |
| + if (HBLANK_MARGIN_EXTRA != 0) { | |
| + HBLANK_MARGIN += HBLANK_MARGIN_EXTRA; | |
| + drm_dbg_dp(drm_dev, "Info: increase HBLANK_MARGIN to %lld. (PLUS%lld)\n", HBLANK_MARGIN, | |
| + HBLANK_MARGIN_EXTRA); | |
| + } | |
| + | |
| + for (tu.tu_size = 32; tu.tu_size <= 64; tu.tu_size++) { | |
| + temp1_fp = drm_fixp_from_fraction(tu.tu_size, 1); | |
| + temp2_fp = drm_fixp_mul(tu.ratio_fp, temp1_fp); | |
| + temp = msm_fixp2int_ceil(temp2_fp); | |
| + temp1_fp = drm_fixp_from_fraction(temp, 1); | |
| + tu.n_err_fp = temp1_fp - temp2_fp; | |
| + | |
| + if (tu.n_err_fp < tu.err_fp) { | |
| + tu.err_fp = tu.n_err_fp; | |
| + tu.tu_size_desired = tu.tu_size; | |
| + } | |
| + } | |
| + | |
| + tu.tu_size_minus1 = tu.tu_size_desired - 1; | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu.tu_size_desired, 1); | |
| + temp2_fp = drm_fixp_mul(tu.ratio_fp, temp1_fp); | |
| + tu.valid_boundary_link = msm_fixp2int_ceil(temp2_fp); | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu.bpp, 8); | |
| + temp2_fp = tu.lwidth_fp; | |
| + temp2_fp = drm_fixp_mul(temp2_fp, temp1_fp); | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu.valid_boundary_link, 1); | |
| + temp2_fp = drm_fixp_div(temp2_fp, temp1_fp); | |
| + tu.n_tus = drm_fixp2int(temp2_fp); | |
| + if ((temp2_fp & 0xFFFFFFFF) > 0xFFFFF000) | |
| + tu.n_tus += 1; | |
| + | |
| + tu.even_distribution_legacy = tu.n_tus % tu.nlanes == 0 ? 1 : 0; | |
| + drm_dbg_dp(drm_dev, "Info: n_sym = %d, num_of_tus = %d\n", | |
| + tu.valid_boundary_link, tu.n_tus); | |
| + | |
| + _dp_calc_extra_bytes(&tu); | |
| + | |
| + tu.filler_size = tu.tu_size_desired - tu.valid_boundary_link; | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu.tu_size_desired, 1); | |
| + tu.ratio_by_tu_fp = drm_fixp_mul(tu.ratio_fp, temp1_fp); | |
| + | |
| + tu.delay_start_link = tu.extra_pclk_cycles_in_link_clk + | |
| + tu.filler_size + tu.extra_buffer_margin; | |
| + | |
| + tu.resulting_valid_fp = | |
| + drm_fixp_from_fraction(tu.valid_boundary_link, 1); | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu.tu_size_desired, 1); | |
| + temp2_fp = drm_fixp_div(tu.resulting_valid_fp, temp1_fp); | |
| + tu.TU_ratio_err_fp = temp2_fp - tu.original_ratio_fp; | |
| + | |
| + temp1_fp = drm_fixp_from_fraction((tu.hbp_relative_to_pclk - HBLANK_MARGIN), 1); | |
| + tu.hbp_time_fp = drm_fixp_div(temp1_fp, tu.pclk_fp); | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu.delay_start_link, 1); | |
| + tu.delay_start_time_fp = drm_fixp_div(temp1_fp, tu.lclk_fp); | |
| + | |
| + compare_result_1 = _tu_param_compare(tu.hbp_time_fp, | |
| + tu.delay_start_time_fp); | |
| + if (compare_result_1 == 2) /* hbp_time_fp < delay_start_time_fp */ | |
| + tu.min_hblank_violated = 1; | |
| + | |
| + tu.hactive_time_fp = drm_fixp_div(tu.lwidth_fp, tu.pclk_fp); | |
| + | |
| + compare_result_2 = _tu_param_compare(tu.hactive_time_fp, | |
| + tu.delay_start_time_fp); | |
| + if (compare_result_2 == 2) | |
| + tu.min_hblank_violated = 1; | |
| + | |
| + /* brute force */ | |
| + | |
| + tu.delay_start_link_extra_pixclk = EXTRA_PIXCLK_CYCLE_DELAY; | |
| + tu.diff_abs_fp = tu.resulting_valid_fp - tu.ratio_by_tu_fp; | |
| + | |
| + temp = drm_fixp2int(tu.diff_abs_fp); | |
| + if (!temp && tu.diff_abs_fp <= 0xffff) | |
| + tu.diff_abs_fp = 0; | |
| + | |
| + /* if(diff_abs < 0) diff_abs *= -1 */ | |
| + if (tu.diff_abs_fp < 0) | |
| + tu.diff_abs_fp = drm_fixp_mul(tu.diff_abs_fp, -1); | |
| + | |
| + tu.boundary_mod_lower_err = 0; | |
| + | |
| + temp1_fp = drm_fixp_div(tu.orig_lclk_fp, tu.orig_pclk_fp); | |
| + | |
| + temp2_fp = drm_fixp_from_fraction(tu.orig_lwidth + tu.orig_hbp, 2); | |
| + temp_fp = drm_fixp_mul(temp1_fp, temp2_fp); | |
| + tu.resolution_line_time = drm_fixp2int(temp_fp); | |
| + tu.pre_tu_hw_pipe_delay = msm_fixp2int_ceil(temp1_fp) + 2 /*cdc fifo write jitter+2*/ | |
| + + 3 /*pre-delay start cycles*/ | |
| + + 3 /*post-delay start cycles*/ + 1 /*BE on the link*/; | |
| + tu.post_tu_hw_pipe_delay = 4 /*BS_on_the_link*/ + 1 /*BE_next_ren*/; | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu.bpp, 8); | |
| + temp1_fp = drm_fixp_mul(tu.lwidth_fp, temp1_fp); | |
| + tu.n_symbols = msm_fixp2int_ceil(temp1_fp); | |
| + | |
| + if (tu.rb2) { | |
| + temp1_fp = drm_fixp_mul(tu.delay_start_time_fp, tu.lclk_fp); | |
| + tu.delay_start_link_lclk = msm_fixp2int_ceil(temp1_fp); | |
| + | |
| + tu.new_valid_boundary_link = tu.valid_boundary_link; | |
| + tu.i_upper_boundary_count = 1; | |
| + tu.i_lower_boundary_count = 0; | |
| + | |
| + temp1 = tu.i_upper_boundary_count * tu.new_valid_boundary_link; | |
| + temp1 += tu.i_lower_boundary_count * (tu.new_valid_boundary_link - 1); | |
| + tu.average_valid2_fp = drm_fixp_from_fraction(temp1, | |
| + (tu.i_upper_boundary_count + tu.i_lower_boundary_count)); | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu.bpp, 8); | |
| + temp1_fp = drm_fixp_mul(tu.lwidth_fp, temp1_fp); | |
| + temp2_fp = drm_fixp_div(temp1_fp, tu.average_valid2_fp); | |
| + tu.n_tus = drm_fixp2int(temp2_fp); | |
| + | |
| + tu.n_tus_per_lane = tu.n_tus / tu.nlanes; | |
| + tu.paired_tus = (int)((tu.n_tus_per_lane) / | |
| + (tu.i_upper_boundary_count + tu.i_lower_boundary_count)); | |
| + | |
| + tu.remainder_tus = tu.n_tus_per_lane - tu.paired_tus * | |
| + (tu.i_upper_boundary_count + tu.i_lower_boundary_count); | |
| + | |
| + if (tu.remainder_tus > tu.i_upper_boundary_count) { | |
| + temp = (tu.remainder_tus - tu.i_upper_boundary_count) * | |
| + (tu.new_valid_boundary_link - 1); | |
| + temp += (tu.i_upper_boundary_count * tu.new_valid_boundary_link); | |
| + temp *= tu.nlanes; | |
| + } else { | |
| + temp = tu.nlanes * tu.remainder_tus * tu.new_valid_boundary_link; | |
| + } | |
| + | |
| + temp1 = tu.i_lower_boundary_count * (tu.new_valid_boundary_link - 1); | |
| + temp1 += tu.i_upper_boundary_count * tu.new_valid_boundary_link; | |
| + temp1 *= tu.paired_tus * tu.nlanes; | |
| + temp1_fp = drm_fixp_from_fraction(tu.n_symbols - temp1 - temp, tu.nlanes); | |
| + tu.last_partial_lclk = msm_fixp2int_ceil(temp1_fp); | |
| + | |
| + tu.tu_active_cycles = (int)((tu.n_tus_per_lane * tu.tu_size) + | |
| + tu.last_partial_lclk); | |
| + | |
| + temp = tu.pre_tu_hw_pipe_delay + tu.delay_start_link_lclk + | |
| + tu.tu_active_cycles + tu.post_tu_hw_pipe_delay; | |
| + | |
| + if (tu.fec_en == 1) { | |
| + if (tu.nlanes == 1) { | |
| + temp1_fp = drm_fixp_from_fraction(temp, 500); | |
| + tu.parity_symbols = msm_fixp2int_ceil(temp1_fp) * 12 + 1; | |
| + } else { | |
| + temp1_fp = drm_fixp_from_fraction(temp, 250); | |
| + tu.parity_symbols = msm_fixp2int_ceil(temp1_fp) * 6 + 1; | |
| + } | |
| + } else { //no fec BW impact | |
| + tu.parity_symbols = 0; | |
| + } | |
| + | |
| + tu.link_config_hactive_time = temp + tu.parity_symbols; | |
| + | |
| + if (tu.link_config_hactive_time + 1 /*margin*/ >= tu.resolution_line_time) | |
| + tu.min_hblank_violated = 1; | |
| + } | |
| + | |
| + tu.delay_start_time_fp = 0; | |
| + | |
| + if ((tu.diff_abs_fp != 0 && | |
| + ((tu.diff_abs_fp > BRUTE_FORCE_THRESHOLD_fp) || | |
| + (tu.even_distribution_legacy == 0) || | |
| + (DP_BRUTE_FORCE == 1))) || | |
| + (tu.min_hblank_violated == 1)) { | |
| + _dp_calc_boundary(&tu); | |
| + | |
| + if (tu.boundary_moderation_en) { | |
| + temp1_fp = drm_fixp_from_fraction( | |
| + (tu.upper_boundary_count * | |
| + tu.valid_boundary_link + | |
| + tu.lower_boundary_count * | |
| + (tu.valid_boundary_link - 1)), 1); | |
| + temp2_fp = drm_fixp_from_fraction( | |
| + (tu.upper_boundary_count + | |
| + tu.lower_boundary_count), 1); | |
| + tu.resulting_valid_fp = | |
| + drm_fixp_div(temp1_fp, temp2_fp); | |
| + | |
| + temp1_fp = drm_fixp_from_fraction( | |
| + tu.tu_size_desired, 1); | |
| + tu.ratio_by_tu_fp = | |
| + drm_fixp_mul(tu.original_ratio_fp, temp1_fp); | |
| + | |
| + tu.valid_lower_boundary_link = | |
| + tu.valid_boundary_link - 1; | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu.bpp, 8); | |
| + temp1_fp = drm_fixp_mul(tu.lwidth_fp, temp1_fp); | |
| + temp2_fp = drm_fixp_div(temp1_fp, | |
| + tu.resulting_valid_fp); | |
| + tu.n_tus = drm_fixp2int(temp2_fp); | |
| + | |
| + tu.tu_size_minus1 = tu.tu_size_desired - 1; | |
| + tu.even_distribution_BF = 1; | |
| + | |
| + temp1_fp = | |
| + drm_fixp_from_fraction(tu.tu_size_desired, 1); | |
| + temp2_fp = | |
| + drm_fixp_div(tu.resulting_valid_fp, temp1_fp); | |
| + tu.TU_ratio_err_fp = temp2_fp - tu.original_ratio_fp; | |
| + } | |
| + } | |
| + | |
| + if (tu.async_en) { | |
| + temp2_fp = drm_fixp_mul(LCLK_FAST_SKEW_fp, tu.lwidth_fp); | |
| + temp = msm_fixp2int_ceil(temp2_fp); | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu.nlanes, 1); | |
| + temp2_fp = drm_fixp_mul(tu.original_ratio_fp, temp1_fp); | |
| + temp1_fp = drm_fixp_from_fraction(tu.bpp, 8); | |
| + temp2_fp = drm_fixp_div(temp1_fp, temp2_fp); | |
| + temp1_fp = drm_fixp_from_fraction(temp, 1); | |
| + temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp); | |
| + temp = drm_fixp2int(temp2_fp); | |
| + | |
| + tu.delay_start_link += (int)temp; | |
| + } | |
| + | |
| + temp1_fp = drm_fixp_from_fraction(tu.delay_start_link, 1); | |
| + tu.delay_start_time_fp = drm_fixp_div(temp1_fp, tu.lclk_fp); | |
| + | |
| + /* OUTPUTS */ | |
| + tu_table->valid_boundary_link = tu.valid_boundary_link; | |
| + tu_table->delay_start_link = tu.delay_start_link; | |
| + tu_table->boundary_moderation_en = tu.boundary_moderation_en; | |
| + tu_table->valid_lower_boundary_link = tu.valid_lower_boundary_link; | |
| + tu_table->upper_boundary_count = tu.upper_boundary_count; | |
| + tu_table->lower_boundary_count = tu.lower_boundary_count; | |
| + tu_table->tu_size_minus1 = tu.tu_size_minus1; | |
| + | |
| + drm_dbg_dp(drm_dev, "TU: valid_boundary_link: %d\n", tu_table->valid_boundary_link); | |
| + drm_dbg_dp(drm_dev, "TU: delay_start_link: %d\n", tu_table->delay_start_link); | |
| + drm_dbg_dp(drm_dev, "TU: boundary_moderation_en: %d\n", | |
| + tu_table->boundary_moderation_en); | |
| + drm_dbg_dp(drm_dev, "TU: valid_lower_boundary_link: %d\n", | |
| + tu_table->valid_lower_boundary_link); | |
| + drm_dbg_dp(drm_dev, "TU: upper_boundary_count: %d\n", | |
| + tu_table->upper_boundary_count); | |
| + drm_dbg_dp(drm_dev, "TU: lower_boundary_count: %d\n", | |
| + tu_table->lower_boundary_count); | |
| + drm_dbg_dp(drm_dev, "TU: tu_size_minus1: %d\n", tu_table->tu_size_minus1); | |
| +} | |
| diff --git a/drivers/gpu/drm/msm/dp/dp_tu_calc.h b/drivers/gpu/drm/msm/dp/dp_tu_calc.h | |
| new file mode 100644 | |
| index 000000000000..95af5b6b43b9 | |
| --- /dev/null | |
| +++ b/drivers/gpu/drm/msm/dp/dp_tu_calc.h | |
| @@ -0,0 +1,40 @@ | |
| +#ifndef _DP_TU_CALC_H_ | |
| +#define _DP_TU_CALC_H_ | |
| + | |
| +#include <linux/types.h> | |
| +#include <drm/drm_device.h> | |
| + | |
| +struct msm_dp_tu_calc_input { | |
| + u64 lclk; /* 162, 270, 540 and 810 */ | |
| + u64 pclk_khz; /* in KHz */ | |
| + u64 hactive; /* active h-width */ | |
| + u64 hporch; /* bp + fp + pulse */ | |
| + int nlanes; /* no.of.lanes */ | |
| + int bpp; /* bits */ | |
| + int pixel_enc; /* 444, 420, 422 */ | |
| + int dsc_en; /* dsc on/off */ | |
| + int async_en; /* async mode */ | |
| + int fec_en; /* fec */ | |
| + int compress_ratio; /* 2:1 = 200, 3:1 = 300, 3.75:1 = 375 */ | |
| + int num_of_dsc_slices; /* number of slices per line */ | |
| +}; | |
| + | |
| +struct msm_dp_vc_tu_mapping_table { | |
| + u32 vic; | |
| + u8 lanes; | |
| + u8 lrate; /* DP_LINK_RATE -> 162(6), 270(10), 540(20), 810 (30) */ | |
| + u8 bpp; | |
| + u8 valid_boundary_link; | |
| + u16 delay_start_link; | |
| + bool boundary_moderation_en; | |
| + u8 valid_lower_boundary_link; | |
| + u8 upper_boundary_count; | |
| + u8 lower_boundary_count; | |
| + u8 tu_size_minus1; | |
| +}; | |
| + | |
| +void msm_dp_ctrl_calc_tu(struct drm_device *drm_dev, | |
| + struct msm_dp_tu_calc_input *in, | |
| + struct msm_dp_vc_tu_mapping_table *tu_table); | |
| + | |
| +#endif /* _DP_TU_CALC_H_ */ | |
| diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c | |
| index d8bb40ef820e..dec3a4ce7fab 100644 | |
| --- a/drivers/gpu/drm/msm/dsi/dsi.c | |
| +++ b/drivers/gpu/drm/msm/dsi/dsi.c | |
| @@ -12,11 +12,16 @@ bool msm_dsi_is_cmd_mode(struct msm_dsi *msm_dsi) | |
| return !(host_flags & MIPI_DSI_MODE_VIDEO); | |
| } | |
| -struct drm_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi) | |
| +struct msm_compression_info *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi) | |
| { | |
| return msm_dsi_host_get_dsc_config(msm_dsi->host); | |
| } | |
| +bool msm_dsi_dsc_enabled_for_mode(struct msm_dsi *msm_dsi, const struct drm_display_mode *adj_mode) | |
| +{ | |
| + return msm_dsi_host_dsc_enabled_for_mode(msm_dsi->host, adj_mode); | |
| +} | |
| + | |
| bool msm_dsi_wide_bus_enabled(struct msm_dsi *msm_dsi) | |
| { | |
| return msm_dsi_host_is_wide_bus_enabled(msm_dsi->host); | |
| diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h | |
| index 93c028a122f3..72fbfc43cbd8 100644 | |
| --- a/drivers/gpu/drm/msm/dsi/dsi.h | |
| +++ b/drivers/gpu/drm/msm/dsi/dsi.h | |
| @@ -121,7 +121,8 @@ int dsi_calc_clk_rate_v2(struct msm_dsi_host *msm_host, bool is_bonded_dsi); | |
| int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi); | |
| void msm_dsi_host_snapshot(struct msm_disp_state *disp_state, struct mipi_dsi_host *host); | |
| void msm_dsi_host_test_pattern_en(struct mipi_dsi_host *host); | |
| -struct drm_dsc_config *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host); | |
| +struct msm_compression_info *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host); | |
| +bool msm_dsi_host_dsc_enabled_for_mode(struct mipi_dsi_host *host, const struct drm_display_mode *adj_mode); | |
| bool msm_dsi_host_is_wide_bus_enabled(struct mipi_dsi_host *host); | |
| /* dsi phy */ | |
| diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c | |
| index db6da99375a1..bb36f0cc327e 100644 | |
| --- a/drivers/gpu/drm/msm/dsi/dsi_host.c | |
| +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c | |
| @@ -34,8 +34,6 @@ | |
| #define DSI_RESET_TOGGLE_DELAY_MS 20 | |
| -static int dsi_populate_dsc_params(struct msm_dsi_host *msm_host, struct drm_dsc_config *dsc); | |
| - | |
| static int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor) | |
| { | |
| u32 ver; | |
| @@ -166,6 +164,7 @@ struct msm_dsi_host { | |
| struct drm_display_mode *mode; | |
| struct drm_dsc_config *dsc; | |
| + struct msm_compression_info comp_info; | |
| /* connected device info */ | |
| unsigned int channel; | |
| @@ -1026,7 +1025,7 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) | |
| /* we do the calculations for dsc parameters here so that | |
| * panel can use these parameters | |
| */ | |
| - ret = dsi_populate_dsc_params(msm_host, dsc); | |
| + ret = msm_populate_dsc_params(&msm_host->pdev->dev, dsc); | |
| if (ret) | |
| return; | |
| @@ -1830,52 +1829,6 @@ static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host, | |
| return -EINVAL; | |
| } | |
| -static int dsi_populate_dsc_params(struct msm_dsi_host *msm_host, struct drm_dsc_config *dsc) | |
| -{ | |
| - int ret; | |
| - | |
| - if (dsc->bits_per_pixel & 0xf) { | |
| - DRM_DEV_ERROR(&msm_host->pdev->dev, "DSI does not support fractional bits_per_pixel\n"); | |
| - return -EINVAL; | |
| - } | |
| - | |
| - switch (dsc->bits_per_component) { | |
| - case 8: | |
| - case 10: | |
| - case 12: | |
| - /* | |
| - * Only 8, 10, and 12 bpc are supported for DSC 1.1 block. | |
| - * If additional bpc values need to be supported, update | |
| - * this quard with the appropriate DSC version verification. | |
| - */ | |
| - break; | |
| - default: | |
| - DRM_DEV_ERROR(&msm_host->pdev->dev, | |
| - "Unsupported bits_per_component value: %d\n", | |
| - dsc->bits_per_component); | |
| - return -EOPNOTSUPP; | |
| - } | |
| - | |
| - dsc->simple_422 = 0; | |
| - dsc->convert_rgb = 1; | |
| - dsc->vbr_enable = 0; | |
| - | |
| - drm_dsc_set_const_params(dsc); | |
| - drm_dsc_set_rc_buf_thresh(dsc); | |
| - | |
| - /* DPU supports only pre-SCR panels */ | |
| - ret = drm_dsc_setup_rc_params(dsc, DRM_DSC_1_1_PRE_SCR); | |
| - if (ret) { | |
| - DRM_DEV_ERROR(&msm_host->pdev->dev, "could not find DSC RC parameters\n"); | |
| - return ret; | |
| - } | |
| - | |
| - dsc->initial_scale_value = drm_dsc_initial_scale_value(dsc); | |
| - dsc->line_buf_depth = dsc->bits_per_component + 1; | |
| - | |
| - return drm_dsc_compute_rc_parameters(dsc); | |
| -} | |
| - | |
| static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) | |
| { | |
| struct msm_dsi *msm_dsi = platform_get_drvdata(msm_host->pdev); | |
| @@ -2659,9 +2612,21 @@ void msm_dsi_host_test_pattern_en(struct mipi_dsi_host *host) | |
| DSI_TEST_PATTERN_GEN_CMD_STREAM0_TRIGGER_SW_TRIGGER); | |
| } | |
| -struct drm_dsc_config *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host) | |
| +struct msm_compression_info *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host) | |
| { | |
| struct msm_dsi_host *msm_host = to_msm_dsi_host(host); | |
| + struct msm_compression_info *comp_info = NULL; | |
| - return msm_host->dsc; | |
| + if (msm_host->dsc) { | |
| + comp_info = &msm_host->comp_info; | |
| + comp_info->msm_dsc_info.drm_dsc = *msm_host->dsc; | |
| + comp_info->comp_type = MSM_DISPLAY_COMPRESSION_DSC; | |
| + } | |
| + return comp_info; | |
| +} | |
| + | |
| +bool msm_dsi_host_dsc_enabled_for_mode(struct mipi_dsi_host *host, const struct drm_display_mode *adj_mode) | |
| +{ | |
| + struct msm_dsi_host *msm_host = to_msm_dsi_host(host); | |
| + return !!msm_host->dsc; | |
| } | |
| diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h | |
| index 6d847d593f1a..6215ff1a488d 100644 | |
| --- a/drivers/gpu/drm/msm/msm_drv.h | |
| +++ b/drivers/gpu/drm/msm/msm_drv.h | |
| @@ -70,6 +70,67 @@ enum msm_dsi_controller { | |
| #define MSM_GPU_MAX_RINGS 4 | |
| +#define MSM_CHROMA_444 0x0 | |
| +#define MSM_CHROMA_422 0x1 | |
| +#define MSM_CHROMA_420 0x2 | |
| + | |
| +enum msm_display_compression_type { | |
| + MSM_DISPLAY_COMPRESSION_NONE, | |
| + MSM_DISPLAY_COMPRESSION_DSC, | |
| +}; | |
| + | |
| +struct msm_display_dsc_info { | |
| + struct drm_dsc_config drm_dsc; | |
| + u8 scr_rev; | |
| + | |
| + int initial_lines; | |
| + int pkt_per_line; | |
| + int bytes_in_slice; | |
| + int bytes_per_pkt; | |
| + int eol_byte_num; | |
| + int pclk_per_line; | |
| + int slice_last_group_size; | |
| + int slice_per_pkt; | |
| + int num_active_ss_per_enc; | |
| + int source_color_space; | |
| + int chroma_format; | |
| + int det_thresh_flatness; | |
| + u32 extra_width; | |
| +}; | |
| + | |
| +#define to_msm_dsc_info(dsc) container_of((dsc), struct msm_display_dsc_info, drm_dsc) | |
| + | |
| +#define DSC_BPP(drm_dsc) ((drm_dsc).bits_per_pixel >> 4) | |
| + | |
| +struct msm_compression_info { | |
| + enum msm_display_compression_type comp_type; | |
| + u32 comp_ratio; | |
| + u32 src_bpp; | |
| + u32 tgt_bpp; | |
| + struct msm_display_dsc_info msm_dsc_info; | |
| +}; | |
| + | |
| +struct msm_dp_dsc_cfg_data { | |
| + bool dsc_en; | |
| + bool continuous_pps; | |
| + char pps[128]; | |
| + u32 pps_len; | |
| + u32 pps_word[32]; | |
| + u32 pps_word_len; | |
| + u8 parity[32]; | |
| + u8 parity_len; | |
| + u32 parity_word[8]; | |
| + u32 parity_word_len; | |
| + u32 slice_per_pkt; | |
| + u32 bytes_per_pkt; | |
| + u32 eol_byte_num; | |
| + u32 be_in_lane; | |
| + u32 dto_en; | |
| + u32 dto_n; | |
| + u32 dto_d; | |
| + u32 dto_count; | |
| +}; | |
| + | |
| struct msm_drm_private { | |
| struct drm_device *dev; | |
| @@ -304,7 +365,8 @@ bool msm_dsi_is_cmd_mode(struct msm_dsi *msm_dsi); | |
| bool msm_dsi_is_bonded_dsi(struct msm_dsi *msm_dsi); | |
| bool msm_dsi_is_master_dsi(struct msm_dsi *msm_dsi); | |
| bool msm_dsi_wide_bus_enabled(struct msm_dsi *msm_dsi); | |
| -struct drm_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi); | |
| +struct msm_compression_info *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi); | |
| +bool msm_dsi_dsc_enabled_for_mode(struct msm_dsi *msm_dsi, const struct drm_display_mode *adj_mode); | |
| const char *msm_dsi_get_te_source(struct msm_dsi *msm_dsi); | |
| #else | |
| static inline void __init msm_dsi_register(void) | |
| @@ -339,11 +401,16 @@ static inline bool msm_dsi_wide_bus_enabled(struct msm_dsi *msm_dsi) | |
| return false; | |
| } | |
| -static inline struct drm_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi) | |
| +static inline struct msm_compression_info *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi) | |
| { | |
| return NULL; | |
| } | |
| +static inline bool msm_dsi_dsc_enabled_for_mode(struct msm_dsi *msm_dsi, const struct drm_display_mode *adj_mode) | |
| +{ | |
| + return false; | |
| +} | |
| + | |
| static inline const char *msm_dsi_get_te_source(struct msm_dsi *msm_dsi) | |
| { | |
| return NULL; | |
| @@ -362,7 +429,8 @@ bool msm_dp_is_yuv_420_enabled(const struct msm_dp *dp_display, | |
| bool msm_dp_needs_periph_flush(const struct msm_dp *dp_display, | |
| const struct drm_display_mode *mode); | |
| bool msm_dp_wide_bus_available(const struct msm_dp *dp_display); | |
| - | |
| +struct msm_compression_info *msm_dp_get_dsc_config(struct msm_dp *msm_dp); | |
| +bool msm_dp_dsc_enabled_for_mode(struct msm_dp *msm_dp, const struct drm_display_mode *adj_mode); | |
| #else | |
| static inline int __init msm_dp_register(void) | |
| { | |
| @@ -400,6 +468,15 @@ static inline bool msm_dp_wide_bus_available(const struct msm_dp *dp_display) | |
| return false; | |
| } | |
| +static inline struct msm_compression_info *msm_dp_get_dsc_config(struct msm_dp *msm_dp) | |
| +{ | |
| + return NULL; | |
| +} | |
| + | |
| +static inline bool msm_dp_dsc_enabled_for_mode(struct msm_dp *msm_dp, const struct drm_display_mode *adj_mode) | |
| +{ | |
| + return false; | |
| +} | |
| #endif | |
| #ifdef CONFIG_DRM_MSM_MDP4 | |
| diff --git a/drivers/gpu/drm/msm/msm_dsc_helper.h b/drivers/gpu/drm/msm/msm_dsc_helper.h | |
| index 63f95523b2cb..844d6d649af2 100644 | |
| --- a/drivers/gpu/drm/msm/msm_dsc_helper.h | |
| +++ b/drivers/gpu/drm/msm/msm_dsc_helper.h | |
| @@ -11,6 +11,7 @@ | |
| #include <linux/math.h> | |
| #include <drm/display/drm_dsc_helper.h> | |
| +#include "dpu_dsc_helper.h" | |
| /** | |
| * msm_dsc_get_bytes_per_line() - calculate bytes per line |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note:
Bugs remain:
DSC doesn't work on low res (currently worked around by disabling DSC on low res)Fixed in rev 2Fixed:
Ported toward v7.0.2 kernel
Later kernel versions have changes from https://gitlab.freedesktop.org/drm/msm and will need adjustment.
Edit:
Rev3:
Less aggressive in handing out layer mixers, fixes resource starvation when connecting to many displays.
Rev4, Rev5, Rev6, Rev7, and Rev8:
Minor fix/cleanup.