0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022 #include "head.h"
0023 #include "base.h"
0024 #include "core.h"
0025 #include "curs.h"
0026 #include "ovly.h"
0027 #include "crc.h"
0028
0029 #include <nvif/class.h>
0030 #include <nvif/event.h>
0031 #include <nvif/cl0046.h>
0032
0033 #include <drm/drm_atomic.h>
0034 #include <drm/drm_atomic_helper.h>
0035 #include <drm/drm_crtc_helper.h>
0036 #include <drm/drm_vblank.h>
0037 #include "nouveau_connector.h"
0038
0039 void
0040 nv50_head_flush_clr(struct nv50_head *head,
0041 struct nv50_head_atom *asyh, bool flush)
0042 {
0043 union nv50_head_atom_mask clr = {
0044 .mask = asyh->clr.mask & ~(flush ? 0 : asyh->set.mask),
0045 };
0046 if (clr.crc) nv50_crc_atomic_clr(head);
0047 if (clr.olut) head->func->olut_clr(head);
0048 if (clr.core) head->func->core_clr(head);
0049 if (clr.curs) head->func->curs_clr(head);
0050 }
0051
0052 void
0053 nv50_head_flush_set_wndw(struct nv50_head *head, struct nv50_head_atom *asyh)
0054 {
0055 if (asyh->set.curs ) head->func->curs_set(head, asyh);
0056 if (asyh->set.olut ) {
0057 asyh->olut.offset = nv50_lut_load(&head->olut,
0058 asyh->olut.buffer,
0059 asyh->state.gamma_lut,
0060 asyh->olut.load);
0061 head->func->olut_set(head, asyh);
0062 }
0063 }
0064
0065 void
0066 nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh)
0067 {
0068 if (asyh->set.view ) head->func->view (head, asyh);
0069 if (asyh->set.mode ) head->func->mode (head, asyh);
0070 if (asyh->set.core ) head->func->core_set(head, asyh);
0071 if (asyh->set.base ) head->func->base (head, asyh);
0072 if (asyh->set.ovly ) head->func->ovly (head, asyh);
0073 if (asyh->set.dither ) head->func->dither (head, asyh);
0074 if (asyh->set.procamp) head->func->procamp (head, asyh);
0075 if (asyh->set.crc ) nv50_crc_atomic_set (head, asyh);
0076 if (asyh->set.or ) head->func->or (head, asyh);
0077 }
0078
0079 static void
0080 nv50_head_atomic_check_procamp(struct nv50_head_atom *armh,
0081 struct nv50_head_atom *asyh,
0082 struct nouveau_conn_atom *asyc)
0083 {
0084 const int vib = asyc->procamp.color_vibrance - 100;
0085 const int hue = asyc->procamp.vibrant_hue - 90;
0086 const int adj = (vib > 0) ? 50 : 0;
0087 asyh->procamp.sat.cos = ((vib * 2047 + adj) / 100) & 0xfff;
0088 asyh->procamp.sat.sin = ((hue * 2047) / 100) & 0xfff;
0089 asyh->set.procamp = true;
0090 }
0091
0092 static void
0093 nv50_head_atomic_check_dither(struct nv50_head_atom *armh,
0094 struct nv50_head_atom *asyh,
0095 struct nouveau_conn_atom *asyc)
0096 {
0097 u32 mode = 0x00;
0098
0099 if (asyc->dither.mode) {
0100 if (asyc->dither.mode == DITHERING_MODE_AUTO) {
0101 if (asyh->base.depth > asyh->or.bpc * 3)
0102 mode = DITHERING_MODE_DYNAMIC2X2;
0103 } else {
0104 mode = asyc->dither.mode;
0105 }
0106
0107 if (asyc->dither.depth == DITHERING_DEPTH_AUTO) {
0108 if (asyh->or.bpc >= 8)
0109 mode |= DITHERING_DEPTH_8BPC;
0110 } else {
0111 mode |= asyc->dither.depth;
0112 }
0113 }
0114
0115 asyh->dither.enable = NVVAL_GET(mode, NV507D, HEAD_SET_DITHER_CONTROL, ENABLE);
0116 asyh->dither.bits = NVVAL_GET(mode, NV507D, HEAD_SET_DITHER_CONTROL, BITS);
0117 asyh->dither.mode = NVVAL_GET(mode, NV507D, HEAD_SET_DITHER_CONTROL, MODE);
0118 asyh->set.dither = true;
0119 }
0120
0121 static void
0122 nv50_head_atomic_check_view(struct nv50_head_atom *armh,
0123 struct nv50_head_atom *asyh,
0124 struct nouveau_conn_atom *asyc)
0125 {
0126 struct drm_connector *connector = asyc->state.connector;
0127 struct drm_display_mode *omode = &asyh->state.adjusted_mode;
0128 struct drm_display_mode *umode = &asyh->state.mode;
0129 int mode = asyc->scaler.mode;
0130 struct edid *edid;
0131 int umode_vdisplay, omode_hdisplay, omode_vdisplay;
0132
0133 if (connector->edid_blob_ptr)
0134 edid = (struct edid *)connector->edid_blob_ptr->data;
0135 else
0136 edid = NULL;
0137
0138 if (!asyc->scaler.full) {
0139 if (mode == DRM_MODE_SCALE_NONE)
0140 omode = umode;
0141 } else {
0142
0143 mode = DRM_MODE_SCALE_FULLSCREEN;
0144 }
0145
0146
0147
0148
0149 umode_vdisplay = umode->vdisplay;
0150 if ((umode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
0151 umode_vdisplay += umode->vtotal;
0152 asyh->view.iW = umode->hdisplay;
0153 asyh->view.iH = umode_vdisplay;
0154
0155 drm_mode_get_hv_timing(omode, &omode_hdisplay, &omode_vdisplay);
0156 asyh->view.oW = omode_hdisplay;
0157 asyh->view.oH = omode_vdisplay;
0158
0159
0160
0161
0162
0163 if ((asyc->scaler.underscan.mode == UNDERSCAN_ON ||
0164 (asyc->scaler.underscan.mode == UNDERSCAN_AUTO &&
0165 drm_detect_hdmi_monitor(edid)))) {
0166 u32 bX = asyc->scaler.underscan.hborder;
0167 u32 bY = asyc->scaler.underscan.vborder;
0168 u32 r = (asyh->view.oH << 19) / asyh->view.oW;
0169
0170 if (bX) {
0171 asyh->view.oW -= (bX * 2);
0172 if (bY) asyh->view.oH -= (bY * 2);
0173 else asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19;
0174 } else {
0175 asyh->view.oW -= (asyh->view.oW >> 4) + 32;
0176 if (bY) asyh->view.oH -= (bY * 2);
0177 else asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19;
0178 }
0179 }
0180
0181
0182
0183
0184 switch (mode) {
0185 case DRM_MODE_SCALE_CENTER:
0186
0187
0188
0189 asyh->view.oW = min(asyh->view.iW, asyh->view.oW);
0190 asyh->view.oH = min(asyh->view.iH, asyh->view.oH);
0191 break;
0192 case DRM_MODE_SCALE_ASPECT:
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208 if (asyh->view.oW * asyh->view.iH > asyh->view.iW * asyh->view.oH) {
0209
0210 u32 r = (asyh->view.iW << 19) / asyh->view.iH;
0211 asyh->view.oW = ((asyh->view.oH * r) + (r / 2)) >> 19;
0212 } else {
0213
0214 u32 r = (asyh->view.iH << 19) / asyh->view.iW;
0215 asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19;
0216 }
0217 break;
0218 default:
0219 break;
0220 }
0221
0222 asyh->set.view = true;
0223 }
0224
0225 static int
0226 nv50_head_atomic_check_lut(struct nv50_head *head,
0227 struct nv50_head_atom *asyh)
0228 {
0229 struct drm_device *dev = head->base.base.dev;
0230 struct drm_crtc *crtc = &head->base.base;
0231 struct nv50_disp *disp = nv50_disp(dev);
0232 struct nouveau_drm *drm = nouveau_drm(dev);
0233 struct drm_property_blob *olut = asyh->state.gamma_lut,
0234 *ilut = asyh->state.degamma_lut;
0235 int size;
0236
0237
0238 if (ilut) {
0239 size = drm_color_lut_size(ilut);
0240 if (!head->func->ilut_check(size)) {
0241 NV_ATOMIC(drm, "Invalid size %d for degamma on [CRTC:%d:%s]\n",
0242 size, crtc->base.id, crtc->name);
0243 return -EINVAL;
0244 }
0245 }
0246
0247
0248 if (olut) {
0249
0250
0251
0252 if (asyh->wndw.olut) {
0253
0254
0255
0256 if (asyh->wndw.olut != asyh->wndw.mask)
0257 return -EINVAL;
0258 olut = NULL;
0259 }
0260 }
0261
0262 if (!olut) {
0263 if (!head->func->olut_identity) {
0264 asyh->olut.handle = 0;
0265 return 0;
0266 }
0267 size = 0;
0268 } else {
0269 size = drm_color_lut_size(olut);
0270 }
0271
0272 if (!head->func->olut(head, asyh, size)) {
0273 NV_ATOMIC(drm, "Invalid size %d for gamma on [CRTC:%d:%s]\n",
0274 size, crtc->base.id, crtc->name);
0275 return -EINVAL;
0276 }
0277 asyh->olut.handle = disp->core->chan.vram.handle;
0278 asyh->olut.buffer = !asyh->olut.buffer;
0279
0280 return 0;
0281 }
0282
0283 static void
0284 nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
0285 {
0286 struct drm_display_mode *mode = &asyh->state.adjusted_mode;
0287 struct nv50_head_mode *m = &asyh->mode;
0288 u32 blankus;
0289
0290 drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE);
0291
0292
0293
0294
0295
0296
0297
0298
0299 m->h.active = mode->crtc_htotal;
0300 m->h.synce = mode->crtc_hsync_end - mode->crtc_hsync_start - 1;
0301 m->h.blanke = mode->crtc_hblank_end - mode->crtc_hsync_start - 1;
0302 m->h.blanks = m->h.blanke + mode->crtc_hdisplay;
0303
0304 m->v.active = mode->crtc_vtotal;
0305 m->v.synce = mode->crtc_vsync_end - mode->crtc_vsync_start - 1;
0306 m->v.blanke = mode->crtc_vblank_end - mode->crtc_vsync_start - 1;
0307 m->v.blanks = m->v.blanke + mode->crtc_vdisplay;
0308
0309
0310 blankus = (m->v.active - mode->crtc_vdisplay - 2) * m->h.active;
0311 blankus *= 1000;
0312 blankus /= mode->crtc_clock;
0313 m->v.blankus = blankus;
0314
0315 if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
0316 m->v.blank2e = m->v.active + m->v.blanke;
0317 m->v.blank2s = m->v.blank2e + mode->crtc_vdisplay;
0318 m->v.active = (m->v.active * 2) + 1;
0319 m->interlace = true;
0320 } else {
0321 m->v.blank2e = 0;
0322 m->v.blank2s = 1;
0323 m->interlace = false;
0324 }
0325 m->clock = mode->crtc_clock;
0326
0327 asyh->or.nhsync = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
0328 asyh->or.nvsync = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
0329 asyh->set.or = head->func->or != NULL;
0330 asyh->set.mode = true;
0331 }
0332
0333 static int
0334 nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
0335 {
0336 struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
0337 crtc);
0338 struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
0339 crtc);
0340 struct nouveau_drm *drm = nouveau_drm(crtc->dev);
0341 struct nv50_head *head = nv50_head(crtc);
0342 struct nv50_head_atom *armh = nv50_head_atom(old_crtc_state);
0343 struct nv50_head_atom *asyh = nv50_head_atom(crtc_state);
0344 struct nouveau_conn_atom *asyc = NULL;
0345 struct drm_connector_state *conns;
0346 struct drm_connector *conn;
0347 int i, ret;
0348 bool check_lut = asyh->state.color_mgmt_changed ||
0349 memcmp(&armh->wndw, &asyh->wndw, sizeof(asyh->wndw));
0350
0351 NV_ATOMIC(drm, "%s atomic_check %d\n", crtc->name, asyh->state.active);
0352
0353 if (check_lut) {
0354 ret = nv50_head_atomic_check_lut(head, asyh);
0355 if (ret)
0356 return ret;
0357 }
0358
0359 if (asyh->state.active) {
0360 for_each_new_connector_in_state(asyh->state.state, conn, conns, i) {
0361 if (conns->crtc == crtc) {
0362 asyc = nouveau_conn_atom(conns);
0363 break;
0364 }
0365 }
0366
0367 if (armh->state.active) {
0368 if (asyc) {
0369 if (asyh->state.mode_changed)
0370 asyc->set.scaler = true;
0371 if (armh->base.depth != asyh->base.depth)
0372 asyc->set.dither = true;
0373 }
0374 } else {
0375 if (asyc)
0376 asyc->set.mask = ~0;
0377 asyh->set.mask = ~0;
0378 asyh->set.or = head->func->or != NULL;
0379 }
0380
0381 if (asyh->state.mode_changed || asyh->state.connectors_changed)
0382 nv50_head_atomic_check_mode(head, asyh);
0383
0384 if (check_lut)
0385 asyh->olut.visible = asyh->olut.handle != 0;
0386
0387 if (asyc) {
0388 if (asyc->set.scaler)
0389 nv50_head_atomic_check_view(armh, asyh, asyc);
0390 if (asyc->set.dither)
0391 nv50_head_atomic_check_dither(armh, asyh, asyc);
0392 if (asyc->set.procamp)
0393 nv50_head_atomic_check_procamp(armh, asyh, asyc);
0394 }
0395
0396 if (head->func->core_calc) {
0397 head->func->core_calc(head, asyh);
0398 if (!asyh->core.visible)
0399 asyh->olut.visible = false;
0400 }
0401
0402 asyh->set.base = armh->base.cpp != asyh->base.cpp;
0403 asyh->set.ovly = armh->ovly.cpp != asyh->ovly.cpp;
0404 } else {
0405 asyh->olut.visible = false;
0406 asyh->core.visible = false;
0407 asyh->curs.visible = false;
0408 asyh->base.cpp = 0;
0409 asyh->ovly.cpp = 0;
0410 }
0411
0412 if (!drm_atomic_crtc_needs_modeset(&asyh->state)) {
0413 if (asyh->core.visible) {
0414 if (memcmp(&armh->core, &asyh->core, sizeof(asyh->core)))
0415 asyh->set.core = true;
0416 } else
0417 if (armh->core.visible) {
0418 asyh->clr.core = true;
0419 }
0420
0421 if (asyh->curs.visible) {
0422 if (memcmp(&armh->curs, &asyh->curs, sizeof(asyh->curs)))
0423 asyh->set.curs = true;
0424 } else
0425 if (armh->curs.visible) {
0426 asyh->clr.curs = true;
0427 }
0428
0429 if (asyh->olut.visible) {
0430 if (memcmp(&armh->olut, &asyh->olut, sizeof(asyh->olut)))
0431 asyh->set.olut = true;
0432 } else
0433 if (armh->olut.visible) {
0434 asyh->clr.olut = true;
0435 }
0436 } else {
0437 asyh->clr.olut = armh->olut.visible;
0438 asyh->clr.core = armh->core.visible;
0439 asyh->clr.curs = armh->curs.visible;
0440 asyh->set.olut = asyh->olut.visible;
0441 asyh->set.core = asyh->core.visible;
0442 asyh->set.curs = asyh->curs.visible;
0443 }
0444
0445 ret = nv50_crc_atomic_check_head(head, asyh, armh);
0446 if (ret)
0447 return ret;
0448
0449 if (asyh->clr.mask || asyh->set.mask)
0450 nv50_atom(asyh->state.state)->lock_core = true;
0451 return 0;
0452 }
0453
0454 static const struct drm_crtc_helper_funcs
0455 nv50_head_help = {
0456 .atomic_check = nv50_head_atomic_check,
0457 .get_scanout_position = nouveau_display_scanoutpos,
0458 };
0459
0460 static void
0461 nv50_head_atomic_destroy_state(struct drm_crtc *crtc,
0462 struct drm_crtc_state *state)
0463 {
0464 struct nv50_head_atom *asyh = nv50_head_atom(state);
0465 __drm_atomic_helper_crtc_destroy_state(&asyh->state);
0466 kfree(asyh);
0467 }
0468
0469 static struct drm_crtc_state *
0470 nv50_head_atomic_duplicate_state(struct drm_crtc *crtc)
0471 {
0472 struct nv50_head_atom *armh = nv50_head_atom(crtc->state);
0473 struct nv50_head_atom *asyh;
0474 if (!(asyh = kmalloc(sizeof(*asyh), GFP_KERNEL)))
0475 return NULL;
0476 __drm_atomic_helper_crtc_duplicate_state(crtc, &asyh->state);
0477 asyh->wndw = armh->wndw;
0478 asyh->view = armh->view;
0479 asyh->mode = armh->mode;
0480 asyh->olut = armh->olut;
0481 asyh->core = armh->core;
0482 asyh->curs = armh->curs;
0483 asyh->base = armh->base;
0484 asyh->ovly = armh->ovly;
0485 asyh->dither = armh->dither;
0486 asyh->procamp = armh->procamp;
0487 asyh->crc = armh->crc;
0488 asyh->or = armh->or;
0489 asyh->dp = armh->dp;
0490 asyh->clr.mask = 0;
0491 asyh->set.mask = 0;
0492 return &asyh->state;
0493 }
0494
0495 static void
0496 nv50_head_reset(struct drm_crtc *crtc)
0497 {
0498 struct nv50_head_atom *asyh;
0499
0500 if (WARN_ON(!(asyh = kzalloc(sizeof(*asyh), GFP_KERNEL))))
0501 return;
0502
0503 if (crtc->state)
0504 nv50_head_atomic_destroy_state(crtc, crtc->state);
0505
0506 __drm_atomic_helper_crtc_reset(crtc, &asyh->state);
0507 }
0508
0509 static int
0510 nv50_head_late_register(struct drm_crtc *crtc)
0511 {
0512 return nv50_head_crc_late_register(nv50_head(crtc));
0513 }
0514
0515 static void
0516 nv50_head_destroy(struct drm_crtc *crtc)
0517 {
0518 struct nv50_head *head = nv50_head(crtc);
0519
0520 nvif_notify_dtor(&head->base.vblank);
0521 nv50_lut_fini(&head->olut);
0522 drm_crtc_cleanup(crtc);
0523 kfree(head);
0524 }
0525
0526 static const struct drm_crtc_funcs
0527 nv50_head_func = {
0528 .reset = nv50_head_reset,
0529 .destroy = nv50_head_destroy,
0530 .set_config = drm_atomic_helper_set_config,
0531 .page_flip = drm_atomic_helper_page_flip,
0532 .atomic_duplicate_state = nv50_head_atomic_duplicate_state,
0533 .atomic_destroy_state = nv50_head_atomic_destroy_state,
0534 .enable_vblank = nouveau_display_vblank_enable,
0535 .disable_vblank = nouveau_display_vblank_disable,
0536 .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
0537 .late_register = nv50_head_late_register,
0538 };
0539
0540 static const struct drm_crtc_funcs
0541 nvd9_head_func = {
0542 .reset = nv50_head_reset,
0543 .destroy = nv50_head_destroy,
0544 .set_config = drm_atomic_helper_set_config,
0545 .page_flip = drm_atomic_helper_page_flip,
0546 .atomic_duplicate_state = nv50_head_atomic_duplicate_state,
0547 .atomic_destroy_state = nv50_head_atomic_destroy_state,
0548 .enable_vblank = nouveau_display_vblank_enable,
0549 .disable_vblank = nouveau_display_vblank_disable,
0550 .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
0551 .verify_crc_source = nv50_crc_verify_source,
0552 .get_crc_sources = nv50_crc_get_sources,
0553 .set_crc_source = nv50_crc_set_source,
0554 .late_register = nv50_head_late_register,
0555 };
0556
0557 static int nv50_head_vblank_handler(struct nvif_notify *notify)
0558 {
0559 struct nouveau_crtc *nv_crtc =
0560 container_of(notify, struct nouveau_crtc, vblank);
0561
0562 if (drm_crtc_handle_vblank(&nv_crtc->base))
0563 nv50_crc_handle_vblank(nv50_head(&nv_crtc->base));
0564
0565 return NVIF_NOTIFY_KEEP;
0566 }
0567
0568 struct nv50_head *
0569 nv50_head_create(struct drm_device *dev, int index)
0570 {
0571 struct nouveau_drm *drm = nouveau_drm(dev);
0572 struct nv50_disp *disp = nv50_disp(dev);
0573 struct nv50_head *head;
0574 struct nv50_wndw *base, *ovly, *curs;
0575 struct nouveau_crtc *nv_crtc;
0576 struct drm_crtc *crtc;
0577 const struct drm_crtc_funcs *funcs;
0578 int ret;
0579
0580 head = kzalloc(sizeof(*head), GFP_KERNEL);
0581 if (!head)
0582 return ERR_PTR(-ENOMEM);
0583
0584 head->func = disp->core->func->head;
0585 head->base.index = index;
0586
0587 if (disp->disp->object.oclass < GF110_DISP)
0588 funcs = &nv50_head_func;
0589 else
0590 funcs = &nvd9_head_func;
0591
0592 if (disp->disp->object.oclass < GV100_DISP) {
0593 ret = nv50_base_new(drm, head->base.index, &base);
0594 ret = nv50_ovly_new(drm, head->base.index, &ovly);
0595 } else {
0596 ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_PRIMARY,
0597 head->base.index * 2 + 0, &base);
0598 ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_OVERLAY,
0599 head->base.index * 2 + 1, &ovly);
0600 }
0601 if (ret == 0)
0602 ret = nv50_curs_new(drm, head->base.index, &curs);
0603 if (ret) {
0604 kfree(head);
0605 return ERR_PTR(ret);
0606 }
0607
0608 nv_crtc = &head->base;
0609 crtc = &nv_crtc->base;
0610 drm_crtc_init_with_planes(dev, crtc, &base->plane, &curs->plane,
0611 funcs, "head-%d", head->base.index);
0612 drm_crtc_helper_add(crtc, &nv50_head_help);
0613
0614 drm_mode_crtc_set_gamma_size(crtc, 256);
0615 drm_crtc_enable_color_mgmt(crtc, base->func->ilut_size,
0616 disp->disp->object.oclass >= GF110_DISP,
0617 head->func->olut_size);
0618
0619 if (head->func->olut_set) {
0620 ret = nv50_lut_init(disp, &drm->client.mmu, &head->olut);
0621 if (ret) {
0622 nv50_head_destroy(crtc);
0623 return ERR_PTR(ret);
0624 }
0625 }
0626
0627 ret = nvif_notify_ctor(&disp->disp->object, "kmsVbl", nv50_head_vblank_handler,
0628 false, NV04_DISP_NTFY_VBLANK,
0629 &(struct nvif_notify_head_req_v0) {
0630 .head = nv_crtc->index,
0631 },
0632 sizeof(struct nvif_notify_head_req_v0),
0633 sizeof(struct nvif_notify_head_rep_v0),
0634 &nv_crtc->vblank);
0635 if (ret)
0636 return ERR_PTR(ret);
0637
0638 return head;
0639 }