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