1 /* $NetBSD: nouveau_nvkm_engine_disp_rootnv50.c,v 1.4 2021/12/18 23:45:35 riastradh Exp $ */
2
3 /*
4 * Copyright 2012 Red Hat 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: Ben Skeggs
25 */
26 #include <sys/cdefs.h>
27 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_engine_disp_rootnv50.c,v 1.4 2021/12/18 23:45:35 riastradh Exp $");
28
29 #include "rootnv50.h"
30 #include "channv50.h"
31 #include "dp.h"
32 #include "head.h"
33 #include "ior.h"
34
35 #include <core/client.h>
36
37 #include <nvif/class.h>
38 #include <nvif/cl5070.h>
39 #include <nvif/unpack.h>
40
41 static int
nv50_disp_root_mthd_(struct nvkm_object * object,u32 mthd,void * data,u32 size)42 nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
43 {
44 union {
45 struct nv50_disp_mthd_v0 v0;
46 struct nv50_disp_mthd_v1 v1;
47 } *args = data;
48 struct nv50_disp_root *root = nv50_disp_root(object);
49 struct nv50_disp *disp = root->disp;
50 struct nvkm_outp *temp, *outp = NULL;
51 struct nvkm_head *head;
52 u16 type, mask = 0;
53 int hidx, ret = -ENOSYS;
54
55 if (mthd != NV50_DISP_MTHD)
56 return -EINVAL;
57
58 nvif_ioctl(object, "disp mthd size %d\n", size);
59 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
60 nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n",
61 args->v0.version, args->v0.method, args->v0.head);
62 mthd = args->v0.method;
63 hidx = args->v0.head;
64 } else
65 if (!(ret = nvif_unpack(ret, &data, &size, args->v1, 1, 1, true))) {
66 nvif_ioctl(object, "disp mthd vers %d mthd %02x "
67 "type %04x mask %04x\n",
68 args->v1.version, args->v1.method,
69 args->v1.hasht, args->v1.hashm);
70 mthd = args->v1.method;
71 type = args->v1.hasht;
72 mask = args->v1.hashm;
73 hidx = ffs((mask >> 8) & 0x0f) - 1;
74 } else
75 return ret;
76
77 if (!(head = nvkm_head_find(&disp->base, hidx)))
78 return -ENXIO;
79
80 if (mask) {
81 list_for_each_entry(temp, &disp->base.outp, head) {
82 if ((temp->info.hasht == type) &&
83 (temp->info.hashm & mask) == mask) {
84 outp = temp;
85 break;
86 }
87 }
88 if (outp == NULL)
89 return -ENXIO;
90 }
91
92 switch (mthd) {
93 case NV50_DISP_SCANOUTPOS: {
94 return nvkm_head_mthd_scanoutpos(object, head, data, size);
95 }
96 default:
97 break;
98 }
99
100 switch (mthd * !!outp) {
101 case NV50_DISP_MTHD_V1_ACQUIRE: {
102 union {
103 struct nv50_disp_acquire_v0 v0;
104 } *args = data;
105 int ret = -ENOSYS;
106 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
107 ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER);
108 if (ret == 0) {
109 args->v0.or = outp->ior->id;
110 args->v0.link = outp->ior->asy.link;
111 }
112 }
113 return ret;
114 }
115 break;
116 case NV50_DISP_MTHD_V1_RELEASE:
117 nvkm_outp_release(outp, NVKM_OUTP_USER);
118 return 0;
119 case NV50_DISP_MTHD_V1_DAC_LOAD: {
120 union {
121 struct nv50_disp_dac_load_v0 v0;
122 } *args = data;
123 int ret = -ENOSYS;
124 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
125 if (args->v0.data & 0xfff00000)
126 return -EINVAL;
127 ret = nvkm_outp_acquire(outp, NVKM_OUTP_PRIV);
128 if (ret)
129 return ret;
130 ret = outp->ior->func->sense(outp->ior, args->v0.data);
131 nvkm_outp_release(outp, NVKM_OUTP_PRIV);
132 if (ret < 0)
133 return ret;
134 args->v0.load = ret;
135 return 0;
136 } else
137 return ret;
138 }
139 break;
140 case NV50_DISP_MTHD_V1_SOR_HDA_ELD: {
141 union {
142 struct nv50_disp_sor_hda_eld_v0 v0;
143 } *args = data;
144 struct nvkm_ior *ior = outp->ior;
145 int ret = -ENOSYS;
146
147 nvif_ioctl(object, "disp sor hda eld size %d\n", size);
148 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
149 nvif_ioctl(object, "disp sor hda eld vers %d\n",
150 args->v0.version);
151 if (size > 0x60)
152 return -E2BIG;
153 } else
154 return ret;
155
156 if (!ior->func->hda.hpd)
157 return -ENODEV;
158
159 if (size && args->v0.data[0]) {
160 if (outp->info.type == DCB_OUTPUT_DP)
161 ior->func->dp.audio(ior, hidx, true);
162 ior->func->hda.hpd(ior, hidx, true);
163 ior->func->hda.eld(ior, data, size);
164 } else {
165 if (outp->info.type == DCB_OUTPUT_DP)
166 ior->func->dp.audio(ior, hidx, false);
167 ior->func->hda.hpd(ior, hidx, false);
168 }
169
170 return 0;
171 }
172 break;
173 case NV50_DISP_MTHD_V1_SOR_HDMI_PWR: {
174 union {
175 struct nv50_disp_sor_hdmi_pwr_v0 v0;
176 } *args = data;
177 u8 *vendor, vendor_size;
178 u8 *avi, avi_size;
179 int ret = -ENOSYS;
180
181 nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
182 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
183 nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
184 "max_ac_packet %d rekey %d scdc %d\n",
185 args->v0.version, args->v0.state,
186 args->v0.max_ac_packet, args->v0.rekey,
187 args->v0.scdc);
188 if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
189 return -EINVAL;
190 if ((args->v0.avi_infoframe_length
191 + args->v0.vendor_infoframe_length) > size)
192 return -EINVAL;
193 else
194 if ((args->v0.avi_infoframe_length
195 + args->v0.vendor_infoframe_length) < size)
196 return -E2BIG;
197 avi = data;
198 avi_size = args->v0.avi_infoframe_length;
199 vendor = avi + avi_size;
200 vendor_size = args->v0.vendor_infoframe_length;
201 } else
202 return ret;
203
204 if (!outp->ior->func->hdmi.ctrl)
205 return -ENODEV;
206
207 outp->ior->func->hdmi.ctrl(outp->ior, hidx, args->v0.state,
208 args->v0.max_ac_packet,
209 args->v0.rekey, avi, avi_size,
210 vendor, vendor_size);
211
212 if (outp->ior->func->hdmi.scdc)
213 outp->ior->func->hdmi.scdc(
214 outp->ior, hidx, args->v0.scdc);
215
216 return 0;
217 }
218 break;
219 case NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT: {
220 union {
221 struct nv50_disp_sor_lvds_script_v0 v0;
222 } *args = data;
223 int ret = -ENOSYS;
224 nvif_ioctl(object, "disp sor lvds script size %d\n", size);
225 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
226 nvif_ioctl(object, "disp sor lvds script "
227 "vers %d name %04x\n",
228 args->v0.version, args->v0.script);
229 disp->sor.lvdsconf = args->v0.script;
230 return 0;
231 } else
232 return ret;
233 }
234 break;
235 case NV50_DISP_MTHD_V1_SOR_DP_MST_LINK: {
236 struct nvkm_dp *dp = nvkm_dp(outp);
237 union {
238 struct nv50_disp_sor_dp_mst_link_v0 v0;
239 } *args = data;
240 int ret = -ENOSYS;
241 nvif_ioctl(object, "disp sor dp mst link size %d\n", size);
242 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
243 nvif_ioctl(object, "disp sor dp mst link vers %d state %d\n",
244 args->v0.version, args->v0.state);
245 dp->lt.mst = !!args->v0.state;
246 return 0;
247 } else
248 return ret;
249 }
250 break;
251 case NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI: {
252 union {
253 struct nv50_disp_sor_dp_mst_vcpi_v0 v0;
254 } *args = data;
255 int ret = -ENOSYS;
256 nvif_ioctl(object, "disp sor dp mst vcpi size %d\n", size);
257 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
258 nvif_ioctl(object, "disp sor dp mst vcpi vers %d "
259 "slot %02x/%02x pbn %04x/%04x\n",
260 args->v0.version, args->v0.start_slot,
261 args->v0.num_slots, args->v0.pbn,
262 args->v0.aligned_pbn);
263 if (!outp->ior->func->dp.vcpi)
264 return -ENODEV;
265 outp->ior->func->dp.vcpi(outp->ior, hidx,
266 args->v0.start_slot,
267 args->v0.num_slots,
268 args->v0.pbn,
269 args->v0.aligned_pbn);
270 return 0;
271 } else
272 return ret;
273 }
274 break;
275 default:
276 break;
277 }
278
279 return -EINVAL;
280 }
281
282 static int
nv50_disp_root_child_new_(const struct nvkm_oclass * oclass,void * argv,u32 argc,struct nvkm_object ** pobject)283 nv50_disp_root_child_new_(const struct nvkm_oclass *oclass,
284 void *argv, u32 argc, struct nvkm_object **pobject)
285 {
286 struct nv50_disp *disp = nv50_disp_root(oclass->parent)->disp;
287 const struct nv50_disp_user *user = oclass->priv;
288 return user->ctor(oclass, argv, argc, disp, pobject);
289 }
290
291 static int
nv50_disp_root_child_get_(struct nvkm_object * object,int index,struct nvkm_oclass * sclass)292 nv50_disp_root_child_get_(struct nvkm_object *object, int index,
293 struct nvkm_oclass *sclass)
294 {
295 struct nv50_disp_root *root = nv50_disp_root(object);
296
297 if (root->func->user[index].ctor) {
298 sclass->base = root->func->user[index].base;
299 sclass->priv = root->func->user + index;
300 sclass->ctor = nv50_disp_root_child_new_;
301 return 0;
302 }
303
304 return -EINVAL;
305 }
306
307 static void *
nv50_disp_root_dtor_(struct nvkm_object * object)308 nv50_disp_root_dtor_(struct nvkm_object *object)
309 {
310 struct nv50_disp_root *root = nv50_disp_root(object);
311 return root;
312 }
313
314 static const struct nvkm_object_func
315 nv50_disp_root_ = {
316 .dtor = nv50_disp_root_dtor_,
317 .mthd = nv50_disp_root_mthd_,
318 .ntfy = nvkm_disp_ntfy,
319 .sclass = nv50_disp_root_child_get_,
320 };
321
322 int
nv50_disp_root_new_(const struct nv50_disp_root_func * func,struct nvkm_disp * base,const struct nvkm_oclass * oclass,void * data,u32 size,struct nvkm_object ** pobject)323 nv50_disp_root_new_(const struct nv50_disp_root_func *func,
324 struct nvkm_disp *base, const struct nvkm_oclass *oclass,
325 void *data, u32 size, struct nvkm_object **pobject)
326 {
327 struct nv50_disp *disp = nv50_disp(base);
328 struct nv50_disp_root *root;
329
330 if (!(root = kzalloc(sizeof(*root), GFP_KERNEL)))
331 return -ENOMEM;
332 *pobject = &root->object;
333
334 nvkm_object_ctor(&nv50_disp_root_, oclass, &root->object);
335 root->func = func;
336 root->disp = disp;
337 return 0;
338 }
339
340 static const struct nv50_disp_root_func
341 nv50_disp_root = {
342 .user = {
343 {{0,0,NV50_DISP_CURSOR }, nv50_disp_curs_new },
344 {{0,0,NV50_DISP_OVERLAY }, nv50_disp_oimm_new },
345 {{0,0,NV50_DISP_BASE_CHANNEL_DMA }, nv50_disp_base_new },
346 {{0,0,NV50_DISP_CORE_CHANNEL_DMA }, nv50_disp_core_new },
347 {{0,0,NV50_DISP_OVERLAY_CHANNEL_DMA}, nv50_disp_ovly_new },
348 {}
349 },
350 };
351
352 static int
nv50_disp_root_new(struct nvkm_disp * disp,const struct nvkm_oclass * oclass,void * data,u32 size,struct nvkm_object ** pobject)353 nv50_disp_root_new(struct nvkm_disp *disp, const struct nvkm_oclass *oclass,
354 void *data, u32 size, struct nvkm_object **pobject)
355 {
356 return nv50_disp_root_new_(&nv50_disp_root, disp, oclass,
357 data, size, pobject);
358 }
359
360 const struct nvkm_disp_oclass
361 nv50_disp_root_oclass = {
362 .base.oclass = NV50_DISP,
363 .base.minver = -1,
364 .base.maxver = -1,
365 .ctor = nv50_disp_root_new,
366 };
367