Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5c2f0701 authored by Jeykumar Sankaran's avatar Jeykumar Sankaran Committed by Gerrit - the friendly Code Review server
Browse files

drm/msm/sde: add DSC encoder support for display topologies



Add display stream compression support for sde drm driver. Read
DSC compression details of the panel from the display driver and
configure SDE as per the topology selected for the display panel.
Patch adds support for 3 different DSC compression modes:
(1) Single DSC encoder mode
(2) Dual DSC encoder merge mode
(3) Dual DSC encoder mode

Change-Id: Ibbb8fe37d0e01cc84c740d55e1d9c5c58da9d2d7
Signed-off-by: default avatarAlexander Beykun <abeykun@codeaurora.org>
Signed-off-by: default avatarJeykumar Sankaran <jsanka@codeaurora.org>
parent c9e440da
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -136,7 +136,7 @@ msm_drm-$(CONFIG_DRM_MSM) += \
	sde/sde_hw_color_processing_v1_7.o \
	sde/sde_reg_dma.o \
	sde/sde_hw_reg_dma_v1.o \

	sde/sde_hw_dsc.o

msm_drm-$(CONFIG_DRM_SDE_WB) += sde/sde_wb.o \
	sde/sde_encoder_phys_wb.o
+0 −2
Original line number Diff line number Diff line
@@ -169,12 +169,10 @@ struct msm_vblank_ctrl {
 * enum msm_display_compression_type - compression method used for pixel stream
 * @MSM_DISPLAY_COMPRESSION_NONE:     Pixel data is not compressed
 * @MSM_DISPLAY_COMPRESSION_DSC:      DSC compresison is used
 * @MSM_DISPLAY_COMPRESSION_FBC:      FBC compression is used
 */
enum msm_display_compression_type {
	MSM_DISPLAY_COMPRESSION_NONE,
	MSM_DISPLAY_COMPRESSION_DSC,
	MSM_DISPLAY_COMPRESSION_FBC,
};

/**
+8 −0
Original line number Diff line number Diff line
@@ -63,6 +63,14 @@ struct sde_connector_ops {
	int (*get_modes)(struct drm_connector *connector,
			void *display);

	/**
	 * update_pps - update pps command for the display panel
	 * @pps_cmd: Pointer to pps command
	 * @display: Pointer to private display handle
	 * Returns: Zero on success
	 */
	int (*update_pps)(char *pps_cmd, void *display);

	/**
	 * mode_valid - determine if specified mode is valid
	 * @connector: Pointer to drm connector structure
+345 −2
Original line number Diff line number Diff line
@@ -33,8 +33,8 @@
#include "sde_encoder_phys.h"
#include "sde_color_processing.h"
#include "sde_rsc.h"

#include "sde_power_handle.h"
#include "sde_hw_dsc.h"

#define SDE_DEBUG_ENC(e, fmt, ...) SDE_DEBUG("enc%d " fmt,\
		(e) ? (e)->base.base.id : -1, ##__VA_ARGS__)
@@ -72,6 +72,7 @@
 *			Only valid after enable. Cleared as disable.
 * @hw_pp		Handle to the pingpong blocks used for the display. No.
 *                      pingpong blocks can be different than num_phys_encs.
 * @hw_dsc:		Array of DSC block handles used for the display.
 * @crtc_vblank_cb:	Callback into the upper layer / CRTC for
 *			notification of the VBLANK
 * @crtc_vblank_cb_data:	Data from upper layer for VBLANK notification
@@ -101,6 +102,7 @@ struct sde_encoder_virt {
	struct sde_encoder_phys *phys_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL];
	struct sde_encoder_phys *cur_master;
	struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
	struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];

	void (*crtc_vblank_cb)(void *);
	void *crtc_vblank_cb_data;
@@ -122,6 +124,13 @@ struct sde_encoder_virt {

#define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base)

inline bool _sde_is_dsc_enabled(struct sde_encoder_virt *sde_enc)
{
	struct msm_compression_info *comp_info = &sde_enc->disp_info.comp_info;

	return (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC);
}

void sde_encoder_get_hw_resources(struct drm_encoder *drm_enc,
		struct sde_encoder_hw_resources *hw_res,
		struct drm_connector_state *conn_state)
@@ -142,6 +151,9 @@ void sde_encoder_get_hw_resources(struct drm_encoder *drm_enc,
	memset(hw_res, 0, sizeof(*hw_res));
	hw_res->display_num_of_h_tiles = sde_enc->display_num_of_h_tiles;

	if (_sde_is_dsc_enabled(sde_enc))
		hw_res->needs_dsc = true;

	for (i = 0; i < sde_enc->num_phys_encs; i++) {
		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];

@@ -298,6 +310,319 @@ static int sde_encoder_virt_atomic_check(
	return ret;
}

static int _sde_encoder_dsc_update_pic_dim(struct msm_display_dsc_info *dsc,
		int pic_width, int pic_height)
{
	if (!dsc || !pic_width || !pic_height) {
		SDE_ERROR("invalid input: pic_width=%d pic_height=%d\n",
			pic_width, pic_height);
		return -EINVAL;
	}

	if ((pic_width % dsc->slice_width) ||
	    (pic_height % dsc->slice_height)) {
		SDE_ERROR("pic_dim=%dx%d has to be multiple of slice=%dx%d\n",
			pic_width, pic_height,
			dsc->slice_width, dsc->slice_height);
		return -EINVAL;
	}

	dsc->pic_width = pic_width;
	dsc->pic_height = pic_height;

	return 0;
}

static void _sde_encoder_dsc_pclk_param_calc(struct msm_display_dsc_info *dsc,
		int intf_width)
{
	int slice_per_pkt, slice_per_intf;
	int bytes_in_slice, total_bytes_per_intf;

	if (!dsc || !dsc->slice_width || !dsc->slice_per_pkt ||
	    (intf_width < dsc->slice_width)) {
		SDE_ERROR("invalid input: intf_width=%d slice_width=%d\n",
			intf_width, dsc ? dsc->slice_width : -1);
		return;
	}

	slice_per_pkt = dsc->slice_per_pkt;
	slice_per_intf = DIV_ROUND_UP(intf_width, 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;

	bytes_in_slice = DIV_ROUND_UP(dsc->slice_width * dsc->bpp, 8);
	total_bytes_per_intf = bytes_in_slice * slice_per_intf;

	dsc->eol_byte_num = total_bytes_per_intf % 3;
	dsc->pclk_per_line =  DIV_ROUND_UP(total_bytes_per_intf, 3);
	dsc->bytes_in_slice = bytes_in_slice;
	dsc->bytes_per_pkt = bytes_in_slice * slice_per_pkt;
	dsc->pkt_per_line = slice_per_intf / slice_per_pkt;
}

static int _sde_encoder_dsc_initial_line_calc(struct msm_display_dsc_info *dsc,
		int 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->bpc < 10) ? 84 : 92);
	total_pixels = ssm_delay * 3 + dsc->initial_xmit_delay + 47;
	if (soft_slice_per_enc > 1)
		total_pixels += (ssm_delay * 3);
	dsc->initial_lines = DIV_ROUND_UP(total_pixels, dsc->slice_width);
	return 0;
}

static bool _sde_encoder_dsc_ich_reset_override_needed(bool pu_en,
		struct msm_display_dsc_info *dsc)
{
	/*
	 * As per the DSC spec, ICH_RESET can be either end of the slice line
	 * or at the end of the slice. HW internally generates ich_reset at
	 * end of the slice line if DSC_MERGE is used or encoder has two
	 * soft slices. However, if encoder has only 1 soft slice and DSC_MERGE
	 * is not used then it will generate ich_reset at the end of slice.
	 *
	 * Now as per the spec, during one PPS session, position where
	 * ich_reset is generated should not change. Now if full-screen frame
	 * has more than 1 soft slice then HW will automatically generate
	 * ich_reset at the end of slice_line. But for the same panel, if
	 * partial frame is enabled and only 1 encoder is used with 1 slice,
	 * then HW will generate ich_reset at end of the slice. This is a
	 * mismatch. Prevent this by overriding HW's decision.
	 */
	return pu_en && dsc && (dsc->full_frame_slices > 1) &&
		(dsc->slice_width == dsc->pic_width);
}

static void _sde_encoder_dsc_pipe_cfg(struct sde_hw_dsc *hw_dsc,
		struct sde_hw_pingpong *hw_pp, struct msm_display_dsc_info *dsc,
		u32 common_mode, bool ich_reset)
{
	if (hw_dsc->ops.dsc_config)
		hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, ich_reset);

	if (hw_dsc->ops.dsc_config_thresh)
		hw_dsc->ops.dsc_config_thresh(hw_dsc, dsc);

	if (hw_pp->ops.setup_dsc)
		hw_pp->ops.setup_dsc(hw_pp);

	if (hw_pp->ops.enable_dsc)
		hw_pp->ops.enable_dsc(hw_pp);
}

static int _sde_encoder_dsc_1_lm_1_enc_1_intf(struct sde_encoder_virt *sde_enc)
{
	int pic_width, pic_height;
	int this_frame_slices;
	int intf_ip_w, enc_ip_w;
	int ich_res, dsc_common_mode = 0;

	struct sde_hw_pingpong *hw_pp = sde_enc->hw_pp[0];
	struct sde_hw_dsc *hw_dsc = sde_enc->hw_dsc[0];
	struct sde_encoder_phys *enc_master = sde_enc->cur_master;
	struct sde_hw_mdp *hw_mdp_top  = enc_master->hw_mdptop;
	struct msm_display_dsc_info *dsc =
		&sde_enc->disp_info.comp_info.dsc_info;

	if (dsc == NULL || hw_dsc == NULL || hw_pp == NULL ||
						hw_mdp_top == NULL) {
		SDE_ERROR_ENC(sde_enc, "invalid params for DSC\n");
		return -EINVAL;
	}

	pic_width = dsc->pic_width;
	pic_height = dsc->pic_height;

	_sde_encoder_dsc_update_pic_dim(dsc, pic_width, pic_height);

	this_frame_slices = pic_width / dsc->slice_width;
	intf_ip_w = this_frame_slices * dsc->slice_width;
	_sde_encoder_dsc_pclk_param_calc(dsc, intf_ip_w);

	enc_ip_w = intf_ip_w;
	_sde_encoder_dsc_initial_line_calc(dsc, enc_ip_w);

	ich_res = _sde_encoder_dsc_ich_reset_override_needed(false, dsc);

	if (enc_master->intf_mode == INTF_MODE_VIDEO)
		dsc_common_mode = DSC_MODE_VIDEO;

	SDE_DEBUG_ENC(sde_enc, "pic_w: %d pic_h: %d mode:%d\n",
		pic_width, pic_height, dsc_common_mode);
	SDE_EVT32(DRMID(&sde_enc->base), pic_width, pic_height,
			dsc_common_mode);

	_sde_encoder_dsc_pipe_cfg(hw_dsc, hw_pp, dsc, dsc_common_mode,
			ich_res);

	return 0;
}
static int _sde_encoder_dsc_2_lm_2_enc_2_intf(struct sde_encoder_virt *sde_enc)
{
	int pic_width, pic_height;
	int this_frame_slices;
	int intf_ip_w, enc_ip_w;
	int ich_res, dsc_common_mode;

	struct sde_encoder_phys *enc_master = sde_enc->cur_master;
	struct sde_hw_dsc *l_hw_dsc = sde_enc->hw_dsc[0];
	struct sde_hw_dsc *r_hw_dsc = sde_enc->hw_dsc[1];
	struct sde_hw_pingpong *l_hw_pp = sde_enc->hw_pp[0];
	struct sde_hw_pingpong *r_hw_pp = sde_enc->hw_pp[1];
	struct sde_hw_mdp *hw_mdp_top  = enc_master->hw_mdptop;
	struct msm_display_dsc_info *dsc =
		&sde_enc->disp_info.comp_info.dsc_info;

	if (l_hw_dsc == NULL || r_hw_dsc == NULL || hw_mdp_top == NULL ||
		l_hw_pp == NULL || r_hw_pp == NULL) {
		SDE_ERROR_ENC(sde_enc, "invalid params for DSC\n");
		return -EINVAL;
	}

	pic_width = dsc->pic_width * sde_enc->display_num_of_h_tiles;
	pic_height = dsc->pic_height;

	_sde_encoder_dsc_update_pic_dim(dsc, pic_width, pic_height);

	this_frame_slices = pic_width / dsc->slice_width;
	intf_ip_w = this_frame_slices * dsc->slice_width;

	intf_ip_w /= 2;
	_sde_encoder_dsc_pclk_param_calc(dsc, intf_ip_w);

	enc_ip_w = intf_ip_w;
	_sde_encoder_dsc_initial_line_calc(dsc, enc_ip_w);

	ich_res = _sde_encoder_dsc_ich_reset_override_needed(false, dsc);

	dsc_common_mode = DSC_MODE_SPLIT_PANEL;
	if (enc_master->intf_mode == INTF_MODE_VIDEO)
		dsc_common_mode |= DSC_MODE_VIDEO;

	SDE_DEBUG_ENC(sde_enc, "pic_w: %d pic_h: %d mode:%d\n",
		pic_width, pic_height, dsc_common_mode);
	SDE_EVT32(DRMID(&sde_enc->base), pic_width, pic_height,
			dsc_common_mode);

	_sde_encoder_dsc_pipe_cfg(l_hw_dsc, l_hw_pp, dsc, dsc_common_mode,
			ich_res);
	_sde_encoder_dsc_pipe_cfg(r_hw_dsc, r_hw_pp, dsc, dsc_common_mode,
			ich_res);

	return 0;
}

static int _sde_encoder_dsc_2_lm_2_enc_1_intf(struct sde_encoder_virt *sde_enc)
{
	int pic_width, pic_height;
	int this_frame_slices;
	int intf_ip_w, enc_ip_w;
	int ich_res, dsc_common_mode;

	struct sde_encoder_phys *enc_master = sde_enc->cur_master;
	struct sde_hw_dsc *l_hw_dsc = sde_enc->hw_dsc[0];
	struct sde_hw_dsc *r_hw_dsc = sde_enc->hw_dsc[1];
	struct sde_hw_pingpong *l_hw_pp = sde_enc->hw_pp[0];
	struct sde_hw_pingpong *r_hw_pp = sde_enc->hw_pp[1];
	struct sde_hw_mdp *hw_mdp_top  = enc_master->hw_mdptop;
	struct msm_display_dsc_info *dsc =
		&sde_enc->disp_info.comp_info.dsc_info;

	if (l_hw_dsc == NULL || r_hw_dsc == NULL || hw_mdp_top == NULL ||
					l_hw_pp == NULL || r_hw_pp == NULL) {
		SDE_ERROR_ENC(sde_enc, "invalid params for DSC\n");
		return -EINVAL;
	}

	pic_width = dsc->pic_width;
	pic_height = dsc->pic_height;
	_sde_encoder_dsc_update_pic_dim(dsc, pic_width, pic_height);

	this_frame_slices = pic_width / dsc->slice_width;
	intf_ip_w = this_frame_slices * dsc->slice_width;
	_sde_encoder_dsc_pclk_param_calc(dsc, intf_ip_w);

	/*
	 * 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;
	_sde_encoder_dsc_initial_line_calc(dsc, enc_ip_w);

	ich_res = _sde_encoder_dsc_ich_reset_override_needed(false, dsc);

	dsc_common_mode = DSC_MODE_MULTIPLEX | DSC_MODE_SPLIT_PANEL;
	if (enc_master->intf_mode == INTF_MODE_VIDEO)
		dsc_common_mode |= DSC_MODE_VIDEO;

	SDE_DEBUG_ENC(sde_enc, "pic_w: %d pic_h: %d mode:%d\n",
		pic_width, pic_height, dsc_common_mode);
	SDE_EVT32(DRMID(&sde_enc->base), pic_width, pic_height,
			dsc_common_mode);

	_sde_encoder_dsc_pipe_cfg(l_hw_dsc, l_hw_pp, dsc, dsc_common_mode,
			ich_res);
	_sde_encoder_dsc_pipe_cfg(r_hw_dsc, r_hw_pp, dsc, dsc_common_mode,
			ich_res);

	return 0;
}

static int _sde_encoder_dsc_setup(struct sde_encoder_virt *sde_enc)
{
	enum sde_rm_topology_name topology;
	struct drm_connector *drm_conn = sde_enc->phys_encs[0]->connector;
	int ret = 0;

	topology = sde_connector_get_topology_name(drm_conn);
	if (topology == SDE_RM_TOPOLOGY_UNKNOWN) {
		SDE_ERROR_ENC(sde_enc, "topology not set yet\n");
		return -EINVAL;
	}

	SDE_DEBUG_ENC(sde_enc, "\n");
	SDE_EVT32(DRMID(&sde_enc->base));

	switch (topology) {
	case SDE_RM_TOPOLOGY_SINGLEPIPE:
		ret = _sde_encoder_dsc_1_lm_1_enc_1_intf(sde_enc);
		break;
	case SDE_RM_TOPOLOGY_DUALPIPEMERGE:
		ret = _sde_encoder_dsc_2_lm_2_enc_1_intf(sde_enc);
		break;
	case SDE_RM_TOPOLOGY_DUALPIPE:
		ret = _sde_encoder_dsc_2_lm_2_enc_2_intf(sde_enc);
		break;
	case SDE_RM_TOPOLOGY_PPSPLIT:
	default:
		SDE_ERROR_ENC(sde_enc, "No DSC support for topology %d",
				topology);
		return -EINVAL;
	};

	return ret;
}

static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc,
				      struct drm_display_mode *mode,
				      struct drm_display_mode *adj_mode)
@@ -307,7 +632,7 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc,
	struct sde_kms *sde_kms;
	struct list_head *connector_list;
	struct drm_connector *conn = NULL, *conn_iter;
	struct sde_rm_hw_iter pp_iter;
	struct sde_rm_hw_iter dsc_iter, pp_iter;
	int i = 0, ret;

	if (!drm_enc) {
@@ -353,6 +678,14 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc,
		sde_enc->hw_pp[i] = (struct sde_hw_pingpong *) pp_iter.hw;
	}

	sde_rm_init_hw_iter(&dsc_iter, drm_enc->base.id, SDE_HW_BLK_DSC);
	for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
		sde_enc->hw_dsc[i] = NULL;
		if (!sde_rm_get_hw(&sde_kms->rm, &dsc_iter))
			break;
		sde_enc->hw_dsc[i] = (struct sde_hw_dsc *) dsc_iter.hw;
	}

	for (i = 0; i < sde_enc->num_phys_encs; i++) {
		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];

@@ -376,6 +709,7 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc)
	struct msm_drm_private *priv;
	struct sde_kms *sde_kms;
	int i = 0;
	int ret = 0;

	if (!drm_enc) {
		SDE_ERROR("invalid encoder\n");
@@ -417,6 +751,13 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc)
		SDE_ERROR("virt encoder has no master! num_phys %d\n", i);
	else if (sde_enc->cur_master->ops.enable)
		sde_enc->cur_master->ops.enable(sde_enc->cur_master);

	if (_sde_is_dsc_enabled(sde_enc)) {
		ret = _sde_encoder_dsc_setup(sde_enc);
		if (ret)
			SDE_ERROR_ENC(sde_enc, "failed to setup DSC: %d\n",
					ret);
	}
}

static void sde_encoder_virt_disable(struct drm_encoder *drm_enc)
@@ -1285,6 +1626,8 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc,

	SDE_DEBUG("dsi_info->num_of_h_tiles %d\n", disp_info->num_of_h_tiles);

	phys_params.comp_type = disp_info->comp_info.comp_type;

	mutex_lock(&sde_enc->enc_lock);
	for (i = 0; i < disp_info->num_of_h_tiles && !ret; i++) {
		/*
+2 −0
Original line number Diff line number Diff line
@@ -33,12 +33,14 @@
 * @intfs:	Interfaces this encoder is using, INTF_MODE_NONE if unused
 * @wbs:	Writebacks this encoder is using, INTF_MODE_NONE if unused
 * @needs_cdm:	Encoder requests a CDM based on pixel format conversion needs
 * @needs_dsc:	Request to allocate DSC block
 * @display_num_of_h_tiles:
 */
struct sde_encoder_hw_resources {
	enum sde_intf_mode intfs[INTF_MAX];
	enum sde_intf_mode wbs[WB_MAX];
	bool needs_cdm;
	bool needs_dsc;
	u32 display_num_of_h_tiles;
};

Loading