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 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 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 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 * 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 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 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