18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 1993-2003 NVIDIA, Corporation 38c2ecf20Sopenharmony_ci * Copyright 2006 Dave Airlie 48c2ecf20Sopenharmony_ci * Copyright 2007 Maarten Maathuis 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 78c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 88c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 98c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 108c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 118c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next 148c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 158c2ecf20Sopenharmony_ci * Software. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 188c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 198c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 208c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 218c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 228c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 238c2ecf20Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 268c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 278c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h> 288c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "nouveau_drv.h" 318c2ecf20Sopenharmony_ci#include "nouveau_reg.h" 328c2ecf20Sopenharmony_ci#include "nouveau_ttm.h" 338c2ecf20Sopenharmony_ci#include "nouveau_bo.h" 348c2ecf20Sopenharmony_ci#include "nouveau_gem.h" 358c2ecf20Sopenharmony_ci#include "nouveau_encoder.h" 368c2ecf20Sopenharmony_ci#include "nouveau_connector.h" 378c2ecf20Sopenharmony_ci#include "nouveau_crtc.h" 388c2ecf20Sopenharmony_ci#include "hw.h" 398c2ecf20Sopenharmony_ci#include "nvreg.h" 408c2ecf20Sopenharmony_ci#include "nouveau_fbcon.h" 418c2ecf20Sopenharmony_ci#include "disp.h" 428c2ecf20Sopenharmony_ci#include "nouveau_dma.h" 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#include <subdev/bios/pll.h> 458c2ecf20Sopenharmony_ci#include <subdev/clk.h> 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#include <nvif/push006c.h> 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#include <nvif/event.h> 508c2ecf20Sopenharmony_ci#include <nvif/cl0046.h> 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int 538c2ecf20Sopenharmony_cinv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, 548c2ecf20Sopenharmony_ci struct drm_framebuffer *old_fb); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic void 578c2ecf20Sopenharmony_cicrtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index, 608c2ecf20Sopenharmony_ci crtcstate->CRTC[index]); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic void nv_crtc_set_digital_vibrance(struct drm_crtc *crtc, int level) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 668c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 678c2ecf20Sopenharmony_ci struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_CSB] = nv_crtc->saturation = level; 708c2ecf20Sopenharmony_ci if (nv_crtc->saturation && nv_gf4_disp_arch(crtc->dev)) { 718c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_CSB] = 0x80; 728c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_5B] = nv_crtc->saturation << 2; 738c2ecf20Sopenharmony_ci crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_5B); 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_CSB); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void nv_crtc_set_image_sharpening(struct drm_crtc *crtc, int level) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 818c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 828c2ecf20Sopenharmony_ci struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci nv_crtc->sharpness = level; 858c2ecf20Sopenharmony_ci if (level < 0) /* blur is in hw range 0x3f -> 0x20 */ 868c2ecf20Sopenharmony_ci level += 0x40; 878c2ecf20Sopenharmony_ci regp->ramdac_634 = level; 888c2ecf20Sopenharmony_ci NVWriteRAMDAC(crtc->dev, nv_crtc->index, NV_PRAMDAC_634, regp->ramdac_634); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define PLLSEL_VPLL1_MASK \ 928c2ecf20Sopenharmony_ci (NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL \ 938c2ecf20Sopenharmony_ci | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2) 948c2ecf20Sopenharmony_ci#define PLLSEL_VPLL2_MASK \ 958c2ecf20Sopenharmony_ci (NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 \ 968c2ecf20Sopenharmony_ci | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2) 978c2ecf20Sopenharmony_ci#define PLLSEL_TV_MASK \ 988c2ecf20Sopenharmony_ci (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \ 998c2ecf20Sopenharmony_ci | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 \ 1008c2ecf20Sopenharmony_ci | NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \ 1018c2ecf20Sopenharmony_ci | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* NV4x 0x40.. pll notes: 1048c2ecf20Sopenharmony_ci * gpu pll: 0x4000 + 0x4004 1058c2ecf20Sopenharmony_ci * ?gpu? pll: 0x4008 + 0x400c 1068c2ecf20Sopenharmony_ci * vpll1: 0x4010 + 0x4014 1078c2ecf20Sopenharmony_ci * vpll2: 0x4018 + 0x401c 1088c2ecf20Sopenharmony_ci * mpll: 0x4020 + 0x4024 1098c2ecf20Sopenharmony_ci * mpll: 0x4038 + 0x403c 1108c2ecf20Sopenharmony_ci * 1118c2ecf20Sopenharmony_ci * the first register of each pair has some unknown details: 1128c2ecf20Sopenharmony_ci * bits 0-7: redirected values from elsewhere? (similar to PLL_SETUP_CONTROL?) 1138c2ecf20Sopenharmony_ci * bits 20-23: (mpll) something to do with post divider? 1148c2ecf20Sopenharmony_ci * bits 28-31: related to single stage mode? (bit 8/12) 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mode * mode, int dot_clock) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 1208c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 1218c2ecf20Sopenharmony_ci struct nvkm_bios *bios = nvxx_bios(&drm->client.device); 1228c2ecf20Sopenharmony_ci struct nvkm_clk *clk = nvxx_clk(&drm->client.device); 1238c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 1248c2ecf20Sopenharmony_ci struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; 1258c2ecf20Sopenharmony_ci struct nv04_crtc_reg *regp = &state->crtc_reg[nv_crtc->index]; 1268c2ecf20Sopenharmony_ci struct nvkm_pll_vals *pv = ®p->pllvals; 1278c2ecf20Sopenharmony_ci struct nvbios_pll pll_lim; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (nvbios_pll_parse(bios, nv_crtc->index ? PLL_VPLL1 : PLL_VPLL0, 1308c2ecf20Sopenharmony_ci &pll_lim)) 1318c2ecf20Sopenharmony_ci return; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* NM2 == 0 is used to determine single stage mode on two stage plls */ 1348c2ecf20Sopenharmony_ci pv->NM2 = 0; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* for newer nv4x the blob uses only the first stage of the vpll below a 1378c2ecf20Sopenharmony_ci * certain clock. for a certain nv4b this is 150MHz. since the max 1388c2ecf20Sopenharmony_ci * output frequency of the first stage for this card is 300MHz, it is 1398c2ecf20Sopenharmony_ci * assumed the threshold is given by vco1 maxfreq/2 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci /* for early nv4x, specifically nv40 and *some* nv43 (devids 0 and 6, 1428c2ecf20Sopenharmony_ci * not 8, others unknown), the blob always uses both plls. no problem 1438c2ecf20Sopenharmony_ci * has yet been observed in allowing the use a single stage pll on all 1448c2ecf20Sopenharmony_ci * nv43 however. the behaviour of single stage use is untested on nv40 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_ci if (drm->client.device.info.chipset > 0x40 && dot_clock <= (pll_lim.vco1.max_freq / 2)) 1478c2ecf20Sopenharmony_ci memset(&pll_lim.vco2, 0, sizeof(pll_lim.vco2)); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (!clk->pll_calc(clk, &pll_lim, dot_clock, pv)) 1518c2ecf20Sopenharmony_ci return; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci state->pllsel &= PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* The blob uses this always, so let's do the same */ 1568c2ecf20Sopenharmony_ci if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) 1578c2ecf20Sopenharmony_ci state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE; 1588c2ecf20Sopenharmony_ci /* again nv40 and some nv43 act more like nv3x as described above */ 1598c2ecf20Sopenharmony_ci if (drm->client.device.info.chipset < 0x41) 1608c2ecf20Sopenharmony_ci state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL | 1618c2ecf20Sopenharmony_ci NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL; 1628c2ecf20Sopenharmony_ci state->pllsel |= nv_crtc->index ? PLLSEL_VPLL2_MASK : PLLSEL_VPLL1_MASK; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (pv->NM2) 1658c2ecf20Sopenharmony_ci NV_DEBUG(drm, "vpll: n1 %d n2 %d m1 %d m2 %d log2p %d\n", 1668c2ecf20Sopenharmony_ci pv->N1, pv->N2, pv->M1, pv->M2, pv->log2P); 1678c2ecf20Sopenharmony_ci else 1688c2ecf20Sopenharmony_ci NV_DEBUG(drm, "vpll: n %d m %d log2p %d\n", 1698c2ecf20Sopenharmony_ci pv->N1, pv->M1, pv->log2P); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void 1758c2ecf20Sopenharmony_cinv_crtc_dpms(struct drm_crtc *crtc, int mode) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 1788c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 1798c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 1808c2ecf20Sopenharmony_ci unsigned char seq1 = 0, crtc17 = 0; 1818c2ecf20Sopenharmony_ci unsigned char crtc1A; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci NV_DEBUG(drm, "Setting dpms mode %d on CRTC %d\n", mode, 1848c2ecf20Sopenharmony_ci nv_crtc->index); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (nv_crtc->last_dpms == mode) /* Don't do unnecessary mode changes. */ 1878c2ecf20Sopenharmony_ci return; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci nv_crtc->last_dpms = mode; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (nv_two_heads(dev)) 1928c2ecf20Sopenharmony_ci NVSetOwner(dev, nv_crtc->index); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* nv4ref indicates these two RPC1 bits inhibit h/v sync */ 1958c2ecf20Sopenharmony_ci crtc1A = NVReadVgaCrtc(dev, nv_crtc->index, 1968c2ecf20Sopenharmony_ci NV_CIO_CRE_RPC1_INDEX) & ~0xC0; 1978c2ecf20Sopenharmony_ci switch (mode) { 1988c2ecf20Sopenharmony_ci case DRM_MODE_DPMS_STANDBY: 1998c2ecf20Sopenharmony_ci /* Screen: Off; HSync: Off, VSync: On -- Not Supported */ 2008c2ecf20Sopenharmony_ci seq1 = 0x20; 2018c2ecf20Sopenharmony_ci crtc17 = 0x80; 2028c2ecf20Sopenharmony_ci crtc1A |= 0x80; 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci case DRM_MODE_DPMS_SUSPEND: 2058c2ecf20Sopenharmony_ci /* Screen: Off; HSync: On, VSync: Off -- Not Supported */ 2068c2ecf20Sopenharmony_ci seq1 = 0x20; 2078c2ecf20Sopenharmony_ci crtc17 = 0x80; 2088c2ecf20Sopenharmony_ci crtc1A |= 0x40; 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci case DRM_MODE_DPMS_OFF: 2118c2ecf20Sopenharmony_ci /* Screen: Off; HSync: Off, VSync: Off */ 2128c2ecf20Sopenharmony_ci seq1 = 0x20; 2138c2ecf20Sopenharmony_ci crtc17 = 0x00; 2148c2ecf20Sopenharmony_ci crtc1A |= 0xC0; 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci case DRM_MODE_DPMS_ON: 2178c2ecf20Sopenharmony_ci default: 2188c2ecf20Sopenharmony_ci /* Screen: On; HSync: On, VSync: On */ 2198c2ecf20Sopenharmony_ci seq1 = 0x00; 2208c2ecf20Sopenharmony_ci crtc17 = 0x80; 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci NVVgaSeqReset(dev, nv_crtc->index, true); 2258c2ecf20Sopenharmony_ci /* Each head has it's own sequencer, so we can turn it off when we want */ 2268c2ecf20Sopenharmony_ci seq1 |= (NVReadVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX) & ~0x20); 2278c2ecf20Sopenharmony_ci NVWriteVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX, seq1); 2288c2ecf20Sopenharmony_ci crtc17 |= (NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX) & ~0x80); 2298c2ecf20Sopenharmony_ci mdelay(10); 2308c2ecf20Sopenharmony_ci NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX, crtc17); 2318c2ecf20Sopenharmony_ci NVVgaSeqReset(dev, nv_crtc->index, false); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RPC1_INDEX, crtc1A); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void 2378c2ecf20Sopenharmony_cinv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 2408c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 2418c2ecf20Sopenharmony_ci struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; 2428c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = crtc->primary->fb; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* Calculate our timings */ 2458c2ecf20Sopenharmony_ci int horizDisplay = (mode->crtc_hdisplay >> 3) - 1; 2468c2ecf20Sopenharmony_ci int horizStart = (mode->crtc_hsync_start >> 3) + 1; 2478c2ecf20Sopenharmony_ci int horizEnd = (mode->crtc_hsync_end >> 3) + 1; 2488c2ecf20Sopenharmony_ci int horizTotal = (mode->crtc_htotal >> 3) - 5; 2498c2ecf20Sopenharmony_ci int horizBlankStart = (mode->crtc_hdisplay >> 3) - 1; 2508c2ecf20Sopenharmony_ci int horizBlankEnd = (mode->crtc_htotal >> 3) - 1; 2518c2ecf20Sopenharmony_ci int vertDisplay = mode->crtc_vdisplay - 1; 2528c2ecf20Sopenharmony_ci int vertStart = mode->crtc_vsync_start - 1; 2538c2ecf20Sopenharmony_ci int vertEnd = mode->crtc_vsync_end - 1; 2548c2ecf20Sopenharmony_ci int vertTotal = mode->crtc_vtotal - 2; 2558c2ecf20Sopenharmony_ci int vertBlankStart = mode->crtc_vdisplay - 1; 2568c2ecf20Sopenharmony_ci int vertBlankEnd = mode->crtc_vtotal - 1; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 2598c2ecf20Sopenharmony_ci bool fp_output = false; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 2628c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (encoder->crtc == crtc && 2658c2ecf20Sopenharmony_ci (nv_encoder->dcb->type == DCB_OUTPUT_LVDS || 2668c2ecf20Sopenharmony_ci nv_encoder->dcb->type == DCB_OUTPUT_TMDS)) 2678c2ecf20Sopenharmony_ci fp_output = true; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (fp_output) { 2718c2ecf20Sopenharmony_ci vertStart = vertTotal - 3; 2728c2ecf20Sopenharmony_ci vertEnd = vertTotal - 2; 2738c2ecf20Sopenharmony_ci vertBlankStart = vertStart; 2748c2ecf20Sopenharmony_ci horizStart = horizTotal - 5; 2758c2ecf20Sopenharmony_ci horizEnd = horizTotal - 2; 2768c2ecf20Sopenharmony_ci horizBlankEnd = horizTotal + 4; 2778c2ecf20Sopenharmony_ci#if 0 2788c2ecf20Sopenharmony_ci if (dev->overlayAdaptor && drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) 2798c2ecf20Sopenharmony_ci /* This reportedly works around some video overlay bandwidth problems */ 2808c2ecf20Sopenharmony_ci horizTotal += 2; 2818c2ecf20Sopenharmony_ci#endif 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_INTERLACE) 2858c2ecf20Sopenharmony_ci vertTotal |= 1; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci#if 0 2888c2ecf20Sopenharmony_ci ErrorF("horizDisplay: 0x%X \n", horizDisplay); 2898c2ecf20Sopenharmony_ci ErrorF("horizStart: 0x%X \n", horizStart); 2908c2ecf20Sopenharmony_ci ErrorF("horizEnd: 0x%X \n", horizEnd); 2918c2ecf20Sopenharmony_ci ErrorF("horizTotal: 0x%X \n", horizTotal); 2928c2ecf20Sopenharmony_ci ErrorF("horizBlankStart: 0x%X \n", horizBlankStart); 2938c2ecf20Sopenharmony_ci ErrorF("horizBlankEnd: 0x%X \n", horizBlankEnd); 2948c2ecf20Sopenharmony_ci ErrorF("vertDisplay: 0x%X \n", vertDisplay); 2958c2ecf20Sopenharmony_ci ErrorF("vertStart: 0x%X \n", vertStart); 2968c2ecf20Sopenharmony_ci ErrorF("vertEnd: 0x%X \n", vertEnd); 2978c2ecf20Sopenharmony_ci ErrorF("vertTotal: 0x%X \n", vertTotal); 2988c2ecf20Sopenharmony_ci ErrorF("vertBlankStart: 0x%X \n", vertBlankStart); 2998c2ecf20Sopenharmony_ci ErrorF("vertBlankEnd: 0x%X \n", vertBlankEnd); 3008c2ecf20Sopenharmony_ci#endif 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* 3038c2ecf20Sopenharmony_ci * compute correct Hsync & Vsync polarity 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ci if ((mode->flags & (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC)) 3068c2ecf20Sopenharmony_ci && (mode->flags & (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC))) { 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci regp->MiscOutReg = 0x23; 3098c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_NHSYNC) 3108c2ecf20Sopenharmony_ci regp->MiscOutReg |= 0x40; 3118c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_NVSYNC) 3128c2ecf20Sopenharmony_ci regp->MiscOutReg |= 0x80; 3138c2ecf20Sopenharmony_ci } else { 3148c2ecf20Sopenharmony_ci int vdisplay = mode->vdisplay; 3158c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 3168c2ecf20Sopenharmony_ci vdisplay *= 2; 3178c2ecf20Sopenharmony_ci if (mode->vscan > 1) 3188c2ecf20Sopenharmony_ci vdisplay *= mode->vscan; 3198c2ecf20Sopenharmony_ci if (vdisplay < 400) 3208c2ecf20Sopenharmony_ci regp->MiscOutReg = 0xA3; /* +hsync -vsync */ 3218c2ecf20Sopenharmony_ci else if (vdisplay < 480) 3228c2ecf20Sopenharmony_ci regp->MiscOutReg = 0x63; /* -hsync +vsync */ 3238c2ecf20Sopenharmony_ci else if (vdisplay < 768) 3248c2ecf20Sopenharmony_ci regp->MiscOutReg = 0xE3; /* -hsync -vsync */ 3258c2ecf20Sopenharmony_ci else 3268c2ecf20Sopenharmony_ci regp->MiscOutReg = 0x23; /* +hsync +vsync */ 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* 3308c2ecf20Sopenharmony_ci * Time Sequencer 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_ci regp->Sequencer[NV_VIO_SR_RESET_INDEX] = 0x00; 3338c2ecf20Sopenharmony_ci /* 0x20 disables the sequencer */ 3348c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_CLKDIV2) 3358c2ecf20Sopenharmony_ci regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x29; 3368c2ecf20Sopenharmony_ci else 3378c2ecf20Sopenharmony_ci regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x21; 3388c2ecf20Sopenharmony_ci regp->Sequencer[NV_VIO_SR_PLANE_MASK_INDEX] = 0x0F; 3398c2ecf20Sopenharmony_ci regp->Sequencer[NV_VIO_SR_CHAR_MAP_INDEX] = 0x00; 3408c2ecf20Sopenharmony_ci regp->Sequencer[NV_VIO_SR_MEM_MODE_INDEX] = 0x0E; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* 3438c2ecf20Sopenharmony_ci * CRTC 3448c2ecf20Sopenharmony_ci */ 3458c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_HDT_INDEX] = horizTotal; 3468c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_HDE_INDEX] = horizDisplay; 3478c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_HBS_INDEX] = horizBlankStart; 3488c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_HBE_INDEX] = (1 << 7) | 3498c2ecf20Sopenharmony_ci XLATE(horizBlankEnd, 0, NV_CIO_CR_HBE_4_0); 3508c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_HRS_INDEX] = horizStart; 3518c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_HRE_INDEX] = XLATE(horizBlankEnd, 5, NV_CIO_CR_HRE_HBE_5) | 3528c2ecf20Sopenharmony_ci XLATE(horizEnd, 0, NV_CIO_CR_HRE_4_0); 3538c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_VDT_INDEX] = vertTotal; 3548c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_OVL_INDEX] = XLATE(vertStart, 9, NV_CIO_CR_OVL_VRS_9) | 3558c2ecf20Sopenharmony_ci XLATE(vertDisplay, 9, NV_CIO_CR_OVL_VDE_9) | 3568c2ecf20Sopenharmony_ci XLATE(vertTotal, 9, NV_CIO_CR_OVL_VDT_9) | 3578c2ecf20Sopenharmony_ci (1 << 4) | 3588c2ecf20Sopenharmony_ci XLATE(vertBlankStart, 8, NV_CIO_CR_OVL_VBS_8) | 3598c2ecf20Sopenharmony_ci XLATE(vertStart, 8, NV_CIO_CR_OVL_VRS_8) | 3608c2ecf20Sopenharmony_ci XLATE(vertDisplay, 8, NV_CIO_CR_OVL_VDE_8) | 3618c2ecf20Sopenharmony_ci XLATE(vertTotal, 8, NV_CIO_CR_OVL_VDT_8); 3628c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_RSAL_INDEX] = 0x00; 3638c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_CELL_HT_INDEX] = ((mode->flags & DRM_MODE_FLAG_DBLSCAN) ? MASK(NV_CIO_CR_CELL_HT_SCANDBL) : 0) | 3648c2ecf20Sopenharmony_ci 1 << 6 | 3658c2ecf20Sopenharmony_ci XLATE(vertBlankStart, 9, NV_CIO_CR_CELL_HT_VBS_9); 3668c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_CURS_ST_INDEX] = 0x00; 3678c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_CURS_END_INDEX] = 0x00; 3688c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_SA_HI_INDEX] = 0x00; 3698c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_SA_LO_INDEX] = 0x00; 3708c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_TCOFF_HI_INDEX] = 0x00; 3718c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_TCOFF_LO_INDEX] = 0x00; 3728c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_VRS_INDEX] = vertStart; 3738c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_VRE_INDEX] = 1 << 5 | XLATE(vertEnd, 0, NV_CIO_CR_VRE_3_0); 3748c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_VDE_INDEX] = vertDisplay; 3758c2ecf20Sopenharmony_ci /* framebuffer can be larger than crtc scanout area. */ 3768c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = fb->pitches[0] / 8; 3778c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_ULINE_INDEX] = 0x00; 3788c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_VBS_INDEX] = vertBlankStart; 3798c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_VBE_INDEX] = vertBlankEnd; 3808c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_MODE_INDEX] = 0x43; 3818c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_LCOMP_INDEX] = 0xff; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* 3848c2ecf20Sopenharmony_ci * Some extended CRTC registers (they are not saved with the rest of the vga regs). 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* framebuffer can be larger than crtc scanout area. */ 3888c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_RPC0_INDEX] = 3898c2ecf20Sopenharmony_ci XLATE(fb->pitches[0] / 8, 8, NV_CIO_CRE_RPC0_OFFSET_10_8); 3908c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_42] = 3918c2ecf20Sopenharmony_ci XLATE(fb->pitches[0] / 8, 11, NV_CIO_CRE_42_OFFSET_11); 3928c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_RPC1_INDEX] = mode->crtc_hdisplay < 1280 ? 3938c2ecf20Sopenharmony_ci MASK(NV_CIO_CRE_RPC1_LARGE) : 0x00; 3948c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_LSR_INDEX] = XLATE(horizBlankEnd, 6, NV_CIO_CRE_LSR_HBE_6) | 3958c2ecf20Sopenharmony_ci XLATE(vertBlankStart, 10, NV_CIO_CRE_LSR_VBS_10) | 3968c2ecf20Sopenharmony_ci XLATE(vertStart, 10, NV_CIO_CRE_LSR_VRS_10) | 3978c2ecf20Sopenharmony_ci XLATE(vertDisplay, 10, NV_CIO_CRE_LSR_VDE_10) | 3988c2ecf20Sopenharmony_ci XLATE(vertTotal, 10, NV_CIO_CRE_LSR_VDT_10); 3998c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_HEB__INDEX] = XLATE(horizStart, 8, NV_CIO_CRE_HEB_HRS_8) | 4008c2ecf20Sopenharmony_ci XLATE(horizBlankStart, 8, NV_CIO_CRE_HEB_HBS_8) | 4018c2ecf20Sopenharmony_ci XLATE(horizDisplay, 8, NV_CIO_CRE_HEB_HDE_8) | 4028c2ecf20Sopenharmony_ci XLATE(horizTotal, 8, NV_CIO_CRE_HEB_HDT_8); 4038c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_EBR_INDEX] = XLATE(vertBlankStart, 11, NV_CIO_CRE_EBR_VBS_11) | 4048c2ecf20Sopenharmony_ci XLATE(vertStart, 11, NV_CIO_CRE_EBR_VRS_11) | 4058c2ecf20Sopenharmony_ci XLATE(vertDisplay, 11, NV_CIO_CRE_EBR_VDE_11) | 4068c2ecf20Sopenharmony_ci XLATE(vertTotal, 11, NV_CIO_CRE_EBR_VDT_11); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_INTERLACE) { 4098c2ecf20Sopenharmony_ci horizTotal = (horizTotal >> 1) & ~1; 4108c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = horizTotal; 4118c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_HEB__INDEX] |= XLATE(horizTotal, 8, NV_CIO_CRE_HEB_ILC_8); 4128c2ecf20Sopenharmony_ci } else 4138c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = 0xff; /* interlace off */ 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* 4168c2ecf20Sopenharmony_ci * Graphics Display Controller 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_ci regp->Graphics[NV_VIO_GX_SR_INDEX] = 0x00; 4198c2ecf20Sopenharmony_ci regp->Graphics[NV_VIO_GX_SREN_INDEX] = 0x00; 4208c2ecf20Sopenharmony_ci regp->Graphics[NV_VIO_GX_CCOMP_INDEX] = 0x00; 4218c2ecf20Sopenharmony_ci regp->Graphics[NV_VIO_GX_ROP_INDEX] = 0x00; 4228c2ecf20Sopenharmony_ci regp->Graphics[NV_VIO_GX_READ_MAP_INDEX] = 0x00; 4238c2ecf20Sopenharmony_ci regp->Graphics[NV_VIO_GX_MODE_INDEX] = 0x40; /* 256 color mode */ 4248c2ecf20Sopenharmony_ci regp->Graphics[NV_VIO_GX_MISC_INDEX] = 0x05; /* map 64k mem + graphic mode */ 4258c2ecf20Sopenharmony_ci regp->Graphics[NV_VIO_GX_DONT_CARE_INDEX] = 0x0F; 4268c2ecf20Sopenharmony_ci regp->Graphics[NV_VIO_GX_BIT_MASK_INDEX] = 0xFF; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci regp->Attribute[0] = 0x00; /* standard colormap translation */ 4298c2ecf20Sopenharmony_ci regp->Attribute[1] = 0x01; 4308c2ecf20Sopenharmony_ci regp->Attribute[2] = 0x02; 4318c2ecf20Sopenharmony_ci regp->Attribute[3] = 0x03; 4328c2ecf20Sopenharmony_ci regp->Attribute[4] = 0x04; 4338c2ecf20Sopenharmony_ci regp->Attribute[5] = 0x05; 4348c2ecf20Sopenharmony_ci regp->Attribute[6] = 0x06; 4358c2ecf20Sopenharmony_ci regp->Attribute[7] = 0x07; 4368c2ecf20Sopenharmony_ci regp->Attribute[8] = 0x08; 4378c2ecf20Sopenharmony_ci regp->Attribute[9] = 0x09; 4388c2ecf20Sopenharmony_ci regp->Attribute[10] = 0x0A; 4398c2ecf20Sopenharmony_ci regp->Attribute[11] = 0x0B; 4408c2ecf20Sopenharmony_ci regp->Attribute[12] = 0x0C; 4418c2ecf20Sopenharmony_ci regp->Attribute[13] = 0x0D; 4428c2ecf20Sopenharmony_ci regp->Attribute[14] = 0x0E; 4438c2ecf20Sopenharmony_ci regp->Attribute[15] = 0x0F; 4448c2ecf20Sopenharmony_ci regp->Attribute[NV_CIO_AR_MODE_INDEX] = 0x01; /* Enable graphic mode */ 4458c2ecf20Sopenharmony_ci /* Non-vga */ 4468c2ecf20Sopenharmony_ci regp->Attribute[NV_CIO_AR_OSCAN_INDEX] = 0x00; 4478c2ecf20Sopenharmony_ci regp->Attribute[NV_CIO_AR_PLANE_INDEX] = 0x0F; /* enable all color planes */ 4488c2ecf20Sopenharmony_ci regp->Attribute[NV_CIO_AR_HPP_INDEX] = 0x00; 4498c2ecf20Sopenharmony_ci regp->Attribute[NV_CIO_AR_CSEL_INDEX] = 0x00; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci/** 4538c2ecf20Sopenharmony_ci * Sets up registers for the given mode/adjusted_mode pair. 4548c2ecf20Sopenharmony_ci * 4558c2ecf20Sopenharmony_ci * The clocks, CRTCs and outputs attached to this CRTC must be off. 4568c2ecf20Sopenharmony_ci * 4578c2ecf20Sopenharmony_ci * This shouldn't enable any clocks, CRTCs, or outputs, but they should 4588c2ecf20Sopenharmony_ci * be easily turned on/off after this. 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_cistatic void 4618c2ecf20Sopenharmony_cinv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 4648c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 4658c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 4668c2ecf20Sopenharmony_ci struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; 4678c2ecf20Sopenharmony_ci struct nv04_crtc_reg *savep = &nv04_display(dev)->saved_reg.crtc_reg[nv_crtc->index]; 4688c2ecf20Sopenharmony_ci const struct drm_framebuffer *fb = crtc->primary->fb; 4698c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 4708c2ecf20Sopenharmony_ci bool lvds_output = false, tmds_output = false, tv_output = false, 4718c2ecf20Sopenharmony_ci off_chip_digital = false; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 4748c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 4758c2ecf20Sopenharmony_ci bool digital = false; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (encoder->crtc != crtc) 4788c2ecf20Sopenharmony_ci continue; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) 4818c2ecf20Sopenharmony_ci digital = lvds_output = true; 4828c2ecf20Sopenharmony_ci if (nv_encoder->dcb->type == DCB_OUTPUT_TV) 4838c2ecf20Sopenharmony_ci tv_output = true; 4848c2ecf20Sopenharmony_ci if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS) 4858c2ecf20Sopenharmony_ci digital = tmds_output = true; 4868c2ecf20Sopenharmony_ci if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && digital) 4878c2ecf20Sopenharmony_ci off_chip_digital = true; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* Registers not directly related to the (s)vga mode */ 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* What is the meaning of this register? */ 4938c2ecf20Sopenharmony_ci /* A few popular values are 0x18, 0x1c, 0x38, 0x3c */ 4948c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_ENH_INDEX] = savep->CRTC[NV_CIO_CRE_ENH_INDEX] & ~(1<<5); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci regp->crtc_eng_ctrl = 0; 4978c2ecf20Sopenharmony_ci /* Except for rare conditions I2C is enabled on the primary crtc */ 4988c2ecf20Sopenharmony_ci if (nv_crtc->index == 0) 4998c2ecf20Sopenharmony_ci regp->crtc_eng_ctrl |= NV_CRTC_FSEL_I2C; 5008c2ecf20Sopenharmony_ci#if 0 5018c2ecf20Sopenharmony_ci /* Set overlay to desired crtc. */ 5028c2ecf20Sopenharmony_ci if (dev->overlayAdaptor) { 5038c2ecf20Sopenharmony_ci NVPortPrivPtr pPriv = GET_OVERLAY_PRIVATE(dev); 5048c2ecf20Sopenharmony_ci if (pPriv->overlayCRTC == nv_crtc->index) 5058c2ecf20Sopenharmony_ci regp->crtc_eng_ctrl |= NV_CRTC_FSEL_OVERLAY; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci#endif 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* ADDRESS_SPACE_PNVM is the same as setting HCUR_ASI */ 5108c2ecf20Sopenharmony_ci regp->cursor_cfg = NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 | 5118c2ecf20Sopenharmony_ci NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 | 5128c2ecf20Sopenharmony_ci NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM; 5138c2ecf20Sopenharmony_ci if (drm->client.device.info.chipset >= 0x11) 5148c2ecf20Sopenharmony_ci regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32; 5158c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 5168c2ecf20Sopenharmony_ci regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* Unblock some timings */ 5198c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_53] = 0; 5208c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_54] = 0; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* 0x00 is disabled, 0x11 is lvds, 0x22 crt and 0x88 tmds */ 5238c2ecf20Sopenharmony_ci if (lvds_output) 5248c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x11; 5258c2ecf20Sopenharmony_ci else if (tmds_output) 5268c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x88; 5278c2ecf20Sopenharmony_ci else 5288c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x22; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* These values seem to vary */ 5318c2ecf20Sopenharmony_ci /* This register seems to be used by the bios to make certain decisions on some G70 cards? */ 5328c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_SCRATCH4__INDEX] = savep->CRTC[NV_CIO_CRE_SCRATCH4__INDEX]; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci nv_crtc_set_digital_vibrance(crtc, nv_crtc->saturation); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* probably a scratch reg, but kept for cargo-cult purposes: 5378c2ecf20Sopenharmony_ci * bit0: crtc0?, head A 5388c2ecf20Sopenharmony_ci * bit6: lvds, head A 5398c2ecf20Sopenharmony_ci * bit7: (only in X), head A 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_ci if (nv_crtc->index == 0) 5428c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_4B] = savep->CRTC[NV_CIO_CRE_4B] | 0x80; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* The blob seems to take the current value from crtc 0, add 4 to that 5458c2ecf20Sopenharmony_ci * and reuse the old value for crtc 1 */ 5468c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] = nv04_display(dev)->saved_reg.crtc_reg[0].CRTC[NV_CIO_CRE_TVOUT_LATENCY]; 5478c2ecf20Sopenharmony_ci if (!nv_crtc->index) 5488c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] += 4; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* the blob sometimes sets |= 0x10 (which is the same as setting |= 5518c2ecf20Sopenharmony_ci * 1 << 30 on 0x60.830), for no apparent reason */ 5528c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_59] = off_chip_digital; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_RANKINE) 5558c2ecf20Sopenharmony_ci regp->CRTC[0x9f] = off_chip_digital ? 0x11 : 0x1; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci regp->crtc_830 = mode->crtc_vdisplay - 3; 5588c2ecf20Sopenharmony_ci regp->crtc_834 = mode->crtc_vdisplay - 1; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) 5618c2ecf20Sopenharmony_ci /* This is what the blob does */ 5628c2ecf20Sopenharmony_ci regp->crtc_850 = NVReadCRTC(dev, 0, NV_PCRTC_850); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_RANKINE) 5658c2ecf20Sopenharmony_ci regp->gpio_ext = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) 5688c2ecf20Sopenharmony_ci regp->crtc_cfg = NV10_PCRTC_CONFIG_START_ADDRESS_HSYNC; 5698c2ecf20Sopenharmony_ci else 5708c2ecf20Sopenharmony_ci regp->crtc_cfg = NV04_PCRTC_CONFIG_START_ADDRESS_HSYNC; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* Some misc regs */ 5738c2ecf20Sopenharmony_ci if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) { 5748c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_85] = 0xFF; 5758c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_86] = 0x1; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (fb->format->depth + 1) / 8; 5798c2ecf20Sopenharmony_ci /* Enable slaved mode (called MODE_TV in nv4ref.h) */ 5808c2ecf20Sopenharmony_ci if (lvds_output || tmds_output || tv_output) 5818c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (1 << 7); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* Generic PRAMDAC regs */ 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) 5868c2ecf20Sopenharmony_ci /* Only bit that bios and blob set. */ 5878c2ecf20Sopenharmony_ci regp->nv10_cursync = (1 << 25); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci regp->ramdac_gen_ctrl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | 5908c2ecf20Sopenharmony_ci NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL | 5918c2ecf20Sopenharmony_ci NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON; 5928c2ecf20Sopenharmony_ci if (fb->format->depth == 16) 5938c2ecf20Sopenharmony_ci regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; 5948c2ecf20Sopenharmony_ci if (drm->client.device.info.chipset >= 0x11) 5958c2ecf20Sopenharmony_ci regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci regp->ramdac_630 = 0; /* turn off green mode (tv test pattern?) */ 5988c2ecf20Sopenharmony_ci regp->tv_setup = 0; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci nv_crtc_set_image_sharpening(crtc, nv_crtc->sharpness); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* Some values the blob sets */ 6038c2ecf20Sopenharmony_ci regp->ramdac_8c0 = 0x100; 6048c2ecf20Sopenharmony_ci regp->ramdac_a20 = 0x0; 6058c2ecf20Sopenharmony_ci regp->ramdac_a24 = 0xfffff; 6068c2ecf20Sopenharmony_ci regp->ramdac_a34 = 0x1; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic int 6108c2ecf20Sopenharmony_cinv_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct nv04_display *disp = nv04_display(crtc->dev); 6138c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = crtc->primary->fb; 6148c2ecf20Sopenharmony_ci struct nouveau_bo *nvbo = nouveau_gem_object(fb->obj[0]); 6158c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 6168c2ecf20Sopenharmony_ci int ret; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, false); 6198c2ecf20Sopenharmony_ci if (ret == 0) { 6208c2ecf20Sopenharmony_ci if (disp->image[nv_crtc->index]) 6218c2ecf20Sopenharmony_ci nouveau_bo_unpin(disp->image[nv_crtc->index]); 6228c2ecf20Sopenharmony_ci nouveau_bo_ref(nvbo, &disp->image[nv_crtc->index]); 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci return ret; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci/** 6298c2ecf20Sopenharmony_ci * Sets up registers for the given mode/adjusted_mode pair. 6308c2ecf20Sopenharmony_ci * 6318c2ecf20Sopenharmony_ci * The clocks, CRTCs and outputs attached to this CRTC must be off. 6328c2ecf20Sopenharmony_ci * 6338c2ecf20Sopenharmony_ci * This shouldn't enable any clocks, CRTCs, or outputs, but they should 6348c2ecf20Sopenharmony_ci * be easily turned on/off after this. 6358c2ecf20Sopenharmony_ci */ 6368c2ecf20Sopenharmony_cistatic int 6378c2ecf20Sopenharmony_cinv_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, 6388c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode, 6398c2ecf20Sopenharmony_ci int x, int y, struct drm_framebuffer *old_fb) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 6428c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 6438c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 6448c2ecf20Sopenharmony_ci int ret; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci NV_DEBUG(drm, "CTRC mode on CRTC %d:\n", nv_crtc->index); 6478c2ecf20Sopenharmony_ci drm_mode_debug_printmodeline(adjusted_mode); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci ret = nv_crtc_swap_fbs(crtc, old_fb); 6508c2ecf20Sopenharmony_ci if (ret) 6518c2ecf20Sopenharmony_ci return ret; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci /* unlock must come after turning off FP_TG_CONTROL in output_prepare */ 6548c2ecf20Sopenharmony_ci nv_lock_vga_crtc_shadow(dev, nv_crtc->index, -1); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci nv_crtc_mode_set_vga(crtc, adjusted_mode); 6578c2ecf20Sopenharmony_ci /* calculated in nv04_dfp_prepare, nv40 needs it written before calculating PLLs */ 6588c2ecf20Sopenharmony_ci if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) 6598c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, nv04_display(dev)->mode_reg.sel_clk); 6608c2ecf20Sopenharmony_ci nv_crtc_mode_set_regs(crtc, adjusted_mode); 6618c2ecf20Sopenharmony_ci nv_crtc_calc_state_ext(crtc, mode, adjusted_mode->clock); 6628c2ecf20Sopenharmony_ci return 0; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic void nv_crtc_save(struct drm_crtc *crtc) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 6688c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 6698c2ecf20Sopenharmony_ci struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; 6708c2ecf20Sopenharmony_ci struct nv04_crtc_reg *crtc_state = &state->crtc_reg[nv_crtc->index]; 6718c2ecf20Sopenharmony_ci struct nv04_mode_state *saved = &nv04_display(dev)->saved_reg; 6728c2ecf20Sopenharmony_ci struct nv04_crtc_reg *crtc_saved = &saved->crtc_reg[nv_crtc->index]; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (nv_two_heads(crtc->dev)) 6758c2ecf20Sopenharmony_ci NVSetOwner(crtc->dev, nv_crtc->index); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci nouveau_hw_save_state(crtc->dev, nv_crtc->index, saved); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci /* init some state to saved value */ 6808c2ecf20Sopenharmony_ci state->sel_clk = saved->sel_clk & ~(0x5 << 16); 6818c2ecf20Sopenharmony_ci crtc_state->CRTC[NV_CIO_CRE_LCD__INDEX] = crtc_saved->CRTC[NV_CIO_CRE_LCD__INDEX]; 6828c2ecf20Sopenharmony_ci state->pllsel = saved->pllsel & ~(PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK); 6838c2ecf20Sopenharmony_ci crtc_state->gpio_ext = crtc_saved->gpio_ext; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic void nv_crtc_restore(struct drm_crtc *crtc) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 6898c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 6908c2ecf20Sopenharmony_ci int head = nv_crtc->index; 6918c2ecf20Sopenharmony_ci uint8_t saved_cr21 = nv04_display(dev)->saved_reg.crtc_reg[head].CRTC[NV_CIO_CRE_21]; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci if (nv_two_heads(crtc->dev)) 6948c2ecf20Sopenharmony_ci NVSetOwner(crtc->dev, head); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci nouveau_hw_load_state(crtc->dev, head, &nv04_display(dev)->saved_reg); 6978c2ecf20Sopenharmony_ci nv_lock_vga_crtc_shadow(crtc->dev, head, saved_cr21); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci nv_crtc->last_dpms = NV_DPMS_CLEARED; 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic void nv_crtc_prepare(struct drm_crtc *crtc) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 7058c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 7068c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 7078c2ecf20Sopenharmony_ci const struct drm_crtc_helper_funcs *funcs = crtc->helper_private; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (nv_two_heads(dev)) 7108c2ecf20Sopenharmony_ci NVSetOwner(dev, nv_crtc->index); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci drm_crtc_vblank_off(crtc); 7138c2ecf20Sopenharmony_ci funcs->dpms(crtc, DRM_MODE_DPMS_OFF); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci NVBlankScreen(dev, nv_crtc->index, true); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci /* Some more preparation. */ 7188c2ecf20Sopenharmony_ci NVWriteCRTC(dev, nv_crtc->index, NV_PCRTC_CONFIG, NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA); 7198c2ecf20Sopenharmony_ci if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) { 7208c2ecf20Sopenharmony_ci uint32_t reg900 = NVReadRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900); 7218c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900, reg900 & ~0x10000); 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_cistatic void nv_crtc_commit(struct drm_crtc *crtc) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 7288c2ecf20Sopenharmony_ci const struct drm_crtc_helper_funcs *funcs = crtc->helper_private; 7298c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci nouveau_hw_load_state(dev, nv_crtc->index, &nv04_display(dev)->mode_reg); 7328c2ecf20Sopenharmony_ci nv04_crtc_mode_set_base(crtc, crtc->x, crtc->y, NULL); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN 7358c2ecf20Sopenharmony_ci /* turn on LFB swapping */ 7368c2ecf20Sopenharmony_ci { 7378c2ecf20Sopenharmony_ci uint8_t tmp = NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR); 7388c2ecf20Sopenharmony_ci tmp |= MASK(NV_CIO_CRE_RCR_ENDIAN_BIG); 7398c2ecf20Sopenharmony_ci NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR, tmp); 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci#endif 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci funcs->dpms(crtc, DRM_MODE_DPMS_ON); 7448c2ecf20Sopenharmony_ci drm_crtc_vblank_on(crtc); 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic void nv_crtc_destroy(struct drm_crtc *crtc) 7488c2ecf20Sopenharmony_ci{ 7498c2ecf20Sopenharmony_ci struct nv04_display *disp = nv04_display(crtc->dev); 7508c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci if (!nv_crtc) 7538c2ecf20Sopenharmony_ci return; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci drm_crtc_cleanup(crtc); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (disp->image[nv_crtc->index]) 7588c2ecf20Sopenharmony_ci nouveau_bo_unpin(disp->image[nv_crtc->index]); 7598c2ecf20Sopenharmony_ci nouveau_bo_ref(NULL, &disp->image[nv_crtc->index]); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci nouveau_bo_unmap(nv_crtc->cursor.nvbo); 7628c2ecf20Sopenharmony_ci nouveau_bo_unpin(nv_crtc->cursor.nvbo); 7638c2ecf20Sopenharmony_ci nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); 7648c2ecf20Sopenharmony_ci nvif_notify_dtor(&nv_crtc->vblank); 7658c2ecf20Sopenharmony_ci kfree(nv_crtc); 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_cistatic void 7698c2ecf20Sopenharmony_cinv_crtc_gamma_load(struct drm_crtc *crtc) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 7728c2ecf20Sopenharmony_ci struct drm_device *dev = nv_crtc->base.dev; 7738c2ecf20Sopenharmony_ci struct rgb { uint8_t r, g, b; } __attribute__((packed)) *rgbs; 7748c2ecf20Sopenharmony_ci u16 *r, *g, *b; 7758c2ecf20Sopenharmony_ci int i; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci rgbs = (struct rgb *)nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index].DAC; 7788c2ecf20Sopenharmony_ci r = crtc->gamma_store; 7798c2ecf20Sopenharmony_ci g = r + crtc->gamma_size; 7808c2ecf20Sopenharmony_ci b = g + crtc->gamma_size; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) { 7838c2ecf20Sopenharmony_ci rgbs[i].r = *r++ >> 8; 7848c2ecf20Sopenharmony_ci rgbs[i].g = *g++ >> 8; 7858c2ecf20Sopenharmony_ci rgbs[i].b = *b++ >> 8; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci nouveau_hw_load_state_palette(dev, nv_crtc->index, &nv04_display(dev)->mode_reg); 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_cistatic void 7928c2ecf20Sopenharmony_cinv_crtc_disable(struct drm_crtc *crtc) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci struct nv04_display *disp = nv04_display(crtc->dev); 7958c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 7968c2ecf20Sopenharmony_ci if (disp->image[nv_crtc->index]) 7978c2ecf20Sopenharmony_ci nouveau_bo_unpin(disp->image[nv_crtc->index]); 7988c2ecf20Sopenharmony_ci nouveau_bo_ref(NULL, &disp->image[nv_crtc->index]); 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic int 8028c2ecf20Sopenharmony_cinv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, 8038c2ecf20Sopenharmony_ci uint32_t size, 8048c2ecf20Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* We need to know the depth before we upload, but it's possible to 8098c2ecf20Sopenharmony_ci * get called before a framebuffer is bound. If this is the case, 8108c2ecf20Sopenharmony_ci * mark the lut values as dirty by setting depth==0, and it'll be 8118c2ecf20Sopenharmony_ci * uploaded on the first mode_set_base() 8128c2ecf20Sopenharmony_ci */ 8138c2ecf20Sopenharmony_ci if (!nv_crtc->base.primary->fb) { 8148c2ecf20Sopenharmony_ci nv_crtc->lut.depth = 0; 8158c2ecf20Sopenharmony_ci return 0; 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci nv_crtc_gamma_load(crtc); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci return 0; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic int 8248c2ecf20Sopenharmony_cinv04_crtc_do_mode_set_base(struct drm_crtc *crtc, 8258c2ecf20Sopenharmony_ci struct drm_framebuffer *passed_fb, 8268c2ecf20Sopenharmony_ci int x, int y, bool atomic) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 8298c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 8308c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 8318c2ecf20Sopenharmony_ci struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; 8328c2ecf20Sopenharmony_ci struct nouveau_bo *nvbo; 8338c2ecf20Sopenharmony_ci struct drm_framebuffer *drm_fb; 8348c2ecf20Sopenharmony_ci int arb_burst, arb_lwm; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci NV_DEBUG(drm, "index %d\n", nv_crtc->index); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci /* no fb bound */ 8398c2ecf20Sopenharmony_ci if (!atomic && !crtc->primary->fb) { 8408c2ecf20Sopenharmony_ci NV_DEBUG(drm, "No FB bound\n"); 8418c2ecf20Sopenharmony_ci return 0; 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* If atomic, we want to switch to the fb we were passed, so 8458c2ecf20Sopenharmony_ci * now we update pointers to do that. 8468c2ecf20Sopenharmony_ci */ 8478c2ecf20Sopenharmony_ci if (atomic) { 8488c2ecf20Sopenharmony_ci drm_fb = passed_fb; 8498c2ecf20Sopenharmony_ci } else { 8508c2ecf20Sopenharmony_ci drm_fb = crtc->primary->fb; 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci nvbo = nouveau_gem_object(drm_fb->obj[0]); 8548c2ecf20Sopenharmony_ci nv_crtc->fb.offset = nvbo->offset; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci if (nv_crtc->lut.depth != drm_fb->format->depth) { 8578c2ecf20Sopenharmony_ci nv_crtc->lut.depth = drm_fb->format->depth; 8588c2ecf20Sopenharmony_ci nv_crtc_gamma_load(crtc); 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci /* Update the framebuffer format. */ 8628c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] &= ~3; 8638c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (drm_fb->format->depth + 1) / 8; 8648c2ecf20Sopenharmony_ci regp->ramdac_gen_ctrl &= ~NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; 8658c2ecf20Sopenharmony_ci if (drm_fb->format->depth == 16) 8668c2ecf20Sopenharmony_ci regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; 8678c2ecf20Sopenharmony_ci crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_PIXEL_INDEX); 8688c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_GENERAL_CONTROL, 8698c2ecf20Sopenharmony_ci regp->ramdac_gen_ctrl); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = drm_fb->pitches[0] >> 3; 8728c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_RPC0_INDEX] = 8738c2ecf20Sopenharmony_ci XLATE(drm_fb->pitches[0] >> 3, 8, NV_CIO_CRE_RPC0_OFFSET_10_8); 8748c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_42] = 8758c2ecf20Sopenharmony_ci XLATE(drm_fb->pitches[0] / 8, 11, NV_CIO_CRE_42_OFFSET_11); 8768c2ecf20Sopenharmony_ci crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_RPC0_INDEX); 8778c2ecf20Sopenharmony_ci crtc_wr_cio_state(crtc, regp, NV_CIO_CR_OFFSET_INDEX); 8788c2ecf20Sopenharmony_ci crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_42); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci /* Update the framebuffer location. */ 8818c2ecf20Sopenharmony_ci regp->fb_start = nv_crtc->fb.offset & ~3; 8828c2ecf20Sopenharmony_ci regp->fb_start += (y * drm_fb->pitches[0]) + (x * drm_fb->format->cpp[0]); 8838c2ecf20Sopenharmony_ci nv_set_crtc_base(dev, nv_crtc->index, regp->fb_start); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci /* Update the arbitration parameters. */ 8868c2ecf20Sopenharmony_ci nouveau_calc_arb(dev, crtc->mode.clock, drm_fb->format->cpp[0] * 8, 8878c2ecf20Sopenharmony_ci &arb_burst, &arb_lwm); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_FF_INDEX] = arb_burst; 8908c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_FFLWM__INDEX] = arb_lwm & 0xff; 8918c2ecf20Sopenharmony_ci crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FF_INDEX); 8928c2ecf20Sopenharmony_ci crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FFLWM__INDEX); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KELVIN) { 8958c2ecf20Sopenharmony_ci regp->CRTC[NV_CIO_CRE_47] = arb_lwm >> 8; 8968c2ecf20Sopenharmony_ci crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_47); 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci return 0; 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic int 9038c2ecf20Sopenharmony_cinv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, 9048c2ecf20Sopenharmony_ci struct drm_framebuffer *old_fb) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci int ret = nv_crtc_swap_fbs(crtc, old_fb); 9078c2ecf20Sopenharmony_ci if (ret) 9088c2ecf20Sopenharmony_ci return ret; 9098c2ecf20Sopenharmony_ci return nv04_crtc_do_mode_set_base(crtc, old_fb, x, y, false); 9108c2ecf20Sopenharmony_ci} 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_cistatic int 9138c2ecf20Sopenharmony_cinv04_crtc_mode_set_base_atomic(struct drm_crtc *crtc, 9148c2ecf20Sopenharmony_ci struct drm_framebuffer *fb, 9158c2ecf20Sopenharmony_ci int x, int y, enum mode_set_atomic state) 9168c2ecf20Sopenharmony_ci{ 9178c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(crtc->dev); 9188c2ecf20Sopenharmony_ci struct drm_device *dev = drm->dev; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci if (state == ENTER_ATOMIC_MODE_SET) 9218c2ecf20Sopenharmony_ci nouveau_fbcon_accel_save_disable(dev); 9228c2ecf20Sopenharmony_ci else 9238c2ecf20Sopenharmony_ci nouveau_fbcon_accel_restore(dev); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci return nv04_crtc_do_mode_set_base(crtc, fb, x, y, true); 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_cistatic void nv04_cursor_upload(struct drm_device *dev, struct nouveau_bo *src, 9298c2ecf20Sopenharmony_ci struct nouveau_bo *dst) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci int width = nv_cursor_width(dev); 9328c2ecf20Sopenharmony_ci uint32_t pixel; 9338c2ecf20Sopenharmony_ci int i, j; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci for (i = 0; i < width; i++) { 9368c2ecf20Sopenharmony_ci for (j = 0; j < width; j++) { 9378c2ecf20Sopenharmony_ci pixel = nouveau_bo_rd32(src, i*64 + j); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci nouveau_bo_wr16(dst, i*width + j, (pixel & 0x80000000) >> 16 9408c2ecf20Sopenharmony_ci | (pixel & 0xf80000) >> 9 9418c2ecf20Sopenharmony_ci | (pixel & 0xf800) >> 6 9428c2ecf20Sopenharmony_ci | (pixel & 0xf8) >> 3); 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_cistatic void nv11_cursor_upload(struct drm_device *dev, struct nouveau_bo *src, 9488c2ecf20Sopenharmony_ci struct nouveau_bo *dst) 9498c2ecf20Sopenharmony_ci{ 9508c2ecf20Sopenharmony_ci uint32_t pixel; 9518c2ecf20Sopenharmony_ci int alpha, i; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci /* nv11+ supports premultiplied (PM), or non-premultiplied (NPM) alpha 9548c2ecf20Sopenharmony_ci * cursors (though NPM in combination with fp dithering may not work on 9558c2ecf20Sopenharmony_ci * nv11, from "nv" driver history) 9568c2ecf20Sopenharmony_ci * NPM mode needs NV_PCRTC_CURSOR_CONFIG_ALPHA_BLEND set and is what the 9578c2ecf20Sopenharmony_ci * blob uses, however we get given PM cursors so we use PM mode 9588c2ecf20Sopenharmony_ci */ 9598c2ecf20Sopenharmony_ci for (i = 0; i < 64 * 64; i++) { 9608c2ecf20Sopenharmony_ci pixel = nouveau_bo_rd32(src, i); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci /* hw gets unhappy if alpha <= rgb values. for a PM image "less 9638c2ecf20Sopenharmony_ci * than" shouldn't happen; fix "equal to" case by adding one to 9648c2ecf20Sopenharmony_ci * alpha channel (slightly inaccurate, but so is attempting to 9658c2ecf20Sopenharmony_ci * get back to NPM images, due to limits of integer precision) 9668c2ecf20Sopenharmony_ci */ 9678c2ecf20Sopenharmony_ci alpha = pixel >> 24; 9688c2ecf20Sopenharmony_ci if (alpha > 0 && alpha < 255) 9698c2ecf20Sopenharmony_ci pixel = (pixel & 0x00ffffff) | ((alpha + 1) << 24); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN 9728c2ecf20Sopenharmony_ci { 9738c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci if (drm->client.device.info.chipset == 0x11) { 9768c2ecf20Sopenharmony_ci pixel = ((pixel & 0x000000ff) << 24) | 9778c2ecf20Sopenharmony_ci ((pixel & 0x0000ff00) << 8) | 9788c2ecf20Sopenharmony_ci ((pixel & 0x00ff0000) >> 8) | 9798c2ecf20Sopenharmony_ci ((pixel & 0xff000000) >> 24); 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci#endif 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci nouveau_bo_wr32(dst, i, pixel); 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_cistatic int 9898c2ecf20Sopenharmony_cinv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, 9908c2ecf20Sopenharmony_ci uint32_t buffer_handle, uint32_t width, uint32_t height) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(crtc->dev); 9938c2ecf20Sopenharmony_ci struct drm_device *dev = drm->dev; 9948c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 9958c2ecf20Sopenharmony_ci struct nouveau_bo *cursor = NULL; 9968c2ecf20Sopenharmony_ci struct drm_gem_object *gem; 9978c2ecf20Sopenharmony_ci int ret = 0; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (!buffer_handle) { 10008c2ecf20Sopenharmony_ci nv_crtc->cursor.hide(nv_crtc, true); 10018c2ecf20Sopenharmony_ci return 0; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci if (width != 64 || height != 64) 10058c2ecf20Sopenharmony_ci return -EINVAL; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci gem = drm_gem_object_lookup(file_priv, buffer_handle); 10088c2ecf20Sopenharmony_ci if (!gem) 10098c2ecf20Sopenharmony_ci return -ENOENT; 10108c2ecf20Sopenharmony_ci cursor = nouveau_gem_object(gem); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci ret = nouveau_bo_map(cursor); 10138c2ecf20Sopenharmony_ci if (ret) 10148c2ecf20Sopenharmony_ci goto out; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci if (drm->client.device.info.chipset >= 0x11) 10178c2ecf20Sopenharmony_ci nv11_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo); 10188c2ecf20Sopenharmony_ci else 10198c2ecf20Sopenharmony_ci nv04_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci nouveau_bo_unmap(cursor); 10228c2ecf20Sopenharmony_ci nv_crtc->cursor.offset = nv_crtc->cursor.nvbo->offset; 10238c2ecf20Sopenharmony_ci nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset); 10248c2ecf20Sopenharmony_ci nv_crtc->cursor.show(nv_crtc, true); 10258c2ecf20Sopenharmony_ciout: 10268c2ecf20Sopenharmony_ci drm_gem_object_put(gem); 10278c2ecf20Sopenharmony_ci return ret; 10288c2ecf20Sopenharmony_ci} 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_cistatic int 10318c2ecf20Sopenharmony_cinv04_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) 10328c2ecf20Sopenharmony_ci{ 10338c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci nv_crtc->cursor.set_pos(nv_crtc, x, y); 10368c2ecf20Sopenharmony_ci return 0; 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistruct nv04_page_flip_state { 10408c2ecf20Sopenharmony_ci struct list_head head; 10418c2ecf20Sopenharmony_ci struct drm_pending_vblank_event *event; 10428c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 10438c2ecf20Sopenharmony_ci int bpp, pitch; 10448c2ecf20Sopenharmony_ci u64 offset; 10458c2ecf20Sopenharmony_ci}; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_cistatic int 10488c2ecf20Sopenharmony_cinv04_finish_page_flip(struct nouveau_channel *chan, 10498c2ecf20Sopenharmony_ci struct nv04_page_flip_state *ps) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci struct nouveau_fence_chan *fctx = chan->fence; 10528c2ecf20Sopenharmony_ci struct nouveau_drm *drm = chan->drm; 10538c2ecf20Sopenharmony_ci struct drm_device *dev = drm->dev; 10548c2ecf20Sopenharmony_ci struct nv04_page_flip_state *s; 10558c2ecf20Sopenharmony_ci unsigned long flags; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->event_lock, flags); 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci if (list_empty(&fctx->flip)) { 10608c2ecf20Sopenharmony_ci NV_ERROR(drm, "unexpected pageflip\n"); 10618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->event_lock, flags); 10628c2ecf20Sopenharmony_ci return -EINVAL; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci s = list_first_entry(&fctx->flip, struct nv04_page_flip_state, head); 10668c2ecf20Sopenharmony_ci if (s->event) { 10678c2ecf20Sopenharmony_ci drm_crtc_arm_vblank_event(s->crtc, s->event); 10688c2ecf20Sopenharmony_ci } else { 10698c2ecf20Sopenharmony_ci /* Give up ownership of vblank for page-flipped crtc */ 10708c2ecf20Sopenharmony_ci drm_crtc_vblank_put(s->crtc); 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci list_del(&s->head); 10748c2ecf20Sopenharmony_ci if (ps) 10758c2ecf20Sopenharmony_ci *ps = *s; 10768c2ecf20Sopenharmony_ci kfree(s); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->event_lock, flags); 10798c2ecf20Sopenharmony_ci return 0; 10808c2ecf20Sopenharmony_ci} 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ciint 10838c2ecf20Sopenharmony_cinv04_flip_complete(struct nvif_notify *notify) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci struct nouveau_cli *cli = (void *)notify->object->client; 10868c2ecf20Sopenharmony_ci struct nouveau_drm *drm = cli->drm; 10878c2ecf20Sopenharmony_ci struct nouveau_channel *chan = drm->channel; 10888c2ecf20Sopenharmony_ci struct nv04_page_flip_state state; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci if (!nv04_finish_page_flip(chan, &state)) { 10918c2ecf20Sopenharmony_ci nv_set_crtc_base(drm->dev, drm_crtc_index(state.crtc), 10928c2ecf20Sopenharmony_ci state.offset + state.crtc->y * 10938c2ecf20Sopenharmony_ci state.pitch + state.crtc->x * 10948c2ecf20Sopenharmony_ci state.bpp / 8); 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci return NVIF_NOTIFY_KEEP; 10988c2ecf20Sopenharmony_ci} 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_cistatic int 11018c2ecf20Sopenharmony_cinv04_page_flip_emit(struct nouveau_channel *chan, 11028c2ecf20Sopenharmony_ci struct nouveau_bo *old_bo, 11038c2ecf20Sopenharmony_ci struct nouveau_bo *new_bo, 11048c2ecf20Sopenharmony_ci struct nv04_page_flip_state *s, 11058c2ecf20Sopenharmony_ci struct nouveau_fence **pfence) 11068c2ecf20Sopenharmony_ci{ 11078c2ecf20Sopenharmony_ci struct nouveau_fence_chan *fctx = chan->fence; 11088c2ecf20Sopenharmony_ci struct nouveau_drm *drm = chan->drm; 11098c2ecf20Sopenharmony_ci struct drm_device *dev = drm->dev; 11108c2ecf20Sopenharmony_ci struct nvif_push *push = chan->chan.push; 11118c2ecf20Sopenharmony_ci unsigned long flags; 11128c2ecf20Sopenharmony_ci int ret; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci /* Queue it to the pending list */ 11158c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->event_lock, flags); 11168c2ecf20Sopenharmony_ci list_add_tail(&s->head, &fctx->flip); 11178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->event_lock, flags); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci /* Synchronize with the old framebuffer */ 11208c2ecf20Sopenharmony_ci ret = nouveau_fence_sync(old_bo, chan, false, false); 11218c2ecf20Sopenharmony_ci if (ret) 11228c2ecf20Sopenharmony_ci goto fail; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci /* Emit the pageflip */ 11258c2ecf20Sopenharmony_ci ret = PUSH_WAIT(push, 2); 11268c2ecf20Sopenharmony_ci if (ret) 11278c2ecf20Sopenharmony_ci goto fail; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci PUSH_NVSQ(push, NV_SW, NV_SW_PAGE_FLIP, 0x00000000); 11308c2ecf20Sopenharmony_ci PUSH_KICK(push); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci ret = nouveau_fence_new(chan, false, pfence); 11338c2ecf20Sopenharmony_ci if (ret) 11348c2ecf20Sopenharmony_ci goto fail; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci return 0; 11378c2ecf20Sopenharmony_cifail: 11388c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->event_lock, flags); 11398c2ecf20Sopenharmony_ci list_del(&s->head); 11408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->event_lock, flags); 11418c2ecf20Sopenharmony_ci return ret; 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_cistatic int 11458c2ecf20Sopenharmony_cinv04_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, 11468c2ecf20Sopenharmony_ci struct drm_pending_vblank_event *event, u32 flags, 11478c2ecf20Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci const int swap_interval = (flags & DRM_MODE_PAGE_FLIP_ASYNC) ? 0 : 1; 11508c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 11518c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 11528c2ecf20Sopenharmony_ci struct drm_framebuffer *old_fb = crtc->primary->fb; 11538c2ecf20Sopenharmony_ci struct nouveau_bo *old_bo = nouveau_gem_object(old_fb->obj[0]); 11548c2ecf20Sopenharmony_ci struct nouveau_bo *new_bo = nouveau_gem_object(fb->obj[0]); 11558c2ecf20Sopenharmony_ci struct nv04_page_flip_state *s; 11568c2ecf20Sopenharmony_ci struct nouveau_channel *chan; 11578c2ecf20Sopenharmony_ci struct nouveau_cli *cli; 11588c2ecf20Sopenharmony_ci struct nouveau_fence *fence; 11598c2ecf20Sopenharmony_ci struct nv04_display *dispnv04 = nv04_display(dev); 11608c2ecf20Sopenharmony_ci struct nvif_push *push; 11618c2ecf20Sopenharmony_ci int head = nouveau_crtc(crtc)->index; 11628c2ecf20Sopenharmony_ci int ret; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci chan = drm->channel; 11658c2ecf20Sopenharmony_ci if (!chan) 11668c2ecf20Sopenharmony_ci return -ENODEV; 11678c2ecf20Sopenharmony_ci cli = (void *)chan->user.client; 11688c2ecf20Sopenharmony_ci push = chan->chan.push; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci s = kzalloc(sizeof(*s), GFP_KERNEL); 11718c2ecf20Sopenharmony_ci if (!s) 11728c2ecf20Sopenharmony_ci return -ENOMEM; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci if (new_bo != old_bo) { 11758c2ecf20Sopenharmony_ci ret = nouveau_bo_pin(new_bo, NOUVEAU_GEM_DOMAIN_VRAM, true); 11768c2ecf20Sopenharmony_ci if (ret) 11778c2ecf20Sopenharmony_ci goto fail_free; 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci mutex_lock(&cli->mutex); 11818c2ecf20Sopenharmony_ci ret = ttm_bo_reserve(&new_bo->bo, true, false, NULL); 11828c2ecf20Sopenharmony_ci if (ret) 11838c2ecf20Sopenharmony_ci goto fail_unpin; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci /* synchronise rendering channel with the kernel's channel */ 11868c2ecf20Sopenharmony_ci ret = nouveau_fence_sync(new_bo, chan, false, true); 11878c2ecf20Sopenharmony_ci if (ret) { 11888c2ecf20Sopenharmony_ci ttm_bo_unreserve(&new_bo->bo); 11898c2ecf20Sopenharmony_ci goto fail_unpin; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci if (new_bo != old_bo) { 11938c2ecf20Sopenharmony_ci ttm_bo_unreserve(&new_bo->bo); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci ret = ttm_bo_reserve(&old_bo->bo, true, false, NULL); 11968c2ecf20Sopenharmony_ci if (ret) 11978c2ecf20Sopenharmony_ci goto fail_unpin; 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci /* Initialize a page flip struct */ 12018c2ecf20Sopenharmony_ci *s = (struct nv04_page_flip_state) 12028c2ecf20Sopenharmony_ci { { }, event, crtc, fb->format->cpp[0] * 8, fb->pitches[0], 12038c2ecf20Sopenharmony_ci new_bo->offset }; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci /* Keep vblanks on during flip, for the target crtc of this flip */ 12068c2ecf20Sopenharmony_ci drm_crtc_vblank_get(crtc); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci /* Emit a page flip */ 12098c2ecf20Sopenharmony_ci if (swap_interval) { 12108c2ecf20Sopenharmony_ci ret = PUSH_WAIT(push, 8); 12118c2ecf20Sopenharmony_ci if (ret) 12128c2ecf20Sopenharmony_ci goto fail_unreserve; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci PUSH_NVSQ(push, NV05F, 0x012c, 0); 12158c2ecf20Sopenharmony_ci PUSH_NVSQ(push, NV05F, 0x0134, head); 12168c2ecf20Sopenharmony_ci PUSH_NVSQ(push, NV05F, 0x0100, 0); 12178c2ecf20Sopenharmony_ci PUSH_NVSQ(push, NV05F, 0x0130, 0); 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci nouveau_bo_ref(new_bo, &dispnv04->image[head]); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci ret = nv04_page_flip_emit(chan, old_bo, new_bo, s, &fence); 12238c2ecf20Sopenharmony_ci if (ret) 12248c2ecf20Sopenharmony_ci goto fail_unreserve; 12258c2ecf20Sopenharmony_ci mutex_unlock(&cli->mutex); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci /* Update the crtc struct and cleanup */ 12288c2ecf20Sopenharmony_ci crtc->primary->fb = fb; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci nouveau_bo_fence(old_bo, fence, false); 12318c2ecf20Sopenharmony_ci ttm_bo_unreserve(&old_bo->bo); 12328c2ecf20Sopenharmony_ci if (old_bo != new_bo) 12338c2ecf20Sopenharmony_ci nouveau_bo_unpin(old_bo); 12348c2ecf20Sopenharmony_ci nouveau_fence_unref(&fence); 12358c2ecf20Sopenharmony_ci return 0; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_cifail_unreserve: 12388c2ecf20Sopenharmony_ci drm_crtc_vblank_put(crtc); 12398c2ecf20Sopenharmony_ci ttm_bo_unreserve(&old_bo->bo); 12408c2ecf20Sopenharmony_cifail_unpin: 12418c2ecf20Sopenharmony_ci mutex_unlock(&cli->mutex); 12428c2ecf20Sopenharmony_ci if (old_bo != new_bo) 12438c2ecf20Sopenharmony_ci nouveau_bo_unpin(new_bo); 12448c2ecf20Sopenharmony_cifail_free: 12458c2ecf20Sopenharmony_ci kfree(s); 12468c2ecf20Sopenharmony_ci return ret; 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs nv04_crtc_funcs = { 12508c2ecf20Sopenharmony_ci .cursor_set = nv04_crtc_cursor_set, 12518c2ecf20Sopenharmony_ci .cursor_move = nv04_crtc_cursor_move, 12528c2ecf20Sopenharmony_ci .gamma_set = nv_crtc_gamma_set, 12538c2ecf20Sopenharmony_ci .set_config = drm_crtc_helper_set_config, 12548c2ecf20Sopenharmony_ci .page_flip = nv04_crtc_page_flip, 12558c2ecf20Sopenharmony_ci .destroy = nv_crtc_destroy, 12568c2ecf20Sopenharmony_ci .enable_vblank = nouveau_display_vblank_enable, 12578c2ecf20Sopenharmony_ci .disable_vblank = nouveau_display_vblank_disable, 12588c2ecf20Sopenharmony_ci .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, 12598c2ecf20Sopenharmony_ci}; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = { 12628c2ecf20Sopenharmony_ci .dpms = nv_crtc_dpms, 12638c2ecf20Sopenharmony_ci .prepare = nv_crtc_prepare, 12648c2ecf20Sopenharmony_ci .commit = nv_crtc_commit, 12658c2ecf20Sopenharmony_ci .mode_set = nv_crtc_mode_set, 12668c2ecf20Sopenharmony_ci .mode_set_base = nv04_crtc_mode_set_base, 12678c2ecf20Sopenharmony_ci .mode_set_base_atomic = nv04_crtc_mode_set_base_atomic, 12688c2ecf20Sopenharmony_ci .disable = nv_crtc_disable, 12698c2ecf20Sopenharmony_ci .get_scanout_position = nouveau_display_scanoutpos, 12708c2ecf20Sopenharmony_ci}; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_cistatic const uint32_t modeset_formats[] = { 12738c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB8888, 12748c2ecf20Sopenharmony_ci DRM_FORMAT_RGB565, 12758c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB1555, 12768c2ecf20Sopenharmony_ci}; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_cistatic struct drm_plane * 12798c2ecf20Sopenharmony_cicreate_primary_plane(struct drm_device *dev) 12808c2ecf20Sopenharmony_ci{ 12818c2ecf20Sopenharmony_ci struct drm_plane *primary; 12828c2ecf20Sopenharmony_ci int ret; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci primary = kzalloc(sizeof(*primary), GFP_KERNEL); 12858c2ecf20Sopenharmony_ci if (primary == NULL) { 12868c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Failed to allocate primary plane\n"); 12878c2ecf20Sopenharmony_ci return NULL; 12888c2ecf20Sopenharmony_ci } 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci /* possible_crtc's will be filled in later by crtc_init */ 12918c2ecf20Sopenharmony_ci ret = drm_universal_plane_init(dev, primary, 0, 12928c2ecf20Sopenharmony_ci &drm_primary_helper_funcs, 12938c2ecf20Sopenharmony_ci modeset_formats, 12948c2ecf20Sopenharmony_ci ARRAY_SIZE(modeset_formats), NULL, 12958c2ecf20Sopenharmony_ci DRM_PLANE_TYPE_PRIMARY, NULL); 12968c2ecf20Sopenharmony_ci if (ret) { 12978c2ecf20Sopenharmony_ci kfree(primary); 12988c2ecf20Sopenharmony_ci primary = NULL; 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci return primary; 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_cistatic int nv04_crtc_vblank_handler(struct nvif_notify *notify) 13058c2ecf20Sopenharmony_ci{ 13068c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = 13078c2ecf20Sopenharmony_ci container_of(notify, struct nouveau_crtc, vblank); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci drm_crtc_handle_vblank(&nv_crtc->base); 13108c2ecf20Sopenharmony_ci return NVIF_NOTIFY_KEEP; 13118c2ecf20Sopenharmony_ci} 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ciint 13148c2ecf20Sopenharmony_cinv04_crtc_create(struct drm_device *dev, int crtc_num) 13158c2ecf20Sopenharmony_ci{ 13168c2ecf20Sopenharmony_ci struct nouveau_display *disp = nouveau_display(dev); 13178c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc; 13188c2ecf20Sopenharmony_ci int ret; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL); 13218c2ecf20Sopenharmony_ci if (!nv_crtc) 13228c2ecf20Sopenharmony_ci return -ENOMEM; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci nv_crtc->lut.depth = 0; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci nv_crtc->index = crtc_num; 13278c2ecf20Sopenharmony_ci nv_crtc->last_dpms = NV_DPMS_CLEARED; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci nv_crtc->save = nv_crtc_save; 13308c2ecf20Sopenharmony_ci nv_crtc->restore = nv_crtc_restore; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci drm_crtc_init_with_planes(dev, &nv_crtc->base, 13338c2ecf20Sopenharmony_ci create_primary_plane(dev), NULL, 13348c2ecf20Sopenharmony_ci &nv04_crtc_funcs, NULL); 13358c2ecf20Sopenharmony_ci drm_crtc_helper_add(&nv_crtc->base, &nv04_crtc_helper_funcs); 13368c2ecf20Sopenharmony_ci drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256); 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci ret = nouveau_bo_new(&nouveau_drm(dev)->client, 64*64*4, 0x100, 13398c2ecf20Sopenharmony_ci NOUVEAU_GEM_DOMAIN_VRAM, 0, 0x0000, NULL, NULL, 13408c2ecf20Sopenharmony_ci &nv_crtc->cursor.nvbo); 13418c2ecf20Sopenharmony_ci if (!ret) { 13428c2ecf20Sopenharmony_ci ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, 13438c2ecf20Sopenharmony_ci NOUVEAU_GEM_DOMAIN_VRAM, false); 13448c2ecf20Sopenharmony_ci if (!ret) { 13458c2ecf20Sopenharmony_ci ret = nouveau_bo_map(nv_crtc->cursor.nvbo); 13468c2ecf20Sopenharmony_ci if (ret) 13478c2ecf20Sopenharmony_ci nouveau_bo_unpin(nv_crtc->cursor.nvbo); 13488c2ecf20Sopenharmony_ci } 13498c2ecf20Sopenharmony_ci if (ret) 13508c2ecf20Sopenharmony_ci nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci nv04_cursor_init(nv_crtc); 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci ret = nvif_notify_ctor(&disp->disp.object, "kmsVbl", nv04_crtc_vblank_handler, 13568c2ecf20Sopenharmony_ci false, NV04_DISP_NTFY_VBLANK, 13578c2ecf20Sopenharmony_ci &(struct nvif_notify_head_req_v0) { 13588c2ecf20Sopenharmony_ci .head = nv_crtc->index, 13598c2ecf20Sopenharmony_ci }, 13608c2ecf20Sopenharmony_ci sizeof(struct nvif_notify_head_req_v0), 13618c2ecf20Sopenharmony_ci sizeof(struct nvif_notify_head_rep_v0), 13628c2ecf20Sopenharmony_ci &nv_crtc->vblank); 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci return ret; 13658c2ecf20Sopenharmony_ci} 1366