1/* $NetBSD: nouveau_nvkm_engine_disp_nv50.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_nv50.c,v 1.4 2021/12/18 23:45:35 riastradh Exp $"); 28 29#include "nv50.h" 30#include "head.h" 31#include "ior.h" 32#include "channv50.h" 33#include "rootnv50.h" 34 35#include <core/client.h> 36#include <core/ramht.h> 37#include <subdev/bios.h> 38#include <subdev/bios/disp.h> 39#include <subdev/bios/init.h> 40#include <subdev/bios/pll.h> 41#include <subdev/devinit.h> 42#include <subdev/timer.h> 43 44static const struct nvkm_disp_oclass * 45nv50_disp_root_(struct nvkm_disp *base) 46{ 47 return nv50_disp(base)->func->root; 48} 49 50static void 51nv50_disp_intr_(struct nvkm_disp *base) 52{ 53 struct nv50_disp *disp = nv50_disp(base); 54 disp->func->intr(disp); 55} 56 57static void 58nv50_disp_fini_(struct nvkm_disp *base) 59{ 60 struct nv50_disp *disp = nv50_disp(base); 61 disp->func->fini(disp); 62} 63 64static int 65nv50_disp_init_(struct nvkm_disp *base) 66{ 67 struct nv50_disp *disp = nv50_disp(base); 68 return disp->func->init(disp); 69} 70 71static void * 72nv50_disp_dtor_(struct nvkm_disp *base) 73{ 74 struct nv50_disp *disp = nv50_disp(base); 75 76 nvkm_ramht_del(&disp->ramht); 77 nvkm_gpuobj_del(&disp->inst); 78 79 nvkm_event_fini(&disp->uevent); 80 if (disp->wq) 81 destroy_workqueue(disp->wq); 82 83 return disp; 84} 85 86static int 87nv50_disp_oneinit_(struct nvkm_disp *base) 88{ 89 struct nv50_disp *disp = nv50_disp(base); 90 const struct nv50_disp_func *func = disp->func; 91 struct nvkm_subdev *subdev = &disp->base.engine.subdev; 92 struct nvkm_device *device = subdev->device; 93 int ret, i; 94 95 if (func->wndw.cnt) { 96 disp->wndw.nr = func->wndw.cnt(&disp->base, &disp->wndw.mask); 97 nvkm_debug(subdev, "Window(s): %d (%08lx)\n", 98 disp->wndw.nr, disp->wndw.mask); 99 } 100 101 disp->head.nr = func->head.cnt(&disp->base, &disp->head.mask); 102 nvkm_debug(subdev, " Head(s): %d (%02lx)\n", 103 disp->head.nr, disp->head.mask); 104 for_each_set_bit(i, &disp->head.mask, disp->head.nr) { 105 ret = func->head.new(&disp->base, i); 106 if (ret) 107 return ret; 108 } 109 110 if (func->dac.cnt) { 111 disp->dac.nr = func->dac.cnt(&disp->base, &disp->dac.mask); 112 nvkm_debug(subdev, " DAC(s): %d (%02lx)\n", 113 disp->dac.nr, disp->dac.mask); 114 for_each_set_bit(i, &disp->dac.mask, disp->dac.nr) { 115 ret = func->dac.new(&disp->base, i); 116 if (ret) 117 return ret; 118 } 119 } 120 121 if (func->pior.cnt) { 122 disp->pior.nr = func->pior.cnt(&disp->base, &disp->pior.mask); 123 nvkm_debug(subdev, " PIOR(s): %d (%02lx)\n", 124 disp->pior.nr, disp->pior.mask); 125 for_each_set_bit(i, &disp->pior.mask, disp->pior.nr) { 126 ret = func->pior.new(&disp->base, i); 127 if (ret) 128 return ret; 129 } 130 } 131 132 disp->sor.nr = func->sor.cnt(&disp->base, &disp->sor.mask); 133 nvkm_debug(subdev, " SOR(s): %d (%02lx)\n", 134 disp->sor.nr, disp->sor.mask); 135 for_each_set_bit(i, &disp->sor.mask, disp->sor.nr) { 136 ret = func->sor.new(&disp->base, i); 137 if (ret) 138 return ret; 139 } 140 141 ret = nvkm_gpuobj_new(device, 0x10000, 0x10000, false, NULL, 142 &disp->inst); 143 if (ret) 144 return ret; 145 146 return nvkm_ramht_new(device, func->ramht_size ? func->ramht_size : 147 0x1000, 0, disp->inst, &disp->ramht); 148} 149 150static const struct nvkm_disp_func 151nv50_disp_ = { 152 .dtor = nv50_disp_dtor_, 153 .oneinit = nv50_disp_oneinit_, 154 .init = nv50_disp_init_, 155 .fini = nv50_disp_fini_, 156 .intr = nv50_disp_intr_, 157 .root = nv50_disp_root_, 158}; 159 160int 161nv50_disp_new_(const struct nv50_disp_func *func, struct nvkm_device *device, 162 int index, struct nvkm_disp **pdisp) 163{ 164 struct nv50_disp *disp; 165 int ret; 166 167 if (!(disp = kzalloc(sizeof(*disp), GFP_KERNEL))) 168 return -ENOMEM; 169 disp->func = func; 170 *pdisp = &disp->base; 171 172 ret = nvkm_disp_ctor(&nv50_disp_, device, index, &disp->base); 173 if (ret) 174 return ret; 175 176 disp->wq = create_singlethread_workqueue("nvkm-disp"); 177 if (!disp->wq) 178 return -ENOMEM; 179 180 INIT_WORK(&disp->supervisor, func->super); 181 182 return nvkm_event_init(func->uevent, 1, ARRAY_SIZE(disp->chan), 183 &disp->uevent); 184} 185 186static u32 187nv50_disp_super_iedt(struct nvkm_head *head, struct nvkm_outp *outp, 188 u8 *ver, u8 *hdr, u8 *cnt, u8 *len, 189 struct nvbios_outp *iedt) 190{ 191 struct nvkm_bios *bios = head->disp->engine.subdev.device->bios; 192 const u8 l = ffs(outp->info.link); 193 const u16 t = outp->info.hasht; 194 const u16 m = (0x0100 << head->id) | (l << 6) | outp->info.or; 195 u32 data = nvbios_outp_match(bios, t, m, ver, hdr, cnt, len, iedt); 196 if (!data) 197 OUTP_DBG(outp, "missing IEDT for %04x:%04x", t, m); 198 return data; 199} 200 201static void 202nv50_disp_super_ied_on(struct nvkm_head *head, 203 struct nvkm_ior *ior, int id, u32 khz) 204{ 205 struct nvkm_subdev *subdev = &head->disp->engine.subdev; 206 struct nvkm_bios *bios = subdev->device->bios; 207 struct nvkm_outp *outp = ior->asy.outp; 208 struct nvbios_ocfg iedtrs; 209 struct nvbios_outp iedt; 210 u8 ver, hdr, cnt, len, flags = 0x00; 211 u32 data; 212 213 if (!outp) { 214 IOR_DBG(ior, "nothing to attach"); 215 return; 216 } 217 218 /* Lookup IED table for the device. */ 219 data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt); 220 if (!data) 221 return; 222 223 /* Lookup IEDT runtime settings for the current configuration. */ 224 if (ior->type == SOR) { 225 if (ior->asy.proto == LVDS) { 226 if (head->asy.or.depth == 24) 227 flags |= 0x02; 228 } 229 if (ior->asy.link == 3) 230 flags |= 0x01; 231 } 232 233 data = nvbios_ocfg_match(bios, data, ior->asy.proto_evo, flags, 234 &ver, &hdr, &cnt, &len, &iedtrs); 235 if (!data) { 236 OUTP_DBG(outp, "missing IEDT RS for %02x:%02x", 237 ior->asy.proto_evo, flags); 238 return; 239 } 240 241 /* Execute the OnInt[23] script for the current frequency. */ 242 data = nvbios_oclk_match(bios, iedtrs.clkcmp[id], khz); 243 if (!data) { 244 OUTP_DBG(outp, "missing IEDT RSS %d for %02x:%02x %d khz", 245 id, ior->asy.proto_evo, flags, khz); 246 return; 247 } 248 249 nvbios_init(subdev, data, 250 init.outp = &outp->info; 251 init.or = ior->id; 252 init.link = ior->asy.link; 253 init.head = head->id; 254 ); 255} 256 257static void 258nv50_disp_super_ied_off(struct nvkm_head *head, struct nvkm_ior *ior, int id) 259{ 260 struct nvkm_outp *outp = ior->arm.outp; 261 struct nvbios_outp iedt; 262 u8 ver, hdr, cnt, len; 263 u32 data; 264 265 if (!outp) { 266 IOR_DBG(ior, "nothing attached"); 267 return; 268 } 269 270 data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt); 271 if (!data) 272 return; 273 274 nvbios_init(&head->disp->engine.subdev, iedt.script[id], 275 init.outp = &outp->info; 276 init.or = ior->id; 277 init.link = ior->arm.link; 278 init.head = head->id; 279 ); 280} 281 282static struct nvkm_ior * 283nv50_disp_super_ior_asy(struct nvkm_head *head) 284{ 285 struct nvkm_ior *ior; 286 list_for_each_entry(ior, &head->disp->ior, head) { 287 if (ior->asy.head & (1 << head->id)) { 288 HEAD_DBG(head, "to %s", ior->name); 289 return ior; 290 } 291 } 292 HEAD_DBG(head, "nothing to attach"); 293 return NULL; 294} 295 296static struct nvkm_ior * 297nv50_disp_super_ior_arm(struct nvkm_head *head) 298{ 299 struct nvkm_ior *ior; 300 list_for_each_entry(ior, &head->disp->ior, head) { 301 if (ior->arm.head & (1 << head->id)) { 302 HEAD_DBG(head, "on %s", ior->name); 303 return ior; 304 } 305 } 306 HEAD_DBG(head, "nothing attached"); 307 return NULL; 308} 309 310void 311nv50_disp_super_3_0(struct nv50_disp *disp, struct nvkm_head *head) 312{ 313 struct nvkm_ior *ior; 314 315 /* Determine which OR, if any, we're attaching to the head. */ 316 HEAD_DBG(head, "supervisor 3.0"); 317 ior = nv50_disp_super_ior_asy(head); 318 if (!ior) 319 return; 320 321 /* Execute OnInt3 IED script. */ 322 nv50_disp_super_ied_on(head, ior, 1, head->asy.hz / 1000); 323 324 /* OR-specific handling. */ 325 if (ior->func->war_3) 326 ior->func->war_3(ior); 327} 328 329static void 330nv50_disp_super_2_2_dp(struct nvkm_head *head, struct nvkm_ior *ior) 331{ 332 struct nvkm_subdev *subdev = &head->disp->engine.subdev; 333 const u32 khz = head->asy.hz / 1000; 334 const u32 linkKBps = ior->dp.bw * 27000; 335 const u32 symbol = 100000; 336 int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; 337 int TU, VTUi, VTUf, VTUa; 338 u64 link_data_rate, link_ratio, unk; 339 u32 best_diff = 64 * symbol; 340 u64 h, v; 341 342 /* symbols/hblank - algorithm taken from comments in tegra driver */ 343 h = head->asy.hblanke + head->asy.htotal - head->asy.hblanks - 7; 344 h = h * linkKBps; 345 do_div(h, khz); 346 h = h - (3 * ior->dp.ef) - (12 / ior->dp.nr); 347 348 /* symbols/vblank - algorithm taken from comments in tegra driver */ 349 v = head->asy.vblanks - head->asy.vblanke - 25; 350 v = v * linkKBps; 351 do_div(v, khz); 352 v = v - ((36 / ior->dp.nr) + 3) - 1; 353 354 ior->func->dp.audio_sym(ior, head->id, h, v); 355 356 /* watermark / activesym */ 357 link_data_rate = (khz * head->asy.or.depth / 8) / ior->dp.nr; 358 359 /* calculate ratio of packed data rate to link symbol rate */ 360 link_ratio = link_data_rate * symbol; 361 do_div(link_ratio, linkKBps); 362 363 for (TU = 64; ior->func->dp.activesym && TU >= 32; TU--) { 364 /* calculate average number of valid symbols in each TU */ 365 u32 tu_valid = link_ratio * TU; 366 u32 calc, diff; 367 368 /* find a hw representation for the fraction.. */ 369 VTUi = tu_valid / symbol; 370 calc = VTUi * symbol; 371 diff = tu_valid - calc; 372 if (diff) { 373 if (diff >= (symbol / 2)) { 374 VTUf = symbol / (symbol - diff); 375 if (symbol - (VTUf * diff)) 376 VTUf++; 377 378 if (VTUf <= 15) { 379 VTUa = 1; 380 calc += symbol - (symbol / VTUf); 381 } else { 382 VTUa = 0; 383 VTUf = 1; 384 calc += symbol; 385 } 386 } else { 387 VTUa = 0; 388 VTUf = min((int)(symbol / diff), 15); 389 calc += symbol / VTUf; 390 } 391 392 diff = calc - tu_valid; 393 } else { 394 /* no remainder, but the hw doesn't like the fractional 395 * part to be zero. decrement the integer part and 396 * have the fraction add a whole symbol back 397 */ 398 VTUa = 0; 399 VTUf = 1; 400 VTUi--; 401 } 402 403 if (diff < best_diff) { 404 best_diff = diff; 405 bestTU = TU; 406 bestVTUa = VTUa; 407 bestVTUf = VTUf; 408 bestVTUi = VTUi; 409 if (diff == 0) 410 break; 411 } 412 } 413 414 if (ior->func->dp.activesym) { 415 if (!bestTU) { 416 nvkm_error(subdev, "unable to determine dp config\n"); 417 return; 418 } 419 ior->func->dp.activesym(ior, head->id, bestTU, 420 bestVTUa, bestVTUf, bestVTUi); 421 } else { 422 bestTU = 64; 423 } 424 425 /* XXX close to vbios numbers, but not right */ 426 unk = (symbol - link_ratio) * bestTU; 427 unk *= link_ratio; 428 do_div(unk, symbol); 429 do_div(unk, symbol); 430 unk += 6; 431 432 ior->func->dp.watermark(ior, head->id, unk); 433} 434 435void 436nv50_disp_super_2_2(struct nv50_disp *disp, struct nvkm_head *head) 437{ 438 const u32 khz = head->asy.hz / 1000; 439 struct nvkm_outp *outp; 440 struct nvkm_ior *ior; 441 442 /* Determine which OR, if any, we're attaching from the head. */ 443 HEAD_DBG(head, "supervisor 2.2"); 444 ior = nv50_disp_super_ior_asy(head); 445 if (!ior) 446 return; 447 448 /* For some reason, NVIDIA decided not to: 449 * 450 * A) Give dual-link LVDS a separate EVO protocol, like for TMDS. 451 * and 452 * B) Use SetControlOutputResource.PixelDepth on LVDS. 453 * 454 * Override the values we usually read from HW with the same 455 * data we pass though an ioctl instead. 456 */ 457 if (ior->type == SOR && ior->asy.proto == LVDS) { 458 head->asy.or.depth = (disp->sor.lvdsconf & 0x0200) ? 24 : 18; 459 ior->asy.link = (disp->sor.lvdsconf & 0x0100) ? 3 : 1; 460 } 461 462 /* Handle any link training, etc. */ 463 if ((outp = ior->asy.outp) && outp->func->acquire) 464 outp->func->acquire(outp); 465 466 /* Execute OnInt2 IED script. */ 467 nv50_disp_super_ied_on(head, ior, 0, khz); 468 469 /* Program RG clock divider. */ 470 head->func->rgclk(head, ior->asy.rgdiv); 471 472 /* Mode-specific internal DP configuration. */ 473 if (ior->type == SOR && ior->asy.proto == DP) 474 nv50_disp_super_2_2_dp(head, ior); 475 476 /* OR-specific handling. */ 477 ior->func->clock(ior); 478 if (ior->func->war_2) 479 ior->func->war_2(ior); 480} 481 482void 483nv50_disp_super_2_1(struct nv50_disp *disp, struct nvkm_head *head) 484{ 485 struct nvkm_devinit *devinit = disp->base.engine.subdev.device->devinit; 486 const u32 khz = head->asy.hz / 1000; 487 HEAD_DBG(head, "supervisor 2.1 - %d khz", khz); 488 if (khz) 489 nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head->id, khz); 490} 491 492void 493nv50_disp_super_2_0(struct nv50_disp *disp, struct nvkm_head *head) 494{ 495 struct nvkm_outp *outp; 496 struct nvkm_ior *ior; 497 498 /* Determine which OR, if any, we're detaching from the head. */ 499 HEAD_DBG(head, "supervisor 2.0"); 500 ior = nv50_disp_super_ior_arm(head); 501 if (!ior) 502 return; 503 504 /* Execute OffInt2 IED script. */ 505 nv50_disp_super_ied_off(head, ior, 2); 506 507 /* If we're shutting down the OR's only active head, execute 508 * the output path's disable function. 509 */ 510 if (ior->arm.head == (1 << head->id)) { 511 if ((outp = ior->arm.outp) && outp->func->disable) 512 outp->func->disable(outp, ior); 513 } 514} 515 516void 517nv50_disp_super_1_0(struct nv50_disp *disp, struct nvkm_head *head) 518{ 519 struct nvkm_ior *ior; 520 521 /* Determine which OR, if any, we're detaching from the head. */ 522 HEAD_DBG(head, "supervisor 1.0"); 523 ior = nv50_disp_super_ior_arm(head); 524 if (!ior) 525 return; 526 527 /* Execute OffInt1 IED script. */ 528 nv50_disp_super_ied_off(head, ior, 1); 529} 530 531void 532nv50_disp_super_1(struct nv50_disp *disp) 533{ 534 struct nvkm_head *head; 535 struct nvkm_ior *ior; 536 537 list_for_each_entry(head, &disp->base.head, head) { 538 head->func->state(head, &head->arm); 539 head->func->state(head, &head->asy); 540 } 541 542 list_for_each_entry(ior, &disp->base.ior, head) { 543 ior->func->state(ior, &ior->arm); 544 ior->func->state(ior, &ior->asy); 545 } 546} 547 548void 549nv50_disp_super(struct work_struct *work) 550{ 551 struct nv50_disp *disp = 552 container_of(work, struct nv50_disp, supervisor); 553 struct nvkm_subdev *subdev = &disp->base.engine.subdev; 554 struct nvkm_device *device = subdev->device; 555 struct nvkm_head *head; 556 u32 super = nvkm_rd32(device, 0x610030); 557 558 nvkm_debug(subdev, "supervisor %08x %08x\n", disp->super, super); 559 560 if (disp->super & 0x00000010) { 561 nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG); 562 nv50_disp_super_1(disp); 563 list_for_each_entry(head, &disp->base.head, head) { 564 if (!(super & (0x00000020 << head->id))) 565 continue; 566 if (!(super & (0x00000080 << head->id))) 567 continue; 568 nv50_disp_super_1_0(disp, head); 569 } 570 } else 571 if (disp->super & 0x00000020) { 572 list_for_each_entry(head, &disp->base.head, head) { 573 if (!(super & (0x00000080 << head->id))) 574 continue; 575 nv50_disp_super_2_0(disp, head); 576 } 577 nvkm_outp_route(&disp->base); 578 list_for_each_entry(head, &disp->base.head, head) { 579 if (!(super & (0x00000200 << head->id))) 580 continue; 581 nv50_disp_super_2_1(disp, head); 582 } 583 list_for_each_entry(head, &disp->base.head, head) { 584 if (!(super & (0x00000080 << head->id))) 585 continue; 586 nv50_disp_super_2_2(disp, head); 587 } 588 } else 589 if (disp->super & 0x00000040) { 590 list_for_each_entry(head, &disp->base.head, head) { 591 if (!(super & (0x00000080 << head->id))) 592 continue; 593 nv50_disp_super_3_0(disp, head); 594 } 595 } 596 597 nvkm_wr32(device, 0x610030, 0x80000000); 598} 599 600const struct nvkm_enum 601nv50_disp_intr_error_type[] = { 602 { 0, "NONE" }, 603 { 1, "PUSHBUFFER_ERR" }, 604 { 2, "TRAP" }, 605 { 3, "RESERVED_METHOD" }, 606 { 4, "INVALID_ARG" }, 607 { 5, "INVALID_STATE" }, 608 { 7, "UNRESOLVABLE_HANDLE" }, 609 {} 610}; 611 612static const struct nvkm_enum 613nv50_disp_intr_error_code[] = { 614 { 0x00, "" }, 615 {} 616}; 617 618static void 619nv50_disp_intr_error(struct nv50_disp *disp, int chid) 620{ 621 struct nvkm_subdev *subdev = &disp->base.engine.subdev; 622 struct nvkm_device *device = subdev->device; 623 u32 data = nvkm_rd32(device, 0x610084 + (chid * 0x08)); 624 u32 addr = nvkm_rd32(device, 0x610080 + (chid * 0x08)); 625 u32 code = (addr & 0x00ff0000) >> 16; 626 u32 type = (addr & 0x00007000) >> 12; 627 u32 mthd = (addr & 0x00000ffc); 628 const struct nvkm_enum *ec, *et; 629 630 et = nvkm_enum_find(nv50_disp_intr_error_type, type); 631 ec = nvkm_enum_find(nv50_disp_intr_error_code, code); 632 633 nvkm_error(subdev, 634 "ERROR %d [%s] %02x [%s] chid %d mthd %04x data %08x\n", 635 type, et ? et->name : "", code, ec ? ec->name : "", 636 chid, mthd, data); 637 638 if (chid < ARRAY_SIZE(disp->chan)) { 639 switch (mthd) { 640 case 0x0080: 641 nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR); 642 break; 643 default: 644 break; 645 } 646 } 647 648 nvkm_wr32(device, 0x610020, 0x00010000 << chid); 649 nvkm_wr32(device, 0x610080 + (chid * 0x08), 0x90000000); 650} 651 652void 653nv50_disp_intr(struct nv50_disp *disp) 654{ 655 struct nvkm_device *device = disp->base.engine.subdev.device; 656 u32 intr0 = nvkm_rd32(device, 0x610020); 657 u32 intr1 = nvkm_rd32(device, 0x610024); 658 659 while (intr0 & 0x001f0000) { 660 u32 chid = __ffs(intr0 & 0x001f0000) - 16; 661 nv50_disp_intr_error(disp, chid); 662 intr0 &= ~(0x00010000 << chid); 663 } 664 665 while (intr0 & 0x0000001f) { 666 u32 chid = __ffs(intr0 & 0x0000001f); 667 nv50_disp_chan_uevent_send(disp, chid); 668 intr0 &= ~(0x00000001 << chid); 669 } 670 671 if (intr1 & 0x00000004) { 672 nvkm_disp_vblank(&disp->base, 0); 673 nvkm_wr32(device, 0x610024, 0x00000004); 674 } 675 676 if (intr1 & 0x00000008) { 677 nvkm_disp_vblank(&disp->base, 1); 678 nvkm_wr32(device, 0x610024, 0x00000008); 679 } 680 681 if (intr1 & 0x00000070) { 682 disp->super = (intr1 & 0x00000070); 683 queue_work(disp->wq, &disp->supervisor); 684 nvkm_wr32(device, 0x610024, disp->super); 685 } 686} 687 688void 689nv50_disp_fini(struct nv50_disp *disp) 690{ 691 struct nvkm_device *device = disp->base.engine.subdev.device; 692 /* disable all interrupts */ 693 nvkm_wr32(device, 0x610024, 0x00000000); 694 nvkm_wr32(device, 0x610020, 0x00000000); 695} 696 697int 698nv50_disp_init(struct nv50_disp *disp) 699{ 700 struct nvkm_device *device = disp->base.engine.subdev.device; 701 struct nvkm_head *head; 702 u32 tmp; 703 int i; 704 705 /* The below segments of code copying values from one register to 706 * another appear to inform EVO of the display capabilities or 707 * something similar. NFI what the 0x614004 caps are for.. 708 */ 709 tmp = nvkm_rd32(device, 0x614004); 710 nvkm_wr32(device, 0x610184, tmp); 711 712 /* ... CRTC caps */ 713 list_for_each_entry(head, &disp->base.head, head) { 714 tmp = nvkm_rd32(device, 0x616100 + (head->id * 0x800)); 715 nvkm_wr32(device, 0x610190 + (head->id * 0x10), tmp); 716 tmp = nvkm_rd32(device, 0x616104 + (head->id * 0x800)); 717 nvkm_wr32(device, 0x610194 + (head->id * 0x10), tmp); 718 tmp = nvkm_rd32(device, 0x616108 + (head->id * 0x800)); 719 nvkm_wr32(device, 0x610198 + (head->id * 0x10), tmp); 720 tmp = nvkm_rd32(device, 0x61610c + (head->id * 0x800)); 721 nvkm_wr32(device, 0x61019c + (head->id * 0x10), tmp); 722 } 723 724 /* ... DAC caps */ 725 for (i = 0; i < disp->dac.nr; i++) { 726 tmp = nvkm_rd32(device, 0x61a000 + (i * 0x800)); 727 nvkm_wr32(device, 0x6101d0 + (i * 0x04), tmp); 728 } 729 730 /* ... SOR caps */ 731 for (i = 0; i < disp->sor.nr; i++) { 732 tmp = nvkm_rd32(device, 0x61c000 + (i * 0x800)); 733 nvkm_wr32(device, 0x6101e0 + (i * 0x04), tmp); 734 } 735 736 /* ... PIOR caps */ 737 for (i = 0; i < disp->pior.nr; i++) { 738 tmp = nvkm_rd32(device, 0x61e000 + (i * 0x800)); 739 nvkm_wr32(device, 0x6101f0 + (i * 0x04), tmp); 740 } 741 742 /* steal display away from vbios, or something like that */ 743 if (nvkm_rd32(device, 0x610024) & 0x00000100) { 744 nvkm_wr32(device, 0x610024, 0x00000100); 745 nvkm_mask(device, 0x6194e8, 0x00000001, 0x00000000); 746 if (nvkm_msec(device, 2000, 747 if (!(nvkm_rd32(device, 0x6194e8) & 0x00000002)) 748 break; 749 ) < 0) 750 return -EBUSY; 751 } 752 753 /* point at display engine memory area (hash table, objects) */ 754 nvkm_wr32(device, 0x610010, (disp->inst->addr >> 8) | 9); 755 756 /* enable supervisor interrupts, disable everything else */ 757 nvkm_wr32(device, 0x61002c, 0x00000370); 758 nvkm_wr32(device, 0x610028, 0x00000000); 759 return 0; 760} 761 762static const struct nv50_disp_func 763nv50_disp = { 764 .init = nv50_disp_init, 765 .fini = nv50_disp_fini, 766 .intr = nv50_disp_intr, 767 .uevent = &nv50_disp_chan_uevent, 768 .super = nv50_disp_super, 769 .root = &nv50_disp_root_oclass, 770 .head = { .cnt = nv50_head_cnt, .new = nv50_head_new }, 771 .dac = { .cnt = nv50_dac_cnt, .new = nv50_dac_new }, 772 .sor = { .cnt = nv50_sor_cnt, .new = nv50_sor_new }, 773 .pior = { .cnt = nv50_pior_cnt, .new = nv50_pior_new }, 774}; 775 776int 777nv50_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp) 778{ 779 return nv50_disp_new_(&nv50_disp, device, index, pdisp); 780} 781