1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#include "head.h"
23#include "base.h"
24#include "core.h"
25#include "curs.h"
26#include "ovly.h"
27#include "crc.h"
28
29#include <nvif/class.h>
30#include <nvif/event.h>
31#include <nvif/cl0046.h>
32
33#include <drm/drm_atomic_helper.h>
34#include <drm/drm_crtc_helper.h>
35#include <drm/drm_vblank.h>
36#include "nouveau_connector.h"
37
38void
39nv50_head_flush_clr(struct nv50_head *head,
40 struct nv50_head_atom *asyh, bool flush)
41{
42 union nv50_head_atom_mask clr = {
43 .mask = asyh->clr.mask & ~(flush ? 0 : asyh->set.mask),
44 };
45 if (clr.crc) nv50_crc_atomic_clr(head);
46 if (clr.olut) head->func->olut_clr(head);
47 if (clr.core) head->func->core_clr(head);
48 if (clr.curs) head->func->curs_clr(head);
49}
50
51void
52nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh)
53{
54 if (asyh->set.view ) head->func->view (head, asyh);
55 if (asyh->set.mode ) head->func->mode (head, asyh);
56 if (asyh->set.core ) head->func->core_set(head, asyh);
57 if (asyh->set.olut ) {
58 asyh->olut.offset = nv50_lut_load(&head->olut,
59 asyh->olut.buffer,
60 asyh->state.gamma_lut,
61 asyh->olut.load);
62 head->func->olut_set(head, asyh);
63 }
64 if (asyh->set.curs ) head->func->curs_set(head, asyh);
65 if (asyh->set.base ) head->func->base (head, asyh);
66 if (asyh->set.ovly ) head->func->ovly (head, asyh);
67 if (asyh->set.dither ) head->func->dither (head, asyh);
68 if (asyh->set.procamp) head->func->procamp (head, asyh);
69 if (asyh->set.crc ) nv50_crc_atomic_set (head, asyh);
70 if (asyh->set.or ) head->func->or (head, asyh);
71}
72
73static void
74nv50_head_atomic_check_procamp(struct nv50_head_atom *armh,
75 struct nv50_head_atom *asyh,
76 struct nouveau_conn_atom *asyc)
77{
78 const int vib = asyc->procamp.color_vibrance - 100;
79 const int hue = asyc->procamp.vibrant_hue - 90;
80 const int adj = (vib > 0) ? 50 : 0;
81 asyh->procamp.sat.cos = ((vib * 2047 + adj) / 100) & 0xfff;
82 asyh->procamp.sat.sin = ((hue * 2047) / 100) & 0xfff;
83 asyh->set.procamp = true;
84}
85
86static void
87nv50_head_atomic_check_dither(struct nv50_head_atom *armh,
88 struct nv50_head_atom *asyh,
89 struct nouveau_conn_atom *asyc)
90{
91 u32 mode = 0x00;
92
93 if (asyc->dither.mode) {
94 if (asyc->dither.mode == DITHERING_MODE_AUTO) {
95 if (asyh->base.depth > asyh->or.bpc * 3)
96 mode = DITHERING_MODE_DYNAMIC2X2;
97 } else {
98 mode = asyc->dither.mode;
99 }
100
101 if (asyc->dither.depth == DITHERING_DEPTH_AUTO) {
102 if (asyh->or.bpc >= 8)
103 mode |= DITHERING_DEPTH_8BPC;
104 } else {
105 mode |= asyc->dither.depth;
106 }
107 }
108
109 asyh->dither.enable = NVVAL_GET(mode, NV507D, HEAD_SET_DITHER_CONTROL, ENABLE);
110 asyh->dither.bits = NVVAL_GET(mode, NV507D, HEAD_SET_DITHER_CONTROL, BITS);
111 asyh->dither.mode = NVVAL_GET(mode, NV507D, HEAD_SET_DITHER_CONTROL, MODE);
112 asyh->set.dither = true;
113}
114
115static void
116nv50_head_atomic_check_view(struct nv50_head_atom *armh,
117 struct nv50_head_atom *asyh,
118 struct nouveau_conn_atom *asyc)
119{
120 struct drm_connector *connector = asyc->state.connector;
121 struct drm_display_mode *omode = &asyh->state.adjusted_mode;
122 struct drm_display_mode *umode = &asyh->state.mode;
123 int mode = asyc->scaler.mode;
124 struct edid *edid;
125 int umode_vdisplay, omode_hdisplay, omode_vdisplay;
126
127 if (connector->edid_blob_ptr)
128 edid = (struct edid *)connector->edid_blob_ptr->data;
129 else
130 edid = NULL;
131
132 if (!asyc->scaler.full) {
133 if (mode == DRM_MODE_SCALE_NONE)
134 omode = umode;
135 } else {
136
137 mode = DRM_MODE_SCALE_FULLSCREEN;
138 }
139
140
141
142
143 umode_vdisplay = umode->vdisplay;
144 if ((umode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
145 umode_vdisplay += umode->vtotal;
146 asyh->view.iW = umode->hdisplay;
147 asyh->view.iH = umode_vdisplay;
148
149 drm_mode_get_hv_timing(omode, &omode_hdisplay, &omode_vdisplay);
150 asyh->view.oW = omode_hdisplay;
151 asyh->view.oH = omode_vdisplay;
152
153
154
155
156
157 if ((asyc->scaler.underscan.mode == UNDERSCAN_ON ||
158 (asyc->scaler.underscan.mode == UNDERSCAN_AUTO &&
159 drm_detect_hdmi_monitor(edid)))) {
160 u32 bX = asyc->scaler.underscan.hborder;
161 u32 bY = asyc->scaler.underscan.vborder;
162 u32 r = (asyh->view.oH << 19) / asyh->view.oW;
163
164 if (bX) {
165 asyh->view.oW -= (bX * 2);
166 if (bY) asyh->view.oH -= (bY * 2);
167 else asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19;
168 } else {
169 asyh->view.oW -= (asyh->view.oW >> 4) + 32;
170 if (bY) asyh->view.oH -= (bY * 2);
171 else asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19;
172 }
173 }
174
175
176
177
178 switch (mode) {
179 case DRM_MODE_SCALE_CENTER:
180
181
182
183 asyh->view.oW = min(asyh->view.iW, asyh->view.oW);
184 asyh->view.oH = min(asyh->view.iH, asyh->view.oH);
185 break;
186 case DRM_MODE_SCALE_ASPECT:
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202 if (asyh->view.oW * asyh->view.iH > asyh->view.iW * asyh->view.oH) {
203
204 u32 r = (asyh->view.iW << 19) / asyh->view.iH;
205 asyh->view.oW = ((asyh->view.oH * r) + (r / 2)) >> 19;
206 } else {
207
208 u32 r = (asyh->view.iH << 19) / asyh->view.iW;
209 asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19;
210 }
211 break;
212 default:
213 break;
214 }
215
216 asyh->set.view = true;
217}
218
219static int
220nv50_head_atomic_check_lut(struct nv50_head *head,
221 struct nv50_head_atom *asyh)
222{
223 struct nv50_disp *disp = nv50_disp(head->base.base.dev);
224 struct drm_property_blob *olut = asyh->state.gamma_lut;
225 int size;
226
227
228 if (olut) {
229
230
231
232 if (asyh->wndw.olut) {
233
234
235
236 if (asyh->wndw.olut != asyh->wndw.mask)
237 return -EINVAL;
238 olut = NULL;
239 }
240 }
241
242 if (!olut) {
243 if (!head->func->olut_identity) {
244 asyh->olut.handle = 0;
245 return 0;
246 }
247 size = 0;
248 } else {
249 size = drm_color_lut_size(olut);
250 }
251
252 if (!head->func->olut(head, asyh, size)) {
253 DRM_DEBUG_KMS("Invalid olut\n");
254 return -EINVAL;
255 }
256 asyh->olut.handle = disp->core->chan.vram.handle;
257 asyh->olut.buffer = !asyh->olut.buffer;
258
259 return 0;
260}
261
262static void
263nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
264{
265 struct drm_display_mode *mode = &asyh->state.adjusted_mode;
266 struct nv50_head_mode *m = &asyh->mode;
267 u32 blankus;
268
269 drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE);
270
271
272
273
274
275
276
277
278 m->h.active = mode->crtc_htotal;
279 m->h.synce = mode->crtc_hsync_end - mode->crtc_hsync_start - 1;
280 m->h.blanke = mode->crtc_hblank_end - mode->crtc_hsync_start - 1;
281 m->h.blanks = m->h.blanke + mode->crtc_hdisplay;
282
283 m->v.active = mode->crtc_vtotal;
284 m->v.synce = mode->crtc_vsync_end - mode->crtc_vsync_start - 1;
285 m->v.blanke = mode->crtc_vblank_end - mode->crtc_vsync_start - 1;
286 m->v.blanks = m->v.blanke + mode->crtc_vdisplay;
287
288
289 blankus = (m->v.active - mode->crtc_vdisplay - 2) * m->h.active;
290 blankus *= 1000;
291 blankus /= mode->crtc_clock;
292 m->v.blankus = blankus;
293
294 if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
295 m->v.blank2e = m->v.active + m->v.blanke;
296 m->v.blank2s = m->v.blank2e + mode->crtc_vdisplay;
297 m->v.active = (m->v.active * 2) + 1;
298 m->interlace = true;
299 } else {
300 m->v.blank2e = 0;
301 m->v.blank2s = 1;
302 m->interlace = false;
303 }
304 m->clock = mode->crtc_clock;
305
306 asyh->or.nhsync = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
307 asyh->or.nvsync = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
308 asyh->set.or = head->func->or != NULL;
309 asyh->set.mode = true;
310}
311
312static int
313nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state)
314{
315 struct nouveau_drm *drm = nouveau_drm(crtc->dev);
316 struct nv50_head *head = nv50_head(crtc);
317 struct nv50_head_atom *armh = nv50_head_atom(crtc->state);
318 struct nv50_head_atom *asyh = nv50_head_atom(state);
319 struct nouveau_conn_atom *asyc = NULL;
320 struct drm_connector_state *conns;
321 struct drm_connector *conn;
322 int i, ret;
323
324 NV_ATOMIC(drm, "%s atomic_check %d\n", crtc->name, asyh->state.active);
325 if (asyh->state.active) {
326 for_each_new_connector_in_state(asyh->state.state, conn, conns, i) {
327 if (conns->crtc == crtc) {
328 asyc = nouveau_conn_atom(conns);
329 break;
330 }
331 }
332
333 if (armh->state.active) {
334 if (asyc) {
335 if (asyh->state.mode_changed)
336 asyc->set.scaler = true;
337 if (armh->base.depth != asyh->base.depth)
338 asyc->set.dither = true;
339 }
340 } else {
341 if (asyc)
342 asyc->set.mask = ~0;
343 asyh->set.mask = ~0;
344 asyh->set.or = head->func->or != NULL;
345 }
346
347 if (asyh->state.mode_changed || asyh->state.connectors_changed)
348 nv50_head_atomic_check_mode(head, asyh);
349
350 if (asyh->state.color_mgmt_changed ||
351 memcmp(&armh->wndw, &asyh->wndw, sizeof(asyh->wndw))) {
352 int ret = nv50_head_atomic_check_lut(head, asyh);
353 if (ret)
354 return ret;
355
356 asyh->olut.visible = asyh->olut.handle != 0;
357 }
358
359 if (asyc) {
360 if (asyc->set.scaler)
361 nv50_head_atomic_check_view(armh, asyh, asyc);
362 if (asyc->set.dither)
363 nv50_head_atomic_check_dither(armh, asyh, asyc);
364 if (asyc->set.procamp)
365 nv50_head_atomic_check_procamp(armh, asyh, asyc);
366 }
367
368 if (head->func->core_calc) {
369 head->func->core_calc(head, asyh);
370 if (!asyh->core.visible)
371 asyh->olut.visible = false;
372 }
373
374 asyh->set.base = armh->base.cpp != asyh->base.cpp;
375 asyh->set.ovly = armh->ovly.cpp != asyh->ovly.cpp;
376 } else {
377 asyh->olut.visible = false;
378 asyh->core.visible = false;
379 asyh->curs.visible = false;
380 asyh->base.cpp = 0;
381 asyh->ovly.cpp = 0;
382 }
383
384 if (!drm_atomic_crtc_needs_modeset(&asyh->state)) {
385 if (asyh->core.visible) {
386 if (memcmp(&armh->core, &asyh->core, sizeof(asyh->core)))
387 asyh->set.core = true;
388 } else
389 if (armh->core.visible) {
390 asyh->clr.core = true;
391 }
392
393 if (asyh->curs.visible) {
394 if (memcmp(&armh->curs, &asyh->curs, sizeof(asyh->curs)))
395 asyh->set.curs = true;
396 } else
397 if (armh->curs.visible) {
398 asyh->clr.curs = true;
399 }
400
401 if (asyh->olut.visible) {
402 if (memcmp(&armh->olut, &asyh->olut, sizeof(asyh->olut)))
403 asyh->set.olut = true;
404 } else
405 if (armh->olut.visible) {
406 asyh->clr.olut = true;
407 }
408 } else {
409 asyh->clr.olut = armh->olut.visible;
410 asyh->clr.core = armh->core.visible;
411 asyh->clr.curs = armh->curs.visible;
412 asyh->set.olut = asyh->olut.visible;
413 asyh->set.core = asyh->core.visible;
414 asyh->set.curs = asyh->curs.visible;
415 }
416
417 ret = nv50_crc_atomic_check_head(head, asyh, armh);
418 if (ret)
419 return ret;
420
421 if (asyh->clr.mask || asyh->set.mask)
422 nv50_atom(asyh->state.state)->lock_core = true;
423 return 0;
424}
425
426static const struct drm_crtc_helper_funcs
427nv50_head_help = {
428 .atomic_check = nv50_head_atomic_check,
429 .get_scanout_position = nouveau_display_scanoutpos,
430};
431
432static void
433nv50_head_atomic_destroy_state(struct drm_crtc *crtc,
434 struct drm_crtc_state *state)
435{
436 struct nv50_head_atom *asyh = nv50_head_atom(state);
437 __drm_atomic_helper_crtc_destroy_state(&asyh->state);
438 kfree(asyh);
439}
440
441static struct drm_crtc_state *
442nv50_head_atomic_duplicate_state(struct drm_crtc *crtc)
443{
444 struct nv50_head_atom *armh = nv50_head_atom(crtc->state);
445 struct nv50_head_atom *asyh;
446 if (!(asyh = kmalloc(sizeof(*asyh), GFP_KERNEL)))
447 return NULL;
448 __drm_atomic_helper_crtc_duplicate_state(crtc, &asyh->state);
449 asyh->wndw = armh->wndw;
450 asyh->view = armh->view;
451 asyh->mode = armh->mode;
452 asyh->olut = armh->olut;
453 asyh->core = armh->core;
454 asyh->curs = armh->curs;
455 asyh->base = armh->base;
456 asyh->ovly = armh->ovly;
457 asyh->dither = armh->dither;
458 asyh->procamp = armh->procamp;
459 asyh->crc = armh->crc;
460 asyh->or = armh->or;
461 asyh->dp = armh->dp;
462 asyh->clr.mask = 0;
463 asyh->set.mask = 0;
464 return &asyh->state;
465}
466
467static void
468nv50_head_reset(struct drm_crtc *crtc)
469{
470 struct nv50_head_atom *asyh;
471
472 if (WARN_ON(!(asyh = kzalloc(sizeof(*asyh), GFP_KERNEL))))
473 return;
474
475 if (crtc->state)
476 nv50_head_atomic_destroy_state(crtc, crtc->state);
477
478 __drm_atomic_helper_crtc_reset(crtc, &asyh->state);
479}
480
481static int
482nv50_head_late_register(struct drm_crtc *crtc)
483{
484 return nv50_head_crc_late_register(nv50_head(crtc));
485}
486
487static void
488nv50_head_destroy(struct drm_crtc *crtc)
489{
490 struct nv50_head *head = nv50_head(crtc);
491
492 nvif_notify_dtor(&head->base.vblank);
493 nv50_lut_fini(&head->olut);
494 drm_crtc_cleanup(crtc);
495 kfree(head);
496}
497
498static const struct drm_crtc_funcs
499nv50_head_func = {
500 .reset = nv50_head_reset,
501 .gamma_set = drm_atomic_helper_legacy_gamma_set,
502 .destroy = nv50_head_destroy,
503 .set_config = drm_atomic_helper_set_config,
504 .page_flip = drm_atomic_helper_page_flip,
505 .atomic_duplicate_state = nv50_head_atomic_duplicate_state,
506 .atomic_destroy_state = nv50_head_atomic_destroy_state,
507 .enable_vblank = nouveau_display_vblank_enable,
508 .disable_vblank = nouveau_display_vblank_disable,
509 .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
510 .late_register = nv50_head_late_register,
511};
512
513static const struct drm_crtc_funcs
514nvd9_head_func = {
515 .reset = nv50_head_reset,
516 .gamma_set = drm_atomic_helper_legacy_gamma_set,
517 .destroy = nv50_head_destroy,
518 .set_config = drm_atomic_helper_set_config,
519 .page_flip = drm_atomic_helper_page_flip,
520 .atomic_duplicate_state = nv50_head_atomic_duplicate_state,
521 .atomic_destroy_state = nv50_head_atomic_destroy_state,
522 .enable_vblank = nouveau_display_vblank_enable,
523 .disable_vblank = nouveau_display_vblank_disable,
524 .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
525 .verify_crc_source = nv50_crc_verify_source,
526 .get_crc_sources = nv50_crc_get_sources,
527 .set_crc_source = nv50_crc_set_source,
528 .late_register = nv50_head_late_register,
529};
530
531static int nv50_head_vblank_handler(struct nvif_notify *notify)
532{
533 struct nouveau_crtc *nv_crtc =
534 container_of(notify, struct nouveau_crtc, vblank);
535
536 if (drm_crtc_handle_vblank(&nv_crtc->base))
537 nv50_crc_handle_vblank(nv50_head(&nv_crtc->base));
538
539 return NVIF_NOTIFY_KEEP;
540}
541
542struct nv50_head *
543nv50_head_create(struct drm_device *dev, int index)
544{
545 struct nouveau_drm *drm = nouveau_drm(dev);
546 struct nv50_disp *disp = nv50_disp(dev);
547 struct nv50_head *head;
548 struct nv50_wndw *base, *ovly, *curs;
549 struct nouveau_crtc *nv_crtc;
550 struct drm_crtc *crtc;
551 const struct drm_crtc_funcs *funcs;
552 int ret;
553
554 head = kzalloc(sizeof(*head), GFP_KERNEL);
555 if (!head)
556 return ERR_PTR(-ENOMEM);
557
558 head->func = disp->core->func->head;
559 head->base.index = index;
560
561 if (disp->disp->object.oclass < GF110_DISP)
562 funcs = &nv50_head_func;
563 else
564 funcs = &nvd9_head_func;
565
566 if (disp->disp->object.oclass < GV100_DISP) {
567 ret = nv50_base_new(drm, head->base.index, &base);
568 ret = nv50_ovly_new(drm, head->base.index, &ovly);
569 } else {
570 ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_PRIMARY,
571 head->base.index * 2 + 0, &base);
572 ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_OVERLAY,
573 head->base.index * 2 + 1, &ovly);
574 }
575 if (ret == 0)
576 ret = nv50_curs_new(drm, head->base.index, &curs);
577 if (ret) {
578 kfree(head);
579 return ERR_PTR(ret);
580 }
581
582 nv_crtc = &head->base;
583 crtc = &nv_crtc->base;
584 drm_crtc_init_with_planes(dev, crtc, &base->plane, &curs->plane,
585 funcs, "head-%d", head->base.index);
586 drm_crtc_helper_add(crtc, &nv50_head_help);
587
588 drm_mode_crtc_set_gamma_size(crtc, 256);
589 drm_crtc_enable_color_mgmt(crtc, base->func->ilut_size,
590 disp->disp->object.oclass >= GF110_DISP,
591 head->func->olut_size);
592
593 if (head->func->olut_set) {
594 ret = nv50_lut_init(disp, &drm->client.mmu, &head->olut);
595 if (ret) {
596 nv50_head_destroy(crtc);
597 return ERR_PTR(ret);
598 }
599 }
600
601 ret = nvif_notify_ctor(&disp->disp->object, "kmsVbl", nv50_head_vblank_handler,
602 false, NV04_DISP_NTFY_VBLANK,
603 &(struct nvif_notify_head_req_v0) {
604 .head = nv_crtc->index,
605 },
606 sizeof(struct nvif_notify_head_req_v0),
607 sizeof(struct nvif_notify_head_rep_v0),
608 &nv_crtc->vblank);
609 if (ret)
610 return ERR_PTR(ret);
611
612 return head;
613}
614