xref: /openbmc/linux/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
11b255f1cSBen Skeggs /*
21b255f1cSBen Skeggs  * Copyright 2021 Red Hat Inc.
31b255f1cSBen Skeggs  *
41b255f1cSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
51b255f1cSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
61b255f1cSBen Skeggs  * to deal in the Software without restriction, including without limitation
71b255f1cSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
81b255f1cSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
91b255f1cSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
101b255f1cSBen Skeggs  *
111b255f1cSBen Skeggs  * The above copyright notice and this permission notice shall be included in
121b255f1cSBen Skeggs  * all copies or substantial portions of the Software.
131b255f1cSBen Skeggs  *
141b255f1cSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
151b255f1cSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
161b255f1cSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
171b255f1cSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
181b255f1cSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
191b255f1cSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
201b255f1cSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
211b255f1cSBen Skeggs  */
221b255f1cSBen Skeggs #define nvkm_uoutp(p) container_of((p), struct nvkm_outp, object)
231b255f1cSBen Skeggs #include "outp.h"
24a62b7493SBen Skeggs #include "dp.h"
25f530bc60SBen Skeggs #include "head.h"
26dfc4005fSBen Skeggs #include "ior.h"
271b255f1cSBen Skeggs 
281b255f1cSBen Skeggs #include <nvif/if0012.h>
291b255f1cSBen Skeggs 
301b255f1cSBen Skeggs static int
nvkm_uoutp_mthd_dp_mst_vcpi(struct nvkm_outp * outp,void * argv,u32 argc)318c7d980dSBen Skeggs nvkm_uoutp_mthd_dp_mst_vcpi(struct nvkm_outp *outp, void *argv, u32 argc)
328c7d980dSBen Skeggs {
338c7d980dSBen Skeggs 	struct nvkm_ior *ior = outp->ior;
348c7d980dSBen Skeggs 	union nvif_outp_dp_mst_vcpi_args *args = argv;
358c7d980dSBen Skeggs 
368c7d980dSBen Skeggs 	if (argc != sizeof(args->v0) || args->v0.version != 0)
378c7d980dSBen Skeggs 		return -ENOSYS;
388c7d980dSBen Skeggs 	if (!ior->func->dp || !ior->func->dp->vcpi || !nvkm_head_find(outp->disp, args->v0.head))
398c7d980dSBen Skeggs 		return -EINVAL;
408c7d980dSBen Skeggs 
418c7d980dSBen Skeggs 	ior->func->dp->vcpi(ior, args->v0.head, args->v0.start_slot, args->v0.num_slots,
428c7d980dSBen Skeggs 				 args->v0.pbn, args->v0.aligned_pbn);
438c7d980dSBen Skeggs 	return 0;
448c7d980dSBen Skeggs }
458c7d980dSBen Skeggs 
468c7d980dSBen Skeggs static int
nvkm_uoutp_mthd_dp_retrain(struct nvkm_outp * outp,void * argv,u32 argc)478bb30c88SBen Skeggs nvkm_uoutp_mthd_dp_retrain(struct nvkm_outp *outp, void *argv, u32 argc)
488bb30c88SBen Skeggs {
498bb30c88SBen Skeggs 	union nvif_outp_dp_retrain_args *args = argv;
508bb30c88SBen Skeggs 
518bb30c88SBen Skeggs 	if (argc != sizeof(args->vn))
528bb30c88SBen Skeggs 		return -ENOSYS;
538bb30c88SBen Skeggs 
548bb30c88SBen Skeggs 	if (!atomic_read(&outp->dp.lt.done))
558bb30c88SBen Skeggs 		return 0;
568bb30c88SBen Skeggs 
578bb30c88SBen Skeggs 	return outp->func->acquire(outp);
588bb30c88SBen Skeggs }
598bb30c88SBen Skeggs 
608bb30c88SBen Skeggs static int
nvkm_uoutp_mthd_dp_aux_pwr(struct nvkm_outp * outp,void * argv,u32 argc)61a62b7493SBen Skeggs nvkm_uoutp_mthd_dp_aux_pwr(struct nvkm_outp *outp, void *argv, u32 argc)
62a62b7493SBen Skeggs {
63a62b7493SBen Skeggs 	union nvif_outp_dp_aux_pwr_args *args = argv;
64a62b7493SBen Skeggs 
65a62b7493SBen Skeggs 	if (argc != sizeof(args->v0) || args->v0.version != 0)
66a62b7493SBen Skeggs 		return -ENOSYS;
67a62b7493SBen Skeggs 
68a62b7493SBen Skeggs 	outp->dp.enabled = !!args->v0.state;
69a62b7493SBen Skeggs 	nvkm_dp_enable(outp, outp->dp.enabled);
70a62b7493SBen Skeggs 	return 0;
71a62b7493SBen Skeggs }
72a62b7493SBen Skeggs 
73a62b7493SBen Skeggs static int
nvkm_uoutp_mthd_hda_eld(struct nvkm_outp * outp,void * argv,u32 argc)74a9f5d772SBen Skeggs nvkm_uoutp_mthd_hda_eld(struct nvkm_outp *outp, void *argv, u32 argc)
75a9f5d772SBen Skeggs {
76a9f5d772SBen Skeggs 	struct nvkm_ior *ior = outp->ior;
77a9f5d772SBen Skeggs 	union nvif_outp_hda_eld_args *args = argv;
78a9f5d772SBen Skeggs 
79a9f5d772SBen Skeggs 	if (argc < sizeof(args->v0) || args->v0.version != 0)
80a9f5d772SBen Skeggs 		return -ENOSYS;
81a9f5d772SBen Skeggs 	argc -= sizeof(args->v0);
82a9f5d772SBen Skeggs 
83a9f5d772SBen Skeggs 	if (!ior->hda || !nvkm_head_find(outp->disp, args->v0.head))
84a9f5d772SBen Skeggs 		return -EINVAL;
85a9f5d772SBen Skeggs 	if (argc > 0x60)
86a9f5d772SBen Skeggs 		return -E2BIG;
87a9f5d772SBen Skeggs 
88a9f5d772SBen Skeggs 	if (argc && args->v0.data[0]) {
89a9f5d772SBen Skeggs 		if (outp->info.type == DCB_OUTPUT_DP)
90a9f5d772SBen Skeggs 			ior->func->dp->audio(ior, args->v0.head, true);
91a9f5d772SBen Skeggs 		ior->func->hda->hpd(ior, args->v0.head, true);
92a9f5d772SBen Skeggs 		ior->func->hda->eld(ior, args->v0.head, args->v0.data, argc);
93a9f5d772SBen Skeggs 	} else {
94a9f5d772SBen Skeggs 		if (outp->info.type == DCB_OUTPUT_DP)
95a9f5d772SBen Skeggs 			ior->func->dp->audio(ior, args->v0.head, false);
96a9f5d772SBen Skeggs 		ior->func->hda->hpd(ior, args->v0.head, false);
97a9f5d772SBen Skeggs 	}
98a9f5d772SBen Skeggs 
99a9f5d772SBen Skeggs 	return 0;
100a9f5d772SBen Skeggs }
101a9f5d772SBen Skeggs 
102a9f5d772SBen Skeggs static int
nvkm_uoutp_mthd_infoframe(struct nvkm_outp * outp,void * argv,u32 argc)103f530bc60SBen Skeggs nvkm_uoutp_mthd_infoframe(struct nvkm_outp *outp, void *argv, u32 argc)
104f530bc60SBen Skeggs {
105f530bc60SBen Skeggs 	struct nvkm_ior *ior = outp->ior;
106f530bc60SBen Skeggs 	union nvif_outp_infoframe_args *args = argv;
107c02f20d3SBen Skeggs 	ssize_t size = argc - sizeof(*args);
108f530bc60SBen Skeggs 
109f530bc60SBen Skeggs 	if (argc < sizeof(args->v0) || args->v0.version != 0)
110f530bc60SBen Skeggs 		return -ENOSYS;
111f530bc60SBen Skeggs 	if (!nvkm_head_find(outp->disp, args->v0.head))
112f530bc60SBen Skeggs 		return -EINVAL;
113f530bc60SBen Skeggs 
114f530bc60SBen Skeggs 	switch (ior->func->hdmi ? args->v0.type : 0xff) {
115f530bc60SBen Skeggs 	case NVIF_OUTP_INFOFRAME_V0_AVI:
116c02f20d3SBen Skeggs 		ior->func->hdmi->infoframe_avi(ior, args->v0.head, &args->v0.data, size);
117f530bc60SBen Skeggs 		return 0;
118f530bc60SBen Skeggs 	case NVIF_OUTP_INFOFRAME_V0_VSI:
119c02f20d3SBen Skeggs 		ior->func->hdmi->infoframe_vsi(ior, args->v0.head, &args->v0.data, size);
120f530bc60SBen Skeggs 		return 0;
121f530bc60SBen Skeggs 	default:
122f530bc60SBen Skeggs 		break;
123f530bc60SBen Skeggs 	}
124f530bc60SBen Skeggs 
125f530bc60SBen Skeggs 	return -EINVAL;
126f530bc60SBen Skeggs }
127f530bc60SBen Skeggs 
128f530bc60SBen Skeggs static int
nvkm_uoutp_mthd_release(struct nvkm_outp * outp,void * argv,u32 argc)129ea6143a8SBen Skeggs nvkm_uoutp_mthd_release(struct nvkm_outp *outp, void *argv, u32 argc)
130ea6143a8SBen Skeggs {
131f530bc60SBen Skeggs 	struct nvkm_head *head = outp->asy.head;
132f530bc60SBen Skeggs 	struct nvkm_ior *ior = outp->ior;
133ea6143a8SBen Skeggs 	union nvif_outp_release_args *args = argv;
134ea6143a8SBen Skeggs 
135ea6143a8SBen Skeggs 	if (argc != sizeof(args->vn))
136ea6143a8SBen Skeggs 		return -ENOSYS;
137ea6143a8SBen Skeggs 
138f530bc60SBen Skeggs 	if (ior->func->hdmi && head) {
139f530bc60SBen Skeggs 		ior->func->hdmi->infoframe_avi(ior, head->id, NULL, 0);
140f530bc60SBen Skeggs 		ior->func->hdmi->infoframe_vsi(ior, head->id, NULL, 0);
141f530bc60SBen Skeggs 		ior->func->hdmi->ctrl(ior, head->id, false, 0, 0);
142f530bc60SBen Skeggs 	}
143f530bc60SBen Skeggs 
144ea6143a8SBen Skeggs 	nvkm_outp_release(outp, NVKM_OUTP_USER);
145ea6143a8SBen Skeggs 	return 0;
146ea6143a8SBen Skeggs }
147ea6143a8SBen Skeggs 
148ea6143a8SBen Skeggs static int
nvkm_uoutp_mthd_acquire_dp(struct nvkm_outp * outp,u8 dpcd[DP_RECEIVER_CAP_SIZE],u8 link_nr,u8 link_bw,bool hda,bool mst)149*25feda6fSKees Cook nvkm_uoutp_mthd_acquire_dp(struct nvkm_outp *outp, u8 dpcd[DP_RECEIVER_CAP_SIZE],
15081344372SBen Skeggs 			   u8 link_nr, u8 link_bw, bool hda, bool mst)
15181344372SBen Skeggs {
15281344372SBen Skeggs 	int ret;
15381344372SBen Skeggs 
15481344372SBen Skeggs 	ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, hda);
15581344372SBen Skeggs 	if (ret)
15681344372SBen Skeggs 		return ret;
15781344372SBen Skeggs 
15881344372SBen Skeggs 	memcpy(outp->dp.dpcd, dpcd, sizeof(outp->dp.dpcd));
15981344372SBen Skeggs 	outp->dp.lt.nr = link_nr;
16081344372SBen Skeggs 	outp->dp.lt.bw = link_bw;
16181344372SBen Skeggs 	outp->dp.lt.mst = mst;
16281344372SBen Skeggs 	return 0;
16381344372SBen Skeggs }
16481344372SBen Skeggs 
16581344372SBen Skeggs static int
nvkm_uoutp_mthd_acquire_tmds(struct nvkm_outp * outp,u8 head,u8 hdmi,u8 hdmi_max_ac_packet,u8 hdmi_rekey,u8 hdmi_scdc,u8 hdmi_hda)166f530bc60SBen Skeggs nvkm_uoutp_mthd_acquire_tmds(struct nvkm_outp *outp, u8 head, u8 hdmi, u8 hdmi_max_ac_packet,
167f530bc60SBen Skeggs 			     u8 hdmi_rekey, u8 hdmi_scdc, u8 hdmi_hda)
168f530bc60SBen Skeggs {
169f530bc60SBen Skeggs 	struct nvkm_ior *ior;
170f530bc60SBen Skeggs 	int ret;
171f530bc60SBen Skeggs 
172f530bc60SBen Skeggs 	if (!(outp->asy.head = nvkm_head_find(outp->disp, head)))
173f530bc60SBen Skeggs 		return -EINVAL;
174f530bc60SBen Skeggs 
175f530bc60SBen Skeggs 	ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, hdmi && hdmi_hda);
176f530bc60SBen Skeggs 	if (ret)
177f530bc60SBen Skeggs 		return ret;
178f530bc60SBen Skeggs 
179f530bc60SBen Skeggs 	ior = outp->ior;
180f530bc60SBen Skeggs 
181f530bc60SBen Skeggs 	if (hdmi) {
182f530bc60SBen Skeggs 		if (!ior->func->hdmi ||
183f530bc60SBen Skeggs 		    hdmi_max_ac_packet > 0x1f || hdmi_rekey > 0x7f ||
184f530bc60SBen Skeggs 		    (hdmi_scdc && !ior->func->hdmi->scdc)) {
185f530bc60SBen Skeggs 			nvkm_outp_release(outp, NVKM_OUTP_USER);
186f530bc60SBen Skeggs 			return -EINVAL;
187f530bc60SBen Skeggs 		}
188f530bc60SBen Skeggs 
189f530bc60SBen Skeggs 		ior->func->hdmi->ctrl(ior, head, hdmi, hdmi_max_ac_packet, hdmi_rekey);
190f530bc60SBen Skeggs 		if (ior->func->hdmi->scdc)
191f530bc60SBen Skeggs 			ior->func->hdmi->scdc(ior, hdmi_scdc);
192f530bc60SBen Skeggs 	}
193f530bc60SBen Skeggs 
194f530bc60SBen Skeggs 	return 0;
195f530bc60SBen Skeggs }
196f530bc60SBen Skeggs 
197f530bc60SBen Skeggs static int
nvkm_uoutp_mthd_acquire_lvds(struct nvkm_outp * outp,bool dual,bool bpc8)1989793083fSBen Skeggs nvkm_uoutp_mthd_acquire_lvds(struct nvkm_outp *outp, bool dual, bool bpc8)
1999793083fSBen Skeggs {
2009793083fSBen Skeggs 	if (outp->info.type != DCB_OUTPUT_LVDS)
2019793083fSBen Skeggs 		return -EINVAL;
2029793083fSBen Skeggs 
2039793083fSBen Skeggs 	outp->lvds.dual = dual;
2049793083fSBen Skeggs 	outp->lvds.bpc8 = bpc8;
2059793083fSBen Skeggs 
2069793083fSBen Skeggs 	return nvkm_outp_acquire(outp, NVKM_OUTP_USER, false);
2079793083fSBen Skeggs }
2089793083fSBen Skeggs 
2099793083fSBen Skeggs static int
nvkm_uoutp_mthd_acquire(struct nvkm_outp * outp,void * argv,u32 argc)210ea6143a8SBen Skeggs nvkm_uoutp_mthd_acquire(struct nvkm_outp *outp, void *argv, u32 argc)
211ea6143a8SBen Skeggs {
212ea6143a8SBen Skeggs 	union nvif_outp_acquire_args *args = argv;
213ea6143a8SBen Skeggs 	int ret;
214ea6143a8SBen Skeggs 
215ea6143a8SBen Skeggs 	if (argc != sizeof(args->v0) || args->v0.version != 0)
216ea6143a8SBen Skeggs 		return -ENOSYS;
21781344372SBen Skeggs 	if (outp->ior)
21881344372SBen Skeggs 		return -EBUSY;
219ea6143a8SBen Skeggs 
220ea6143a8SBen Skeggs 	switch (args->v0.proto) {
221ea6143a8SBen Skeggs 	case NVIF_OUTP_ACQUIRE_V0_RGB_CRT:
222ea6143a8SBen Skeggs 		ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, false);
223ea6143a8SBen Skeggs 		break;
224ea6143a8SBen Skeggs 	case NVIF_OUTP_ACQUIRE_V0_TMDS:
225f530bc60SBen Skeggs 		ret = nvkm_uoutp_mthd_acquire_tmds(outp, args->v0.tmds.head,
226f530bc60SBen Skeggs 							 args->v0.tmds.hdmi,
227f530bc60SBen Skeggs 							 args->v0.tmds.hdmi_max_ac_packet,
228f530bc60SBen Skeggs 							 args->v0.tmds.hdmi_rekey,
229f530bc60SBen Skeggs 							 args->v0.tmds.hdmi_scdc,
230f530bc60SBen Skeggs 							 args->v0.tmds.hdmi_hda);
231f530bc60SBen Skeggs 		break;
2329793083fSBen Skeggs 	case NVIF_OUTP_ACQUIRE_V0_LVDS:
2339793083fSBen Skeggs 		ret = nvkm_uoutp_mthd_acquire_lvds(outp, args->v0.lvds.dual, args->v0.lvds.bpc8);
2349793083fSBen Skeggs 		break;
23581344372SBen Skeggs 	case NVIF_OUTP_ACQUIRE_V0_DP:
23681344372SBen Skeggs 		ret = nvkm_uoutp_mthd_acquire_dp(outp, args->v0.dp.dpcd,
23781344372SBen Skeggs 						       args->v0.dp.link_nr,
23881344372SBen Skeggs 						       args->v0.dp.link_bw,
23981344372SBen Skeggs 						       args->v0.dp.hda != 0,
24081344372SBen Skeggs 						       args->v0.dp.mst != 0);
24181344372SBen Skeggs 		break;
242ea6143a8SBen Skeggs 	default:
243ea6143a8SBen Skeggs 		ret = -EINVAL;
244ea6143a8SBen Skeggs 		break;
245ea6143a8SBen Skeggs 	}
246ea6143a8SBen Skeggs 
247ea6143a8SBen Skeggs 	if (ret)
248ea6143a8SBen Skeggs 		return ret;
249ea6143a8SBen Skeggs 
250ea6143a8SBen Skeggs 	args->v0.or = outp->ior->id;
251ea6143a8SBen Skeggs 	args->v0.link = outp->ior->asy.link;
252ea6143a8SBen Skeggs 	return 0;
253ea6143a8SBen Skeggs }
254ea6143a8SBen Skeggs 
255ea6143a8SBen Skeggs static int
nvkm_uoutp_mthd_load_detect(struct nvkm_outp * outp,void * argv,u32 argc)256dfc4005fSBen Skeggs nvkm_uoutp_mthd_load_detect(struct nvkm_outp *outp, void *argv, u32 argc)
257dfc4005fSBen Skeggs {
258dfc4005fSBen Skeggs 	union nvif_outp_load_detect_args *args = argv;
259dfc4005fSBen Skeggs 	int ret;
260dfc4005fSBen Skeggs 
261dfc4005fSBen Skeggs 	if (argc != sizeof(args->v0) || args->v0.version != 0)
262dfc4005fSBen Skeggs 		return -ENOSYS;
263dfc4005fSBen Skeggs 
264dfc4005fSBen Skeggs 	ret = nvkm_outp_acquire(outp, NVKM_OUTP_PRIV, false);
265dfc4005fSBen Skeggs 	if (ret == 0) {
266dfc4005fSBen Skeggs 		if (outp->ior->func->sense) {
267dfc4005fSBen Skeggs 			ret = outp->ior->func->sense(outp->ior, args->v0.data);
268dfc4005fSBen Skeggs 			args->v0.load = ret < 0 ? 0 : ret;
269dfc4005fSBen Skeggs 		} else {
270dfc4005fSBen Skeggs 			ret = -EINVAL;
271dfc4005fSBen Skeggs 		}
272dfc4005fSBen Skeggs 		nvkm_outp_release(outp, NVKM_OUTP_PRIV);
273dfc4005fSBen Skeggs 	}
274dfc4005fSBen Skeggs 
275dfc4005fSBen Skeggs 	return ret;
276dfc4005fSBen Skeggs }
277dfc4005fSBen Skeggs 
278dfc4005fSBen Skeggs static int
nvkm_uoutp_mthd_acquired(struct nvkm_outp * outp,u32 mthd,void * argv,u32 argc)279ea6143a8SBen Skeggs nvkm_uoutp_mthd_acquired(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc)
280ea6143a8SBen Skeggs {
281ea6143a8SBen Skeggs 	switch (mthd) {
282ea6143a8SBen Skeggs 	case NVIF_OUTP_V0_RELEASE    : return nvkm_uoutp_mthd_release    (outp, argv, argc);
283f530bc60SBen Skeggs 	case NVIF_OUTP_V0_INFOFRAME  : return nvkm_uoutp_mthd_infoframe  (outp, argv, argc);
284a9f5d772SBen Skeggs 	case NVIF_OUTP_V0_HDA_ELD    : return nvkm_uoutp_mthd_hda_eld    (outp, argv, argc);
2858bb30c88SBen Skeggs 	case NVIF_OUTP_V0_DP_RETRAIN : return nvkm_uoutp_mthd_dp_retrain (outp, argv, argc);
2868c7d980dSBen Skeggs 	case NVIF_OUTP_V0_DP_MST_VCPI: return nvkm_uoutp_mthd_dp_mst_vcpi(outp, argv, argc);
287ea6143a8SBen Skeggs 	default:
288ea6143a8SBen Skeggs 		break;
289ea6143a8SBen Skeggs 	}
290ea6143a8SBen Skeggs 
291ea6143a8SBen Skeggs 	return -EINVAL;
292ea6143a8SBen Skeggs }
293ea6143a8SBen Skeggs 
294ea6143a8SBen Skeggs static int
nvkm_uoutp_mthd_noacquire(struct nvkm_outp * outp,u32 mthd,void * argv,u32 argc)2951b255f1cSBen Skeggs nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc)
2961b255f1cSBen Skeggs {
2971b255f1cSBen Skeggs 	switch (mthd) {
298dfc4005fSBen Skeggs 	case NVIF_OUTP_V0_LOAD_DETECT: return nvkm_uoutp_mthd_load_detect(outp, argv, argc);
299ea6143a8SBen Skeggs 	case NVIF_OUTP_V0_ACQUIRE    : return nvkm_uoutp_mthd_acquire    (outp, argv, argc);
300a62b7493SBen Skeggs 	case NVIF_OUTP_V0_DP_AUX_PWR : return nvkm_uoutp_mthd_dp_aux_pwr (outp, argv, argc);
3011b255f1cSBen Skeggs 	default:
3021b255f1cSBen Skeggs 		break;
3031b255f1cSBen Skeggs 	}
3041b255f1cSBen Skeggs 
3051b255f1cSBen Skeggs 	return 1;
3061b255f1cSBen Skeggs }
3071b255f1cSBen Skeggs 
3081b255f1cSBen Skeggs static int
nvkm_uoutp_mthd(struct nvkm_object * object,u32 mthd,void * argv,u32 argc)3091b255f1cSBen Skeggs nvkm_uoutp_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc)
3101b255f1cSBen Skeggs {
3111b255f1cSBen Skeggs 	struct nvkm_outp *outp = nvkm_uoutp(object);
3121b255f1cSBen Skeggs 	struct nvkm_disp *disp = outp->disp;
3131b255f1cSBen Skeggs 	int ret;
3141b255f1cSBen Skeggs 
3151b255f1cSBen Skeggs 	mutex_lock(&disp->super.mutex);
3161b255f1cSBen Skeggs 
3171b255f1cSBen Skeggs 	ret = nvkm_uoutp_mthd_noacquire(outp, mthd, argv, argc);
3181b255f1cSBen Skeggs 	if (ret <= 0)
3191b255f1cSBen Skeggs 		goto done;
3201b255f1cSBen Skeggs 
321ea6143a8SBen Skeggs 	if (outp->ior)
322ea6143a8SBen Skeggs 		ret = nvkm_uoutp_mthd_acquired(outp, mthd, argv, argc);
323ea6143a8SBen Skeggs 	else
324ea6143a8SBen Skeggs 		ret = -EIO;
325ea6143a8SBen Skeggs 
3261b255f1cSBen Skeggs done:
3271b255f1cSBen Skeggs 	mutex_unlock(&disp->super.mutex);
3281b255f1cSBen Skeggs 	return ret;
3291b255f1cSBen Skeggs }
3301b255f1cSBen Skeggs 
3311b255f1cSBen Skeggs static void *
nvkm_uoutp_dtor(struct nvkm_object * object)3321b255f1cSBen Skeggs nvkm_uoutp_dtor(struct nvkm_object *object)
3331b255f1cSBen Skeggs {
3341b255f1cSBen Skeggs 	struct nvkm_outp *outp = nvkm_uoutp(object);
3351b255f1cSBen Skeggs 	struct nvkm_disp *disp = outp->disp;
3361b255f1cSBen Skeggs 
3371b255f1cSBen Skeggs 	spin_lock(&disp->client.lock);
3381b255f1cSBen Skeggs 	outp->object.func = NULL;
3391b255f1cSBen Skeggs 	spin_unlock(&disp->client.lock);
3401b255f1cSBen Skeggs 	return NULL;
3411b255f1cSBen Skeggs }
3421b255f1cSBen Skeggs 
3431b255f1cSBen Skeggs static const struct nvkm_object_func
3441b255f1cSBen Skeggs nvkm_uoutp = {
3451b255f1cSBen Skeggs 	.dtor = nvkm_uoutp_dtor,
3461b255f1cSBen Skeggs 	.mthd = nvkm_uoutp_mthd,
3471b255f1cSBen Skeggs };
3481b255f1cSBen Skeggs 
3491b255f1cSBen Skeggs int
nvkm_uoutp_new(const struct nvkm_oclass * oclass,void * argv,u32 argc,struct nvkm_object ** pobject)3501b255f1cSBen Skeggs nvkm_uoutp_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nvkm_object **pobject)
3511b255f1cSBen Skeggs {
3521b255f1cSBen Skeggs 	struct nvkm_disp *disp = nvkm_udisp(oclass->parent);
3531b255f1cSBen Skeggs 	struct nvkm_outp *outt, *outp = NULL;
3541b255f1cSBen Skeggs 	union nvif_outp_args *args = argv;
3551b255f1cSBen Skeggs 	int ret;
3561b255f1cSBen Skeggs 
3571b255f1cSBen Skeggs 	if (argc != sizeof(args->v0) || args->v0.version != 0)
3581b255f1cSBen Skeggs 		return -ENOSYS;
3591b255f1cSBen Skeggs 
3601b255f1cSBen Skeggs 	list_for_each_entry(outt, &disp->outps, head) {
3611b255f1cSBen Skeggs 		if (outt->index == args->v0.id) {
3621b255f1cSBen Skeggs 			outp = outt;
3631b255f1cSBen Skeggs 			break;
3641b255f1cSBen Skeggs 		}
3651b255f1cSBen Skeggs 	}
3661b255f1cSBen Skeggs 
3671b255f1cSBen Skeggs 	if (!outp)
3681b255f1cSBen Skeggs 		return -EINVAL;
3691b255f1cSBen Skeggs 
3701b255f1cSBen Skeggs 	ret = -EBUSY;
3711b255f1cSBen Skeggs 	spin_lock(&disp->client.lock);
3721b255f1cSBen Skeggs 	if (!outp->object.func) {
3731b255f1cSBen Skeggs 		nvkm_object_ctor(&nvkm_uoutp, oclass, &outp->object);
3741b255f1cSBen Skeggs 		*pobject = &outp->object;
3751b255f1cSBen Skeggs 		ret = 0;
3761b255f1cSBen Skeggs 	}
3771b255f1cSBen Skeggs 	spin_unlock(&disp->client.lock);
3781b255f1cSBen Skeggs 	return ret;
3791b255f1cSBen Skeggs }
380