1/*	$NetBSD: amdgpu_dcn10_mpc.c,v 1.2 2021/12/18 23:45:03 riastradh Exp $	*/
2
3/*
4 * Copyright 2012-15 Advanced Micro Devices, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * Authors: AMD
25 *
26 */
27
28#include <sys/cdefs.h>
29__KERNEL_RCSID(0, "$NetBSD: amdgpu_dcn10_mpc.c,v 1.2 2021/12/18 23:45:03 riastradh Exp $");
30
31#include "reg_helper.h"
32#include "dcn10_mpc.h"
33
34#define REG(reg)\
35	mpc10->mpc_regs->reg
36
37#define CTX \
38	mpc10->base.ctx
39
40#undef FN
41#define FN(reg_name, field_name) \
42	mpc10->mpc_shift->field_name, mpc10->mpc_mask->field_name
43
44
45void mpc1_set_bg_color(struct mpc *mpc,
46		struct tg_color *bg_color,
47		int mpcc_id)
48{
49	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
50	struct mpcc *bottommost_mpcc = mpc1_get_mpcc(mpc, mpcc_id);
51	uint32_t bg_r_cr, bg_g_y, bg_b_cb;
52
53	/* find bottommost mpcc. */
54	while (bottommost_mpcc->mpcc_bot) {
55		bottommost_mpcc = bottommost_mpcc->mpcc_bot;
56	}
57
58	/* mpc color is 12 bit.  tg_color is 10 bit */
59	/* todo: might want to use 16 bit to represent color and have each
60	 * hw block translate to correct color depth.
61	 */
62	bg_r_cr = bg_color->color_r_cr << 2;
63	bg_g_y = bg_color->color_g_y << 2;
64	bg_b_cb = bg_color->color_b_cb << 2;
65
66	REG_SET(MPCC_BG_R_CR[bottommost_mpcc->mpcc_id], 0,
67			MPCC_BG_R_CR, bg_r_cr);
68	REG_SET(MPCC_BG_G_Y[bottommost_mpcc->mpcc_id], 0,
69			MPCC_BG_G_Y, bg_g_y);
70	REG_SET(MPCC_BG_B_CB[bottommost_mpcc->mpcc_id], 0,
71			MPCC_BG_B_CB, bg_b_cb);
72}
73
74static void mpc1_update_blending(
75	struct mpc *mpc,
76	struct mpcc_blnd_cfg *blnd_cfg,
77	int mpcc_id)
78{
79	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
80	struct mpcc *mpcc = mpc1_get_mpcc(mpc, mpcc_id);
81
82	REG_UPDATE_5(MPCC_CONTROL[mpcc_id],
83			MPCC_ALPHA_BLND_MODE,		blnd_cfg->alpha_mode,
84			MPCC_ALPHA_MULTIPLIED_MODE,	blnd_cfg->pre_multiplied_alpha,
85			MPCC_BLND_ACTIVE_OVERLAP_ONLY,	blnd_cfg->overlap_only,
86			MPCC_GLOBAL_ALPHA,		blnd_cfg->global_alpha,
87			MPCC_GLOBAL_GAIN,		blnd_cfg->global_gain);
88
89	mpc1_set_bg_color(mpc, &blnd_cfg->black_color, mpcc_id);
90	mpcc->blnd_cfg = *blnd_cfg;
91}
92
93void mpc1_update_stereo_mix(
94	struct mpc *mpc,
95	struct mpcc_sm_cfg *sm_cfg,
96	int mpcc_id)
97{
98	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
99
100	REG_UPDATE_6(MPCC_SM_CONTROL[mpcc_id],
101			MPCC_SM_EN,			sm_cfg->enable,
102			MPCC_SM_MODE,			sm_cfg->sm_mode,
103			MPCC_SM_FRAME_ALT,		sm_cfg->frame_alt,
104			MPCC_SM_FIELD_ALT,		sm_cfg->field_alt,
105			MPCC_SM_FORCE_NEXT_FRAME_POL,	sm_cfg->force_next_frame_porlarity,
106			MPCC_SM_FORCE_NEXT_TOP_POL,	sm_cfg->force_next_field_polarity);
107}
108void mpc1_assert_idle_mpcc(struct mpc *mpc, int id)
109{
110	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
111
112	ASSERT(!(mpc10->mpcc_in_use_mask & 1 << id));
113	REG_WAIT(MPCC_STATUS[id],
114			MPCC_IDLE, 1,
115			1, 100000);
116}
117
118struct mpcc *mpc1_get_mpcc(struct mpc *mpc, int mpcc_id)
119{
120	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
121
122	ASSERT(mpcc_id < mpc10->num_mpcc);
123	return &(mpc->mpcc_array[mpcc_id]);
124}
125
126struct mpcc *mpc1_get_mpcc_for_dpp(struct mpc_tree *tree, int dpp_id)
127{
128	struct mpcc *tmp_mpcc = tree->opp_list;
129
130	while (tmp_mpcc != NULL) {
131		if (tmp_mpcc->dpp_id == dpp_id)
132			return tmp_mpcc;
133		tmp_mpcc = tmp_mpcc->mpcc_bot;
134	}
135	return NULL;
136}
137
138bool mpc1_is_mpcc_idle(struct mpc *mpc, int mpcc_id)
139{
140	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
141	unsigned int top_sel;
142	unsigned int opp_id;
143	unsigned int idle;
144
145	REG_GET(MPCC_TOP_SEL[mpcc_id], MPCC_TOP_SEL, &top_sel);
146	REG_GET(MPCC_OPP_ID[mpcc_id],  MPCC_OPP_ID, &opp_id);
147	REG_GET(MPCC_STATUS[mpcc_id],  MPCC_IDLE,   &idle);
148	if (top_sel == 0xf && opp_id == 0xf && idle)
149		return true;
150	else
151		return false;
152}
153
154void mpc1_assert_mpcc_idle_before_connect(struct mpc *mpc, int mpcc_id)
155{
156	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
157	unsigned int top_sel, mpc_busy, mpc_idle;
158
159	REG_GET(MPCC_TOP_SEL[mpcc_id],
160			MPCC_TOP_SEL, &top_sel);
161
162	if (top_sel == 0xf) {
163		REG_GET_2(MPCC_STATUS[mpcc_id],
164				MPCC_BUSY, &mpc_busy,
165				MPCC_IDLE, &mpc_idle);
166
167		ASSERT(mpc_busy == 0);
168		ASSERT(mpc_idle == 1);
169	}
170}
171
172/*
173 * Insert DPP into MPC tree based on specified blending position.
174 * Only used for planes that are part of blending chain for OPP output
175 *
176 * Parameters:
177 * [in/out] mpc		- MPC context.
178 * [in/out] tree	- MPC tree structure that plane will be added to.
179 * [in]	blnd_cfg	- MPCC blending configuration for the new blending layer.
180 * [in]	sm_cfg		- MPCC stereo mix configuration for the new blending layer.
181 *			  stereo mix must disable for the very bottom layer of the tree config.
182 * [in]	insert_above_mpcc - Insert new plane above this MPCC.  If NULL, insert as bottom plane.
183 * [in]	dpp_id		- DPP instance for the plane to be added.
184 * [in]	mpcc_id		- The MPCC physical instance to use for blending.
185 *
186 * Return:  struct mpcc* - MPCC that was added.
187 */
188struct mpcc *mpc1_insert_plane(
189	struct mpc *mpc,
190	struct mpc_tree *tree,
191	struct mpcc_blnd_cfg *blnd_cfg,
192	struct mpcc_sm_cfg *sm_cfg,
193	struct mpcc *insert_above_mpcc,
194	int dpp_id,
195	int mpcc_id)
196{
197	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
198	struct mpcc *new_mpcc = NULL;
199
200	/* sanity check parameters */
201	ASSERT(mpcc_id < mpc10->num_mpcc);
202	ASSERT(!(mpc10->mpcc_in_use_mask & 1 << mpcc_id));
203
204	if (insert_above_mpcc) {
205		/* check insert_above_mpcc exist in tree->opp_list */
206		struct mpcc *temp_mpcc = tree->opp_list;
207
208		while (temp_mpcc && temp_mpcc->mpcc_bot != insert_above_mpcc)
209			temp_mpcc = temp_mpcc->mpcc_bot;
210		if (temp_mpcc == NULL)
211			return NULL;
212	}
213
214	/* Get and update MPCC struct parameters */
215	new_mpcc = mpc1_get_mpcc(mpc, mpcc_id);
216	new_mpcc->dpp_id = dpp_id;
217
218	/* program mux and MPCC_MODE */
219	if (insert_above_mpcc) {
220		new_mpcc->mpcc_bot = insert_above_mpcc;
221		REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, insert_above_mpcc->mpcc_id);
222		REG_UPDATE(MPCC_CONTROL[mpcc_id], MPCC_MODE, MPCC_BLEND_MODE_TOP_BOT_BLENDING);
223	} else {
224		new_mpcc->mpcc_bot = NULL;
225		REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, 0xf);
226		REG_UPDATE(MPCC_CONTROL[mpcc_id], MPCC_MODE, MPCC_BLEND_MODE_TOP_LAYER_ONLY);
227	}
228	REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, dpp_id);
229	REG_SET(MPCC_OPP_ID[mpcc_id], 0, MPCC_OPP_ID, tree->opp_id);
230
231	/* update mpc tree mux setting */
232	if (tree->opp_list == insert_above_mpcc) {
233		/* insert the toppest mpcc */
234		tree->opp_list = new_mpcc;
235		REG_UPDATE(MUX[tree->opp_id], MPC_OUT_MUX, mpcc_id);
236	} else {
237		/* find insert position */
238		struct mpcc *temp_mpcc = tree->opp_list;
239
240		while (temp_mpcc && temp_mpcc->mpcc_bot != insert_above_mpcc)
241			temp_mpcc = temp_mpcc->mpcc_bot;
242		if (temp_mpcc && temp_mpcc->mpcc_bot == insert_above_mpcc) {
243			REG_SET(MPCC_BOT_SEL[temp_mpcc->mpcc_id], 0, MPCC_BOT_SEL, mpcc_id);
244			temp_mpcc->mpcc_bot = new_mpcc;
245			if (!insert_above_mpcc)
246				REG_UPDATE(MPCC_CONTROL[temp_mpcc->mpcc_id],
247						MPCC_MODE, MPCC_BLEND_MODE_TOP_BOT_BLENDING);
248		}
249	}
250
251	/* update the blending configuration */
252	mpc->funcs->update_blending(mpc, blnd_cfg, mpcc_id);
253
254	/* update the stereo mix settings, if provided */
255	if (sm_cfg != NULL) {
256		new_mpcc->sm_cfg = *sm_cfg;
257		mpc1_update_stereo_mix(mpc, sm_cfg, mpcc_id);
258	}
259
260	/* mark this mpcc as in use */
261	mpc10->mpcc_in_use_mask |= 1 << mpcc_id;
262
263	return new_mpcc;
264}
265
266/*
267 * Remove a specified MPCC from the MPC tree.
268 *
269 * Parameters:
270 * [in/out] mpc		- MPC context.
271 * [in/out] tree	- MPC tree structure that plane will be removed from.
272 * [in/out] mpcc	- MPCC to be removed from tree.
273 *
274 * Return:  void
275 */
276void mpc1_remove_mpcc(
277	struct mpc *mpc,
278	struct mpc_tree *tree,
279	struct mpcc *mpcc_to_remove)
280{
281	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
282	bool found = false;
283	int mpcc_id = mpcc_to_remove->mpcc_id;
284
285	if (tree->opp_list == mpcc_to_remove) {
286		found = true;
287		/* remove MPCC from top of tree */
288		if (mpcc_to_remove->mpcc_bot) {
289			/* set the next MPCC in list to be the top MPCC */
290			tree->opp_list = mpcc_to_remove->mpcc_bot;
291			REG_UPDATE(MUX[tree->opp_id], MPC_OUT_MUX, tree->opp_list->mpcc_id);
292		} else {
293			/* there are no other MPCC is list */
294			tree->opp_list = NULL;
295			REG_UPDATE(MUX[tree->opp_id], MPC_OUT_MUX, 0xf);
296		}
297	} else {
298		/* find mpcc to remove MPCC list */
299		struct mpcc *temp_mpcc = tree->opp_list;
300
301		while (temp_mpcc && temp_mpcc->mpcc_bot != mpcc_to_remove)
302			temp_mpcc = temp_mpcc->mpcc_bot;
303
304		if (temp_mpcc && temp_mpcc->mpcc_bot == mpcc_to_remove) {
305			found = true;
306			temp_mpcc->mpcc_bot = mpcc_to_remove->mpcc_bot;
307			if (mpcc_to_remove->mpcc_bot) {
308				/* remove MPCC in middle of list */
309				REG_SET(MPCC_BOT_SEL[temp_mpcc->mpcc_id], 0,
310						MPCC_BOT_SEL, mpcc_to_remove->mpcc_bot->mpcc_id);
311			} else {
312				/* remove MPCC from bottom of list */
313				REG_SET(MPCC_BOT_SEL[temp_mpcc->mpcc_id], 0,
314						MPCC_BOT_SEL, 0xf);
315				REG_UPDATE(MPCC_CONTROL[temp_mpcc->mpcc_id],
316						MPCC_MODE, MPCC_BLEND_MODE_TOP_LAYER_PASSTHROUGH);
317			}
318		}
319	}
320
321	if (found) {
322		/* turn off MPCC mux registers */
323		REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, 0xf);
324		REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, 0xf);
325		REG_SET(MPCC_OPP_ID[mpcc_id],  0, MPCC_OPP_ID,  0xf);
326
327		/* mark this mpcc as not in use */
328		mpc10->mpcc_in_use_mask &= ~(1 << mpcc_id);
329		mpcc_to_remove->dpp_id = 0xf;
330		mpcc_to_remove->mpcc_bot = NULL;
331	} else {
332		/* In case of resume from S3/S4, remove mpcc from bios left over */
333		REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, 0xf);
334		REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, 0xf);
335		REG_SET(MPCC_OPP_ID[mpcc_id],  0, MPCC_OPP_ID,  0xf);
336	}
337}
338
339static void mpc1_init_mpcc(struct mpcc *mpcc, int mpcc_inst)
340{
341	mpcc->mpcc_id = mpcc_inst;
342	mpcc->dpp_id = 0xf;
343	mpcc->mpcc_bot = NULL;
344	mpcc->blnd_cfg.overlap_only = false;
345	mpcc->blnd_cfg.global_alpha = 0xff;
346	mpcc->blnd_cfg.global_gain = 0xff;
347	mpcc->sm_cfg.enable = false;
348}
349
350/*
351 * Reset the MPCC HW status by disconnecting all muxes.
352 *
353 * Parameters:
354 * [in/out] mpc		- MPC context.
355 *
356 * Return:  void
357 */
358void mpc1_mpc_init(struct mpc *mpc)
359{
360	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
361	int mpcc_id;
362	int opp_id;
363
364	mpc10->mpcc_in_use_mask = 0;
365	for (mpcc_id = 0; mpcc_id < mpc10->num_mpcc; mpcc_id++) {
366		REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, 0xf);
367		REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, 0xf);
368		REG_SET(MPCC_OPP_ID[mpcc_id],  0, MPCC_OPP_ID,  0xf);
369
370		mpc1_init_mpcc(&(mpc->mpcc_array[mpcc_id]), mpcc_id);
371	}
372
373	for (opp_id = 0; opp_id < MAX_OPP; opp_id++) {
374		if (REG(MUX[opp_id]))
375			REG_UPDATE(MUX[opp_id], MPC_OUT_MUX, 0xf);
376	}
377}
378
379void mpc1_mpc_init_single_inst(struct mpc *mpc, unsigned int mpcc_id)
380{
381	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
382	int opp_id;
383
384	REG_GET(MPCC_OPP_ID[mpcc_id], MPCC_OPP_ID, &opp_id);
385
386	REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, 0xf);
387	REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, 0xf);
388	REG_SET(MPCC_OPP_ID[mpcc_id],  0, MPCC_OPP_ID,  0xf);
389
390	mpc1_init_mpcc(&(mpc->mpcc_array[mpcc_id]), mpcc_id);
391
392	if (opp_id < MAX_OPP && REG(MUX[opp_id]))
393		REG_UPDATE(MUX[opp_id], MPC_OUT_MUX, 0xf);
394}
395
396
397void mpc1_init_mpcc_list_from_hw(
398	struct mpc *mpc,
399	struct mpc_tree *tree)
400{
401	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
402	unsigned int opp_id;
403	unsigned int top_sel;
404	unsigned int bot_sel;
405	unsigned int out_mux;
406	struct mpcc *mpcc;
407	int mpcc_id;
408	int bot_mpcc_id;
409
410	REG_GET(MUX[tree->opp_id], MPC_OUT_MUX, &out_mux);
411
412	if (out_mux != 0xf) {
413		for (mpcc_id = 0; mpcc_id < mpc10->num_mpcc; mpcc_id++) {
414			REG_GET(MPCC_OPP_ID[mpcc_id],  MPCC_OPP_ID,  &opp_id);
415			REG_GET(MPCC_TOP_SEL[mpcc_id], MPCC_TOP_SEL, &top_sel);
416			REG_GET(MPCC_BOT_SEL[mpcc_id],  MPCC_BOT_SEL, &bot_sel);
417
418			if (bot_sel == mpcc_id)
419				bot_sel = 0xf;
420
421			if ((opp_id == tree->opp_id) && (top_sel != 0xf)) {
422				mpcc = mpc1_get_mpcc(mpc, mpcc_id);
423				mpcc->dpp_id = top_sel;
424				mpc10->mpcc_in_use_mask |= 1 << mpcc_id;
425
426				if (out_mux == mpcc_id)
427					tree->opp_list = mpcc;
428				if (bot_sel != 0xf && bot_sel < mpc10->num_mpcc) {
429					bot_mpcc_id = bot_sel;
430					REG_GET(MPCC_OPP_ID[bot_mpcc_id],  MPCC_OPP_ID,  &opp_id);
431					REG_GET(MPCC_TOP_SEL[bot_mpcc_id], MPCC_TOP_SEL, &top_sel);
432					if ((opp_id == tree->opp_id) && (top_sel != 0xf)) {
433						struct mpcc *mpcc_bottom = mpc1_get_mpcc(mpc, bot_mpcc_id);
434
435						mpcc->mpcc_bot = mpcc_bottom;
436					}
437				}
438			}
439		}
440	}
441}
442
443void mpc1_read_mpcc_state(
444		struct mpc *mpc,
445		int mpcc_inst,
446		struct mpcc_state *s)
447{
448	struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
449
450	REG_GET(MPCC_OPP_ID[mpcc_inst], MPCC_OPP_ID, &s->opp_id);
451	REG_GET(MPCC_TOP_SEL[mpcc_inst], MPCC_TOP_SEL, &s->dpp_id);
452	REG_GET(MPCC_BOT_SEL[mpcc_inst], MPCC_BOT_SEL, &s->bot_mpcc_id);
453	REG_GET_4(MPCC_CONTROL[mpcc_inst], MPCC_MODE, &s->mode,
454			MPCC_ALPHA_BLND_MODE, &s->alpha_mode,
455			MPCC_ALPHA_MULTIPLIED_MODE, &s->pre_multiplied_alpha,
456			MPCC_BLND_ACTIVE_OVERLAP_ONLY, &s->overlap_only);
457	REG_GET_2(MPCC_STATUS[mpcc_inst], MPCC_IDLE, &s->idle,
458			MPCC_BUSY, &s->busy);
459}
460
461static const struct mpc_funcs dcn10_mpc_funcs = {
462	.read_mpcc_state = mpc1_read_mpcc_state,
463	.insert_plane = mpc1_insert_plane,
464	.remove_mpcc = mpc1_remove_mpcc,
465	.mpc_init = mpc1_mpc_init,
466	.mpc_init_single_inst = mpc1_mpc_init_single_inst,
467	.get_mpcc_for_dpp = mpc1_get_mpcc_for_dpp,
468	.wait_for_idle = mpc1_assert_idle_mpcc,
469	.assert_mpcc_idle_before_connect = mpc1_assert_mpcc_idle_before_connect,
470	.init_mpcc_list_from_hw = mpc1_init_mpcc_list_from_hw,
471	.update_blending = mpc1_update_blending,
472	.set_denorm = NULL,
473	.set_denorm_clamp = NULL,
474	.set_output_csc = NULL,
475	.set_output_gamma = NULL,
476};
477
478void dcn10_mpc_construct(struct dcn10_mpc *mpc10,
479	struct dc_context *ctx,
480	const struct dcn_mpc_registers *mpc_regs,
481	const struct dcn_mpc_shift *mpc_shift,
482	const struct dcn_mpc_mask *mpc_mask,
483	int num_mpcc)
484{
485	int i;
486
487	mpc10->base.ctx = ctx;
488
489	mpc10->base.funcs = &dcn10_mpc_funcs;
490
491	mpc10->mpc_regs = mpc_regs;
492	mpc10->mpc_shift = mpc_shift;
493	mpc10->mpc_mask = mpc_mask;
494
495	mpc10->mpcc_in_use_mask = 0;
496	mpc10->num_mpcc = num_mpcc;
497
498	for (i = 0; i < MAX_MPCC; i++)
499		mpc1_init_mpcc(&mpc10->base.mpcc_array[i], i);
500}
501
502