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

wndw.c (22981B)


      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 "wndw.h"
     23#include "wimm.h"
     24#include "handles.h"
     25
     26#include <nvif/class.h>
     27#include <nvif/cl0002.h>
     28
     29#include <nvhw/class/cl507c.h>
     30#include <nvhw/class/cl507e.h>
     31#include <nvhw/class/clc37e.h>
     32
     33#include <drm/drm_atomic.h>
     34#include <drm/drm_atomic_helper.h>
     35#include <drm/drm_gem_atomic_helper.h>
     36#include <drm/drm_fourcc.h>
     37
     38#include "nouveau_bo.h"
     39#include "nouveau_gem.h"
     40
     41static void
     42nv50_wndw_ctxdma_del(struct nv50_wndw_ctxdma *ctxdma)
     43{
     44	nvif_object_dtor(&ctxdma->object);
     45	list_del(&ctxdma->head);
     46	kfree(ctxdma);
     47}
     48
     49static struct nv50_wndw_ctxdma *
     50nv50_wndw_ctxdma_new(struct nv50_wndw *wndw, struct drm_framebuffer *fb)
     51{
     52	struct nouveau_drm *drm = nouveau_drm(fb->dev);
     53	struct nv50_wndw_ctxdma *ctxdma;
     54	u32 handle;
     55	u32 unused;
     56	u8  kind;
     57	struct {
     58		struct nv_dma_v0 base;
     59		union {
     60			struct nv50_dma_v0 nv50;
     61			struct gf100_dma_v0 gf100;
     62			struct gf119_dma_v0 gf119;
     63		};
     64	} args = {};
     65	u32 argc = sizeof(args.base);
     66	int ret;
     67
     68	nouveau_framebuffer_get_layout(fb, &unused, &kind);
     69	handle = NV50_DISP_HANDLE_WNDW_CTX(kind);
     70
     71	list_for_each_entry(ctxdma, &wndw->ctxdma.list, head) {
     72		if (ctxdma->object.handle == handle)
     73			return ctxdma;
     74	}
     75
     76	if (!(ctxdma = kzalloc(sizeof(*ctxdma), GFP_KERNEL)))
     77		return ERR_PTR(-ENOMEM);
     78	list_add(&ctxdma->head, &wndw->ctxdma.list);
     79
     80	args.base.target = NV_DMA_V0_TARGET_VRAM;
     81	args.base.access = NV_DMA_V0_ACCESS_RDWR;
     82	args.base.start  = 0;
     83	args.base.limit  = drm->client.device.info.ram_user - 1;
     84
     85	if (drm->client.device.info.chipset < 0x80) {
     86		args.nv50.part = NV50_DMA_V0_PART_256;
     87		argc += sizeof(args.nv50);
     88	} else
     89	if (drm->client.device.info.chipset < 0xc0) {
     90		args.nv50.part = NV50_DMA_V0_PART_256;
     91		args.nv50.kind = kind;
     92		argc += sizeof(args.nv50);
     93	} else
     94	if (drm->client.device.info.chipset < 0xd0) {
     95		args.gf100.kind = kind;
     96		argc += sizeof(args.gf100);
     97	} else {
     98		args.gf119.page = GF119_DMA_V0_PAGE_LP;
     99		args.gf119.kind = kind;
    100		argc += sizeof(args.gf119);
    101	}
    102
    103	ret = nvif_object_ctor(wndw->ctxdma.parent, "kmsFbCtxDma", handle,
    104			       NV_DMA_IN_MEMORY, &args, argc, &ctxdma->object);
    105	if (ret) {
    106		nv50_wndw_ctxdma_del(ctxdma);
    107		return ERR_PTR(ret);
    108	}
    109
    110	return ctxdma;
    111}
    112
    113int
    114nv50_wndw_wait_armed(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
    115{
    116	struct nv50_disp *disp = nv50_disp(wndw->plane.dev);
    117	if (asyw->set.ntfy) {
    118		return wndw->func->ntfy_wait_begun(disp->sync,
    119						   asyw->ntfy.offset,
    120						   wndw->wndw.base.device);
    121	}
    122	return 0;
    123}
    124
    125void
    126nv50_wndw_flush_clr(struct nv50_wndw *wndw, u32 *interlock, bool flush,
    127		    struct nv50_wndw_atom *asyw)
    128{
    129	union nv50_wndw_atom_mask clr = {
    130		.mask = asyw->clr.mask & ~(flush ? 0 : asyw->set.mask),
    131	};
    132	if (clr.sema ) wndw->func-> sema_clr(wndw);
    133	if (clr.ntfy ) wndw->func-> ntfy_clr(wndw);
    134	if (clr.xlut ) wndw->func-> xlut_clr(wndw);
    135	if (clr.csc  ) wndw->func->  csc_clr(wndw);
    136	if (clr.image) wndw->func->image_clr(wndw);
    137
    138	interlock[wndw->interlock.type] |= wndw->interlock.data;
    139}
    140
    141void
    142nv50_wndw_flush_set(struct nv50_wndw *wndw, u32 *interlock,
    143		    struct nv50_wndw_atom *asyw)
    144{
    145	if (interlock[NV50_DISP_INTERLOCK_CORE]) {
    146		asyw->image.mode = NV507C_SET_PRESENT_CONTROL_BEGIN_MODE_NON_TEARING;
    147		asyw->image.interval = 1;
    148	}
    149
    150	if (asyw->set.sema ) wndw->func->sema_set (wndw, asyw);
    151	if (asyw->set.ntfy ) wndw->func->ntfy_set (wndw, asyw);
    152	if (asyw->set.image) wndw->func->image_set(wndw, asyw);
    153
    154	if (asyw->set.xlut ) {
    155		if (asyw->ilut) {
    156			asyw->xlut.i.offset =
    157				nv50_lut_load(&wndw->ilut, asyw->xlut.i.buffer,
    158					      asyw->ilut, asyw->xlut.i.load);
    159		}
    160		wndw->func->xlut_set(wndw, asyw);
    161	}
    162
    163	if (asyw->set.csc  ) wndw->func->csc_set  (wndw, asyw);
    164	if (asyw->set.scale) wndw->func->scale_set(wndw, asyw);
    165	if (asyw->set.blend) wndw->func->blend_set(wndw, asyw);
    166	if (asyw->set.point) {
    167		if (asyw->set.point = false, asyw->set.mask)
    168			interlock[wndw->interlock.type] |= wndw->interlock.data;
    169		interlock[NV50_DISP_INTERLOCK_WIMM] |= wndw->interlock.wimm;
    170
    171		wndw->immd->point(wndw, asyw);
    172		wndw->immd->update(wndw, interlock);
    173	} else {
    174		interlock[wndw->interlock.type] |= wndw->interlock.data;
    175	}
    176}
    177
    178void
    179nv50_wndw_ntfy_enable(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
    180{
    181	struct nv50_disp *disp = nv50_disp(wndw->plane.dev);
    182
    183	asyw->ntfy.handle = wndw->wndw.sync.handle;
    184	asyw->ntfy.offset = wndw->ntfy;
    185	asyw->ntfy.awaken = false;
    186	asyw->set.ntfy = true;
    187
    188	wndw->func->ntfy_reset(disp->sync, wndw->ntfy);
    189	wndw->ntfy ^= 0x10;
    190}
    191
    192static void
    193nv50_wndw_atomic_check_release(struct nv50_wndw *wndw,
    194			       struct nv50_wndw_atom *asyw,
    195			       struct nv50_head_atom *asyh)
    196{
    197	struct nouveau_drm *drm = nouveau_drm(wndw->plane.dev);
    198	NV_ATOMIC(drm, "%s release\n", wndw->plane.name);
    199	wndw->func->release(wndw, asyw, asyh);
    200	asyw->ntfy.handle = 0;
    201	asyw->sema.handle = 0;
    202	asyw->xlut.handle = 0;
    203	memset(asyw->image.handle, 0x00, sizeof(asyw->image.handle));
    204}
    205
    206static int
    207nv50_wndw_atomic_check_acquire_yuv(struct nv50_wndw_atom *asyw)
    208{
    209	switch (asyw->state.fb->format->format) {
    210	case DRM_FORMAT_YUYV:
    211		asyw->image.format = NV507E_SURFACE_SET_PARAMS_FORMAT_VE8YO8UE8YE8;
    212		break;
    213	case DRM_FORMAT_UYVY:
    214		asyw->image.format = NV507E_SURFACE_SET_PARAMS_FORMAT_YO8VE8YE8UE8;
    215		break;
    216	default:
    217		WARN_ON(1);
    218		return -EINVAL;
    219	}
    220
    221	asyw->image.colorspace = NV507E_SURFACE_SET_PARAMS_COLOR_SPACE_YUV_601;
    222	return 0;
    223}
    224
    225static int
    226nv50_wndw_atomic_check_acquire_rgb(struct nv50_wndw_atom *asyw)
    227{
    228	switch (asyw->state.fb->format->format) {
    229	case DRM_FORMAT_C8:
    230		asyw->image.format = NV507C_SURFACE_SET_PARAMS_FORMAT_I8;
    231		break;
    232	case DRM_FORMAT_XRGB8888:
    233	case DRM_FORMAT_ARGB8888:
    234		asyw->image.format = NV507C_SURFACE_SET_PARAMS_FORMAT_A8R8G8B8;
    235		break;
    236	case DRM_FORMAT_RGB565:
    237		asyw->image.format = NV507C_SURFACE_SET_PARAMS_FORMAT_R5G6B5;
    238		break;
    239	case DRM_FORMAT_XRGB1555:
    240	case DRM_FORMAT_ARGB1555:
    241		asyw->image.format = NV507C_SURFACE_SET_PARAMS_FORMAT_A1R5G5B5;
    242		break;
    243	case DRM_FORMAT_XBGR2101010:
    244	case DRM_FORMAT_ABGR2101010:
    245		asyw->image.format = NV507C_SURFACE_SET_PARAMS_FORMAT_A2B10G10R10;
    246		break;
    247	case DRM_FORMAT_XBGR8888:
    248	case DRM_FORMAT_ABGR8888:
    249		asyw->image.format = NV507C_SURFACE_SET_PARAMS_FORMAT_A8B8G8R8;
    250		break;
    251	case DRM_FORMAT_XRGB2101010:
    252	case DRM_FORMAT_ARGB2101010:
    253		asyw->image.format = NVC37E_SET_PARAMS_FORMAT_A2R10G10B10;
    254		break;
    255	case DRM_FORMAT_XBGR16161616F:
    256	case DRM_FORMAT_ABGR16161616F:
    257		asyw->image.format = NV507C_SURFACE_SET_PARAMS_FORMAT_RF16_GF16_BF16_AF16;
    258		break;
    259	default:
    260		return -EINVAL;
    261	}
    262
    263	asyw->image.colorspace = NV507E_SURFACE_SET_PARAMS_COLOR_SPACE_RGB;
    264	return 0;
    265}
    266
    267static int
    268nv50_wndw_atomic_check_acquire(struct nv50_wndw *wndw, bool modeset,
    269			       struct nv50_wndw_atom *armw,
    270			       struct nv50_wndw_atom *asyw,
    271			       struct nv50_head_atom *asyh)
    272{
    273	struct drm_framebuffer *fb = asyw->state.fb;
    274	struct nouveau_drm *drm = nouveau_drm(wndw->plane.dev);
    275	uint8_t kind;
    276	uint32_t tile_mode;
    277	int ret;
    278
    279	NV_ATOMIC(drm, "%s acquire\n", wndw->plane.name);
    280
    281	if (fb != armw->state.fb || !armw->visible || modeset) {
    282		nouveau_framebuffer_get_layout(fb, &tile_mode, &kind);
    283
    284		asyw->image.w = fb->width;
    285		asyw->image.h = fb->height;
    286		asyw->image.kind = kind;
    287
    288		ret = nv50_wndw_atomic_check_acquire_rgb(asyw);
    289		if (ret) {
    290			ret = nv50_wndw_atomic_check_acquire_yuv(asyw);
    291			if (ret)
    292				return ret;
    293		}
    294
    295		if (asyw->image.kind) {
    296			asyw->image.layout = NV507C_SURFACE_SET_STORAGE_MEMORY_LAYOUT_BLOCKLINEAR;
    297			if (drm->client.device.info.chipset >= 0xc0)
    298				asyw->image.blockh = tile_mode >> 4;
    299			else
    300				asyw->image.blockh = tile_mode;
    301			asyw->image.blocks[0] = fb->pitches[0] / 64;
    302			asyw->image.pitch[0] = 0;
    303		} else {
    304			asyw->image.layout = NV507C_SURFACE_SET_STORAGE_MEMORY_LAYOUT_PITCH;
    305			asyw->image.blockh = NV507C_SURFACE_SET_STORAGE_BLOCK_HEIGHT_ONE_GOB;
    306			asyw->image.blocks[0] = 0;
    307			asyw->image.pitch[0] = fb->pitches[0];
    308		}
    309
    310		if (!asyh->state.async_flip)
    311			asyw->image.interval = 1;
    312		else
    313			asyw->image.interval = 0;
    314
    315		if (asyw->image.interval)
    316			asyw->image.mode = NV507C_SET_PRESENT_CONTROL_BEGIN_MODE_NON_TEARING;
    317		else
    318			asyw->image.mode = NV507C_SET_PRESENT_CONTROL_BEGIN_MODE_IMMEDIATE;
    319
    320		asyw->set.image = wndw->func->image_set != NULL;
    321	}
    322
    323	if (wndw->func->scale_set) {
    324		asyw->scale.sx = asyw->state.src_x >> 16;
    325		asyw->scale.sy = asyw->state.src_y >> 16;
    326		asyw->scale.sw = asyw->state.src_w >> 16;
    327		asyw->scale.sh = asyw->state.src_h >> 16;
    328		asyw->scale.dw = asyw->state.crtc_w;
    329		asyw->scale.dh = asyw->state.crtc_h;
    330		if (memcmp(&armw->scale, &asyw->scale, sizeof(asyw->scale)))
    331			asyw->set.scale = true;
    332	}
    333
    334	if (wndw->func->blend_set) {
    335		asyw->blend.depth = 255 - asyw->state.normalized_zpos;
    336		asyw->blend.k1 = asyw->state.alpha >> 8;
    337		switch (asyw->state.pixel_blend_mode) {
    338		case DRM_MODE_BLEND_PREMULTI:
    339			asyw->blend.src_color = NVC37E_SET_COMPOSITION_FACTOR_SELECT_SRC_COLOR_FACTOR_MATCH_SELECT_K1;
    340			asyw->blend.dst_color = NVC37E_SET_COMPOSITION_FACTOR_SELECT_DST_COLOR_FACTOR_MATCH_SELECT_NEG_K1_TIMES_SRC;
    341			break;
    342		case DRM_MODE_BLEND_COVERAGE:
    343			asyw->blend.src_color = NVC37E_SET_COMPOSITION_FACTOR_SELECT_SRC_COLOR_FACTOR_MATCH_SELECT_K1_TIMES_SRC;
    344			asyw->blend.dst_color = NVC37E_SET_COMPOSITION_FACTOR_SELECT_DST_COLOR_FACTOR_MATCH_SELECT_NEG_K1_TIMES_SRC;
    345			break;
    346		case DRM_MODE_BLEND_PIXEL_NONE:
    347		default:
    348			asyw->blend.src_color = NVC37E_SET_COMPOSITION_FACTOR_SELECT_SRC_COLOR_FACTOR_MATCH_SELECT_K1;
    349			asyw->blend.dst_color = NVC37E_SET_COMPOSITION_FACTOR_SELECT_DST_COLOR_FACTOR_MATCH_SELECT_NEG_K1;
    350			break;
    351		}
    352		if (memcmp(&armw->blend, &asyw->blend, sizeof(asyw->blend)))
    353			asyw->set.blend = true;
    354	}
    355
    356	if (wndw->immd) {
    357		asyw->point.x = asyw->state.crtc_x;
    358		asyw->point.y = asyw->state.crtc_y;
    359		if (memcmp(&armw->point, &asyw->point, sizeof(asyw->point)))
    360			asyw->set.point = true;
    361	}
    362
    363	return wndw->func->acquire(wndw, asyw, asyh);
    364}
    365
    366static int
    367nv50_wndw_atomic_check_lut(struct nv50_wndw *wndw,
    368			   struct nv50_wndw_atom *armw,
    369			   struct nv50_wndw_atom *asyw,
    370			   struct nv50_head_atom *asyh)
    371{
    372	struct drm_property_blob *ilut = asyh->state.degamma_lut;
    373
    374	/* I8 format without an input LUT makes no sense, and the
    375	 * HW error-checks for this.
    376	 *
    377	 * In order to handle legacy gamma, when there's no input
    378	 * LUT we need to steal the output LUT and use it instead.
    379	 */
    380	if (!ilut && asyw->state.fb->format->format == DRM_FORMAT_C8) {
    381		/* This should be an error, but there's legacy clients
    382		 * that do a modeset before providing a gamma table.
    383		 *
    384		 * We keep the window disabled to avoid angering HW.
    385		 */
    386		if (!(ilut = asyh->state.gamma_lut)) {
    387			asyw->visible = false;
    388			return 0;
    389		}
    390
    391		if (wndw->func->ilut)
    392			asyh->wndw.olut |= BIT(wndw->id);
    393	} else {
    394		asyh->wndw.olut &= ~BIT(wndw->id);
    395	}
    396
    397	if (!ilut && wndw->func->ilut_identity &&
    398	    asyw->state.fb->format->format != DRM_FORMAT_XBGR16161616F &&
    399	    asyw->state.fb->format->format != DRM_FORMAT_ABGR16161616F) {
    400		static struct drm_property_blob dummy = {};
    401		ilut = &dummy;
    402	}
    403
    404	/* Recalculate LUT state. */
    405	memset(&asyw->xlut, 0x00, sizeof(asyw->xlut));
    406	if ((asyw->ilut = wndw->func->ilut ? ilut : NULL)) {
    407		wndw->func->ilut(wndw, asyw, drm_color_lut_size(ilut));
    408		asyw->xlut.handle = wndw->wndw.vram.handle;
    409		asyw->xlut.i.buffer = !asyw->xlut.i.buffer;
    410		asyw->set.xlut = true;
    411	} else {
    412		asyw->clr.xlut = armw->xlut.handle != 0;
    413	}
    414
    415	/* Handle setting base SET_OUTPUT_LUT_LO_ENABLE_USE_CORE_LUT. */
    416	if (wndw->func->olut_core &&
    417	    (!armw->visible || (armw->xlut.handle && !asyw->xlut.handle)))
    418		asyw->set.xlut = true;
    419
    420	if (wndw->func->csc && asyh->state.ctm) {
    421		const struct drm_color_ctm *ctm = asyh->state.ctm->data;
    422		wndw->func->csc(wndw, asyw, ctm);
    423		asyw->csc.valid = true;
    424		asyw->set.csc = true;
    425	} else {
    426		asyw->csc.valid = false;
    427		asyw->clr.csc = armw->csc.valid;
    428	}
    429
    430	/* Can't do an immediate flip while changing the LUT. */
    431	asyh->state.async_flip = false;
    432	return 0;
    433}
    434
    435static int
    436nv50_wndw_atomic_check(struct drm_plane *plane,
    437		       struct drm_atomic_state *state)
    438{
    439	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
    440										 plane);
    441	struct nouveau_drm *drm = nouveau_drm(plane->dev);
    442	struct nv50_wndw *wndw = nv50_wndw(plane);
    443	struct nv50_wndw_atom *armw = nv50_wndw_atom(wndw->plane.state);
    444	struct nv50_wndw_atom *asyw = nv50_wndw_atom(new_plane_state);
    445	struct nv50_head_atom *harm = NULL, *asyh = NULL;
    446	bool modeset = false;
    447	int ret;
    448
    449	NV_ATOMIC(drm, "%s atomic_check\n", plane->name);
    450
    451	/* Fetch the assembly state for the head the window will belong to,
    452	 * and determine whether the window will be visible.
    453	 */
    454	if (asyw->state.crtc) {
    455		asyh = nv50_head_atom_get(asyw->state.state, asyw->state.crtc);
    456		if (IS_ERR(asyh))
    457			return PTR_ERR(asyh);
    458		modeset = drm_atomic_crtc_needs_modeset(&asyh->state);
    459		asyw->visible = asyh->state.active;
    460	} else {
    461		asyw->visible = false;
    462	}
    463
    464	/* Fetch assembly state for the head the window used to belong to. */
    465	if (armw->state.crtc) {
    466		harm = nv50_head_atom_get(asyw->state.state, armw->state.crtc);
    467		if (IS_ERR(harm))
    468			return PTR_ERR(harm);
    469	}
    470
    471	/* LUT configuration can potentially cause the window to be disabled. */
    472	if (asyw->visible && wndw->func->xlut_set &&
    473	    (!armw->visible ||
    474	     asyh->state.color_mgmt_changed ||
    475	     asyw->state.fb->format->format !=
    476	     armw->state.fb->format->format)) {
    477		ret = nv50_wndw_atomic_check_lut(wndw, armw, asyw, asyh);
    478		if (ret)
    479			return ret;
    480	}
    481
    482	/* Calculate new window state. */
    483	if (asyw->visible) {
    484		ret = nv50_wndw_atomic_check_acquire(wndw, modeset,
    485						     armw, asyw, asyh);
    486		if (ret)
    487			return ret;
    488
    489		asyh->wndw.mask |= BIT(wndw->id);
    490	} else
    491	if (armw->visible) {
    492		nv50_wndw_atomic_check_release(wndw, asyw, harm);
    493		harm->wndw.mask &= ~BIT(wndw->id);
    494	} else {
    495		return 0;
    496	}
    497
    498	/* Aside from the obvious case where the window is actively being
    499	 * disabled, we might also need to temporarily disable the window
    500	 * when performing certain modeset operations.
    501	 */
    502	if (!asyw->visible || modeset) {
    503		asyw->clr.ntfy = armw->ntfy.handle != 0;
    504		asyw->clr.sema = armw->sema.handle != 0;
    505		asyw->clr.xlut = armw->xlut.handle != 0;
    506		if (asyw->clr.xlut && asyw->visible)
    507			asyw->set.xlut = asyw->xlut.handle != 0;
    508		asyw->clr.csc  = armw->csc.valid;
    509		if (wndw->func->image_clr)
    510			asyw->clr.image = armw->image.handle[0] != 0;
    511	}
    512
    513	return 0;
    514}
    515
    516static void
    517nv50_wndw_cleanup_fb(struct drm_plane *plane, struct drm_plane_state *old_state)
    518{
    519	struct nouveau_drm *drm = nouveau_drm(plane->dev);
    520	struct nouveau_bo *nvbo;
    521
    522	NV_ATOMIC(drm, "%s cleanup: %p\n", plane->name, old_state->fb);
    523	if (!old_state->fb)
    524		return;
    525
    526	nvbo = nouveau_gem_object(old_state->fb->obj[0]);
    527	nouveau_bo_unpin(nvbo);
    528}
    529
    530static int
    531nv50_wndw_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state)
    532{
    533	struct drm_framebuffer *fb = state->fb;
    534	struct nouveau_drm *drm = nouveau_drm(plane->dev);
    535	struct nv50_wndw *wndw = nv50_wndw(plane);
    536	struct nv50_wndw_atom *asyw = nv50_wndw_atom(state);
    537	struct nouveau_bo *nvbo;
    538	struct nv50_head_atom *asyh;
    539	struct nv50_wndw_ctxdma *ctxdma;
    540	int ret;
    541
    542	NV_ATOMIC(drm, "%s prepare: %p\n", plane->name, fb);
    543	if (!asyw->state.fb)
    544		return 0;
    545
    546	nvbo = nouveau_gem_object(fb->obj[0]);
    547	ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, true);
    548	if (ret)
    549		return ret;
    550
    551	if (wndw->ctxdma.parent) {
    552		ctxdma = nv50_wndw_ctxdma_new(wndw, fb);
    553		if (IS_ERR(ctxdma)) {
    554			nouveau_bo_unpin(nvbo);
    555			return PTR_ERR(ctxdma);
    556		}
    557
    558		if (asyw->visible)
    559			asyw->image.handle[0] = ctxdma->object.handle;
    560	}
    561
    562	ret = drm_gem_plane_helper_prepare_fb(plane, state);
    563	if (ret)
    564		return ret;
    565
    566	asyw->image.offset[0] = nvbo->offset;
    567
    568	if (wndw->func->prepare) {
    569		asyh = nv50_head_atom_get(asyw->state.state, asyw->state.crtc);
    570		if (IS_ERR(asyh))
    571			return PTR_ERR(asyh);
    572
    573		wndw->func->prepare(wndw, asyh, asyw);
    574	}
    575
    576	return 0;
    577}
    578
    579static const struct drm_plane_helper_funcs
    580nv50_wndw_helper = {
    581	.prepare_fb = nv50_wndw_prepare_fb,
    582	.cleanup_fb = nv50_wndw_cleanup_fb,
    583	.atomic_check = nv50_wndw_atomic_check,
    584};
    585
    586static void
    587nv50_wndw_atomic_destroy_state(struct drm_plane *plane,
    588			       struct drm_plane_state *state)
    589{
    590	struct nv50_wndw_atom *asyw = nv50_wndw_atom(state);
    591	__drm_atomic_helper_plane_destroy_state(&asyw->state);
    592	kfree(asyw);
    593}
    594
    595static struct drm_plane_state *
    596nv50_wndw_atomic_duplicate_state(struct drm_plane *plane)
    597{
    598	struct nv50_wndw_atom *armw = nv50_wndw_atom(plane->state);
    599	struct nv50_wndw_atom *asyw;
    600	if (!(asyw = kmalloc(sizeof(*asyw), GFP_KERNEL)))
    601		return NULL;
    602	__drm_atomic_helper_plane_duplicate_state(plane, &asyw->state);
    603	asyw->sema = armw->sema;
    604	asyw->ntfy = armw->ntfy;
    605	asyw->ilut = NULL;
    606	asyw->xlut = armw->xlut;
    607	asyw->csc  = armw->csc;
    608	asyw->image = armw->image;
    609	asyw->point = armw->point;
    610	asyw->clr.mask = 0;
    611	asyw->set.mask = 0;
    612	return &asyw->state;
    613}
    614
    615static int
    616nv50_wndw_zpos_default(struct drm_plane *plane)
    617{
    618	return (plane->type == DRM_PLANE_TYPE_PRIMARY) ? 0 :
    619	       (plane->type == DRM_PLANE_TYPE_OVERLAY) ? 1 : 255;
    620}
    621
    622static void
    623nv50_wndw_reset(struct drm_plane *plane)
    624{
    625	struct nv50_wndw_atom *asyw;
    626
    627	if (WARN_ON(!(asyw = kzalloc(sizeof(*asyw), GFP_KERNEL))))
    628		return;
    629
    630	if (plane->state)
    631		plane->funcs->atomic_destroy_state(plane, plane->state);
    632
    633	__drm_atomic_helper_plane_reset(plane, &asyw->state);
    634}
    635
    636static void
    637nv50_wndw_destroy(struct drm_plane *plane)
    638{
    639	struct nv50_wndw *wndw = nv50_wndw(plane);
    640	struct nv50_wndw_ctxdma *ctxdma, *ctxtmp;
    641
    642	list_for_each_entry_safe(ctxdma, ctxtmp, &wndw->ctxdma.list, head) {
    643		nv50_wndw_ctxdma_del(ctxdma);
    644	}
    645
    646	nvif_notify_dtor(&wndw->notify);
    647	nv50_dmac_destroy(&wndw->wimm);
    648	nv50_dmac_destroy(&wndw->wndw);
    649
    650	nv50_lut_fini(&wndw->ilut);
    651
    652	drm_plane_cleanup(&wndw->plane);
    653	kfree(wndw);
    654}
    655
    656/* This function assumes the format has already been validated against the plane
    657 * and the modifier was validated against the device-wides modifier list at FB
    658 * creation time.
    659 */
    660static bool nv50_plane_format_mod_supported(struct drm_plane *plane,
    661					    u32 format, u64 modifier)
    662{
    663	struct nouveau_drm *drm = nouveau_drm(plane->dev);
    664	uint8_t i;
    665
    666	if (drm->client.device.info.chipset < 0xc0) {
    667		const struct drm_format_info *info = drm_format_info(format);
    668		const uint8_t kind = (modifier >> 12) & 0xff;
    669
    670		if (!format) return false;
    671
    672		for (i = 0; i < info->num_planes; i++)
    673			if ((info->cpp[i] != 4) && kind != 0x70) return false;
    674	}
    675
    676	return true;
    677}
    678
    679const struct drm_plane_funcs
    680nv50_wndw = {
    681	.update_plane = drm_atomic_helper_update_plane,
    682	.disable_plane = drm_atomic_helper_disable_plane,
    683	.destroy = nv50_wndw_destroy,
    684	.reset = nv50_wndw_reset,
    685	.atomic_duplicate_state = nv50_wndw_atomic_duplicate_state,
    686	.atomic_destroy_state = nv50_wndw_atomic_destroy_state,
    687	.format_mod_supported = nv50_plane_format_mod_supported,
    688};
    689
    690static int
    691nv50_wndw_notify(struct nvif_notify *notify)
    692{
    693	return NVIF_NOTIFY_KEEP;
    694}
    695
    696void
    697nv50_wndw_fini(struct nv50_wndw *wndw)
    698{
    699	nvif_notify_put(&wndw->notify);
    700}
    701
    702void
    703nv50_wndw_init(struct nv50_wndw *wndw)
    704{
    705	nvif_notify_get(&wndw->notify);
    706}
    707
    708static const u64 nv50_cursor_format_modifiers[] = {
    709	DRM_FORMAT_MOD_LINEAR,
    710	DRM_FORMAT_MOD_INVALID,
    711};
    712
    713int
    714nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev,
    715	       enum drm_plane_type type, const char *name, int index,
    716	       const u32 *format, u32 heads,
    717	       enum nv50_disp_interlock_type interlock_type, u32 interlock_data,
    718	       struct nv50_wndw **pwndw)
    719{
    720	struct nouveau_drm *drm = nouveau_drm(dev);
    721	struct nvif_mmu *mmu = &drm->client.mmu;
    722	struct nv50_disp *disp = nv50_disp(dev);
    723	struct nv50_wndw *wndw;
    724	const u64 *format_modifiers;
    725	int nformat;
    726	int ret;
    727
    728	if (!(wndw = *pwndw = kzalloc(sizeof(*wndw), GFP_KERNEL)))
    729		return -ENOMEM;
    730	wndw->func = func;
    731	wndw->id = index;
    732	wndw->interlock.type = interlock_type;
    733	wndw->interlock.data = interlock_data;
    734
    735	wndw->ctxdma.parent = &wndw->wndw.base.user;
    736	INIT_LIST_HEAD(&wndw->ctxdma.list);
    737
    738	for (nformat = 0; format[nformat]; nformat++);
    739
    740	if (type == DRM_PLANE_TYPE_CURSOR)
    741		format_modifiers = nv50_cursor_format_modifiers;
    742	else
    743		format_modifiers = nouveau_display(dev)->format_modifiers;
    744
    745	ret = drm_universal_plane_init(dev, &wndw->plane, heads, &nv50_wndw, format, nformat,
    746				       format_modifiers, type, "%s-%d", name, index);
    747	if (ret) {
    748		kfree(*pwndw);
    749		*pwndw = NULL;
    750		return ret;
    751	}
    752
    753	drm_plane_helper_add(&wndw->plane, &nv50_wndw_helper);
    754
    755	if (wndw->func->ilut) {
    756		ret = nv50_lut_init(disp, mmu, &wndw->ilut);
    757		if (ret)
    758			return ret;
    759	}
    760
    761	wndw->notify.func = nv50_wndw_notify;
    762
    763	if (wndw->func->blend_set) {
    764		ret = drm_plane_create_zpos_property(&wndw->plane,
    765				nv50_wndw_zpos_default(&wndw->plane), 0, 254);
    766		if (ret)
    767			return ret;
    768
    769		ret = drm_plane_create_alpha_property(&wndw->plane);
    770		if (ret)
    771			return ret;
    772
    773		ret = drm_plane_create_blend_mode_property(&wndw->plane,
    774				BIT(DRM_MODE_BLEND_PIXEL_NONE) |
    775				BIT(DRM_MODE_BLEND_PREMULTI) |
    776				BIT(DRM_MODE_BLEND_COVERAGE));
    777		if (ret)
    778			return ret;
    779	} else {
    780		ret = drm_plane_create_zpos_immutable_property(&wndw->plane,
    781				nv50_wndw_zpos_default(&wndw->plane));
    782		if (ret)
    783			return ret;
    784	}
    785
    786	return 0;
    787}
    788
    789int
    790nv50_wndw_new(struct nouveau_drm *drm, enum drm_plane_type type, int index,
    791	      struct nv50_wndw **pwndw)
    792{
    793	struct {
    794		s32 oclass;
    795		int version;
    796		int (*new)(struct nouveau_drm *, enum drm_plane_type,
    797			   int, s32, struct nv50_wndw **);
    798	} wndws[] = {
    799		{ GA102_DISP_WINDOW_CHANNEL_DMA, 0, wndwc67e_new },
    800		{ TU102_DISP_WINDOW_CHANNEL_DMA, 0, wndwc57e_new },
    801		{ GV100_DISP_WINDOW_CHANNEL_DMA, 0, wndwc37e_new },
    802		{}
    803	};
    804	struct nv50_disp *disp = nv50_disp(drm->dev);
    805	int cid, ret;
    806
    807	cid = nvif_mclass(&disp->disp->object, wndws);
    808	if (cid < 0) {
    809		NV_ERROR(drm, "No supported window class\n");
    810		return cid;
    811	}
    812
    813	ret = wndws[cid].new(drm, type, index, wndws[cid].oclass, pwndw);
    814	if (ret)
    815		return ret;
    816
    817	return nv50_wimm_init(drm, *pwndw);
    818}