162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright 2018 Red Hat Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 1262306a36Sopenharmony_ci * all copies or substantial portions of the Software. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1562306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1662306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1762306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 1862306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 1962306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2062306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci#include "head.h" 2362306a36Sopenharmony_ci#include "base.h" 2462306a36Sopenharmony_ci#include "core.h" 2562306a36Sopenharmony_ci#include "curs.h" 2662306a36Sopenharmony_ci#include "ovly.h" 2762306a36Sopenharmony_ci#include "crc.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <nvif/class.h> 3062306a36Sopenharmony_ci#include <nvif/event.h> 3162306a36Sopenharmony_ci#include <nvif/cl0046.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <drm/drm_atomic.h> 3462306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 3562306a36Sopenharmony_ci#include <drm/drm_vblank.h> 3662306a36Sopenharmony_ci#include "nouveau_connector.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_civoid 3962306a36Sopenharmony_cinv50_head_flush_clr(struct nv50_head *head, 4062306a36Sopenharmony_ci struct nv50_head_atom *asyh, bool flush) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci union nv50_head_atom_mask clr = { 4362306a36Sopenharmony_ci .mask = asyh->clr.mask & ~(flush ? 0 : asyh->set.mask), 4462306a36Sopenharmony_ci }; 4562306a36Sopenharmony_ci if (clr.crc) nv50_crc_atomic_clr(head); 4662306a36Sopenharmony_ci if (clr.olut) head->func->olut_clr(head); 4762306a36Sopenharmony_ci if (clr.core) head->func->core_clr(head); 4862306a36Sopenharmony_ci if (clr.curs) head->func->curs_clr(head); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_civoid 5262306a36Sopenharmony_cinv50_head_flush_set_wndw(struct nv50_head *head, struct nv50_head_atom *asyh) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci if (asyh->set.curs ) head->func->curs_set(head, asyh); 5562306a36Sopenharmony_ci if (asyh->set.olut ) { 5662306a36Sopenharmony_ci asyh->olut.offset = nv50_lut_load(&head->olut, 5762306a36Sopenharmony_ci asyh->olut.buffer, 5862306a36Sopenharmony_ci asyh->state.gamma_lut, 5962306a36Sopenharmony_ci asyh->olut.load); 6062306a36Sopenharmony_ci head->func->olut_set(head, asyh); 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_civoid 6562306a36Sopenharmony_cinv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci if (asyh->set.view ) head->func->view (head, asyh); 6862306a36Sopenharmony_ci if (asyh->set.mode ) head->func->mode (head, asyh); 6962306a36Sopenharmony_ci if (asyh->set.core ) head->func->core_set(head, asyh); 7062306a36Sopenharmony_ci if (asyh->set.base ) head->func->base (head, asyh); 7162306a36Sopenharmony_ci if (asyh->set.ovly ) head->func->ovly (head, asyh); 7262306a36Sopenharmony_ci if (asyh->set.dither ) head->func->dither (head, asyh); 7362306a36Sopenharmony_ci if (asyh->set.procamp) head->func->procamp (head, asyh); 7462306a36Sopenharmony_ci if (asyh->set.crc ) nv50_crc_atomic_set (head, asyh); 7562306a36Sopenharmony_ci if (asyh->set.or ) head->func->or (head, asyh); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void 7962306a36Sopenharmony_cinv50_head_atomic_check_procamp(struct nv50_head_atom *armh, 8062306a36Sopenharmony_ci struct nv50_head_atom *asyh, 8162306a36Sopenharmony_ci struct nouveau_conn_atom *asyc) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci const int vib = asyc->procamp.color_vibrance - 100; 8462306a36Sopenharmony_ci const int hue = asyc->procamp.vibrant_hue - 90; 8562306a36Sopenharmony_ci const int adj = (vib > 0) ? 50 : 0; 8662306a36Sopenharmony_ci asyh->procamp.sat.cos = ((vib * 2047 + adj) / 100) & 0xfff; 8762306a36Sopenharmony_ci asyh->procamp.sat.sin = ((hue * 2047) / 100) & 0xfff; 8862306a36Sopenharmony_ci asyh->set.procamp = true; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void 9262306a36Sopenharmony_cinv50_head_atomic_check_dither(struct nv50_head_atom *armh, 9362306a36Sopenharmony_ci struct nv50_head_atom *asyh, 9462306a36Sopenharmony_ci struct nouveau_conn_atom *asyc) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci u32 mode = 0x00; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (asyc->dither.mode) { 9962306a36Sopenharmony_ci if (asyc->dither.mode == DITHERING_MODE_AUTO) { 10062306a36Sopenharmony_ci if (asyh->base.depth > asyh->or.bpc * 3) 10162306a36Sopenharmony_ci mode = DITHERING_MODE_DYNAMIC2X2; 10262306a36Sopenharmony_ci } else { 10362306a36Sopenharmony_ci mode = asyc->dither.mode; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (asyc->dither.depth == DITHERING_DEPTH_AUTO) { 10762306a36Sopenharmony_ci if (asyh->or.bpc >= 8) 10862306a36Sopenharmony_ci mode |= DITHERING_DEPTH_8BPC; 10962306a36Sopenharmony_ci } else { 11062306a36Sopenharmony_ci mode |= asyc->dither.depth; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci asyh->dither.enable = NVVAL_GET(mode, NV507D, HEAD_SET_DITHER_CONTROL, ENABLE); 11562306a36Sopenharmony_ci asyh->dither.bits = NVVAL_GET(mode, NV507D, HEAD_SET_DITHER_CONTROL, BITS); 11662306a36Sopenharmony_ci asyh->dither.mode = NVVAL_GET(mode, NV507D, HEAD_SET_DITHER_CONTROL, MODE); 11762306a36Sopenharmony_ci asyh->set.dither = true; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void 12162306a36Sopenharmony_cinv50_head_atomic_check_view(struct nv50_head_atom *armh, 12262306a36Sopenharmony_ci struct nv50_head_atom *asyh, 12362306a36Sopenharmony_ci struct nouveau_conn_atom *asyc) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct drm_connector *connector = asyc->state.connector; 12662306a36Sopenharmony_ci struct drm_display_mode *omode = &asyh->state.adjusted_mode; 12762306a36Sopenharmony_ci struct drm_display_mode *umode = &asyh->state.mode; 12862306a36Sopenharmony_ci int mode = asyc->scaler.mode; 12962306a36Sopenharmony_ci struct edid *edid; 13062306a36Sopenharmony_ci int umode_vdisplay, omode_hdisplay, omode_vdisplay; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (connector->edid_blob_ptr) 13362306a36Sopenharmony_ci edid = (struct edid *)connector->edid_blob_ptr->data; 13462306a36Sopenharmony_ci else 13562306a36Sopenharmony_ci edid = NULL; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (!asyc->scaler.full) { 13862306a36Sopenharmony_ci if (mode == DRM_MODE_SCALE_NONE) 13962306a36Sopenharmony_ci omode = umode; 14062306a36Sopenharmony_ci } else { 14162306a36Sopenharmony_ci /* Non-EDID LVDS/eDP mode. */ 14262306a36Sopenharmony_ci mode = DRM_MODE_SCALE_FULLSCREEN; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* For the user-specified mode, we must ignore doublescan and 14662306a36Sopenharmony_ci * the like, but honor frame packing. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci umode_vdisplay = umode->vdisplay; 14962306a36Sopenharmony_ci if ((umode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) 15062306a36Sopenharmony_ci umode_vdisplay += umode->vtotal; 15162306a36Sopenharmony_ci asyh->view.iW = umode->hdisplay; 15262306a36Sopenharmony_ci asyh->view.iH = umode_vdisplay; 15362306a36Sopenharmony_ci /* For the output mode, we can just use the stock helper. */ 15462306a36Sopenharmony_ci drm_mode_get_hv_timing(omode, &omode_hdisplay, &omode_vdisplay); 15562306a36Sopenharmony_ci asyh->view.oW = omode_hdisplay; 15662306a36Sopenharmony_ci asyh->view.oH = omode_vdisplay; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* Add overscan compensation if necessary, will keep the aspect 15962306a36Sopenharmony_ci * ratio the same as the backend mode unless overridden by the 16062306a36Sopenharmony_ci * user setting both hborder and vborder properties. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ci if ((asyc->scaler.underscan.mode == UNDERSCAN_ON || 16362306a36Sopenharmony_ci (asyc->scaler.underscan.mode == UNDERSCAN_AUTO && 16462306a36Sopenharmony_ci drm_detect_hdmi_monitor(edid)))) { 16562306a36Sopenharmony_ci u32 bX = asyc->scaler.underscan.hborder; 16662306a36Sopenharmony_ci u32 bY = asyc->scaler.underscan.vborder; 16762306a36Sopenharmony_ci u32 r = (asyh->view.oH << 19) / asyh->view.oW; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (bX) { 17062306a36Sopenharmony_ci asyh->view.oW -= (bX * 2); 17162306a36Sopenharmony_ci if (bY) asyh->view.oH -= (bY * 2); 17262306a36Sopenharmony_ci else asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19; 17362306a36Sopenharmony_ci } else { 17462306a36Sopenharmony_ci asyh->view.oW -= (asyh->view.oW >> 4) + 32; 17562306a36Sopenharmony_ci if (bY) asyh->view.oH -= (bY * 2); 17662306a36Sopenharmony_ci else asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* Handle CENTER/ASPECT scaling, taking into account the areas 18162306a36Sopenharmony_ci * removed already for overscan compensation. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci switch (mode) { 18462306a36Sopenharmony_ci case DRM_MODE_SCALE_CENTER: 18562306a36Sopenharmony_ci /* NOTE: This will cause scaling when the input is 18662306a36Sopenharmony_ci * larger than the output. 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_ci asyh->view.oW = min(asyh->view.iW, asyh->view.oW); 18962306a36Sopenharmony_ci asyh->view.oH = min(asyh->view.iH, asyh->view.oH); 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci case DRM_MODE_SCALE_ASPECT: 19262306a36Sopenharmony_ci /* Determine whether the scaling should be on width or on 19362306a36Sopenharmony_ci * height. This is done by comparing the aspect ratios of the 19462306a36Sopenharmony_ci * sizes. If the output AR is larger than input AR, that means 19562306a36Sopenharmony_ci * we want to change the width (letterboxed on the 19662306a36Sopenharmony_ci * left/right), otherwise on the height (letterboxed on the 19762306a36Sopenharmony_ci * top/bottom). 19862306a36Sopenharmony_ci * 19962306a36Sopenharmony_ci * E.g. 4:3 (1.333) AR image displayed on a 16:10 (1.6) AR 20062306a36Sopenharmony_ci * screen will have letterboxes on the left/right. However a 20162306a36Sopenharmony_ci * 16:9 (1.777) AR image on that same screen will have 20262306a36Sopenharmony_ci * letterboxes on the top/bottom. 20362306a36Sopenharmony_ci * 20462306a36Sopenharmony_ci * inputAR = iW / iH; outputAR = oW / oH 20562306a36Sopenharmony_ci * outputAR > inputAR is equivalent to oW * iH > iW * oH 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ci if (asyh->view.oW * asyh->view.iH > asyh->view.iW * asyh->view.oH) { 20862306a36Sopenharmony_ci /* Recompute output width, i.e. left/right letterbox */ 20962306a36Sopenharmony_ci u32 r = (asyh->view.iW << 19) / asyh->view.iH; 21062306a36Sopenharmony_ci asyh->view.oW = ((asyh->view.oH * r) + (r / 2)) >> 19; 21162306a36Sopenharmony_ci } else { 21262306a36Sopenharmony_ci /* Recompute output height, i.e. top/bottom letterbox */ 21362306a36Sopenharmony_ci u32 r = (asyh->view.iH << 19) / asyh->view.iW; 21462306a36Sopenharmony_ci asyh->view.oH = ((asyh->view.oW * r) + (r / 2)) >> 19; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci break; 21762306a36Sopenharmony_ci default: 21862306a36Sopenharmony_ci break; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci asyh->set.view = true; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int 22562306a36Sopenharmony_cinv50_head_atomic_check_lut(struct nv50_head *head, 22662306a36Sopenharmony_ci struct nv50_head_atom *asyh) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct drm_device *dev = head->base.base.dev; 22962306a36Sopenharmony_ci struct drm_crtc *crtc = &head->base.base; 23062306a36Sopenharmony_ci struct nv50_disp *disp = nv50_disp(dev); 23162306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 23262306a36Sopenharmony_ci struct drm_property_blob *olut = asyh->state.gamma_lut, 23362306a36Sopenharmony_ci *ilut = asyh->state.degamma_lut; 23462306a36Sopenharmony_ci int size; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* Ensure that the ilut is valid */ 23762306a36Sopenharmony_ci if (ilut) { 23862306a36Sopenharmony_ci size = drm_color_lut_size(ilut); 23962306a36Sopenharmony_ci if (!head->func->ilut_check(size)) { 24062306a36Sopenharmony_ci NV_ATOMIC(drm, "Invalid size %d for degamma on [CRTC:%d:%s]\n", 24162306a36Sopenharmony_ci size, crtc->base.id, crtc->name); 24262306a36Sopenharmony_ci return -EINVAL; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Determine whether core output LUT should be enabled. */ 24762306a36Sopenharmony_ci if (olut) { 24862306a36Sopenharmony_ci /* Check if any window(s) have stolen the core output LUT 24962306a36Sopenharmony_ci * to as an input LUT for legacy gamma + I8 colour format. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci if (asyh->wndw.olut) { 25262306a36Sopenharmony_ci /* If any window has stolen the core output LUT, 25362306a36Sopenharmony_ci * all of them must. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci if (asyh->wndw.olut != asyh->wndw.mask) 25662306a36Sopenharmony_ci return -EINVAL; 25762306a36Sopenharmony_ci olut = NULL; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (!olut) { 26262306a36Sopenharmony_ci if (!head->func->olut_identity) { 26362306a36Sopenharmony_ci asyh->olut.handle = 0; 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci size = 0; 26762306a36Sopenharmony_ci } else { 26862306a36Sopenharmony_ci size = drm_color_lut_size(olut); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (!head->func->olut(head, asyh, size)) { 27262306a36Sopenharmony_ci NV_ATOMIC(drm, "Invalid size %d for gamma on [CRTC:%d:%s]\n", 27362306a36Sopenharmony_ci size, crtc->base.id, crtc->name); 27462306a36Sopenharmony_ci return -EINVAL; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci asyh->olut.handle = disp->core->chan.vram.handle; 27762306a36Sopenharmony_ci asyh->olut.buffer = !asyh->olut.buffer; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return 0; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic void 28362306a36Sopenharmony_cinv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct drm_display_mode *mode = &asyh->state.adjusted_mode; 28662306a36Sopenharmony_ci struct nv50_head_mode *m = &asyh->mode; 28762306a36Sopenharmony_ci u32 blankus; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * DRM modes are defined in terms of a repeating interval 29362306a36Sopenharmony_ci * starting with the active display area. The hardware modes 29462306a36Sopenharmony_ci * are defined in terms of a repeating interval starting one 29562306a36Sopenharmony_ci * unit (pixel or line) into the sync pulse. So, add bias. 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci m->h.active = mode->crtc_htotal; 29962306a36Sopenharmony_ci m->h.synce = mode->crtc_hsync_end - mode->crtc_hsync_start - 1; 30062306a36Sopenharmony_ci m->h.blanke = mode->crtc_hblank_end - mode->crtc_hsync_start - 1; 30162306a36Sopenharmony_ci m->h.blanks = m->h.blanke + mode->crtc_hdisplay; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci m->v.active = mode->crtc_vtotal; 30462306a36Sopenharmony_ci m->v.synce = mode->crtc_vsync_end - mode->crtc_vsync_start - 1; 30562306a36Sopenharmony_ci m->v.blanke = mode->crtc_vblank_end - mode->crtc_vsync_start - 1; 30662306a36Sopenharmony_ci m->v.blanks = m->v.blanke + mode->crtc_vdisplay; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /*XXX: Safe underestimate, even "0" works */ 30962306a36Sopenharmony_ci blankus = (m->v.active - mode->crtc_vdisplay - 2) * m->h.active; 31062306a36Sopenharmony_ci blankus *= 1000; 31162306a36Sopenharmony_ci blankus /= mode->crtc_clock; 31262306a36Sopenharmony_ci m->v.blankus = blankus; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_INTERLACE) { 31562306a36Sopenharmony_ci m->v.blank2e = m->v.active + m->v.blanke; 31662306a36Sopenharmony_ci m->v.blank2s = m->v.blank2e + mode->crtc_vdisplay; 31762306a36Sopenharmony_ci m->v.active = (m->v.active * 2) + 1; 31862306a36Sopenharmony_ci m->interlace = true; 31962306a36Sopenharmony_ci } else { 32062306a36Sopenharmony_ci m->v.blank2e = 0; 32162306a36Sopenharmony_ci m->v.blank2s = 1; 32262306a36Sopenharmony_ci m->interlace = false; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci m->clock = mode->crtc_clock; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci asyh->or.nhsync = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); 32762306a36Sopenharmony_ci asyh->or.nvsync = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); 32862306a36Sopenharmony_ci asyh->set.or = head->func->or != NULL; 32962306a36Sopenharmony_ci asyh->set.mode = true; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int 33362306a36Sopenharmony_cinv50_head_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, 33662306a36Sopenharmony_ci crtc); 33762306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, 33862306a36Sopenharmony_ci crtc); 33962306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(crtc->dev); 34062306a36Sopenharmony_ci struct nv50_head *head = nv50_head(crtc); 34162306a36Sopenharmony_ci struct nv50_head_atom *armh = nv50_head_atom(old_crtc_state); 34262306a36Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); 34362306a36Sopenharmony_ci struct nouveau_conn_atom *asyc = NULL; 34462306a36Sopenharmony_ci struct drm_connector_state *conns; 34562306a36Sopenharmony_ci struct drm_connector *conn; 34662306a36Sopenharmony_ci int i, ret; 34762306a36Sopenharmony_ci bool check_lut = asyh->state.color_mgmt_changed || 34862306a36Sopenharmony_ci memcmp(&armh->wndw, &asyh->wndw, sizeof(asyh->wndw)); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci NV_ATOMIC(drm, "%s atomic_check %d\n", crtc->name, asyh->state.active); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (check_lut) { 35362306a36Sopenharmony_ci ret = nv50_head_atomic_check_lut(head, asyh); 35462306a36Sopenharmony_ci if (ret) 35562306a36Sopenharmony_ci return ret; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (asyh->state.active) { 35962306a36Sopenharmony_ci for_each_new_connector_in_state(asyh->state.state, conn, conns, i) { 36062306a36Sopenharmony_ci if (conns->crtc == crtc) { 36162306a36Sopenharmony_ci asyc = nouveau_conn_atom(conns); 36262306a36Sopenharmony_ci break; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (armh->state.active) { 36762306a36Sopenharmony_ci if (asyc) { 36862306a36Sopenharmony_ci if (asyh->state.mode_changed) 36962306a36Sopenharmony_ci asyc->set.scaler = true; 37062306a36Sopenharmony_ci if (armh->base.depth != asyh->base.depth) 37162306a36Sopenharmony_ci asyc->set.dither = true; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci } else { 37462306a36Sopenharmony_ci if (asyc) 37562306a36Sopenharmony_ci asyc->set.mask = ~0; 37662306a36Sopenharmony_ci asyh->set.mask = ~0; 37762306a36Sopenharmony_ci asyh->set.or = head->func->or != NULL; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (asyh->state.mode_changed || asyh->state.connectors_changed) 38162306a36Sopenharmony_ci nv50_head_atomic_check_mode(head, asyh); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (check_lut) 38462306a36Sopenharmony_ci asyh->olut.visible = asyh->olut.handle != 0; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (asyc) { 38762306a36Sopenharmony_ci if (asyc->set.scaler) 38862306a36Sopenharmony_ci nv50_head_atomic_check_view(armh, asyh, asyc); 38962306a36Sopenharmony_ci if (asyc->set.dither) 39062306a36Sopenharmony_ci nv50_head_atomic_check_dither(armh, asyh, asyc); 39162306a36Sopenharmony_ci if (asyc->set.procamp) 39262306a36Sopenharmony_ci nv50_head_atomic_check_procamp(armh, asyh, asyc); 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (head->func->core_calc) { 39662306a36Sopenharmony_ci head->func->core_calc(head, asyh); 39762306a36Sopenharmony_ci if (!asyh->core.visible) 39862306a36Sopenharmony_ci asyh->olut.visible = false; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci asyh->set.base = armh->base.cpp != asyh->base.cpp; 40262306a36Sopenharmony_ci asyh->set.ovly = armh->ovly.cpp != asyh->ovly.cpp; 40362306a36Sopenharmony_ci } else { 40462306a36Sopenharmony_ci asyh->olut.visible = false; 40562306a36Sopenharmony_ci asyh->core.visible = false; 40662306a36Sopenharmony_ci asyh->curs.visible = false; 40762306a36Sopenharmony_ci asyh->base.cpp = 0; 40862306a36Sopenharmony_ci asyh->ovly.cpp = 0; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (!drm_atomic_crtc_needs_modeset(&asyh->state)) { 41262306a36Sopenharmony_ci if (asyh->core.visible) { 41362306a36Sopenharmony_ci if (memcmp(&armh->core, &asyh->core, sizeof(asyh->core))) 41462306a36Sopenharmony_ci asyh->set.core = true; 41562306a36Sopenharmony_ci } else 41662306a36Sopenharmony_ci if (armh->core.visible) { 41762306a36Sopenharmony_ci asyh->clr.core = true; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (asyh->curs.visible) { 42162306a36Sopenharmony_ci if (memcmp(&armh->curs, &asyh->curs, sizeof(asyh->curs))) 42262306a36Sopenharmony_ci asyh->set.curs = true; 42362306a36Sopenharmony_ci } else 42462306a36Sopenharmony_ci if (armh->curs.visible) { 42562306a36Sopenharmony_ci asyh->clr.curs = true; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (asyh->olut.visible) { 42962306a36Sopenharmony_ci if (memcmp(&armh->olut, &asyh->olut, sizeof(asyh->olut))) 43062306a36Sopenharmony_ci asyh->set.olut = true; 43162306a36Sopenharmony_ci } else 43262306a36Sopenharmony_ci if (armh->olut.visible) { 43362306a36Sopenharmony_ci asyh->clr.olut = true; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci } else { 43662306a36Sopenharmony_ci asyh->clr.olut = armh->olut.visible; 43762306a36Sopenharmony_ci asyh->clr.core = armh->core.visible; 43862306a36Sopenharmony_ci asyh->clr.curs = armh->curs.visible; 43962306a36Sopenharmony_ci asyh->set.olut = asyh->olut.visible; 44062306a36Sopenharmony_ci asyh->set.core = asyh->core.visible; 44162306a36Sopenharmony_ci asyh->set.curs = asyh->curs.visible; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci ret = nv50_crc_atomic_check_head(head, asyh, armh); 44562306a36Sopenharmony_ci if (ret) 44662306a36Sopenharmony_ci return ret; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (asyh->clr.mask || asyh->set.mask) 44962306a36Sopenharmony_ci nv50_atom(asyh->state.state)->lock_core = true; 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs 45462306a36Sopenharmony_cinv50_head_help = { 45562306a36Sopenharmony_ci .atomic_check = nv50_head_atomic_check, 45662306a36Sopenharmony_ci .get_scanout_position = nouveau_display_scanoutpos, 45762306a36Sopenharmony_ci}; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic void 46062306a36Sopenharmony_cinv50_head_atomic_destroy_state(struct drm_crtc *crtc, 46162306a36Sopenharmony_ci struct drm_crtc_state *state) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(state); 46462306a36Sopenharmony_ci __drm_atomic_helper_crtc_destroy_state(&asyh->state); 46562306a36Sopenharmony_ci kfree(asyh); 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic struct drm_crtc_state * 46962306a36Sopenharmony_cinv50_head_atomic_duplicate_state(struct drm_crtc *crtc) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct nv50_head_atom *armh = nv50_head_atom(crtc->state); 47262306a36Sopenharmony_ci struct nv50_head_atom *asyh; 47362306a36Sopenharmony_ci if (!(asyh = kmalloc(sizeof(*asyh), GFP_KERNEL))) 47462306a36Sopenharmony_ci return NULL; 47562306a36Sopenharmony_ci __drm_atomic_helper_crtc_duplicate_state(crtc, &asyh->state); 47662306a36Sopenharmony_ci asyh->wndw = armh->wndw; 47762306a36Sopenharmony_ci asyh->view = armh->view; 47862306a36Sopenharmony_ci asyh->mode = armh->mode; 47962306a36Sopenharmony_ci asyh->olut = armh->olut; 48062306a36Sopenharmony_ci asyh->core = armh->core; 48162306a36Sopenharmony_ci asyh->curs = armh->curs; 48262306a36Sopenharmony_ci asyh->base = armh->base; 48362306a36Sopenharmony_ci asyh->ovly = armh->ovly; 48462306a36Sopenharmony_ci asyh->dither = armh->dither; 48562306a36Sopenharmony_ci asyh->procamp = armh->procamp; 48662306a36Sopenharmony_ci asyh->crc = armh->crc; 48762306a36Sopenharmony_ci asyh->or = armh->or; 48862306a36Sopenharmony_ci asyh->dp = armh->dp; 48962306a36Sopenharmony_ci asyh->clr.mask = 0; 49062306a36Sopenharmony_ci asyh->set.mask = 0; 49162306a36Sopenharmony_ci return &asyh->state; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic void 49562306a36Sopenharmony_cinv50_head_reset(struct drm_crtc *crtc) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct nv50_head_atom *asyh; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (WARN_ON(!(asyh = kzalloc(sizeof(*asyh), GFP_KERNEL)))) 50062306a36Sopenharmony_ci return; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (crtc->state) 50362306a36Sopenharmony_ci nv50_head_atomic_destroy_state(crtc, crtc->state); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, &asyh->state); 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic int 50962306a36Sopenharmony_cinv50_head_late_register(struct drm_crtc *crtc) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci return nv50_head_crc_late_register(nv50_head(crtc)); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic void 51562306a36Sopenharmony_cinv50_head_destroy(struct drm_crtc *crtc) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct nv50_head *head = nv50_head(crtc); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci nvif_event_dtor(&head->base.vblank); 52062306a36Sopenharmony_ci nvif_head_dtor(&head->base.head); 52162306a36Sopenharmony_ci nv50_lut_fini(&head->olut); 52262306a36Sopenharmony_ci drm_crtc_cleanup(crtc); 52362306a36Sopenharmony_ci kfree(head); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic const struct drm_crtc_funcs 52762306a36Sopenharmony_cinv50_head_func = { 52862306a36Sopenharmony_ci .reset = nv50_head_reset, 52962306a36Sopenharmony_ci .destroy = nv50_head_destroy, 53062306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 53162306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 53262306a36Sopenharmony_ci .atomic_duplicate_state = nv50_head_atomic_duplicate_state, 53362306a36Sopenharmony_ci .atomic_destroy_state = nv50_head_atomic_destroy_state, 53462306a36Sopenharmony_ci .enable_vblank = nouveau_display_vblank_enable, 53562306a36Sopenharmony_ci .disable_vblank = nouveau_display_vblank_disable, 53662306a36Sopenharmony_ci .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, 53762306a36Sopenharmony_ci .late_register = nv50_head_late_register, 53862306a36Sopenharmony_ci}; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic const struct drm_crtc_funcs 54162306a36Sopenharmony_cinvd9_head_func = { 54262306a36Sopenharmony_ci .reset = nv50_head_reset, 54362306a36Sopenharmony_ci .destroy = nv50_head_destroy, 54462306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 54562306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 54662306a36Sopenharmony_ci .atomic_duplicate_state = nv50_head_atomic_duplicate_state, 54762306a36Sopenharmony_ci .atomic_destroy_state = nv50_head_atomic_destroy_state, 54862306a36Sopenharmony_ci .enable_vblank = nouveau_display_vblank_enable, 54962306a36Sopenharmony_ci .disable_vblank = nouveau_display_vblank_disable, 55062306a36Sopenharmony_ci .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, 55162306a36Sopenharmony_ci .verify_crc_source = nv50_crc_verify_source, 55262306a36Sopenharmony_ci .get_crc_sources = nv50_crc_get_sources, 55362306a36Sopenharmony_ci .set_crc_source = nv50_crc_set_source, 55462306a36Sopenharmony_ci .late_register = nv50_head_late_register, 55562306a36Sopenharmony_ci}; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic int 55862306a36Sopenharmony_cinv50_head_vblank_handler(struct nvif_event *event, void *repv, u32 repc) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci struct nouveau_crtc *nv_crtc = container_of(event, struct nouveau_crtc, vblank); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (drm_crtc_handle_vblank(&nv_crtc->base)) 56362306a36Sopenharmony_ci nv50_crc_handle_vblank(nv50_head(&nv_crtc->base)); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return NVIF_EVENT_KEEP; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistruct nv50_head * 56962306a36Sopenharmony_cinv50_head_create(struct drm_device *dev, int index) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 57262306a36Sopenharmony_ci struct nv50_disp *disp = nv50_disp(dev); 57362306a36Sopenharmony_ci struct nv50_head *head; 57462306a36Sopenharmony_ci struct nv50_wndw *base, *ovly, *curs; 57562306a36Sopenharmony_ci struct nouveau_crtc *nv_crtc; 57662306a36Sopenharmony_ci struct drm_crtc *crtc; 57762306a36Sopenharmony_ci const struct drm_crtc_funcs *funcs; 57862306a36Sopenharmony_ci int ret; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci head = kzalloc(sizeof(*head), GFP_KERNEL); 58162306a36Sopenharmony_ci if (!head) 58262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci head->func = disp->core->func->head; 58562306a36Sopenharmony_ci head->base.index = index; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (disp->disp->object.oclass < GF110_DISP) 58862306a36Sopenharmony_ci funcs = &nv50_head_func; 58962306a36Sopenharmony_ci else 59062306a36Sopenharmony_ci funcs = &nvd9_head_func; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (disp->disp->object.oclass < GV100_DISP) { 59362306a36Sopenharmony_ci ret = nv50_base_new(drm, head->base.index, &base); 59462306a36Sopenharmony_ci ret = nv50_ovly_new(drm, head->base.index, &ovly); 59562306a36Sopenharmony_ci } else { 59662306a36Sopenharmony_ci ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_PRIMARY, 59762306a36Sopenharmony_ci head->base.index * 2 + 0, &base); 59862306a36Sopenharmony_ci ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_OVERLAY, 59962306a36Sopenharmony_ci head->base.index * 2 + 1, &ovly); 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci if (ret == 0) 60262306a36Sopenharmony_ci ret = nv50_curs_new(drm, head->base.index, &curs); 60362306a36Sopenharmony_ci if (ret) { 60462306a36Sopenharmony_ci kfree(head); 60562306a36Sopenharmony_ci return ERR_PTR(ret); 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci nv_crtc = &head->base; 60962306a36Sopenharmony_ci crtc = &nv_crtc->base; 61062306a36Sopenharmony_ci drm_crtc_init_with_planes(dev, crtc, &base->plane, &curs->plane, 61162306a36Sopenharmony_ci funcs, "head-%d", head->base.index); 61262306a36Sopenharmony_ci drm_crtc_helper_add(crtc, &nv50_head_help); 61362306a36Sopenharmony_ci /* Keep the legacy gamma size at 256 to avoid compatibility issues */ 61462306a36Sopenharmony_ci drm_mode_crtc_set_gamma_size(crtc, 256); 61562306a36Sopenharmony_ci drm_crtc_enable_color_mgmt(crtc, base->func->ilut_size, 61662306a36Sopenharmony_ci disp->disp->object.oclass >= GF110_DISP, 61762306a36Sopenharmony_ci head->func->olut_size); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (head->func->olut_set) { 62062306a36Sopenharmony_ci ret = nv50_lut_init(disp, &drm->client.mmu, &head->olut); 62162306a36Sopenharmony_ci if (ret) { 62262306a36Sopenharmony_ci nv50_head_destroy(crtc); 62362306a36Sopenharmony_ci return ERR_PTR(ret); 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci ret = nvif_head_ctor(disp->disp, head->base.base.name, head->base.index, &head->base.head); 62862306a36Sopenharmony_ci if (ret) 62962306a36Sopenharmony_ci return ERR_PTR(ret); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci ret = nvif_head_vblank_event_ctor(&head->base.head, "kmsVbl", nv50_head_vblank_handler, 63262306a36Sopenharmony_ci false, &nv_crtc->vblank); 63362306a36Sopenharmony_ci if (ret) 63462306a36Sopenharmony_ci return ERR_PTR(ret); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci return head; 63762306a36Sopenharmony_ci} 638