| .. | .. |
|---|
| 24 | 24 | #include "core.h" |
|---|
| 25 | 25 | #include "curs.h" |
|---|
| 26 | 26 | #include "ovly.h" |
|---|
| 27 | +#include "crc.h" |
|---|
| 27 | 28 | |
|---|
| 28 | 29 | #include <nvif/class.h> |
|---|
| 30 | +#include <nvif/event.h> |
|---|
| 31 | +#include <nvif/cl0046.h> |
|---|
| 29 | 32 | |
|---|
| 30 | 33 | #include <drm/drm_atomic_helper.h> |
|---|
| 31 | 34 | #include <drm/drm_crtc_helper.h> |
|---|
| 35 | +#include <drm/drm_vblank.h> |
|---|
| 32 | 36 | #include "nouveau_connector.h" |
|---|
| 37 | + |
|---|
| 33 | 38 | void |
|---|
| 34 | 39 | nv50_head_flush_clr(struct nv50_head *head, |
|---|
| 35 | 40 | struct nv50_head_atom *asyh, bool flush) |
|---|
| .. | .. |
|---|
| 37 | 42 | union nv50_head_atom_mask clr = { |
|---|
| 38 | 43 | .mask = asyh->clr.mask & ~(flush ? 0 : asyh->set.mask), |
|---|
| 39 | 44 | }; |
|---|
| 45 | + if (clr.crc) nv50_crc_atomic_clr(head); |
|---|
| 40 | 46 | if (clr.olut) head->func->olut_clr(head); |
|---|
| 41 | 47 | if (clr.core) head->func->core_clr(head); |
|---|
| 42 | 48 | if (clr.curs) head->func->curs_clr(head); |
|---|
| 49 | +} |
|---|
| 50 | + |
|---|
| 51 | +void |
|---|
| 52 | +nv50_head_flush_set_wndw(struct nv50_head *head, struct nv50_head_atom *asyh) |
|---|
| 53 | +{ |
|---|
| 54 | + if (asyh->set.curs ) head->func->curs_set(head, asyh); |
|---|
| 55 | + if (asyh->set.olut ) { |
|---|
| 56 | + asyh->olut.offset = nv50_lut_load(&head->olut, |
|---|
| 57 | + asyh->olut.buffer, |
|---|
| 58 | + asyh->state.gamma_lut, |
|---|
| 59 | + asyh->olut.load); |
|---|
| 60 | + head->func->olut_set(head, asyh); |
|---|
| 61 | + } |
|---|
| 43 | 62 | } |
|---|
| 44 | 63 | |
|---|
| 45 | 64 | void |
|---|
| .. | .. |
|---|
| 48 | 67 | if (asyh->set.view ) head->func->view (head, asyh); |
|---|
| 49 | 68 | if (asyh->set.mode ) head->func->mode (head, asyh); |
|---|
| 50 | 69 | if (asyh->set.core ) head->func->core_set(head, asyh); |
|---|
| 51 | | - if (asyh->set.olut ) { |
|---|
| 52 | | - asyh->olut.offset = nv50_lut_load(&head->olut, |
|---|
| 53 | | - asyh->olut.mode <= 1, |
|---|
| 54 | | - asyh->olut.buffer, |
|---|
| 55 | | - asyh->state.gamma_lut); |
|---|
| 56 | | - head->func->olut_set(head, asyh); |
|---|
| 57 | | - } |
|---|
| 58 | | - if (asyh->set.curs ) head->func->curs_set(head, asyh); |
|---|
| 59 | 70 | if (asyh->set.base ) head->func->base (head, asyh); |
|---|
| 60 | 71 | if (asyh->set.ovly ) head->func->ovly (head, asyh); |
|---|
| 61 | 72 | if (asyh->set.dither ) head->func->dither (head, asyh); |
|---|
| 62 | 73 | if (asyh->set.procamp) head->func->procamp (head, asyh); |
|---|
| 74 | + if (asyh->set.crc ) nv50_crc_atomic_set (head, asyh); |
|---|
| 63 | 75 | if (asyh->set.or ) head->func->or (head, asyh); |
|---|
| 64 | 76 | } |
|---|
| 65 | 77 | |
|---|
| .. | .. |
|---|
| 81 | 93 | struct nv50_head_atom *asyh, |
|---|
| 82 | 94 | struct nouveau_conn_atom *asyc) |
|---|
| 83 | 95 | { |
|---|
| 84 | | - struct drm_connector *connector = asyc->state.connector; |
|---|
| 85 | 96 | u32 mode = 0x00; |
|---|
| 86 | 97 | |
|---|
| 87 | | - if (asyc->dither.mode == DITHERING_MODE_AUTO) { |
|---|
| 88 | | - if (asyh->base.depth > connector->display_info.bpc * 3) |
|---|
| 89 | | - mode = DITHERING_MODE_DYNAMIC2X2; |
|---|
| 90 | | - } else { |
|---|
| 91 | | - mode = asyc->dither.mode; |
|---|
| 98 | + if (asyc->dither.mode) { |
|---|
| 99 | + if (asyc->dither.mode == DITHERING_MODE_AUTO) { |
|---|
| 100 | + if (asyh->base.depth > asyh->or.bpc * 3) |
|---|
| 101 | + mode = DITHERING_MODE_DYNAMIC2X2; |
|---|
| 102 | + } else { |
|---|
| 103 | + mode = asyc->dither.mode; |
|---|
| 104 | + } |
|---|
| 105 | + |
|---|
| 106 | + if (asyc->dither.depth == DITHERING_DEPTH_AUTO) { |
|---|
| 107 | + if (asyh->or.bpc >= 8) |
|---|
| 108 | + mode |= DITHERING_DEPTH_8BPC; |
|---|
| 109 | + } else { |
|---|
| 110 | + mode |= asyc->dither.depth; |
|---|
| 111 | + } |
|---|
| 92 | 112 | } |
|---|
| 93 | 113 | |
|---|
| 94 | | - if (asyc->dither.depth == DITHERING_DEPTH_AUTO) { |
|---|
| 95 | | - if (connector->display_info.bpc >= 8) |
|---|
| 96 | | - mode |= DITHERING_DEPTH_8BPC; |
|---|
| 97 | | - } else { |
|---|
| 98 | | - mode |= asyc->dither.depth; |
|---|
| 99 | | - } |
|---|
| 100 | | - |
|---|
| 101 | | - asyh->dither.enable = mode; |
|---|
| 102 | | - asyh->dither.bits = mode >> 1; |
|---|
| 103 | | - asyh->dither.mode = mode >> 3; |
|---|
| 114 | + asyh->dither.enable = NVVAL_GET(mode, NV507D, HEAD_SET_DITHER_CONTROL, ENABLE); |
|---|
| 115 | + asyh->dither.bits = NVVAL_GET(mode, NV507D, HEAD_SET_DITHER_CONTROL, BITS); |
|---|
| 116 | + asyh->dither.mode = NVVAL_GET(mode, NV507D, HEAD_SET_DITHER_CONTROL, MODE); |
|---|
| 104 | 117 | asyh->set.dither = true; |
|---|
| 105 | 118 | } |
|---|
| 106 | 119 | |
|---|
| .. | .. |
|---|
| 214 | 227 | { |
|---|
| 215 | 228 | struct nv50_disp *disp = nv50_disp(head->base.base.dev); |
|---|
| 216 | 229 | struct drm_property_blob *olut = asyh->state.gamma_lut; |
|---|
| 230 | + int size; |
|---|
| 217 | 231 | |
|---|
| 218 | 232 | /* Determine whether core output LUT should be enabled. */ |
|---|
| 219 | 233 | if (olut) { |
|---|
| .. | .. |
|---|
| 231 | 245 | } |
|---|
| 232 | 246 | |
|---|
| 233 | 247 | if (!olut) { |
|---|
| 234 | | - asyh->olut.handle = 0; |
|---|
| 235 | | - return 0; |
|---|
| 248 | + if (!head->func->olut_identity) { |
|---|
| 249 | + asyh->olut.handle = 0; |
|---|
| 250 | + return 0; |
|---|
| 251 | + } |
|---|
| 252 | + size = 0; |
|---|
| 253 | + } else { |
|---|
| 254 | + size = drm_color_lut_size(olut); |
|---|
| 236 | 255 | } |
|---|
| 237 | 256 | |
|---|
| 257 | + if (!head->func->olut(head, asyh, size)) { |
|---|
| 258 | + DRM_DEBUG_KMS("Invalid olut\n"); |
|---|
| 259 | + return -EINVAL; |
|---|
| 260 | + } |
|---|
| 238 | 261 | asyh->olut.handle = disp->core->chan.vram.handle; |
|---|
| 239 | 262 | asyh->olut.buffer = !asyh->olut.buffer; |
|---|
| 240 | | - head->func->olut(head, asyh); |
|---|
| 263 | + |
|---|
| 241 | 264 | return 0; |
|---|
| 242 | 265 | } |
|---|
| 243 | 266 | |
|---|
| .. | .. |
|---|
| 301 | 324 | struct nouveau_conn_atom *asyc = NULL; |
|---|
| 302 | 325 | struct drm_connector_state *conns; |
|---|
| 303 | 326 | struct drm_connector *conn; |
|---|
| 304 | | - int i; |
|---|
| 327 | + int i, ret; |
|---|
| 305 | 328 | |
|---|
| 306 | 329 | NV_ATOMIC(drm, "%s atomic_check %d\n", crtc->name, asyh->state.active); |
|---|
| 307 | 330 | if (asyh->state.active) { |
|---|
| .. | .. |
|---|
| 396 | 419 | asyh->set.curs = asyh->curs.visible; |
|---|
| 397 | 420 | } |
|---|
| 398 | 421 | |
|---|
| 422 | + ret = nv50_crc_atomic_check_head(head, asyh, armh); |
|---|
| 423 | + if (ret) |
|---|
| 424 | + return ret; |
|---|
| 425 | + |
|---|
| 399 | 426 | if (asyh->clr.mask || asyh->set.mask) |
|---|
| 400 | 427 | nv50_atom(asyh->state.state)->lock_core = true; |
|---|
| 401 | 428 | return 0; |
|---|
| .. | .. |
|---|
| 404 | 431 | static const struct drm_crtc_helper_funcs |
|---|
| 405 | 432 | nv50_head_help = { |
|---|
| 406 | 433 | .atomic_check = nv50_head_atomic_check, |
|---|
| 434 | + .get_scanout_position = nouveau_display_scanoutpos, |
|---|
| 407 | 435 | }; |
|---|
| 408 | 436 | |
|---|
| 409 | 437 | static void |
|---|
| .. | .. |
|---|
| 433 | 461 | asyh->ovly = armh->ovly; |
|---|
| 434 | 462 | asyh->dither = armh->dither; |
|---|
| 435 | 463 | asyh->procamp = armh->procamp; |
|---|
| 464 | + asyh->crc = armh->crc; |
|---|
| 465 | + asyh->or = armh->or; |
|---|
| 466 | + asyh->dp = armh->dp; |
|---|
| 436 | 467 | asyh->clr.mask = 0; |
|---|
| 437 | 468 | asyh->set.mask = 0; |
|---|
| 438 | 469 | return &asyh->state; |
|---|
| 439 | | -} |
|---|
| 440 | | - |
|---|
| 441 | | -static void |
|---|
| 442 | | -__drm_atomic_helper_crtc_reset(struct drm_crtc *crtc, |
|---|
| 443 | | - struct drm_crtc_state *state) |
|---|
| 444 | | -{ |
|---|
| 445 | | - if (crtc->state) |
|---|
| 446 | | - crtc->funcs->atomic_destroy_state(crtc, crtc->state); |
|---|
| 447 | | - crtc->state = state; |
|---|
| 448 | | - crtc->state->crtc = crtc; |
|---|
| 449 | 470 | } |
|---|
| 450 | 471 | |
|---|
| 451 | 472 | static void |
|---|
| .. | .. |
|---|
| 456 | 477 | if (WARN_ON(!(asyh = kzalloc(sizeof(*asyh), GFP_KERNEL)))) |
|---|
| 457 | 478 | return; |
|---|
| 458 | 479 | |
|---|
| 480 | + if (crtc->state) |
|---|
| 481 | + nv50_head_atomic_destroy_state(crtc, crtc->state); |
|---|
| 482 | + |
|---|
| 459 | 483 | __drm_atomic_helper_crtc_reset(crtc, &asyh->state); |
|---|
| 484 | +} |
|---|
| 485 | + |
|---|
| 486 | +static int |
|---|
| 487 | +nv50_head_late_register(struct drm_crtc *crtc) |
|---|
| 488 | +{ |
|---|
| 489 | + return nv50_head_crc_late_register(nv50_head(crtc)); |
|---|
| 460 | 490 | } |
|---|
| 461 | 491 | |
|---|
| 462 | 492 | static void |
|---|
| 463 | 493 | nv50_head_destroy(struct drm_crtc *crtc) |
|---|
| 464 | 494 | { |
|---|
| 465 | 495 | struct nv50_head *head = nv50_head(crtc); |
|---|
| 496 | + |
|---|
| 497 | + nvif_notify_dtor(&head->base.vblank); |
|---|
| 466 | 498 | nv50_lut_fini(&head->olut); |
|---|
| 467 | 499 | drm_crtc_cleanup(crtc); |
|---|
| 468 | 500 | kfree(head); |
|---|
| .. | .. |
|---|
| 477 | 509 | .page_flip = drm_atomic_helper_page_flip, |
|---|
| 478 | 510 | .atomic_duplicate_state = nv50_head_atomic_duplicate_state, |
|---|
| 479 | 511 | .atomic_destroy_state = nv50_head_atomic_destroy_state, |
|---|
| 512 | + .enable_vblank = nouveau_display_vblank_enable, |
|---|
| 513 | + .disable_vblank = nouveau_display_vblank_disable, |
|---|
| 514 | + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, |
|---|
| 515 | + .late_register = nv50_head_late_register, |
|---|
| 480 | 516 | }; |
|---|
| 481 | 517 | |
|---|
| 482 | | -int |
|---|
| 518 | +static const struct drm_crtc_funcs |
|---|
| 519 | +nvd9_head_func = { |
|---|
| 520 | + .reset = nv50_head_reset, |
|---|
| 521 | + .gamma_set = drm_atomic_helper_legacy_gamma_set, |
|---|
| 522 | + .destroy = nv50_head_destroy, |
|---|
| 523 | + .set_config = drm_atomic_helper_set_config, |
|---|
| 524 | + .page_flip = drm_atomic_helper_page_flip, |
|---|
| 525 | + .atomic_duplicate_state = nv50_head_atomic_duplicate_state, |
|---|
| 526 | + .atomic_destroy_state = nv50_head_atomic_destroy_state, |
|---|
| 527 | + .enable_vblank = nouveau_display_vblank_enable, |
|---|
| 528 | + .disable_vblank = nouveau_display_vblank_disable, |
|---|
| 529 | + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, |
|---|
| 530 | + .verify_crc_source = nv50_crc_verify_source, |
|---|
| 531 | + .get_crc_sources = nv50_crc_get_sources, |
|---|
| 532 | + .set_crc_source = nv50_crc_set_source, |
|---|
| 533 | + .late_register = nv50_head_late_register, |
|---|
| 534 | +}; |
|---|
| 535 | + |
|---|
| 536 | +static int nv50_head_vblank_handler(struct nvif_notify *notify) |
|---|
| 537 | +{ |
|---|
| 538 | + struct nouveau_crtc *nv_crtc = |
|---|
| 539 | + container_of(notify, struct nouveau_crtc, vblank); |
|---|
| 540 | + |
|---|
| 541 | + if (drm_crtc_handle_vblank(&nv_crtc->base)) |
|---|
| 542 | + nv50_crc_handle_vblank(nv50_head(&nv_crtc->base)); |
|---|
| 543 | + |
|---|
| 544 | + return NVIF_NOTIFY_KEEP; |
|---|
| 545 | +} |
|---|
| 546 | + |
|---|
| 547 | +struct nv50_head * |
|---|
| 483 | 548 | nv50_head_create(struct drm_device *dev, int index) |
|---|
| 484 | 549 | { |
|---|
| 485 | 550 | struct nouveau_drm *drm = nouveau_drm(dev); |
|---|
| 486 | 551 | struct nv50_disp *disp = nv50_disp(dev); |
|---|
| 487 | 552 | struct nv50_head *head; |
|---|
| 488 | | - struct nv50_wndw *curs, *wndw; |
|---|
| 553 | + struct nv50_wndw *base, *ovly, *curs; |
|---|
| 554 | + struct nouveau_crtc *nv_crtc; |
|---|
| 489 | 555 | struct drm_crtc *crtc; |
|---|
| 556 | + const struct drm_crtc_funcs *funcs; |
|---|
| 490 | 557 | int ret; |
|---|
| 491 | 558 | |
|---|
| 492 | 559 | head = kzalloc(sizeof(*head), GFP_KERNEL); |
|---|
| 493 | 560 | if (!head) |
|---|
| 494 | | - return -ENOMEM; |
|---|
| 561 | + return ERR_PTR(-ENOMEM); |
|---|
| 495 | 562 | |
|---|
| 496 | 563 | head->func = disp->core->func->head; |
|---|
| 497 | 564 | head->base.index = index; |
|---|
| 498 | 565 | |
|---|
| 566 | + if (disp->disp->object.oclass < GF110_DISP) |
|---|
| 567 | + funcs = &nv50_head_func; |
|---|
| 568 | + else |
|---|
| 569 | + funcs = &nvd9_head_func; |
|---|
| 570 | + |
|---|
| 499 | 571 | if (disp->disp->object.oclass < GV100_DISP) { |
|---|
| 500 | | - ret = nv50_ovly_new(drm, head->base.index, &wndw); |
|---|
| 501 | | - ret = nv50_base_new(drm, head->base.index, &wndw); |
|---|
| 572 | + ret = nv50_base_new(drm, head->base.index, &base); |
|---|
| 573 | + ret = nv50_ovly_new(drm, head->base.index, &ovly); |
|---|
| 502 | 574 | } else { |
|---|
| 503 | | - ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_OVERLAY, |
|---|
| 504 | | - head->base.index * 2 + 1, &wndw); |
|---|
| 505 | 575 | ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_PRIMARY, |
|---|
| 506 | | - head->base.index * 2 + 0, &wndw); |
|---|
| 576 | + head->base.index * 2 + 0, &base); |
|---|
| 577 | + ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_OVERLAY, |
|---|
| 578 | + head->base.index * 2 + 1, &ovly); |
|---|
| 507 | 579 | } |
|---|
| 508 | 580 | if (ret == 0) |
|---|
| 509 | 581 | ret = nv50_curs_new(drm, head->base.index, &curs); |
|---|
| 510 | 582 | if (ret) { |
|---|
| 511 | 583 | kfree(head); |
|---|
| 512 | | - return ret; |
|---|
| 584 | + return ERR_PTR(ret); |
|---|
| 513 | 585 | } |
|---|
| 514 | 586 | |
|---|
| 515 | | - crtc = &head->base.base; |
|---|
| 516 | | - drm_crtc_init_with_planes(dev, crtc, &wndw->plane, &curs->plane, |
|---|
| 517 | | - &nv50_head_func, "head-%d", head->base.index); |
|---|
| 587 | + nv_crtc = &head->base; |
|---|
| 588 | + crtc = &nv_crtc->base; |
|---|
| 589 | + drm_crtc_init_with_planes(dev, crtc, &base->plane, &curs->plane, |
|---|
| 590 | + funcs, "head-%d", head->base.index); |
|---|
| 518 | 591 | drm_crtc_helper_add(crtc, &nv50_head_help); |
|---|
| 592 | + /* Keep the legacy gamma size at 256 to avoid compatibility issues */ |
|---|
| 519 | 593 | drm_mode_crtc_set_gamma_size(crtc, 256); |
|---|
| 594 | + drm_crtc_enable_color_mgmt(crtc, base->func->ilut_size, |
|---|
| 595 | + disp->disp->object.oclass >= GF110_DISP, |
|---|
| 596 | + head->func->olut_size); |
|---|
| 520 | 597 | |
|---|
| 521 | 598 | if (head->func->olut_set) { |
|---|
| 522 | 599 | ret = nv50_lut_init(disp, &drm->client.mmu, &head->olut); |
|---|
| 523 | | - if (ret) |
|---|
| 524 | | - goto out; |
|---|
| 600 | + if (ret) { |
|---|
| 601 | + nv50_head_destroy(crtc); |
|---|
| 602 | + return ERR_PTR(ret); |
|---|
| 603 | + } |
|---|
| 525 | 604 | } |
|---|
| 526 | 605 | |
|---|
| 527 | | -out: |
|---|
| 606 | + ret = nvif_notify_ctor(&disp->disp->object, "kmsVbl", nv50_head_vblank_handler, |
|---|
| 607 | + false, NV04_DISP_NTFY_VBLANK, |
|---|
| 608 | + &(struct nvif_notify_head_req_v0) { |
|---|
| 609 | + .head = nv_crtc->index, |
|---|
| 610 | + }, |
|---|
| 611 | + sizeof(struct nvif_notify_head_req_v0), |
|---|
| 612 | + sizeof(struct nvif_notify_head_rep_v0), |
|---|
| 613 | + &nv_crtc->vblank); |
|---|
| 528 | 614 | if (ret) |
|---|
| 529 | | - nv50_head_destroy(crtc); |
|---|
| 530 | | - return ret; |
|---|
| 615 | + return ERR_PTR(ret); |
|---|
| 616 | + |
|---|
| 617 | + return head; |
|---|
| 531 | 618 | } |
|---|