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

Commit 33113c04 authored by Yuan Zhao's avatar Yuan Zhao
Browse files

drm/msm: add YUV format support for HDMI



This change adds YUV format support for HDMI.
Also, chooses the best mode for turning on the
sink based on sink source capabilities. This
can be either RGB or YUV. For YUV formats adjust
the pixel clock and also configure the relevant
hardware blocks in SDE.

Change-Id: I48a36a991c194badb3ddca4bbf5bcbc21d838b8f
Signed-off-by: default avatarYuan Zhao <yzhao@codeaurora.org>
parent fcef1e04
Loading
Loading
Loading
Loading
+24 −6
Original line number Diff line number Diff line
@@ -577,6 +577,10 @@ static void _sde_hdmi_bridge_set_avi_infoframe(struct hdmi *hdmi,
	struct hdmi_avi_infoframe info;

	drm_hdmi_avi_infoframe_from_display_mode(&info, mode);

	if (mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)
		info.colorspace = HDMI_COLORSPACE_YUV420;

	hdmi_avi_infoframe_pack(&info, avi_iframe, sizeof(avi_iframe));
	checksum = avi_iframe[HDMI_INFOFRAME_HEADER_SIZE - 1];

@@ -711,6 +715,15 @@ static u32 _sde_hdmi_choose_best_format(struct hdmi *hdmi,
	if (dc_format & MSM_MODE_FLAG_RGB444_DC_ENABLE)
		return (MSM_MODE_FLAG_COLOR_FORMAT_RGB444
			| MSM_MODE_FLAG_RGB444_DC_ENABLE);
	else if (dc_format & MSM_MODE_FLAG_YUV420_DC_ENABLE)
		return (MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420
			| MSM_MODE_FLAG_YUV420_DC_ENABLE);
	else if (mode->flags & DRM_MODE_FLAG_SUPPORTS_RGB)
		return MSM_MODE_FLAG_COLOR_FORMAT_RGB444;
	else if (mode->flags & DRM_MODE_FLAG_SUPPORTS_YUV)
		return MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420;

	SDE_ERROR("Can't get available best display format\n");

	return MSM_MODE_FLAG_COLOR_FORMAT_RGB444;
}
@@ -726,13 +739,13 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
	struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display;
	int hstart, hend, vstart, vend;
	uint32_t frame_ctrl;
	u32 div = 0;

	mode = adjusted_mode;

	display->dc_enable = mode->private_flags &
				(MSM_MODE_FLAG_RGB444_DC_ENABLE |
				 MSM_MODE_FLAG_YUV420_DC_ENABLE);

	/* compute pixclock as per color format and bit depth */
	hdmi->pixclock = sde_hdmi_calc_pixclk(
				mode->clock * HDMI_KHZ_TO_HZ,
@@ -741,18 +754,21 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
	SDE_DEBUG("Actual PCLK: %lu, Mode PCLK: %d\n",
		hdmi->pixclock, mode->clock);

	hstart = mode->htotal - mode->hsync_start;
	hend   = mode->htotal - mode->hsync_start + mode->hdisplay;
	if (mode->private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)
		div = 1;

	hstart = (mode->htotal - mode->hsync_start) >> div;
	hend   = (mode->htotal - mode->hsync_start + mode->hdisplay) >> div;

	vstart = mode->vtotal - mode->vsync_start - 1;
	vend   = mode->vtotal - mode->vsync_start + mode->vdisplay - 1;

	DRM_DEBUG(
	SDE_DEBUG(
		"htotal=%d, vtotal=%d, hstart=%d, hend=%d, vstart=%d, vend=%d",
		mode->htotal, mode->vtotal, hstart, hend, vstart, vend);

	hdmi_write(hdmi, REG_HDMI_TOTAL,
			SDE_HDMI_TOTAL_H_TOTAL(mode->htotal - 1) |
			SDE_HDMI_TOTAL_H_TOTAL((mode->htotal >> div) - 1) |
			SDE_HDMI_TOTAL_V_TOTAL(mode->vtotal - 1));

	hdmi_write(hdmi, REG_HDMI_ACTIVE_HSYNC,
@@ -814,6 +830,8 @@ static bool _sde_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,

	adjusted_mode->private_flags |=
		_sde_hdmi_choose_best_format(hdmi, adjusted_mode);
	SDE_DEBUG("Adjusted mode private flags: 0x%x\n",
		  adjusted_mode->private_flags);

	return true;
}
+132 −0
Original line number Diff line number Diff line
@@ -55,6 +55,33 @@

#define MAX_CHANNELS_PER_ENC 2

/* rgb to yuv color space conversion matrix */
static struct sde_csc_cfg sde_csc_10bit_convert[SDE_MAX_CSC] = {
	[SDE_CSC_RGB2YUV_601L] = {
		{
			TO_S15D16(0x0083), TO_S15D16(0x0102), TO_S15D16(0x0032),
			TO_S15D16(0xffb4), TO_S15D16(0xff6b), TO_S15D16(0x00e1),
			TO_S15D16(0x00e1), TO_S15D16(0xff44), TO_S15D16(0xffdb),
		},
		{ 0x0, 0x0, 0x0,},
		{ 0x0040, 0x0200, 0x0200,},
		{ 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
		{ 0x0040, 0x03ac, 0x0040, 0x03c0, 0x0040, 0x03c0,},
	},

	[SDE_CSC_RGB2YUV_601FR] = {
		{
			TO_S15D16(0x0099), TO_S15D16(0x012d), TO_S15D16(0x003a),
			TO_S15D16(0xffaa), TO_S15D16(0xff56), TO_S15D16(0x0100),
			TO_S15D16(0x0100), TO_S15D16(0xff2a), TO_S15D16(0xffd6),
		},
		{ 0x0, 0x0, 0x0,},
		{ 0x0000, 0x0200, 0x0200,},
		{ 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
		{ 0x0, 0x3ff, 0x0, 0x3ff, 0x0, 0x3ff,},
	},
};

/**
 * struct sde_encoder_virt - virtual encoder. Container of one or more physical
 *	encoders. Virtual encoder manages one "logical" display. Physical
@@ -1374,3 +1401,108 @@ enum sde_intf_mode sde_encoder_get_intf_mode(struct drm_encoder *encoder)

	return INTF_MODE_NONE;
}

/**
 * sde_encoder_phys_setup_cdm - setup chroma down block
 * @phys_enc:	Pointer to physical encoder
 * @output_type: HDMI/WB
 * @format:	Output format
 * @roi: Output size
 */
void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc,
		const struct sde_format *format, u32 output_type,
		struct sde_rect *roi)
{
	struct drm_encoder *encoder = phys_enc->parent;
	struct sde_encoder_virt *sde_enc = NULL;
	struct sde_hw_cdm *hw_cdm = phys_enc->hw_cdm;
	struct sde_hw_cdm_cfg *cdm_cfg = &phys_enc->cdm_cfg;
	int ret;
	u32 csc_type = 0;

	if (!encoder) {
		SDE_ERROR("invalid encoder\n");
		return;
	}
	sde_enc = to_sde_encoder_virt(encoder);

	if (!SDE_FORMAT_IS_YUV(format)) {
		SDE_DEBUG_ENC(sde_enc, "[cdm_disable fmt:%x]\n",
				format->base.pixel_format);

		if (hw_cdm && hw_cdm->ops.disable)
			hw_cdm->ops.disable(hw_cdm);

		return;
	}

	memset(cdm_cfg, 0, sizeof(struct sde_hw_cdm_cfg));

	cdm_cfg->output_width = roi->w;
	cdm_cfg->output_height = roi->h;
	cdm_cfg->output_fmt = format;
	cdm_cfg->output_type = output_type;
	cdm_cfg->output_bit_depth = SDE_FORMAT_IS_DX(format) ?
		CDM_CDWN_OUTPUT_10BIT : CDM_CDWN_OUTPUT_8BIT;

	/* enable 10 bit logic */
	switch (cdm_cfg->output_fmt->chroma_sample) {
	case SDE_CHROMA_RGB:
		cdm_cfg->h_cdwn_type = CDM_CDWN_DISABLE;
		cdm_cfg->v_cdwn_type = CDM_CDWN_DISABLE;
		break;
	case SDE_CHROMA_H2V1:
		cdm_cfg->h_cdwn_type = CDM_CDWN_COSITE;
		cdm_cfg->v_cdwn_type = CDM_CDWN_DISABLE;
		break;
	case SDE_CHROMA_420:
		cdm_cfg->h_cdwn_type = CDM_CDWN_COSITE;
		cdm_cfg->v_cdwn_type = CDM_CDWN_OFFSITE;
		break;
	case SDE_CHROMA_H1V2:
	default:
		SDE_ERROR("unsupported chroma sampling type\n");
		cdm_cfg->h_cdwn_type = CDM_CDWN_DISABLE;
		cdm_cfg->v_cdwn_type = CDM_CDWN_DISABLE;
		break;
	}

	SDE_DEBUG_ENC(sde_enc, "[cdm_enable:%d,%d,%X,%d,%d,%d,%d]\n",
			cdm_cfg->output_width,
			cdm_cfg->output_height,
			cdm_cfg->output_fmt->base.pixel_format,
			cdm_cfg->output_type,
			cdm_cfg->output_bit_depth,
			cdm_cfg->h_cdwn_type,
			cdm_cfg->v_cdwn_type);

	if (output_type == CDM_CDWN_OUTPUT_HDMI)
		csc_type = SDE_CSC_RGB2YUV_601FR;
	else if (output_type == CDM_CDWN_OUTPUT_WB)
		csc_type = SDE_CSC_RGB2YUV_601L;

	if (hw_cdm && hw_cdm->ops.setup_csc_data) {
		ret = hw_cdm->ops.setup_csc_data(hw_cdm,
				&sde_csc_10bit_convert[csc_type]);
		if (ret < 0) {
			SDE_ERROR("failed to setup CSC %d\n", ret);
			return;
		}
	}

	if (hw_cdm && hw_cdm->ops.setup_cdwn) {
		ret = hw_cdm->ops.setup_cdwn(hw_cdm, cdm_cfg);
		if (ret < 0) {
			SDE_ERROR("failed to setup CDM %d\n", ret);
			return;
		}
	}

	if (hw_cdm && hw_cdm->ops.enable) {
		ret = hw_cdm->ops.enable(hw_cdm, cdm_cfg);
		if (ret < 0) {
			SDE_ERROR("failed to enable CDM %d\n", ret);
			return;
		}
	}
}
+2 −2
Original line number Diff line number Diff line
@@ -349,8 +349,8 @@ struct sde_encoder_phys *sde_encoder_phys_wb_init(
#endif

void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc,
		struct drm_framebuffer *fb, const struct sde_format *format,
		struct sde_rect *wb_roi);
		const struct sde_format *format, u32 output_type,
		struct sde_rect *roi);

/**
 * sde_encoder_helper_trigger_start - control start helper function
+50 −4
Original line number Diff line number Diff line
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -242,17 +242,20 @@ static void sde_encoder_phys_vid_setup_timing_engine(
	SDE_DEBUG_VIDENC(vid_enc, "enabling mode:\n");
	drm_mode_debug_printmodeline(&mode);

	if (phys_enc->split_role != ENC_ROLE_SOLO) {
	if (phys_enc->split_role != ENC_ROLE_SOLO ||
	    (mode.private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)) {
		mode.hdisplay >>= 1;
		mode.htotal >>= 1;
		mode.hsync_start >>= 1;
		mode.hsync_end >>= 1;
		mode.hskew >>= 1;

		SDE_DEBUG_VIDENC(vid_enc,
			"split_role %d, halve horizontal %d %d %d %d\n",
			"split_role %d, halve horizontal %d %d %d %d %d\n",
			phys_enc->split_role,
			mode.hdisplay, mode.htotal,
			mode.hsync_start, mode.hsync_end);
			mode.hsync_start, mode.hsync_end,
			mode.hskew);
	}

	drm_mode_to_intf_timing_params(vid_enc, &mode, &timing_params);
@@ -407,6 +410,9 @@ static void sde_encoder_phys_vid_mode_set(
		return;
	}

	phys_enc->hw_ctl = NULL;
	phys_enc->hw_cdm = NULL;

	rm = &phys_enc->sde_kms->rm;
	vid_enc = to_sde_encoder_phys_vid(phys_enc);
	phys_enc->cached_mode = *adj_mode;
@@ -427,6 +433,20 @@ static void sde_encoder_phys_vid_mode_set(
		phys_enc->hw_ctl = NULL;
		return;
	}

	/* CDM is optional */
	sde_rm_init_hw_iter(&iter, phys_enc->parent->base.id, SDE_HW_BLK_CDM);
	for (i = 0; i <= instance; i++) {
		sde_rm_get_hw(rm, &iter);
		if (i == instance)
			phys_enc->hw_cdm = (struct sde_hw_cdm *) iter.hw;
	}

	if (IS_ERR(phys_enc->hw_cdm)) {
		SDE_ERROR("CDM required but not allocated: %ld\n",
				PTR_ERR(phys_enc->hw_cdm));
		phys_enc->hw_cdm = NULL;
	}
}

static int sde_encoder_phys_vid_control_vblank_irq(
@@ -477,6 +497,9 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc)
	struct sde_encoder_phys_vid *vid_enc;
	struct sde_hw_intf *intf;
	struct sde_hw_ctl *ctl;
	struct sde_hw_cdm *hw_cdm = NULL;
	struct drm_display_mode mode;
	const struct sde_format *fmt = NULL;
	u32 flush_mask = 0;
	int ret;

@@ -485,7 +508,9 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc)
		SDE_ERROR("invalid encoder/device\n");
		return;
	}
	hw_cdm = phys_enc->hw_cdm;
	priv = phys_enc->parent->dev->dev_private;
	mode = phys_enc->cached_mode;

	vid_enc = to_sde_encoder_phys_vid(phys_enc);
	intf = vid_enc->hw_intf;
@@ -520,7 +545,21 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc)
		goto end;
	}

	if (mode.private_flags & MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420)
		fmt = sde_get_sde_format(DRM_FORMAT_YUV420);

	if (fmt) {
		struct sde_rect hdmi_roi;

		hdmi_roi.w = mode.hdisplay;
		hdmi_roi.h = mode.vdisplay;
		sde_encoder_phys_setup_cdm(phys_enc, fmt,
			CDM_CDWN_OUTPUT_HDMI, &hdmi_roi);
	}

	ctl->ops.get_bitmask_intf(ctl, &flush_mask, intf->idx);
	if (ctl->ops.get_bitmask_cdm && hw_cdm)
		ctl->ops.get_bitmask_cdm(ctl, &flush_mask, hw_cdm->idx);
	ctl->ops.update_pending_flush(ctl, flush_mask);

	SDE_DEBUG_VIDENC(vid_enc, "update pending flush ctl %d flush_mask %x\n",
@@ -569,6 +608,8 @@ static void sde_encoder_phys_vid_get_hw_resources(

	SDE_DEBUG_VIDENC(vid_enc, "\n");
	hw_res->intfs[vid_enc->hw_intf->idx - INTF_0] = INTF_MODE_VIDEO;
	hw_res->needs_cdm = true;
	SDE_DEBUG_DRIVER("[vid] needs_cdm=%d\n", hw_res->needs_cdm);
}

static int sde_encoder_phys_vid_wait_for_vblank(
@@ -713,6 +754,11 @@ static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc)
		SDE_ERROR_VIDENC(vid_enc, "invalid vblank refcount %d\n",
				atomic_read(&phys_enc->vblank_refcount));

	if (phys_enc->hw_cdm && phys_enc->hw_cdm->ops.disable) {
		SDE_DEBUG_DRIVER("[cdm_disable]\n");
		phys_enc->hw_cdm->ops.disable(phys_enc->hw_cdm);
	}

	phys_enc->enable_state = SDE_ENC_DISABLED;
}

+2 −109
Original line number Diff line number Diff line
@@ -28,24 +28,6 @@

#define WBID(wb_enc) ((wb_enc) ? wb_enc->wb_dev->wb_idx : -1)

#define TO_S15D16(_x_)	((_x_) << 7)

/**
 * sde_rgb2yuv_601l - rgb to yuv color space conversion matrix
 *
 */
static struct sde_csc_cfg sde_encoder_phys_wb_rgb2yuv_601l = {
	{
		TO_S15D16(0x0083), TO_S15D16(0x0102), TO_S15D16(0x0032),
		TO_S15D16(0x1fb5), TO_S15D16(0x1f6c), TO_S15D16(0x00e1),
		TO_S15D16(0x00e1), TO_S15D16(0x1f45), TO_S15D16(0x1fdc)
	},
	{ 0x00, 0x00, 0x00 },
	{ 0x0040, 0x0200, 0x0200 },
	{ 0x000, 0x3ff, 0x000, 0x3ff, 0x000, 0x3ff },
	{ 0x040, 0x3ac, 0x040, 0x3c0, 0x040, 0x3c0 },
};

/**
 * sde_encoder_phys_wb_is_master - report wb always as master encoder
 */
@@ -104,96 +86,6 @@ static void sde_encoder_phys_wb_set_traffic_shaper(
	wb_cfg->ts_cfg.en = false;
}

/**
 * sde_encoder_phys_setup_cdm - setup chroma down block
 * @phys_enc:	Pointer to physical encoder
 * @fb:		Pointer to output framebuffer
 * @format:	Output format
 */
void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc,
		struct drm_framebuffer *fb, const struct sde_format *format,
		struct sde_rect *wb_roi)
{
	struct sde_hw_cdm *hw_cdm = phys_enc->hw_cdm;
	struct sde_hw_cdm_cfg *cdm_cfg = &phys_enc->cdm_cfg;
	int ret;

	if (!SDE_FORMAT_IS_YUV(format)) {
		SDE_DEBUG("[cdm_disable fmt:%x]\n",
				format->base.pixel_format);

		if (hw_cdm && hw_cdm->ops.disable)
			hw_cdm->ops.disable(hw_cdm);

		return;
	}

	memset(cdm_cfg, 0, sizeof(struct sde_hw_cdm_cfg));

	cdm_cfg->output_width = wb_roi->w;
	cdm_cfg->output_height = wb_roi->h;
	cdm_cfg->output_fmt = format;
	cdm_cfg->output_type = CDM_CDWN_OUTPUT_WB;
	cdm_cfg->output_bit_depth = SDE_FORMAT_IS_DX(format) ?
		CDM_CDWN_OUTPUT_10BIT : CDM_CDWN_OUTPUT_8BIT;

	/* enable 10 bit logic */
	switch (cdm_cfg->output_fmt->chroma_sample) {
	case SDE_CHROMA_RGB:
		cdm_cfg->h_cdwn_type = CDM_CDWN_DISABLE;
		cdm_cfg->v_cdwn_type = CDM_CDWN_DISABLE;
		break;
	case SDE_CHROMA_H2V1:
		cdm_cfg->h_cdwn_type = CDM_CDWN_COSITE;
		cdm_cfg->v_cdwn_type = CDM_CDWN_DISABLE;
		break;
	case SDE_CHROMA_420:
		cdm_cfg->h_cdwn_type = CDM_CDWN_COSITE;
		cdm_cfg->v_cdwn_type = CDM_CDWN_OFFSITE;
		break;
	case SDE_CHROMA_H1V2:
	default:
		SDE_ERROR("unsupported chroma sampling type\n");
		cdm_cfg->h_cdwn_type = CDM_CDWN_DISABLE;
		cdm_cfg->v_cdwn_type = CDM_CDWN_DISABLE;
		break;
	}

	SDE_DEBUG("[cdm_enable:%d,%d,%X,%d,%d,%d,%d]\n",
			cdm_cfg->output_width,
			cdm_cfg->output_height,
			cdm_cfg->output_fmt->base.pixel_format,
			cdm_cfg->output_type,
			cdm_cfg->output_bit_depth,
			cdm_cfg->h_cdwn_type,
			cdm_cfg->v_cdwn_type);

	if (hw_cdm && hw_cdm->ops.setup_csc_data) {
		ret = hw_cdm->ops.setup_csc_data(hw_cdm,
				&sde_encoder_phys_wb_rgb2yuv_601l);
		if (ret < 0) {
			SDE_ERROR("failed to setup CSC %d\n", ret);
			return;
		}
	}

	if (hw_cdm && hw_cdm->ops.setup_cdwn) {
		ret = hw_cdm->ops.setup_cdwn(hw_cdm, cdm_cfg);
		if (ret < 0) {
			SDE_ERROR("failed to setup CDM %d\n", ret);
			return;
		}
	}

	if (hw_cdm && hw_cdm->ops.enable) {
		ret = hw_cdm->ops.enable(hw_cdm, cdm_cfg);
		if (ret < 0) {
			SDE_ERROR("failed to enable CDM %d\n", ret);
			return;
		}
	}
}

/**
 * sde_encoder_phys_wb_setup_fb - setup output framebuffer
 * @phys_enc:	Pointer to physical encoder
@@ -520,7 +412,8 @@ static void sde_encoder_phys_wb_setup(

	sde_encoder_phys_wb_set_traffic_shaper(phys_enc);

	sde_encoder_phys_setup_cdm(phys_enc, fb, wb_enc->wb_fmt, wb_roi);
	sde_encoder_phys_setup_cdm(phys_enc, wb_enc->wb_fmt,
		CDM_CDWN_OUTPUT_WB, wb_roi);

	sde_encoder_phys_wb_setup_fb(phys_enc, fb, wb_roi);

Loading