mdp5_cmd_encoder.c 5.3 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-only
2 3 4 5
/*
 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
 */

6
#include <drm/drm_crtc.h>
7
#include <drm/drm_probe_helper.h>
8

9
#include "mdp5_kms.h"
10 11 12 13 14 15 16 17 18

static struct mdp5_kms *get_kms(struct drm_encoder *encoder)
{
	struct msm_drm_private *priv = encoder->dev->dev_private;
	return to_mdp5_kms(to_mdp_kms(priv->kms));
}

#define VSYNC_CLK_RATE 19200000
static int pingpong_tearcheck_setup(struct drm_encoder *encoder,
19
				    struct drm_display_mode *mode)
20 21 22 23 24
{
	struct mdp5_kms *mdp5_kms = get_kms(encoder);
	struct device *dev = encoder->dev->dev;
	u32 total_lines_x100, vclks_line, cfg;
	long vsync_clk_speed;
25
	struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc);
26
	int pp_id = mixer->pp;
27 28

	if (IS_ERR_OR_NULL(mdp5_kms->vsync_clk)) {
29
		DRM_DEV_ERROR(dev, "vsync_clk is not initialized\n");
30 31 32
		return -EINVAL;
	}

33
	total_lines_x100 = mode->vtotal * drm_mode_vrefresh(mode);
34
	if (!total_lines_x100) {
35
		DRM_DEV_ERROR(dev, "%s: vtotal(%d) or vrefresh(%d) is 0\n",
36
			      __func__, mode->vtotal, drm_mode_vrefresh(mode));
37 38 39 40 41
		return -EINVAL;
	}

	vsync_clk_speed = clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE);
	if (vsync_clk_speed <= 0) {
42
		DRM_DEV_ERROR(dev, "vsync_clk round rate failed %ld\n",
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
							vsync_clk_speed);
		return -EINVAL;
	}
	vclks_line = vsync_clk_speed * 100 / total_lines_x100;

	cfg = MDP5_PP_SYNC_CONFIG_VSYNC_COUNTER_EN
		| MDP5_PP_SYNC_CONFIG_VSYNC_IN_EN;
	cfg |= MDP5_PP_SYNC_CONFIG_VSYNC_COUNT(vclks_line);

	mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_CONFIG_VSYNC(pp_id), cfg);
	mdp5_write(mdp5_kms,
		REG_MDP5_PP_SYNC_CONFIG_HEIGHT(pp_id), 0xfff0);
	mdp5_write(mdp5_kms,
		REG_MDP5_PP_VSYNC_INIT_VAL(pp_id), mode->vdisplay);
	mdp5_write(mdp5_kms, REG_MDP5_PP_RD_PTR_IRQ(pp_id), mode->vdisplay + 1);
	mdp5_write(mdp5_kms, REG_MDP5_PP_START_POS(pp_id), mode->vdisplay);
	mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_THRESH(pp_id),
			MDP5_PP_SYNC_THRESH_START(4) |
			MDP5_PP_SYNC_THRESH_CONTINUE(4));

	return 0;
}

static int pingpong_tearcheck_enable(struct drm_encoder *encoder)
{
	struct mdp5_kms *mdp5_kms = get_kms(encoder);
69
	struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc);
70
	int pp_id = mixer->pp;
71 72 73 74 75
	int ret;

	ret = clk_set_rate(mdp5_kms->vsync_clk,
		clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE));
	if (ret) {
76
		DRM_DEV_ERROR(encoder->dev->dev,
77 78 79 80 81
			"vsync_clk clk_set_rate failed, %d\n", ret);
		return ret;
	}
	ret = clk_prepare_enable(mdp5_kms->vsync_clk);
	if (ret) {
82
		DRM_DEV_ERROR(encoder->dev->dev,
83 84 85 86 87 88 89 90 91 92 93 94
			"vsync_clk clk_prepare_enable failed, %d\n", ret);
		return ret;
	}

	mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 1);

	return 0;
}

static void pingpong_tearcheck_disable(struct drm_encoder *encoder)
{
	struct mdp5_kms *mdp5_kms = get_kms(encoder);
95
	struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc);
96
	int pp_id = mixer->pp;
97 98 99 100 101

	mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 0);
	clk_disable_unprepare(mdp5_kms->vsync_clk);
}

102 103 104
void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder,
			       struct drm_display_mode *mode,
			       struct drm_display_mode *adjusted_mode)
105 106 107
{
	mode = adjusted_mode;

108
	DBG("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode));
109
	pingpong_tearcheck_setup(encoder, mode);
110
	mdp5_crtc_set_pipeline(encoder->crtc);
111 112
}

113
void mdp5_cmd_encoder_disable(struct drm_encoder *encoder)
114
{
115
	struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder);
116
	struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl;
117
	struct mdp5_interface *intf = mdp5_cmd_enc->intf;
118
	struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc);
119 120 121 122 123 124

	if (WARN_ON(!mdp5_cmd_enc->enabled))
		return;

	pingpong_tearcheck_disable(encoder);

125
	mdp5_ctl_set_encoder_state(ctl, pipeline, false);
126
	mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf), true);
127 128 129 130

	mdp5_cmd_enc->enabled = false;
}

131
void mdp5_cmd_encoder_enable(struct drm_encoder *encoder)
132
{
133
	struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder);
134
	struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl;
135
	struct mdp5_interface *intf = mdp5_cmd_enc->intf;
136
	struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc);
137 138 139 140 141 142 143

	if (WARN_ON(mdp5_cmd_enc->enabled))
		return;

	if (pingpong_tearcheck_enable(encoder))
		return;

144
	mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf), true);
145

146
	mdp5_ctl_set_encoder_state(ctl, pipeline, true);
147 148 149 150 151

	mdp5_cmd_enc->enabled = true;
}

int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder,
152
				       struct drm_encoder *slave_encoder)
153
{
154
	struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder);
155
	struct mdp5_kms *mdp5_kms;
156
	struct device *dev;
157 158 159 160 161 162 163
	int intf_num;
	u32 data = 0;

	if (!encoder || !slave_encoder)
		return -EINVAL;

	mdp5_kms = get_kms(encoder);
164
	intf_num = mdp5_cmd_enc->intf->num;
165 166 167 168 169

	/* Switch slave encoder's trigger MUX, to use the master's
	 * start signal for the slave encoder
	 */
	if (intf_num == 1)
170
		data |= MDP5_SPLIT_DPL_UPPER_INTF2_SW_TRG_MUX;
171
	else if (intf_num == 2)
172
		data |= MDP5_SPLIT_DPL_UPPER_INTF1_SW_TRG_MUX;
173 174 175 176
	else
		return -EINVAL;

	/* Smart Panel, Sync mode */
177
	data |= MDP5_SPLIT_DPL_UPPER_SMART_PANEL;
178

179 180
	dev = &mdp5_kms->pdev->dev;

181
	/* Make sure clocks are on when connectors calling this function. */
182
	pm_runtime_get_sync(dev);
183
	mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_UPPER, data);
184

185 186 187
	mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_LOWER,
		   MDP5_SPLIT_DPL_LOWER_SMART_PANEL);
	mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_EN, 1);
R
Rob Clark 已提交
188
	pm_runtime_put_sync(dev);
189 190 191

	return 0;
}