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 
29 #define REG(reg)\
30 	mpc->mpc_regs->reg
31 
32 #define CTX \
33 	mpc->base.ctx
34 
35 #undef FN
36 #define FN(reg_name, field_name) \
37 	mpc->mpc_shift->field_name, mpc->mpc_mask->field_name
38 
39 /* Internal function to set mpc output mux */
40 static void set_output_mux(struct dcn10_mpc *mpc,
41 	uint8_t opp_id,
42 	uint8_t mpcc_id)
43 {
44 	if (mpcc_id != 0xf)
45 		REG_UPDATE(OPP_PIPE_CONTROL[opp_id],
46 				OPP_PIPE_CLOCK_EN, 1);
47 
48 	REG_SET(MUX[opp_id], 0,
49 			MPC_OUT_MUX, mpcc_id);
50 
51 /*	TODO: Move to post when ready.
52    if (mpcc_id == 0xf) {
53 		MPCC_REG_UPDATE(OPP_PIPE0_OPP_PIPE_CONTROL,
54 				OPP_PIPE_CLOCK_EN, 0);
55 	}
56 */
57 }
58 
59 static void set_blend_mode(struct dcn10_mpc *mpc,
60 	enum blend_mode mode,
61 	uint8_t mpcc_id)
62 {
63 	/* Enable per-pixel alpha on this pipe */
64 	if (mode == TOP_BLND)
65 		REG_UPDATE_3(MPCC_CONTROL[mpcc_id],
66 				MPCC_ALPHA_BLND_MODE, 0,
67 				MPCC_ALPHA_MULTIPLIED_MODE, 0,
68 				MPCC_BLND_ACTIVE_OVERLAP_ONLY, 0);
69 	else
70 		REG_UPDATE_3(MPCC_CONTROL[mpcc_id],
71 				MPCC_ALPHA_BLND_MODE, 0,
72 				MPCC_ALPHA_MULTIPLIED_MODE, 1,
73 				MPCC_BLND_ACTIVE_OVERLAP_ONLY, 1);
74 }
75 
76 void dcn10_set_mpc_background_color(struct dcn10_mpc *mpc,
77 	unsigned int mpcc_inst,
78 	struct tg_color *bg_color)
79 {
80 	/* mpc color is 12 bit.  tg_color is 10 bit */
81 	/* todo: might want to use 16 bit to represent color and have each
82 	 * hw block translate to correct color depth.
83 	 */
84 	uint32_t bg_r_cr = bg_color->color_r_cr << 2;
85 	uint32_t bg_g_y = bg_color->color_g_y << 2;
86 	uint32_t bg_b_cb = bg_color->color_b_cb << 2;
87 
88 	REG_SET(MPCC_BG_R_CR[mpcc_inst], 0,
89 			MPCC_BG_R_CR, bg_r_cr);
90 	REG_SET(MPCC_BG_G_Y[mpcc_inst], 0,
91 			MPCC_BG_G_Y, bg_g_y);
92 	REG_SET(MPCC_BG_B_CB[mpcc_inst], 0,
93 			MPCC_BG_B_CB, bg_b_cb);
94 }
95 
96 /* This function programs MPC tree configuration
97  * Assume it is the initial time to setup MPC tree_configure, means
98  * the instance of dpp/mpcc/opp specified in structure tree_cfg are
99  * in idle status.
100  * Before invoke this function, ensure that master lock of OPTC specified
101  * by opp_id is set.
102  *
103  * tree_cfg[in] - new MPC_TREE_CFG
104  */
105 
106 void dcn10_set_mpc_tree(struct dcn10_mpc *mpc,
107 	struct mpc_tree_cfg *tree_cfg)
108 {
109 	int i;
110 
111 	for (i = 0; i < tree_cfg->num_pipes; i++) {
112 		uint8_t mpcc_inst = tree_cfg->mpcc[i];
113 
114 		REG_SET(MPCC_OPP_ID[mpcc_inst], 0,
115 			MPCC_OPP_ID, tree_cfg->opp_id);
116 
117 		REG_SET(MPCC_TOP_SEL[mpcc_inst], 0,
118 			MPCC_TOP_SEL, tree_cfg->dpp[i]);
119 
120 		if (i == tree_cfg->num_pipes-1) {
121 			REG_SET(MPCC_BOT_SEL[mpcc_inst], 0,
122 				MPCC_BOT_SEL, 0xF);
123 
124 			/* MPCC_CONTROL->MPCC_MODE */
125 			REG_UPDATE(MPCC_CONTROL[mpcc_inst],
126 					MPCC_MODE, tree_cfg->mode);
127 		} else {
128 			REG_SET(MPCC_BOT_SEL[mpcc_inst], 0,
129 				MPCC_BOT_SEL, tree_cfg->dpp[i+1]);
130 
131 			/* MPCC_CONTROL->MPCC_MODE */
132 			REG_UPDATE(MPCC_CONTROL[mpcc_inst],
133 					MPCC_MODE, 3);
134 		}
135 
136 		if (i == 0)
137 			set_output_mux(
138 				mpc, tree_cfg->opp_id, mpcc_inst);
139 
140 		set_blend_mode(mpc, tree_cfg->mode, mpcc_inst);
141 	}
142 }
143 
144 void dcn10_set_mpc_passthrough(struct dcn10_mpc *mpc,
145 	uint8_t dpp_idx,
146 	uint8_t mpcc_idx,
147 	uint8_t opp_idx)
148 {
149 	struct mpc_tree_cfg tree_cfg = { 0 };
150 
151 	tree_cfg.num_pipes = 1;
152 	tree_cfg.opp_id = opp_idx;
153 	tree_cfg.mode = TOP_PASSTHRU;
154 	/* TODO: FPGA bring up one MPC has only 1 DPP and 1 MPCC
155 	 * For blend case, need fill mode DPP and cascade MPCC
156 	 */
157 	tree_cfg.dpp[0] = dpp_idx;
158 	tree_cfg.mpcc[0] = mpcc_idx;
159 	dcn10_set_mpc_tree(mpc, &tree_cfg);
160 }
161 
162 /*
163  * This is the function to remove current MPC tree specified by tree_cfg
164  * Before invoke this function, ensure that master lock of OPTC specified
165  * by opp_id is set.
166  *
167  *tree_cfg[in/out] - current MPC_TREE_CFG
168  */
169 void dcn10_delete_mpc_tree(struct dcn10_mpc *mpc,
170 	struct mpc_tree_cfg *tree_cfg)
171 {
172 	int i;
173 
174 	for (i = 0; i < tree_cfg->num_pipes; i++) {
175 		uint8_t mpcc_inst = tree_cfg->mpcc[i];
176 
177 		REG_SET(MPCC_OPP_ID[mpcc_inst], 0,
178 			MPCC_OPP_ID, 0xf);
179 
180 		REG_SET(MPCC_TOP_SEL[mpcc_inst], 0,
181 			MPCC_TOP_SEL, 0xf);
182 
183 		REG_SET(MPCC_BOT_SEL[mpcc_inst], 0,
184 			MPCC_BOT_SEL, 0xF);
185 
186 		/* add remove dpp/mpcc pair into pending list
187 		 * TODO FPGA AddToPendingList if empty from pseudo code
188 		 */
189 		tree_cfg->dpp[i] = 0xf;
190 		tree_cfg->mpcc[i] = 0xf;
191 	}
192 	set_output_mux(mpc, tree_cfg->opp_id, 0xf);
193 	tree_cfg->opp_id = 0xf;
194 	tree_cfg->num_pipes = 0;
195 }
196 
197 /* TODO FPGA: how to handle DPP?
198  * Function to remove one of pipe from MPC configure tree by dpp idx
199  * Before invoke this function, ensure that master lock of OPTC specified
200  * by opp_id is set
201  * This function can be invoke multiple times to remove more than 1 dpps.
202  *
203  * tree_cfg[in/out] - current MPC_TREE_CFG
204  * idx[in] - index of dpp from tree_cfg to be removed.
205  */
206 bool dcn10_remove_dpp(struct dcn10_mpc *mpc,
207 	struct mpc_tree_cfg *tree_cfg,
208 	uint8_t idx)
209 {
210 	int i;
211 	bool found = false;
212 
213 	/* find dpp_idx from dpp array of tree_cfg */
214 	for (i = 0; i < tree_cfg->num_pipes; i++) {
215 		if (tree_cfg->dpp[i] == idx) {
216 			found = true;
217 			break;
218 		}
219 	}
220 
221 	if (found) {
222 		/* add remove dpp/mpcc pair into pending list */
223 
224 		/* TODO FPGA AddToPendingList if empty from pseudo code
225 		 * AddToPendingList(tree_cfg->dpp[i],tree_cfg->mpcc[i]);
226 		 */
227 		uint8_t mpcc_inst = tree_cfg->mpcc[i];
228 
229 		REG_SET(MPCC_OPP_ID[mpcc_inst], 0,
230 				MPCC_OPP_ID, 0xf);
231 
232 		REG_SET(MPCC_TOP_SEL[mpcc_inst], 0,
233 				MPCC_TOP_SEL, 0xf);
234 
235 		REG_SET(MPCC_BOT_SEL[mpcc_inst], 0,
236 				MPCC_BOT_SEL, 0xF);
237 
238 		if (i == 0) {
239 			if (tree_cfg->num_pipes > 1)
240 				set_output_mux(mpc,
241 					tree_cfg->opp_id, tree_cfg->mpcc[i+1]);
242 			else
243 				set_output_mux(mpc, tree_cfg->opp_id, 0xf);
244 		} else if (i == tree_cfg->num_pipes-1) {
245 			mpcc_inst = tree_cfg->mpcc[i - 1];
246 
247 			REG_SET(MPCC_BOT_SEL[mpcc_inst], 0,
248 					MPCC_BOT_SEL, 0xF);
249 
250 			REG_UPDATE(MPCC_CONTROL[mpcc_inst],
251 					MPCC_MODE, tree_cfg->mode);
252 		} else {
253 			mpcc_inst = tree_cfg->mpcc[i - 1];
254 
255 			REG_SET(MPCC_BOT_SEL[mpcc_inst], 0,
256 				MPCC_BOT_SEL, tree_cfg->mpcc[i+1]);
257 		}
258 		set_blend_mode(mpc, tree_cfg->mode, mpcc_inst);
259 
260 		/* update tree_cfg structure */
261 		while (i < tree_cfg->num_pipes - 1) {
262 			tree_cfg->dpp[i] = tree_cfg->dpp[i+1];
263 			tree_cfg->mpcc[i] = tree_cfg->mpcc[i+1];
264 			i++;
265 		}
266 		tree_cfg->num_pipes--;
267 	}
268 	return found;
269 }
270 
271 /* TODO FPGA: how to handle DPP?
272  * Function to add DPP/MPCC pair into MPC configure tree by position.
273  * Before invoke this function, ensure that master lock of OPTC specified
274  * by opp_id is set
275  * This function can be invoke multiple times to add more than 1 pipes.
276  *
277  * tree_cfg[in/out] - current MPC_TREE_CFG
278  * dpp_idx[in]	 - index of an idle dpp insatnce to be added.
279  * mpcc_idx[in]	 - index of an idle mpcc instance to be added.
280  * poistion[in]	 - position of dpp/mpcc pair to be added into current tree_cfg
281  *                 0 means insert to the most top layer of MPC tree
282  */
283 void dcn10_add_dpp(struct dcn10_mpc *mpc,
284 	struct mpc_tree_cfg *tree_cfg,
285 	uint8_t dpp_idx,
286 	uint8_t mpcc_idx,
287 	uint8_t position)
288 {
289 	uint8_t temp;
290 	uint8_t temp1;
291 
292 	REG_SET(MPCC_OPP_ID[mpcc_idx], 0,
293 			MPCC_OPP_ID, tree_cfg->opp_id);
294 
295 	REG_SET(MPCC_TOP_SEL[mpcc_idx], 0,
296 			MPCC_TOP_SEL, dpp_idx);
297 
298 	if (position == 0) {
299 		/* idle dpp/mpcc is added to the top layer of tree */
300 		REG_SET(MPCC_BOT_SEL[mpcc_idx], 0,
301 				MPCC_BOT_SEL, tree_cfg->mpcc[0]);
302 		REG_UPDATE(MPCC_CONTROL[mpcc_idx],
303 				MPCC_MODE, 3);
304 
305 		/* opp will get new output. from new added mpcc */
306 		set_output_mux(mpc, tree_cfg->opp_id, mpcc_idx);
307 
308 		set_blend_mode(mpc, tree_cfg->mode, mpcc_idx);
309 
310 	} else if (position == tree_cfg->num_pipes) {
311 		/* idle dpp/mpcc is added to the bottom layer of tree */
312 
313 		/* get instance of previous bottom mpcc, set to middle layer */
314 		temp = tree_cfg->mpcc[tree_cfg->num_pipes - 1];
315 
316 		REG_SET(MPCC_BOT_SEL[temp], 0,
317 				MPCC_BOT_SEL, mpcc_idx);
318 
319 		REG_UPDATE(MPCC_CONTROL[temp],
320 				MPCC_MODE, 3);
321 
322 		/* mpcc_idx become new bottom mpcc*/
323 		REG_SET(MPCC_BOT_SEL[mpcc_idx], 0,
324 				MPCC_BOT_SEL, 0xf);
325 
326 		REG_UPDATE(MPCC_CONTROL[mpcc_idx],
327 				MPCC_MODE, tree_cfg->mode);
328 
329 		set_blend_mode(mpc, tree_cfg->mode, mpcc_idx);
330 	} else {
331 		/* idle dpp/mpcc is added to middle of tree */
332 		temp = tree_cfg->mpcc[position - 1];
333 		temp1 = tree_cfg->mpcc[position];
334 
335 		/* new mpcc instance temp1 is added right after temp*/
336 		REG_SET(MPCC_BOT_SEL[temp], 0,
337 				MPCC_BOT_SEL, mpcc_idx);
338 
339 		/* mpcc_idx connect previous temp+1 to new mpcc */
340 		REG_SET(MPCC_BOT_SEL[mpcc_idx], 0,
341 				MPCC_BOT_SEL, temp1);
342 
343 		/* temp TODO: may not need*/
344 		REG_UPDATE(MPCC_CONTROL[temp],
345 				MPCC_MODE, 3);
346 
347 		set_blend_mode(mpc, tree_cfg->mode, temp);
348 	}
349 
350 	/* update tree_cfg structure */
351 	temp = tree_cfg->num_pipes - 1;
352 
353 	/*
354 	 * iterating from the last mpc/dpp pair to the one being added, shift
355 	 * them down one position
356 	 */
357 	while (temp > position) {
358 		tree_cfg->dpp[temp + 1] = tree_cfg->dpp[temp];
359 		tree_cfg->mpcc[temp + 1] = tree_cfg->mpcc[temp];
360 		temp--;
361 	}
362 
363 	/* insert the new mpc/dpp pair into the tree_cfg*/
364 	tree_cfg->dpp[position] = dpp_idx;
365 	tree_cfg->mpcc[position] = mpcc_idx;
366 	tree_cfg->num_pipes++;
367 }
368 
369 void wait_mpcc_idle(struct dcn10_mpc *mpc,
370 	uint8_t mpcc_id)
371 {
372 	REG_WAIT(MPCC_STATUS[mpcc_id],
373 			MPCC_IDLE, 1,
374 			1000, 1000);
375 }
376 
377