162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright 1993-2003 NVIDIA, Corporation
362306a36Sopenharmony_ci * Copyright 2006 Dave Airlie
462306a36Sopenharmony_ci * Copyright 2007 Maarten Maathuis
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
762306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
862306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
962306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1062306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
1162306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the next
1462306a36Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
1562306a36Sopenharmony_ci * Software.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1862306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1962306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2062306a36Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2162306a36Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2262306a36Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2362306a36Sopenharmony_ci * DEALINGS IN THE SOFTWARE.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci#include <drm/drm_crtc_helper.h>
2662306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
2762306a36Sopenharmony_ci#include <drm/drm_modeset_helper_vtables.h>
2862306a36Sopenharmony_ci#include <drm/drm_plane_helper.h>
2962306a36Sopenharmony_ci#include <drm/drm_vblank.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include "nouveau_drv.h"
3262306a36Sopenharmony_ci#include "nouveau_reg.h"
3362306a36Sopenharmony_ci#include "nouveau_ttm.h"
3462306a36Sopenharmony_ci#include "nouveau_bo.h"
3562306a36Sopenharmony_ci#include "nouveau_gem.h"
3662306a36Sopenharmony_ci#include "nouveau_encoder.h"
3762306a36Sopenharmony_ci#include "nouveau_connector.h"
3862306a36Sopenharmony_ci#include "nouveau_crtc.h"
3962306a36Sopenharmony_ci#include "hw.h"
4062306a36Sopenharmony_ci#include "nvreg.h"
4162306a36Sopenharmony_ci#include "disp.h"
4262306a36Sopenharmony_ci#include "nouveau_dma.h"
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#include <subdev/bios/pll.h>
4562306a36Sopenharmony_ci#include <subdev/clk.h>
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#include <nvif/push006c.h>
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#include <nvif/event.h>
5062306a36Sopenharmony_ci#include <nvif/cl0046.h>
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic int
5362306a36Sopenharmony_cinv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
5462306a36Sopenharmony_ci			struct drm_framebuffer *old_fb);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic void
5762306a36Sopenharmony_cicrtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index,
6062306a36Sopenharmony_ci		       crtcstate->CRTC[index]);
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void nv_crtc_set_digital_vibrance(struct drm_crtc *crtc, int level)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
6662306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
6762306a36Sopenharmony_ci	struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index];
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_CSB] = nv_crtc->saturation = level;
7062306a36Sopenharmony_ci	if (nv_crtc->saturation && nv_gf4_disp_arch(crtc->dev)) {
7162306a36Sopenharmony_ci		regp->CRTC[NV_CIO_CRE_CSB] = 0x80;
7262306a36Sopenharmony_ci		regp->CRTC[NV_CIO_CRE_5B] = nv_crtc->saturation << 2;
7362306a36Sopenharmony_ci		crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_5B);
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci	crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_CSB);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void nv_crtc_set_image_sharpening(struct drm_crtc *crtc, int level)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
8162306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
8262306a36Sopenharmony_ci	struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index];
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	nv_crtc->sharpness = level;
8562306a36Sopenharmony_ci	if (level < 0)	/* blur is in hw range 0x3f -> 0x20 */
8662306a36Sopenharmony_ci		level += 0x40;
8762306a36Sopenharmony_ci	regp->ramdac_634 = level;
8862306a36Sopenharmony_ci	NVWriteRAMDAC(crtc->dev, nv_crtc->index, NV_PRAMDAC_634, regp->ramdac_634);
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci#define PLLSEL_VPLL1_MASK				\
9262306a36Sopenharmony_ci	(NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL	\
9362306a36Sopenharmony_ci	 | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2)
9462306a36Sopenharmony_ci#define PLLSEL_VPLL2_MASK				\
9562306a36Sopenharmony_ci	(NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2		\
9662306a36Sopenharmony_ci	 | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2)
9762306a36Sopenharmony_ci#define PLLSEL_TV_MASK					\
9862306a36Sopenharmony_ci	(NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1		\
9962306a36Sopenharmony_ci	 | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1		\
10062306a36Sopenharmony_ci	 | NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2	\
10162306a36Sopenharmony_ci	 | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2)
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/* NV4x 0x40.. pll notes:
10462306a36Sopenharmony_ci * gpu pll: 0x4000 + 0x4004
10562306a36Sopenharmony_ci * ?gpu? pll: 0x4008 + 0x400c
10662306a36Sopenharmony_ci * vpll1: 0x4010 + 0x4014
10762306a36Sopenharmony_ci * vpll2: 0x4018 + 0x401c
10862306a36Sopenharmony_ci * mpll: 0x4020 + 0x4024
10962306a36Sopenharmony_ci * mpll: 0x4038 + 0x403c
11062306a36Sopenharmony_ci *
11162306a36Sopenharmony_ci * the first register of each pair has some unknown details:
11262306a36Sopenharmony_ci * bits 0-7: redirected values from elsewhere? (similar to PLL_SETUP_CONTROL?)
11362306a36Sopenharmony_ci * bits 20-23: (mpll) something to do with post divider?
11462306a36Sopenharmony_ci * bits 28-31: related to single stage mode? (bit 8/12)
11562306a36Sopenharmony_ci */
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mode * mode, int dot_clock)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
12062306a36Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(dev);
12162306a36Sopenharmony_ci	struct nvkm_bios *bios = nvxx_bios(&drm->client.device);
12262306a36Sopenharmony_ci	struct nvkm_clk *clk = nvxx_clk(&drm->client.device);
12362306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
12462306a36Sopenharmony_ci	struct nv04_mode_state *state = &nv04_display(dev)->mode_reg;
12562306a36Sopenharmony_ci	struct nv04_crtc_reg *regp = &state->crtc_reg[nv_crtc->index];
12662306a36Sopenharmony_ci	struct nvkm_pll_vals *pv = &regp->pllvals;
12762306a36Sopenharmony_ci	struct nvbios_pll pll_lim;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (nvbios_pll_parse(bios, nv_crtc->index ? PLL_VPLL1 : PLL_VPLL0,
13062306a36Sopenharmony_ci			    &pll_lim))
13162306a36Sopenharmony_ci		return;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* NM2 == 0 is used to determine single stage mode on two stage plls */
13462306a36Sopenharmony_ci	pv->NM2 = 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* for newer nv4x the blob uses only the first stage of the vpll below a
13762306a36Sopenharmony_ci	 * certain clock.  for a certain nv4b this is 150MHz.  since the max
13862306a36Sopenharmony_ci	 * output frequency of the first stage for this card is 300MHz, it is
13962306a36Sopenharmony_ci	 * assumed the threshold is given by vco1 maxfreq/2
14062306a36Sopenharmony_ci	 */
14162306a36Sopenharmony_ci	/* for early nv4x, specifically nv40 and *some* nv43 (devids 0 and 6,
14262306a36Sopenharmony_ci	 * not 8, others unknown), the blob always uses both plls.  no problem
14362306a36Sopenharmony_ci	 * has yet been observed in allowing the use a single stage pll on all
14462306a36Sopenharmony_ci	 * nv43 however.  the behaviour of single stage use is untested on nv40
14562306a36Sopenharmony_ci	 */
14662306a36Sopenharmony_ci	if (drm->client.device.info.chipset > 0x40 && dot_clock <= (pll_lim.vco1.max_freq / 2))
14762306a36Sopenharmony_ci		memset(&pll_lim.vco2, 0, sizeof(pll_lim.vco2));
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (!clk->pll_calc(clk, &pll_lim, dot_clock, pv))
15162306a36Sopenharmony_ci		return;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	state->pllsel &= PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* The blob uses this always, so let's do the same */
15662306a36Sopenharmony_ci	if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE)
15762306a36Sopenharmony_ci		state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE;
15862306a36Sopenharmony_ci	/* again nv40 and some nv43 act more like nv3x as described above */
15962306a36Sopenharmony_ci	if (drm->client.device.info.chipset < 0x41)
16062306a36Sopenharmony_ci		state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL |
16162306a36Sopenharmony_ci				 NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL;
16262306a36Sopenharmony_ci	state->pllsel |= nv_crtc->index ? PLLSEL_VPLL2_MASK : PLLSEL_VPLL1_MASK;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (pv->NM2)
16562306a36Sopenharmony_ci		NV_DEBUG(drm, "vpll: n1 %d n2 %d m1 %d m2 %d log2p %d\n",
16662306a36Sopenharmony_ci			 pv->N1, pv->N2, pv->M1, pv->M2, pv->log2P);
16762306a36Sopenharmony_ci	else
16862306a36Sopenharmony_ci		NV_DEBUG(drm, "vpll: n %d m %d log2p %d\n",
16962306a36Sopenharmony_ci			 pv->N1, pv->M1, pv->log2P);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic void
17562306a36Sopenharmony_cinv_crtc_dpms(struct drm_crtc *crtc, int mode)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
17862306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
17962306a36Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(dev);
18062306a36Sopenharmony_ci	unsigned char seq1 = 0, crtc17 = 0;
18162306a36Sopenharmony_ci	unsigned char crtc1A;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	NV_DEBUG(drm, "Setting dpms mode %d on CRTC %d\n", mode,
18462306a36Sopenharmony_ci							nv_crtc->index);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (nv_crtc->last_dpms == mode) /* Don't do unnecessary mode changes. */
18762306a36Sopenharmony_ci		return;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	nv_crtc->last_dpms = mode;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (nv_two_heads(dev))
19262306a36Sopenharmony_ci		NVSetOwner(dev, nv_crtc->index);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/* nv4ref indicates these two RPC1 bits inhibit h/v sync */
19562306a36Sopenharmony_ci	crtc1A = NVReadVgaCrtc(dev, nv_crtc->index,
19662306a36Sopenharmony_ci					NV_CIO_CRE_RPC1_INDEX) & ~0xC0;
19762306a36Sopenharmony_ci	switch (mode) {
19862306a36Sopenharmony_ci	case DRM_MODE_DPMS_STANDBY:
19962306a36Sopenharmony_ci		/* Screen: Off; HSync: Off, VSync: On -- Not Supported */
20062306a36Sopenharmony_ci		seq1 = 0x20;
20162306a36Sopenharmony_ci		crtc17 = 0x80;
20262306a36Sopenharmony_ci		crtc1A |= 0x80;
20362306a36Sopenharmony_ci		break;
20462306a36Sopenharmony_ci	case DRM_MODE_DPMS_SUSPEND:
20562306a36Sopenharmony_ci		/* Screen: Off; HSync: On, VSync: Off -- Not Supported */
20662306a36Sopenharmony_ci		seq1 = 0x20;
20762306a36Sopenharmony_ci		crtc17 = 0x80;
20862306a36Sopenharmony_ci		crtc1A |= 0x40;
20962306a36Sopenharmony_ci		break;
21062306a36Sopenharmony_ci	case DRM_MODE_DPMS_OFF:
21162306a36Sopenharmony_ci		/* Screen: Off; HSync: Off, VSync: Off */
21262306a36Sopenharmony_ci		seq1 = 0x20;
21362306a36Sopenharmony_ci		crtc17 = 0x00;
21462306a36Sopenharmony_ci		crtc1A |= 0xC0;
21562306a36Sopenharmony_ci		break;
21662306a36Sopenharmony_ci	case DRM_MODE_DPMS_ON:
21762306a36Sopenharmony_ci	default:
21862306a36Sopenharmony_ci		/* Screen: On; HSync: On, VSync: On */
21962306a36Sopenharmony_ci		seq1 = 0x00;
22062306a36Sopenharmony_ci		crtc17 = 0x80;
22162306a36Sopenharmony_ci		break;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	NVVgaSeqReset(dev, nv_crtc->index, true);
22562306a36Sopenharmony_ci	/* Each head has it's own sequencer, so we can turn it off when we want */
22662306a36Sopenharmony_ci	seq1 |= (NVReadVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX) & ~0x20);
22762306a36Sopenharmony_ci	NVWriteVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX, seq1);
22862306a36Sopenharmony_ci	crtc17 |= (NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX) & ~0x80);
22962306a36Sopenharmony_ci	mdelay(10);
23062306a36Sopenharmony_ci	NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX, crtc17);
23162306a36Sopenharmony_ci	NVVgaSeqReset(dev, nv_crtc->index, false);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RPC1_INDEX, crtc1A);
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic void
23762306a36Sopenharmony_cinv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
24062306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
24162306a36Sopenharmony_ci	struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index];
24262306a36Sopenharmony_ci	struct drm_framebuffer *fb = crtc->primary->fb;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Calculate our timings */
24562306a36Sopenharmony_ci	int horizDisplay	= (mode->crtc_hdisplay >> 3)		- 1;
24662306a36Sopenharmony_ci	int horizStart		= (mode->crtc_hsync_start >> 3) 	+ 1;
24762306a36Sopenharmony_ci	int horizEnd		= (mode->crtc_hsync_end >> 3)		+ 1;
24862306a36Sopenharmony_ci	int horizTotal		= (mode->crtc_htotal >> 3)		- 5;
24962306a36Sopenharmony_ci	int horizBlankStart	= (mode->crtc_hdisplay >> 3)		- 1;
25062306a36Sopenharmony_ci	int horizBlankEnd	= (mode->crtc_htotal >> 3)		- 1;
25162306a36Sopenharmony_ci	int vertDisplay		= mode->crtc_vdisplay			- 1;
25262306a36Sopenharmony_ci	int vertStart		= mode->crtc_vsync_start 		- 1;
25362306a36Sopenharmony_ci	int vertEnd		= mode->crtc_vsync_end			- 1;
25462306a36Sopenharmony_ci	int vertTotal		= mode->crtc_vtotal 			- 2;
25562306a36Sopenharmony_ci	int vertBlankStart	= mode->crtc_vdisplay 			- 1;
25662306a36Sopenharmony_ci	int vertBlankEnd	= mode->crtc_vtotal			- 1;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	struct drm_encoder *encoder;
25962306a36Sopenharmony_ci	bool fp_output = false;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
26262306a36Sopenharmony_ci		struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci		if (encoder->crtc == crtc &&
26562306a36Sopenharmony_ci		    (nv_encoder->dcb->type == DCB_OUTPUT_LVDS ||
26662306a36Sopenharmony_ci		     nv_encoder->dcb->type == DCB_OUTPUT_TMDS))
26762306a36Sopenharmony_ci			fp_output = true;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (fp_output) {
27162306a36Sopenharmony_ci		vertStart = vertTotal - 3;
27262306a36Sopenharmony_ci		vertEnd = vertTotal - 2;
27362306a36Sopenharmony_ci		vertBlankStart = vertStart;
27462306a36Sopenharmony_ci		horizStart = horizTotal - 5;
27562306a36Sopenharmony_ci		horizEnd = horizTotal - 2;
27662306a36Sopenharmony_ci		horizBlankEnd = horizTotal + 4;
27762306a36Sopenharmony_ci#if 0
27862306a36Sopenharmony_ci		if (dev->overlayAdaptor && drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS)
27962306a36Sopenharmony_ci			/* This reportedly works around some video overlay bandwidth problems */
28062306a36Sopenharmony_ci			horizTotal += 2;
28162306a36Sopenharmony_ci#endif
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
28562306a36Sopenharmony_ci		vertTotal |= 1;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci#if 0
28862306a36Sopenharmony_ci	ErrorF("horizDisplay: 0x%X \n", horizDisplay);
28962306a36Sopenharmony_ci	ErrorF("horizStart: 0x%X \n", horizStart);
29062306a36Sopenharmony_ci	ErrorF("horizEnd: 0x%X \n", horizEnd);
29162306a36Sopenharmony_ci	ErrorF("horizTotal: 0x%X \n", horizTotal);
29262306a36Sopenharmony_ci	ErrorF("horizBlankStart: 0x%X \n", horizBlankStart);
29362306a36Sopenharmony_ci	ErrorF("horizBlankEnd: 0x%X \n", horizBlankEnd);
29462306a36Sopenharmony_ci	ErrorF("vertDisplay: 0x%X \n", vertDisplay);
29562306a36Sopenharmony_ci	ErrorF("vertStart: 0x%X \n", vertStart);
29662306a36Sopenharmony_ci	ErrorF("vertEnd: 0x%X \n", vertEnd);
29762306a36Sopenharmony_ci	ErrorF("vertTotal: 0x%X \n", vertTotal);
29862306a36Sopenharmony_ci	ErrorF("vertBlankStart: 0x%X \n", vertBlankStart);
29962306a36Sopenharmony_ci	ErrorF("vertBlankEnd: 0x%X \n", vertBlankEnd);
30062306a36Sopenharmony_ci#endif
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/*
30362306a36Sopenharmony_ci	* compute correct Hsync & Vsync polarity
30462306a36Sopenharmony_ci	*/
30562306a36Sopenharmony_ci	if ((mode->flags & (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC))
30662306a36Sopenharmony_ci		&& (mode->flags & (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC))) {
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		regp->MiscOutReg = 0x23;
30962306a36Sopenharmony_ci		if (mode->flags & DRM_MODE_FLAG_NHSYNC)
31062306a36Sopenharmony_ci			regp->MiscOutReg |= 0x40;
31162306a36Sopenharmony_ci		if (mode->flags & DRM_MODE_FLAG_NVSYNC)
31262306a36Sopenharmony_ci			regp->MiscOutReg |= 0x80;
31362306a36Sopenharmony_ci	} else {
31462306a36Sopenharmony_ci		int vdisplay = mode->vdisplay;
31562306a36Sopenharmony_ci		if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
31662306a36Sopenharmony_ci			vdisplay *= 2;
31762306a36Sopenharmony_ci		if (mode->vscan > 1)
31862306a36Sopenharmony_ci			vdisplay *= mode->vscan;
31962306a36Sopenharmony_ci		if (vdisplay < 400)
32062306a36Sopenharmony_ci			regp->MiscOutReg = 0xA3;	/* +hsync -vsync */
32162306a36Sopenharmony_ci		else if (vdisplay < 480)
32262306a36Sopenharmony_ci			regp->MiscOutReg = 0x63;	/* -hsync +vsync */
32362306a36Sopenharmony_ci		else if (vdisplay < 768)
32462306a36Sopenharmony_ci			regp->MiscOutReg = 0xE3;	/* -hsync -vsync */
32562306a36Sopenharmony_ci		else
32662306a36Sopenharmony_ci			regp->MiscOutReg = 0x23;	/* +hsync +vsync */
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	/*
33062306a36Sopenharmony_ci	 * Time Sequencer
33162306a36Sopenharmony_ci	 */
33262306a36Sopenharmony_ci	regp->Sequencer[NV_VIO_SR_RESET_INDEX] = 0x00;
33362306a36Sopenharmony_ci	/* 0x20 disables the sequencer */
33462306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_CLKDIV2)
33562306a36Sopenharmony_ci		regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x29;
33662306a36Sopenharmony_ci	else
33762306a36Sopenharmony_ci		regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x21;
33862306a36Sopenharmony_ci	regp->Sequencer[NV_VIO_SR_PLANE_MASK_INDEX] = 0x0F;
33962306a36Sopenharmony_ci	regp->Sequencer[NV_VIO_SR_CHAR_MAP_INDEX] = 0x00;
34062306a36Sopenharmony_ci	regp->Sequencer[NV_VIO_SR_MEM_MODE_INDEX] = 0x0E;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/*
34362306a36Sopenharmony_ci	 * CRTC
34462306a36Sopenharmony_ci	 */
34562306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_HDT_INDEX] = horizTotal;
34662306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_HDE_INDEX] = horizDisplay;
34762306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_HBS_INDEX] = horizBlankStart;
34862306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_HBE_INDEX] = (1 << 7) |
34962306a36Sopenharmony_ci					  XLATE(horizBlankEnd, 0, NV_CIO_CR_HBE_4_0);
35062306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_HRS_INDEX] = horizStart;
35162306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_HRE_INDEX] = XLATE(horizBlankEnd, 5, NV_CIO_CR_HRE_HBE_5) |
35262306a36Sopenharmony_ci					  XLATE(horizEnd, 0, NV_CIO_CR_HRE_4_0);
35362306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_VDT_INDEX] = vertTotal;
35462306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_OVL_INDEX] = XLATE(vertStart, 9, NV_CIO_CR_OVL_VRS_9) |
35562306a36Sopenharmony_ci					  XLATE(vertDisplay, 9, NV_CIO_CR_OVL_VDE_9) |
35662306a36Sopenharmony_ci					  XLATE(vertTotal, 9, NV_CIO_CR_OVL_VDT_9) |
35762306a36Sopenharmony_ci					  (1 << 4) |
35862306a36Sopenharmony_ci					  XLATE(vertBlankStart, 8, NV_CIO_CR_OVL_VBS_8) |
35962306a36Sopenharmony_ci					  XLATE(vertStart, 8, NV_CIO_CR_OVL_VRS_8) |
36062306a36Sopenharmony_ci					  XLATE(vertDisplay, 8, NV_CIO_CR_OVL_VDE_8) |
36162306a36Sopenharmony_ci					  XLATE(vertTotal, 8, NV_CIO_CR_OVL_VDT_8);
36262306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_RSAL_INDEX] = 0x00;
36362306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_CELL_HT_INDEX] = ((mode->flags & DRM_MODE_FLAG_DBLSCAN) ? MASK(NV_CIO_CR_CELL_HT_SCANDBL) : 0) |
36462306a36Sopenharmony_ci					      1 << 6 |
36562306a36Sopenharmony_ci					      XLATE(vertBlankStart, 9, NV_CIO_CR_CELL_HT_VBS_9);
36662306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_CURS_ST_INDEX] = 0x00;
36762306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_CURS_END_INDEX] = 0x00;
36862306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_SA_HI_INDEX] = 0x00;
36962306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_SA_LO_INDEX] = 0x00;
37062306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_TCOFF_HI_INDEX] = 0x00;
37162306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_TCOFF_LO_INDEX] = 0x00;
37262306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_VRS_INDEX] = vertStart;
37362306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_VRE_INDEX] = 1 << 5 | XLATE(vertEnd, 0, NV_CIO_CR_VRE_3_0);
37462306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_VDE_INDEX] = vertDisplay;
37562306a36Sopenharmony_ci	/* framebuffer can be larger than crtc scanout area. */
37662306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = fb->pitches[0] / 8;
37762306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_ULINE_INDEX] = 0x00;
37862306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_VBS_INDEX] = vertBlankStart;
37962306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_VBE_INDEX] = vertBlankEnd;
38062306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_MODE_INDEX] = 0x43;
38162306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_LCOMP_INDEX] = 0xff;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/*
38462306a36Sopenharmony_ci	 * Some extended CRTC registers (they are not saved with the rest of the vga regs).
38562306a36Sopenharmony_ci	 */
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/* framebuffer can be larger than crtc scanout area. */
38862306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_RPC0_INDEX] =
38962306a36Sopenharmony_ci		XLATE(fb->pitches[0] / 8, 8, NV_CIO_CRE_RPC0_OFFSET_10_8);
39062306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_42] =
39162306a36Sopenharmony_ci		XLATE(fb->pitches[0] / 8, 11, NV_CIO_CRE_42_OFFSET_11);
39262306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_RPC1_INDEX] = mode->crtc_hdisplay < 1280 ?
39362306a36Sopenharmony_ci					    MASK(NV_CIO_CRE_RPC1_LARGE) : 0x00;
39462306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_LSR_INDEX] = XLATE(horizBlankEnd, 6, NV_CIO_CRE_LSR_HBE_6) |
39562306a36Sopenharmony_ci					   XLATE(vertBlankStart, 10, NV_CIO_CRE_LSR_VBS_10) |
39662306a36Sopenharmony_ci					   XLATE(vertStart, 10, NV_CIO_CRE_LSR_VRS_10) |
39762306a36Sopenharmony_ci					   XLATE(vertDisplay, 10, NV_CIO_CRE_LSR_VDE_10) |
39862306a36Sopenharmony_ci					   XLATE(vertTotal, 10, NV_CIO_CRE_LSR_VDT_10);
39962306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_HEB__INDEX] = XLATE(horizStart, 8, NV_CIO_CRE_HEB_HRS_8) |
40062306a36Sopenharmony_ci					    XLATE(horizBlankStart, 8, NV_CIO_CRE_HEB_HBS_8) |
40162306a36Sopenharmony_ci					    XLATE(horizDisplay, 8, NV_CIO_CRE_HEB_HDE_8) |
40262306a36Sopenharmony_ci					    XLATE(horizTotal, 8, NV_CIO_CRE_HEB_HDT_8);
40362306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_EBR_INDEX] = XLATE(vertBlankStart, 11, NV_CIO_CRE_EBR_VBS_11) |
40462306a36Sopenharmony_ci					   XLATE(vertStart, 11, NV_CIO_CRE_EBR_VRS_11) |
40562306a36Sopenharmony_ci					   XLATE(vertDisplay, 11, NV_CIO_CRE_EBR_VDE_11) |
40662306a36Sopenharmony_ci					   XLATE(vertTotal, 11, NV_CIO_CRE_EBR_VDT_11);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
40962306a36Sopenharmony_ci		horizTotal = (horizTotal >> 1) & ~1;
41062306a36Sopenharmony_ci		regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = horizTotal;
41162306a36Sopenharmony_ci		regp->CRTC[NV_CIO_CRE_HEB__INDEX] |= XLATE(horizTotal, 8, NV_CIO_CRE_HEB_ILC_8);
41262306a36Sopenharmony_ci	} else
41362306a36Sopenharmony_ci		regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = 0xff;  /* interlace off */
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/*
41662306a36Sopenharmony_ci	* Graphics Display Controller
41762306a36Sopenharmony_ci	*/
41862306a36Sopenharmony_ci	regp->Graphics[NV_VIO_GX_SR_INDEX] = 0x00;
41962306a36Sopenharmony_ci	regp->Graphics[NV_VIO_GX_SREN_INDEX] = 0x00;
42062306a36Sopenharmony_ci	regp->Graphics[NV_VIO_GX_CCOMP_INDEX] = 0x00;
42162306a36Sopenharmony_ci	regp->Graphics[NV_VIO_GX_ROP_INDEX] = 0x00;
42262306a36Sopenharmony_ci	regp->Graphics[NV_VIO_GX_READ_MAP_INDEX] = 0x00;
42362306a36Sopenharmony_ci	regp->Graphics[NV_VIO_GX_MODE_INDEX] = 0x40; /* 256 color mode */
42462306a36Sopenharmony_ci	regp->Graphics[NV_VIO_GX_MISC_INDEX] = 0x05; /* map 64k mem + graphic mode */
42562306a36Sopenharmony_ci	regp->Graphics[NV_VIO_GX_DONT_CARE_INDEX] = 0x0F;
42662306a36Sopenharmony_ci	regp->Graphics[NV_VIO_GX_BIT_MASK_INDEX] = 0xFF;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	regp->Attribute[0]  = 0x00; /* standard colormap translation */
42962306a36Sopenharmony_ci	regp->Attribute[1]  = 0x01;
43062306a36Sopenharmony_ci	regp->Attribute[2]  = 0x02;
43162306a36Sopenharmony_ci	regp->Attribute[3]  = 0x03;
43262306a36Sopenharmony_ci	regp->Attribute[4]  = 0x04;
43362306a36Sopenharmony_ci	regp->Attribute[5]  = 0x05;
43462306a36Sopenharmony_ci	regp->Attribute[6]  = 0x06;
43562306a36Sopenharmony_ci	regp->Attribute[7]  = 0x07;
43662306a36Sopenharmony_ci	regp->Attribute[8]  = 0x08;
43762306a36Sopenharmony_ci	regp->Attribute[9]  = 0x09;
43862306a36Sopenharmony_ci	regp->Attribute[10] = 0x0A;
43962306a36Sopenharmony_ci	regp->Attribute[11] = 0x0B;
44062306a36Sopenharmony_ci	regp->Attribute[12] = 0x0C;
44162306a36Sopenharmony_ci	regp->Attribute[13] = 0x0D;
44262306a36Sopenharmony_ci	regp->Attribute[14] = 0x0E;
44362306a36Sopenharmony_ci	regp->Attribute[15] = 0x0F;
44462306a36Sopenharmony_ci	regp->Attribute[NV_CIO_AR_MODE_INDEX] = 0x01; /* Enable graphic mode */
44562306a36Sopenharmony_ci	/* Non-vga */
44662306a36Sopenharmony_ci	regp->Attribute[NV_CIO_AR_OSCAN_INDEX] = 0x00;
44762306a36Sopenharmony_ci	regp->Attribute[NV_CIO_AR_PLANE_INDEX] = 0x0F; /* enable all color planes */
44862306a36Sopenharmony_ci	regp->Attribute[NV_CIO_AR_HPP_INDEX] = 0x00;
44962306a36Sopenharmony_ci	regp->Attribute[NV_CIO_AR_CSEL_INDEX] = 0x00;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci/**
45362306a36Sopenharmony_ci * Sets up registers for the given mode/adjusted_mode pair.
45462306a36Sopenharmony_ci *
45562306a36Sopenharmony_ci * The clocks, CRTCs and outputs attached to this CRTC must be off.
45662306a36Sopenharmony_ci *
45762306a36Sopenharmony_ci * This shouldn't enable any clocks, CRTCs, or outputs, but they should
45862306a36Sopenharmony_ci * be easily turned on/off after this.
45962306a36Sopenharmony_ci */
46062306a36Sopenharmony_cistatic void
46162306a36Sopenharmony_cinv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
46462306a36Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(dev);
46562306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
46662306a36Sopenharmony_ci	struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index];
46762306a36Sopenharmony_ci	struct nv04_crtc_reg *savep = &nv04_display(dev)->saved_reg.crtc_reg[nv_crtc->index];
46862306a36Sopenharmony_ci	const struct drm_framebuffer *fb = crtc->primary->fb;
46962306a36Sopenharmony_ci	struct drm_encoder *encoder;
47062306a36Sopenharmony_ci	bool lvds_output = false, tmds_output = false, tv_output = false,
47162306a36Sopenharmony_ci		off_chip_digital = false;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
47462306a36Sopenharmony_ci		struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
47562306a36Sopenharmony_ci		bool digital = false;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		if (encoder->crtc != crtc)
47862306a36Sopenharmony_ci			continue;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci		if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS)
48162306a36Sopenharmony_ci			digital = lvds_output = true;
48262306a36Sopenharmony_ci		if (nv_encoder->dcb->type == DCB_OUTPUT_TV)
48362306a36Sopenharmony_ci			tv_output = true;
48462306a36Sopenharmony_ci		if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS)
48562306a36Sopenharmony_ci			digital = tmds_output = true;
48662306a36Sopenharmony_ci		if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && digital)
48762306a36Sopenharmony_ci			off_chip_digital = true;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	/* Registers not directly related to the (s)vga mode */
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	/* What is the meaning of this register? */
49362306a36Sopenharmony_ci	/* A few popular values are 0x18, 0x1c, 0x38, 0x3c */
49462306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_ENH_INDEX] = savep->CRTC[NV_CIO_CRE_ENH_INDEX] & ~(1<<5);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	regp->crtc_eng_ctrl = 0;
49762306a36Sopenharmony_ci	/* Except for rare conditions I2C is enabled on the primary crtc */
49862306a36Sopenharmony_ci	if (nv_crtc->index == 0)
49962306a36Sopenharmony_ci		regp->crtc_eng_ctrl |= NV_CRTC_FSEL_I2C;
50062306a36Sopenharmony_ci#if 0
50162306a36Sopenharmony_ci	/* Set overlay to desired crtc. */
50262306a36Sopenharmony_ci	if (dev->overlayAdaptor) {
50362306a36Sopenharmony_ci		NVPortPrivPtr pPriv = GET_OVERLAY_PRIVATE(dev);
50462306a36Sopenharmony_ci		if (pPriv->overlayCRTC == nv_crtc->index)
50562306a36Sopenharmony_ci			regp->crtc_eng_ctrl |= NV_CRTC_FSEL_OVERLAY;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci#endif
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* ADDRESS_SPACE_PNVM is the same as setting HCUR_ASI */
51062306a36Sopenharmony_ci	regp->cursor_cfg = NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 |
51162306a36Sopenharmony_ci			     NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 |
51262306a36Sopenharmony_ci			     NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM;
51362306a36Sopenharmony_ci	if (drm->client.device.info.chipset >= 0x11)
51462306a36Sopenharmony_ci		regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32;
51562306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
51662306a36Sopenharmony_ci		regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/* Unblock some timings */
51962306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_53] = 0;
52062306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_54] = 0;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	/* 0x00 is disabled, 0x11 is lvds, 0x22 crt and 0x88 tmds */
52362306a36Sopenharmony_ci	if (lvds_output)
52462306a36Sopenharmony_ci		regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x11;
52562306a36Sopenharmony_ci	else if (tmds_output)
52662306a36Sopenharmony_ci		regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x88;
52762306a36Sopenharmony_ci	else
52862306a36Sopenharmony_ci		regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x22;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* These values seem to vary */
53162306a36Sopenharmony_ci	/* This register seems to be used by the bios to make certain decisions on some G70 cards? */
53262306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_SCRATCH4__INDEX] = savep->CRTC[NV_CIO_CRE_SCRATCH4__INDEX];
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	nv_crtc_set_digital_vibrance(crtc, nv_crtc->saturation);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	/* probably a scratch reg, but kept for cargo-cult purposes:
53762306a36Sopenharmony_ci	 * bit0: crtc0?, head A
53862306a36Sopenharmony_ci	 * bit6: lvds, head A
53962306a36Sopenharmony_ci	 * bit7: (only in X), head A
54062306a36Sopenharmony_ci	 */
54162306a36Sopenharmony_ci	if (nv_crtc->index == 0)
54262306a36Sopenharmony_ci		regp->CRTC[NV_CIO_CRE_4B] = savep->CRTC[NV_CIO_CRE_4B] | 0x80;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	/* The blob seems to take the current value from crtc 0, add 4 to that
54562306a36Sopenharmony_ci	 * and reuse the old value for crtc 1 */
54662306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] = nv04_display(dev)->saved_reg.crtc_reg[0].CRTC[NV_CIO_CRE_TVOUT_LATENCY];
54762306a36Sopenharmony_ci	if (!nv_crtc->index)
54862306a36Sopenharmony_ci		regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] += 4;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/* the blob sometimes sets |= 0x10 (which is the same as setting |=
55162306a36Sopenharmony_ci	 * 1 << 30 on 0x60.830), for no apparent reason */
55262306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_59] = off_chip_digital;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_RANKINE)
55562306a36Sopenharmony_ci		regp->CRTC[0x9f] = off_chip_digital ? 0x11 : 0x1;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	regp->crtc_830 = mode->crtc_vdisplay - 3;
55862306a36Sopenharmony_ci	regp->crtc_834 = mode->crtc_vdisplay - 1;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE)
56162306a36Sopenharmony_ci		/* This is what the blob does */
56262306a36Sopenharmony_ci		regp->crtc_850 = NVReadCRTC(dev, 0, NV_PCRTC_850);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_RANKINE)
56562306a36Sopenharmony_ci		regp->gpio_ext = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS)
56862306a36Sopenharmony_ci		regp->crtc_cfg = NV10_PCRTC_CONFIG_START_ADDRESS_HSYNC;
56962306a36Sopenharmony_ci	else
57062306a36Sopenharmony_ci		regp->crtc_cfg = NV04_PCRTC_CONFIG_START_ADDRESS_HSYNC;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* Some misc regs */
57362306a36Sopenharmony_ci	if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) {
57462306a36Sopenharmony_ci		regp->CRTC[NV_CIO_CRE_85] = 0xFF;
57562306a36Sopenharmony_ci		regp->CRTC[NV_CIO_CRE_86] = 0x1;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (fb->format->depth + 1) / 8;
57962306a36Sopenharmony_ci	/* Enable slaved mode (called MODE_TV in nv4ref.h) */
58062306a36Sopenharmony_ci	if (lvds_output || tmds_output || tv_output)
58162306a36Sopenharmony_ci		regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (1 << 7);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/* Generic PRAMDAC regs */
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS)
58662306a36Sopenharmony_ci		/* Only bit that bios and blob set. */
58762306a36Sopenharmony_ci		regp->nv10_cursync = (1 << 25);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	regp->ramdac_gen_ctrl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS |
59062306a36Sopenharmony_ci				NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL |
59162306a36Sopenharmony_ci				NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON;
59262306a36Sopenharmony_ci	if (fb->format->depth == 16)
59362306a36Sopenharmony_ci		regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
59462306a36Sopenharmony_ci	if (drm->client.device.info.chipset >= 0x11)
59562306a36Sopenharmony_ci		regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	regp->ramdac_630 = 0; /* turn off green mode (tv test pattern?) */
59862306a36Sopenharmony_ci	regp->tv_setup = 0;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	nv_crtc_set_image_sharpening(crtc, nv_crtc->sharpness);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	/* Some values the blob sets */
60362306a36Sopenharmony_ci	regp->ramdac_8c0 = 0x100;
60462306a36Sopenharmony_ci	regp->ramdac_a20 = 0x0;
60562306a36Sopenharmony_ci	regp->ramdac_a24 = 0xfffff;
60662306a36Sopenharmony_ci	regp->ramdac_a34 = 0x1;
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic int
61062306a36Sopenharmony_cinv_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct nv04_display *disp = nv04_display(crtc->dev);
61362306a36Sopenharmony_ci	struct drm_framebuffer *fb = crtc->primary->fb;
61462306a36Sopenharmony_ci	struct nouveau_bo *nvbo = nouveau_gem_object(fb->obj[0]);
61562306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
61662306a36Sopenharmony_ci	int ret;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, false);
61962306a36Sopenharmony_ci	if (ret == 0) {
62062306a36Sopenharmony_ci		if (disp->image[nv_crtc->index])
62162306a36Sopenharmony_ci			nouveau_bo_unpin(disp->image[nv_crtc->index]);
62262306a36Sopenharmony_ci		nouveau_bo_ref(nvbo, &disp->image[nv_crtc->index]);
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	return ret;
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci/**
62962306a36Sopenharmony_ci * Sets up registers for the given mode/adjusted_mode pair.
63062306a36Sopenharmony_ci *
63162306a36Sopenharmony_ci * The clocks, CRTCs and outputs attached to this CRTC must be off.
63262306a36Sopenharmony_ci *
63362306a36Sopenharmony_ci * This shouldn't enable any clocks, CRTCs, or outputs, but they should
63462306a36Sopenharmony_ci * be easily turned on/off after this.
63562306a36Sopenharmony_ci */
63662306a36Sopenharmony_cistatic int
63762306a36Sopenharmony_cinv_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
63862306a36Sopenharmony_ci		 struct drm_display_mode *adjusted_mode,
63962306a36Sopenharmony_ci		 int x, int y, struct drm_framebuffer *old_fb)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
64262306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
64362306a36Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(dev);
64462306a36Sopenharmony_ci	int ret;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	NV_DEBUG(drm, "CTRC mode on CRTC %d:\n", nv_crtc->index);
64762306a36Sopenharmony_ci	drm_mode_debug_printmodeline(adjusted_mode);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	ret = nv_crtc_swap_fbs(crtc, old_fb);
65062306a36Sopenharmony_ci	if (ret)
65162306a36Sopenharmony_ci		return ret;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	/* unlock must come after turning off FP_TG_CONTROL in output_prepare */
65462306a36Sopenharmony_ci	nv_lock_vga_crtc_shadow(dev, nv_crtc->index, -1);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	nv_crtc_mode_set_vga(crtc, adjusted_mode);
65762306a36Sopenharmony_ci	/* calculated in nv04_dfp_prepare, nv40 needs it written before calculating PLLs */
65862306a36Sopenharmony_ci	if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE)
65962306a36Sopenharmony_ci		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, nv04_display(dev)->mode_reg.sel_clk);
66062306a36Sopenharmony_ci	nv_crtc_mode_set_regs(crtc, adjusted_mode);
66162306a36Sopenharmony_ci	nv_crtc_calc_state_ext(crtc, mode, adjusted_mode->clock);
66262306a36Sopenharmony_ci	return 0;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic void nv_crtc_save(struct drm_crtc *crtc)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
66862306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
66962306a36Sopenharmony_ci	struct nv04_mode_state *state = &nv04_display(dev)->mode_reg;
67062306a36Sopenharmony_ci	struct nv04_crtc_reg *crtc_state = &state->crtc_reg[nv_crtc->index];
67162306a36Sopenharmony_ci	struct nv04_mode_state *saved = &nv04_display(dev)->saved_reg;
67262306a36Sopenharmony_ci	struct nv04_crtc_reg *crtc_saved = &saved->crtc_reg[nv_crtc->index];
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	if (nv_two_heads(crtc->dev))
67562306a36Sopenharmony_ci		NVSetOwner(crtc->dev, nv_crtc->index);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	nouveau_hw_save_state(crtc->dev, nv_crtc->index, saved);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	/* init some state to saved value */
68062306a36Sopenharmony_ci	state->sel_clk = saved->sel_clk & ~(0x5 << 16);
68162306a36Sopenharmony_ci	crtc_state->CRTC[NV_CIO_CRE_LCD__INDEX] = crtc_saved->CRTC[NV_CIO_CRE_LCD__INDEX];
68262306a36Sopenharmony_ci	state->pllsel = saved->pllsel & ~(PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK);
68362306a36Sopenharmony_ci	crtc_state->gpio_ext = crtc_saved->gpio_ext;
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic void nv_crtc_restore(struct drm_crtc *crtc)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
68962306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
69062306a36Sopenharmony_ci	int head = nv_crtc->index;
69162306a36Sopenharmony_ci	uint8_t saved_cr21 = nv04_display(dev)->saved_reg.crtc_reg[head].CRTC[NV_CIO_CRE_21];
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (nv_two_heads(crtc->dev))
69462306a36Sopenharmony_ci		NVSetOwner(crtc->dev, head);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	nouveau_hw_load_state(crtc->dev, head, &nv04_display(dev)->saved_reg);
69762306a36Sopenharmony_ci	nv_lock_vga_crtc_shadow(crtc->dev, head, saved_cr21);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	nv_crtc->last_dpms = NV_DPMS_CLEARED;
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistatic void nv_crtc_prepare(struct drm_crtc *crtc)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
70562306a36Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(dev);
70662306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
70762306a36Sopenharmony_ci	const struct drm_crtc_helper_funcs *funcs = crtc->helper_private;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	if (nv_two_heads(dev))
71062306a36Sopenharmony_ci		NVSetOwner(dev, nv_crtc->index);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	drm_crtc_vblank_off(crtc);
71362306a36Sopenharmony_ci	funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	NVBlankScreen(dev, nv_crtc->index, true);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	/* Some more preparation. */
71862306a36Sopenharmony_ci	NVWriteCRTC(dev, nv_crtc->index, NV_PCRTC_CONFIG, NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA);
71962306a36Sopenharmony_ci	if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) {
72062306a36Sopenharmony_ci		uint32_t reg900 = NVReadRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900);
72162306a36Sopenharmony_ci		NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900, reg900 & ~0x10000);
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_cistatic void nv_crtc_commit(struct drm_crtc *crtc)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
72862306a36Sopenharmony_ci	const struct drm_crtc_helper_funcs *funcs = crtc->helper_private;
72962306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	nouveau_hw_load_state(dev, nv_crtc->index, &nv04_display(dev)->mode_reg);
73262306a36Sopenharmony_ci	nv04_crtc_mode_set_base(crtc, crtc->x, crtc->y, NULL);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
73562306a36Sopenharmony_ci	/* turn on LFB swapping */
73662306a36Sopenharmony_ci	{
73762306a36Sopenharmony_ci		uint8_t tmp = NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR);
73862306a36Sopenharmony_ci		tmp |= MASK(NV_CIO_CRE_RCR_ENDIAN_BIG);
73962306a36Sopenharmony_ci		NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR, tmp);
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_ci#endif
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	funcs->dpms(crtc, DRM_MODE_DPMS_ON);
74462306a36Sopenharmony_ci	drm_crtc_vblank_on(crtc);
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_cistatic void nv_crtc_destroy(struct drm_crtc *crtc)
74862306a36Sopenharmony_ci{
74962306a36Sopenharmony_ci	struct nv04_display *disp = nv04_display(crtc->dev);
75062306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	if (!nv_crtc)
75362306a36Sopenharmony_ci		return;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	drm_crtc_cleanup(crtc);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	if (disp->image[nv_crtc->index])
75862306a36Sopenharmony_ci		nouveau_bo_unpin(disp->image[nv_crtc->index]);
75962306a36Sopenharmony_ci	nouveau_bo_ref(NULL, &disp->image[nv_crtc->index]);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	nouveau_bo_unmap(nv_crtc->cursor.nvbo);
76262306a36Sopenharmony_ci	nouveau_bo_unpin(nv_crtc->cursor.nvbo);
76362306a36Sopenharmony_ci	nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
76462306a36Sopenharmony_ci	nvif_event_dtor(&nv_crtc->vblank);
76562306a36Sopenharmony_ci	nvif_head_dtor(&nv_crtc->head);
76662306a36Sopenharmony_ci	kfree(nv_crtc);
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic void
77062306a36Sopenharmony_cinv_crtc_gamma_load(struct drm_crtc *crtc)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
77362306a36Sopenharmony_ci	struct drm_device *dev = nv_crtc->base.dev;
77462306a36Sopenharmony_ci	struct rgb { uint8_t r, g, b; } __attribute__((packed)) *rgbs;
77562306a36Sopenharmony_ci	u16 *r, *g, *b;
77662306a36Sopenharmony_ci	int i;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	rgbs = (struct rgb *)nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index].DAC;
77962306a36Sopenharmony_ci	r = crtc->gamma_store;
78062306a36Sopenharmony_ci	g = r + crtc->gamma_size;
78162306a36Sopenharmony_ci	b = g + crtc->gamma_size;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	for (i = 0; i < 256; i++) {
78462306a36Sopenharmony_ci		rgbs[i].r = *r++ >> 8;
78562306a36Sopenharmony_ci		rgbs[i].g = *g++ >> 8;
78662306a36Sopenharmony_ci		rgbs[i].b = *b++ >> 8;
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	nouveau_hw_load_state_palette(dev, nv_crtc->index, &nv04_display(dev)->mode_reg);
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_cistatic void
79362306a36Sopenharmony_cinv_crtc_disable(struct drm_crtc *crtc)
79462306a36Sopenharmony_ci{
79562306a36Sopenharmony_ci	struct nv04_display *disp = nv04_display(crtc->dev);
79662306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
79762306a36Sopenharmony_ci	if (disp->image[nv_crtc->index])
79862306a36Sopenharmony_ci		nouveau_bo_unpin(disp->image[nv_crtc->index]);
79962306a36Sopenharmony_ci	nouveau_bo_ref(NULL, &disp->image[nv_crtc->index]);
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic int
80362306a36Sopenharmony_cinv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
80462306a36Sopenharmony_ci		  uint32_t size,
80562306a36Sopenharmony_ci		  struct drm_modeset_acquire_ctx *ctx)
80662306a36Sopenharmony_ci{
80762306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	/* We need to know the depth before we upload, but it's possible to
81062306a36Sopenharmony_ci	 * get called before a framebuffer is bound.  If this is the case,
81162306a36Sopenharmony_ci	 * mark the lut values as dirty by setting depth==0, and it'll be
81262306a36Sopenharmony_ci	 * uploaded on the first mode_set_base()
81362306a36Sopenharmony_ci	 */
81462306a36Sopenharmony_ci	if (!nv_crtc->base.primary->fb) {
81562306a36Sopenharmony_ci		nv_crtc->lut.depth = 0;
81662306a36Sopenharmony_ci		return 0;
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	nv_crtc_gamma_load(crtc);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	return 0;
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic int
82562306a36Sopenharmony_cinv04_crtc_do_mode_set_base(struct drm_crtc *crtc,
82662306a36Sopenharmony_ci			   struct drm_framebuffer *passed_fb,
82762306a36Sopenharmony_ci			   int x, int y, bool atomic)
82862306a36Sopenharmony_ci{
82962306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
83062306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
83162306a36Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(dev);
83262306a36Sopenharmony_ci	struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index];
83362306a36Sopenharmony_ci	struct nouveau_bo *nvbo;
83462306a36Sopenharmony_ci	struct drm_framebuffer *drm_fb;
83562306a36Sopenharmony_ci	int arb_burst, arb_lwm;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	NV_DEBUG(drm, "index %d\n", nv_crtc->index);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	/* no fb bound */
84062306a36Sopenharmony_ci	if (!atomic && !crtc->primary->fb) {
84162306a36Sopenharmony_ci		NV_DEBUG(drm, "No FB bound\n");
84262306a36Sopenharmony_ci		return 0;
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	/* If atomic, we want to switch to the fb we were passed, so
84662306a36Sopenharmony_ci	 * now we update pointers to do that.
84762306a36Sopenharmony_ci	 */
84862306a36Sopenharmony_ci	if (atomic) {
84962306a36Sopenharmony_ci		drm_fb = passed_fb;
85062306a36Sopenharmony_ci	} else {
85162306a36Sopenharmony_ci		drm_fb = crtc->primary->fb;
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	nvbo = nouveau_gem_object(drm_fb->obj[0]);
85562306a36Sopenharmony_ci	nv_crtc->fb.offset = nvbo->offset;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	if (nv_crtc->lut.depth != drm_fb->format->depth) {
85862306a36Sopenharmony_ci		nv_crtc->lut.depth = drm_fb->format->depth;
85962306a36Sopenharmony_ci		nv_crtc_gamma_load(crtc);
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	/* Update the framebuffer format. */
86362306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] &= ~3;
86462306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (drm_fb->format->depth + 1) / 8;
86562306a36Sopenharmony_ci	regp->ramdac_gen_ctrl &= ~NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
86662306a36Sopenharmony_ci	if (drm_fb->format->depth == 16)
86762306a36Sopenharmony_ci		regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
86862306a36Sopenharmony_ci	crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_PIXEL_INDEX);
86962306a36Sopenharmony_ci	NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_GENERAL_CONTROL,
87062306a36Sopenharmony_ci		      regp->ramdac_gen_ctrl);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = drm_fb->pitches[0] >> 3;
87362306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_RPC0_INDEX] =
87462306a36Sopenharmony_ci		XLATE(drm_fb->pitches[0] >> 3, 8, NV_CIO_CRE_RPC0_OFFSET_10_8);
87562306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_42] =
87662306a36Sopenharmony_ci		XLATE(drm_fb->pitches[0] / 8, 11, NV_CIO_CRE_42_OFFSET_11);
87762306a36Sopenharmony_ci	crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_RPC0_INDEX);
87862306a36Sopenharmony_ci	crtc_wr_cio_state(crtc, regp, NV_CIO_CR_OFFSET_INDEX);
87962306a36Sopenharmony_ci	crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_42);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	/* Update the framebuffer location. */
88262306a36Sopenharmony_ci	regp->fb_start = nv_crtc->fb.offset & ~3;
88362306a36Sopenharmony_ci	regp->fb_start += (y * drm_fb->pitches[0]) + (x * drm_fb->format->cpp[0]);
88462306a36Sopenharmony_ci	nv_set_crtc_base(dev, nv_crtc->index, regp->fb_start);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	/* Update the arbitration parameters. */
88762306a36Sopenharmony_ci	nouveau_calc_arb(dev, crtc->mode.clock, drm_fb->format->cpp[0] * 8,
88862306a36Sopenharmony_ci			 &arb_burst, &arb_lwm);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_FF_INDEX] = arb_burst;
89162306a36Sopenharmony_ci	regp->CRTC[NV_CIO_CRE_FFLWM__INDEX] = arb_lwm & 0xff;
89262306a36Sopenharmony_ci	crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FF_INDEX);
89362306a36Sopenharmony_ci	crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FFLWM__INDEX);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KELVIN) {
89662306a36Sopenharmony_ci		regp->CRTC[NV_CIO_CRE_47] = arb_lwm >> 8;
89762306a36Sopenharmony_ci		crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_47);
89862306a36Sopenharmony_ci	}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	return 0;
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic int
90462306a36Sopenharmony_cinv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
90562306a36Sopenharmony_ci			struct drm_framebuffer *old_fb)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	int ret = nv_crtc_swap_fbs(crtc, old_fb);
90862306a36Sopenharmony_ci	if (ret)
90962306a36Sopenharmony_ci		return ret;
91062306a36Sopenharmony_ci	return nv04_crtc_do_mode_set_base(crtc, old_fb, x, y, false);
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cistatic int
91462306a36Sopenharmony_cinv04_crtc_mode_set_base_atomic(struct drm_crtc *crtc,
91562306a36Sopenharmony_ci			       struct drm_framebuffer *fb,
91662306a36Sopenharmony_ci			       int x, int y, enum mode_set_atomic state)
91762306a36Sopenharmony_ci{
91862306a36Sopenharmony_ci	return nv04_crtc_do_mode_set_base(crtc, fb, x, y, true);
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_cistatic void nv04_cursor_upload(struct drm_device *dev, struct nouveau_bo *src,
92262306a36Sopenharmony_ci			       struct nouveau_bo *dst)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	int width = nv_cursor_width(dev);
92562306a36Sopenharmony_ci	uint32_t pixel;
92662306a36Sopenharmony_ci	int i, j;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	for (i = 0; i < width; i++) {
92962306a36Sopenharmony_ci		for (j = 0; j < width; j++) {
93062306a36Sopenharmony_ci			pixel = nouveau_bo_rd32(src, i*64 + j);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci			nouveau_bo_wr16(dst, i*width + j, (pixel & 0x80000000) >> 16
93362306a36Sopenharmony_ci				     | (pixel & 0xf80000) >> 9
93462306a36Sopenharmony_ci				     | (pixel & 0xf800) >> 6
93562306a36Sopenharmony_ci				     | (pixel & 0xf8) >> 3);
93662306a36Sopenharmony_ci		}
93762306a36Sopenharmony_ci	}
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_cistatic void nv11_cursor_upload(struct drm_device *dev, struct nouveau_bo *src,
94162306a36Sopenharmony_ci			       struct nouveau_bo *dst)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci	uint32_t pixel;
94462306a36Sopenharmony_ci	int alpha, i;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	/* nv11+ supports premultiplied (PM), or non-premultiplied (NPM) alpha
94762306a36Sopenharmony_ci	 * cursors (though NPM in combination with fp dithering may not work on
94862306a36Sopenharmony_ci	 * nv11, from "nv" driver history)
94962306a36Sopenharmony_ci	 * NPM mode needs NV_PCRTC_CURSOR_CONFIG_ALPHA_BLEND set and is what the
95062306a36Sopenharmony_ci	 * blob uses, however we get given PM cursors so we use PM mode
95162306a36Sopenharmony_ci	 */
95262306a36Sopenharmony_ci	for (i = 0; i < 64 * 64; i++) {
95362306a36Sopenharmony_ci		pixel = nouveau_bo_rd32(src, i);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci		/* hw gets unhappy if alpha <= rgb values.  for a PM image "less
95662306a36Sopenharmony_ci		 * than" shouldn't happen; fix "equal to" case by adding one to
95762306a36Sopenharmony_ci		 * alpha channel (slightly inaccurate, but so is attempting to
95862306a36Sopenharmony_ci		 * get back to NPM images, due to limits of integer precision)
95962306a36Sopenharmony_ci		 */
96062306a36Sopenharmony_ci		alpha = pixel >> 24;
96162306a36Sopenharmony_ci		if (alpha > 0 && alpha < 255)
96262306a36Sopenharmony_ci			pixel = (pixel & 0x00ffffff) | ((alpha + 1) << 24);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
96562306a36Sopenharmony_ci		{
96662306a36Sopenharmony_ci			struct nouveau_drm *drm = nouveau_drm(dev);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci			if (drm->client.device.info.chipset == 0x11) {
96962306a36Sopenharmony_ci				pixel = ((pixel & 0x000000ff) << 24) |
97062306a36Sopenharmony_ci					((pixel & 0x0000ff00) << 8) |
97162306a36Sopenharmony_ci					((pixel & 0x00ff0000) >> 8) |
97262306a36Sopenharmony_ci					((pixel & 0xff000000) >> 24);
97362306a36Sopenharmony_ci			}
97462306a36Sopenharmony_ci		}
97562306a36Sopenharmony_ci#endif
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci		nouveau_bo_wr32(dst, i, pixel);
97862306a36Sopenharmony_ci	}
97962306a36Sopenharmony_ci}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_cistatic int
98262306a36Sopenharmony_cinv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
98362306a36Sopenharmony_ci		     uint32_t buffer_handle, uint32_t width, uint32_t height)
98462306a36Sopenharmony_ci{
98562306a36Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(crtc->dev);
98662306a36Sopenharmony_ci	struct drm_device *dev = drm->dev;
98762306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
98862306a36Sopenharmony_ci	struct nouveau_bo *cursor = NULL;
98962306a36Sopenharmony_ci	struct drm_gem_object *gem;
99062306a36Sopenharmony_ci	int ret = 0;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	if (!buffer_handle) {
99362306a36Sopenharmony_ci		nv_crtc->cursor.hide(nv_crtc, true);
99462306a36Sopenharmony_ci		return 0;
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	if (width != 64 || height != 64)
99862306a36Sopenharmony_ci		return -EINVAL;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	gem = drm_gem_object_lookup(file_priv, buffer_handle);
100162306a36Sopenharmony_ci	if (!gem)
100262306a36Sopenharmony_ci		return -ENOENT;
100362306a36Sopenharmony_ci	cursor = nouveau_gem_object(gem);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	ret = nouveau_bo_map(cursor);
100662306a36Sopenharmony_ci	if (ret)
100762306a36Sopenharmony_ci		goto out;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	if (drm->client.device.info.chipset >= 0x11)
101062306a36Sopenharmony_ci		nv11_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo);
101162306a36Sopenharmony_ci	else
101262306a36Sopenharmony_ci		nv04_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	nouveau_bo_unmap(cursor);
101562306a36Sopenharmony_ci	nv_crtc->cursor.offset = nv_crtc->cursor.nvbo->offset;
101662306a36Sopenharmony_ci	nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset);
101762306a36Sopenharmony_ci	nv_crtc->cursor.show(nv_crtc, true);
101862306a36Sopenharmony_ciout:
101962306a36Sopenharmony_ci	drm_gem_object_put(gem);
102062306a36Sopenharmony_ci	return ret;
102162306a36Sopenharmony_ci}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_cistatic int
102462306a36Sopenharmony_cinv04_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
102562306a36Sopenharmony_ci{
102662306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	nv_crtc->cursor.set_pos(nv_crtc, x, y);
102962306a36Sopenharmony_ci	return 0;
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_cistruct nv04_page_flip_state {
103362306a36Sopenharmony_ci	struct list_head head;
103462306a36Sopenharmony_ci	struct drm_pending_vblank_event *event;
103562306a36Sopenharmony_ci	struct drm_crtc *crtc;
103662306a36Sopenharmony_ci	int bpp, pitch;
103762306a36Sopenharmony_ci	u64 offset;
103862306a36Sopenharmony_ci};
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_cistatic int
104162306a36Sopenharmony_cinv04_finish_page_flip(struct nouveau_channel *chan,
104262306a36Sopenharmony_ci		      struct nv04_page_flip_state *ps)
104362306a36Sopenharmony_ci{
104462306a36Sopenharmony_ci	struct nouveau_fence_chan *fctx = chan->fence;
104562306a36Sopenharmony_ci	struct nouveau_drm *drm = chan->drm;
104662306a36Sopenharmony_ci	struct drm_device *dev = drm->dev;
104762306a36Sopenharmony_ci	struct nv04_page_flip_state *s;
104862306a36Sopenharmony_ci	unsigned long flags;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	spin_lock_irqsave(&dev->event_lock, flags);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	if (list_empty(&fctx->flip)) {
105362306a36Sopenharmony_ci		NV_ERROR(drm, "unexpected pageflip\n");
105462306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->event_lock, flags);
105562306a36Sopenharmony_ci		return -EINVAL;
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	s = list_first_entry(&fctx->flip, struct nv04_page_flip_state, head);
105962306a36Sopenharmony_ci	if (s->event) {
106062306a36Sopenharmony_ci		drm_crtc_arm_vblank_event(s->crtc, s->event);
106162306a36Sopenharmony_ci	} else {
106262306a36Sopenharmony_ci		/* Give up ownership of vblank for page-flipped crtc */
106362306a36Sopenharmony_ci		drm_crtc_vblank_put(s->crtc);
106462306a36Sopenharmony_ci	}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	list_del(&s->head);
106762306a36Sopenharmony_ci	if (ps)
106862306a36Sopenharmony_ci		*ps = *s;
106962306a36Sopenharmony_ci	kfree(s);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->event_lock, flags);
107262306a36Sopenharmony_ci	return 0;
107362306a36Sopenharmony_ci}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ciint
107662306a36Sopenharmony_cinv04_flip_complete(struct nvif_event *event, void *argv, u32 argc)
107762306a36Sopenharmony_ci{
107862306a36Sopenharmony_ci	struct nv04_display *disp = container_of(event, typeof(*disp), flip);
107962306a36Sopenharmony_ci	struct nouveau_drm *drm = disp->drm;
108062306a36Sopenharmony_ci	struct nouveau_channel *chan = drm->channel;
108162306a36Sopenharmony_ci	struct nv04_page_flip_state state;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	if (!nv04_finish_page_flip(chan, &state)) {
108462306a36Sopenharmony_ci		nv_set_crtc_base(drm->dev, drm_crtc_index(state.crtc),
108562306a36Sopenharmony_ci				 state.offset + state.crtc->y *
108662306a36Sopenharmony_ci				 state.pitch + state.crtc->x *
108762306a36Sopenharmony_ci				 state.bpp / 8);
108862306a36Sopenharmony_ci	}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	return NVIF_EVENT_KEEP;
109162306a36Sopenharmony_ci}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_cistatic int
109462306a36Sopenharmony_cinv04_page_flip_emit(struct nouveau_channel *chan,
109562306a36Sopenharmony_ci		    struct nouveau_bo *old_bo,
109662306a36Sopenharmony_ci		    struct nouveau_bo *new_bo,
109762306a36Sopenharmony_ci		    struct nv04_page_flip_state *s,
109862306a36Sopenharmony_ci		    struct nouveau_fence **pfence)
109962306a36Sopenharmony_ci{
110062306a36Sopenharmony_ci	struct nouveau_fence_chan *fctx = chan->fence;
110162306a36Sopenharmony_ci	struct nouveau_drm *drm = chan->drm;
110262306a36Sopenharmony_ci	struct drm_device *dev = drm->dev;
110362306a36Sopenharmony_ci	struct nvif_push *push = chan->chan.push;
110462306a36Sopenharmony_ci	unsigned long flags;
110562306a36Sopenharmony_ci	int ret;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	/* Queue it to the pending list */
110862306a36Sopenharmony_ci	spin_lock_irqsave(&dev->event_lock, flags);
110962306a36Sopenharmony_ci	list_add_tail(&s->head, &fctx->flip);
111062306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->event_lock, flags);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	/* Synchronize with the old framebuffer */
111362306a36Sopenharmony_ci	ret = nouveau_fence_sync(old_bo, chan, false, false);
111462306a36Sopenharmony_ci	if (ret)
111562306a36Sopenharmony_ci		goto fail;
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	/* Emit the pageflip */
111862306a36Sopenharmony_ci	ret = PUSH_WAIT(push, 2);
111962306a36Sopenharmony_ci	if (ret)
112062306a36Sopenharmony_ci		goto fail;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	PUSH_NVSQ(push, NV_SW, NV_SW_PAGE_FLIP, 0x00000000);
112362306a36Sopenharmony_ci	PUSH_KICK(push);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	ret = nouveau_fence_new(pfence, chan);
112662306a36Sopenharmony_ci	if (ret)
112762306a36Sopenharmony_ci		goto fail;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	return 0;
113062306a36Sopenharmony_cifail:
113162306a36Sopenharmony_ci	spin_lock_irqsave(&dev->event_lock, flags);
113262306a36Sopenharmony_ci	list_del(&s->head);
113362306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->event_lock, flags);
113462306a36Sopenharmony_ci	return ret;
113562306a36Sopenharmony_ci}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_cistatic int
113862306a36Sopenharmony_cinv04_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
113962306a36Sopenharmony_ci		    struct drm_pending_vblank_event *event, u32 flags,
114062306a36Sopenharmony_ci		    struct drm_modeset_acquire_ctx *ctx)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci	const int swap_interval = (flags & DRM_MODE_PAGE_FLIP_ASYNC) ? 0 : 1;
114362306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
114462306a36Sopenharmony_ci	struct nouveau_drm *drm = nouveau_drm(dev);
114562306a36Sopenharmony_ci	struct drm_framebuffer *old_fb = crtc->primary->fb;
114662306a36Sopenharmony_ci	struct nouveau_bo *old_bo = nouveau_gem_object(old_fb->obj[0]);
114762306a36Sopenharmony_ci	struct nouveau_bo *new_bo = nouveau_gem_object(fb->obj[0]);
114862306a36Sopenharmony_ci	struct nv04_page_flip_state *s;
114962306a36Sopenharmony_ci	struct nouveau_channel *chan;
115062306a36Sopenharmony_ci	struct nouveau_cli *cli;
115162306a36Sopenharmony_ci	struct nouveau_fence *fence;
115262306a36Sopenharmony_ci	struct nv04_display *dispnv04 = nv04_display(dev);
115362306a36Sopenharmony_ci	struct nvif_push *push;
115462306a36Sopenharmony_ci	int head = nouveau_crtc(crtc)->index;
115562306a36Sopenharmony_ci	int ret;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	chan = drm->channel;
115862306a36Sopenharmony_ci	if (!chan)
115962306a36Sopenharmony_ci		return -ENODEV;
116062306a36Sopenharmony_ci	cli = (void *)chan->user.client;
116162306a36Sopenharmony_ci	push = chan->chan.push;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	s = kzalloc(sizeof(*s), GFP_KERNEL);
116462306a36Sopenharmony_ci	if (!s)
116562306a36Sopenharmony_ci		return -ENOMEM;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	if (new_bo != old_bo) {
116862306a36Sopenharmony_ci		ret = nouveau_bo_pin(new_bo, NOUVEAU_GEM_DOMAIN_VRAM, true);
116962306a36Sopenharmony_ci		if (ret)
117062306a36Sopenharmony_ci			goto fail_free;
117162306a36Sopenharmony_ci	}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	mutex_lock(&cli->mutex);
117462306a36Sopenharmony_ci	ret = ttm_bo_reserve(&new_bo->bo, true, false, NULL);
117562306a36Sopenharmony_ci	if (ret)
117662306a36Sopenharmony_ci		goto fail_unpin;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	/* synchronise rendering channel with the kernel's channel */
117962306a36Sopenharmony_ci	ret = nouveau_fence_sync(new_bo, chan, false, true);
118062306a36Sopenharmony_ci	if (ret) {
118162306a36Sopenharmony_ci		ttm_bo_unreserve(&new_bo->bo);
118262306a36Sopenharmony_ci		goto fail_unpin;
118362306a36Sopenharmony_ci	}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	if (new_bo != old_bo) {
118662306a36Sopenharmony_ci		ttm_bo_unreserve(&new_bo->bo);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		ret = ttm_bo_reserve(&old_bo->bo, true, false, NULL);
118962306a36Sopenharmony_ci		if (ret)
119062306a36Sopenharmony_ci			goto fail_unpin;
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	/* Initialize a page flip struct */
119462306a36Sopenharmony_ci	*s = (struct nv04_page_flip_state)
119562306a36Sopenharmony_ci		{ { }, event, crtc, fb->format->cpp[0] * 8, fb->pitches[0],
119662306a36Sopenharmony_ci		  new_bo->offset };
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	/* Keep vblanks on during flip, for the target crtc of this flip */
119962306a36Sopenharmony_ci	drm_crtc_vblank_get(crtc);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	/* Emit a page flip */
120262306a36Sopenharmony_ci	if (swap_interval) {
120362306a36Sopenharmony_ci		ret = PUSH_WAIT(push, 8);
120462306a36Sopenharmony_ci		if (ret)
120562306a36Sopenharmony_ci			goto fail_unreserve;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci		PUSH_NVSQ(push, NV05F, 0x012c, 0);
120862306a36Sopenharmony_ci		PUSH_NVSQ(push, NV05F, 0x0134, head);
120962306a36Sopenharmony_ci		PUSH_NVSQ(push, NV05F, 0x0100, 0);
121062306a36Sopenharmony_ci		PUSH_NVSQ(push, NV05F, 0x0130, 0);
121162306a36Sopenharmony_ci	}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	nouveau_bo_ref(new_bo, &dispnv04->image[head]);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	ret = nv04_page_flip_emit(chan, old_bo, new_bo, s, &fence);
121662306a36Sopenharmony_ci	if (ret)
121762306a36Sopenharmony_ci		goto fail_unreserve;
121862306a36Sopenharmony_ci	mutex_unlock(&cli->mutex);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	/* Update the crtc struct and cleanup */
122162306a36Sopenharmony_ci	crtc->primary->fb = fb;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	nouveau_bo_fence(old_bo, fence, false);
122462306a36Sopenharmony_ci	ttm_bo_unreserve(&old_bo->bo);
122562306a36Sopenharmony_ci	if (old_bo != new_bo)
122662306a36Sopenharmony_ci		nouveau_bo_unpin(old_bo);
122762306a36Sopenharmony_ci	nouveau_fence_unref(&fence);
122862306a36Sopenharmony_ci	return 0;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_cifail_unreserve:
123162306a36Sopenharmony_ci	drm_crtc_vblank_put(crtc);
123262306a36Sopenharmony_ci	ttm_bo_unreserve(&old_bo->bo);
123362306a36Sopenharmony_cifail_unpin:
123462306a36Sopenharmony_ci	mutex_unlock(&cli->mutex);
123562306a36Sopenharmony_ci	if (old_bo != new_bo)
123662306a36Sopenharmony_ci		nouveau_bo_unpin(new_bo);
123762306a36Sopenharmony_cifail_free:
123862306a36Sopenharmony_ci	kfree(s);
123962306a36Sopenharmony_ci	return ret;
124062306a36Sopenharmony_ci}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_cistatic const struct drm_crtc_funcs nv04_crtc_funcs = {
124362306a36Sopenharmony_ci	.cursor_set = nv04_crtc_cursor_set,
124462306a36Sopenharmony_ci	.cursor_move = nv04_crtc_cursor_move,
124562306a36Sopenharmony_ci	.gamma_set = nv_crtc_gamma_set,
124662306a36Sopenharmony_ci	.set_config = drm_crtc_helper_set_config,
124762306a36Sopenharmony_ci	.page_flip = nv04_crtc_page_flip,
124862306a36Sopenharmony_ci	.destroy = nv_crtc_destroy,
124962306a36Sopenharmony_ci	.enable_vblank = nouveau_display_vblank_enable,
125062306a36Sopenharmony_ci	.disable_vblank = nouveau_display_vblank_disable,
125162306a36Sopenharmony_ci	.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
125262306a36Sopenharmony_ci};
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = {
125562306a36Sopenharmony_ci	.dpms = nv_crtc_dpms,
125662306a36Sopenharmony_ci	.prepare = nv_crtc_prepare,
125762306a36Sopenharmony_ci	.commit = nv_crtc_commit,
125862306a36Sopenharmony_ci	.mode_set = nv_crtc_mode_set,
125962306a36Sopenharmony_ci	.mode_set_base = nv04_crtc_mode_set_base,
126062306a36Sopenharmony_ci	.mode_set_base_atomic = nv04_crtc_mode_set_base_atomic,
126162306a36Sopenharmony_ci	.disable = nv_crtc_disable,
126262306a36Sopenharmony_ci	.get_scanout_position = nouveau_display_scanoutpos,
126362306a36Sopenharmony_ci};
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_cistatic const uint32_t modeset_formats[] = {
126662306a36Sopenharmony_ci        DRM_FORMAT_XRGB8888,
126762306a36Sopenharmony_ci        DRM_FORMAT_RGB565,
126862306a36Sopenharmony_ci        DRM_FORMAT_XRGB1555,
126962306a36Sopenharmony_ci};
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_cistatic const struct drm_plane_funcs nv04_primary_plane_funcs = {
127262306a36Sopenharmony_ci	DRM_PLANE_NON_ATOMIC_FUNCS,
127362306a36Sopenharmony_ci};
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_cistatic int
127662306a36Sopenharmony_cinv04_crtc_vblank_handler(struct nvif_event *event, void *repv, u32 repc)
127762306a36Sopenharmony_ci{
127862306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc = container_of(event, struct nouveau_crtc, vblank);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	drm_crtc_handle_vblank(&nv_crtc->base);
128162306a36Sopenharmony_ci	return NVIF_EVENT_KEEP;
128262306a36Sopenharmony_ci}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ciint
128562306a36Sopenharmony_cinv04_crtc_create(struct drm_device *dev, int crtc_num)
128662306a36Sopenharmony_ci{
128762306a36Sopenharmony_ci	struct nouveau_display *disp = nouveau_display(dev);
128862306a36Sopenharmony_ci	struct nouveau_crtc *nv_crtc;
128962306a36Sopenharmony_ci	struct drm_plane *primary;
129062306a36Sopenharmony_ci	int ret;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL);
129362306a36Sopenharmony_ci	if (!nv_crtc)
129462306a36Sopenharmony_ci		return -ENOMEM;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	nv_crtc->lut.depth = 0;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	nv_crtc->index = crtc_num;
129962306a36Sopenharmony_ci	nv_crtc->last_dpms = NV_DPMS_CLEARED;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	nv_crtc->save = nv_crtc_save;
130262306a36Sopenharmony_ci	nv_crtc->restore = nv_crtc_restore;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	primary = __drm_universal_plane_alloc(dev, sizeof(*primary), 0, 0,
130562306a36Sopenharmony_ci					      &nv04_primary_plane_funcs,
130662306a36Sopenharmony_ci					      modeset_formats,
130762306a36Sopenharmony_ci					      ARRAY_SIZE(modeset_formats), NULL,
130862306a36Sopenharmony_ci					      DRM_PLANE_TYPE_PRIMARY, NULL);
130962306a36Sopenharmony_ci	if (IS_ERR(primary)) {
131062306a36Sopenharmony_ci		ret = PTR_ERR(primary);
131162306a36Sopenharmony_ci		kfree(nv_crtc);
131262306a36Sopenharmony_ci		return ret;
131362306a36Sopenharmony_ci	}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	drm_crtc_init_with_planes(dev, &nv_crtc->base, primary, NULL,
131662306a36Sopenharmony_ci                                  &nv04_crtc_funcs, NULL);
131762306a36Sopenharmony_ci	drm_crtc_helper_add(&nv_crtc->base, &nv04_crtc_helper_funcs);
131862306a36Sopenharmony_ci	drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256);
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	ret = nouveau_bo_new(&nouveau_drm(dev)->client, 64*64*4, 0x100,
132162306a36Sopenharmony_ci			     NOUVEAU_GEM_DOMAIN_VRAM, 0, 0x0000, NULL, NULL,
132262306a36Sopenharmony_ci			     &nv_crtc->cursor.nvbo);
132362306a36Sopenharmony_ci	if (!ret) {
132462306a36Sopenharmony_ci		ret = nouveau_bo_pin(nv_crtc->cursor.nvbo,
132562306a36Sopenharmony_ci				     NOUVEAU_GEM_DOMAIN_VRAM, false);
132662306a36Sopenharmony_ci		if (!ret) {
132762306a36Sopenharmony_ci			ret = nouveau_bo_map(nv_crtc->cursor.nvbo);
132862306a36Sopenharmony_ci			if (ret)
132962306a36Sopenharmony_ci				nouveau_bo_unpin(nv_crtc->cursor.nvbo);
133062306a36Sopenharmony_ci		}
133162306a36Sopenharmony_ci		if (ret)
133262306a36Sopenharmony_ci			nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
133362306a36Sopenharmony_ci	}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	nv04_cursor_init(nv_crtc);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	ret = nvif_head_ctor(&disp->disp, nv_crtc->base.name, nv_crtc->index, &nv_crtc->head);
133862306a36Sopenharmony_ci	if (ret)
133962306a36Sopenharmony_ci		return ret;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	return nvif_head_vblank_event_ctor(&nv_crtc->head, "kmsVbl", nv04_crtc_vblank_handler,
134262306a36Sopenharmony_ci					   false, &nv_crtc->vblank);
134362306a36Sopenharmony_ci}
1344