xref: /openbmc/linux/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c (revision 56ea353ea49ad21dd4c14e7baa235493ec27e766)
1 /*
2  * Copyright 2021 Red Hat 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 #define nvkm_uoutp(p) container_of((p), struct nvkm_outp, object)
23 #include "outp.h"
24 #include "dp.h"
25 #include "head.h"
26 #include "ior.h"
27 
28 #include <nvif/if0012.h>
29 
30 static int
31 nvkm_uoutp_mthd_dp_mst_vcpi(struct nvkm_outp *outp, void *argv, u32 argc)
32 {
33 	struct nvkm_ior *ior = outp->ior;
34 	union nvif_outp_dp_mst_vcpi_args *args = argv;
35 
36 	if (argc != sizeof(args->v0) || args->v0.version != 0)
37 		return -ENOSYS;
38 	if (!ior->func->dp || !ior->func->dp->vcpi || !nvkm_head_find(outp->disp, args->v0.head))
39 		return -EINVAL;
40 
41 	ior->func->dp->vcpi(ior, args->v0.head, args->v0.start_slot, args->v0.num_slots,
42 				 args->v0.pbn, args->v0.aligned_pbn);
43 	return 0;
44 }
45 
46 static int
47 nvkm_uoutp_mthd_dp_retrain(struct nvkm_outp *outp, void *argv, u32 argc)
48 {
49 	union nvif_outp_dp_retrain_args *args = argv;
50 
51 	if (argc != sizeof(args->vn))
52 		return -ENOSYS;
53 
54 	if (!atomic_read(&outp->dp.lt.done))
55 		return 0;
56 
57 	return outp->func->acquire(outp);
58 }
59 
60 static int
61 nvkm_uoutp_mthd_dp_aux_pwr(struct nvkm_outp *outp, void *argv, u32 argc)
62 {
63 	union nvif_outp_dp_aux_pwr_args *args = argv;
64 
65 	if (argc != sizeof(args->v0) || args->v0.version != 0)
66 		return -ENOSYS;
67 
68 	outp->dp.enabled = !!args->v0.state;
69 	nvkm_dp_enable(outp, outp->dp.enabled);
70 	return 0;
71 }
72 
73 static int
74 nvkm_uoutp_mthd_hda_eld(struct nvkm_outp *outp, void *argv, u32 argc)
75 {
76 	struct nvkm_ior *ior = outp->ior;
77 	union nvif_outp_hda_eld_args *args = argv;
78 
79 	if (argc < sizeof(args->v0) || args->v0.version != 0)
80 		return -ENOSYS;
81 	argc -= sizeof(args->v0);
82 
83 	if (!ior->hda || !nvkm_head_find(outp->disp, args->v0.head))
84 		return -EINVAL;
85 	if (argc > 0x60)
86 		return -E2BIG;
87 
88 	if (argc && args->v0.data[0]) {
89 		if (outp->info.type == DCB_OUTPUT_DP)
90 			ior->func->dp->audio(ior, args->v0.head, true);
91 		ior->func->hda->hpd(ior, args->v0.head, true);
92 		ior->func->hda->eld(ior, args->v0.head, args->v0.data, argc);
93 	} else {
94 		if (outp->info.type == DCB_OUTPUT_DP)
95 			ior->func->dp->audio(ior, args->v0.head, false);
96 		ior->func->hda->hpd(ior, args->v0.head, false);
97 	}
98 
99 	return 0;
100 }
101 
102 static int
103 nvkm_uoutp_mthd_infoframe(struct nvkm_outp *outp, void *argv, u32 argc)
104 {
105 	struct nvkm_ior *ior = outp->ior;
106 	union nvif_outp_infoframe_args *args = argv;
107 
108 	if (argc < sizeof(args->v0) || args->v0.version != 0)
109 		return -ENOSYS;
110 	if (!nvkm_head_find(outp->disp, args->v0.head))
111 		return -EINVAL;
112 
113 	switch (ior->func->hdmi ? args->v0.type : 0xff) {
114 	case NVIF_OUTP_INFOFRAME_V0_AVI:
115 		ior->func->hdmi->infoframe_avi(ior, args->v0.head, argv, argc);
116 		return 0;
117 	case NVIF_OUTP_INFOFRAME_V0_VSI:
118 		ior->func->hdmi->infoframe_vsi(ior, args->v0.head, argv, argc);
119 		return 0;
120 	default:
121 		break;
122 	}
123 
124 	return -EINVAL;
125 }
126 
127 static int
128 nvkm_uoutp_mthd_release(struct nvkm_outp *outp, void *argv, u32 argc)
129 {
130 	struct nvkm_head *head = outp->asy.head;
131 	struct nvkm_ior *ior = outp->ior;
132 	union nvif_outp_release_args *args = argv;
133 
134 	if (argc != sizeof(args->vn))
135 		return -ENOSYS;
136 
137 	if (ior->func->hdmi && head) {
138 		ior->func->hdmi->infoframe_avi(ior, head->id, NULL, 0);
139 		ior->func->hdmi->infoframe_vsi(ior, head->id, NULL, 0);
140 		ior->func->hdmi->ctrl(ior, head->id, false, 0, 0);
141 	}
142 
143 	nvkm_outp_release(outp, NVKM_OUTP_USER);
144 	return 0;
145 }
146 
147 static int
148 nvkm_uoutp_mthd_acquire_dp(struct nvkm_outp *outp, u8 dpcd[16],
149 			   u8 link_nr, u8 link_bw, bool hda, bool mst)
150 {
151 	int ret;
152 
153 	ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, hda);
154 	if (ret)
155 		return ret;
156 
157 	memcpy(outp->dp.dpcd, dpcd, sizeof(outp->dp.dpcd));
158 	outp->dp.lt.nr = link_nr;
159 	outp->dp.lt.bw = link_bw;
160 	outp->dp.lt.mst = mst;
161 	return 0;
162 }
163 
164 static int
165 nvkm_uoutp_mthd_acquire_tmds(struct nvkm_outp *outp, u8 head, u8 hdmi, u8 hdmi_max_ac_packet,
166 			     u8 hdmi_rekey, u8 hdmi_scdc, u8 hdmi_hda)
167 {
168 	struct nvkm_ior *ior;
169 	int ret;
170 
171 	if (!(outp->asy.head = nvkm_head_find(outp->disp, head)))
172 		return -EINVAL;
173 
174 	ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, hdmi && hdmi_hda);
175 	if (ret)
176 		return ret;
177 
178 	ior = outp->ior;
179 
180 	if (hdmi) {
181 		if (!ior->func->hdmi ||
182 		    hdmi_max_ac_packet > 0x1f || hdmi_rekey > 0x7f ||
183 		    (hdmi_scdc && !ior->func->hdmi->scdc)) {
184 			nvkm_outp_release(outp, NVKM_OUTP_USER);
185 			return -EINVAL;
186 		}
187 
188 		ior->func->hdmi->ctrl(ior, head, hdmi, hdmi_max_ac_packet, hdmi_rekey);
189 		if (ior->func->hdmi->scdc)
190 			ior->func->hdmi->scdc(ior, hdmi_scdc);
191 	}
192 
193 	return 0;
194 }
195 
196 static int
197 nvkm_uoutp_mthd_acquire_lvds(struct nvkm_outp *outp, bool dual, bool bpc8)
198 {
199 	if (outp->info.type != DCB_OUTPUT_LVDS)
200 		return -EINVAL;
201 
202 	outp->lvds.dual = dual;
203 	outp->lvds.bpc8 = bpc8;
204 
205 	return nvkm_outp_acquire(outp, NVKM_OUTP_USER, false);
206 }
207 
208 static int
209 nvkm_uoutp_mthd_acquire(struct nvkm_outp *outp, void *argv, u32 argc)
210 {
211 	union nvif_outp_acquire_args *args = argv;
212 	int ret;
213 
214 	if (argc != sizeof(args->v0) || args->v0.version != 0)
215 		return -ENOSYS;
216 	if (outp->ior)
217 		return -EBUSY;
218 
219 	switch (args->v0.proto) {
220 	case NVIF_OUTP_ACQUIRE_V0_RGB_CRT:
221 		ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, false);
222 		break;
223 	case NVIF_OUTP_ACQUIRE_V0_TMDS:
224 		ret = nvkm_uoutp_mthd_acquire_tmds(outp, args->v0.tmds.head,
225 							 args->v0.tmds.hdmi,
226 							 args->v0.tmds.hdmi_max_ac_packet,
227 							 args->v0.tmds.hdmi_rekey,
228 							 args->v0.tmds.hdmi_scdc,
229 							 args->v0.tmds.hdmi_hda);
230 		break;
231 	case NVIF_OUTP_ACQUIRE_V0_LVDS:
232 		ret = nvkm_uoutp_mthd_acquire_lvds(outp, args->v0.lvds.dual, args->v0.lvds.bpc8);
233 		break;
234 	case NVIF_OUTP_ACQUIRE_V0_DP:
235 		ret = nvkm_uoutp_mthd_acquire_dp(outp, args->v0.dp.dpcd,
236 						       args->v0.dp.link_nr,
237 						       args->v0.dp.link_bw,
238 						       args->v0.dp.hda != 0,
239 						       args->v0.dp.mst != 0);
240 		break;
241 	default:
242 		ret = -EINVAL;
243 		break;
244 	}
245 
246 	if (ret)
247 		return ret;
248 
249 	args->v0.or = outp->ior->id;
250 	args->v0.link = outp->ior->asy.link;
251 	return 0;
252 }
253 
254 static int
255 nvkm_uoutp_mthd_load_detect(struct nvkm_outp *outp, void *argv, u32 argc)
256 {
257 	union nvif_outp_load_detect_args *args = argv;
258 	int ret;
259 
260 	if (argc != sizeof(args->v0) || args->v0.version != 0)
261 		return -ENOSYS;
262 
263 	ret = nvkm_outp_acquire(outp, NVKM_OUTP_PRIV, false);
264 	if (ret == 0) {
265 		if (outp->ior->func->sense) {
266 			ret = outp->ior->func->sense(outp->ior, args->v0.data);
267 			args->v0.load = ret < 0 ? 0 : ret;
268 		} else {
269 			ret = -EINVAL;
270 		}
271 		nvkm_outp_release(outp, NVKM_OUTP_PRIV);
272 	}
273 
274 	return ret;
275 }
276 
277 static int
278 nvkm_uoutp_mthd_acquired(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc)
279 {
280 	switch (mthd) {
281 	case NVIF_OUTP_V0_RELEASE    : return nvkm_uoutp_mthd_release    (outp, argv, argc);
282 	case NVIF_OUTP_V0_INFOFRAME  : return nvkm_uoutp_mthd_infoframe  (outp, argv, argc);
283 	case NVIF_OUTP_V0_HDA_ELD    : return nvkm_uoutp_mthd_hda_eld    (outp, argv, argc);
284 	case NVIF_OUTP_V0_DP_RETRAIN : return nvkm_uoutp_mthd_dp_retrain (outp, argv, argc);
285 	case NVIF_OUTP_V0_DP_MST_VCPI: return nvkm_uoutp_mthd_dp_mst_vcpi(outp, argv, argc);
286 	default:
287 		break;
288 	}
289 
290 	return -EINVAL;
291 }
292 
293 static int
294 nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc)
295 {
296 	switch (mthd) {
297 	case NVIF_OUTP_V0_LOAD_DETECT: return nvkm_uoutp_mthd_load_detect(outp, argv, argc);
298 	case NVIF_OUTP_V0_ACQUIRE    : return nvkm_uoutp_mthd_acquire    (outp, argv, argc);
299 	case NVIF_OUTP_V0_DP_AUX_PWR : return nvkm_uoutp_mthd_dp_aux_pwr (outp, argv, argc);
300 	default:
301 		break;
302 	}
303 
304 	return 1;
305 }
306 
307 static int
308 nvkm_uoutp_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc)
309 {
310 	struct nvkm_outp *outp = nvkm_uoutp(object);
311 	struct nvkm_disp *disp = outp->disp;
312 	int ret;
313 
314 	mutex_lock(&disp->super.mutex);
315 
316 	ret = nvkm_uoutp_mthd_noacquire(outp, mthd, argv, argc);
317 	if (ret <= 0)
318 		goto done;
319 
320 	if (outp->ior)
321 		ret = nvkm_uoutp_mthd_acquired(outp, mthd, argv, argc);
322 	else
323 		ret = -EIO;
324 
325 done:
326 	mutex_unlock(&disp->super.mutex);
327 	return ret;
328 }
329 
330 static void *
331 nvkm_uoutp_dtor(struct nvkm_object *object)
332 {
333 	struct nvkm_outp *outp = nvkm_uoutp(object);
334 	struct nvkm_disp *disp = outp->disp;
335 
336 	spin_lock(&disp->client.lock);
337 	outp->object.func = NULL;
338 	spin_unlock(&disp->client.lock);
339 	return NULL;
340 }
341 
342 static const struct nvkm_object_func
343 nvkm_uoutp = {
344 	.dtor = nvkm_uoutp_dtor,
345 	.mthd = nvkm_uoutp_mthd,
346 };
347 
348 int
349 nvkm_uoutp_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nvkm_object **pobject)
350 {
351 	struct nvkm_disp *disp = nvkm_udisp(oclass->parent);
352 	struct nvkm_outp *outt, *outp = NULL;
353 	union nvif_outp_args *args = argv;
354 	int ret;
355 
356 	if (argc != sizeof(args->v0) || args->v0.version != 0)
357 		return -ENOSYS;
358 
359 	list_for_each_entry(outt, &disp->outps, head) {
360 		if (outt->index == args->v0.id) {
361 			outp = outt;
362 			break;
363 		}
364 	}
365 
366 	if (!outp)
367 		return -EINVAL;
368 
369 	ret = -EBUSY;
370 	spin_lock(&disp->client.lock);
371 	if (!outp->object.func) {
372 		nvkm_object_ctor(&nvkm_uoutp, oclass, &outp->object);
373 		*pobject = &outp->object;
374 		ret = 0;
375 	}
376 	spin_unlock(&disp->client.lock);
377 	return ret;
378 }
379