cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

head.c (19178B)


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