]>
Commit | Line | Data |
---|---|---|
1590700d BS |
1 | /* |
2 | * Copyright 2018 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 | #include "head.h" | |
23 | #include "base.h" | |
24 | #include "core.h" | |
25 | #include "curs.h" | |
26 | #include "ovly.h" | |
27 | ||
28 | #include <nvif/class.h> | |
29 | ||
30 | #include <drm/drm_atomic_helper.h> | |
31 | #include <drm/drm_crtc_helper.h> | |
32 | #include "nouveau_connector.h" | |
1590700d | 33 | void |
f88bc9d3 BS |
34 | nv50_head_flush_clr(struct nv50_head *head, |
35 | struct nv50_head_atom *asyh, bool flush) | |
1590700d | 36 | { |
f88bc9d3 BS |
37 | union nv50_head_atom_mask clr = { |
38 | .mask = asyh->clr.mask & ~(flush ? 0 : asyh->set.mask), | |
39 | }; | |
119608a7 | 40 | if (clr.olut) head->func->olut_clr(head); |
f88bc9d3 BS |
41 | if (clr.core) head->func->core_clr(head); |
42 | if (clr.curs) head->func->curs_clr(head); | |
1590700d BS |
43 | } |
44 | ||
45 | void | |
46 | nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh) | |
47 | { | |
48 | if (asyh->set.view ) head->func->view (head, asyh); | |
49 | if (asyh->set.mode ) head->func->mode (head, asyh); | |
1590700d | 50 | if (asyh->set.core ) head->func->core_set(head, asyh); |
119608a7 BS |
51 | if (asyh->set.olut ) { |
52 | asyh->olut.offset = nv50_lut_load(&head->olut, | |
119608a7 | 53 | asyh->olut.buffer, |
cb55cd0c BS |
54 | asyh->state.gamma_lut, |
55 | asyh->olut.load); | |
119608a7 BS |
56 | head->func->olut_set(head, asyh); |
57 | } | |
1590700d BS |
58 | if (asyh->set.curs ) head->func->curs_set(head, asyh); |
59 | if (asyh->set.base ) head->func->base (head, asyh); | |
60 | if (asyh->set.ovly ) head->func->ovly (head, asyh); | |
61 | if (asyh->set.dither ) head->func->dither (head, asyh); | |
62 | if (asyh->set.procamp) head->func->procamp (head, asyh); | |
63 | if (asyh->set.or ) head->func->or (head, asyh); | |
64 | } | |
65 | ||
66 | static void | |
67 | nv50_head_atomic_check_procamp(struct nv50_head_atom *armh, | |
68 | struct nv50_head_atom *asyh, | |
69 | struct nouveau_conn_atom *asyc) | |
70 | { | |
71 | const int vib = asyc->procamp.color_vibrance - 100; | |
72 | const int hue = asyc->procamp.vibrant_hue - 90; | |
73 | const int adj = (vib > 0) ? 50 : 0; | |
74 | asyh->procamp.sat.cos = ((vib * 2047 + adj) / 100) & 0xfff; | |
75 | asyh->procamp.sat.sin = ((hue * 2047) / 100) & 0xfff; | |
76 | asyh->set.procamp = true; | |
77 | } | |
78 | ||
79 | static void | |
80 | nv50_head_atomic_check_dither(struct nv50_head_atom *armh, | |
81 | struct nv50_head_atom *asyh, | |
82 | struct nouveau_conn_atom *asyc) | |
83 | { | |
1590700d BS |
84 | u32 mode = 0x00; |
85 | ||
86 | if (asyc->dither.mode == DITHERING_MODE_AUTO) { | |
ac2d9275 | 87 | if (asyh->base.depth > asyh->or.bpc * 3) |
1590700d BS |
88 | mode = DITHERING_MODE_DYNAMIC2X2; |
89 | } else { | |
90 | mode = asyc->dither.mode; | |
91 | } | |
92 | ||
93 | if (asyc->dither.depth == DITHERING_DEPTH_AUTO) { | |
ac2d9275 | 94 | if (asyh->or.bpc >= 8) |
1590700d BS |
95 | mode |= DITHERING_DEPTH_8BPC; |
96 | } else { | |
97 | mode |= asyc->dither.depth; | |
98 | } | |
99 | ||
100 | asyh->dither.enable = mode; | |
101 | asyh->dither.bits = mode >> 1; | |
102 | asyh->dither.mode = mode >> 3; | |
103 | asyh->set.dither = true; | |
104 | } | |
105 | ||
106 | static void | |
107 | nv50_head_atomic_check_view(struct nv50_head_atom *armh, | |
108 | struct nv50_head_atom *asyh, | |
109 | struct nouveau_conn_atom *asyc) | |
110 | { | |
111 | struct drm_connector *connector = asyc->state.connector; | |
112 | struct drm_display_mode *omode = &asyh->state.adjusted_mode; | |
113 | struct drm_display_mode *umode = &asyh->state.mode; | |
114 | int mode = asyc->scaler.mode; | |
115 | struct edid *edid; | |
116 | int umode_vdisplay, omode_hdisplay, omode_vdisplay; | |
117 | ||
118 | if (connector->edid_blob_ptr) | |
119 | edid = (struct edid *)connector->edid_blob_ptr->data; | |
120 | else | |
121 | edid = NULL; | |
122 | ||
123 | if (!asyc->scaler.full) { | |
124 | if (mode == DRM_MODE_SCALE_NONE) | |
125 | omode = umode; | |
126 | } else { | |
127 | /* Non-EDID LVDS/eDP mode. */ | |
128 | mode = DRM_MODE_SCALE_FULLSCREEN; | |
129 | } | |
130 | ||
131 | /* For the user-specified mode, we must ignore doublescan and | |
132 | * the like, but honor frame packing. | |
133 | */ | |
134 | umode_vdisplay = umode->vdisplay; | |
135 | if ((umode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) | |
136 | umode_vdisplay += umode->vtotal; | |
137 | asyh->view.iW = umode->hdisplay; | |
138 | asyh->view.iH = umode_vdisplay; | |
139 | /* For the output mode, we can just use the stock helper. */ | |
140 | drm_mode_get_hv_timing(omode, &omode_hdisplay, &omode_vdisplay); | |
141 | asyh->view.oW = omode_hdisplay; | |
142 | asyh->view.oH = omode_vdisplay; | |
143 | ||
144 | /* Add overscan compensation if necessary, will keep the aspect | |
145 | * ratio the same as the backend mode unless overridden by the | |
146 | * user setting both hborder and vborder properties. | |
147 | */ | |
148 | if ((asyc->scaler.underscan.mode == UNDERSCAN_ON || | |
149 | (asyc->scaler.underscan.mode == UNDERSCAN_AUTO && | |
150 | drm_detect_hdmi_monitor(edid)))) { | |
151 | u32 bX = asyc->scaler.underscan.hborder; | |
152 | u32 bY = asyc->scaler.underscan.vborder; | |
153 | u32 r = (asyh->view.oH << 19) / asyh->view.oW; | |
154 | ||
155 | if (bX) { | |
156 | asyh->view.oW -= (bX * 2); | |
157 | if (bY) asyh->view.oH -= (bY * 2); | |
158 | else asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19; | |
159 | } else { | |
160 | asyh->view.oW -= (asyh->view.oW >> 4) + 32; | |
161 | if (bY) asyh->view.oH -= (bY * 2); | |
162 | else asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19; | |
163 | } | |
164 | } | |
165 | ||
166 | /* Handle CENTER/ASPECT scaling, taking into account the areas | |
167 | * removed already for overscan compensation. | |
168 | */ | |
169 | switch (mode) { | |
170 | case DRM_MODE_SCALE_CENTER: | |
533f4752 IM |
171 | /* NOTE: This will cause scaling when the input is |
172 | * larger than the output. | |
173 | */ | |
174 | asyh->view.oW = min(asyh->view.iW, asyh->view.oW); | |
175 | asyh->view.oH = min(asyh->view.iH, asyh->view.oH); | |
176 | break; | |
1590700d | 177 | case DRM_MODE_SCALE_ASPECT: |
533f4752 IM |
178 | /* Determine whether the scaling should be on width or on |
179 | * height. This is done by comparing the aspect ratios of the | |
180 | * sizes. If the output AR is larger than input AR, that means | |
181 | * we want to change the width (letterboxed on the | |
182 | * left/right), otherwise on the height (letterboxed on the | |
183 | * top/bottom). | |
184 | * | |
185 | * E.g. 4:3 (1.333) AR image displayed on a 16:10 (1.6) AR | |
186 | * screen will have letterboxes on the left/right. However a | |
187 | * 16:9 (1.777) AR image on that same screen will have | |
188 | * letterboxes on the top/bottom. | |
189 | * | |
190 | * inputAR = iW / iH; outputAR = oW / oH | |
191 | * outputAR > inputAR is equivalent to oW * iH > iW * oH | |
192 | */ | |
193 | if (asyh->view.oW * asyh->view.iH > asyh->view.iW * asyh->view.oH) { | |
194 | /* Recompute output width, i.e. left/right letterbox */ | |
1590700d BS |
195 | u32 r = (asyh->view.iW << 19) / asyh->view.iH; |
196 | asyh->view.oW = ((asyh->view.oH * r) + (r / 2)) >> 19; | |
197 | } else { | |
533f4752 | 198 | /* Recompute output height, i.e. top/bottom letterbox */ |
1590700d BS |
199 | u32 r = (asyh->view.iH << 19) / asyh->view.iW; |
200 | asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19; | |
201 | } | |
202 | break; | |
203 | default: | |
204 | break; | |
205 | } | |
206 | ||
207 | asyh->set.view = true; | |
208 | } | |
209 | ||
119608a7 | 210 | static int |
1590700d | 211 | nv50_head_atomic_check_lut(struct nv50_head *head, |
1590700d BS |
212 | struct nv50_head_atom *asyh) |
213 | { | |
214 | struct nv50_disp *disp = nv50_disp(head->base.base.dev); | |
119608a7 BS |
215 | struct drm_property_blob *olut = asyh->state.gamma_lut; |
216 | ||
217 | /* Determine whether core output LUT should be enabled. */ | |
218 | if (olut) { | |
219 | /* Check if any window(s) have stolen the core output LUT | |
220 | * to as an input LUT for legacy gamma + I8 colour format. | |
221 | */ | |
222 | if (asyh->wndw.olut) { | |
223 | /* If any window has stolen the core output LUT, | |
224 | * all of them must. | |
225 | */ | |
226 | if (asyh->wndw.olut != asyh->wndw.mask) | |
227 | return -EINVAL; | |
228 | olut = NULL; | |
229 | } | |
1590700d BS |
230 | } |
231 | ||
cb55cd0c | 232 | if (!olut && !head->func->olut_identity) { |
119608a7 BS |
233 | asyh->olut.handle = 0; |
234 | return 0; | |
1590700d | 235 | } |
119608a7 BS |
236 | |
237 | asyh->olut.handle = disp->core->chan.vram.handle; | |
238 | asyh->olut.buffer = !asyh->olut.buffer; | |
239 | head->func->olut(head, asyh); | |
240 | return 0; | |
1590700d BS |
241 | } |
242 | ||
243 | static void | |
244 | nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) | |
245 | { | |
246 | struct drm_display_mode *mode = &asyh->state.adjusted_mode; | |
247 | struct nv50_head_mode *m = &asyh->mode; | |
248 | u32 blankus; | |
249 | ||
250 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE); | |
251 | ||
252 | /* | |
253 | * DRM modes are defined in terms of a repeating interval | |
254 | * starting with the active display area. The hardware modes | |
255 | * are defined in terms of a repeating interval starting one | |
256 | * unit (pixel or line) into the sync pulse. So, add bias. | |
257 | */ | |
258 | ||
259 | m->h.active = mode->crtc_htotal; | |
260 | m->h.synce = mode->crtc_hsync_end - mode->crtc_hsync_start - 1; | |
261 | m->h.blanke = mode->crtc_hblank_end - mode->crtc_hsync_start - 1; | |
262 | m->h.blanks = m->h.blanke + mode->crtc_hdisplay; | |
263 | ||
264 | m->v.active = mode->crtc_vtotal; | |
265 | m->v.synce = mode->crtc_vsync_end - mode->crtc_vsync_start - 1; | |
266 | m->v.blanke = mode->crtc_vblank_end - mode->crtc_vsync_start - 1; | |
267 | m->v.blanks = m->v.blanke + mode->crtc_vdisplay; | |
268 | ||
269 | /*XXX: Safe underestimate, even "0" works */ | |
270 | blankus = (m->v.active - mode->crtc_vdisplay - 2) * m->h.active; | |
271 | blankus *= 1000; | |
272 | blankus /= mode->crtc_clock; | |
273 | m->v.blankus = blankus; | |
274 | ||
275 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) { | |
276 | m->v.blank2e = m->v.active + m->v.blanke; | |
277 | m->v.blank2s = m->v.blank2e + mode->crtc_vdisplay; | |
278 | m->v.active = (m->v.active * 2) + 1; | |
279 | m->interlace = true; | |
280 | } else { | |
281 | m->v.blank2e = 0; | |
282 | m->v.blank2s = 1; | |
283 | m->interlace = false; | |
284 | } | |
285 | m->clock = mode->crtc_clock; | |
286 | ||
287 | asyh->or.nhsync = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); | |
288 | asyh->or.nvsync = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); | |
289 | asyh->set.or = head->func->or != NULL; | |
290 | asyh->set.mode = true; | |
291 | } | |
292 | ||
293 | static int | |
294 | nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) | |
295 | { | |
296 | struct nouveau_drm *drm = nouveau_drm(crtc->dev); | |
1590700d BS |
297 | struct nv50_head *head = nv50_head(crtc); |
298 | struct nv50_head_atom *armh = nv50_head_atom(crtc->state); | |
299 | struct nv50_head_atom *asyh = nv50_head_atom(state); | |
300 | struct nouveau_conn_atom *asyc = NULL; | |
301 | struct drm_connector_state *conns; | |
302 | struct drm_connector *conn; | |
303 | int i; | |
304 | ||
305 | NV_ATOMIC(drm, "%s atomic_check %d\n", crtc->name, asyh->state.active); | |
306 | if (asyh->state.active) { | |
307 | for_each_new_connector_in_state(asyh->state.state, conn, conns, i) { | |
308 | if (conns->crtc == crtc) { | |
309 | asyc = nouveau_conn_atom(conns); | |
310 | break; | |
311 | } | |
312 | } | |
313 | ||
314 | if (armh->state.active) { | |
315 | if (asyc) { | |
316 | if (asyh->state.mode_changed) | |
317 | asyc->set.scaler = true; | |
318 | if (armh->base.depth != asyh->base.depth) | |
319 | asyc->set.dither = true; | |
320 | } | |
321 | } else { | |
322 | if (asyc) | |
323 | asyc->set.mask = ~0; | |
324 | asyh->set.mask = ~0; | |
325 | asyh->set.or = head->func->or != NULL; | |
326 | } | |
327 | ||
a0b694d0 | 328 | if (asyh->state.mode_changed || asyh->state.connectors_changed) |
1590700d BS |
329 | nv50_head_atomic_check_mode(head, asyh); |
330 | ||
331 | if (asyh->state.color_mgmt_changed || | |
119608a7 BS |
332 | memcmp(&armh->wndw, &asyh->wndw, sizeof(asyh->wndw))) { |
333 | int ret = nv50_head_atomic_check_lut(head, asyh); | |
334 | if (ret) | |
335 | return ret; | |
336 | ||
337 | asyh->olut.visible = asyh->olut.handle != 0; | |
338 | } | |
1590700d BS |
339 | |
340 | if (asyc) { | |
341 | if (asyc->set.scaler) | |
342 | nv50_head_atomic_check_view(armh, asyh, asyc); | |
343 | if (asyc->set.dither) | |
344 | nv50_head_atomic_check_dither(armh, asyh, asyc); | |
345 | if (asyc->set.procamp) | |
346 | nv50_head_atomic_check_procamp(armh, asyh, asyc); | |
347 | } | |
348 | ||
119608a7 | 349 | if (head->func->core_calc) { |
09e1b78a | 350 | head->func->core_calc(head, asyh); |
119608a7 BS |
351 | if (!asyh->core.visible) |
352 | asyh->olut.visible = false; | |
353 | } | |
09e1b78a | 354 | |
1590700d BS |
355 | asyh->set.base = armh->base.cpp != asyh->base.cpp; |
356 | asyh->set.ovly = armh->ovly.cpp != asyh->ovly.cpp; | |
357 | } else { | |
119608a7 | 358 | asyh->olut.visible = false; |
1590700d BS |
359 | asyh->core.visible = false; |
360 | asyh->curs.visible = false; | |
361 | asyh->base.cpp = 0; | |
362 | asyh->ovly.cpp = 0; | |
363 | } | |
364 | ||
365 | if (!drm_atomic_crtc_needs_modeset(&asyh->state)) { | |
366 | if (asyh->core.visible) { | |
367 | if (memcmp(&armh->core, &asyh->core, sizeof(asyh->core))) | |
368 | asyh->set.core = true; | |
369 | } else | |
370 | if (armh->core.visible) { | |
371 | asyh->clr.core = true; | |
372 | } | |
373 | ||
374 | if (asyh->curs.visible) { | |
375 | if (memcmp(&armh->curs, &asyh->curs, sizeof(asyh->curs))) | |
376 | asyh->set.curs = true; | |
377 | } else | |
378 | if (armh->curs.visible) { | |
379 | asyh->clr.curs = true; | |
380 | } | |
119608a7 BS |
381 | |
382 | if (asyh->olut.visible) { | |
383 | if (memcmp(&armh->olut, &asyh->olut, sizeof(asyh->olut))) | |
384 | asyh->set.olut = true; | |
385 | } else | |
386 | if (armh->olut.visible) { | |
387 | asyh->clr.olut = true; | |
388 | } | |
1590700d | 389 | } else { |
119608a7 | 390 | asyh->clr.olut = armh->olut.visible; |
1590700d BS |
391 | asyh->clr.core = armh->core.visible; |
392 | asyh->clr.curs = armh->curs.visible; | |
119608a7 | 393 | asyh->set.olut = asyh->olut.visible; |
1590700d BS |
394 | asyh->set.core = asyh->core.visible; |
395 | asyh->set.curs = asyh->curs.visible; | |
396 | } | |
397 | ||
398 | if (asyh->clr.mask || asyh->set.mask) | |
399 | nv50_atom(asyh->state.state)->lock_core = true; | |
400 | return 0; | |
401 | } | |
402 | ||
403 | static const struct drm_crtc_helper_funcs | |
404 | nv50_head_help = { | |
405 | .atomic_check = nv50_head_atomic_check, | |
406 | }; | |
407 | ||
408 | static void | |
409 | nv50_head_atomic_destroy_state(struct drm_crtc *crtc, | |
410 | struct drm_crtc_state *state) | |
411 | { | |
412 | struct nv50_head_atom *asyh = nv50_head_atom(state); | |
413 | __drm_atomic_helper_crtc_destroy_state(&asyh->state); | |
414 | kfree(asyh); | |
415 | } | |
416 | ||
417 | static struct drm_crtc_state * | |
418 | nv50_head_atomic_duplicate_state(struct drm_crtc *crtc) | |
419 | { | |
420 | struct nv50_head_atom *armh = nv50_head_atom(crtc->state); | |
421 | struct nv50_head_atom *asyh; | |
422 | if (!(asyh = kmalloc(sizeof(*asyh), GFP_KERNEL))) | |
423 | return NULL; | |
424 | __drm_atomic_helper_crtc_duplicate_state(crtc, &asyh->state); | |
119608a7 | 425 | asyh->wndw = armh->wndw; |
1590700d BS |
426 | asyh->view = armh->view; |
427 | asyh->mode = armh->mode; | |
119608a7 | 428 | asyh->olut = armh->olut; |
1590700d BS |
429 | asyh->core = armh->core; |
430 | asyh->curs = armh->curs; | |
431 | asyh->base = armh->base; | |
432 | asyh->ovly = armh->ovly; | |
433 | asyh->dither = armh->dither; | |
434 | asyh->procamp = armh->procamp; | |
c4a52d66 | 435 | asyh->or = armh->or; |
88ec89ad | 436 | asyh->dp = armh->dp; |
1590700d BS |
437 | asyh->clr.mask = 0; |
438 | asyh->set.mask = 0; | |
439 | return &asyh->state; | |
440 | } | |
441 | ||
1590700d BS |
442 | static void |
443 | nv50_head_reset(struct drm_crtc *crtc) | |
444 | { | |
445 | struct nv50_head_atom *asyh; | |
446 | ||
447 | if (WARN_ON(!(asyh = kzalloc(sizeof(*asyh), GFP_KERNEL)))) | |
448 | return; | |
449 | ||
7d26097b ML |
450 | if (crtc->state) |
451 | nv50_head_atomic_destroy_state(crtc, crtc->state); | |
452 | ||
1590700d BS |
453 | __drm_atomic_helper_crtc_reset(crtc, &asyh->state); |
454 | } | |
455 | ||
456 | static void | |
457 | nv50_head_destroy(struct drm_crtc *crtc) | |
458 | { | |
459 | struct nv50_head *head = nv50_head(crtc); | |
119608a7 | 460 | nv50_lut_fini(&head->olut); |
1590700d BS |
461 | drm_crtc_cleanup(crtc); |
462 | kfree(head); | |
463 | } | |
464 | ||
465 | static const struct drm_crtc_funcs | |
466 | nv50_head_func = { | |
467 | .reset = nv50_head_reset, | |
468 | .gamma_set = drm_atomic_helper_legacy_gamma_set, | |
469 | .destroy = nv50_head_destroy, | |
470 | .set_config = drm_atomic_helper_set_config, | |
471 | .page_flip = drm_atomic_helper_page_flip, | |
472 | .atomic_duplicate_state = nv50_head_atomic_duplicate_state, | |
473 | .atomic_destroy_state = nv50_head_atomic_destroy_state, | |
474 | }; | |
475 | ||
476 | int | |
477 | nv50_head_create(struct drm_device *dev, int index) | |
478 | { | |
479 | struct nouveau_drm *drm = nouveau_drm(dev); | |
480 | struct nv50_disp *disp = nv50_disp(dev); | |
481 | struct nv50_head *head; | |
6f78991f | 482 | struct nv50_wndw *base, *ovly, *curs; |
1590700d | 483 | struct drm_crtc *crtc; |
119608a7 | 484 | int ret; |
1590700d BS |
485 | |
486 | head = kzalloc(sizeof(*head), GFP_KERNEL); | |
487 | if (!head) | |
488 | return -ENOMEM; | |
489 | ||
490 | head->func = disp->core->func->head; | |
491 | head->base.index = index; | |
facaed62 BS |
492 | |
493 | if (disp->disp->object.oclass < GV100_DISP) { | |
6f78991f BS |
494 | ret = nv50_base_new(drm, head->base.index, &base); |
495 | ret = nv50_ovly_new(drm, head->base.index, &ovly); | |
facaed62 | 496 | } else { |
facaed62 | 497 | ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_PRIMARY, |
6f78991f BS |
498 | head->base.index * 2 + 0, &base); |
499 | ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_OVERLAY, | |
500 | head->base.index * 2 + 1, &ovly); | |
facaed62 | 501 | } |
1590700d BS |
502 | if (ret == 0) |
503 | ret = nv50_curs_new(drm, head->base.index, &curs); | |
504 | if (ret) { | |
505 | kfree(head); | |
506 | return ret; | |
507 | } | |
508 | ||
509 | crtc = &head->base.base; | |
6f78991f | 510 | drm_crtc_init_with_planes(dev, crtc, &base->plane, &curs->plane, |
1590700d BS |
511 | &nv50_head_func, "head-%d", head->base.index); |
512 | drm_crtc_helper_add(crtc, &nv50_head_help); | |
513 | drm_mode_crtc_set_gamma_size(crtc, 256); | |
dffa4878 | 514 | if (disp->disp->object.oclass >= GF110_DISP) |
af364a44 IM |
515 | drm_crtc_enable_color_mgmt(crtc, 256, true, 256); |
516 | else | |
517 | drm_crtc_enable_color_mgmt(crtc, 0, false, 256); | |
1590700d | 518 | |
119608a7 BS |
519 | if (head->func->olut_set) { |
520 | ret = nv50_lut_init(disp, &drm->client.mmu, &head->olut); | |
1590700d BS |
521 | if (ret) |
522 | goto out; | |
523 | } | |
524 | ||
1590700d BS |
525 | out: |
526 | if (ret) | |
527 | nv50_head_destroy(crtc); | |
528 | return ret; | |
529 | } |