1 /*
2  * Copyright 2019 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 
27 #include "dc_bios_types.h"
28 #include "hw_shared.h"
29 #include "dcn31_apg.h"
30 #include "reg_helper.h"
31 
32 #define DC_LOGGER \
33 		apg31->base.ctx->logger
34 
35 #define REG(reg)\
36 	(apg31->regs->reg)
37 
38 #undef FN
39 #define FN(reg_name, field_name) \
40 	apg31->apg_shift->field_name, apg31->apg_mask->field_name
41 
42 
43 #define CTX \
44 	apg31->base.ctx
45 
46 
47 static void apg31_enable(
48 	struct apg *apg)
49 {
50 	struct dcn31_apg *apg31 = DCN31_APG_FROM_APG(apg);
51 
52 	/* Reset APG */
53 	REG_UPDATE(APG_CONTROL, APG_RESET, 1);
54 	REG_WAIT(APG_CONTROL,
55 			APG_RESET_DONE, 1,
56 			1, 10);
57 	REG_UPDATE(APG_CONTROL, APG_RESET, 0);
58 	REG_WAIT(APG_CONTROL,
59 			APG_RESET_DONE, 0,
60 			1, 10);
61 
62 	/* Enable APG */
63 	REG_UPDATE(APG_CONTROL2, APG_ENABLE, 1);
64 }
65 
66 static void apg31_disable(
67 	struct apg *apg)
68 {
69 	struct dcn31_apg *apg31 = DCN31_APG_FROM_APG(apg);
70 
71 	/* Disable APG */
72 	REG_UPDATE(APG_CONTROL2, APG_ENABLE, 0);
73 }
74 
75 static union audio_cea_channels speakers_to_channels(
76 	struct audio_speaker_flags speaker_flags)
77 {
78 	union audio_cea_channels cea_channels = {0};
79 
80 	/* these are one to one */
81 	cea_channels.channels.FL = speaker_flags.FL_FR;
82 	cea_channels.channels.FR = speaker_flags.FL_FR;
83 	cea_channels.channels.LFE = speaker_flags.LFE;
84 	cea_channels.channels.FC = speaker_flags.FC;
85 
86 	/* if Rear Left and Right exist move RC speaker to channel 7
87 	 * otherwise to channel 5
88 	 */
89 	if (speaker_flags.RL_RR) {
90 		cea_channels.channels.RL_RC = speaker_flags.RL_RR;
91 		cea_channels.channels.RR = speaker_flags.RL_RR;
92 		cea_channels.channels.RC_RLC_FLC = speaker_flags.RC;
93 	} else {
94 		cea_channels.channels.RL_RC = speaker_flags.RC;
95 	}
96 
97 	/* FRONT Left Right Center and REAR Left Right Center are exclusive */
98 	if (speaker_flags.FLC_FRC) {
99 		cea_channels.channels.RC_RLC_FLC = speaker_flags.FLC_FRC;
100 		cea_channels.channels.RRC_FRC = speaker_flags.FLC_FRC;
101 	} else {
102 		cea_channels.channels.RC_RLC_FLC = speaker_flags.RLC_RRC;
103 		cea_channels.channels.RRC_FRC = speaker_flags.RLC_RRC;
104 	}
105 
106 	return cea_channels;
107 }
108 
109 static void apg31_se_audio_setup(
110 	struct apg *apg,
111 	unsigned int az_inst,
112 	struct audio_info *audio_info)
113 {
114 	struct dcn31_apg *apg31 = DCN31_APG_FROM_APG(apg);
115 
116 	uint32_t speakers = 0;
117 	uint32_t channels = 0;
118 
119 	ASSERT(audio_info);
120 	/* This should not happen.it does so we don't get BSOD*/
121 	if (audio_info == NULL)
122 		return;
123 
124 	speakers = audio_info->flags.info.ALLSPEAKERS;
125 	channels = speakers_to_channels(audio_info->flags.speaker_flags).all;
126 
127 	/* DisplayPort only allows for one audio stream with stream ID 0 */
128 	REG_UPDATE(APG_CONTROL2, APG_DP_AUDIO_STREAM_ID, 0);
129 
130 	/* When running in "pair mode", pairs of audio channels have their own enable
131 	 * this is for really old audio drivers */
132 	REG_UPDATE(APG_DBG_GEN_CONTROL, APG_DBG_AUDIO_CHANNEL_ENABLE, 0xFF);
133 	// REG_UPDATE(APG_DBG_GEN_CONTROL, APG_DBG_AUDIO_CHANNEL_ENABLE, channels);
134 
135 	/* Disable forced mem power off */
136 	REG_UPDATE(APG_MEM_PWR, APG_MEM_PWR_FORCE, 0);
137 }
138 
139 static struct apg_funcs dcn31_apg_funcs = {
140 	.se_audio_setup			= apg31_se_audio_setup,
141 	.enable_apg			= apg31_enable,
142 	.disable_apg			= apg31_disable,
143 };
144 
145 void apg31_construct(struct dcn31_apg *apg31,
146 	struct dc_context *ctx,
147 	uint32_t inst,
148 	const struct dcn31_apg_registers *apg_regs,
149 	const struct dcn31_apg_shift *apg_shift,
150 	const struct dcn31_apg_mask *apg_mask)
151 {
152 	apg31->base.ctx = ctx;
153 
154 	apg31->base.inst = inst;
155 	apg31->base.funcs = &dcn31_apg_funcs;
156 
157 	apg31->regs = apg_regs;
158 	apg31->apg_shift = apg_shift;
159 	apg31->apg_mask = apg_mask;
160 }
161