1 /*
2  * Copyright 2012-15 Advanced Micro Devices, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: AMD
23  *
24  */
25 
26 #include "reg_helper.h"
27 #include "dcn10_mpc.h"
28 #include "dc.h"
29 #include "mem_input.h"
30 
31 #define REG(reg)\
32 	mpc10->mpc_regs->reg
33 
34 #define CTX \
35 	mpc10->base.ctx
36 
37 #undef FN
38 #define FN(reg_name, field_name) \
39 	mpc10->mpc_shift->field_name, mpc10->mpc_mask->field_name
40 
41 #define MODE_TOP_ONLY 1
42 #define MODE_BLEND 3
43 #define BLND_PP_ALPHA 0
44 #define BLND_GLOBAL_ALPHA 2
45 
46 
47 static void mpc10_set_bg_color(
48 		struct dcn10_mpc *mpc10,
49 		struct tg_color *bg_color,
50 		int id)
51 {
52 	/* mpc color is 12 bit.  tg_color is 10 bit */
53 	/* todo: might want to use 16 bit to represent color and have each
54 	 * hw block translate to correct color depth.
55 	 */
56 	uint32_t bg_r_cr = bg_color->color_r_cr << 2;
57 	uint32_t bg_g_y = bg_color->color_g_y << 2;
58 	uint32_t bg_b_cb = bg_color->color_b_cb << 2;
59 
60 	REG_SET(MPCC_BG_R_CR[id], 0,
61 			MPCC_BG_R_CR, bg_r_cr);
62 	REG_SET(MPCC_BG_G_Y[id], 0,
63 			MPCC_BG_G_Y, bg_g_y);
64 	REG_SET(MPCC_BG_B_CB[id], 0,
65 			MPCC_BG_B_CB, bg_b_cb);
66 }
67 
68 void mpc10_assert_idle_mpcc(struct mpc *mpc, int id)
69 {
70 	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
71 
72 	ASSERT(!(mpc10->mpcc_in_use_mask & 1 << id));
73 	REG_WAIT(MPCC_STATUS[id],
74 			MPCC_IDLE, 1,
75 			1, 100000);
76 }
77 
78 static int mpc10_get_idle_mpcc_id(struct dcn10_mpc *mpc10)
79 {
80 	int i;
81 	int last_free_mpcc_id = -1;
82 
83 	for (i = 0; i < mpc10->num_mpcc; i++) {
84 		uint32_t is_idle = 0;
85 
86 		if (mpc10->mpcc_in_use_mask & 1 << i)
87 			continue;
88 
89 		last_free_mpcc_id = i;
90 		REG_GET(MPCC_STATUS[i], MPCC_IDLE, &is_idle);
91 		if (is_idle)
92 			return i;
93 	}
94 
95 	/* This assert should never trigger, we have mpcc leak if it does */
96 	ASSERT(last_free_mpcc_id != -1);
97 
98 	mpc10_assert_idle_mpcc(&mpc10->base, last_free_mpcc_id);
99 	return last_free_mpcc_id;
100 }
101 
102 static void mpc10_assert_mpcc_idle_before_connect(struct dcn10_mpc *mpc10, int id)
103 {
104 	unsigned int top_sel, mpc_busy, mpc_idle;
105 
106 	REG_GET(MPCC_TOP_SEL[id],
107 			MPCC_TOP_SEL, &top_sel);
108 
109 	if (top_sel == 0xf) {
110 		REG_GET_2(MPCC_STATUS[id],
111 				MPCC_BUSY, &mpc_busy,
112 				MPCC_IDLE, &mpc_idle);
113 
114 		ASSERT(mpc_busy == 0);
115 		ASSERT(mpc_idle == 1);
116 	}
117 }
118 
119 void mpc10_mpcc_remove(
120 		struct mpc *mpc,
121 		struct mpc_tree_cfg *tree_cfg,
122 		int opp_id,
123 		int dpp_id)
124 {
125 	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
126 	int mpcc_id, z_idx;
127 
128 	/* find z_idx for the dpp to be removed */
129 	for (z_idx = 0; z_idx < tree_cfg->num_pipes; z_idx++)
130 		if (tree_cfg->dpp[z_idx] == dpp_id)
131 			break;
132 
133 	if (z_idx == tree_cfg->num_pipes) {
134 		/* In case of resume from S3/S4, remove mpcc from bios left over */
135 		REG_SET(MPCC_OPP_ID[dpp_id], 0,
136 				MPCC_OPP_ID, 0xf);
137 		REG_SET(MPCC_TOP_SEL[dpp_id], 0,
138 				MPCC_TOP_SEL, 0xf);
139 		REG_SET(MPCC_BOT_SEL[dpp_id], 0,
140 				MPCC_BOT_SEL, 0xf);
141 		return;
142 	}
143 
144 	mpcc_id = tree_cfg->mpcc[z_idx];
145 
146 	REG_SET(MPCC_OPP_ID[mpcc_id], 0,
147 			MPCC_OPP_ID, 0xf);
148 	REG_SET(MPCC_TOP_SEL[mpcc_id], 0,
149 			MPCC_TOP_SEL, 0xf);
150 	REG_SET(MPCC_BOT_SEL[mpcc_id], 0,
151 			MPCC_BOT_SEL, 0xf);
152 
153 	if (z_idx > 0) {
154 		int top_mpcc_id = tree_cfg->mpcc[z_idx - 1];
155 
156 		if (z_idx + 1 < tree_cfg->num_pipes)
157 			/* mpcc to be removed is in the middle of the tree */
158 			REG_SET(MPCC_BOT_SEL[top_mpcc_id], 0,
159 					MPCC_BOT_SEL, tree_cfg->mpcc[z_idx + 1]);
160 		else {
161 			/* mpcc to be removed is at the bottom of the tree */
162 			REG_SET(MPCC_BOT_SEL[top_mpcc_id], 0,
163 					MPCC_BOT_SEL, 0xf);
164 			REG_UPDATE(MPCC_CONTROL[top_mpcc_id],
165 					MPCC_MODE, MODE_TOP_ONLY);
166 		}
167 	} else if (tree_cfg->num_pipes > 1)
168 		/* mpcc to be removed is at the top of the tree */
169 		REG_SET(MUX[opp_id], 0,
170 				MPC_OUT_MUX, tree_cfg->mpcc[z_idx + 1]);
171 	else
172 		/* mpcc to be removed is the only one in the tree */
173 		REG_SET(MUX[opp_id], 0, MPC_OUT_MUX, 0xf);
174 
175 	/* mark this mpcc as not in use */
176 	mpc10->mpcc_in_use_mask &= ~(1 << mpcc_id);
177 	tree_cfg->num_pipes--;
178 	for (; z_idx < tree_cfg->num_pipes; z_idx++) {
179 		tree_cfg->dpp[z_idx] = tree_cfg->dpp[z_idx + 1];
180 		tree_cfg->mpcc[z_idx] = tree_cfg->mpcc[z_idx + 1];
181 	}
182 	tree_cfg->dpp[tree_cfg->num_pipes] = 0xdeadbeef;
183 	tree_cfg->mpcc[tree_cfg->num_pipes] = 0xdeadbeef;
184 }
185 
186 static void mpc10_add_to_tree_cfg(
187 	struct mpc *mpc,
188 	struct mpcc_cfg *cfg,
189 	int mpcc_id)
190 {
191 	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
192 	int mpcc_mode = MODE_TOP_ONLY;
193 	int position = cfg->z_index;
194 	struct mpc_tree_cfg *tree_cfg = cfg->tree_cfg;
195 	int alpha_blnd_mode = cfg->per_pixel_alpha ?
196 			BLND_PP_ALPHA : BLND_GLOBAL_ALPHA;
197 	int z_idx;
198 
199 	REG_SET(MPCC_OPP_ID[mpcc_id], 0,
200 			MPCC_OPP_ID, cfg->opp_id);
201 
202 	REG_SET(MPCC_TOP_SEL[mpcc_id], 0,
203 			MPCC_TOP_SEL, cfg->dpp_id);
204 
205 	if (position == 0) {
206 		/* idle dpp/mpcc is added to the top layer of tree */
207 
208 		if (tree_cfg->num_pipes > 0) {
209 			/* get instance of previous top mpcc */
210 			int prev_top_mpcc_id = tree_cfg->mpcc[0];
211 
212 			REG_SET(MPCC_BOT_SEL[mpcc_id], 0,
213 					MPCC_BOT_SEL, prev_top_mpcc_id);
214 			mpcc_mode = MODE_BLEND;
215 		}
216 
217 		/* opp will get new output. from new added mpcc */
218 		REG_SET(MUX[cfg->opp_id], 0, MPC_OUT_MUX, mpcc_id);
219 
220 	} else if (position == tree_cfg->num_pipes) {
221 		/* idle dpp/mpcc is added to the bottom layer of tree */
222 
223 		/* get instance of previous bottom mpcc, set to middle layer */
224 		int prev_bot_mpcc_id = tree_cfg->mpcc[tree_cfg->num_pipes - 1];
225 
226 		REG_SET(MPCC_BOT_SEL[prev_bot_mpcc_id], 0,
227 				MPCC_BOT_SEL, mpcc_id);
228 		REG_UPDATE(MPCC_CONTROL[prev_bot_mpcc_id],
229 				MPCC_MODE, MODE_BLEND);
230 
231 		/* mpcc_id become new bottom mpcc*/
232 		REG_SET(MPCC_BOT_SEL[mpcc_id], 0,
233 				MPCC_BOT_SEL, 0xf);
234 
235 	} else {
236 		/* idle dpp/mpcc is added to middle of tree */
237 		int above_mpcc_id = tree_cfg->mpcc[position - 1];
238 		int below_mpcc_id = tree_cfg->mpcc[position];
239 
240 		/* mpcc above new mpcc_id has new bottom mux*/
241 		REG_SET(MPCC_BOT_SEL[above_mpcc_id], 0,
242 				MPCC_BOT_SEL, mpcc_id);
243 		REG_UPDATE(MPCC_CONTROL[above_mpcc_id],
244 				MPCC_MODE, MODE_BLEND);
245 
246 		/* mpcc_id bottom mux is from below mpcc*/
247 		REG_SET(MPCC_BOT_SEL[mpcc_id], 0,
248 				MPCC_BOT_SEL, below_mpcc_id);
249 		mpcc_mode = MODE_BLEND;
250 	}
251 
252 	REG_SET_4(MPCC_CONTROL[mpcc_id], 0xffffffff,
253 		MPCC_MODE, mpcc_mode,
254 		MPCC_ALPHA_BLND_MODE, alpha_blnd_mode,
255 		MPCC_ALPHA_MULTIPLIED_MODE, cfg->pre_multiplied_alpha,
256 		MPCC_BLND_ACTIVE_OVERLAP_ONLY, false);
257 
258 	/* update mpc_tree_cfg with new mpcc */
259 	for (z_idx = tree_cfg->num_pipes; z_idx > position; z_idx--) {
260 		tree_cfg->dpp[z_idx] = tree_cfg->dpp[z_idx - 1];
261 		tree_cfg->mpcc[z_idx] = tree_cfg->mpcc[z_idx - 1];
262 	}
263 	tree_cfg->dpp[position] = cfg->dpp_id;
264 	tree_cfg->mpcc[position] = mpcc_id;
265 	tree_cfg->num_pipes++;
266 }
267 
268 int mpc10_mpcc_add(struct mpc *mpc, struct mpcc_cfg *cfg)
269 {
270 	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
271 	int mpcc_id, z_idx;
272 
273 	ASSERT(cfg->z_index < mpc10->num_mpcc);
274 
275 	/* check in dpp already exists in mpc tree */
276 	for (z_idx = 0; z_idx < cfg->tree_cfg->num_pipes; z_idx++)
277 		if (cfg->tree_cfg->dpp[z_idx] == cfg->dpp_id)
278 			break;
279 	if (z_idx == cfg->tree_cfg->num_pipes) {
280 		ASSERT(cfg->z_index <= cfg->tree_cfg->num_pipes);
281 		mpcc_id = mpc10_get_idle_mpcc_id(mpc10);
282 
283 		/*
284 		 * TODO: remove hack
285 		 * Note: currently there is a bug in init_hw such that
286 		 * on resume from hibernate, BIOS sets up MPCC0, and
287 		 * we do mpcc_remove but the mpcc cannot go to idle
288 		 * after remove. This cause us to pick mpcc1 here,
289 		 * which causes a pstate hang for yet unknown reason.
290 		 */
291 		mpcc_id = cfg->dpp_id;
292 		/* end hack*/
293 
294 		ASSERT(!(mpc10->mpcc_in_use_mask & 1 << mpcc_id));
295 
296 		if (mpc->ctx->dc->debug.sanity_checks)
297 			mpc10_assert_mpcc_idle_before_connect(mpc10, mpcc_id);
298 	} else {
299 		ASSERT(cfg->z_index < cfg->tree_cfg->num_pipes);
300 		mpcc_id = cfg->tree_cfg->mpcc[z_idx];
301 		mpc10_mpcc_remove(mpc, cfg->tree_cfg, cfg->opp_id, cfg->dpp_id);
302 	}
303 
304 	/* add dpp/mpcc pair to mpc_tree_cfg and update mpcc registers */
305 	mpc10_add_to_tree_cfg(mpc, cfg, mpcc_id);
306 
307 	/* set background color */
308 	mpc10_set_bg_color(mpc10, &cfg->black_color, mpcc_id);
309 
310 	/* mark this mpcc as in use */
311 	mpc10->mpcc_in_use_mask |= 1 << mpcc_id;
312 
313 	return mpcc_id;
314 }
315 
316 void mpc10_update_blend_mode(
317 		struct mpc *mpc,
318 		struct mpcc_cfg *cfg)
319 {
320 	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
321 	int mpcc_id, z_idx;
322 	int alpha_blnd_mode = cfg->per_pixel_alpha ?
323 			BLND_PP_ALPHA : BLND_GLOBAL_ALPHA;
324 
325 	/* find z_idx for the dpp that requires blending mode update*/
326 	for (z_idx = 0; z_idx < cfg->tree_cfg->num_pipes; z_idx++)
327 		if (cfg->tree_cfg->dpp[z_idx] == cfg->dpp_id)
328 			break;
329 
330 	ASSERT(z_idx < cfg->tree_cfg->num_pipes);
331 	mpcc_id = cfg->tree_cfg->mpcc[z_idx];
332 
333 	REG_UPDATE_2(MPCC_CONTROL[mpcc_id],
334 			MPCC_ALPHA_BLND_MODE, alpha_blnd_mode,
335 			MPCC_ALPHA_MULTIPLIED_MODE, cfg->pre_multiplied_alpha);
336 }
337 
338 const struct mpc_funcs dcn10_mpc_funcs = {
339 		.add = mpc10_mpcc_add,
340 		.remove = mpc10_mpcc_remove,
341 		.wait_for_idle = mpc10_assert_idle_mpcc,
342 		.update_blend_mode = mpc10_update_blend_mode,
343 };
344 
345 void dcn10_mpc_construct(struct dcn10_mpc *mpc10,
346 	struct dc_context *ctx,
347 	const struct dcn_mpc_registers *mpc_regs,
348 	const struct dcn_mpc_shift *mpc_shift,
349 	const struct dcn_mpc_mask *mpc_mask,
350 	int num_mpcc)
351 {
352 	mpc10->base.ctx = ctx;
353 
354 	mpc10->base.funcs = &dcn10_mpc_funcs;
355 
356 	mpc10->mpc_regs = mpc_regs;
357 	mpc10->mpc_shift = mpc_shift;
358 	mpc10->mpc_mask = mpc_mask;
359 
360 	mpc10->mpcc_in_use_mask = 0;
361 	mpc10->num_mpcc = num_mpcc;
362 }
363 
364