From 1a6463425552a8b9960e5a19b25421895846925c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 21 Mar 2013 15:45:11 +1000 Subject: drm/nv04/disp: hide all the cruft away in its own little hole It'd be pretty awesome if someone would care enough to port this all properly to a class interface, perhaps submitting a command stream to the core via a sw object on PFIFO (emulating how EVO works basically, and also what nvidia have done forever..).. But, this seems unlikely given how old this hardware is now, so, lets just hide it away. There's a heap of other bits and pieces laying around that are still tangled. I'll (re)move them in pieces. Signed-off-by: Ben Skeggs diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 0c65479..1a8f6e9 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -225,9 +225,7 @@ nouveau-y += nouveau_connector.o nouveau_dp.o nouveau-y += nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o # drm/kms/nv04:nv50 -nouveau-y += nouveau_hw.o nouveau_calc.o -nouveau-y += nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o -nouveau-y += nv04_crtc.o nv04_display.o nv04_cursor.o +include $(src)/dispnv04/Makefile # drm/kms/nv50- nouveau-y += nv50_display.o diff --git a/drivers/gpu/drm/nouveau/dispnv04/Makefile b/drivers/gpu/drm/nouveau/dispnv04/Makefile new file mode 100644 index 0000000..ea3f5b8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv04/Makefile @@ -0,0 +1,10 @@ +nouveau-y += dispnv04/arb.o +nouveau-y += dispnv04/crtc.o +nouveau-y += dispnv04/cursor.o +nouveau-y += dispnv04/dac.o +nouveau-y += dispnv04/dfp.o +nouveau-y += dispnv04/disp.o +nouveau-y += dispnv04/hw.o +nouveau-y += dispnv04/tvmodesnv17.o +nouveau-y += dispnv04/tvnv04.o +nouveau-y += dispnv04/tvnv17.o diff --git a/drivers/gpu/drm/nouveau/dispnv04/arb.c b/drivers/gpu/drm/nouveau/dispnv04/arb.c new file mode 100644 index 0000000..2e70462 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv04/arb.c @@ -0,0 +1,265 @@ +/* + * Copyright 1993-2003 NVIDIA, Corporation + * Copyright 2007-2009 Stuart Bennett + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "nouveau_drm.h" +#include "nouveau_reg.h" +#include "hw.h" + +/****************************************************************************\ +* * +* The video arbitration routines calculate some "magic" numbers. Fixes * +* the snow seen when accessing the framebuffer without it. * +* It just works (I hope). * +* * +\****************************************************************************/ + +struct nv_fifo_info { + int lwm; + int burst; +}; + +struct nv_sim_state { + int pclk_khz; + int mclk_khz; + int nvclk_khz; + int bpp; + int mem_page_miss; + int mem_latency; + int memory_type; + int memory_width; + int two_heads; +}; + +static void +nv04_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb) +{ + int pagemiss, cas, width, bpp; + int nvclks, mclks, pclks, crtpagemiss; + int found, mclk_extra, mclk_loop, cbs, m1, p1; + int mclk_freq, pclk_freq, nvclk_freq; + int us_m, us_n, us_p, crtc_drain_rate; + int cpm_us, us_crt, clwm; + + pclk_freq = arb->pclk_khz; + mclk_freq = arb->mclk_khz; + nvclk_freq = arb->nvclk_khz; + pagemiss = arb->mem_page_miss; + cas = arb->mem_latency; + width = arb->memory_width >> 6; + bpp = arb->bpp; + cbs = 128; + + pclks = 2; + nvclks = 10; + mclks = 13 + cas; + mclk_extra = 3; + found = 0; + + while (!found) { + found = 1; + + mclk_loop = mclks + mclk_extra; + us_m = mclk_loop * 1000 * 1000 / mclk_freq; + us_n = nvclks * 1000 * 1000 / nvclk_freq; + us_p = nvclks * 1000 * 1000 / pclk_freq; + + crtc_drain_rate = pclk_freq * bpp / 8; + crtpagemiss = 2; + crtpagemiss += 1; + cpm_us = crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq; + us_crt = cpm_us + us_m + us_n + us_p; + clwm = us_crt * crtc_drain_rate / (1000 * 1000); + clwm++; + + m1 = clwm + cbs - 512; + p1 = m1 * pclk_freq / mclk_freq; + p1 = p1 * bpp / 8; + if ((p1 < m1 && m1 > 0) || clwm > 519) { + found = !mclk_extra; + mclk_extra--; + } + if (clwm < 384) + clwm = 384; + + fifo->lwm = clwm; + fifo->burst = cbs; + } +} + +static void +nv10_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb) +{ + int fill_rate, drain_rate; + int pclks, nvclks, mclks, xclks; + int pclk_freq, nvclk_freq, mclk_freq; + int fill_lat, extra_lat; + int max_burst_o, max_burst_l; + int fifo_len, min_lwm, max_lwm; + const int burst_lat = 80; /* Maximum allowable latency due + * to the CRTC FIFO burst. (ns) */ + + pclk_freq = arb->pclk_khz; + nvclk_freq = arb->nvclk_khz; + mclk_freq = arb->mclk_khz; + + fill_rate = mclk_freq * arb->memory_width / 8; /* kB/s */ + drain_rate = pclk_freq * arb->bpp / 8; /* kB/s */ + + fifo_len = arb->two_heads ? 1536 : 1024; /* B */ + + /* Fixed FIFO refill latency. */ + + pclks = 4; /* lwm detect. */ + + nvclks = 3 /* lwm -> sync. */ + + 2 /* fbi bus cycles (1 req + 1 busy) */ + + 1 /* 2 edge sync. may be very close to edge so + * just put one. */ + + 1 /* fbi_d_rdv_n */ + + 1 /* Fbi_d_rdata */ + + 1; /* crtfifo load */ + + mclks = 1 /* 2 edge sync. may be very close to edge so + * just put one. */ + + 1 /* arb_hp_req */ + + 5 /* tiling pipeline */ + + 2 /* latency fifo */ + + 2 /* memory request to fbio block */ + + 7; /* data returned from fbio block */ + + /* Need to accumulate 256 bits for read */ + mclks += (arb->memory_type == 0 ? 2 : 1) + * arb->memory_width / 32; + + fill_lat = mclks * 1000 * 1000 / mclk_freq /* minimum mclk latency */ + + nvclks * 1000 * 1000 / nvclk_freq /* nvclk latency */ + + pclks * 1000 * 1000 / pclk_freq; /* pclk latency */ + + /* Conditional FIFO refill latency. */ + + xclks = 2 * arb->mem_page_miss + mclks /* Extra latency due to + * the overlay. */ + + 2 * arb->mem_page_miss /* Extra pagemiss latency. */ + + (arb->bpp == 32 ? 8 : 4); /* Margin of error. */ + + extra_lat = xclks * 1000 * 1000 / mclk_freq; + + if (arb->two_heads) + /* Account for another CRTC. */ + extra_lat += fill_lat + extra_lat + burst_lat; + + /* FIFO burst */ + + /* Max burst not leading to overflows. */ + max_burst_o = (1 + fifo_len - extra_lat * drain_rate / (1000 * 1000)) + * (fill_rate / 1000) / ((fill_rate - drain_rate) / 1000); + fifo->burst = min(max_burst_o, 1024); + + /* Max burst value with an acceptable latency. */ + max_burst_l = burst_lat * fill_rate / (1000 * 1000); + fifo->burst = min(max_burst_l, fifo->burst); + + fifo->burst = rounddown_pow_of_two(fifo->burst); + + /* FIFO low watermark */ + + min_lwm = (fill_lat + extra_lat) * drain_rate / (1000 * 1000) + 1; + max_lwm = fifo_len - fifo->burst + + fill_lat * drain_rate / (1000 * 1000) + + fifo->burst * drain_rate / fill_rate; + + fifo->lwm = min_lwm + 10 * (max_lwm - min_lwm) / 100; /* Empirical. */ +} + +static void +nv04_update_arb(struct drm_device *dev, int VClk, int bpp, + int *burst, int *lwm) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_device *device = nouveau_dev(dev); + struct nv_fifo_info fifo_data; + struct nv_sim_state sim_data; + int MClk = nouveau_hw_get_clock(dev, PLL_MEMORY); + int NVClk = nouveau_hw_get_clock(dev, PLL_CORE); + uint32_t cfg1 = nv_rd32(device, NV04_PFB_CFG1); + + sim_data.pclk_khz = VClk; + sim_data.mclk_khz = MClk; + sim_data.nvclk_khz = NVClk; + sim_data.bpp = bpp; + sim_data.two_heads = nv_two_heads(dev); + if ((dev->pci_device & 0xffff) == 0x01a0 /*CHIPSET_NFORCE*/ || + (dev->pci_device & 0xffff) == 0x01f0 /*CHIPSET_NFORCE2*/) { + uint32_t type; + + pci_read_config_dword(pci_get_bus_and_slot(0, 1), 0x7c, &type); + + sim_data.memory_type = (type >> 12) & 1; + sim_data.memory_width = 64; + sim_data.mem_latency = 3; + sim_data.mem_page_miss = 10; + } else { + sim_data.memory_type = nv_rd32(device, NV04_PFB_CFG0) & 0x1; + sim_data.memory_width = (nv_rd32(device, NV_PEXTDEV_BOOT_0) & 0x10) ? 128 : 64; + sim_data.mem_latency = cfg1 & 0xf; + sim_data.mem_page_miss = ((cfg1 >> 4) & 0xf) + ((cfg1 >> 31) & 0x1); + } + + if (nv_device(drm->device)->card_type == NV_04) + nv04_calc_arb(&fifo_data, &sim_data); + else + nv10_calc_arb(&fifo_data, &sim_data); + + *burst = ilog2(fifo_data.burst >> 4); + *lwm = fifo_data.lwm >> 3; +} + +static void +nv20_update_arb(int *burst, int *lwm) +{ + unsigned int fifo_size, burst_size, graphics_lwm; + + fifo_size = 2048; + burst_size = 512; + graphics_lwm = fifo_size - burst_size; + + *burst = ilog2(burst_size >> 5); + *lwm = graphics_lwm >> 3; +} + +void +nouveau_calc_arb(struct drm_device *dev, int vclk, int bpp, int *burst, int *lwm) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + if (nv_device(drm->device)->card_type < NV_20) + nv04_update_arb(dev, vclk, bpp, burst, lwm); + else if ((dev->pci_device & 0xfff0) == 0x0240 /*CHIPSET_C51*/ || + (dev->pci_device & 0xfff0) == 0x03d0 /*CHIPSET_C512*/) { + *burst = 128; + *lwm = 0x0480; + } else + nv20_update_arb(burst, lwm); +} diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c new file mode 100644 index 0000000..0782bd2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -0,0 +1,1072 @@ +/* + * Copyright 1993-2003 NVIDIA, Corporation + * Copyright 2006 Dave Airlie + * Copyright 2007 Maarten Maathuis + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "nouveau_drm.h" +#include "nouveau_reg.h" +#include "nouveau_bo.h" +#include "nouveau_gem.h" +#include "nouveau_encoder.h" +#include "nouveau_connector.h" +#include "nouveau_crtc.h" +#include "hw.h" +#include "nvreg.h" +#include "nouveau_fbcon.h" +#include "disp.h" + +#include +#include + +static int +nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb); + +static void +crtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index) +{ + NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index, + crtcstate->CRTC[index]); +} + +static void nv_crtc_set_digital_vibrance(struct drm_crtc *crtc, int level) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + + regp->CRTC[NV_CIO_CRE_CSB] = nv_crtc->saturation = level; + if (nv_crtc->saturation && nv_gf4_disp_arch(crtc->dev)) { + regp->CRTC[NV_CIO_CRE_CSB] = 0x80; + regp->CRTC[NV_CIO_CRE_5B] = nv_crtc->saturation << 2; + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_5B); + } + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_CSB); +} + +static void nv_crtc_set_image_sharpening(struct drm_crtc *crtc, int level) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + + nv_crtc->sharpness = level; + if (level < 0) /* blur is in hw range 0x3f -> 0x20 */ + level += 0x40; + regp->ramdac_634 = level; + NVWriteRAMDAC(crtc->dev, nv_crtc->index, NV_PRAMDAC_634, regp->ramdac_634); +} + +#define PLLSEL_VPLL1_MASK \ + (NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL \ + | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2) +#define PLLSEL_VPLL2_MASK \ + (NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 \ + | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2) +#define PLLSEL_TV_MASK \ + (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \ + | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 \ + | NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \ + | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2) + +/* NV4x 0x40.. pll notes: + * gpu pll: 0x4000 + 0x4004 + * ?gpu? pll: 0x4008 + 0x400c + * vpll1: 0x4010 + 0x4014 + * vpll2: 0x4018 + 0x401c + * mpll: 0x4020 + 0x4024 + * mpll: 0x4038 + 0x403c + * + * the first register of each pair has some unknown details: + * bits 0-7: redirected values from elsewhere? (similar to PLL_SETUP_CONTROL?) + * bits 20-23: (mpll) something to do with post divider? + * bits 28-31: related to single stage mode? (bit 8/12) + */ + +static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mode * mode, int dot_clock) +{ + struct drm_device *dev = crtc->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_bios *bios = nouveau_bios(drm->device); + struct nouveau_clock *clk = nouveau_clock(drm->device); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; + struct nv04_crtc_reg *regp = &state->crtc_reg[nv_crtc->index]; + struct nouveau_pll_vals *pv = ®p->pllvals; + struct nvbios_pll pll_lim; + + if (nvbios_pll_parse(bios, nv_crtc->index ? PLL_VPLL1 : PLL_VPLL0, + &pll_lim)) + return; + + /* NM2 == 0 is used to determine single stage mode on two stage plls */ + pv->NM2 = 0; + + /* for newer nv4x the blob uses only the first stage of the vpll below a + * certain clock. for a certain nv4b this is 150MHz. since the max + * output frequency of the first stage for this card is 300MHz, it is + * assumed the threshold is given by vco1 maxfreq/2 + */ + /* for early nv4x, specifically nv40 and *some* nv43 (devids 0 and 6, + * not 8, others unknown), the blob always uses both plls. no problem + * has yet been observed in allowing the use a single stage pll on all + * nv43 however. the behaviour of single stage use is untested on nv40 + */ + if (nv_device(drm->device)->chipset > 0x40 && dot_clock <= (pll_lim.vco1.max_freq / 2)) + memset(&pll_lim.vco2, 0, sizeof(pll_lim.vco2)); + + + if (!clk->pll_calc(clk, &pll_lim, dot_clock, pv)) + return; + + state->pllsel &= PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK; + + /* The blob uses this always, so let's do the same */ + if (nv_device(drm->device)->card_type == NV_40) + state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE; + /* again nv40 and some nv43 act more like nv3x as described above */ + if (nv_device(drm->device)->chipset < 0x41) + state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL | + NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL; + state->pllsel |= nv_crtc->index ? PLLSEL_VPLL2_MASK : PLLSEL_VPLL1_MASK; + + if (pv->NM2) + NV_DEBUG(drm, "vpll: n1 %d n2 %d m1 %d m2 %d log2p %d\n", + pv->N1, pv->N2, pv->M1, pv->M2, pv->log2P); + else + NV_DEBUG(drm, "vpll: n %d m %d log2p %d\n", + pv->N1, pv->M1, pv->log2P); + + nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset); +} + +static void +nv_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + unsigned char seq1 = 0, crtc17 = 0; + unsigned char crtc1A; + + NV_DEBUG(drm, "Setting dpms mode %d on CRTC %d\n", mode, + nv_crtc->index); + + if (nv_crtc->last_dpms == mode) /* Don't do unnecessary mode changes. */ + return; + + nv_crtc->last_dpms = mode; + + if (nv_two_heads(dev)) + NVSetOwner(dev, nv_crtc->index); + + /* nv4ref indicates these two RPC1 bits inhibit h/v sync */ + crtc1A = NVReadVgaCrtc(dev, nv_crtc->index, + NV_CIO_CRE_RPC1_INDEX) & ~0xC0; + switch (mode) { + case DRM_MODE_DPMS_STANDBY: + /* Screen: Off; HSync: Off, VSync: On -- Not Supported */ + seq1 = 0x20; + crtc17 = 0x80; + crtc1A |= 0x80; + break; + case DRM_MODE_DPMS_SUSPEND: + /* Screen: Off; HSync: On, VSync: Off -- Not Supported */ + seq1 = 0x20; + crtc17 = 0x80; + crtc1A |= 0x40; + break; + case DRM_MODE_DPMS_OFF: + /* Screen: Off; HSync: Off, VSync: Off */ + seq1 = 0x20; + crtc17 = 0x00; + crtc1A |= 0xC0; + break; + case DRM_MODE_DPMS_ON: + default: + /* Screen: On; HSync: On, VSync: On */ + seq1 = 0x00; + crtc17 = 0x80; + break; + } + + NVVgaSeqReset(dev, nv_crtc->index, true); + /* Each head has it's own sequencer, so we can turn it off when we want */ + seq1 |= (NVReadVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX) & ~0x20); + NVWriteVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX, seq1); + crtc17 |= (NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX) & ~0x80); + mdelay(10); + NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX, crtc17); + NVVgaSeqReset(dev, nv_crtc->index, false); + + NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RPC1_INDEX, crtc1A); +} + +static bool +nv_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void +nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + struct drm_framebuffer *fb = crtc->fb; + + /* Calculate our timings */ + int horizDisplay = (mode->crtc_hdisplay >> 3) - 1; + int horizStart = (mode->crtc_hsync_start >> 3) + 1; + int horizEnd = (mode->crtc_hsync_end >> 3) + 1; + int horizTotal = (mode->crtc_htotal >> 3) - 5; + int horizBlankStart = (mode->crtc_hdisplay >> 3) - 1; + int horizBlankEnd = (mode->crtc_htotal >> 3) - 1; + int vertDisplay = mode->crtc_vdisplay - 1; + int vertStart = mode->crtc_vsync_start - 1; + int vertEnd = mode->crtc_vsync_end - 1; + int vertTotal = mode->crtc_vtotal - 2; + int vertBlankStart = mode->crtc_vdisplay - 1; + int vertBlankEnd = mode->crtc_vtotal - 1; + + struct drm_encoder *encoder; + bool fp_output = false; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + + if (encoder->crtc == crtc && + (nv_encoder->dcb->type == DCB_OUTPUT_LVDS || + nv_encoder->dcb->type == DCB_OUTPUT_TMDS)) + fp_output = true; + } + + if (fp_output) { + vertStart = vertTotal - 3; + vertEnd = vertTotal - 2; + vertBlankStart = vertStart; + horizStart = horizTotal - 5; + horizEnd = horizTotal - 2; + horizBlankEnd = horizTotal + 4; +#if 0 + if (dev->overlayAdaptor && nv_device(drm->device)->card_type >= NV_10) + /* This reportedly works around some video overlay bandwidth problems */ + horizTotal += 2; +#endif + } + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + vertTotal |= 1; + +#if 0 + ErrorF("horizDisplay: 0x%X \n", horizDisplay); + ErrorF("horizStart: 0x%X \n", horizStart); + ErrorF("horizEnd: 0x%X \n", horizEnd); + ErrorF("horizTotal: 0x%X \n", horizTotal); + ErrorF("horizBlankStart: 0x%X \n", horizBlankStart); + ErrorF("horizBlankEnd: 0x%X \n", horizBlankEnd); + ErrorF("vertDisplay: 0x%X \n", vertDisplay); + ErrorF("vertStart: 0x%X \n", vertStart); + ErrorF("vertEnd: 0x%X \n", vertEnd); + ErrorF("vertTotal: 0x%X \n", vertTotal); + ErrorF("vertBlankStart: 0x%X \n", vertBlankStart); + ErrorF("vertBlankEnd: 0x%X \n", vertBlankEnd); +#endif + + /* + * compute correct Hsync & Vsync polarity + */ + if ((mode->flags & (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC)) + && (mode->flags & (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC))) { + + regp->MiscOutReg = 0x23; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + regp->MiscOutReg |= 0x40; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + regp->MiscOutReg |= 0x80; + } else { + int vdisplay = mode->vdisplay; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + vdisplay *= 2; + if (mode->vscan > 1) + vdisplay *= mode->vscan; + if (vdisplay < 400) + regp->MiscOutReg = 0xA3; /* +hsync -vsync */ + else if (vdisplay < 480) + regp->MiscOutReg = 0x63; /* -hsync +vsync */ + else if (vdisplay < 768) + regp->MiscOutReg = 0xE3; /* -hsync -vsync */ + else + regp->MiscOutReg = 0x23; /* +hsync +vsync */ + } + + regp->MiscOutReg |= (mode->clock_index & 0x03) << 2; + + /* + * Time Sequencer + */ + regp->Sequencer[NV_VIO_SR_RESET_INDEX] = 0x00; + /* 0x20 disables the sequencer */ + if (mode->flags & DRM_MODE_FLAG_CLKDIV2) + regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x29; + else + regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x21; + regp->Sequencer[NV_VIO_SR_PLANE_MASK_INDEX] = 0x0F; + regp->Sequencer[NV_VIO_SR_CHAR_MAP_INDEX] = 0x00; + regp->Sequencer[NV_VIO_SR_MEM_MODE_INDEX] = 0x0E; + + /* + * CRTC + */ + regp->CRTC[NV_CIO_CR_HDT_INDEX] = horizTotal; + regp->CRTC[NV_CIO_CR_HDE_INDEX] = horizDisplay; + regp->CRTC[NV_CIO_CR_HBS_INDEX] = horizBlankStart; + regp->CRTC[NV_CIO_CR_HBE_INDEX] = (1 << 7) | + XLATE(horizBlankEnd, 0, NV_CIO_CR_HBE_4_0); + regp->CRTC[NV_CIO_CR_HRS_INDEX] = horizStart; + regp->CRTC[NV_CIO_CR_HRE_INDEX] = XLATE(horizBlankEnd, 5, NV_CIO_CR_HRE_HBE_5) | + XLATE(horizEnd, 0, NV_CIO_CR_HRE_4_0); + regp->CRTC[NV_CIO_CR_VDT_INDEX] = vertTotal; + regp->CRTC[NV_CIO_CR_OVL_INDEX] = XLATE(vertStart, 9, NV_CIO_CR_OVL_VRS_9) | + XLATE(vertDisplay, 9, NV_CIO_CR_OVL_VDE_9) | + XLATE(vertTotal, 9, NV_CIO_CR_OVL_VDT_9) | + (1 << 4) | + XLATE(vertBlankStart, 8, NV_CIO_CR_OVL_VBS_8) | + XLATE(vertStart, 8, NV_CIO_CR_OVL_VRS_8) | + XLATE(vertDisplay, 8, NV_CIO_CR_OVL_VDE_8) | + XLATE(vertTotal, 8, NV_CIO_CR_OVL_VDT_8); + regp->CRTC[NV_CIO_CR_RSAL_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_CELL_HT_INDEX] = ((mode->flags & DRM_MODE_FLAG_DBLSCAN) ? MASK(NV_CIO_CR_CELL_HT_SCANDBL) : 0) | + 1 << 6 | + XLATE(vertBlankStart, 9, NV_CIO_CR_CELL_HT_VBS_9); + regp->CRTC[NV_CIO_CR_CURS_ST_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_CURS_END_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_SA_HI_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_SA_LO_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_TCOFF_HI_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_TCOFF_LO_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_VRS_INDEX] = vertStart; + regp->CRTC[NV_CIO_CR_VRE_INDEX] = 1 << 5 | XLATE(vertEnd, 0, NV_CIO_CR_VRE_3_0); + regp->CRTC[NV_CIO_CR_VDE_INDEX] = vertDisplay; + /* framebuffer can be larger than crtc scanout area. */ + regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = fb->pitches[0] / 8; + regp->CRTC[NV_CIO_CR_ULINE_INDEX] = 0x00; + regp->CRTC[NV_CIO_CR_VBS_INDEX] = vertBlankStart; + regp->CRTC[NV_CIO_CR_VBE_INDEX] = vertBlankEnd; + regp->CRTC[NV_CIO_CR_MODE_INDEX] = 0x43; + regp->CRTC[NV_CIO_CR_LCOMP_INDEX] = 0xff; + + /* + * Some extended CRTC registers (they are not saved with the rest of the vga regs). + */ + + /* framebuffer can be larger than crtc scanout area. */ + regp->CRTC[NV_CIO_CRE_RPC0_INDEX] = + XLATE(fb->pitches[0] / 8, 8, NV_CIO_CRE_RPC0_OFFSET_10_8); + regp->CRTC[NV_CIO_CRE_42] = + XLATE(fb->pitches[0] / 8, 11, NV_CIO_CRE_42_OFFSET_11); + regp->CRTC[NV_CIO_CRE_RPC1_INDEX] = mode->crtc_hdisplay < 1280 ? + MASK(NV_CIO_CRE_RPC1_LARGE) : 0x00; + regp->CRTC[NV_CIO_CRE_LSR_INDEX] = XLATE(horizBlankEnd, 6, NV_CIO_CRE_LSR_HBE_6) | + XLATE(vertBlankStart, 10, NV_CIO_CRE_LSR_VBS_10) | + XLATE(vertStart, 10, NV_CIO_CRE_LSR_VRS_10) | + XLATE(vertDisplay, 10, NV_CIO_CRE_LSR_VDE_10) | + XLATE(vertTotal, 10, NV_CIO_CRE_LSR_VDT_10); + regp->CRTC[NV_CIO_CRE_HEB__INDEX] = XLATE(horizStart, 8, NV_CIO_CRE_HEB_HRS_8) | + XLATE(horizBlankStart, 8, NV_CIO_CRE_HEB_HBS_8) | + XLATE(horizDisplay, 8, NV_CIO_CRE_HEB_HDE_8) | + XLATE(horizTotal, 8, NV_CIO_CRE_HEB_HDT_8); + regp->CRTC[NV_CIO_CRE_EBR_INDEX] = XLATE(vertBlankStart, 11, NV_CIO_CRE_EBR_VBS_11) | + XLATE(vertStart, 11, NV_CIO_CRE_EBR_VRS_11) | + XLATE(vertDisplay, 11, NV_CIO_CRE_EBR_VDE_11) | + XLATE(vertTotal, 11, NV_CIO_CRE_EBR_VDT_11); + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + horizTotal = (horizTotal >> 1) & ~1; + regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = horizTotal; + regp->CRTC[NV_CIO_CRE_HEB__INDEX] |= XLATE(horizTotal, 8, NV_CIO_CRE_HEB_ILC_8); + } else + regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = 0xff; /* interlace off */ + + /* + * Graphics Display Controller + */ + regp->Graphics[NV_VIO_GX_SR_INDEX] = 0x00; + regp->Graphics[NV_VIO_GX_SREN_INDEX] = 0x00; + regp->Graphics[NV_VIO_GX_CCOMP_INDEX] = 0x00; + regp->Graphics[NV_VIO_GX_ROP_INDEX] = 0x00; + regp->Graphics[NV_VIO_GX_READ_MAP_INDEX] = 0x00; + regp->Graphics[NV_VIO_GX_MODE_INDEX] = 0x40; /* 256 color mode */ + regp->Graphics[NV_VIO_GX_MISC_INDEX] = 0x05; /* map 64k mem + graphic mode */ + regp->Graphics[NV_VIO_GX_DONT_CARE_INDEX] = 0x0F; + regp->Graphics[NV_VIO_GX_BIT_MASK_INDEX] = 0xFF; + + regp->Attribute[0] = 0x00; /* standard colormap translation */ + regp->Attribute[1] = 0x01; + regp->Attribute[2] = 0x02; + regp->Attribute[3] = 0x03; + regp->Attribute[4] = 0x04; + regp->Attribute[5] = 0x05; + regp->Attribute[6] = 0x06; + regp->Attribute[7] = 0x07; + regp->Attribute[8] = 0x08; + regp->Attribute[9] = 0x09; + regp->Attribute[10] = 0x0A; + regp->Attribute[11] = 0x0B; + regp->Attribute[12] = 0x0C; + regp->Attribute[13] = 0x0D; + regp->Attribute[14] = 0x0E; + regp->Attribute[15] = 0x0F; + regp->Attribute[NV_CIO_AR_MODE_INDEX] = 0x01; /* Enable graphic mode */ + /* Non-vga */ + regp->Attribute[NV_CIO_AR_OSCAN_INDEX] = 0x00; + regp->Attribute[NV_CIO_AR_PLANE_INDEX] = 0x0F; /* enable all color planes */ + regp->Attribute[NV_CIO_AR_HPP_INDEX] = 0x00; + regp->Attribute[NV_CIO_AR_CSEL_INDEX] = 0x00; +} + +/** + * Sets up registers for the given mode/adjusted_mode pair. + * + * The clocks, CRTCs and outputs attached to this CRTC must be off. + * + * This shouldn't enable any clocks, CRTCs, or outputs, but they should + * be easily turned on/off after this. + */ +static void +nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode) +{ + struct drm_device *dev = crtc->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + struct nv04_crtc_reg *savep = &nv04_display(dev)->saved_reg.crtc_reg[nv_crtc->index]; + struct drm_encoder *encoder; + bool lvds_output = false, tmds_output = false, tv_output = false, + off_chip_digital = false; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + bool digital = false; + + if (encoder->crtc != crtc) + continue; + + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) + digital = lvds_output = true; + if (nv_encoder->dcb->type == DCB_OUTPUT_TV) + tv_output = true; + if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS) + digital = tmds_output = true; + if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && digital) + off_chip_digital = true; + } + + /* Registers not directly related to the (s)vga mode */ + + /* What is the meaning of this register? */ + /* A few popular values are 0x18, 0x1c, 0x38, 0x3c */ + regp->CRTC[NV_CIO_CRE_ENH_INDEX] = savep->CRTC[NV_CIO_CRE_ENH_INDEX] & ~(1<<5); + + regp->crtc_eng_ctrl = 0; + /* Except for rare conditions I2C is enabled on the primary crtc */ + if (nv_crtc->index == 0) + regp->crtc_eng_ctrl |= NV_CRTC_FSEL_I2C; +#if 0 + /* Set overlay to desired crtc. */ + if (dev->overlayAdaptor) { + NVPortPrivPtr pPriv = GET_OVERLAY_PRIVATE(dev); + if (pPriv->overlayCRTC == nv_crtc->index) + regp->crtc_eng_ctrl |= NV_CRTC_FSEL_OVERLAY; + } +#endif + + /* ADDRESS_SPACE_PNVM is the same as setting HCUR_ASI */ + regp->cursor_cfg = NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 | + NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 | + NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM; + if (nv_device(drm->device)->chipset >= 0x11) + regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE; + + /* Unblock some timings */ + regp->CRTC[NV_CIO_CRE_53] = 0; + regp->CRTC[NV_CIO_CRE_54] = 0; + + /* 0x00 is disabled, 0x11 is lvds, 0x22 crt and 0x88 tmds */ + if (lvds_output) + regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x11; + else if (tmds_output) + regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x88; + else + regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x22; + + /* These values seem to vary */ + /* This register seems to be used by the bios to make certain decisions on some G70 cards? */ + regp->CRTC[NV_CIO_CRE_SCRATCH4__INDEX] = savep->CRTC[NV_CIO_CRE_SCRATCH4__INDEX]; + + nv_crtc_set_digital_vibrance(crtc, nv_crtc->saturation); + + /* probably a scratch reg, but kept for cargo-cult purposes: + * bit0: crtc0?, head A + * bit6: lvds, head A + * bit7: (only in X), head A + */ + if (nv_crtc->index == 0) + regp->CRTC[NV_CIO_CRE_4B] = savep->CRTC[NV_CIO_CRE_4B] | 0x80; + + /* The blob seems to take the current value from crtc 0, add 4 to that + * and reuse the old value for crtc 1 */ + regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] = nv04_display(dev)->saved_reg.crtc_reg[0].CRTC[NV_CIO_CRE_TVOUT_LATENCY]; + if (!nv_crtc->index) + regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] += 4; + + /* the blob sometimes sets |= 0x10 (which is the same as setting |= + * 1 << 30 on 0x60.830), for no apparent reason */ + regp->CRTC[NV_CIO_CRE_59] = off_chip_digital; + + if (nv_device(drm->device)->card_type >= NV_30) + regp->CRTC[0x9f] = off_chip_digital ? 0x11 : 0x1; + + regp->crtc_830 = mode->crtc_vdisplay - 3; + regp->crtc_834 = mode->crtc_vdisplay - 1; + + if (nv_device(drm->device)->card_type == NV_40) + /* This is what the blob does */ + regp->crtc_850 = NVReadCRTC(dev, 0, NV_PCRTC_850); + + if (nv_device(drm->device)->card_type >= NV_30) + regp->gpio_ext = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT); + + if (nv_device(drm->device)->card_type >= NV_10) + regp->crtc_cfg = NV10_PCRTC_CONFIG_START_ADDRESS_HSYNC; + else + regp->crtc_cfg = NV04_PCRTC_CONFIG_START_ADDRESS_HSYNC; + + /* Some misc regs */ + if (nv_device(drm->device)->card_type == NV_40) { + regp->CRTC[NV_CIO_CRE_85] = 0xFF; + regp->CRTC[NV_CIO_CRE_86] = 0x1; + } + + regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (crtc->fb->depth + 1) / 8; + /* Enable slaved mode (called MODE_TV in nv4ref.h) */ + if (lvds_output || tmds_output || tv_output) + regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (1 << 7); + + /* Generic PRAMDAC regs */ + + if (nv_device(drm->device)->card_type >= NV_10) + /* Only bit that bios and blob set. */ + regp->nv10_cursync = (1 << 25); + + regp->ramdac_gen_ctrl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | + NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL | + NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON; + if (crtc->fb->depth == 16) + regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; + if (nv_device(drm->device)->chipset >= 0x11) + regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG; + + regp->ramdac_630 = 0; /* turn off green mode (tv test pattern?) */ + regp->tv_setup = 0; + + nv_crtc_set_image_sharpening(crtc, nv_crtc->sharpness); + + /* Some values the blob sets */ + regp->ramdac_8c0 = 0x100; + regp->ramdac_a20 = 0x0; + regp->ramdac_a24 = 0xfffff; + regp->ramdac_a34 = 0x1; +} + +/** + * Sets up registers for the given mode/adjusted_mode pair. + * + * The clocks, CRTCs and outputs attached to this CRTC must be off. + * + * This shouldn't enable any clocks, CRTCs, or outputs, but they should + * be easily turned on/off after this. + */ +static int +nv_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nouveau_drm *drm = nouveau_drm(dev); + + NV_DEBUG(drm, "CTRC mode on CRTC %d:\n", nv_crtc->index); + drm_mode_debug_printmodeline(adjusted_mode); + + /* unlock must come after turning off FP_TG_CONTROL in output_prepare */ + nv_lock_vga_crtc_shadow(dev, nv_crtc->index, -1); + + nv_crtc_mode_set_vga(crtc, adjusted_mode); + /* calculated in nv04_dfp_prepare, nv40 needs it written before calculating PLLs */ + if (nv_device(drm->device)->card_type == NV_40) + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, nv04_display(dev)->mode_reg.sel_clk); + nv_crtc_mode_set_regs(crtc, adjusted_mode); + nv_crtc_calc_state_ext(crtc, mode, adjusted_mode->clock); + return 0; +} + +static void nv_crtc_save(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; + struct nv04_crtc_reg *crtc_state = &state->crtc_reg[nv_crtc->index]; + struct nv04_mode_state *saved = &nv04_display(dev)->saved_reg; + struct nv04_crtc_reg *crtc_saved = &saved->crtc_reg[nv_crtc->index]; + + if (nv_two_heads(crtc->dev)) + NVSetOwner(crtc->dev, nv_crtc->index); + + nouveau_hw_save_state(crtc->dev, nv_crtc->index, saved); + + /* init some state to saved value */ + state->sel_clk = saved->sel_clk & ~(0x5 << 16); + crtc_state->CRTC[NV_CIO_CRE_LCD__INDEX] = crtc_saved->CRTC[NV_CIO_CRE_LCD__INDEX]; + state->pllsel = saved->pllsel & ~(PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK); + crtc_state->gpio_ext = crtc_saved->gpio_ext; +} + +static void nv_crtc_restore(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = crtc->dev; + int head = nv_crtc->index; + uint8_t saved_cr21 = nv04_display(dev)->saved_reg.crtc_reg[head].CRTC[NV_CIO_CRE_21]; + + if (nv_two_heads(crtc->dev)) + NVSetOwner(crtc->dev, head); + + nouveau_hw_load_state(crtc->dev, head, &nv04_display(dev)->saved_reg); + nv_lock_vga_crtc_shadow(crtc->dev, head, saved_cr21); + + nv_crtc->last_dpms = NV_DPMS_CLEARED; +} + +static void nv_crtc_prepare(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_crtc_helper_funcs *funcs = crtc->helper_private; + + if (nv_two_heads(dev)) + NVSetOwner(dev, nv_crtc->index); + + drm_vblank_pre_modeset(dev, nv_crtc->index); + funcs->dpms(crtc, DRM_MODE_DPMS_OFF); + + NVBlankScreen(dev, nv_crtc->index, true); + + /* Some more preparation. */ + NVWriteCRTC(dev, nv_crtc->index, NV_PCRTC_CONFIG, NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA); + if (nv_device(drm->device)->card_type == NV_40) { + uint32_t reg900 = NVReadRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900); + NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900, reg900 & ~0x10000); + } +} + +static void nv_crtc_commit(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_crtc_helper_funcs *funcs = crtc->helper_private; + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + + nouveau_hw_load_state(dev, nv_crtc->index, &nv04_display(dev)->mode_reg); + nv04_crtc_mode_set_base(crtc, crtc->x, crtc->y, NULL); + +#ifdef __BIG_ENDIAN + /* turn on LFB swapping */ + { + uint8_t tmp = NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR); + tmp |= MASK(NV_CIO_CRE_RCR_ENDIAN_BIG); + NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR, tmp); + } +#endif + + funcs->dpms(crtc, DRM_MODE_DPMS_ON); + drm_vblank_post_modeset(dev, nv_crtc->index); +} + +static void nv_crtc_destroy(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + + if (!nv_crtc) + return; + + drm_crtc_cleanup(crtc); + + nouveau_bo_unmap(nv_crtc->cursor.nvbo); + nouveau_bo_unpin(nv_crtc->cursor.nvbo); + nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); + kfree(nv_crtc); +} + +static void +nv_crtc_gamma_load(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = nv_crtc->base.dev; + struct rgb { uint8_t r, g, b; } __attribute__((packed)) *rgbs; + int i; + + rgbs = (struct rgb *)nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index].DAC; + for (i = 0; i < 256; i++) { + rgbs[i].r = nv_crtc->lut.r[i] >> 8; + rgbs[i].g = nv_crtc->lut.g[i] >> 8; + rgbs[i].b = nv_crtc->lut.b[i] >> 8; + } + + nouveau_hw_load_state_palette(dev, nv_crtc->index, &nv04_display(dev)->mode_reg); +} + +static void +nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t start, + uint32_t size) +{ + int end = (start + size > 256) ? 256 : start + size, i; + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + + for (i = start; i < end; i++) { + nv_crtc->lut.r[i] = r[i]; + nv_crtc->lut.g[i] = g[i]; + nv_crtc->lut.b[i] = b[i]; + } + + /* We need to know the depth before we upload, but it's possible to + * get called before a framebuffer is bound. If this is the case, + * mark the lut values as dirty by setting depth==0, and it'll be + * uploaded on the first mode_set_base() + */ + if (!nv_crtc->base.fb) { + nv_crtc->lut.depth = 0; + return; + } + + nv_crtc_gamma_load(crtc); +} + +static int +nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, + struct drm_framebuffer *passed_fb, + int x, int y, bool atomic) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + struct drm_framebuffer *drm_fb; + struct nouveau_framebuffer *fb; + int arb_burst, arb_lwm; + int ret; + + NV_DEBUG(drm, "index %d\n", nv_crtc->index); + + /* no fb bound */ + if (!atomic && !crtc->fb) { + NV_DEBUG(drm, "No FB bound\n"); + return 0; + } + + + /* If atomic, we want to switch to the fb we were passed, so + * now we update pointers to do that. (We don't pin; just + * assume we're already pinned and update the base address.) + */ + if (atomic) { + drm_fb = passed_fb; + fb = nouveau_framebuffer(passed_fb); + } else { + drm_fb = crtc->fb; + fb = nouveau_framebuffer(crtc->fb); + /* If not atomic, we can go ahead and pin, and unpin the + * old fb we were passed. + */ + ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM); + if (ret) + return ret; + + if (passed_fb) { + struct nouveau_framebuffer *ofb = nouveau_framebuffer(passed_fb); + nouveau_bo_unpin(ofb->nvbo); + } + } + + nv_crtc->fb.offset = fb->nvbo->bo.offset; + + if (nv_crtc->lut.depth != drm_fb->depth) { + nv_crtc->lut.depth = drm_fb->depth; + nv_crtc_gamma_load(crtc); + } + + /* Update the framebuffer format. */ + regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] &= ~3; + regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (crtc->fb->depth + 1) / 8; + regp->ramdac_gen_ctrl &= ~NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; + if (crtc->fb->depth == 16) + regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_PIXEL_INDEX); + NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_GENERAL_CONTROL, + regp->ramdac_gen_ctrl); + + regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = drm_fb->pitches[0] >> 3; + regp->CRTC[NV_CIO_CRE_RPC0_INDEX] = + XLATE(drm_fb->pitches[0] >> 3, 8, NV_CIO_CRE_RPC0_OFFSET_10_8); + regp->CRTC[NV_CIO_CRE_42] = + XLATE(drm_fb->pitches[0] / 8, 11, NV_CIO_CRE_42_OFFSET_11); + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_RPC0_INDEX); + crtc_wr_cio_state(crtc, regp, NV_CIO_CR_OFFSET_INDEX); + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_42); + + /* Update the framebuffer location. */ + regp->fb_start = nv_crtc->fb.offset & ~3; + regp->fb_start += (y * drm_fb->pitches[0]) + (x * drm_fb->bits_per_pixel / 8); + nv_set_crtc_base(dev, nv_crtc->index, regp->fb_start); + + /* Update the arbitration parameters. */ + nouveau_calc_arb(dev, crtc->mode.clock, drm_fb->bits_per_pixel, + &arb_burst, &arb_lwm); + + regp->CRTC[NV_CIO_CRE_FF_INDEX] = arb_burst; + regp->CRTC[NV_CIO_CRE_FFLWM__INDEX] = arb_lwm & 0xff; + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FF_INDEX); + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FFLWM__INDEX); + + if (nv_device(drm->device)->card_type >= NV_20) { + regp->CRTC[NV_CIO_CRE_47] = arb_lwm >> 8; + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_47); + } + + return 0; +} + +static int +nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + return nv04_crtc_do_mode_set_base(crtc, old_fb, x, y, false); +} + +static int +nv04_crtc_mode_set_base_atomic(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int x, int y, enum mode_set_atomic state) +{ + struct nouveau_drm *drm = nouveau_drm(crtc->dev); + struct drm_device *dev = drm->dev; + + if (state == ENTER_ATOMIC_MODE_SET) + nouveau_fbcon_save_disable_accel(dev); + else + nouveau_fbcon_restore_accel(dev); + + return nv04_crtc_do_mode_set_base(crtc, fb, x, y, true); +} + +static void nv04_cursor_upload(struct drm_device *dev, struct nouveau_bo *src, + struct nouveau_bo *dst) +{ + int width = nv_cursor_width(dev); + uint32_t pixel; + int i, j; + + for (i = 0; i < width; i++) { + for (j = 0; j < width; j++) { + pixel = nouveau_bo_rd32(src, i*64 + j); + + nouveau_bo_wr16(dst, i*width + j, (pixel & 0x80000000) >> 16 + | (pixel & 0xf80000) >> 9 + | (pixel & 0xf800) >> 6 + | (pixel & 0xf8) >> 3); + } + } +} + +static void nv11_cursor_upload(struct drm_device *dev, struct nouveau_bo *src, + struct nouveau_bo *dst) +{ + uint32_t pixel; + int alpha, i; + + /* nv11+ supports premultiplied (PM), or non-premultiplied (NPM) alpha + * cursors (though NPM in combination with fp dithering may not work on + * nv11, from "nv" driver history) + * NPM mode needs NV_PCRTC_CURSOR_CONFIG_ALPHA_BLEND set and is what the + * blob uses, however we get given PM cursors so we use PM mode + */ + for (i = 0; i < 64 * 64; i++) { + pixel = nouveau_bo_rd32(src, i); + + /* hw gets unhappy if alpha <= rgb values. for a PM image "less + * than" shouldn't happen; fix "equal to" case by adding one to + * alpha channel (slightly inaccurate, but so is attempting to + * get back to NPM images, due to limits of integer precision) + */ + alpha = pixel >> 24; + if (alpha > 0 && alpha < 255) + pixel = (pixel & 0x00ffffff) | ((alpha + 1) << 24); + +#ifdef __BIG_ENDIAN + { + struct nouveau_drm *drm = nouveau_drm(dev); + + if (nv_device(drm->device)->chipset == 0x11) { + pixel = ((pixel & 0x000000ff) << 24) | + ((pixel & 0x0000ff00) << 8) | + ((pixel & 0x00ff0000) >> 8) | + ((pixel & 0xff000000) >> 24); + } + } +#endif + + nouveau_bo_wr32(dst, i, pixel); + } +} + +static int +nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, + uint32_t buffer_handle, uint32_t width, uint32_t height) +{ + struct nouveau_drm *drm = nouveau_drm(crtc->dev); + struct drm_device *dev = drm->dev; + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nouveau_bo *cursor = NULL; + struct drm_gem_object *gem; + int ret = 0; + + if (!buffer_handle) { + nv_crtc->cursor.hide(nv_crtc, true); + return 0; + } + + if (width != 64 || height != 64) + return -EINVAL; + + gem = drm_gem_object_lookup(dev, file_priv, buffer_handle); + if (!gem) + return -ENOENT; + cursor = nouveau_gem_object(gem); + + ret = nouveau_bo_map(cursor); + if (ret) + goto out; + + if (nv_device(drm->device)->chipset >= 0x11) + nv11_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo); + else + nv04_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo); + + nouveau_bo_unmap(cursor); + nv_crtc->cursor.offset = nv_crtc->cursor.nvbo->bo.offset; + nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset); + nv_crtc->cursor.show(nv_crtc, true); +out: + drm_gem_object_unreference_unlocked(gem); + return ret; +} + +static int +nv04_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + + nv_crtc->cursor.set_pos(nv_crtc, x, y); + return 0; +} + +static const struct drm_crtc_funcs nv04_crtc_funcs = { + .save = nv_crtc_save, + .restore = nv_crtc_restore, + .cursor_set = nv04_crtc_cursor_set, + .cursor_move = nv04_crtc_cursor_move, + .gamma_set = nv_crtc_gamma_set, + .set_config = drm_crtc_helper_set_config, + .page_flip = nouveau_crtc_page_flip, + .destroy = nv_crtc_destroy, +}; + +static const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = { + .dpms = nv_crtc_dpms, + .prepare = nv_crtc_prepare, + .commit = nv_crtc_commit, + .mode_fixup = nv_crtc_mode_fixup, + .mode_set = nv_crtc_mode_set, + .mode_set_base = nv04_crtc_mode_set_base, + .mode_set_base_atomic = nv04_crtc_mode_set_base_atomic, + .load_lut = nv_crtc_gamma_load, +}; + +int +nv04_crtc_create(struct drm_device *dev, int crtc_num) +{ + struct nouveau_crtc *nv_crtc; + int ret, i; + + nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL); + if (!nv_crtc) + return -ENOMEM; + + for (i = 0; i < 256; i++) { + nv_crtc->lut.r[i] = i << 8; + nv_crtc->lut.g[i] = i << 8; + nv_crtc->lut.b[i] = i << 8; + } + nv_crtc->lut.depth = 0; + + nv_crtc->index = crtc_num; + nv_crtc->last_dpms = NV_DPMS_CLEARED; + + drm_crtc_init(dev, &nv_crtc->base, &nv04_crtc_funcs); + drm_crtc_helper_add(&nv_crtc->base, &nv04_crtc_helper_funcs); + drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256); + + ret = nouveau_bo_new(dev, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, + 0, 0x0000, NULL, &nv_crtc->cursor.nvbo); + if (!ret) { + ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); + if (!ret) { + ret = nouveau_bo_map(nv_crtc->cursor.nvbo); + if (ret) + nouveau_bo_unpin(nv_crtc->cursor.nvbo); + } + if (ret) + nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); + } + + nv04_cursor_init(nv_crtc); + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/dispnv04/cursor.c b/drivers/gpu/drm/nouveau/dispnv04/cursor.c new file mode 100644 index 0000000..a810303 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv04/cursor.c @@ -0,0 +1,70 @@ +#include +#include +#include "nouveau_drm.h" +#include "nouveau_reg.h" +#include "nouveau_crtc.h" +#include "hw.h" + +static void +nv04_cursor_show(struct nouveau_crtc *nv_crtc, bool update) +{ + nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, true); +} + +static void +nv04_cursor_hide(struct nouveau_crtc *nv_crtc, bool update) +{ + nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, false); +} + +static void +nv04_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y) +{ + nv_crtc->cursor_saved_x = x; nv_crtc->cursor_saved_y = y; + NVWriteRAMDAC(nv_crtc->base.dev, nv_crtc->index, + NV_PRAMDAC_CU_START_POS, + XLATE(y, 0, NV_PRAMDAC_CU_START_POS_Y) | + XLATE(x, 0, NV_PRAMDAC_CU_START_POS_X)); +} + +static void +crtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index) +{ + NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index, + crtcstate->CRTC[index]); +} + +static void +nv04_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset) +{ + struct drm_device *dev = nv_crtc->base.dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + struct drm_crtc *crtc = &nv_crtc->base; + + regp->CRTC[NV_CIO_CRE_HCUR_ADDR0_INDEX] = + MASK(NV_CIO_CRE_HCUR_ASI) | + XLATE(offset, 17, NV_CIO_CRE_HCUR_ADDR0_ADR); + regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] = + XLATE(offset, 11, NV_CIO_CRE_HCUR_ADDR1_ADR); + if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN) + regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] |= + MASK(NV_CIO_CRE_HCUR_ADDR1_CUR_DBL); + regp->CRTC[NV_CIO_CRE_HCUR_ADDR2_INDEX] = offset >> 24; + + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX); + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX); + if (nv_device(drm->device)->card_type == NV_40) + nv_fix_nv40_hw_cursor(dev, nv_crtc->index); +} + +int +nv04_cursor_init(struct nouveau_crtc *crtc) +{ + crtc->cursor.set_offset = nv04_cursor_set_offset; + crtc->cursor.set_pos = nv04_cursor_set_pos; + crtc->cursor.hide = nv04_cursor_hide; + crtc->cursor.show = nv04_cursor_show; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/dispnv04/dac.c b/drivers/gpu/drm/nouveau/dispnv04/dac.c new file mode 100644 index 0000000..434b920 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv04/dac.c @@ -0,0 +1,556 @@ +/* + * Copyright 2003 NVIDIA, Corporation + * Copyright 2006 Dave Airlie + * Copyright 2007 Maarten Maathuis + * Copyright 2007-2009 Stuart Bennett + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "nouveau_drm.h" +#include "nouveau_encoder.h" +#include "nouveau_connector.h" +#include "nouveau_crtc.h" +#include "hw.h" +#include "nvreg.h" + +#include +#include +#include + +int nv04_dac_output_offset(struct drm_encoder *encoder) +{ + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + int offset = 0; + + if (dcb->or & (8 | DCB_OUTPUT_C)) + offset += 0x68; + if (dcb->or & (8 | DCB_OUTPUT_B)) + offset += 0x2000; + + return offset; +} + +/* + * arbitrary limit to number of sense oscillations tolerated in one sample + * period (observed to be at least 13 in "nvidia") + */ +#define MAX_HBLANK_OSC 20 + +/* + * arbitrary limit to number of conflicting sample pairs to tolerate at a + * voltage step (observed to be at least 5 in "nvidia") + */ +#define MAX_SAMPLE_PAIRS 10 + +static int sample_load_twice(struct drm_device *dev, bool sense[2]) +{ + struct nouveau_device *device = nouveau_dev(dev); + struct nouveau_timer *ptimer = nouveau_timer(device); + int i; + + for (i = 0; i < 2; i++) { + bool sense_a, sense_b, sense_b_prime; + int j = 0; + + /* + * wait for bit 0 clear -- out of hblank -- (say reg value 0x4), + * then wait for transition 0x4->0x5->0x4: enter hblank, leave + * hblank again + * use a 10ms timeout (guards against crtc being inactive, in + * which case blank state would never change) + */ + if (!nouveau_timer_wait_eq(ptimer, 10000000, + NV_PRMCIO_INP0__COLOR, + 0x00000001, 0x00000000)) + return -EBUSY; + if (!nouveau_timer_wait_eq(ptimer, 10000000, + NV_PRMCIO_INP0__COLOR, + 0x00000001, 0x00000001)) + return -EBUSY; + if (!nouveau_timer_wait_eq(ptimer, 10000000, + NV_PRMCIO_INP0__COLOR, + 0x00000001, 0x00000000)) + return -EBUSY; + + udelay(100); + /* when level triggers, sense is _LO_ */ + sense_a = nv_rd08(device, NV_PRMCIO_INP0) & 0x10; + + /* take another reading until it agrees with sense_a... */ + do { + udelay(100); + sense_b = nv_rd08(device, NV_PRMCIO_INP0) & 0x10; + if (sense_a != sense_b) { + sense_b_prime = + nv_rd08(device, NV_PRMCIO_INP0) & 0x10; + if (sense_b == sense_b_prime) { + /* ... unless two consecutive subsequent + * samples agree; sense_a is replaced */ + sense_a = sense_b; + /* force mis-match so we loop */ + sense_b = !sense_a; + } + } + } while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC); + + if (j == MAX_HBLANK_OSC) + /* with so much oscillation, default to sense:LO */ + sense[i] = false; + else + sense[i] = sense_a; + } + + return 0; +} + +static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_device *device = nouveau_dev(dev); + struct nouveau_drm *drm = nouveau_drm(dev); + uint8_t saved_seq1, saved_pi, saved_rpc1, saved_cr_mode; + uint8_t saved_palette0[3], saved_palette_mask; + uint32_t saved_rtest_ctrl, saved_rgen_ctrl; + int i; + uint8_t blue; + bool sense = true; + + /* + * for this detection to work, there needs to be a mode set up on the + * CRTC. this is presumed to be the case + */ + + if (nv_two_heads(dev)) + /* only implemented for head A for now */ + NVSetOwner(dev, 0); + + saved_cr_mode = NVReadVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX); + NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode | 0x80); + + saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX); + NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20); + + saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, + saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); + + msleep(10); + + saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX); + NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, + saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT))); + saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX); + NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0); + + nv_wr08(device, NV_PRMDIO_READ_MODE_ADDRESS, 0x0); + for (i = 0; i < 3; i++) + saved_palette0[i] = nv_rd08(device, NV_PRMDIO_PALETTE_DATA); + saved_palette_mask = nv_rd08(device, NV_PRMDIO_PIXEL_MASK); + nv_wr08(device, NV_PRMDIO_PIXEL_MASK, 0); + + saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, + (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | + NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) | + NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON); + + blue = 8; /* start of test range */ + + do { + bool sense_pair[2]; + + nv_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); + nv_wr08(device, NV_PRMDIO_PALETTE_DATA, 0); + nv_wr08(device, NV_PRMDIO_PALETTE_DATA, 0); + /* testing blue won't find monochrome monitors. I don't care */ + nv_wr08(device, NV_PRMDIO_PALETTE_DATA, blue); + + i = 0; + /* take sample pairs until both samples in the pair agree */ + do { + if (sample_load_twice(dev, sense_pair)) + goto out; + } while ((sense_pair[0] != sense_pair[1]) && + ++i < MAX_SAMPLE_PAIRS); + + if (i == MAX_SAMPLE_PAIRS) + /* too much oscillation defaults to LO */ + sense = false; + else + sense = sense_pair[0]; + + /* + * if sense goes LO before blue ramps to 0x18, monitor is not connected. + * ergo, if blue gets to 0x18, monitor must be connected + */ + } while (++blue < 0x18 && sense); + +out: + nv_wr08(device, NV_PRMDIO_PIXEL_MASK, saved_palette_mask); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl); + nv_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); + for (i = 0; i < 3; i++) + nv_wr08(device, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl); + NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi); + NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1); + NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1); + NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode); + + if (blue == 0x18) { + NV_DEBUG(drm, "Load detected on head A\n"); + return connector_status_connected; + } + + return connector_status_disconnected; +} + +uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_device *device = nouveau_dev(dev); + struct nouveau_gpio *gpio = nouveau_gpio(device); + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder); + uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, + saved_rtest_ctrl, saved_gpio0 = 0, saved_gpio1 = 0, temp, routput; + int head; + +#define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20) + if (dcb->type == DCB_OUTPUT_TV) { + testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0); + + if (drm->vbios.tvdactestval) + testval = drm->vbios.tvdactestval; + } else { + testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */ + + if (drm->vbios.dactestval) + testval = drm->vbios.dactestval; + } + + saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, + saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); + + saved_powerctrl_2 = nv_rd32(device, NV_PBUS_POWERCTRL_2); + + nv_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff); + if (regoffset == 0x68) { + saved_powerctrl_4 = nv_rd32(device, NV_PBUS_POWERCTRL_4); + nv_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf); + } + + if (gpio) { + saved_gpio1 = gpio->get(gpio, 0, DCB_GPIO_TVDAC1, 0xff); + saved_gpio0 = gpio->get(gpio, 0, DCB_GPIO_TVDAC0, 0xff); + gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, dcb->type == DCB_OUTPUT_TV); + gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, dcb->type == DCB_OUTPUT_TV); + } + + msleep(4); + + saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); + head = (saved_routput & 0x100) >> 8; + + /* if there's a spare crtc, using it will minimise flicker */ + if (!(NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX) & 0xC0)) + head ^= 1; + + /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */ + routput = (saved_routput & 0xfffffece) | head << 8; + + if (nv_device(drm->device)->card_type >= NV_40) { + if (dcb->type == DCB_OUTPUT_TV) + routput |= 0x1a << 16; + else + routput &= ~(0x1a << 16); + } + + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput); + msleep(1); + + temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1); + + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, + NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval); + temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, + temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); + msleep(5); + + sample = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); + /* do it again just in case it's a residual current */ + sample &= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); + + temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, + temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0); + + /* bios does something more complex for restoring, but I think this is good enough */ + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl); + if (regoffset == 0x68) + nv_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4); + nv_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2); + + if (gpio) { + gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, saved_gpio1); + gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, saved_gpio0); + } + + return sample; +} + +static enum drm_connector_status +nv17_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) +{ + struct nouveau_drm *drm = nouveau_drm(encoder->dev); + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + + if (nv04_dac_in_use(encoder)) + return connector_status_disconnected; + + if (nv17_dac_sample_load(encoder) & + NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI) { + NV_DEBUG(drm, "Load detected on output %c\n", + '@' + ffs(dcb->or)); + return connector_status_connected; + } else { + return connector_status_disconnected; + } +} + +static bool nv04_dac_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + if (nv04_dac_in_use(encoder)) + return false; + + return true; +} + +static void nv04_dac_prepare(struct drm_encoder *encoder) +{ + struct drm_encoder_helper_funcs *helper = encoder->helper_private; + struct drm_device *dev = encoder->dev; + int head = nouveau_crtc(encoder->crtc)->index; + + helper->dpms(encoder, DRM_MODE_DPMS_OFF); + + nv04_dfp_disable(dev, head); +} + +static void nv04_dac_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + int head = nouveau_crtc(encoder->crtc)->index; + + if (nv_gf4_disp_arch(dev)) { + struct drm_encoder *rebind; + uint32_t dac_offset = nv04_dac_output_offset(encoder); + uint32_t otherdac; + + /* bit 16-19 are bits that are set on some G70 cards, + * but don't seem to have much effect */ + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, + head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK); + /* force any other vga encoders to bind to the other crtc */ + list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) { + if (rebind == encoder + || nouveau_encoder(rebind)->dcb->type != DCB_OUTPUT_ANALOG) + continue; + + dac_offset = nv04_dac_output_offset(rebind); + otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, + (otherdac & ~0x0100) | (head ^ 1) << 8); + } + } + + /* This could use refinement for flatpanels, but it should work this way */ + if (nv_device(drm->device)->chipset < 0x44) + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); + else + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); +} + +static void nv04_dac_commit(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_drm *drm = nouveau_drm(encoder->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct drm_encoder_helper_funcs *helper = encoder->helper_private; + + helper->dpms(encoder, DRM_MODE_DPMS_ON); + + NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n", + drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), + nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); +} + +void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable) +{ + struct drm_device *dev = encoder->dev; + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + + if (nv_gf4_disp_arch(dev)) { + uint32_t *dac_users = &nv04_display(dev)->dac_users[ffs(dcb->or) - 1]; + int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder); + uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off); + + if (enable) { + *dac_users |= 1 << dcb->index; + NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK); + + } else { + *dac_users &= ~(1 << dcb->index); + if (!*dac_users) + NVWriteRAMDAC(dev, 0, dacclk_off, + dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK); + } + } +} + +/* Check if the DAC corresponding to 'encoder' is being used by + * someone else. */ +bool nv04_dac_in_use(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + + return nv_gf4_disp_arch(encoder->dev) && + (nv04_display(dev)->dac_users[ffs(dcb->or) - 1] & ~(1 << dcb->index)); +} + +static void nv04_dac_dpms(struct drm_encoder *encoder, int mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_drm *drm = nouveau_drm(encoder->dev); + + if (nv_encoder->last_dpms == mode) + return; + nv_encoder->last_dpms = mode; + + NV_DEBUG(drm, "Setting dpms mode %d on vga encoder (output %d)\n", + mode, nv_encoder->dcb->index); + + nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); +} + +static void nv04_dac_save(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + + if (nv_gf4_disp_arch(dev)) + nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + + nv04_dac_output_offset(encoder)); +} + +static void nv04_dac_restore(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + + if (nv_gf4_disp_arch(dev)) + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder), + nv_encoder->restore.output); + + nv_encoder->last_dpms = NV_DPMS_CLEARED; +} + +static void nv04_dac_destroy(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + + drm_encoder_cleanup(encoder); + kfree(nv_encoder); +} + +static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = { + .dpms = nv04_dac_dpms, + .save = nv04_dac_save, + .restore = nv04_dac_restore, + .mode_fixup = nv04_dac_mode_fixup, + .prepare = nv04_dac_prepare, + .commit = nv04_dac_commit, + .mode_set = nv04_dac_mode_set, + .detect = nv04_dac_detect +}; + +static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = { + .dpms = nv04_dac_dpms, + .save = nv04_dac_save, + .restore = nv04_dac_restore, + .mode_fixup = nv04_dac_mode_fixup, + .prepare = nv04_dac_prepare, + .commit = nv04_dac_commit, + .mode_set = nv04_dac_mode_set, + .detect = nv17_dac_detect +}; + +static const struct drm_encoder_funcs nv04_dac_funcs = { + .destroy = nv04_dac_destroy, +}; + +int +nv04_dac_create(struct drm_connector *connector, struct dcb_output *entry) +{ + const struct drm_encoder_helper_funcs *helper; + struct nouveau_encoder *nv_encoder = NULL; + struct drm_device *dev = connector->dev; + struct drm_encoder *encoder; + + nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); + if (!nv_encoder) + return -ENOMEM; + + encoder = to_drm_encoder(nv_encoder); + + nv_encoder->dcb = entry; + nv_encoder->or = ffs(entry->or) - 1; + + if (nv_gf4_disp_arch(dev)) + helper = &nv17_dac_helper_funcs; + else + helper = &nv04_dac_helper_funcs; + + drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC); + drm_encoder_helper_add(encoder, helper); + + encoder->possible_crtcs = entry->heads; + encoder->possible_clones = 0; + + drm_mode_connector_attach_encoder(connector, encoder); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/dispnv04/dfp.c b/drivers/gpu/drm/nouveau/dispnv04/dfp.c new file mode 100644 index 0000000..93dd23ff --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv04/dfp.c @@ -0,0 +1,720 @@ +/* + * Copyright 2003 NVIDIA, Corporation + * Copyright 2006 Dave Airlie + * Copyright 2007 Maarten Maathuis + * Copyright 2007-2009 Stuart Bennett + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "nouveau_drm.h" +#include "nouveau_reg.h" +#include "nouveau_encoder.h" +#include "nouveau_connector.h" +#include "nouveau_crtc.h" +#include "hw.h" +#include "nvreg.h" + +#include + +#include + +#define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \ + NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \ + NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS) +#define FP_TG_CONTROL_OFF (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE | \ + NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE | \ + NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE) + +static inline bool is_fpc_off(uint32_t fpc) +{ + return ((fpc & (FP_TG_CONTROL_ON | FP_TG_CONTROL_OFF)) == + FP_TG_CONTROL_OFF); +} + +int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_output *dcbent) +{ + /* special case of nv_read_tmds to find crtc associated with an output. + * this does not give a correct answer for off-chip dvi, but there's no + * use for such an answer anyway + */ + int ramdac = (dcbent->or & DCB_OUTPUT_C) >> 2; + + NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL, + NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | 0x4); + return ((NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA) & 0x8) >> 3) ^ ramdac; +} + +void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_output *dcbent, + int head, bool dl) +{ + /* The BIOS scripts don't do this for us, sadly + * Luckily we do know the values ;-) + * + * head < 0 indicates we wish to force a setting with the overrideval + * (for VT restore etc.) + */ + + int ramdac = (dcbent->or & DCB_OUTPUT_C) >> 2; + uint8_t tmds04 = 0x80; + + if (head != ramdac) + tmds04 = 0x88; + + if (dcbent->type == DCB_OUTPUT_LVDS) + tmds04 |= 0x01; + + nv_write_tmds(dev, dcbent->or, 0, 0x04, tmds04); + + if (dl) /* dual link */ + nv_write_tmds(dev, dcbent->or, 1, 0x04, tmds04 ^ 0x08); +} + +void nv04_dfp_disable(struct drm_device *dev, int head) +{ + struct nv04_crtc_reg *crtcstate = nv04_display(dev)->mode_reg.crtc_reg; + + if (NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL) & + FP_TG_CONTROL_ON) { + /* digital remnants must be cleaned before new crtc + * values programmed. delay is time for the vga stuff + * to realise it's in control again + */ + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, + FP_TG_CONTROL_OFF); + msleep(50); + } + /* don't inadvertently turn it on when state written later */ + crtcstate[head].fp_control = FP_TG_CONTROL_OFF; + crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] &= + ~NV_CIO_CRE_LCD_ROUTE_MASK; +} + +void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_crtc *crtc; + struct nouveau_crtc *nv_crtc; + uint32_t *fpc; + + if (mode == DRM_MODE_DPMS_ON) { + nv_crtc = nouveau_crtc(encoder->crtc); + fpc = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index].fp_control; + + if (is_fpc_off(*fpc)) { + /* using saved value is ok, as (is_digital && dpms_on && + * fp_control==OFF) is (at present) *only* true when + * fpc's most recent change was by below "off" code + */ + *fpc = nv_crtc->dpms_saved_fp_control; + } + + nv_crtc->fp_users |= 1 << nouveau_encoder(encoder)->dcb->index; + NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_FP_TG_CONTROL, *fpc); + } else { + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + nv_crtc = nouveau_crtc(crtc); + fpc = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index].fp_control; + + nv_crtc->fp_users &= ~(1 << nouveau_encoder(encoder)->dcb->index); + if (!is_fpc_off(*fpc) && !nv_crtc->fp_users) { + nv_crtc->dpms_saved_fp_control = *fpc; + /* cut the FP output */ + *fpc &= ~FP_TG_CONTROL_ON; + *fpc |= FP_TG_CONTROL_OFF; + NVWriteRAMDAC(dev, nv_crtc->index, + NV_PRAMDAC_FP_TG_CONTROL, *fpc); + } + } + } +} + +static struct drm_encoder *get_tmds_slave(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + struct drm_encoder *slave; + + if (dcb->type != DCB_OUTPUT_TMDS || dcb->location == DCB_LOC_ON_CHIP) + return NULL; + + /* Some BIOSes (e.g. the one in a Quadro FX1000) report several + * TMDS transmitters at the same I2C address, in the same I2C + * bus. This can still work because in that case one of them is + * always hard-wired to a reasonable configuration using straps, + * and the other one needs to be programmed. + * + * I don't think there's a way to know which is which, even the + * blob programs the one exposed via I2C for *both* heads, so + * let's do the same. + */ + list_for_each_entry(slave, &dev->mode_config.encoder_list, head) { + struct dcb_output *slave_dcb = nouveau_encoder(slave)->dcb; + + if (slave_dcb->type == DCB_OUTPUT_TMDS && get_slave_funcs(slave) && + slave_dcb->tmdsconf.slave_addr == dcb->tmdsconf.slave_addr) + return slave; + } + + return NULL; +} + +static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder); + + if (!nv_connector->native_mode || + nv_connector->scaling_mode == DRM_MODE_SCALE_NONE || + mode->hdisplay > nv_connector->native_mode->hdisplay || + mode->vdisplay > nv_connector->native_mode->vdisplay) { + nv_encoder->mode = *adjusted_mode; + + } else { + nv_encoder->mode = *nv_connector->native_mode; + adjusted_mode->clock = nv_connector->native_mode->clock; + } + + return true; +} + +static void nv04_dfp_prepare_sel_clk(struct drm_device *dev, + struct nouveau_encoder *nv_encoder, int head) +{ + struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; + uint32_t bits1618 = nv_encoder->dcb->or & DCB_OUTPUT_A ? 0x10000 : 0x40000; + + if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP) + return; + + /* SEL_CLK is only used on the primary ramdac + * It toggles spread spectrum PLL output and sets the bindings of PLLs + * to heads on digital outputs + */ + if (head) + state->sel_clk |= bits1618; + else + state->sel_clk &= ~bits1618; + + /* nv30: + * bit 0 NVClk spread spectrum on/off + * bit 2 MemClk spread spectrum on/off + * bit 4 PixClk1 spread spectrum on/off toggle + * bit 6 PixClk2 spread spectrum on/off toggle + * + * nv40 (observations from bios behaviour and mmio traces): + * bits 4&6 as for nv30 + * bits 5&7 head dependent as for bits 4&6, but do not appear with 4&6; + * maybe a different spread mode + * bits 8&10 seen on dual-link dvi outputs, purpose unknown (set by POST scripts) + * The logic behind turning spread spectrum on/off in the first place, + * and which bit-pair to use, is unclear on nv40 (for earlier cards, the fp table + * entry has the necessary info) + */ + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS && nv04_display(dev)->saved_reg.sel_clk & 0xf0) { + int shift = (nv04_display(dev)->saved_reg.sel_clk & 0x50) ? 0 : 1; + + state->sel_clk &= ~0xf0; + state->sel_clk |= (head ? 0x40 : 0x10) << shift; + } +} + +static void nv04_dfp_prepare(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_encoder_helper_funcs *helper = encoder->helper_private; + struct drm_device *dev = encoder->dev; + int head = nouveau_crtc(encoder->crtc)->index; + struct nv04_crtc_reg *crtcstate = nv04_display(dev)->mode_reg.crtc_reg; + uint8_t *cr_lcd = &crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX]; + uint8_t *cr_lcd_oth = &crtcstate[head ^ 1].CRTC[NV_CIO_CRE_LCD__INDEX]; + + helper->dpms(encoder, DRM_MODE_DPMS_OFF); + + nv04_dfp_prepare_sel_clk(dev, nv_encoder, head); + + *cr_lcd = (*cr_lcd & ~NV_CIO_CRE_LCD_ROUTE_MASK) | 0x3; + + if (nv_two_heads(dev)) { + if (nv_encoder->dcb->location == DCB_LOC_ON_CHIP) + *cr_lcd |= head ? 0x0 : 0x8; + else { + *cr_lcd |= (nv_encoder->dcb->or << 4) & 0x30; + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) + *cr_lcd |= 0x30; + if ((*cr_lcd & 0x30) == (*cr_lcd_oth & 0x30)) { + /* avoid being connected to both crtcs */ + *cr_lcd_oth &= ~0x30; + NVWriteVgaCrtc(dev, head ^ 1, + NV_CIO_CRE_LCD__INDEX, + *cr_lcd_oth); + } + } + } +} + + +static void nv04_dfp_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_device *device = nouveau_dev(dev); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + struct nv04_crtc_reg *savep = &nv04_display(dev)->saved_reg.crtc_reg[nv_crtc->index]; + struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_display_mode *output_mode = &nv_encoder->mode; + struct drm_connector *connector = &nv_connector->base; + uint32_t mode_ratio, panel_ratio; + + NV_DEBUG(drm, "Output mode on CRTC %d:\n", nv_crtc->index); + drm_mode_debug_printmodeline(output_mode); + + /* Initialize the FP registers in this CRTC. */ + regp->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1; + regp->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1; + if (!nv_gf4_disp_arch(dev) || + (output_mode->hsync_start - output_mode->hdisplay) >= + drm->vbios.digital_min_front_porch) + regp->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay; + else + regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start - drm->vbios.digital_min_front_porch - 1; + regp->fp_horiz_regs[FP_SYNC_START] = output_mode->hsync_start - 1; + regp->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1; + regp->fp_horiz_regs[FP_VALID_START] = output_mode->hskew; + regp->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - 1; + + regp->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1; + regp->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1; + regp->fp_vert_regs[FP_CRTC] = output_mode->vtotal - 5 - 1; + regp->fp_vert_regs[FP_SYNC_START] = output_mode->vsync_start - 1; + regp->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1; + regp->fp_vert_regs[FP_VALID_START] = 0; + regp->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - 1; + + /* bit26: a bit seen on some g7x, no as yet discernable purpose */ + regp->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | + (savep->fp_control & (1 << 26 | NV_PRAMDAC_FP_TG_CONTROL_READ_PROG)); + /* Deal with vsync/hsync polarity */ + /* LVDS screens do set this, but modes with +ve syncs are very rare */ + if (output_mode->flags & DRM_MODE_FLAG_PVSYNC) + regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS; + if (output_mode->flags & DRM_MODE_FLAG_PHSYNC) + regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS; + /* panel scaling first, as native would get set otherwise */ + if (nv_connector->scaling_mode == DRM_MODE_SCALE_NONE || + nv_connector->scaling_mode == DRM_MODE_SCALE_CENTER) /* panel handles it */ + regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER; + else if (adjusted_mode->hdisplay == output_mode->hdisplay && + adjusted_mode->vdisplay == output_mode->vdisplay) /* native mode */ + regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE; + else /* gpu needs to scale */ + regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE; + if (nv_rd32(device, NV_PEXTDEV_BOOT_0) & NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT) + regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12; + if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && + output_mode->clock > 165000) + regp->fp_control |= (2 << 24); + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) { + bool duallink = false, dummy; + if (nv_connector->edid && + nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { + duallink = (((u8 *)nv_connector->edid)[121] == 2); + } else { + nouveau_bios_parse_lvds_table(dev, output_mode->clock, + &duallink, &dummy); + } + + if (duallink) + regp->fp_control |= (8 << 28); + } else + if (output_mode->clock > 165000) + regp->fp_control |= (8 << 28); + + regp->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND | + NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND | + NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR | + NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR | + NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED | + NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE | + NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE; + + /* We want automatic scaling */ + regp->fp_debug_1 = 0; + /* This can override HTOTAL and VTOTAL */ + regp->fp_debug_2 = 0; + + /* Use 20.12 fixed point format to avoid floats */ + mode_ratio = (1 << 12) * adjusted_mode->hdisplay / adjusted_mode->vdisplay; + panel_ratio = (1 << 12) * output_mode->hdisplay / output_mode->vdisplay; + /* if ratios are equal, SCALE_ASPECT will automatically (and correctly) + * get treated the same as SCALE_FULLSCREEN */ + if (nv_connector->scaling_mode == DRM_MODE_SCALE_ASPECT && + mode_ratio != panel_ratio) { + uint32_t diff, scale; + bool divide_by_2 = nv_gf4_disp_arch(dev); + + if (mode_ratio < panel_ratio) { + /* vertical needs to expand to glass size (automatic) + * horizontal needs to be scaled at vertical scale factor + * to maintain aspect */ + + scale = (1 << 12) * adjusted_mode->vdisplay / output_mode->vdisplay; + regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE | + XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE); + + /* restrict area of screen used, horizontally */ + diff = output_mode->hdisplay - + output_mode->vdisplay * mode_ratio / (1 << 12); + regp->fp_horiz_regs[FP_VALID_START] += diff / 2; + regp->fp_horiz_regs[FP_VALID_END] -= diff / 2; + } + + if (mode_ratio > panel_ratio) { + /* horizontal needs to expand to glass size (automatic) + * vertical needs to be scaled at horizontal scale factor + * to maintain aspect */ + + scale = (1 << 12) * adjusted_mode->hdisplay / output_mode->hdisplay; + regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE | + XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE); + + /* restrict area of screen used, vertically */ + diff = output_mode->vdisplay - + (1 << 12) * output_mode->hdisplay / mode_ratio; + regp->fp_vert_regs[FP_VALID_START] += diff / 2; + regp->fp_vert_regs[FP_VALID_END] -= diff / 2; + } + } + + /* Output property. */ + if ((nv_connector->dithering_mode == DITHERING_MODE_ON) || + (nv_connector->dithering_mode == DITHERING_MODE_AUTO && + encoder->crtc->fb->depth > connector->display_info.bpc * 3)) { + if (nv_device(drm->device)->chipset == 0x11) + regp->dither = savep->dither | 0x00010000; + else { + int i; + regp->dither = savep->dither | 0x00000001; + for (i = 0; i < 3; i++) { + regp->dither_regs[i] = 0xe4e4e4e4; + regp->dither_regs[i + 3] = 0x44444444; + } + } + } else { + if (nv_device(drm->device)->chipset != 0x11) { + /* reset them */ + int i; + for (i = 0; i < 3; i++) { + regp->dither_regs[i] = savep->dither_regs[i]; + regp->dither_regs[i + 3] = savep->dither_regs[i + 3]; + } + } + regp->dither = savep->dither; + } + + regp->fp_margin_color = 0; +} + +static void nv04_dfp_commit(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct drm_encoder_helper_funcs *helper = encoder->helper_private; + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct dcb_output *dcbe = nv_encoder->dcb; + int head = nouveau_crtc(encoder->crtc)->index; + struct drm_encoder *slave_encoder; + + if (dcbe->type == DCB_OUTPUT_TMDS) + run_tmds_table(dev, dcbe, head, nv_encoder->mode.clock); + else if (dcbe->type == DCB_OUTPUT_LVDS) + call_lvds_script(dev, dcbe, head, LVDS_RESET, nv_encoder->mode.clock); + + /* update fp_control state for any changes made by scripts, + * so correct value is written at DPMS on */ + nv04_display(dev)->mode_reg.crtc_reg[head].fp_control = + NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL); + + /* This could use refinement for flatpanels, but it should work this way */ + if (nv_device(drm->device)->chipset < 0x44) + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); + else + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); + + /* Init external transmitters */ + slave_encoder = get_tmds_slave(encoder); + if (slave_encoder) + get_slave_funcs(slave_encoder)->mode_set( + slave_encoder, &nv_encoder->mode, &nv_encoder->mode); + + helper->dpms(encoder, DRM_MODE_DPMS_ON); + + NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n", + drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), + nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); +} + +static void nv04_dfp_update_backlight(struct drm_encoder *encoder, int mode) +{ +#ifdef __powerpc__ + struct drm_device *dev = encoder->dev; + struct nouveau_device *device = nouveau_dev(dev); + + /* BIOS scripts usually take care of the backlight, thanks + * Apple for your consistency. + */ + if (dev->pci_device == 0x0174 || dev->pci_device == 0x0179 || + dev->pci_device == 0x0189 || dev->pci_device == 0x0329) { + if (mode == DRM_MODE_DPMS_ON) { + nv_mask(device, NV_PBUS_DEBUG_DUALHEAD_CTL, 0, 1 << 31); + nv_mask(device, NV_PCRTC_GPIO_EXT, 3, 1); + } else { + nv_mask(device, NV_PBUS_DEBUG_DUALHEAD_CTL, 1 << 31, 0); + nv_mask(device, NV_PCRTC_GPIO_EXT, 3, 0); + } + } +#endif +} + +static inline bool is_powersaving_dpms(int mode) +{ + return mode != DRM_MODE_DPMS_ON && mode != NV_DPMS_CLEARED; +} + +static void nv04_lvds_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_crtc *crtc = encoder->crtc; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + bool was_powersaving = is_powersaving_dpms(nv_encoder->last_dpms); + + if (nv_encoder->last_dpms == mode) + return; + nv_encoder->last_dpms = mode; + + NV_DEBUG(drm, "Setting dpms mode %d on lvds encoder (output %d)\n", + mode, nv_encoder->dcb->index); + + if (was_powersaving && is_powersaving_dpms(mode)) + return; + + if (nv_encoder->dcb->lvdsconf.use_power_scripts) { + /* when removing an output, crtc may not be set, but PANEL_OFF + * must still be run + */ + int head = crtc ? nouveau_crtc(crtc)->index : + nv04_dfp_get_bound_head(dev, nv_encoder->dcb); + + if (mode == DRM_MODE_DPMS_ON) { + call_lvds_script(dev, nv_encoder->dcb, head, + LVDS_PANEL_ON, nv_encoder->mode.clock); + } else + /* pxclk of 0 is fine for PANEL_OFF, and for a + * disconnected LVDS encoder there is no native_mode + */ + call_lvds_script(dev, nv_encoder->dcb, head, + LVDS_PANEL_OFF, 0); + } + + nv04_dfp_update_backlight(encoder, mode); + nv04_dfp_update_fp_control(encoder, mode); + + if (mode == DRM_MODE_DPMS_ON) + nv04_dfp_prepare_sel_clk(dev, nv_encoder, nouveau_crtc(crtc)->index); + else { + nv04_display(dev)->mode_reg.sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK); + nv04_display(dev)->mode_reg.sel_clk &= ~0xf0; + } + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, nv04_display(dev)->mode_reg.sel_clk); +} + +static void nv04_tmds_dpms(struct drm_encoder *encoder, int mode) +{ + struct nouveau_drm *drm = nouveau_drm(encoder->dev); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + + if (nv_encoder->last_dpms == mode) + return; + nv_encoder->last_dpms = mode; + + NV_DEBUG(drm, "Setting dpms mode %d on tmds encoder (output %d)\n", + mode, nv_encoder->dcb->index); + + nv04_dfp_update_backlight(encoder, mode); + nv04_dfp_update_fp_control(encoder, mode); +} + +static void nv04_dfp_save(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + + if (nv_two_heads(dev)) + nv_encoder->restore.head = + nv04_dfp_get_bound_head(dev, nv_encoder->dcb); +} + +static void nv04_dfp_restore(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + int head = nv_encoder->restore.head; + + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) { + struct nouveau_connector *connector = + nouveau_encoder_connector_get(nv_encoder); + + if (connector && connector->native_mode) + call_lvds_script(dev, nv_encoder->dcb, head, + LVDS_PANEL_ON, + connector->native_mode->clock); + + } else if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS) { + int clock = nouveau_hw_pllvals_to_clk + (&nv04_display(dev)->saved_reg.crtc_reg[head].pllvals); + + run_tmds_table(dev, nv_encoder->dcb, head, clock); + } + + nv_encoder->last_dpms = NV_DPMS_CLEARED; +} + +static void nv04_dfp_destroy(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + + if (get_slave_funcs(encoder)) + get_slave_funcs(encoder)->destroy(encoder); + + drm_encoder_cleanup(encoder); + kfree(nv_encoder); +} + +static void nv04_tmds_slave_init(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_i2c *i2c = nouveau_i2c(drm->device); + struct nouveau_i2c_port *port = i2c->find(i2c, 2); + struct i2c_board_info info[] = { + { + .type = "sil164", + .addr = (dcb->tmdsconf.slave_addr == 0x7 ? 0x3a : 0x38), + .platform_data = &(struct sil164_encoder_params) { + SIL164_INPUT_EDGE_RISING + } + }, + { } + }; + int type; + + if (!nv_gf4_disp_arch(dev) || !port || + get_tmds_slave(encoder)) + return; + + type = i2c->identify(i2c, 2, "TMDS transmitter", info, NULL); + if (type < 0) + return; + + drm_i2c_encoder_init(dev, to_encoder_slave(encoder), + &port->adapter, &info[type]); +} + +static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = { + .dpms = nv04_lvds_dpms, + .save = nv04_dfp_save, + .restore = nv04_dfp_restore, + .mode_fixup = nv04_dfp_mode_fixup, + .prepare = nv04_dfp_prepare, + .commit = nv04_dfp_commit, + .mode_set = nv04_dfp_mode_set, + .detect = NULL, +}; + +static const struct drm_encoder_helper_funcs nv04_tmds_helper_funcs = { + .dpms = nv04_tmds_dpms, + .save = nv04_dfp_save, + .restore = nv04_dfp_restore, + .mode_fixup = nv04_dfp_mode_fixup, + .prepare = nv04_dfp_prepare, + .commit = nv04_dfp_commit, + .mode_set = nv04_dfp_mode_set, + .detect = NULL, +}; + +static const struct drm_encoder_funcs nv04_dfp_funcs = { + .destroy = nv04_dfp_destroy, +}; + +int +nv04_dfp_create(struct drm_connector *connector, struct dcb_output *entry) +{ + const struct drm_encoder_helper_funcs *helper; + struct nouveau_encoder *nv_encoder = NULL; + struct drm_encoder *encoder; + int type; + + switch (entry->type) { + case DCB_OUTPUT_TMDS: + type = DRM_MODE_ENCODER_TMDS; + helper = &nv04_tmds_helper_funcs; + break; + case DCB_OUTPUT_LVDS: + type = DRM_MODE_ENCODER_LVDS; + helper = &nv04_lvds_helper_funcs; + break; + default: + return -EINVAL; + } + + nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); + if (!nv_encoder) + return -ENOMEM; + + encoder = to_drm_encoder(nv_encoder); + + nv_encoder->dcb = entry; + nv_encoder->or = ffs(entry->or) - 1; + + drm_encoder_init(connector->dev, encoder, &nv04_dfp_funcs, type); + drm_encoder_helper_add(encoder, helper); + + encoder->possible_crtcs = entry->heads; + encoder->possible_clones = 0; + + if (entry->type == DCB_OUTPUT_TMDS && + entry->location != DCB_LOC_ON_CHIP) + nv04_tmds_slave_init(encoder); + + drm_mode_connector_attach_encoder(connector, encoder); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.c b/drivers/gpu/drm/nouveau/dispnv04/disp.c new file mode 100644 index 0000000..4908d3f --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c @@ -0,0 +1,211 @@ +/* + * Copyright 2009 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Author: Ben Skeggs + */ + +#include +#include + +#include +#include + +#include "nouveau_drm.h" +#include "nouveau_reg.h" +#include "hw.h" +#include "nouveau_encoder.h" +#include "nouveau_connector.h" + +#include + +int +nv04_display_early_init(struct drm_device *dev) +{ + /* ensure vblank interrupts are off, they can't be enabled until + * drm_vblank has been initialised + */ + NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0); + if (nv_two_heads(dev)) + NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0); + + return 0; +} + +void +nv04_display_late_takedown(struct drm_device *dev) +{ +} + +int +nv04_display_create(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_i2c *i2c = nouveau_i2c(drm->device); + struct dcb_table *dcb = &drm->vbios.dcb; + struct drm_connector *connector, *ct; + struct drm_encoder *encoder; + struct drm_crtc *crtc; + struct nv04_display *disp; + int i, ret; + + disp = kzalloc(sizeof(*disp), GFP_KERNEL); + if (!disp) + return -ENOMEM; + + nouveau_display(dev)->priv = disp; + nouveau_display(dev)->dtor = nv04_display_destroy; + nouveau_display(dev)->init = nv04_display_init; + nouveau_display(dev)->fini = nv04_display_fini; + + nouveau_hw_save_vga_fonts(dev, 1); + + ret = nouveau_object_new(nv_object(drm), NVDRM_DEVICE, 0xd1500000, + NV04_DISP_CLASS, NULL, 0, &disp->core); + if (ret) + return ret; + + nv04_crtc_create(dev, 0); + if (nv_two_heads(dev)) + nv04_crtc_create(dev, 1); + + for (i = 0; i < dcb->entries; i++) { + struct dcb_output *dcbent = &dcb->entry[i]; + + connector = nouveau_connector_create(dev, dcbent->connector); + if (IS_ERR(connector)) + continue; + + switch (dcbent->type) { + case DCB_OUTPUT_ANALOG: + ret = nv04_dac_create(connector, dcbent); + break; + case DCB_OUTPUT_LVDS: + case DCB_OUTPUT_TMDS: + ret = nv04_dfp_create(connector, dcbent); + break; + case DCB_OUTPUT_TV: + if (dcbent->location == DCB_LOC_ON_CHIP) + ret = nv17_tv_create(connector, dcbent); + else + ret = nv04_tv_create(connector, dcbent); + break; + default: + NV_WARN(drm, "DCB type %d not known\n", dcbent->type); + continue; + } + + if (ret) + continue; + } + + list_for_each_entry_safe(connector, ct, + &dev->mode_config.connector_list, head) { + if (!connector->encoder_ids[0]) { + NV_WARN(drm, "%s has no encoders, removing\n", + drm_get_connector_name(connector)); + connector->funcs->destroy(connector); + } + } + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + nv_encoder->i2c = i2c->find(i2c, nv_encoder->dcb->i2c_index); + } + + /* Save previous state */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + crtc->funcs->save(crtc); + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + struct drm_encoder_helper_funcs *func = encoder->helper_private; + + func->save(encoder); + } + + return 0; +} + +void +nv04_display_destroy(struct drm_device *dev) +{ + struct nv04_display *disp = nv04_display(dev); + struct drm_encoder *encoder; + struct drm_crtc *crtc; + + /* Turn every CRTC off. */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct drm_mode_set modeset = { + .crtc = crtc, + }; + + drm_mode_set_config_internal(&modeset); + } + + /* Restore state */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + struct drm_encoder_helper_funcs *func = encoder->helper_private; + + func->restore(encoder); + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + crtc->funcs->restore(crtc); + + nouveau_hw_save_vga_fonts(dev, 0); + + nouveau_display(dev)->priv = NULL; + kfree(disp); +} + +int +nv04_display_init(struct drm_device *dev) +{ + struct drm_encoder *encoder; + struct drm_crtc *crtc; + + /* meh.. modeset apparently doesn't setup all the regs and depends + * on pre-existing state, for now load the state of the card *before* + * nouveau was loaded, and then do a modeset. + * + * best thing to do probably is to make save/restore routines not + * save/restore "pre-load" state, but more general so we can save + * on suspend too. + */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + struct drm_encoder_helper_funcs *func = encoder->helper_private; + + func->restore(encoder); + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + crtc->funcs->restore(crtc); + + return 0; +} + +void +nv04_display_fini(struct drm_device *dev) +{ + /* disable vblank interrupts */ + NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0); + if (nv_two_heads(dev)) + NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0); +} diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.h b/drivers/gpu/drm/nouveau/dispnv04/disp.h new file mode 100644 index 0000000..a0a031d --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.h @@ -0,0 +1,185 @@ +#ifndef __NV04_DISPLAY_H__ +#define __NV04_DISPLAY_H__ + +#include + +#include "nouveau_display.h" + +enum nv04_fp_display_regs { + FP_DISPLAY_END, + FP_TOTAL, + FP_CRTC, + FP_SYNC_START, + FP_SYNC_END, + FP_VALID_START, + FP_VALID_END +}; + +struct nv04_crtc_reg { + unsigned char MiscOutReg; + uint8_t CRTC[0xa0]; + uint8_t CR58[0x10]; + uint8_t Sequencer[5]; + uint8_t Graphics[9]; + uint8_t Attribute[21]; + unsigned char DAC[768]; + + /* PCRTC regs */ + uint32_t fb_start; + uint32_t crtc_cfg; + uint32_t cursor_cfg; + uint32_t gpio_ext; + uint32_t crtc_830; + uint32_t crtc_834; + uint32_t crtc_850; + uint32_t crtc_eng_ctrl; + + /* PRAMDAC regs */ + uint32_t nv10_cursync; + struct nouveau_pll_vals pllvals; + uint32_t ramdac_gen_ctrl; + uint32_t ramdac_630; + uint32_t ramdac_634; + uint32_t tv_setup; + uint32_t tv_vtotal; + uint32_t tv_vskew; + uint32_t tv_vsync_delay; + uint32_t tv_htotal; + uint32_t tv_hskew; + uint32_t tv_hsync_delay; + uint32_t tv_hsync_delay2; + uint32_t fp_horiz_regs[7]; + uint32_t fp_vert_regs[7]; + uint32_t dither; + uint32_t fp_control; + uint32_t dither_regs[6]; + uint32_t fp_debug_0; + uint32_t fp_debug_1; + uint32_t fp_debug_2; + uint32_t fp_margin_color; + uint32_t ramdac_8c0; + uint32_t ramdac_a20; + uint32_t ramdac_a24; + uint32_t ramdac_a34; + uint32_t ctv_regs[38]; +}; + +struct nv04_output_reg { + uint32_t output; + int head; +}; + +struct nv04_mode_state { + struct nv04_crtc_reg crtc_reg[2]; + uint32_t pllsel; + uint32_t sel_clk; +}; + +struct nv04_display { + struct nv04_mode_state mode_reg; + struct nv04_mode_state saved_reg; + uint32_t saved_vga_font[4][16384]; + uint32_t dac_users[4]; + struct nouveau_object *core; +}; + +static inline struct nv04_display * +nv04_display(struct drm_device *dev) +{ + return nouveau_display(dev)->priv; +} + +/* nv04_display.c */ +int nv04_display_early_init(struct drm_device *); +void nv04_display_late_takedown(struct drm_device *); +int nv04_display_create(struct drm_device *); +void nv04_display_destroy(struct drm_device *); +int nv04_display_init(struct drm_device *); +void nv04_display_fini(struct drm_device *); + +/* nv04_crtc.c */ +int nv04_crtc_create(struct drm_device *, int index); + +/* nv04_dac.c */ +int nv04_dac_create(struct drm_connector *, struct dcb_output *); +uint32_t nv17_dac_sample_load(struct drm_encoder *encoder); +int nv04_dac_output_offset(struct drm_encoder *encoder); +void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable); +bool nv04_dac_in_use(struct drm_encoder *encoder); + +/* nv04_dfp.c */ +int nv04_dfp_create(struct drm_connector *, struct dcb_output *); +int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_output *dcbent); +void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_output *dcbent, + int head, bool dl); +void nv04_dfp_disable(struct drm_device *dev, int head); +void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode); + +/* nv04_tv.c */ +int nv04_tv_identify(struct drm_device *dev, int i2c_index); +int nv04_tv_create(struct drm_connector *, struct dcb_output *); + +/* nv17_tv.c */ +int nv17_tv_create(struct drm_connector *, struct dcb_output *); + +static inline bool +nv_two_heads(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + const int impl = dev->pci_device & 0x0ff0; + + if (nv_device(drm->device)->card_type >= NV_10 && impl != 0x0100 && + impl != 0x0150 && impl != 0x01a0 && impl != 0x0200) + return true; + + return false; +} + +static inline bool +nv_gf4_disp_arch(struct drm_device *dev) +{ + return nv_two_heads(dev) && (dev->pci_device & 0x0ff0) != 0x0110; +} + +static inline bool +nv_two_reg_pll(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + const int impl = dev->pci_device & 0x0ff0; + + if (impl == 0x0310 || impl == 0x0340 || nv_device(drm->device)->card_type >= NV_40) + return true; + return false; +} + +static inline bool +nv_match_device(struct drm_device *dev, unsigned device, + unsigned sub_vendor, unsigned sub_device) +{ + return dev->pdev->device == device && + dev->pdev->subsystem_vendor == sub_vendor && + dev->pdev->subsystem_device == sub_device; +} + +#include +#include + +static inline void +nouveau_bios_run_init_table(struct drm_device *dev, u16 table, + struct dcb_output *outp, int crtc) +{ + struct nouveau_device *device = nouveau_dev(dev); + struct nouveau_bios *bios = nouveau_bios(device); + struct nvbios_init init = { + .subdev = nv_subdev(bios), + .bios = bios, + .offset = table, + .outp = outp, + .crtc = crtc, + .execute = 1, + }; + + nvbios_exec(&init); +} + +#endif diff --git a/drivers/gpu/drm/nouveau/dispnv04/hw.c b/drivers/gpu/drm/nouveau/dispnv04/hw.c new file mode 100644 index 0000000..973056b --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv04/hw.c @@ -0,0 +1,827 @@ +/* + * Copyright 2006 Dave Airlie + * Copyright 2007 Maarten Maathuis + * Copyright 2007-2009 Stuart Bennett + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "nouveau_drm.h" +#include "hw.h" + +#include +#include +#include + +#define CHIPSET_NFORCE 0x01a0 +#define CHIPSET_NFORCE2 0x01f0 + +/* + * misc hw access wrappers/control functions + */ + +void +NVWriteVgaSeq(struct drm_device *dev, int head, uint8_t index, uint8_t value) +{ + NVWritePRMVIO(dev, head, NV_PRMVIO_SRX, index); + NVWritePRMVIO(dev, head, NV_PRMVIO_SR, value); +} + +uint8_t +NVReadVgaSeq(struct drm_device *dev, int head, uint8_t index) +{ + NVWritePRMVIO(dev, head, NV_PRMVIO_SRX, index); + return NVReadPRMVIO(dev, head, NV_PRMVIO_SR); +} + +void +NVWriteVgaGr(struct drm_device *dev, int head, uint8_t index, uint8_t value) +{ + NVWritePRMVIO(dev, head, NV_PRMVIO_GRX, index); + NVWritePRMVIO(dev, head, NV_PRMVIO_GX, value); +} + +uint8_t +NVReadVgaGr(struct drm_device *dev, int head, uint8_t index) +{ + NVWritePRMVIO(dev, head, NV_PRMVIO_GRX, index); + return NVReadPRMVIO(dev, head, NV_PRMVIO_GX); +} + +/* CR44 takes values 0 (head A), 3 (head B) and 4 (heads tied) + * it affects only the 8 bit vga io regs, which we access using mmio at + * 0xc{0,2}3c*, 0x60{1,3}3*, and 0x68{1,3}3d* + * in general, the set value of cr44 does not matter: reg access works as + * expected and values can be set for the appropriate head by using a 0x2000 + * offset as required + * however: + * a) pre nv40, the head B range of PRMVIO regs at 0xc23c* was not exposed and + * cr44 must be set to 0 or 3 for accessing values on the correct head + * through the common 0xc03c* addresses + * b) in tied mode (4) head B is programmed to the values set on head A, and + * access using the head B addresses can have strange results, ergo we leave + * tied mode in init once we know to what cr44 should be restored on exit + * + * the owner parameter is slightly abused: + * 0 and 1 are treated as head values and so the set value is (owner * 3) + * other values are treated as literal values to set + */ +void +NVSetOwner(struct drm_device *dev, int owner) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + if (owner == 1) + owner *= 3; + + if (nv_device(drm->device)->chipset == 0x11) { + /* This might seem stupid, but the blob does it and + * omitting it often locks the system up. + */ + NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX); + NVReadVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX); + } + + /* CR44 is always changed on CRTC0 */ + NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_44, owner); + + if (nv_device(drm->device)->chipset == 0x11) { /* set me harder */ + NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner); + NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner); + } +} + +void +NVBlankScreen(struct drm_device *dev, int head, bool blank) +{ + unsigned char seq1; + + if (nv_two_heads(dev)) + NVSetOwner(dev, head); + + seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX); + + NVVgaSeqReset(dev, head, true); + if (blank) + NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20); + else + NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20); + NVVgaSeqReset(dev, head, false); +} + +/* + * PLL getting + */ + +static void +nouveau_hw_decode_pll(struct drm_device *dev, uint32_t reg1, uint32_t pll1, + uint32_t pll2, struct nouveau_pll_vals *pllvals) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + /* to force parsing as single stage (i.e. nv40 vplls) pass pll2 as 0 */ + + /* log2P is & 0x7 as never more than 7, and nv30/35 only uses 3 bits */ + pllvals->log2P = (pll1 >> 16) & 0x7; + pllvals->N2 = pllvals->M2 = 1; + + if (reg1 <= 0x405c) { + pllvals->NM1 = pll2 & 0xffff; + /* single stage NVPLL and VPLLs use 1 << 8, MPLL uses 1 << 12 */ + if (!(pll1 & 0x1100)) + pllvals->NM2 = pll2 >> 16; + } else { + pllvals->NM1 = pll1 & 0xffff; + if (nv_two_reg_pll(dev) && pll2 & NV31_RAMDAC_ENABLE_VCO2) + pllvals->NM2 = pll2 & 0xffff; + else if (nv_device(drm->device)->chipset == 0x30 || nv_device(drm->device)->chipset == 0x35) { + pllvals->M1 &= 0xf; /* only 4 bits */ + if (pll1 & NV30_RAMDAC_ENABLE_VCO2) { + pllvals->M2 = (pll1 >> 4) & 0x7; + pllvals->N2 = ((pll1 >> 21) & 0x18) | + ((pll1 >> 19) & 0x7); + } + } + } +} + +int +nouveau_hw_get_pllvals(struct drm_device *dev, enum nvbios_pll_type plltype, + struct nouveau_pll_vals *pllvals) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_device *device = nv_device(drm->device); + struct nouveau_bios *bios = nouveau_bios(device); + uint32_t reg1, pll1, pll2 = 0; + struct nvbios_pll pll_lim; + int ret; + + ret = nvbios_pll_parse(bios, plltype, &pll_lim); + if (ret || !(reg1 = pll_lim.reg)) + return -ENOENT; + + pll1 = nv_rd32(device, reg1); + if (reg1 <= 0x405c) + pll2 = nv_rd32(device, reg1 + 4); + else if (nv_two_reg_pll(dev)) { + uint32_t reg2 = reg1 + (reg1 == NV_RAMDAC_VPLL2 ? 0x5c : 0x70); + + pll2 = nv_rd32(device, reg2); + } + + if (nv_device(drm->device)->card_type == 0x40 && reg1 >= NV_PRAMDAC_VPLL_COEFF) { + uint32_t ramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580); + + /* check whether vpll has been forced into single stage mode */ + if (reg1 == NV_PRAMDAC_VPLL_COEFF) { + if (ramdac580 & NV_RAMDAC_580_VPLL1_ACTIVE) + pll2 = 0; + } else + if (ramdac580 & NV_RAMDAC_580_VPLL2_ACTIVE) + pll2 = 0; + } + + nouveau_hw_decode_pll(dev, reg1, pll1, pll2, pllvals); + pllvals->refclk = pll_lim.refclk; + return 0; +} + +int +nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pv) +{ + /* Avoid divide by zero if called at an inappropriate time */ + if (!pv->M1 || !pv->M2) + return 0; + + return pv->N1 * pv->N2 * pv->refclk / (pv->M1 * pv->M2) >> pv->log2P; +} + +int +nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype) +{ + struct nouveau_pll_vals pllvals; + int ret; + + if (plltype == PLL_MEMORY && + (dev->pci_device & 0x0ff0) == CHIPSET_NFORCE) { + uint32_t mpllP; + + pci_read_config_dword(pci_get_bus_and_slot(0, 3), 0x6c, &mpllP); + if (!mpllP) + mpllP = 4; + + return 400000 / mpllP; + } else + if (plltype == PLL_MEMORY && + (dev->pci_device & 0xff0) == CHIPSET_NFORCE2) { + uint32_t clock; + + pci_read_config_dword(pci_get_bus_and_slot(0, 5), 0x4c, &clock); + return clock; + } + + ret = nouveau_hw_get_pllvals(dev, plltype, &pllvals); + if (ret) + return ret; + + return nouveau_hw_pllvals_to_clk(&pllvals); +} + +static void +nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head) +{ + /* the vpll on an unused head can come up with a random value, way + * beyond the pll limits. for some reason this causes the chip to + * lock up when reading the dac palette regs, so set a valid pll here + * when such a condition detected. only seen on nv11 to date + */ + + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_device *device = nv_device(drm->device); + struct nouveau_clock *clk = nouveau_clock(device); + struct nouveau_bios *bios = nouveau_bios(device); + struct nvbios_pll pll_lim; + struct nouveau_pll_vals pv; + enum nvbios_pll_type pll = head ? PLL_VPLL1 : PLL_VPLL0; + + if (nvbios_pll_parse(bios, pll, &pll_lim)) + return; + nouveau_hw_get_pllvals(dev, pll, &pv); + + if (pv.M1 >= pll_lim.vco1.min_m && pv.M1 <= pll_lim.vco1.max_m && + pv.N1 >= pll_lim.vco1.min_n && pv.N1 <= pll_lim.vco1.max_n && + pv.log2P <= pll_lim.max_p) + return; + + NV_WARN(drm, "VPLL %d outwith limits, attempting to fix\n", head + 1); + + /* set lowest clock within static limits */ + pv.M1 = pll_lim.vco1.max_m; + pv.N1 = pll_lim.vco1.min_n; + pv.log2P = pll_lim.max_p_usable; + clk->pll_prog(clk, pll_lim.reg, &pv); +} + +/* + * vga font save/restore + */ + +static void nouveau_vga_font_io(struct drm_device *dev, + void __iomem *iovram, + bool save, unsigned plane) +{ + unsigned i; + + NVWriteVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX, 1 << plane); + NVWriteVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX, plane); + for (i = 0; i < 16384; i++) { + if (save) { + nv04_display(dev)->saved_vga_font[plane][i] = + ioread32_native(iovram + i * 4); + } else { + iowrite32_native(nv04_display(dev)->saved_vga_font[plane][i], + iovram + i * 4); + } + } +} + +void +nouveau_hw_save_vga_fonts(struct drm_device *dev, bool save) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + uint8_t misc, gr4, gr5, gr6, seq2, seq4; + bool graphicsmode; + unsigned plane; + void __iomem *iovram; + + if (nv_two_heads(dev)) + NVSetOwner(dev, 0); + + NVSetEnablePalette(dev, 0, true); + graphicsmode = NVReadVgaAttr(dev, 0, NV_CIO_AR_MODE_INDEX) & 1; + NVSetEnablePalette(dev, 0, false); + + if (graphicsmode) /* graphics mode => framebuffer => no need to save */ + return; + + NV_INFO(drm, "%sing VGA fonts\n", save ? "Sav" : "Restor"); + + /* map first 64KiB of VRAM, holds VGA fonts etc */ + iovram = ioremap(pci_resource_start(dev->pdev, 1), 65536); + if (!iovram) { + NV_ERROR(drm, "Failed to map VRAM, " + "cannot save/restore VGA fonts.\n"); + return; + } + + if (nv_two_heads(dev)) + NVBlankScreen(dev, 1, true); + NVBlankScreen(dev, 0, true); + + /* save control regs */ + misc = NVReadPRMVIO(dev, 0, NV_PRMVIO_MISC__READ); + seq2 = NVReadVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX); + seq4 = NVReadVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX); + gr4 = NVReadVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX); + gr5 = NVReadVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX); + gr6 = NVReadVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX); + + NVWritePRMVIO(dev, 0, NV_PRMVIO_MISC__WRITE, 0x67); + NVWriteVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX, 0x6); + NVWriteVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX, 0x0); + NVWriteVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX, 0x5); + + /* store font in planes 0..3 */ + for (plane = 0; plane < 4; plane++) + nouveau_vga_font_io(dev, iovram, save, plane); + + /* restore control regs */ + NVWritePRMVIO(dev, 0, NV_PRMVIO_MISC__WRITE, misc); + NVWriteVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX, gr4); + NVWriteVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX, gr5); + NVWriteVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX, gr6); + NVWriteVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX, seq2); + NVWriteVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX, seq4); + + if (nv_two_heads(dev)) + NVBlankScreen(dev, 1, false); + NVBlankScreen(dev, 0, false); + + iounmap(iovram); +} + +/* + * mode state save/load + */ + +static void +rd_cio_state(struct drm_device *dev, int head, + struct nv04_crtc_reg *crtcstate, int index) +{ + crtcstate->CRTC[index] = NVReadVgaCrtc(dev, head, index); +} + +static void +wr_cio_state(struct drm_device *dev, int head, + struct nv04_crtc_reg *crtcstate, int index) +{ + NVWriteVgaCrtc(dev, head, index, crtcstate->CRTC[index]); +} + +static void +nv_save_state_ramdac(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nv04_crtc_reg *regp = &state->crtc_reg[head]; + int i; + + if (nv_device(drm->device)->card_type >= NV_10) + regp->nv10_cursync = NVReadRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC); + + nouveau_hw_get_pllvals(dev, head ? PLL_VPLL1 : PLL_VPLL0, ®p->pllvals); + state->pllsel = NVReadRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT); + if (nv_two_heads(dev)) + state->sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK); + if (nv_device(drm->device)->chipset == 0x11) + regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11); + + regp->ramdac_gen_ctrl = NVReadRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL); + + if (nv_gf4_disp_arch(dev)) + regp->ramdac_630 = NVReadRAMDAC(dev, head, NV_PRAMDAC_630); + if (nv_device(drm->device)->chipset >= 0x30) + regp->ramdac_634 = NVReadRAMDAC(dev, head, NV_PRAMDAC_634); + + regp->tv_setup = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP); + regp->tv_vtotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL); + regp->tv_vskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW); + regp->tv_vsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY); + regp->tv_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL); + regp->tv_hskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW); + regp->tv_hsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY); + regp->tv_hsync_delay2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2); + + for (i = 0; i < 7; i++) { + uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4); + regp->fp_vert_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg); + regp->fp_horiz_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg + 0x20); + } + + if (nv_gf4_disp_arch(dev)) { + regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_FP_DITHER); + for (i = 0; i < 3; i++) { + regp->dither_regs[i] = NVReadRAMDAC(dev, head, NV_PRAMDAC_850 + i * 4); + regp->dither_regs[i + 3] = NVReadRAMDAC(dev, head, NV_PRAMDAC_85C + i * 4); + } + } + + regp->fp_control = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL); + regp->fp_debug_0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_0); + if (!nv_gf4_disp_arch(dev) && head == 0) { + /* early chips don't allow access to PRAMDAC_TMDS_* without + * the head A FPCLK on (nv11 even locks up) */ + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_FP_DEBUG_0, regp->fp_debug_0 & + ~NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK); + } + regp->fp_debug_1 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1); + regp->fp_debug_2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2); + + regp->fp_margin_color = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR); + + if (nv_gf4_disp_arch(dev)) + regp->ramdac_8c0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_8C0); + + if (nv_device(drm->device)->card_type == NV_40) { + regp->ramdac_a20 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A20); + regp->ramdac_a24 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A24); + regp->ramdac_a34 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A34); + + for (i = 0; i < 38; i++) + regp->ctv_regs[i] = NVReadRAMDAC(dev, head, + NV_PRAMDAC_CTV + 4*i); + } +} + +static void +nv_load_state_ramdac(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_clock *clk = nouveau_clock(drm->device); + struct nv04_crtc_reg *regp = &state->crtc_reg[head]; + uint32_t pllreg = head ? NV_RAMDAC_VPLL2 : NV_PRAMDAC_VPLL_COEFF; + int i; + + if (nv_device(drm->device)->card_type >= NV_10) + NVWriteRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC, regp->nv10_cursync); + + clk->pll_prog(clk, pllreg, ®p->pllvals); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel); + if (nv_two_heads(dev)) + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, state->sel_clk); + if (nv_device(drm->device)->chipset == 0x11) + NVWriteRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11, regp->dither); + + NVWriteRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL, regp->ramdac_gen_ctrl); + + if (nv_gf4_disp_arch(dev)) + NVWriteRAMDAC(dev, head, NV_PRAMDAC_630, regp->ramdac_630); + if (nv_device(drm->device)->chipset >= 0x30) + NVWriteRAMDAC(dev, head, NV_PRAMDAC_634, regp->ramdac_634); + + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, regp->tv_setup); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL, regp->tv_vtotal); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW, regp->tv_vskew); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY, regp->tv_vsync_delay); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL, regp->tv_htotal); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW, regp->tv_hskew); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY, regp->tv_hsync_delay); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2, regp->tv_hsync_delay2); + + for (i = 0; i < 7; i++) { + uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4); + + NVWriteRAMDAC(dev, head, ramdac_reg, regp->fp_vert_regs[i]); + NVWriteRAMDAC(dev, head, ramdac_reg + 0x20, regp->fp_horiz_regs[i]); + } + + if (nv_gf4_disp_arch(dev)) { + NVWriteRAMDAC(dev, head, NV_RAMDAC_FP_DITHER, regp->dither); + for (i = 0; i < 3; i++) { + NVWriteRAMDAC(dev, head, NV_PRAMDAC_850 + i * 4, regp->dither_regs[i]); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_85C + i * 4, regp->dither_regs[i + 3]); + } + } + + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, regp->fp_control); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_0, regp->fp_debug_0); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regp->fp_debug_1); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2, regp->fp_debug_2); + + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR, regp->fp_margin_color); + + if (nv_gf4_disp_arch(dev)) + NVWriteRAMDAC(dev, head, NV_PRAMDAC_8C0, regp->ramdac_8c0); + + if (nv_device(drm->device)->card_type == NV_40) { + NVWriteRAMDAC(dev, head, NV_PRAMDAC_A20, regp->ramdac_a20); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_A24, regp->ramdac_a24); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_A34, regp->ramdac_a34); + + for (i = 0; i < 38; i++) + NVWriteRAMDAC(dev, head, + NV_PRAMDAC_CTV + 4*i, regp->ctv_regs[i]); + } +} + +static void +nv_save_state_vga(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nv04_crtc_reg *regp = &state->crtc_reg[head]; + int i; + + regp->MiscOutReg = NVReadPRMVIO(dev, head, NV_PRMVIO_MISC__READ); + + for (i = 0; i < 25; i++) + rd_cio_state(dev, head, regp, i); + + NVSetEnablePalette(dev, head, true); + for (i = 0; i < 21; i++) + regp->Attribute[i] = NVReadVgaAttr(dev, head, i); + NVSetEnablePalette(dev, head, false); + + for (i = 0; i < 9; i++) + regp->Graphics[i] = NVReadVgaGr(dev, head, i); + + for (i = 0; i < 5; i++) + regp->Sequencer[i] = NVReadVgaSeq(dev, head, i); +} + +static void +nv_load_state_vga(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nv04_crtc_reg *regp = &state->crtc_reg[head]; + int i; + + NVWritePRMVIO(dev, head, NV_PRMVIO_MISC__WRITE, regp->MiscOutReg); + + for (i = 0; i < 5; i++) + NVWriteVgaSeq(dev, head, i, regp->Sequencer[i]); + + nv_lock_vga_crtc_base(dev, head, false); + for (i = 0; i < 25; i++) + wr_cio_state(dev, head, regp, i); + nv_lock_vga_crtc_base(dev, head, true); + + for (i = 0; i < 9; i++) + NVWriteVgaGr(dev, head, i, regp->Graphics[i]); + + NVSetEnablePalette(dev, head, true); + for (i = 0; i < 21; i++) + NVWriteVgaAttr(dev, head, i, regp->Attribute[i]); + NVSetEnablePalette(dev, head, false); +} + +static void +nv_save_state_ext(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nv04_crtc_reg *regp = &state->crtc_reg[head]; + int i; + + rd_cio_state(dev, head, regp, NV_CIO_CRE_LCD__INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_RPC0_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_RPC1_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_LSR_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_PIXEL_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_HEB__INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX); + + rd_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_21); + + if (nv_device(drm->device)->card_type >= NV_20) + rd_cio_state(dev, head, regp, NV_CIO_CRE_47); + + if (nv_device(drm->device)->card_type >= NV_30) + rd_cio_state(dev, head, regp, 0x9f); + + rd_cio_state(dev, head, regp, NV_CIO_CRE_49); + rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX); + + if (nv_device(drm->device)->card_type >= NV_10) { + regp->crtc_830 = NVReadCRTC(dev, head, NV_PCRTC_830); + regp->crtc_834 = NVReadCRTC(dev, head, NV_PCRTC_834); + + if (nv_device(drm->device)->card_type >= NV_30) + regp->gpio_ext = NVReadCRTC(dev, head, NV_PCRTC_GPIO_EXT); + + if (nv_device(drm->device)->card_type == NV_40) + regp->crtc_850 = NVReadCRTC(dev, head, NV_PCRTC_850); + + if (nv_two_heads(dev)) + regp->crtc_eng_ctrl = NVReadCRTC(dev, head, NV_PCRTC_ENGINE_CTRL); + regp->cursor_cfg = NVReadCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG); + } + + regp->crtc_cfg = NVReadCRTC(dev, head, NV_PCRTC_CONFIG); + + rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX); + if (nv_device(drm->device)->card_type >= NV_10) { + rd_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX); + rd_cio_state(dev, head, regp, NV_CIO_CRE_CSB); + rd_cio_state(dev, head, regp, NV_CIO_CRE_4B); + rd_cio_state(dev, head, regp, NV_CIO_CRE_TVOUT_LATENCY); + } + /* NV11 and NV20 don't have this, they stop at 0x52. */ + if (nv_gf4_disp_arch(dev)) { + rd_cio_state(dev, head, regp, NV_CIO_CRE_42); + rd_cio_state(dev, head, regp, NV_CIO_CRE_53); + rd_cio_state(dev, head, regp, NV_CIO_CRE_54); + + for (i = 0; i < 0x10; i++) + regp->CR58[i] = NVReadVgaCrtc5758(dev, head, i); + rd_cio_state(dev, head, regp, NV_CIO_CRE_59); + rd_cio_state(dev, head, regp, NV_CIO_CRE_5B); + + rd_cio_state(dev, head, regp, NV_CIO_CRE_85); + rd_cio_state(dev, head, regp, NV_CIO_CRE_86); + } + + regp->fb_start = NVReadCRTC(dev, head, NV_PCRTC_START); +} + +static void +nv_load_state_ext(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_device *device = nv_device(drm->device); + struct nouveau_timer *ptimer = nouveau_timer(device); + struct nv04_crtc_reg *regp = &state->crtc_reg[head]; + uint32_t reg900; + int i; + + if (nv_device(drm->device)->card_type >= NV_10) { + if (nv_two_heads(dev)) + /* setting ENGINE_CTRL (EC) *must* come before + * CIO_CRE_LCD, as writing CRE_LCD sets bits 16 & 17 in + * EC that should not be overwritten by writing stale EC + */ + NVWriteCRTC(dev, head, NV_PCRTC_ENGINE_CTRL, regp->crtc_eng_ctrl); + + nv_wr32(device, NV_PVIDEO_STOP, 1); + nv_wr32(device, NV_PVIDEO_INTR_EN, 0); + nv_wr32(device, NV_PVIDEO_OFFSET_BUFF(0), 0); + nv_wr32(device, NV_PVIDEO_OFFSET_BUFF(1), 0); + nv_wr32(device, NV_PVIDEO_LIMIT(0), 0); //drm->fb_available_size - 1); + nv_wr32(device, NV_PVIDEO_LIMIT(1), 0); //drm->fb_available_size - 1); + nv_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(0), 0); //drm->fb_available_size - 1); + nv_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(1), 0); //drm->fb_available_size - 1); + nv_wr32(device, NV_PBUS_POWERCTRL_2, 0); + + NVWriteCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG, regp->cursor_cfg); + NVWriteCRTC(dev, head, NV_PCRTC_830, regp->crtc_830); + NVWriteCRTC(dev, head, NV_PCRTC_834, regp->crtc_834); + + if (nv_device(drm->device)->card_type >= NV_30) + NVWriteCRTC(dev, head, NV_PCRTC_GPIO_EXT, regp->gpio_ext); + + if (nv_device(drm->device)->card_type == NV_40) { + NVWriteCRTC(dev, head, NV_PCRTC_850, regp->crtc_850); + + reg900 = NVReadRAMDAC(dev, head, NV_PRAMDAC_900); + if (regp->crtc_cfg == NV10_PCRTC_CONFIG_START_ADDRESS_HSYNC) + NVWriteRAMDAC(dev, head, NV_PRAMDAC_900, reg900 | 0x10000); + else + NVWriteRAMDAC(dev, head, NV_PRAMDAC_900, reg900 & ~0x10000); + } + } + + NVWriteCRTC(dev, head, NV_PCRTC_CONFIG, regp->crtc_cfg); + + wr_cio_state(dev, head, regp, NV_CIO_CRE_RPC0_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_RPC1_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_LSR_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_PIXEL_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_LCD__INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_HEB__INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX); + + if (nv_device(drm->device)->card_type >= NV_20) + wr_cio_state(dev, head, regp, NV_CIO_CRE_47); + + if (nv_device(drm->device)->card_type >= NV_30) + wr_cio_state(dev, head, regp, 0x9f); + + wr_cio_state(dev, head, regp, NV_CIO_CRE_49); + wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX); + if (nv_device(drm->device)->card_type == NV_40) + nv_fix_nv40_hw_cursor(dev, head); + wr_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX); + + wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX); + if (nv_device(drm->device)->card_type >= NV_10) { + wr_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX); + wr_cio_state(dev, head, regp, NV_CIO_CRE_CSB); + wr_cio_state(dev, head, regp, NV_CIO_CRE_4B); + wr_cio_state(dev, head, regp, NV_CIO_CRE_TVOUT_LATENCY); + } + /* NV11 and NV20 stop at 0x52. */ + if (nv_gf4_disp_arch(dev)) { + if (nv_device(drm->device)->card_type == NV_10) { + /* Not waiting for vertical retrace before modifying + CRE_53/CRE_54 causes lockups. */ + nouveau_timer_wait_eq(ptimer, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x8); + nouveau_timer_wait_eq(ptimer, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x0); + } + + wr_cio_state(dev, head, regp, NV_CIO_CRE_42); + wr_cio_state(dev, head, regp, NV_CIO_CRE_53); + wr_cio_state(dev, head, regp, NV_CIO_CRE_54); + + for (i = 0; i < 0x10; i++) + NVWriteVgaCrtc5758(dev, head, i, regp->CR58[i]); + wr_cio_state(dev, head, regp, NV_CIO_CRE_59); + wr_cio_state(dev, head, regp, NV_CIO_CRE_5B); + + wr_cio_state(dev, head, regp, NV_CIO_CRE_85); + wr_cio_state(dev, head, regp, NV_CIO_CRE_86); + } + + NVWriteCRTC(dev, head, NV_PCRTC_START, regp->fb_start); +} + +static void +nv_save_state_palette(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nouveau_device *device = nouveau_dev(dev); + int head_offset = head * NV_PRMDIO_SIZE, i; + + nv_wr08(device, NV_PRMDIO_PIXEL_MASK + head_offset, + NV_PRMDIO_PIXEL_MASK_MASK); + nv_wr08(device, NV_PRMDIO_READ_MODE_ADDRESS + head_offset, 0x0); + + for (i = 0; i < 768; i++) { + state->crtc_reg[head].DAC[i] = nv_rd08(device, + NV_PRMDIO_PALETTE_DATA + head_offset); + } + + NVSetEnablePalette(dev, head, false); +} + +void +nouveau_hw_load_state_palette(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nouveau_device *device = nouveau_dev(dev); + int head_offset = head * NV_PRMDIO_SIZE, i; + + nv_wr08(device, NV_PRMDIO_PIXEL_MASK + head_offset, + NV_PRMDIO_PIXEL_MASK_MASK); + nv_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS + head_offset, 0x0); + + for (i = 0; i < 768; i++) { + nv_wr08(device, NV_PRMDIO_PALETTE_DATA + head_offset, + state->crtc_reg[head].DAC[i]); + } + + NVSetEnablePalette(dev, head, false); +} + +void nouveau_hw_save_state(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + if (nv_device(drm->device)->chipset == 0x11) + /* NB: no attempt is made to restore the bad pll later on */ + nouveau_hw_fix_bad_vpll(dev, head); + nv_save_state_ramdac(dev, head, state); + nv_save_state_vga(dev, head, state); + nv_save_state_palette(dev, head, state); + nv_save_state_ext(dev, head, state); +} + +void nouveau_hw_load_state(struct drm_device *dev, int head, + struct nv04_mode_state *state) +{ + NVVgaProtect(dev, head, true); + nv_load_state_ramdac(dev, head, state); + nv_load_state_ext(dev, head, state); + nouveau_hw_load_state_palette(dev, head, state); + nv_load_state_vga(dev, head, state); + NVVgaProtect(dev, head, false); +} diff --git a/drivers/gpu/drm/nouveau/dispnv04/hw.h b/drivers/gpu/drm/nouveau/dispnv04/hw.h new file mode 100644 index 0000000..eeb70d9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv04/hw.h @@ -0,0 +1,409 @@ +/* + * Copyright 2008 Stuart Bennett + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __NOUVEAU_HW_H__ +#define __NOUVEAU_HW_H__ + +#include +#include "disp.h" +#include "nvreg.h" + +#include + +#define MASK(field) ( \ + (0xffffffff >> (31 - ((1 ? field) - (0 ? field)))) << (0 ? field)) + +#define XLATE(src, srclowbit, outfield) ( \ + (((src) >> (srclowbit)) << (0 ? outfield)) & MASK(outfield)) + +void NVWriteVgaSeq(struct drm_device *, int head, uint8_t index, uint8_t value); +uint8_t NVReadVgaSeq(struct drm_device *, int head, uint8_t index); +void NVWriteVgaGr(struct drm_device *, int head, uint8_t index, uint8_t value); +uint8_t NVReadVgaGr(struct drm_device *, int head, uint8_t index); +void NVSetOwner(struct drm_device *, int owner); +void NVBlankScreen(struct drm_device *, int head, bool blank); +int nouveau_hw_get_pllvals(struct drm_device *, enum nvbios_pll_type plltype, + struct nouveau_pll_vals *pllvals); +int nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pllvals); +int nouveau_hw_get_clock(struct drm_device *, enum nvbios_pll_type plltype); +void nouveau_hw_save_vga_fonts(struct drm_device *, bool save); +void nouveau_hw_save_state(struct drm_device *, int head, + struct nv04_mode_state *state); +void nouveau_hw_load_state(struct drm_device *, int head, + struct nv04_mode_state *state); +void nouveau_hw_load_state_palette(struct drm_device *, int head, + struct nv04_mode_state *state); + +/* nouveau_calc.c */ +extern void nouveau_calc_arb(struct drm_device *, int vclk, int bpp, + int *burst, int *lwm); + +static inline uint32_t NVReadCRTC(struct drm_device *dev, + int head, uint32_t reg) +{ + struct nouveau_device *device = nouveau_dev(dev); + uint32_t val; + if (head) + reg += NV_PCRTC0_SIZE; + val = nv_rd32(device, reg); + return val; +} + +static inline void NVWriteCRTC(struct drm_device *dev, + int head, uint32_t reg, uint32_t val) +{ + struct nouveau_device *device = nouveau_dev(dev); + if (head) + reg += NV_PCRTC0_SIZE; + nv_wr32(device, reg, val); +} + +static inline uint32_t NVReadRAMDAC(struct drm_device *dev, + int head, uint32_t reg) +{ + struct nouveau_device *device = nouveau_dev(dev); + uint32_t val; + if (head) + reg += NV_PRAMDAC0_SIZE; + val = nv_rd32(device, reg); + return val; +} + +static inline void NVWriteRAMDAC(struct drm_device *dev, + int head, uint32_t reg, uint32_t val) +{ + struct nouveau_device *device = nouveau_dev(dev); + if (head) + reg += NV_PRAMDAC0_SIZE; + nv_wr32(device, reg, val); +} + +static inline uint8_t nv_read_tmds(struct drm_device *dev, + int or, int dl, uint8_t address) +{ + int ramdac = (or & DCB_OUTPUT_C) >> 2; + + NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8, + NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | address); + return NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8); +} + +static inline void nv_write_tmds(struct drm_device *dev, + int or, int dl, uint8_t address, + uint8_t data) +{ + int ramdac = (or & DCB_OUTPUT_C) >> 2; + + NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8, data); + NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8, address); +} + +static inline void NVWriteVgaCrtc(struct drm_device *dev, + int head, uint8_t index, uint8_t value) +{ + struct nouveau_device *device = nouveau_dev(dev); + nv_wr08(device, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index); + nv_wr08(device, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE, value); +} + +static inline uint8_t NVReadVgaCrtc(struct drm_device *dev, + int head, uint8_t index) +{ + struct nouveau_device *device = nouveau_dev(dev); + uint8_t val; + nv_wr08(device, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index); + val = nv_rd08(device, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE); + return val; +} + +/* CR57 and CR58 are a fun pair of regs. CR57 provides an index (0-0xf) for CR58 + * I suspect they in fact do nothing, but are merely a way to carry useful + * per-head variables around + * + * Known uses: + * CR57 CR58 + * 0x00 index to the appropriate dcb entry (or 7f for inactive) + * 0x02 dcb entry's "or" value (or 00 for inactive) + * 0x03 bit0 set for dual link (LVDS, possibly elsewhere too) + * 0x08 or 0x09 pxclk in MHz + * 0x0f laptop panel info - low nibble for PEXTDEV_BOOT_0 strap + * high nibble for xlat strap value + */ + +static inline void +NVWriteVgaCrtc5758(struct drm_device *dev, int head, uint8_t index, uint8_t value) +{ + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index); + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_58, value); +} + +static inline uint8_t NVReadVgaCrtc5758(struct drm_device *dev, int head, uint8_t index) +{ + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index); + return NVReadVgaCrtc(dev, head, NV_CIO_CRE_58); +} + +static inline uint8_t NVReadPRMVIO(struct drm_device *dev, + int head, uint32_t reg) +{ + struct nouveau_device *device = nouveau_dev(dev); + struct nouveau_drm *drm = nouveau_drm(dev); + uint8_t val; + + /* Only NV4x have two pvio ranges; other twoHeads cards MUST call + * NVSetOwner for the relevant head to be programmed */ + if (head && nv_device(drm->device)->card_type == NV_40) + reg += NV_PRMVIO_SIZE; + + val = nv_rd08(device, reg); + return val; +} + +static inline void NVWritePRMVIO(struct drm_device *dev, + int head, uint32_t reg, uint8_t value) +{ + struct nouveau_device *device = nouveau_dev(dev); + struct nouveau_drm *drm = nouveau_drm(dev); + + /* Only NV4x have two pvio ranges; other twoHeads cards MUST call + * NVSetOwner for the relevant head to be programmed */ + if (head && nv_device(drm->device)->card_type == NV_40) + reg += NV_PRMVIO_SIZE; + + nv_wr08(device, reg, value); +} + +static inline void NVSetEnablePalette(struct drm_device *dev, int head, bool enable) +{ + struct nouveau_device *device = nouveau_dev(dev); + nv_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); + nv_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, enable ? 0 : 0x20); +} + +static inline bool NVGetEnablePalette(struct drm_device *dev, int head) +{ + struct nouveau_device *device = nouveau_dev(dev); + nv_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); + return !(nv_rd08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE) & 0x20); +} + +static inline void NVWriteVgaAttr(struct drm_device *dev, + int head, uint8_t index, uint8_t value) +{ + struct nouveau_device *device = nouveau_dev(dev); + if (NVGetEnablePalette(dev, head)) + index &= ~0x20; + else + index |= 0x20; + + nv_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); + nv_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index); + nv_wr08(device, NV_PRMCIO_AR__WRITE + head * NV_PRMCIO_SIZE, value); +} + +static inline uint8_t NVReadVgaAttr(struct drm_device *dev, + int head, uint8_t index) +{ + struct nouveau_device *device = nouveau_dev(dev); + uint8_t val; + if (NVGetEnablePalette(dev, head)) + index &= ~0x20; + else + index |= 0x20; + + nv_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); + nv_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index); + val = nv_rd08(device, NV_PRMCIO_AR__READ + head * NV_PRMCIO_SIZE); + return val; +} + +static inline void NVVgaSeqReset(struct drm_device *dev, int head, bool start) +{ + NVWriteVgaSeq(dev, head, NV_VIO_SR_RESET_INDEX, start ? 0x1 : 0x3); +} + +static inline void NVVgaProtect(struct drm_device *dev, int head, bool protect) +{ + uint8_t seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX); + + if (protect) { + NVVgaSeqReset(dev, head, true); + NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20); + } else { + /* Reenable sequencer, then turn on screen */ + NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20); /* reenable display */ + NVVgaSeqReset(dev, head, false); + } + NVSetEnablePalette(dev, head, protect); +} + +static inline bool +nv_heads_tied(struct drm_device *dev) +{ + struct nouveau_device *device = nouveau_dev(dev); + struct nouveau_drm *drm = nouveau_drm(dev); + + if (nv_device(drm->device)->chipset == 0x11) + return !!(nv_rd32(device, NV_PBUS_DEBUG_1) & (1 << 28)); + + return NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44) & 0x4; +} + +/* makes cr0-7 on the specified head read-only */ +static inline bool +nv_lock_vga_crtc_base(struct drm_device *dev, int head, bool lock) +{ + uint8_t cr11 = NVReadVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX); + bool waslocked = cr11 & 0x80; + + if (lock) + cr11 |= 0x80; + else + cr11 &= ~0x80; + NVWriteVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX, cr11); + + return waslocked; +} + +static inline void +nv_lock_vga_crtc_shadow(struct drm_device *dev, int head, int lock) +{ + /* shadow lock: connects 0x60?3d? regs to "real" 0x3d? regs + * bit7: unlocks HDT, HBS, HBE, HRS, HRE, HEB + * bit6: seems to have some effect on CR09 (double scan, VBS_9) + * bit5: unlocks HDE + * bit4: unlocks VDE + * bit3: unlocks VDT, OVL, VRS, ?VRE?, VBS, VBE, LSR, EBR + * bit2: same as bit 1 of 0x60?804 + * bit0: same as bit 0 of 0x60?804 + */ + + uint8_t cr21 = lock; + + if (lock < 0) + /* 0xfa is generic "unlock all" mask */ + cr21 = NVReadVgaCrtc(dev, head, NV_CIO_CRE_21) | 0xfa; + + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_21, cr21); +} + +/* renders the extended crtc regs (cr19+) on all crtcs impervious: + * immutable and unreadable + */ +static inline bool +NVLockVgaCrtcs(struct drm_device *dev, bool lock) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + bool waslocked = !NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX); + + NVWriteVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX, + lock ? NV_CIO_SR_LOCK_VALUE : NV_CIO_SR_UNLOCK_RW_VALUE); + /* NV11 has independently lockable extended crtcs, except when tied */ + if (nv_device(drm->device)->chipset == 0x11 && !nv_heads_tied(dev)) + NVWriteVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX, + lock ? NV_CIO_SR_LOCK_VALUE : + NV_CIO_SR_UNLOCK_RW_VALUE); + + return waslocked; +} + +/* nv04 cursor max dimensions of 32x32 (A1R5G5B5) */ +#define NV04_CURSOR_SIZE 32 +/* limit nv10 cursors to 64x64 (ARGB8) (we could go to 64x255) */ +#define NV10_CURSOR_SIZE 64 + +static inline int nv_cursor_width(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + return nv_device(drm->device)->card_type >= NV_10 ? NV10_CURSOR_SIZE : NV04_CURSOR_SIZE; +} + +static inline void +nv_fix_nv40_hw_cursor(struct drm_device *dev, int head) +{ + /* on some nv40 (such as the "true" (in the NV_PFB_BOOT_0 sense) nv40, + * the gf6800gt) a hardware bug requires a write to PRAMDAC_CURSOR_POS + * for changes to the CRTC CURCTL regs to take effect, whether changing + * the pixmap location, or just showing/hiding the cursor + */ + uint32_t curpos = NVReadRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS, curpos); +} + +static inline void +nv_set_crtc_base(struct drm_device *dev, int head, uint32_t offset) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + + NVWriteCRTC(dev, head, NV_PCRTC_START, offset); + + if (nv_device(drm->device)->card_type == NV_04) { + /* + * Hilarious, the 24th bit doesn't want to stick to + * PCRTC_START... + */ + int cre_heb = NVReadVgaCrtc(dev, head, NV_CIO_CRE_HEB__INDEX); + + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_HEB__INDEX, + (cre_heb & ~0x40) | ((offset >> 18) & 0x40)); + } +} + +static inline void +nv_show_cursor(struct drm_device *dev, int head, bool show) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + uint8_t *curctl1 = + &nv04_display(dev)->mode_reg.crtc_reg[head].CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX]; + + if (show) + *curctl1 |= MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE); + else + *curctl1 &= ~MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE); + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_HCUR_ADDR1_INDEX, *curctl1); + + if (nv_device(drm->device)->card_type == NV_40) + nv_fix_nv40_hw_cursor(dev, head); +} + +static inline uint32_t +nv_pitch_align(struct drm_device *dev, uint32_t width, int bpp) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + int mask; + + if (bpp == 15) + bpp = 16; + if (bpp == 24) + bpp = 8; + + /* Alignment requirements taken from the Haiku driver */ + if (nv_device(drm->device)->card_type == NV_04) + mask = 128 / bpp - 1; + else + mask = 512 / bpp - 1; + + return (width + mask) & ~mask; +} + +#endif /* __NOUVEAU_HW_H__ */ diff --git a/drivers/gpu/drm/nouveau/dispnv04/nvreg.h b/drivers/gpu/drm/nouveau/dispnv04/nvreg.h new file mode 100644 index 0000000..bbfb1a6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv04/nvreg.h @@ -0,0 +1,517 @@ +/* $XConsortium: nvreg.h /main/2 1996/10/28 05:13:41 kaleb $ */ +/* + * Copyright 1996-1997 David J. McKay + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * DAVID J. MCKAY BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/nv/nvreg.h,v 1.6 2002/01/25 21:56:06 tsi Exp $ */ + +#ifndef __NVREG_H_ +#define __NVREG_H_ + +#define NV_PMC_OFFSET 0x00000000 +#define NV_PMC_SIZE 0x00001000 + +#define NV_PBUS_OFFSET 0x00001000 +#define NV_PBUS_SIZE 0x00001000 + +#define NV_PFIFO_OFFSET 0x00002000 +#define NV_PFIFO_SIZE 0x00002000 + +#define NV_HDIAG_OFFSET 0x00005000 +#define NV_HDIAG_SIZE 0x00001000 + +#define NV_PRAM_OFFSET 0x00006000 +#define NV_PRAM_SIZE 0x00001000 + +#define NV_PVIDEO_OFFSET 0x00008000 +#define NV_PVIDEO_SIZE 0x00001000 + +#define NV_PTIMER_OFFSET 0x00009000 +#define NV_PTIMER_SIZE 0x00001000 + +#define NV_PPM_OFFSET 0x0000A000 +#define NV_PPM_SIZE 0x00001000 + +#define NV_PTV_OFFSET 0x0000D000 +#define NV_PTV_SIZE 0x00001000 + +#define NV_PRMVGA_OFFSET 0x000A0000 +#define NV_PRMVGA_SIZE 0x00020000 + +#define NV_PRMVIO0_OFFSET 0x000C0000 +#define NV_PRMVIO_SIZE 0x00002000 +#define NV_PRMVIO1_OFFSET 0x000C2000 + +#define NV_PFB_OFFSET 0x00100000 +#define NV_PFB_SIZE 0x00001000 + +#define NV_PEXTDEV_OFFSET 0x00101000 +#define NV_PEXTDEV_SIZE 0x00001000 + +#define NV_PME_OFFSET 0x00200000 +#define NV_PME_SIZE 0x00001000 + +#define NV_PROM_OFFSET 0x00300000 +#define NV_PROM_SIZE 0x00010000 + +#define NV_PGRAPH_OFFSET 0x00400000 +#define NV_PGRAPH_SIZE 0x00010000 + +#define NV_PCRTC0_OFFSET 0x00600000 +#define NV_PCRTC0_SIZE 0x00002000 /* empirical */ + +#define NV_PRMCIO0_OFFSET 0x00601000 +#define NV_PRMCIO_SIZE 0x00002000 +#define NV_PRMCIO1_OFFSET 0x00603000 + +#define NV50_DISPLAY_OFFSET 0x00610000 +#define NV50_DISPLAY_SIZE 0x0000FFFF + +#define NV_PRAMDAC0_OFFSET 0x00680000 +#define NV_PRAMDAC0_SIZE 0x00002000 + +#define NV_PRMDIO0_OFFSET 0x00681000 +#define NV_PRMDIO_SIZE 0x00002000 +#define NV_PRMDIO1_OFFSET 0x00683000 + +#define NV_PRAMIN_OFFSET 0x00700000 +#define NV_PRAMIN_SIZE 0x00100000 + +#define NV_FIFO_OFFSET 0x00800000 +#define NV_FIFO_SIZE 0x00800000 + +#define NV_PMC_BOOT_0 0x00000000 +#define NV_PMC_ENABLE 0x00000200 + +#define NV_VIO_VSE2 0x000003c3 +#define NV_VIO_SRX 0x000003c4 + +#define NV_CIO_CRX__COLOR 0x000003d4 +#define NV_CIO_CR__COLOR 0x000003d5 + +#define NV_PBUS_DEBUG_1 0x00001084 +#define NV_PBUS_DEBUG_4 0x00001098 +#define NV_PBUS_DEBUG_DUALHEAD_CTL 0x000010f0 +#define NV_PBUS_POWERCTRL_1 0x00001584 +#define NV_PBUS_POWERCTRL_2 0x00001588 +#define NV_PBUS_POWERCTRL_4 0x00001590 +#define NV_PBUS_PCI_NV_19 0x0000184C +#define NV_PBUS_PCI_NV_20 0x00001850 +# define NV_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED (0 << 0) +# define NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED (1 << 0) + +#define NV_PFIFO_RAMHT 0x00002210 + +#define NV_PTV_TV_INDEX 0x0000d220 +#define NV_PTV_TV_DATA 0x0000d224 +#define NV_PTV_HFILTER 0x0000d310 +#define NV_PTV_HFILTER2 0x0000d390 +#define NV_PTV_VFILTER 0x0000d510 + +#define NV_PRMVIO_MISC__WRITE 0x000c03c2 +#define NV_PRMVIO_SRX 0x000c03c4 +#define NV_PRMVIO_SR 0x000c03c5 +# define NV_VIO_SR_RESET_INDEX 0x00 +# define NV_VIO_SR_CLOCK_INDEX 0x01 +# define NV_VIO_SR_PLANE_MASK_INDEX 0x02 +# define NV_VIO_SR_CHAR_MAP_INDEX 0x03 +# define NV_VIO_SR_MEM_MODE_INDEX 0x04 +#define NV_PRMVIO_MISC__READ 0x000c03cc +#define NV_PRMVIO_GRX 0x000c03ce +#define NV_PRMVIO_GX 0x000c03cf +# define NV_VIO_GX_SR_INDEX 0x00 +# define NV_VIO_GX_SREN_INDEX 0x01 +# define NV_VIO_GX_CCOMP_INDEX 0x02 +# define NV_VIO_GX_ROP_INDEX 0x03 +# define NV_VIO_GX_READ_MAP_INDEX 0x04 +# define NV_VIO_GX_MODE_INDEX 0x05 +# define NV_VIO_GX_MISC_INDEX 0x06 +# define NV_VIO_GX_DONT_CARE_INDEX 0x07 +# define NV_VIO_GX_BIT_MASK_INDEX 0x08 + +#define NV_PCRTC_INTR_0 0x00600100 +# define NV_PCRTC_INTR_0_VBLANK (1 << 0) +#define NV_PCRTC_INTR_EN_0 0x00600140 +#define NV_PCRTC_START 0x00600800 +#define NV_PCRTC_CONFIG 0x00600804 +# define NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA (1 << 0) +# define NV04_PCRTC_CONFIG_START_ADDRESS_HSYNC (4 << 0) +# define NV10_PCRTC_CONFIG_START_ADDRESS_HSYNC (2 << 0) +#define NV_PCRTC_CURSOR_CONFIG 0x00600810 +# define NV_PCRTC_CURSOR_CONFIG_ENABLE_ENABLE (1 << 0) +# define NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE (1 << 4) +# define NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM (1 << 8) +# define NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32 (1 << 12) +# define NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 (1 << 16) +# define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_32 (2 << 24) +# define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 (4 << 24) +# define NV_PCRTC_CURSOR_CONFIG_CUR_BLEND_ALPHA (1 << 28) + +/* note: PCRTC_GPIO is not available on nv10, and in fact aliases 0x600810 */ +#define NV_PCRTC_GPIO 0x00600818 +#define NV_PCRTC_GPIO_EXT 0x0060081c +#define NV_PCRTC_830 0x00600830 +#define NV_PCRTC_834 0x00600834 +#define NV_PCRTC_850 0x00600850 +#define NV_PCRTC_ENGINE_CTRL 0x00600860 +# define NV_CRTC_FSEL_I2C (1 << 4) +# define NV_CRTC_FSEL_OVERLAY (1 << 12) + +#define NV_PRMCIO_ARX 0x006013c0 +#define NV_PRMCIO_AR__WRITE 0x006013c0 +#define NV_PRMCIO_AR__READ 0x006013c1 +# define NV_CIO_AR_MODE_INDEX 0x10 +# define NV_CIO_AR_OSCAN_INDEX 0x11 +# define NV_CIO_AR_PLANE_INDEX 0x12 +# define NV_CIO_AR_HPP_INDEX 0x13 +# define NV_CIO_AR_CSEL_INDEX 0x14 +#define NV_PRMCIO_INP0 0x006013c2 +#define NV_PRMCIO_CRX__COLOR 0x006013d4 +#define NV_PRMCIO_CR__COLOR 0x006013d5 + /* Standard VGA CRTC registers */ +# define NV_CIO_CR_HDT_INDEX 0x00 /* horizontal display total */ +# define NV_CIO_CR_HDE_INDEX 0x01 /* horizontal display end */ +# define NV_CIO_CR_HBS_INDEX 0x02 /* horizontal blanking start */ +# define NV_CIO_CR_HBE_INDEX 0x03 /* horizontal blanking end */ +# define NV_CIO_CR_HBE_4_0 4:0 +# define NV_CIO_CR_HRS_INDEX 0x04 /* horizontal retrace start */ +# define NV_CIO_CR_HRE_INDEX 0x05 /* horizontal retrace end */ +# define NV_CIO_CR_HRE_4_0 4:0 +# define NV_CIO_CR_HRE_HBE_5 7:7 +# define NV_CIO_CR_VDT_INDEX 0x06 /* vertical display total */ +# define NV_CIO_CR_OVL_INDEX 0x07 /* overflow bits */ +# define NV_CIO_CR_OVL_VDT_8 0:0 +# define NV_CIO_CR_OVL_VDE_8 1:1 +# define NV_CIO_CR_OVL_VRS_8 2:2 +# define NV_CIO_CR_OVL_VBS_8 3:3 +# define NV_CIO_CR_OVL_VDT_9 5:5 +# define NV_CIO_CR_OVL_VDE_9 6:6 +# define NV_CIO_CR_OVL_VRS_9 7:7 +# define NV_CIO_CR_RSAL_INDEX 0x08 /* normally "preset row scan" */ +# define NV_CIO_CR_CELL_HT_INDEX 0x09 /* cell height?! normally "max scan line" */ +# define NV_CIO_CR_CELL_HT_VBS_9 5:5 +# define NV_CIO_CR_CELL_HT_SCANDBL 7:7 +# define NV_CIO_CR_CURS_ST_INDEX 0x0a /* cursor start */ +# define NV_CIO_CR_CURS_END_INDEX 0x0b /* cursor end */ +# define NV_CIO_CR_SA_HI_INDEX 0x0c /* screen start address high */ +# define NV_CIO_CR_SA_LO_INDEX 0x0d /* screen start address low */ +# define NV_CIO_CR_TCOFF_HI_INDEX 0x0e /* cursor offset high */ +# define NV_CIO_CR_TCOFF_LO_INDEX 0x0f /* cursor offset low */ +# define NV_CIO_CR_VRS_INDEX 0x10 /* vertical retrace start */ +# define NV_CIO_CR_VRE_INDEX 0x11 /* vertical retrace end */ +# define NV_CIO_CR_VRE_3_0 3:0 +# define NV_CIO_CR_VDE_INDEX 0x12 /* vertical display end */ +# define NV_CIO_CR_OFFSET_INDEX 0x13 /* sets screen pitch */ +# define NV_CIO_CR_ULINE_INDEX 0x14 /* underline location */ +# define NV_CIO_CR_VBS_INDEX 0x15 /* vertical blank start */ +# define NV_CIO_CR_VBE_INDEX 0x16 /* vertical blank end */ +# define NV_CIO_CR_MODE_INDEX 0x17 /* crtc mode control */ +# define NV_CIO_CR_LCOMP_INDEX 0x18 /* line compare */ + /* Extended VGA CRTC registers */ +# define NV_CIO_CRE_RPC0_INDEX 0x19 /* repaint control 0 */ +# define NV_CIO_CRE_RPC0_OFFSET_10_8 7:5 +# define NV_CIO_CRE_RPC1_INDEX 0x1a /* repaint control 1 */ +# define NV_CIO_CRE_RPC1_LARGE 2:2 +# define NV_CIO_CRE_FF_INDEX 0x1b /* fifo control */ +# define NV_CIO_CRE_ENH_INDEX 0x1c /* enhanced? */ +# define NV_CIO_SR_LOCK_INDEX 0x1f /* crtc lock */ +# define NV_CIO_SR_UNLOCK_RW_VALUE 0x57 +# define NV_CIO_SR_LOCK_VALUE 0x99 +# define NV_CIO_CRE_FFLWM__INDEX 0x20 /* fifo low water mark */ +# define NV_CIO_CRE_21 0x21 /* vga shadow crtc lock */ +# define NV_CIO_CRE_LSR_INDEX 0x25 /* ? */ +# define NV_CIO_CRE_LSR_VDT_10 0:0 +# define NV_CIO_CRE_LSR_VDE_10 1:1 +# define NV_CIO_CRE_LSR_VRS_10 2:2 +# define NV_CIO_CRE_LSR_VBS_10 3:3 +# define NV_CIO_CRE_LSR_HBE_6 4:4 +# define NV_CIO_CR_ARX_INDEX 0x26 /* attribute index -- ro copy of 0x60.3c0 */ +# define NV_CIO_CRE_CHIP_ID_INDEX 0x27 /* chip revision */ +# define NV_CIO_CRE_PIXEL_INDEX 0x28 +# define NV_CIO_CRE_PIXEL_FORMAT 1:0 +# define NV_CIO_CRE_HEB__INDEX 0x2d /* horizontal extra bits? */ +# define NV_CIO_CRE_HEB_HDT_8 0:0 +# define NV_CIO_CRE_HEB_HDE_8 1:1 +# define NV_CIO_CRE_HEB_HBS_8 2:2 +# define NV_CIO_CRE_HEB_HRS_8 3:3 +# define NV_CIO_CRE_HEB_ILC_8 4:4 +# define NV_CIO_CRE_2E 0x2e /* some scratch or dummy reg to force writes to sink in */ +# define NV_CIO_CRE_HCUR_ADDR2_INDEX 0x2f /* cursor */ +# define NV_CIO_CRE_HCUR_ADDR0_INDEX 0x30 /* pixmap */ +# define NV_CIO_CRE_HCUR_ADDR0_ADR 6:0 +# define NV_CIO_CRE_HCUR_ASI 7:7 +# define NV_CIO_CRE_HCUR_ADDR1_INDEX 0x31 /* address */ +# define NV_CIO_CRE_HCUR_ADDR1_ENABLE 0:0 +# define NV_CIO_CRE_HCUR_ADDR1_CUR_DBL 1:1 +# define NV_CIO_CRE_HCUR_ADDR1_ADR 7:2 +# define NV_CIO_CRE_LCD__INDEX 0x33 +# define NV_CIO_CRE_LCD_LCD_SELECT 0:0 +# define NV_CIO_CRE_LCD_ROUTE_MASK 0x3b +# define NV_CIO_CRE_DDC0_STATUS__INDEX 0x36 +# define NV_CIO_CRE_DDC0_WR__INDEX 0x37 +# define NV_CIO_CRE_ILACE__INDEX 0x39 /* interlace */ +# define NV_CIO_CRE_SCRATCH3__INDEX 0x3b +# define NV_CIO_CRE_SCRATCH4__INDEX 0x3c +# define NV_CIO_CRE_DDC_STATUS__INDEX 0x3e +# define NV_CIO_CRE_DDC_WR__INDEX 0x3f +# define NV_CIO_CRE_EBR_INDEX 0x41 /* extra bits ? (vertical) */ +# define NV_CIO_CRE_EBR_VDT_11 0:0 +# define NV_CIO_CRE_EBR_VDE_11 2:2 +# define NV_CIO_CRE_EBR_VRS_11 4:4 +# define NV_CIO_CRE_EBR_VBS_11 6:6 +# define NV_CIO_CRE_42 0x42 +# define NV_CIO_CRE_42_OFFSET_11 6:6 +# define NV_CIO_CRE_43 0x43 +# define NV_CIO_CRE_44 0x44 /* head control */ +# define NV_CIO_CRE_CSB 0x45 /* colour saturation boost */ +# define NV_CIO_CRE_RCR 0x46 +# define NV_CIO_CRE_RCR_ENDIAN_BIG 7:7 +# define NV_CIO_CRE_47 0x47 /* extended fifo lwm, used on nv30+ */ +# define NV_CIO_CRE_49 0x49 +# define NV_CIO_CRE_4B 0x4b /* given patterns in 0x[2-3][a-c] regs, probably scratch 6 */ +# define NV_CIO_CRE_TVOUT_LATENCY 0x52 +# define NV_CIO_CRE_53 0x53 /* `fp_htiming' according to Haiku */ +# define NV_CIO_CRE_54 0x54 /* `fp_vtiming' according to Haiku */ +# define NV_CIO_CRE_57 0x57 /* index reg for cr58 */ +# define NV_CIO_CRE_58 0x58 /* data reg for cr57 */ +# define NV_CIO_CRE_59 0x59 /* related to on/off-chip-ness of digital outputs */ +# define NV_CIO_CRE_5B 0x5B /* newer colour saturation reg */ +# define NV_CIO_CRE_85 0x85 +# define NV_CIO_CRE_86 0x86 +#define NV_PRMCIO_INP0__COLOR 0x006013da + +#define NV_PRAMDAC_CU_START_POS 0x00680300 +# define NV_PRAMDAC_CU_START_POS_X 15:0 +# define NV_PRAMDAC_CU_START_POS_Y 31:16 +#define NV_RAMDAC_NV10_CURSYNC 0x00680404 + +#define NV_PRAMDAC_NVPLL_COEFF 0x00680500 +#define NV_PRAMDAC_MPLL_COEFF 0x00680504 +#define NV_PRAMDAC_VPLL_COEFF 0x00680508 +# define NV30_RAMDAC_ENABLE_VCO2 (8 << 4) + +#define NV_PRAMDAC_PLL_COEFF_SELECT 0x0068050c +# define NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE (4 << 0) +# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL (1 << 8) +# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL (2 << 8) +# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL (4 << 8) +# define NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 (8 << 8) +# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 (1 << 16) +# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 (2 << 16) +# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 (4 << 16) +# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2 (8 << 16) +# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_CLK_SOURCE_VIP (1 << 20) +# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2 (1 << 28) +# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2 (2 << 28) + +#define NV_PRAMDAC_PLL_SETUP_CONTROL 0x00680510 +#define NV_RAMDAC_VPLL2 0x00680520 +#define NV_PRAMDAC_SEL_CLK 0x00680524 +#define NV_RAMDAC_DITHER_NV11 0x00680528 +#define NV_PRAMDAC_DACCLK 0x0068052c +# define NV_PRAMDAC_DACCLK_SEL_DACCLK (1 << 0) + +#define NV_RAMDAC_NVPLL_B 0x00680570 +#define NV_RAMDAC_MPLL_B 0x00680574 +#define NV_RAMDAC_VPLL_B 0x00680578 +#define NV_RAMDAC_VPLL2_B 0x0068057c +# define NV31_RAMDAC_ENABLE_VCO2 (8 << 28) +#define NV_PRAMDAC_580 0x00680580 +# define NV_RAMDAC_580_VPLL1_ACTIVE (1 << 8) +# define NV_RAMDAC_580_VPLL2_ACTIVE (1 << 28) + +#define NV_PRAMDAC_GENERAL_CONTROL 0x00680600 +# define NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON (3 << 4) +# define NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL (1 << 8) +# define NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL (1 << 12) +# define NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM (2 << 16) +# define NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS (1 << 20) +# define NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG (2 << 28) +#define NV_PRAMDAC_TEST_CONTROL 0x00680608 +# define NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED (1 << 12) +# define NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF (1 << 16) +# define NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI (1 << 28) +#define NV_PRAMDAC_TESTPOINT_DATA 0x00680610 +# define NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK (8 << 28) +#define NV_PRAMDAC_630 0x00680630 +#define NV_PRAMDAC_634 0x00680634 + +#define NV_PRAMDAC_TV_SETUP 0x00680700 +#define NV_PRAMDAC_TV_VTOTAL 0x00680720 +#define NV_PRAMDAC_TV_VSKEW 0x00680724 +#define NV_PRAMDAC_TV_VSYNC_DELAY 0x00680728 +#define NV_PRAMDAC_TV_HTOTAL 0x0068072c +#define NV_PRAMDAC_TV_HSKEW 0x00680730 +#define NV_PRAMDAC_TV_HSYNC_DELAY 0x00680734 +#define NV_PRAMDAC_TV_HSYNC_DELAY2 0x00680738 + +#define NV_PRAMDAC_TV_SETUP 0x00680700 + +#define NV_PRAMDAC_FP_VDISPLAY_END 0x00680800 +#define NV_PRAMDAC_FP_VTOTAL 0x00680804 +#define NV_PRAMDAC_FP_VCRTC 0x00680808 +#define NV_PRAMDAC_FP_VSYNC_START 0x0068080c +#define NV_PRAMDAC_FP_VSYNC_END 0x00680810 +#define NV_PRAMDAC_FP_VVALID_START 0x00680814 +#define NV_PRAMDAC_FP_VVALID_END 0x00680818 +#define NV_PRAMDAC_FP_HDISPLAY_END 0x00680820 +#define NV_PRAMDAC_FP_HTOTAL 0x00680824 +#define NV_PRAMDAC_FP_HCRTC 0x00680828 +#define NV_PRAMDAC_FP_HSYNC_START 0x0068082c +#define NV_PRAMDAC_FP_HSYNC_END 0x00680830 +#define NV_PRAMDAC_FP_HVALID_START 0x00680834 +#define NV_PRAMDAC_FP_HVALID_END 0x00680838 + +#define NV_RAMDAC_FP_DITHER 0x0068083c +#define NV_PRAMDAC_FP_TG_CONTROL 0x00680848 +# define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS (1 << 0) +# define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE (2 << 0) +# define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS (1 << 4) +# define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE (2 << 4) +# define NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE (0 << 8) +# define NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER (1 << 8) +# define NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE (2 << 8) +# define NV_PRAMDAC_FP_TG_CONTROL_READ_PROG (1 << 20) +# define NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12 (1 << 24) +# define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS (1 << 28) +# define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE (2 << 28) +#define NV_PRAMDAC_FP_MARGIN_COLOR 0x0068084c +#define NV_PRAMDAC_850 0x00680850 +#define NV_PRAMDAC_85C 0x0068085c +#define NV_PRAMDAC_FP_DEBUG_0 0x00680880 +# define NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE (1 << 0) +# define NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE (1 << 4) +/* This doesn't seem to be essential for tmds, but still often set */ +# define NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED (8 << 4) +# define NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR (1 << 8) +# define NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR (1 << 12) +# define NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND (1 << 20) +# define NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND (1 << 24) +# define NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK (1 << 28) +#define NV_PRAMDAC_FP_DEBUG_1 0x00680884 +# define NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE 11:0 +# define NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE (1 << 12) +# define NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE 27:16 +# define NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE (1 << 28) +#define NV_PRAMDAC_FP_DEBUG_2 0x00680888 +#define NV_PRAMDAC_FP_DEBUG_3 0x0068088C + +/* see NV_PRAMDAC_INDIR_TMDS in rules.xml */ +#define NV_PRAMDAC_FP_TMDS_CONTROL 0x006808b0 +# define NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE (1 << 16) +#define NV_PRAMDAC_FP_TMDS_DATA 0x006808b4 + +#define NV_PRAMDAC_8C0 0x006808c0 + +/* Some kind of switch */ +#define NV_PRAMDAC_900 0x00680900 +#define NV_PRAMDAC_A20 0x00680A20 +#define NV_PRAMDAC_A24 0x00680A24 +#define NV_PRAMDAC_A34 0x00680A34 + +#define NV_PRAMDAC_CTV 0x00680c00 + +/* names fabricated from NV_USER_DAC info */ +#define NV_PRMDIO_PIXEL_MASK 0x006813c6 +# define NV_PRMDIO_PIXEL_MASK_MASK 0xff +#define NV_PRMDIO_READ_MODE_ADDRESS 0x006813c7 +#define NV_PRMDIO_WRITE_MODE_ADDRESS 0x006813c8 +#define NV_PRMDIO_PALETTE_DATA 0x006813c9 + +#define NV_PGRAPH_DEBUG_0 0x00400080 +#define NV_PGRAPH_DEBUG_1 0x00400084 +#define NV_PGRAPH_DEBUG_2_NV04 0x00400088 +#define NV_PGRAPH_DEBUG_2 0x00400620 +#define NV_PGRAPH_DEBUG_3 0x0040008c +#define NV_PGRAPH_DEBUG_4 0x00400090 +#define NV_PGRAPH_INTR 0x00400100 +#define NV_PGRAPH_INTR_EN 0x00400140 +#define NV_PGRAPH_CTX_CONTROL 0x00400144 +#define NV_PGRAPH_CTX_CONTROL_NV04 0x00400170 +#define NV_PGRAPH_ABS_UCLIP_XMIN 0x0040053C +#define NV_PGRAPH_ABS_UCLIP_YMIN 0x00400540 +#define NV_PGRAPH_ABS_UCLIP_XMAX 0x00400544 +#define NV_PGRAPH_ABS_UCLIP_YMAX 0x00400548 +#define NV_PGRAPH_BETA_AND 0x00400608 +#define NV_PGRAPH_LIMIT_VIOL_PIX 0x00400610 +#define NV_PGRAPH_BOFFSET0 0x00400640 +#define NV_PGRAPH_BOFFSET1 0x00400644 +#define NV_PGRAPH_BOFFSET2 0x00400648 +#define NV_PGRAPH_BLIMIT0 0x00400684 +#define NV_PGRAPH_BLIMIT1 0x00400688 +#define NV_PGRAPH_BLIMIT2 0x0040068c +#define NV_PGRAPH_STATUS 0x00400700 +#define NV_PGRAPH_SURFACE 0x00400710 +#define NV_PGRAPH_STATE 0x00400714 +#define NV_PGRAPH_FIFO 0x00400720 +#define NV_PGRAPH_PATTERN_SHAPE 0x00400810 +#define NV_PGRAPH_TILE 0x00400b00 + +#define NV_PVIDEO_INTR_EN 0x00008140 +#define NV_PVIDEO_BUFFER 0x00008700 +#define NV_PVIDEO_STOP 0x00008704 +#define NV_PVIDEO_UVPLANE_BASE(buff) (0x00008800+(buff)*4) +#define NV_PVIDEO_UVPLANE_LIMIT(buff) (0x00008808+(buff)*4) +#define NV_PVIDEO_UVPLANE_OFFSET_BUFF(buff) (0x00008820+(buff)*4) +#define NV_PVIDEO_BASE(buff) (0x00008900+(buff)*4) +#define NV_PVIDEO_LIMIT(buff) (0x00008908+(buff)*4) +#define NV_PVIDEO_LUMINANCE(buff) (0x00008910+(buff)*4) +#define NV_PVIDEO_CHROMINANCE(buff) (0x00008918+(buff)*4) +#define NV_PVIDEO_OFFSET_BUFF(buff) (0x00008920+(buff)*4) +#define NV_PVIDEO_SIZE_IN(buff) (0x00008928+(buff)*4) +#define NV_PVIDEO_POINT_IN(buff) (0x00008930+(buff)*4) +#define NV_PVIDEO_DS_DX(buff) (0x00008938+(buff)*4) +#define NV_PVIDEO_DT_DY(buff) (0x00008940+(buff)*4) +#define NV_PVIDEO_POINT_OUT(buff) (0x00008948+(buff)*4) +#define NV_PVIDEO_SIZE_OUT(buff) (0x00008950+(buff)*4) +#define NV_PVIDEO_FORMAT(buff) (0x00008958+(buff)*4) +# define NV_PVIDEO_FORMAT_PLANAR (1 << 0) +# define NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8 (1 << 16) +# define NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY (1 << 20) +# define NV_PVIDEO_FORMAT_MATRIX_ITURBT709 (1 << 24) +#define NV_PVIDEO_COLOR_KEY 0x00008B00 + +/* NV04 overlay defines from VIDIX & Haiku */ +#define NV_PVIDEO_INTR_EN_0 0x00680140 +#define NV_PVIDEO_STEP_SIZE 0x00680200 +#define NV_PVIDEO_CONTROL_Y 0x00680204 +#define NV_PVIDEO_CONTROL_X 0x00680208 +#define NV_PVIDEO_BUFF0_START_ADDRESS 0x0068020c +#define NV_PVIDEO_BUFF0_PITCH_LENGTH 0x00680214 +#define NV_PVIDEO_BUFF0_OFFSET 0x0068021c +#define NV_PVIDEO_BUFF1_START_ADDRESS 0x00680210 +#define NV_PVIDEO_BUFF1_PITCH_LENGTH 0x00680218 +#define NV_PVIDEO_BUFF1_OFFSET 0x00680220 +#define NV_PVIDEO_OE_STATE 0x00680224 +#define NV_PVIDEO_SU_STATE 0x00680228 +#define NV_PVIDEO_RM_STATE 0x0068022c +#define NV_PVIDEO_WINDOW_START 0x00680230 +#define NV_PVIDEO_WINDOW_SIZE 0x00680234 +#define NV_PVIDEO_FIFO_THRES_SIZE 0x00680238 +#define NV_PVIDEO_FIFO_BURST_LENGTH 0x0068023c +#define NV_PVIDEO_KEY 0x00680240 +#define NV_PVIDEO_OVERLAY 0x00680244 +#define NV_PVIDEO_RED_CSC_OFFSET 0x00680280 +#define NV_PVIDEO_GREEN_CSC_OFFSET 0x00680284 +#define NV_PVIDEO_BLUE_CSC_OFFSET 0x00680288 +#define NV_PVIDEO_CSC_ADJUST 0x0068028c + +#endif diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c b/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c new file mode 100644 index 0000000..08c6f5e --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c @@ -0,0 +1,592 @@ +/* + * Copyright (C) 2009 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include "nouveau_drm.h" +#include "nouveau_encoder.h" +#include "nouveau_crtc.h" +#include "hw.h" +#include "tvnv17.h" + +char *nv17_tv_norm_names[NUM_TV_NORMS] = { + [TV_NORM_PAL] = "PAL", + [TV_NORM_PAL_M] = "PAL-M", + [TV_NORM_PAL_N] = "PAL-N", + [TV_NORM_PAL_NC] = "PAL-Nc", + [TV_NORM_NTSC_M] = "NTSC-M", + [TV_NORM_NTSC_J] = "NTSC-J", + [TV_NORM_HD480I] = "hd480i", + [TV_NORM_HD480P] = "hd480p", + [TV_NORM_HD576I] = "hd576i", + [TV_NORM_HD576P] = "hd576p", + [TV_NORM_HD720P] = "hd720p", + [TV_NORM_HD1080I] = "hd1080i" +}; + +/* TV standard specific parameters */ + +struct nv17_tv_norm_params nv17_tv_norms[NUM_TV_NORMS] = { + [TV_NORM_PAL] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 576, 50000, { + 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3, + 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3, + 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, + 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0 + } } } }, + + [TV_NORM_PAL_M] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 480, 59940, { + 0x21, 0xe6, 0xef, 0xe3, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x44, 0x76, 0x32, 0x25, 0x0, 0x3c, 0x0, + 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, + 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x18, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x40, 0x10, 0x0, 0x9c, + 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 + } } } }, + + [TV_NORM_PAL_N] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 576, 50000, { + 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x40, 0x8a, 0x32, 0x25, 0x0, 0x3c, 0x0, + 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, + 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, + 0xbd, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 + } } } }, + + [TV_NORM_PAL_NC] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 576, 50000, { + 0x21, 0xf6, 0x94, 0x46, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x44, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3, + 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3, + 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, + 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0 + } } } }, + + [TV_NORM_NTSC_M] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 480, 59940, { + 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x3c, 0x0, + 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, + 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0x9c, + 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 + } } } }, + + [TV_NORM_NTSC_J] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 480, 59940, { + 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0, + 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, + 0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4, + 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 + } } } }, + + [TV_NORM_HD480I] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 480, 59940, { + 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0, + 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, + 0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4, + 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 + } } } }, + + [TV_NORM_HD576I] = { TV_ENC_MODE, { + .tv_enc_mode = { 720, 576, 50000, { + 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18, + 0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3, + 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, + 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3, + 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5, + 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, + 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, + 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0 + } } } }, + + + [TV_NORM_HD480P] = { CTV_ENC_MODE, { + .ctv_enc_mode = { + .mode = { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, + 720, 735, 743, 858, 0, 480, 490, 494, 525, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + .ctv_regs = { 0x3540000, 0x0, 0x0, 0x314, + 0x354003a, 0x40000, 0x6f0344, 0x18100000, + 0x10160004, 0x10060005, 0x1006000c, 0x10060020, + 0x10060021, 0x140e0022, 0x10060202, 0x1802020a, + 0x1810020b, 0x10000fff, 0x10000fff, 0x10000fff, + 0x10000fff, 0x10000fff, 0x10000fff, 0x70, + 0x3ff0000, 0x57, 0x2e001e, 0x258012c, + 0xa0aa04ec, 0x30, 0x80960019, 0x12c0300, + 0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400 + } } } }, + + [TV_NORM_HD576P] = { CTV_ENC_MODE, { + .ctv_enc_mode = { + .mode = { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, + 720, 730, 738, 864, 0, 576, 581, 585, 625, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + .ctv_regs = { 0x3540000, 0x0, 0x0, 0x314, + 0x354003a, 0x40000, 0x6f0344, 0x18100000, + 0x10060001, 0x10060009, 0x10060026, 0x10060027, + 0x140e0028, 0x10060268, 0x1810026d, 0x10000fff, + 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff, + 0x10000fff, 0x10000fff, 0x10000fff, 0x69, + 0x3ff0000, 0x57, 0x2e001e, 0x258012c, + 0xa0aa04ec, 0x30, 0x80960019, 0x12c0300, + 0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400 + } } } }, + + [TV_NORM_HD720P] = { CTV_ENC_MODE, { + .ctv_enc_mode = { + .mode = { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, + 1280, 1349, 1357, 1650, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + .ctv_regs = { 0x1260394, 0x0, 0x0, 0x622, + 0x66b0021, 0x6004a, 0x1210626, 0x8170000, + 0x70004, 0x70016, 0x70017, 0x40f0018, + 0x702e8, 0x81702ed, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0xfff, + 0xfff, 0xfff, 0xfff, 0x0, + 0x2e40001, 0x58, 0x2e001e, 0x258012c, + 0xa0aa04ec, 0x30, 0x810c0039, 0x12c0300, + 0xc0002039, 0x600, 0x32060039, 0x0, 0x0, 0x0 + } } } }, + + [TV_NORM_HD1080I] = { CTV_ENC_MODE, { + .ctv_enc_mode = { + .mode = { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, + 1920, 1961, 2049, 2200, 0, 1080, 1084, 1088, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC + | DRM_MODE_FLAG_INTERLACE) }, + .ctv_regs = { 0xac0420, 0x44c0478, 0x4a4, 0x4fc0868, + 0x8940028, 0x60054, 0xe80870, 0xbf70000, + 0xbc70004, 0x70005, 0x70012, 0x70013, + 0x40f0014, 0x70230, 0xbf70232, 0xbf70233, + 0x1c70237, 0x70238, 0x70244, 0x70245, + 0x40f0246, 0x70462, 0x1f70464, 0x0, + 0x2e40001, 0x58, 0x2e001e, 0x258012c, + 0xa0aa04ec, 0x30, 0x815f004c, 0x12c0300, + 0xc000204c, 0x600, 0x3206004c, 0x0, 0x0, 0x0 + } } } } +}; + +/* + * The following is some guesswork on how the TV encoder flicker + * filter/rescaler works: + * + * It seems to use some sort of resampling filter, it is controlled + * through the registers at NV_PTV_HFILTER and NV_PTV_VFILTER, they + * control the horizontal and vertical stage respectively, there is + * also NV_PTV_HFILTER2 the blob fills identically to NV_PTV_HFILTER, + * but they seem to do nothing. A rough guess might be that they could + * be used to independently control the filtering of each interlaced + * field, but I don't know how they are enabled. The whole filtering + * process seems to be disabled with bits 26:27 of PTV_200, but we + * aren't doing that. + * + * The layout of both register sets is the same: + * + * A: [BASE+0x18]...[BASE+0x0] [BASE+0x58]..[BASE+0x40] + * B: [BASE+0x34]...[BASE+0x1c] [BASE+0x74]..[BASE+0x5c] + * + * Each coefficient is stored in bits [31],[15:9] in two's complement + * format. They seem to be some kind of weights used in a low-pass + * filter. Both A and B coefficients are applied to the 14 nearest + * samples on each side (Listed from nearest to furthermost. They + * roughly cover 2 framebuffer pixels on each side). They are + * probably multiplied with some more hardwired weights before being + * used: B-coefficients are applied the same on both sides, + * A-coefficients are inverted before being applied to the opposite + * side. + * + * After all the hassle, I got the following formula by empirical + * means... + */ + +#define calc_overscan(o) interpolate(0x100, 0xe1, 0xc1, o) + +#define id1 (1LL << 8) +#define id2 (1LL << 16) +#define id3 (1LL << 24) +#define id4 (1LL << 32) +#define id5 (1LL << 48) + +static struct filter_params{ + int64_t k1; + int64_t ki; + int64_t ki2; + int64_t ki3; + int64_t kr; + int64_t kir; + int64_t ki2r; + int64_t ki3r; + int64_t kf; + int64_t kif; + int64_t ki2f; + int64_t ki3f; + int64_t krf; + int64_t kirf; + int64_t ki2rf; + int64_t ki3rf; +} fparams[2][4] = { + /* Horizontal filter parameters */ + { + {64.311690 * id5, -39.516924 * id5, 6.586143 * id5, 0.000002 * id5, + 0.051285 * id4, 26.168746 * id4, -4.361449 * id4, -0.000001 * id4, + 9.308169 * id3, 78.180965 * id3, -13.030158 * id3, -0.000001 * id3, + -8.801540 * id1, -46.572890 * id1, 7.762145 * id1, -0.000000 * id1}, + {-44.565569 * id5, -68.081246 * id5, 39.812074 * id5, -4.009316 * id5, + 29.832207 * id4, 50.047322 * id4, -25.380017 * id4, 2.546422 * id4, + 104.605622 * id3, 141.908641 * id3, -74.322319 * id3, 7.484316 * id3, + -37.081621 * id1, -90.397510 * id1, 42.784229 * id1, -4.289952 * id1}, + {-56.793244 * id5, 31.153584 * id5, -5.192247 * id5, -0.000003 * id5, + 33.541131 * id4, -34.149302 * id4, 5.691537 * id4, 0.000002 * id4, + 87.196610 * id3, -88.995169 * id3, 14.832456 * id3, 0.000012 * id3, + 17.288138 * id1, 71.864786 * id1, -11.977408 * id1, -0.000009 * id1}, + {51.787796 * id5, 21.211771 * id5, -18.993730 * id5, 1.853310 * id5, + -41.470726 * id4, -17.775823 * id4, 13.057821 * id4, -1.15823 * id4, + -154.235673 * id3, -44.878641 * id3, 40.656077 * id3, -3.695595 * id3, + 112.201065 * id1, 39.992155 * id1, -25.155714 * id1, 2.113984 * id1}, + }, + + /* Vertical filter parameters */ + { + {67.601979 * id5, 0.428319 * id5, -0.071318 * id5, -0.000012 * id5, + -3.402339 * id4, 0.000209 * id4, -0.000092 * id4, 0.000010 * id4, + -9.180996 * id3, 6.111270 * id3, -1.024457 * id3, 0.001043 * id3, + 6.060315 * id1, -0.017425 * id1, 0.007830 * id1, -0.000869 * id1}, + {6.755647 * id5, 5.841348 * id5, 1.469734 * id5, -0.149656 * id5, + 8.293120 * id4, -1.192888 * id4, -0.947652 * id4, 0.094507 * id4, + 37.526655 * id3, 10.257875 * id3, -10.823275 * id3, 1.081497 * id3, + -2.361928 * id1, -2.059432 * id1, 1.840671 * id1, -0.168100 * id1}, + {-14.780391 * id5, -16.042148 * id5, 2.673692 * id5, -0.000000 * id5, + 39.541978 * id4, 5.680053 * id4, -0.946676 * id4, 0.000000 * id4, + 152.994486 * id3, 12.625439 * id3, -2.119579 * id3, 0.002708 * id3, + -38.125089 * id1, -0.855880 * id1, 0.155359 * id1, -0.002245 * id1}, + {-27.476193 * id5, -1.454976 * id5, 1.286557 * id5, 0.025346 * id5, + 20.687300 * id4, 3.014003 * id4, -0.557786 * id4, -0.01311 * id4, + 60.008737 * id3, -0.738273 * id3, 5.408217 * id3, -0.796798 * id3, + -17.296835 * id1, 4.438577 * id1, -2.809420 * id1, 0.385491 * id1}, + } +}; + +static void tv_setup_filter(struct drm_encoder *encoder) +{ + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + struct drm_display_mode *mode = &encoder->crtc->mode; + uint32_t (*filters[])[4][7] = {&tv_enc->state.hfilter, + &tv_enc->state.vfilter}; + int i, j, k; + int32_t overscan = calc_overscan(tv_enc->overscan); + int64_t flicker = (tv_enc->flicker - 50) * (id3 / 100); + uint64_t rs[] = {mode->hdisplay * id3, + mode->vdisplay * id3}; + + do_div(rs[0], overscan * tv_norm->tv_enc_mode.hdisplay); + do_div(rs[1], overscan * tv_norm->tv_enc_mode.vdisplay); + + for (k = 0; k < 2; k++) { + rs[k] = max((int64_t)rs[k], id2); + + for (j = 0; j < 4; j++) { + struct filter_params *p = &fparams[k][j]; + + for (i = 0; i < 7; i++) { + int64_t c = (p->k1 + p->ki*i + p->ki2*i*i + + p->ki3*i*i*i) + + (p->kr + p->kir*i + p->ki2r*i*i + + p->ki3r*i*i*i) * rs[k] + + (p->kf + p->kif*i + p->ki2f*i*i + + p->ki3f*i*i*i) * flicker + + (p->krf + p->kirf*i + p->ki2rf*i*i + + p->ki3rf*i*i*i) * flicker * rs[k]; + + (*filters[k])[j][i] = (c + id5/2) >> 39 + & (0x1 << 31 | 0x7f << 9); + } + } + } +} + +/* Hardware state saving/restoring */ + +static void tv_save_filter(struct drm_device *dev, uint32_t base, + uint32_t regs[4][7]) +{ + int i, j; + uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c }; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 7; j++) + regs[i][j] = nv_read_ptv(dev, offsets[i]+4*j); + } +} + +static void tv_load_filter(struct drm_device *dev, uint32_t base, + uint32_t regs[4][7]) +{ + int i, j; + uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c }; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 7; j++) + nv_write_ptv(dev, offsets[i]+4*j, regs[i][j]); + } +} + +void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state) +{ + int i; + + for (i = 0; i < 0x40; i++) + state->tv_enc[i] = nv_read_tv_enc(dev, i); + + tv_save_filter(dev, NV_PTV_HFILTER, state->hfilter); + tv_save_filter(dev, NV_PTV_HFILTER2, state->hfilter2); + tv_save_filter(dev, NV_PTV_VFILTER, state->vfilter); + + nv_save_ptv(dev, state, 200); + nv_save_ptv(dev, state, 204); + nv_save_ptv(dev, state, 208); + nv_save_ptv(dev, state, 20c); + nv_save_ptv(dev, state, 304); + nv_save_ptv(dev, state, 500); + nv_save_ptv(dev, state, 504); + nv_save_ptv(dev, state, 508); + nv_save_ptv(dev, state, 600); + nv_save_ptv(dev, state, 604); + nv_save_ptv(dev, state, 608); + nv_save_ptv(dev, state, 60c); + nv_save_ptv(dev, state, 610); + nv_save_ptv(dev, state, 614); +} + +void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state) +{ + int i; + + for (i = 0; i < 0x40; i++) + nv_write_tv_enc(dev, i, state->tv_enc[i]); + + tv_load_filter(dev, NV_PTV_HFILTER, state->hfilter); + tv_load_filter(dev, NV_PTV_HFILTER2, state->hfilter2); + tv_load_filter(dev, NV_PTV_VFILTER, state->vfilter); + + nv_load_ptv(dev, state, 200); + nv_load_ptv(dev, state, 204); + nv_load_ptv(dev, state, 208); + nv_load_ptv(dev, state, 20c); + nv_load_ptv(dev, state, 304); + nv_load_ptv(dev, state, 500); + nv_load_ptv(dev, state, 504); + nv_load_ptv(dev, state, 508); + nv_load_ptv(dev, state, 600); + nv_load_ptv(dev, state, 604); + nv_load_ptv(dev, state, 608); + nv_load_ptv(dev, state, 60c); + nv_load_ptv(dev, state, 610); + nv_load_ptv(dev, state, 614); + + /* This is required for some settings to kick in. */ + nv_write_tv_enc(dev, 0x3e, 1); + nv_write_tv_enc(dev, 0x3e, 0); +} + +/* Timings similar to the ones the blob sets */ + +const struct drm_display_mode nv17_tv_modes[] = { + { DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 0, + 320, 344, 392, 560, 0, 200, 200, 202, 220, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC + | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) }, + { DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 0, + 320, 344, 392, 560, 0, 240, 240, 246, 263, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC + | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) }, + { DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 0, + 400, 432, 496, 640, 0, 300, 300, 303, 314, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC + | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) }, + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 0, + 640, 672, 768, 880, 0, 480, 480, 492, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 0, + 720, 752, 872, 960, 0, 480, 480, 493, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 0, + 720, 776, 856, 960, 0, 576, 576, 588, 597, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 0, + 800, 840, 920, 1040, 0, 600, 600, 604, 618, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 0, + 1024, 1064, 1200, 1344, 0, 768, 768, 777, 806, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + {} +}; + +void nv17_tv_update_properties(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + struct nv17_tv_state *regs = &tv_enc->state; + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + int subconnector = tv_enc->select_subconnector ? + tv_enc->select_subconnector : + tv_enc->subconnector; + + switch (subconnector) { + case DRM_MODE_SUBCONNECTOR_Composite: + { + regs->ptv_204 = 0x2; + + /* The composite connector may be found on either pin. */ + if (tv_enc->pin_mask & 0x4) + regs->ptv_204 |= 0x010000; + else if (tv_enc->pin_mask & 0x2) + regs->ptv_204 |= 0x100000; + else + regs->ptv_204 |= 0x110000; + + regs->tv_enc[0x7] = 0x10; + break; + } + case DRM_MODE_SUBCONNECTOR_SVIDEO: + regs->ptv_204 = 0x11012; + regs->tv_enc[0x7] = 0x18; + break; + + case DRM_MODE_SUBCONNECTOR_Component: + regs->ptv_204 = 0x111333; + regs->tv_enc[0x7] = 0x14; + break; + + case DRM_MODE_SUBCONNECTOR_SCART: + regs->ptv_204 = 0x111012; + regs->tv_enc[0x7] = 0x18; + break; + } + + regs->tv_enc[0x20] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x20], + 255, tv_enc->saturation); + regs->tv_enc[0x22] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x22], + 255, tv_enc->saturation); + regs->tv_enc[0x25] = tv_enc->hue * 255 / 100; + + nv_load_ptv(dev, regs, 204); + nv_load_tv_enc(dev, regs, 7); + nv_load_tv_enc(dev, regs, 20); + nv_load_tv_enc(dev, regs, 22); + nv_load_tv_enc(dev, regs, 25); +} + +void nv17_tv_update_rescaler(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + struct nv17_tv_state *regs = &tv_enc->state; + + regs->ptv_208 = 0x40 | (calc_overscan(tv_enc->overscan) << 8); + + tv_setup_filter(encoder); + + nv_load_ptv(dev, regs, 208); + tv_load_filter(dev, NV_PTV_HFILTER, regs->hfilter); + tv_load_filter(dev, NV_PTV_HFILTER2, regs->hfilter2); + tv_load_filter(dev, NV_PTV_VFILTER, regs->vfilter); +} + +void nv17_ctv_update_rescaler(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + int head = nouveau_crtc(encoder->crtc)->index; + struct nv04_crtc_reg *regs = &nv04_display(dev)->mode_reg.crtc_reg[head]; + struct drm_display_mode *crtc_mode = &encoder->crtc->mode; + struct drm_display_mode *output_mode = + &get_tv_norm(encoder)->ctv_enc_mode.mode; + int overscan, hmargin, vmargin, hratio, vratio; + + /* The rescaler doesn't do the right thing for interlaced modes. */ + if (output_mode->flags & DRM_MODE_FLAG_INTERLACE) + overscan = 100; + else + overscan = tv_enc->overscan; + + hmargin = (output_mode->hdisplay - crtc_mode->hdisplay) / 2; + vmargin = (output_mode->vdisplay - crtc_mode->vdisplay) / 2; + + hmargin = interpolate(0, min(hmargin, output_mode->hdisplay/20), + hmargin, overscan); + vmargin = interpolate(0, min(vmargin, output_mode->vdisplay/20), + vmargin, overscan); + + hratio = crtc_mode->hdisplay * 0x800 / + (output_mode->hdisplay - 2*hmargin); + vratio = crtc_mode->vdisplay * 0x800 / + (output_mode->vdisplay - 2*vmargin) & ~3; + + regs->fp_horiz_regs[FP_VALID_START] = hmargin; + regs->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - hmargin - 1; + regs->fp_vert_regs[FP_VALID_START] = vmargin; + regs->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - vmargin - 1; + + regs->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE | + XLATE(vratio, 0, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE) | + NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE | + XLATE(hratio, 0, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE); + + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_START, + regs->fp_horiz_regs[FP_VALID_START]); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_END, + regs->fp_horiz_regs[FP_VALID_END]); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_START, + regs->fp_vert_regs[FP_VALID_START]); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_END, + regs->fp_vert_regs[FP_VALID_END]); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regs->fp_debug_1); +} diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c new file mode 100644 index 0000000..bf13db4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2009 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include "nouveau_drm.h" +#include "nouveau_reg.h" +#include "nouveau_encoder.h" +#include "nouveau_connector.h" +#include "nouveau_crtc.h" +#include "hw.h" +#include + +#include + +#include + +static struct i2c_board_info nv04_tv_encoder_info[] = { + { + I2C_BOARD_INFO("ch7006", 0x75), + .platform_data = &(struct ch7006_encoder_params) { + CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER, + 0, 0, 0, + CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED, + CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC + } + }, + { } +}; + +int nv04_tv_identify(struct drm_device *dev, int i2c_index) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_i2c *i2c = nouveau_i2c(drm->device); + + return i2c->identify(i2c, i2c_index, "TV encoder", + nv04_tv_encoder_info, NULL); +} + + +#define PLLSEL_TV_CRTC1_MASK \ + (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \ + | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1) +#define PLLSEL_TV_CRTC2_MASK \ + (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \ + | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2) + +static void nv04_tv_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; + uint8_t crtc1A; + + NV_DEBUG(drm, "Setting dpms mode %d on TV encoder (output %d)\n", + mode, nv_encoder->dcb->index); + + state->pllsel &= ~(PLLSEL_TV_CRTC1_MASK | PLLSEL_TV_CRTC2_MASK); + + if (mode == DRM_MODE_DPMS_ON) { + int head = nouveau_crtc(encoder->crtc)->index; + crtc1A = NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX); + + state->pllsel |= head ? PLLSEL_TV_CRTC2_MASK : + PLLSEL_TV_CRTC1_MASK; + + /* Inhibit hsync */ + crtc1A |= 0x80; + + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX, crtc1A); + } + + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel); + + get_slave_funcs(encoder)->dpms(encoder, mode); +} + +static void nv04_tv_bind(struct drm_device *dev, int head, bool bind) +{ + struct nv04_crtc_reg *state = &nv04_display(dev)->mode_reg.crtc_reg[head]; + + state->tv_setup = 0; + + if (bind) + state->CRTC[NV_CIO_CRE_49] |= 0x10; + else + state->CRTC[NV_CIO_CRE_49] &= ~0x10; + + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_LCD__INDEX, + state->CRTC[NV_CIO_CRE_LCD__INDEX]); + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_49, + state->CRTC[NV_CIO_CRE_49]); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, + state->tv_setup); +} + +static void nv04_tv_prepare(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + int head = nouveau_crtc(encoder->crtc)->index; + struct drm_encoder_helper_funcs *helper = encoder->helper_private; + + helper->dpms(encoder, DRM_MODE_DPMS_OFF); + + nv04_dfp_disable(dev, head); + + if (nv_two_heads(dev)) + nv04_tv_bind(dev, head ^ 1, false); + + nv04_tv_bind(dev, head, true); +} + +static void nv04_tv_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; + + regp->tv_htotal = adjusted_mode->htotal; + regp->tv_vtotal = adjusted_mode->vtotal; + + /* These delay the TV signals with respect to the VGA port, + * they might be useful if we ever allow a CRTC to drive + * multiple outputs. + */ + regp->tv_hskew = 1; + regp->tv_hsync_delay = 1; + regp->tv_hsync_delay2 = 64; + regp->tv_vskew = 1; + regp->tv_vsync_delay = 1; + + get_slave_funcs(encoder)->mode_set(encoder, mode, adjusted_mode); +} + +static void nv04_tv_commit(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct drm_encoder_helper_funcs *helper = encoder->helper_private; + + helper->dpms(encoder, DRM_MODE_DPMS_ON); + + NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n", + drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); +} + +static void nv04_tv_destroy(struct drm_encoder *encoder) +{ + get_slave_funcs(encoder)->destroy(encoder); + drm_encoder_cleanup(encoder); + + kfree(encoder->helper_private); + kfree(nouveau_encoder(encoder)); +} + +static const struct drm_encoder_funcs nv04_tv_funcs = { + .destroy = nv04_tv_destroy, +}; + +static const struct drm_encoder_helper_funcs nv04_tv_helper_funcs = { + .dpms = nv04_tv_dpms, + .save = drm_i2c_encoder_save, + .restore = drm_i2c_encoder_restore, + .mode_fixup = drm_i2c_encoder_mode_fixup, + .prepare = nv04_tv_prepare, + .commit = nv04_tv_commit, + .mode_set = nv04_tv_mode_set, + .detect = drm_i2c_encoder_detect, +}; + +int +nv04_tv_create(struct drm_connector *connector, struct dcb_output *entry) +{ + struct nouveau_encoder *nv_encoder; + struct drm_encoder *encoder; + struct drm_device *dev = connector->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_i2c *i2c = nouveau_i2c(drm->device); + struct nouveau_i2c_port *port = i2c->find(i2c, entry->i2c_index); + int type, ret; + + /* Ensure that we can talk to this encoder */ + type = nv04_tv_identify(dev, entry->i2c_index); + if (type < 0) + return type; + + /* Allocate the necessary memory */ + nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); + if (!nv_encoder) + return -ENOMEM; + + /* Initialize the common members */ + encoder = to_drm_encoder(nv_encoder); + + drm_encoder_init(dev, encoder, &nv04_tv_funcs, DRM_MODE_ENCODER_TVDAC); + drm_encoder_helper_add(encoder, &nv04_tv_helper_funcs); + + encoder->possible_crtcs = entry->heads; + encoder->possible_clones = 0; + nv_encoder->dcb = entry; + nv_encoder->or = ffs(entry->or) - 1; + + /* Run the slave-specific initialization */ + ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder), + &port->adapter, &nv04_tv_encoder_info[type]); + if (ret < 0) + goto fail_cleanup; + + /* Attach it to the specified connector. */ + get_slave_funcs(encoder)->create_resources(encoder, connector); + drm_mode_connector_attach_encoder(connector, encoder); + + return 0; + +fail_cleanup: + drm_encoder_cleanup(encoder); + kfree(nv_encoder); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c new file mode 100644 index 0000000..acef48f --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c @@ -0,0 +1,843 @@ +/* + * Copyright (C) 2009 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include "nouveau_drm.h" +#include "nouveau_reg.h" +#include "nouveau_encoder.h" +#include "nouveau_connector.h" +#include "nouveau_crtc.h" +#include "hw.h" +#include "tvnv17.h" + +#include + +#include +#include + +MODULE_PARM_DESC(tv_norm, "Default TV norm.\n" + "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, NTSC-M, NTSC-J,\n" + "\t\t\thd480i, hd480p, hd576i, hd576p, hd720p, hd1080i.\n" + "\t\tDefault: PAL\n" + "\t\t*NOTE* Ignored for cards with external TV encoders."); +static char *nouveau_tv_norm; +module_param_named(tv_norm, nouveau_tv_norm, charp, 0400); + +static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_gpio *gpio = nouveau_gpio(drm->device); + uint32_t testval, regoffset = nv04_dac_output_offset(encoder); + uint32_t gpio0, gpio1, fp_htotal, fp_hsync_start, fp_hsync_end, + fp_control, test_ctrl, dacclk, ctv_14, ctv_1c, ctv_6c; + uint32_t sample = 0; + int head; + +#define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20) + testval = RGB_TEST_DATA(0x82, 0xeb, 0x82); + if (drm->vbios.tvdactestval) + testval = drm->vbios.tvdactestval; + + dacclk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); + head = (dacclk & 0x100) >> 8; + + /* Save the previous state. */ + gpio1 = gpio->get(gpio, 0, DCB_GPIO_TVDAC1, 0xff); + gpio0 = gpio->get(gpio, 0, DCB_GPIO_TVDAC0, 0xff); + fp_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL); + fp_hsync_start = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START); + fp_hsync_end = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END); + fp_control = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL); + test_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); + ctv_1c = NVReadRAMDAC(dev, head, 0x680c1c); + ctv_14 = NVReadRAMDAC(dev, head, 0x680c14); + ctv_6c = NVReadRAMDAC(dev, head, 0x680c6c); + + /* Prepare the DAC for load detection. */ + gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, true); + gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, true); + + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, 1343); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, 1047); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, 1183); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, + NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | + NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12 | + NV_PRAMDAC_FP_TG_CONTROL_READ_PROG | + NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | + NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS); + + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, 0); + + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, + (dacclk & ~0xff) | 0x22); + msleep(1); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, + (dacclk & ~0xff) | 0x21); + + NVWriteRAMDAC(dev, head, 0x680c1c, 1 << 20); + NVWriteRAMDAC(dev, head, 0x680c14, 4 << 16); + + /* Sample pin 0x4 (usually S-video luma). */ + NVWriteRAMDAC(dev, head, 0x680c6c, testval >> 10 & 0x3ff); + msleep(20); + sample |= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset) + & 0x4 << 28; + + /* Sample the remaining pins. */ + NVWriteRAMDAC(dev, head, 0x680c6c, testval & 0x3ff); + msleep(20); + sample |= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset) + & 0xa << 28; + + /* Restore the previous state. */ + NVWriteRAMDAC(dev, head, 0x680c1c, ctv_1c); + NVWriteRAMDAC(dev, head, 0x680c14, ctv_14); + NVWriteRAMDAC(dev, head, 0x680c6c, ctv_6c); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, dacclk); + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, test_ctrl); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, fp_control); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, fp_hsync_end); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, fp_hsync_start); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, fp_htotal); + gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, gpio1); + gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, gpio0); + + return sample; +} + +static bool +get_tv_detect_quirks(struct drm_device *dev, uint32_t *pin_mask) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_object *device = drm->device; + + /* Zotac FX5200 */ + if (nv_device_match(device, 0x0322, 0x19da, 0x1035) || + nv_device_match(device, 0x0322, 0x19da, 0x2035)) { + *pin_mask = 0xc; + return false; + } + + /* MSI nForce2 IGP */ + if (nv_device_match(device, 0x01f0, 0x1462, 0x5710)) { + *pin_mask = 0xc; + return false; + } + + return true; +} + +static enum drm_connector_status +nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct drm_mode_config *conf = &dev->mode_config; + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + struct dcb_output *dcb = tv_enc->base.dcb; + bool reliable = get_tv_detect_quirks(dev, &tv_enc->pin_mask); + + if (nv04_dac_in_use(encoder)) + return connector_status_disconnected; + + if (reliable) { + if (nv_device(drm->device)->chipset == 0x42 || + nv_device(drm->device)->chipset == 0x43) + tv_enc->pin_mask = + nv42_tv_sample_load(encoder) >> 28 & 0xe; + else + tv_enc->pin_mask = + nv17_dac_sample_load(encoder) >> 28 & 0xe; + } + + switch (tv_enc->pin_mask) { + case 0x2: + case 0x4: + tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Composite; + break; + case 0xc: + tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO; + break; + case 0xe: + if (dcb->tvconf.has_component_output) + tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Component; + else + tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SCART; + break; + default: + tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown; + break; + } + + drm_object_property_set_value(&connector->base, + conf->tv_subconnector_property, + tv_enc->subconnector); + + if (!reliable) { + return connector_status_unknown; + } else if (tv_enc->subconnector) { + NV_INFO(drm, "Load detected on output %c\n", + '@' + ffs(dcb->or)); + return connector_status_connected; + } else { + return connector_status_disconnected; + } +} + +static int nv17_tv_get_ld_modes(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + const struct drm_display_mode *tv_mode; + int n = 0; + + for (tv_mode = nv17_tv_modes; tv_mode->hdisplay; tv_mode++) { + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(encoder->dev, tv_mode); + + mode->clock = tv_norm->tv_enc_mode.vrefresh * + mode->htotal / 1000 * + mode->vtotal / 1000; + + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + mode->clock *= 2; + + if (mode->hdisplay == tv_norm->tv_enc_mode.hdisplay && + mode->vdisplay == tv_norm->tv_enc_mode.vdisplay) + mode->type |= DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(connector, mode); + n++; + } + + return n; +} + +static int nv17_tv_get_hd_modes(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + struct drm_display_mode *output_mode = &tv_norm->ctv_enc_mode.mode; + struct drm_display_mode *mode; + const struct { + int hdisplay; + int vdisplay; + } modes[] = { + { 640, 400 }, + { 640, 480 }, + { 720, 480 }, + { 720, 576 }, + { 800, 600 }, + { 1024, 768 }, + { 1280, 720 }, + { 1280, 1024 }, + { 1920, 1080 } + }; + int i, n = 0; + + for (i = 0; i < ARRAY_SIZE(modes); i++) { + if (modes[i].hdisplay > output_mode->hdisplay || + modes[i].vdisplay > output_mode->vdisplay) + continue; + + if (modes[i].hdisplay == output_mode->hdisplay && + modes[i].vdisplay == output_mode->vdisplay) { + mode = drm_mode_duplicate(encoder->dev, output_mode); + mode->type |= DRM_MODE_TYPE_PREFERRED; + + } else { + mode = drm_cvt_mode(encoder->dev, modes[i].hdisplay, + modes[i].vdisplay, 60, false, + (output_mode->flags & + DRM_MODE_FLAG_INTERLACE), false); + } + + /* CVT modes are sometimes unsuitable... */ + if (output_mode->hdisplay <= 720 + || output_mode->hdisplay >= 1920) { + mode->htotal = output_mode->htotal; + mode->hsync_start = (mode->hdisplay + (mode->htotal + - mode->hdisplay) * 9 / 10) & ~7; + mode->hsync_end = mode->hsync_start + 8; + } + + if (output_mode->vdisplay >= 1024) { + mode->vtotal = output_mode->vtotal; + mode->vsync_start = output_mode->vsync_start; + mode->vsync_end = output_mode->vsync_end; + } + + mode->type |= DRM_MODE_TYPE_DRIVER; + drm_mode_probed_add(connector, mode); + n++; + } + + return n; +} + +static int nv17_tv_get_modes(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + + if (tv_norm->kind == CTV_ENC_MODE) + return nv17_tv_get_hd_modes(encoder, connector); + else + return nv17_tv_get_ld_modes(encoder, connector); +} + +static int nv17_tv_mode_valid(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + + if (tv_norm->kind == CTV_ENC_MODE) { + struct drm_display_mode *output_mode = + &tv_norm->ctv_enc_mode.mode; + + if (mode->clock > 400000) + return MODE_CLOCK_HIGH; + + if (mode->hdisplay > output_mode->hdisplay || + mode->vdisplay > output_mode->vdisplay) + return MODE_BAD; + + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) != + (output_mode->flags & DRM_MODE_FLAG_INTERLACE)) + return MODE_NO_INTERLACE; + + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + } else { + const int vsync_tolerance = 600; + + if (mode->clock > 70000) + return MODE_CLOCK_HIGH; + + if (abs(drm_mode_vrefresh(mode) * 1000 - + tv_norm->tv_enc_mode.vrefresh) > vsync_tolerance) + return MODE_VSYNC; + + /* The encoder takes care of the actual interlacing */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + } + + return MODE_OK; +} + +static bool nv17_tv_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + + if (nv04_dac_in_use(encoder)) + return false; + + if (tv_norm->kind == CTV_ENC_MODE) + adjusted_mode->clock = tv_norm->ctv_enc_mode.mode.clock; + else + adjusted_mode->clock = 90000; + + return true; +} + +static void nv17_tv_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_gpio *gpio = nouveau_gpio(drm->device); + struct nv17_tv_state *regs = &to_tv_enc(encoder)->state; + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + + if (nouveau_encoder(encoder)->last_dpms == mode) + return; + nouveau_encoder(encoder)->last_dpms = mode; + + NV_INFO(drm, "Setting dpms mode %d on TV encoder (output %d)\n", + mode, nouveau_encoder(encoder)->dcb->index); + + regs->ptv_200 &= ~1; + + if (tv_norm->kind == CTV_ENC_MODE) { + nv04_dfp_update_fp_control(encoder, mode); + + } else { + nv04_dfp_update_fp_control(encoder, DRM_MODE_DPMS_OFF); + + if (mode == DRM_MODE_DPMS_ON) + regs->ptv_200 |= 1; + } + + nv_load_ptv(dev, regs, 200); + + gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, mode == DRM_MODE_DPMS_ON); + gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, mode == DRM_MODE_DPMS_ON); + + nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); +} + +static void nv17_tv_prepare(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct drm_encoder_helper_funcs *helper = encoder->helper_private; + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + int head = nouveau_crtc(encoder->crtc)->index; + uint8_t *cr_lcd = &nv04_display(dev)->mode_reg.crtc_reg[head].CRTC[ + NV_CIO_CRE_LCD__INDEX]; + uint32_t dacclk_off = NV_PRAMDAC_DACCLK + + nv04_dac_output_offset(encoder); + uint32_t dacclk; + + helper->dpms(encoder, DRM_MODE_DPMS_OFF); + + nv04_dfp_disable(dev, head); + + /* Unbind any FP encoders from this head if we need the FP + * stuff enabled. */ + if (tv_norm->kind == CTV_ENC_MODE) { + struct drm_encoder *enc; + + list_for_each_entry(enc, &dev->mode_config.encoder_list, head) { + struct dcb_output *dcb = nouveau_encoder(enc)->dcb; + + if ((dcb->type == DCB_OUTPUT_TMDS || + dcb->type == DCB_OUTPUT_LVDS) && + !enc->crtc && + nv04_dfp_get_bound_head(dev, dcb) == head) { + nv04_dfp_bind_head(dev, dcb, head ^ 1, + drm->vbios.fp.dual_link); + } + } + + } + + if (tv_norm->kind == CTV_ENC_MODE) + *cr_lcd |= 0x1 | (head ? 0x0 : 0x8); + + /* Set the DACCLK register */ + dacclk = (NVReadRAMDAC(dev, 0, dacclk_off) & ~0x30) | 0x1; + + if (nv_device(drm->device)->card_type == NV_40) + dacclk |= 0x1a << 16; + + if (tv_norm->kind == CTV_ENC_MODE) { + dacclk |= 0x20; + + if (head) + dacclk |= 0x100; + else + dacclk &= ~0x100; + + } else { + dacclk |= 0x10; + + } + + NVWriteRAMDAC(dev, 0, dacclk_off, dacclk); +} + +static void nv17_tv_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *drm_mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + int head = nouveau_crtc(encoder->crtc)->index; + struct nv04_crtc_reg *regs = &nv04_display(dev)->mode_reg.crtc_reg[head]; + struct nv17_tv_state *tv_regs = &to_tv_enc(encoder)->state; + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + int i; + + regs->CRTC[NV_CIO_CRE_53] = 0x40; /* FP_HTIMING */ + regs->CRTC[NV_CIO_CRE_54] = 0; /* FP_VTIMING */ + regs->ramdac_630 = 0x2; /* turn off green mode (tv test pattern?) */ + regs->tv_setup = 1; + regs->ramdac_8c0 = 0x0; + + if (tv_norm->kind == TV_ENC_MODE) { + tv_regs->ptv_200 = 0x13111100; + if (head) + tv_regs->ptv_200 |= 0x10; + + tv_regs->ptv_20c = 0x808010; + tv_regs->ptv_304 = 0x2d00000; + tv_regs->ptv_600 = 0x0; + tv_regs->ptv_60c = 0x0; + tv_regs->ptv_610 = 0x1e00000; + + if (tv_norm->tv_enc_mode.vdisplay == 576) { + tv_regs->ptv_508 = 0x1200000; + tv_regs->ptv_614 = 0x33; + + } else if (tv_norm->tv_enc_mode.vdisplay == 480) { + tv_regs->ptv_508 = 0xf00000; + tv_regs->ptv_614 = 0x13; + } + + if (nv_device(drm->device)->card_type >= NV_30) { + tv_regs->ptv_500 = 0xe8e0; + tv_regs->ptv_504 = 0x1710; + tv_regs->ptv_604 = 0x0; + tv_regs->ptv_608 = 0x0; + } else { + if (tv_norm->tv_enc_mode.vdisplay == 576) { + tv_regs->ptv_604 = 0x20; + tv_regs->ptv_608 = 0x10; + tv_regs->ptv_500 = 0x19710; + tv_regs->ptv_504 = 0x68f0; + + } else if (tv_norm->tv_enc_mode.vdisplay == 480) { + tv_regs->ptv_604 = 0x10; + tv_regs->ptv_608 = 0x20; + tv_regs->ptv_500 = 0x4b90; + tv_regs->ptv_504 = 0x1b480; + } + } + + for (i = 0; i < 0x40; i++) + tv_regs->tv_enc[i] = tv_norm->tv_enc_mode.tv_enc[i]; + + } else { + struct drm_display_mode *output_mode = + &tv_norm->ctv_enc_mode.mode; + + /* The registers in PRAMDAC+0xc00 control some timings and CSC + * parameters for the CTV encoder (It's only used for "HD" TV + * modes, I don't think I have enough working to guess what + * they exactly mean...), it's probably connected at the + * output of the FP encoder, but it also needs the analog + * encoder in its OR enabled and routed to the head it's + * using. It's enabled with the DACCLK register, bits [5:4]. + */ + for (i = 0; i < 38; i++) + regs->ctv_regs[i] = tv_norm->ctv_enc_mode.ctv_regs[i]; + + regs->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1; + regs->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1; + regs->fp_horiz_regs[FP_SYNC_START] = + output_mode->hsync_start - 1; + regs->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1; + regs->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay + + max((output_mode->hdisplay-600)/40 - 1, 1); + + regs->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1; + regs->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1; + regs->fp_vert_regs[FP_SYNC_START] = + output_mode->vsync_start - 1; + regs->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1; + regs->fp_vert_regs[FP_CRTC] = output_mode->vdisplay - 1; + + regs->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | + NV_PRAMDAC_FP_TG_CONTROL_READ_PROG | + NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12; + + if (output_mode->flags & DRM_MODE_FLAG_PVSYNC) + regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS; + if (output_mode->flags & DRM_MODE_FLAG_PHSYNC) + regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS; + + regs->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND | + NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND | + NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR | + NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR | + NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED | + NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE | + NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE; + + regs->fp_debug_2 = 0; + + regs->fp_margin_color = 0x801080; + + } +} + +static void nv17_tv_commit(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_encoder_helper_funcs *helper = encoder->helper_private; + + if (get_tv_norm(encoder)->kind == TV_ENC_MODE) { + nv17_tv_update_rescaler(encoder); + nv17_tv_update_properties(encoder); + } else { + nv17_ctv_update_rescaler(encoder); + } + + nv17_tv_state_load(dev, &to_tv_enc(encoder)->state); + + /* This could use refinement for flatpanels, but it should work */ + if (nv_device(drm->device)->chipset < 0x44) + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + + nv04_dac_output_offset(encoder), + 0xf0000000); + else + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + + nv04_dac_output_offset(encoder), + 0x00100000); + + helper->dpms(encoder, DRM_MODE_DPMS_ON); + + NV_INFO(drm, "Output %s is running on CRTC %d using output %c\n", + drm_get_connector_name( + &nouveau_encoder_connector_get(nv_encoder)->base), + nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); +} + +static void nv17_tv_save(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + + nouveau_encoder(encoder)->restore.output = + NVReadRAMDAC(dev, 0, + NV_PRAMDAC_DACCLK + + nv04_dac_output_offset(encoder)); + + nv17_tv_state_save(dev, &tv_enc->saved_state); + + tv_enc->state.ptv_200 = tv_enc->saved_state.ptv_200; +} + +static void nv17_tv_restore(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + + nv04_dac_output_offset(encoder), + nouveau_encoder(encoder)->restore.output); + + nv17_tv_state_load(dev, &to_tv_enc(encoder)->saved_state); + + nouveau_encoder(encoder)->last_dpms = NV_DPMS_CLEARED; +} + +static int nv17_tv_create_resources(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct drm_device *dev = encoder->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct drm_mode_config *conf = &dev->mode_config; + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; + int num_tv_norms = dcb->tvconf.has_component_output ? NUM_TV_NORMS : + NUM_LD_TV_NORMS; + int i; + + if (nouveau_tv_norm) { + for (i = 0; i < num_tv_norms; i++) { + if (!strcmp(nv17_tv_norm_names[i], nouveau_tv_norm)) { + tv_enc->tv_norm = i; + break; + } + } + + if (i == num_tv_norms) + NV_WARN(drm, "Invalid TV norm setting \"%s\"\n", + nouveau_tv_norm); + } + + drm_mode_create_tv_properties(dev, num_tv_norms, nv17_tv_norm_names); + + drm_object_attach_property(&connector->base, + conf->tv_select_subconnector_property, + tv_enc->select_subconnector); + drm_object_attach_property(&connector->base, + conf->tv_subconnector_property, + tv_enc->subconnector); + drm_object_attach_property(&connector->base, + conf->tv_mode_property, + tv_enc->tv_norm); + drm_object_attach_property(&connector->base, + conf->tv_flicker_reduction_property, + tv_enc->flicker); + drm_object_attach_property(&connector->base, + conf->tv_saturation_property, + tv_enc->saturation); + drm_object_attach_property(&connector->base, + conf->tv_hue_property, + tv_enc->hue); + drm_object_attach_property(&connector->base, + conf->tv_overscan_property, + tv_enc->overscan); + + return 0; +} + +static int nv17_tv_set_property(struct drm_encoder *encoder, + struct drm_connector *connector, + struct drm_property *property, + uint64_t val) +{ + struct drm_mode_config *conf = &encoder->dev->mode_config; + struct drm_crtc *crtc = encoder->crtc; + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + bool modes_changed = false; + + if (property == conf->tv_overscan_property) { + tv_enc->overscan = val; + if (encoder->crtc) { + if (tv_norm->kind == CTV_ENC_MODE) + nv17_ctv_update_rescaler(encoder); + else + nv17_tv_update_rescaler(encoder); + } + + } else if (property == conf->tv_saturation_property) { + if (tv_norm->kind != TV_ENC_MODE) + return -EINVAL; + + tv_enc->saturation = val; + nv17_tv_update_properties(encoder); + + } else if (property == conf->tv_hue_property) { + if (tv_norm->kind != TV_ENC_MODE) + return -EINVAL; + + tv_enc->hue = val; + nv17_tv_update_properties(encoder); + + } else if (property == conf->tv_flicker_reduction_property) { + if (tv_norm->kind != TV_ENC_MODE) + return -EINVAL; + + tv_enc->flicker = val; + if (encoder->crtc) + nv17_tv_update_rescaler(encoder); + + } else if (property == conf->tv_mode_property) { + if (connector->dpms != DRM_MODE_DPMS_OFF) + return -EINVAL; + + tv_enc->tv_norm = val; + + modes_changed = true; + + } else if (property == conf->tv_select_subconnector_property) { + if (tv_norm->kind != TV_ENC_MODE) + return -EINVAL; + + tv_enc->select_subconnector = val; + nv17_tv_update_properties(encoder); + + } else { + return -EINVAL; + } + + if (modes_changed) { + drm_helper_probe_single_connector_modes(connector, 0, 0); + + /* Disable the crtc to ensure a full modeset is + * performed whenever it's turned on again. */ + if (crtc) { + struct drm_mode_set modeset = { + .crtc = crtc, + }; + + drm_mode_set_config_internal(&modeset); + } + } + + return 0; +} + +static void nv17_tv_destroy(struct drm_encoder *encoder) +{ + struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); + + drm_encoder_cleanup(encoder); + kfree(tv_enc); +} + +static struct drm_encoder_helper_funcs nv17_tv_helper_funcs = { + .dpms = nv17_tv_dpms, + .save = nv17_tv_save, + .restore = nv17_tv_restore, + .mode_fixup = nv17_tv_mode_fixup, + .prepare = nv17_tv_prepare, + .commit = nv17_tv_commit, + .mode_set = nv17_tv_mode_set, + .detect = nv17_tv_detect, +}; + +static struct drm_encoder_slave_funcs nv17_tv_slave_funcs = { + .get_modes = nv17_tv_get_modes, + .mode_valid = nv17_tv_mode_valid, + .create_resources = nv17_tv_create_resources, + .set_property = nv17_tv_set_property, +}; + +static struct drm_encoder_funcs nv17_tv_funcs = { + .destroy = nv17_tv_destroy, +}; + +int +nv17_tv_create(struct drm_connector *connector, struct dcb_output *entry) +{ + struct drm_device *dev = connector->dev; + struct drm_encoder *encoder; + struct nv17_tv_encoder *tv_enc = NULL; + + tv_enc = kzalloc(sizeof(*tv_enc), GFP_KERNEL); + if (!tv_enc) + return -ENOMEM; + + tv_enc->overscan = 50; + tv_enc->flicker = 50; + tv_enc->saturation = 50; + tv_enc->hue = 0; + tv_enc->tv_norm = TV_NORM_PAL; + tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown; + tv_enc->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic; + tv_enc->pin_mask = 0; + + encoder = to_drm_encoder(&tv_enc->base); + + tv_enc->base.dcb = entry; + tv_enc->base.or = ffs(entry->or) - 1; + + drm_encoder_init(dev, encoder, &nv17_tv_funcs, DRM_MODE_ENCODER_TVDAC); + drm_encoder_helper_add(encoder, &nv17_tv_helper_funcs); + to_encoder_slave(encoder)->slave_funcs = &nv17_tv_slave_funcs; + + encoder->possible_crtcs = entry->heads; + encoder->possible_clones = 0; + + nv17_tv_create_resources(encoder, connector); + drm_mode_connector_attach_encoder(connector, encoder); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h new file mode 100644 index 0000000..7b33154 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2009 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __NV17_TV_H__ +#define __NV17_TV_H__ + +struct nv17_tv_state { + uint8_t tv_enc[0x40]; + + uint32_t hfilter[4][7]; + uint32_t hfilter2[4][7]; + uint32_t vfilter[4][7]; + + uint32_t ptv_200; + uint32_t ptv_204; + uint32_t ptv_208; + uint32_t ptv_20c; + uint32_t ptv_304; + uint32_t ptv_500; + uint32_t ptv_504; + uint32_t ptv_508; + uint32_t ptv_600; + uint32_t ptv_604; + uint32_t ptv_608; + uint32_t ptv_60c; + uint32_t ptv_610; + uint32_t ptv_614; +}; + +enum nv17_tv_norm{ + TV_NORM_PAL, + TV_NORM_PAL_M, + TV_NORM_PAL_N, + TV_NORM_PAL_NC, + TV_NORM_NTSC_M, + TV_NORM_NTSC_J, + NUM_LD_TV_NORMS, + TV_NORM_HD480I = NUM_LD_TV_NORMS, + TV_NORM_HD480P, + TV_NORM_HD576I, + TV_NORM_HD576P, + TV_NORM_HD720P, + TV_NORM_HD1080I, + NUM_TV_NORMS +}; + +struct nv17_tv_encoder { + struct nouveau_encoder base; + + struct nv17_tv_state state; + struct nv17_tv_state saved_state; + + int overscan; + int flicker; + int saturation; + int hue; + enum nv17_tv_norm tv_norm; + int subconnector; + int select_subconnector; + uint32_t pin_mask; +}; +#define to_tv_enc(x) container_of(nouveau_encoder(x), \ + struct nv17_tv_encoder, base) + +extern char *nv17_tv_norm_names[NUM_TV_NORMS]; + +extern struct nv17_tv_norm_params { + enum { + TV_ENC_MODE, + CTV_ENC_MODE, + } kind; + + union { + struct { + int hdisplay; + int vdisplay; + int vrefresh; /* mHz */ + + uint8_t tv_enc[0x40]; + } tv_enc_mode; + + struct { + struct drm_display_mode mode; + + uint32_t ctv_regs[38]; + } ctv_enc_mode; + }; + +} nv17_tv_norms[NUM_TV_NORMS]; +#define get_tv_norm(enc) (&nv17_tv_norms[to_tv_enc(enc)->tv_norm]) + +extern const struct drm_display_mode nv17_tv_modes[]; + +static inline int interpolate(int y0, int y1, int y2, int x) +{ + return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50; +} + +void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state); +void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state); +void nv17_tv_update_properties(struct drm_encoder *encoder); +void nv17_tv_update_rescaler(struct drm_encoder *encoder); +void nv17_ctv_update_rescaler(struct drm_encoder *encoder); + +/* TV hardware access functions */ + +static inline void nv_write_ptv(struct drm_device *dev, uint32_t reg, + uint32_t val) +{ + struct nouveau_device *device = nouveau_dev(dev); + nv_wr32(device, reg, val); +} + +static inline uint32_t nv_read_ptv(struct drm_device *dev, uint32_t reg) +{ + struct nouveau_device *device = nouveau_dev(dev); + return nv_rd32(device, reg); +} + +static inline void nv_write_tv_enc(struct drm_device *dev, uint8_t reg, + uint8_t val) +{ + nv_write_ptv(dev, NV_PTV_TV_INDEX, reg); + nv_write_ptv(dev, NV_PTV_TV_DATA, val); +} + +static inline uint8_t nv_read_tv_enc(struct drm_device *dev, uint8_t reg) +{ + nv_write_ptv(dev, NV_PTV_TV_INDEX, reg); + return nv_read_ptv(dev, NV_PTV_TV_DATA); +} + +#define nv_load_ptv(dev, state, reg) \ + nv_write_ptv(dev, NV_PTV_OFFSET + 0x##reg, state->ptv_##reg) +#define nv_save_ptv(dev, state, reg) \ + state->ptv_##reg = nv_read_ptv(dev, NV_PTV_OFFSET + 0x##reg) +#define nv_load_tv_enc(dev, state, reg) \ + nv_write_tv_enc(dev, 0x##reg, state->tv_enc[0x##reg]) + +#endif diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 50a6dd0..6aa2137 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -28,7 +28,7 @@ #include "nouveau_drm.h" #include "nouveau_reg.h" -#include "nouveau_hw.h" +#include "dispnv04/hw.h" #include "nouveau_encoder.h" #include diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 7ccd28f..0067586 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -24,8 +24,6 @@ #ifndef __NOUVEAU_DISPBIOS_H__ #define __NOUVEAU_DISPBIOS_H__ -#include "nvreg.h" - #define DCB_MAX_NUM_ENTRIES 16 #define DCB_MAX_NUM_I2C_ENTRIES 16 #define DCB_MAX_NUM_GPIO_ENTRIES 32 diff --git a/drivers/gpu/drm/nouveau/nouveau_calc.c b/drivers/gpu/drm/nouveau/nouveau_calc.c deleted file mode 100644 index 6da5764..0000000 --- a/drivers/gpu/drm/nouveau/nouveau_calc.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 1993-2003 NVIDIA, Corporation - * Copyright 2007-2009 Stuart Bennett - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF - * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#include "nouveau_drm.h" -#include "nouveau_reg.h" -#include "nouveau_hw.h" - -/****************************************************************************\ -* * -* The video arbitration routines calculate some "magic" numbers. Fixes * -* the snow seen when accessing the framebuffer without it. * -* It just works (I hope). * -* * -\****************************************************************************/ - -struct nv_fifo_info { - int lwm; - int burst; -}; - -struct nv_sim_state { - int pclk_khz; - int mclk_khz; - int nvclk_khz; - int bpp; - int mem_page_miss; - int mem_latency; - int memory_type; - int memory_width; - int two_heads; -}; - -static void -nv04_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb) -{ - int pagemiss, cas, width, bpp; - int nvclks, mclks, pclks, crtpagemiss; - int found, mclk_extra, mclk_loop, cbs, m1, p1; - int mclk_freq, pclk_freq, nvclk_freq; - int us_m, us_n, us_p, crtc_drain_rate; - int cpm_us, us_crt, clwm; - - pclk_freq = arb->pclk_khz; - mclk_freq = arb->mclk_khz; - nvclk_freq = arb->nvclk_khz; - pagemiss = arb->mem_page_miss; - cas = arb->mem_latency; - width = arb->memory_width >> 6; - bpp = arb->bpp; - cbs = 128; - - pclks = 2; - nvclks = 10; - mclks = 13 + cas; - mclk_extra = 3; - found = 0; - - while (!found) { - found = 1; - - mclk_loop = mclks + mclk_extra; - us_m = mclk_loop * 1000 * 1000 / mclk_freq; - us_n = nvclks * 1000 * 1000 / nvclk_freq; - us_p = nvclks * 1000 * 1000 / pclk_freq; - - crtc_drain_rate = pclk_freq * bpp / 8; - crtpagemiss = 2; - crtpagemiss += 1; - cpm_us = crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq; - us_crt = cpm_us + us_m + us_n + us_p; - clwm = us_crt * crtc_drain_rate / (1000 * 1000); - clwm++; - - m1 = clwm + cbs - 512; - p1 = m1 * pclk_freq / mclk_freq; - p1 = p1 * bpp / 8; - if ((p1 < m1 && m1 > 0) || clwm > 519) { - found = !mclk_extra; - mclk_extra--; - } - if (clwm < 384) - clwm = 384; - - fifo->lwm = clwm; - fifo->burst = cbs; - } -} - -static void -nv10_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb) -{ - int fill_rate, drain_rate; - int pclks, nvclks, mclks, xclks; - int pclk_freq, nvclk_freq, mclk_freq; - int fill_lat, extra_lat; - int max_burst_o, max_burst_l; - int fifo_len, min_lwm, max_lwm; - const int burst_lat = 80; /* Maximum allowable latency due - * to the CRTC FIFO burst. (ns) */ - - pclk_freq = arb->pclk_khz; - nvclk_freq = arb->nvclk_khz; - mclk_freq = arb->mclk_khz; - - fill_rate = mclk_freq * arb->memory_width / 8; /* kB/s */ - drain_rate = pclk_freq * arb->bpp / 8; /* kB/s */ - - fifo_len = arb->two_heads ? 1536 : 1024; /* B */ - - /* Fixed FIFO refill latency. */ - - pclks = 4; /* lwm detect. */ - - nvclks = 3 /* lwm -> sync. */ - + 2 /* fbi bus cycles (1 req + 1 busy) */ - + 1 /* 2 edge sync. may be very close to edge so - * just put one. */ - + 1 /* fbi_d_rdv_n */ - + 1 /* Fbi_d_rdata */ - + 1; /* crtfifo load */ - - mclks = 1 /* 2 edge sync. may be very close to edge so - * just put one. */ - + 1 /* arb_hp_req */ - + 5 /* tiling pipeline */ - + 2 /* latency fifo */ - + 2 /* memory request to fbio block */ - + 7; /* data returned from fbio block */ - - /* Need to accumulate 256 bits for read */ - mclks += (arb->memory_type == 0 ? 2 : 1) - * arb->memory_width / 32; - - fill_lat = mclks * 1000 * 1000 / mclk_freq /* minimum mclk latency */ - + nvclks * 1000 * 1000 / nvclk_freq /* nvclk latency */ - + pclks * 1000 * 1000 / pclk_freq; /* pclk latency */ - - /* Conditional FIFO refill latency. */ - - xclks = 2 * arb->mem_page_miss + mclks /* Extra latency due to - * the overlay. */ - + 2 * arb->mem_page_miss /* Extra pagemiss latency. */ - + (arb->bpp == 32 ? 8 : 4); /* Margin of error. */ - - extra_lat = xclks * 1000 * 1000 / mclk_freq; - - if (arb->two_heads) - /* Account for another CRTC. */ - extra_lat += fill_lat + extra_lat + burst_lat; - - /* FIFO burst */ - - /* Max burst not leading to overflows. */ - max_burst_o = (1 + fifo_len - extra_lat * drain_rate / (1000 * 1000)) - * (fill_rate / 1000) / ((fill_rate - drain_rate) / 1000); - fifo->burst = min(max_burst_o, 1024); - - /* Max burst value with an acceptable latency. */ - max_burst_l = burst_lat * fill_rate / (1000 * 1000); - fifo->burst = min(max_burst_l, fifo->burst); - - fifo->burst = rounddown_pow_of_two(fifo->burst); - - /* FIFO low watermark */ - - min_lwm = (fill_lat + extra_lat) * drain_rate / (1000 * 1000) + 1; - max_lwm = fifo_len - fifo->burst - + fill_lat * drain_rate / (1000 * 1000) - + fifo->burst * drain_rate / fill_rate; - - fifo->lwm = min_lwm + 10 * (max_lwm - min_lwm) / 100; /* Empirical. */ -} - -static void -nv04_update_arb(struct drm_device *dev, int VClk, int bpp, - int *burst, int *lwm) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_device *device = nouveau_dev(dev); - struct nv_fifo_info fifo_data; - struct nv_sim_state sim_data; - int MClk = nouveau_hw_get_clock(dev, PLL_MEMORY); - int NVClk = nouveau_hw_get_clock(dev, PLL_CORE); - uint32_t cfg1 = nv_rd32(device, NV04_PFB_CFG1); - - sim_data.pclk_khz = VClk; - sim_data.mclk_khz = MClk; - sim_data.nvclk_khz = NVClk; - sim_data.bpp = bpp; - sim_data.two_heads = nv_two_heads(dev); - if ((dev->pci_device & 0xffff) == 0x01a0 /*CHIPSET_NFORCE*/ || - (dev->pci_device & 0xffff) == 0x01f0 /*CHIPSET_NFORCE2*/) { - uint32_t type; - - pci_read_config_dword(pci_get_bus_and_slot(0, 1), 0x7c, &type); - - sim_data.memory_type = (type >> 12) & 1; - sim_data.memory_width = 64; - sim_data.mem_latency = 3; - sim_data.mem_page_miss = 10; - } else { - sim_data.memory_type = nv_rd32(device, NV04_PFB_CFG0) & 0x1; - sim_data.memory_width = (nv_rd32(device, NV_PEXTDEV_BOOT_0) & 0x10) ? 128 : 64; - sim_data.mem_latency = cfg1 & 0xf; - sim_data.mem_page_miss = ((cfg1 >> 4) & 0xf) + ((cfg1 >> 31) & 0x1); - } - - if (nv_device(drm->device)->card_type == NV_04) - nv04_calc_arb(&fifo_data, &sim_data); - else - nv10_calc_arb(&fifo_data, &sim_data); - - *burst = ilog2(fifo_data.burst >> 4); - *lwm = fifo_data.lwm >> 3; -} - -static void -nv20_update_arb(int *burst, int *lwm) -{ - unsigned int fifo_size, burst_size, graphics_lwm; - - fifo_size = 2048; - burst_size = 512; - graphics_lwm = fifo_size - burst_size; - - *burst = ilog2(burst_size >> 5); - *lwm = graphics_lwm >> 3; -} - -void -nouveau_calc_arb(struct drm_device *dev, int vclk, int bpp, int *burst, int *lwm) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - - if (nv_device(drm->device)->card_type < NV_20) - nv04_update_arb(dev, vclk, bpp, burst, lwm); - else if ((dev->pci_device & 0xfff0) == 0x0240 /*CHIPSET_C51*/ || - (dev->pci_device & 0xfff0) == 0x03d0 /*CHIPSET_C512*/) { - *burst = 128; - *lwm = 0x0480; - } else - nv20_update_arb(burst, lwm); -} diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 4dd7ae2..4da776f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -32,7 +32,7 @@ #include "nouveau_reg.h" #include "nouveau_drm.h" -#include "nouveau_hw.h" +#include "dispnv04/hw.h" #include "nouveau_acpi.h" #include "nouveau_display.h" diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 4610c3a..7bf22d4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -28,7 +28,7 @@ #include #include "nouveau_fbcon.h" -#include "nouveau_hw.h" +#include "dispnv04/hw.h" #include "nouveau_crtc.h" #include "nouveau_dma.h" #include "nouveau_gem.h" diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index e243412..24660c0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -30,7 +30,7 @@ #include #include -#include "nv04_display.h" +#include "dispnv04/disp.h" #define NV_DPMS_CLEARED 0x80 diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c deleted file mode 100644 index 617a06f..0000000 --- a/drivers/gpu/drm/nouveau/nouveau_hw.c +++ /dev/null @@ -1,827 +0,0 @@ -/* - * Copyright 2006 Dave Airlie - * Copyright 2007 Maarten Maathuis - * Copyright 2007-2009 Stuart Bennett - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF - * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include "nouveau_drm.h" -#include "nouveau_hw.h" - -#include -#include -#include - -#define CHIPSET_NFORCE 0x01a0 -#define CHIPSET_NFORCE2 0x01f0 - -/* - * misc hw access wrappers/control functions - */ - -void -NVWriteVgaSeq(struct drm_device *dev, int head, uint8_t index, uint8_t value) -{ - NVWritePRMVIO(dev, head, NV_PRMVIO_SRX, index); - NVWritePRMVIO(dev, head, NV_PRMVIO_SR, value); -} - -uint8_t -NVReadVgaSeq(struct drm_device *dev, int head, uint8_t index) -{ - NVWritePRMVIO(dev, head, NV_PRMVIO_SRX, index); - return NVReadPRMVIO(dev, head, NV_PRMVIO_SR); -} - -void -NVWriteVgaGr(struct drm_device *dev, int head, uint8_t index, uint8_t value) -{ - NVWritePRMVIO(dev, head, NV_PRMVIO_GRX, index); - NVWritePRMVIO(dev, head, NV_PRMVIO_GX, value); -} - -uint8_t -NVReadVgaGr(struct drm_device *dev, int head, uint8_t index) -{ - NVWritePRMVIO(dev, head, NV_PRMVIO_GRX, index); - return NVReadPRMVIO(dev, head, NV_PRMVIO_GX); -} - -/* CR44 takes values 0 (head A), 3 (head B) and 4 (heads tied) - * it affects only the 8 bit vga io regs, which we access using mmio at - * 0xc{0,2}3c*, 0x60{1,3}3*, and 0x68{1,3}3d* - * in general, the set value of cr44 does not matter: reg access works as - * expected and values can be set for the appropriate head by using a 0x2000 - * offset as required - * however: - * a) pre nv40, the head B range of PRMVIO regs at 0xc23c* was not exposed and - * cr44 must be set to 0 or 3 for accessing values on the correct head - * through the common 0xc03c* addresses - * b) in tied mode (4) head B is programmed to the values set on head A, and - * access using the head B addresses can have strange results, ergo we leave - * tied mode in init once we know to what cr44 should be restored on exit - * - * the owner parameter is slightly abused: - * 0 and 1 are treated as head values and so the set value is (owner * 3) - * other values are treated as literal values to set - */ -void -NVSetOwner(struct drm_device *dev, int owner) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - - if (owner == 1) - owner *= 3; - - if (nv_device(drm->device)->chipset == 0x11) { - /* This might seem stupid, but the blob does it and - * omitting it often locks the system up. - */ - NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX); - NVReadVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX); - } - - /* CR44 is always changed on CRTC0 */ - NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_44, owner); - - if (nv_device(drm->device)->chipset == 0x11) { /* set me harder */ - NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner); - NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner); - } -} - -void -NVBlankScreen(struct drm_device *dev, int head, bool blank) -{ - unsigned char seq1; - - if (nv_two_heads(dev)) - NVSetOwner(dev, head); - - seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX); - - NVVgaSeqReset(dev, head, true); - if (blank) - NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20); - else - NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20); - NVVgaSeqReset(dev, head, false); -} - -/* - * PLL getting - */ - -static void -nouveau_hw_decode_pll(struct drm_device *dev, uint32_t reg1, uint32_t pll1, - uint32_t pll2, struct nouveau_pll_vals *pllvals) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - - /* to force parsing as single stage (i.e. nv40 vplls) pass pll2 as 0 */ - - /* log2P is & 0x7 as never more than 7, and nv30/35 only uses 3 bits */ - pllvals->log2P = (pll1 >> 16) & 0x7; - pllvals->N2 = pllvals->M2 = 1; - - if (reg1 <= 0x405c) { - pllvals->NM1 = pll2 & 0xffff; - /* single stage NVPLL and VPLLs use 1 << 8, MPLL uses 1 << 12 */ - if (!(pll1 & 0x1100)) - pllvals->NM2 = pll2 >> 16; - } else { - pllvals->NM1 = pll1 & 0xffff; - if (nv_two_reg_pll(dev) && pll2 & NV31_RAMDAC_ENABLE_VCO2) - pllvals->NM2 = pll2 & 0xffff; - else if (nv_device(drm->device)->chipset == 0x30 || nv_device(drm->device)->chipset == 0x35) { - pllvals->M1 &= 0xf; /* only 4 bits */ - if (pll1 & NV30_RAMDAC_ENABLE_VCO2) { - pllvals->M2 = (pll1 >> 4) & 0x7; - pllvals->N2 = ((pll1 >> 21) & 0x18) | - ((pll1 >> 19) & 0x7); - } - } - } -} - -int -nouveau_hw_get_pllvals(struct drm_device *dev, enum nvbios_pll_type plltype, - struct nouveau_pll_vals *pllvals) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_device *device = nv_device(drm->device); - struct nouveau_bios *bios = nouveau_bios(device); - uint32_t reg1, pll1, pll2 = 0; - struct nvbios_pll pll_lim; - int ret; - - ret = nvbios_pll_parse(bios, plltype, &pll_lim); - if (ret || !(reg1 = pll_lim.reg)) - return -ENOENT; - - pll1 = nv_rd32(device, reg1); - if (reg1 <= 0x405c) - pll2 = nv_rd32(device, reg1 + 4); - else if (nv_two_reg_pll(dev)) { - uint32_t reg2 = reg1 + (reg1 == NV_RAMDAC_VPLL2 ? 0x5c : 0x70); - - pll2 = nv_rd32(device, reg2); - } - - if (nv_device(drm->device)->card_type == 0x40 && reg1 >= NV_PRAMDAC_VPLL_COEFF) { - uint32_t ramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580); - - /* check whether vpll has been forced into single stage mode */ - if (reg1 == NV_PRAMDAC_VPLL_COEFF) { - if (ramdac580 & NV_RAMDAC_580_VPLL1_ACTIVE) - pll2 = 0; - } else - if (ramdac580 & NV_RAMDAC_580_VPLL2_ACTIVE) - pll2 = 0; - } - - nouveau_hw_decode_pll(dev, reg1, pll1, pll2, pllvals); - pllvals->refclk = pll_lim.refclk; - return 0; -} - -int -nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pv) -{ - /* Avoid divide by zero if called at an inappropriate time */ - if (!pv->M1 || !pv->M2) - return 0; - - return pv->N1 * pv->N2 * pv->refclk / (pv->M1 * pv->M2) >> pv->log2P; -} - -int -nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype) -{ - struct nouveau_pll_vals pllvals; - int ret; - - if (plltype == PLL_MEMORY && - (dev->pci_device & 0x0ff0) == CHIPSET_NFORCE) { - uint32_t mpllP; - - pci_read_config_dword(pci_get_bus_and_slot(0, 3), 0x6c, &mpllP); - if (!mpllP) - mpllP = 4; - - return 400000 / mpllP; - } else - if (plltype == PLL_MEMORY && - (dev->pci_device & 0xff0) == CHIPSET_NFORCE2) { - uint32_t clock; - - pci_read_config_dword(pci_get_bus_and_slot(0, 5), 0x4c, &clock); - return clock; - } - - ret = nouveau_hw_get_pllvals(dev, plltype, &pllvals); - if (ret) - return ret; - - return nouveau_hw_pllvals_to_clk(&pllvals); -} - -static void -nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head) -{ - /* the vpll on an unused head can come up with a random value, way - * beyond the pll limits. for some reason this causes the chip to - * lock up when reading the dac palette regs, so set a valid pll here - * when such a condition detected. only seen on nv11 to date - */ - - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_device *device = nv_device(drm->device); - struct nouveau_clock *clk = nouveau_clock(device); - struct nouveau_bios *bios = nouveau_bios(device); - struct nvbios_pll pll_lim; - struct nouveau_pll_vals pv; - enum nvbios_pll_type pll = head ? PLL_VPLL1 : PLL_VPLL0; - - if (nvbios_pll_parse(bios, pll, &pll_lim)) - return; - nouveau_hw_get_pllvals(dev, pll, &pv); - - if (pv.M1 >= pll_lim.vco1.min_m && pv.M1 <= pll_lim.vco1.max_m && - pv.N1 >= pll_lim.vco1.min_n && pv.N1 <= pll_lim.vco1.max_n && - pv.log2P <= pll_lim.max_p) - return; - - NV_WARN(drm, "VPLL %d outwith limits, attempting to fix\n", head + 1); - - /* set lowest clock within static limits */ - pv.M1 = pll_lim.vco1.max_m; - pv.N1 = pll_lim.vco1.min_n; - pv.log2P = pll_lim.max_p_usable; - clk->pll_prog(clk, pll_lim.reg, &pv); -} - -/* - * vga font save/restore - */ - -static void nouveau_vga_font_io(struct drm_device *dev, - void __iomem *iovram, - bool save, unsigned plane) -{ - unsigned i; - - NVWriteVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX, 1 << plane); - NVWriteVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX, plane); - for (i = 0; i < 16384; i++) { - if (save) { - nv04_display(dev)->saved_vga_font[plane][i] = - ioread32_native(iovram + i * 4); - } else { - iowrite32_native(nv04_display(dev)->saved_vga_font[plane][i], - iovram + i * 4); - } - } -} - -void -nouveau_hw_save_vga_fonts(struct drm_device *dev, bool save) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - uint8_t misc, gr4, gr5, gr6, seq2, seq4; - bool graphicsmode; - unsigned plane; - void __iomem *iovram; - - if (nv_two_heads(dev)) - NVSetOwner(dev, 0); - - NVSetEnablePalette(dev, 0, true); - graphicsmode = NVReadVgaAttr(dev, 0, NV_CIO_AR_MODE_INDEX) & 1; - NVSetEnablePalette(dev, 0, false); - - if (graphicsmode) /* graphics mode => framebuffer => no need to save */ - return; - - NV_INFO(drm, "%sing VGA fonts\n", save ? "Sav" : "Restor"); - - /* map first 64KiB of VRAM, holds VGA fonts etc */ - iovram = ioremap(pci_resource_start(dev->pdev, 1), 65536); - if (!iovram) { - NV_ERROR(drm, "Failed to map VRAM, " - "cannot save/restore VGA fonts.\n"); - return; - } - - if (nv_two_heads(dev)) - NVBlankScreen(dev, 1, true); - NVBlankScreen(dev, 0, true); - - /* save control regs */ - misc = NVReadPRMVIO(dev, 0, NV_PRMVIO_MISC__READ); - seq2 = NVReadVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX); - seq4 = NVReadVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX); - gr4 = NVReadVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX); - gr5 = NVReadVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX); - gr6 = NVReadVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX); - - NVWritePRMVIO(dev, 0, NV_PRMVIO_MISC__WRITE, 0x67); - NVWriteVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX, 0x6); - NVWriteVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX, 0x0); - NVWriteVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX, 0x5); - - /* store font in planes 0..3 */ - for (plane = 0; plane < 4; plane++) - nouveau_vga_font_io(dev, iovram, save, plane); - - /* restore control regs */ - NVWritePRMVIO(dev, 0, NV_PRMVIO_MISC__WRITE, misc); - NVWriteVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX, gr4); - NVWriteVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX, gr5); - NVWriteVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX, gr6); - NVWriteVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX, seq2); - NVWriteVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX, seq4); - - if (nv_two_heads(dev)) - NVBlankScreen(dev, 1, false); - NVBlankScreen(dev, 0, false); - - iounmap(iovram); -} - -/* - * mode state save/load - */ - -static void -rd_cio_state(struct drm_device *dev, int head, - struct nv04_crtc_reg *crtcstate, int index) -{ - crtcstate->CRTC[index] = NVReadVgaCrtc(dev, head, index); -} - -static void -wr_cio_state(struct drm_device *dev, int head, - struct nv04_crtc_reg *crtcstate, int index) -{ - NVWriteVgaCrtc(dev, head, index, crtcstate->CRTC[index]); -} - -static void -nv_save_state_ramdac(struct drm_device *dev, int head, - struct nv04_mode_state *state) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct nv04_crtc_reg *regp = &state->crtc_reg[head]; - int i; - - if (nv_device(drm->device)->card_type >= NV_10) - regp->nv10_cursync = NVReadRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC); - - nouveau_hw_get_pllvals(dev, head ? PLL_VPLL1 : PLL_VPLL0, ®p->pllvals); - state->pllsel = NVReadRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT); - if (nv_two_heads(dev)) - state->sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK); - if (nv_device(drm->device)->chipset == 0x11) - regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11); - - regp->ramdac_gen_ctrl = NVReadRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL); - - if (nv_gf4_disp_arch(dev)) - regp->ramdac_630 = NVReadRAMDAC(dev, head, NV_PRAMDAC_630); - if (nv_device(drm->device)->chipset >= 0x30) - regp->ramdac_634 = NVReadRAMDAC(dev, head, NV_PRAMDAC_634); - - regp->tv_setup = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP); - regp->tv_vtotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL); - regp->tv_vskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW); - regp->tv_vsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY); - regp->tv_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL); - regp->tv_hskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW); - regp->tv_hsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY); - regp->tv_hsync_delay2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2); - - for (i = 0; i < 7; i++) { - uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4); - regp->fp_vert_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg); - regp->fp_horiz_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg + 0x20); - } - - if (nv_gf4_disp_arch(dev)) { - regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_FP_DITHER); - for (i = 0; i < 3; i++) { - regp->dither_regs[i] = NVReadRAMDAC(dev, head, NV_PRAMDAC_850 + i * 4); - regp->dither_regs[i + 3] = NVReadRAMDAC(dev, head, NV_PRAMDAC_85C + i * 4); - } - } - - regp->fp_control = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL); - regp->fp_debug_0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_0); - if (!nv_gf4_disp_arch(dev) && head == 0) { - /* early chips don't allow access to PRAMDAC_TMDS_* without - * the head A FPCLK on (nv11 even locks up) */ - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_FP_DEBUG_0, regp->fp_debug_0 & - ~NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK); - } - regp->fp_debug_1 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1); - regp->fp_debug_2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2); - - regp->fp_margin_color = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR); - - if (nv_gf4_disp_arch(dev)) - regp->ramdac_8c0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_8C0); - - if (nv_device(drm->device)->card_type == NV_40) { - regp->ramdac_a20 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A20); - regp->ramdac_a24 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A24); - regp->ramdac_a34 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A34); - - for (i = 0; i < 38; i++) - regp->ctv_regs[i] = NVReadRAMDAC(dev, head, - NV_PRAMDAC_CTV + 4*i); - } -} - -static void -nv_load_state_ramdac(struct drm_device *dev, int head, - struct nv04_mode_state *state) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_clock *clk = nouveau_clock(drm->device); - struct nv04_crtc_reg *regp = &state->crtc_reg[head]; - uint32_t pllreg = head ? NV_RAMDAC_VPLL2 : NV_PRAMDAC_VPLL_COEFF; - int i; - - if (nv_device(drm->device)->card_type >= NV_10) - NVWriteRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC, regp->nv10_cursync); - - clk->pll_prog(clk, pllreg, ®p->pllvals); - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel); - if (nv_two_heads(dev)) - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, state->sel_clk); - if (nv_device(drm->device)->chipset == 0x11) - NVWriteRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11, regp->dither); - - NVWriteRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL, regp->ramdac_gen_ctrl); - - if (nv_gf4_disp_arch(dev)) - NVWriteRAMDAC(dev, head, NV_PRAMDAC_630, regp->ramdac_630); - if (nv_device(drm->device)->chipset >= 0x30) - NVWriteRAMDAC(dev, head, NV_PRAMDAC_634, regp->ramdac_634); - - NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, regp->tv_setup); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL, regp->tv_vtotal); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW, regp->tv_vskew); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY, regp->tv_vsync_delay); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL, regp->tv_htotal); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW, regp->tv_hskew); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY, regp->tv_hsync_delay); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2, regp->tv_hsync_delay2); - - for (i = 0; i < 7; i++) { - uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4); - - NVWriteRAMDAC(dev, head, ramdac_reg, regp->fp_vert_regs[i]); - NVWriteRAMDAC(dev, head, ramdac_reg + 0x20, regp->fp_horiz_regs[i]); - } - - if (nv_gf4_disp_arch(dev)) { - NVWriteRAMDAC(dev, head, NV_RAMDAC_FP_DITHER, regp->dither); - for (i = 0; i < 3; i++) { - NVWriteRAMDAC(dev, head, NV_PRAMDAC_850 + i * 4, regp->dither_regs[i]); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_85C + i * 4, regp->dither_regs[i + 3]); - } - } - - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, regp->fp_control); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_0, regp->fp_debug_0); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regp->fp_debug_1); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2, regp->fp_debug_2); - - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR, regp->fp_margin_color); - - if (nv_gf4_disp_arch(dev)) - NVWriteRAMDAC(dev, head, NV_PRAMDAC_8C0, regp->ramdac_8c0); - - if (nv_device(drm->device)->card_type == NV_40) { - NVWriteRAMDAC(dev, head, NV_PRAMDAC_A20, regp->ramdac_a20); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_A24, regp->ramdac_a24); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_A34, regp->ramdac_a34); - - for (i = 0; i < 38; i++) - NVWriteRAMDAC(dev, head, - NV_PRAMDAC_CTV + 4*i, regp->ctv_regs[i]); - } -} - -static void -nv_save_state_vga(struct drm_device *dev, int head, - struct nv04_mode_state *state) -{ - struct nv04_crtc_reg *regp = &state->crtc_reg[head]; - int i; - - regp->MiscOutReg = NVReadPRMVIO(dev, head, NV_PRMVIO_MISC__READ); - - for (i = 0; i < 25; i++) - rd_cio_state(dev, head, regp, i); - - NVSetEnablePalette(dev, head, true); - for (i = 0; i < 21; i++) - regp->Attribute[i] = NVReadVgaAttr(dev, head, i); - NVSetEnablePalette(dev, head, false); - - for (i = 0; i < 9; i++) - regp->Graphics[i] = NVReadVgaGr(dev, head, i); - - for (i = 0; i < 5; i++) - regp->Sequencer[i] = NVReadVgaSeq(dev, head, i); -} - -static void -nv_load_state_vga(struct drm_device *dev, int head, - struct nv04_mode_state *state) -{ - struct nv04_crtc_reg *regp = &state->crtc_reg[head]; - int i; - - NVWritePRMVIO(dev, head, NV_PRMVIO_MISC__WRITE, regp->MiscOutReg); - - for (i = 0; i < 5; i++) - NVWriteVgaSeq(dev, head, i, regp->Sequencer[i]); - - nv_lock_vga_crtc_base(dev, head, false); - for (i = 0; i < 25; i++) - wr_cio_state(dev, head, regp, i); - nv_lock_vga_crtc_base(dev, head, true); - - for (i = 0; i < 9; i++) - NVWriteVgaGr(dev, head, i, regp->Graphics[i]); - - NVSetEnablePalette(dev, head, true); - for (i = 0; i < 21; i++) - NVWriteVgaAttr(dev, head, i, regp->Attribute[i]); - NVSetEnablePalette(dev, head, false); -} - -static void -nv_save_state_ext(struct drm_device *dev, int head, - struct nv04_mode_state *state) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct nv04_crtc_reg *regp = &state->crtc_reg[head]; - int i; - - rd_cio_state(dev, head, regp, NV_CIO_CRE_LCD__INDEX); - rd_cio_state(dev, head, regp, NV_CIO_CRE_RPC0_INDEX); - rd_cio_state(dev, head, regp, NV_CIO_CRE_RPC1_INDEX); - rd_cio_state(dev, head, regp, NV_CIO_CRE_LSR_INDEX); - rd_cio_state(dev, head, regp, NV_CIO_CRE_PIXEL_INDEX); - rd_cio_state(dev, head, regp, NV_CIO_CRE_HEB__INDEX); - rd_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX); - - rd_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX); - rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX); - rd_cio_state(dev, head, regp, NV_CIO_CRE_21); - - if (nv_device(drm->device)->card_type >= NV_20) - rd_cio_state(dev, head, regp, NV_CIO_CRE_47); - - if (nv_device(drm->device)->card_type >= NV_30) - rd_cio_state(dev, head, regp, 0x9f); - - rd_cio_state(dev, head, regp, NV_CIO_CRE_49); - rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); - rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX); - rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX); - rd_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX); - - if (nv_device(drm->device)->card_type >= NV_10) { - regp->crtc_830 = NVReadCRTC(dev, head, NV_PCRTC_830); - regp->crtc_834 = NVReadCRTC(dev, head, NV_PCRTC_834); - - if (nv_device(drm->device)->card_type >= NV_30) - regp->gpio_ext = NVReadCRTC(dev, head, NV_PCRTC_GPIO_EXT); - - if (nv_device(drm->device)->card_type == NV_40) - regp->crtc_850 = NVReadCRTC(dev, head, NV_PCRTC_850); - - if (nv_two_heads(dev)) - regp->crtc_eng_ctrl = NVReadCRTC(dev, head, NV_PCRTC_ENGINE_CTRL); - regp->cursor_cfg = NVReadCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG); - } - - regp->crtc_cfg = NVReadCRTC(dev, head, NV_PCRTC_CONFIG); - - rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX); - rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX); - if (nv_device(drm->device)->card_type >= NV_10) { - rd_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX); - rd_cio_state(dev, head, regp, NV_CIO_CRE_CSB); - rd_cio_state(dev, head, regp, NV_CIO_CRE_4B); - rd_cio_state(dev, head, regp, NV_CIO_CRE_TVOUT_LATENCY); - } - /* NV11 and NV20 don't have this, they stop at 0x52. */ - if (nv_gf4_disp_arch(dev)) { - rd_cio_state(dev, head, regp, NV_CIO_CRE_42); - rd_cio_state(dev, head, regp, NV_CIO_CRE_53); - rd_cio_state(dev, head, regp, NV_CIO_CRE_54); - - for (i = 0; i < 0x10; i++) - regp->CR58[i] = NVReadVgaCrtc5758(dev, head, i); - rd_cio_state(dev, head, regp, NV_CIO_CRE_59); - rd_cio_state(dev, head, regp, NV_CIO_CRE_5B); - - rd_cio_state(dev, head, regp, NV_CIO_CRE_85); - rd_cio_state(dev, head, regp, NV_CIO_CRE_86); - } - - regp->fb_start = NVReadCRTC(dev, head, NV_PCRTC_START); -} - -static void -nv_load_state_ext(struct drm_device *dev, int head, - struct nv04_mode_state *state) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_device *device = nv_device(drm->device); - struct nouveau_timer *ptimer = nouveau_timer(device); - struct nv04_crtc_reg *regp = &state->crtc_reg[head]; - uint32_t reg900; - int i; - - if (nv_device(drm->device)->card_type >= NV_10) { - if (nv_two_heads(dev)) - /* setting ENGINE_CTRL (EC) *must* come before - * CIO_CRE_LCD, as writing CRE_LCD sets bits 16 & 17 in - * EC that should not be overwritten by writing stale EC - */ - NVWriteCRTC(dev, head, NV_PCRTC_ENGINE_CTRL, regp->crtc_eng_ctrl); - - nv_wr32(device, NV_PVIDEO_STOP, 1); - nv_wr32(device, NV_PVIDEO_INTR_EN, 0); - nv_wr32(device, NV_PVIDEO_OFFSET_BUFF(0), 0); - nv_wr32(device, NV_PVIDEO_OFFSET_BUFF(1), 0); - nv_wr32(device, NV_PVIDEO_LIMIT(0), 0); //drm->fb_available_size - 1); - nv_wr32(device, NV_PVIDEO_LIMIT(1), 0); //drm->fb_available_size - 1); - nv_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(0), 0); //drm->fb_available_size - 1); - nv_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(1), 0); //drm->fb_available_size - 1); - nv_wr32(device, NV_PBUS_POWERCTRL_2, 0); - - NVWriteCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG, regp->cursor_cfg); - NVWriteCRTC(dev, head, NV_PCRTC_830, regp->crtc_830); - NVWriteCRTC(dev, head, NV_PCRTC_834, regp->crtc_834); - - if (nv_device(drm->device)->card_type >= NV_30) - NVWriteCRTC(dev, head, NV_PCRTC_GPIO_EXT, regp->gpio_ext); - - if (nv_device(drm->device)->card_type == NV_40) { - NVWriteCRTC(dev, head, NV_PCRTC_850, regp->crtc_850); - - reg900 = NVReadRAMDAC(dev, head, NV_PRAMDAC_900); - if (regp->crtc_cfg == NV10_PCRTC_CONFIG_START_ADDRESS_HSYNC) - NVWriteRAMDAC(dev, head, NV_PRAMDAC_900, reg900 | 0x10000); - else - NVWriteRAMDAC(dev, head, NV_PRAMDAC_900, reg900 & ~0x10000); - } - } - - NVWriteCRTC(dev, head, NV_PCRTC_CONFIG, regp->crtc_cfg); - - wr_cio_state(dev, head, regp, NV_CIO_CRE_RPC0_INDEX); - wr_cio_state(dev, head, regp, NV_CIO_CRE_RPC1_INDEX); - wr_cio_state(dev, head, regp, NV_CIO_CRE_LSR_INDEX); - wr_cio_state(dev, head, regp, NV_CIO_CRE_PIXEL_INDEX); - wr_cio_state(dev, head, regp, NV_CIO_CRE_LCD__INDEX); - wr_cio_state(dev, head, regp, NV_CIO_CRE_HEB__INDEX); - wr_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX); - wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX); - wr_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX); - - if (nv_device(drm->device)->card_type >= NV_20) - wr_cio_state(dev, head, regp, NV_CIO_CRE_47); - - if (nv_device(drm->device)->card_type >= NV_30) - wr_cio_state(dev, head, regp, 0x9f); - - wr_cio_state(dev, head, regp, NV_CIO_CRE_49); - wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); - wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX); - wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX); - if (nv_device(drm->device)->card_type == NV_40) - nv_fix_nv40_hw_cursor(dev, head); - wr_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX); - - wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX); - wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX); - if (nv_device(drm->device)->card_type >= NV_10) { - wr_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX); - wr_cio_state(dev, head, regp, NV_CIO_CRE_CSB); - wr_cio_state(dev, head, regp, NV_CIO_CRE_4B); - wr_cio_state(dev, head, regp, NV_CIO_CRE_TVOUT_LATENCY); - } - /* NV11 and NV20 stop at 0x52. */ - if (nv_gf4_disp_arch(dev)) { - if (nv_device(drm->device)->card_type == NV_10) { - /* Not waiting for vertical retrace before modifying - CRE_53/CRE_54 causes lockups. */ - nouveau_timer_wait_eq(ptimer, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x8); - nouveau_timer_wait_eq(ptimer, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x0); - } - - wr_cio_state(dev, head, regp, NV_CIO_CRE_42); - wr_cio_state(dev, head, regp, NV_CIO_CRE_53); - wr_cio_state(dev, head, regp, NV_CIO_CRE_54); - - for (i = 0; i < 0x10; i++) - NVWriteVgaCrtc5758(dev, head, i, regp->CR58[i]); - wr_cio_state(dev, head, regp, NV_CIO_CRE_59); - wr_cio_state(dev, head, regp, NV_CIO_CRE_5B); - - wr_cio_state(dev, head, regp, NV_CIO_CRE_85); - wr_cio_state(dev, head, regp, NV_CIO_CRE_86); - } - - NVWriteCRTC(dev, head, NV_PCRTC_START, regp->fb_start); -} - -static void -nv_save_state_palette(struct drm_device *dev, int head, - struct nv04_mode_state *state) -{ - struct nouveau_device *device = nouveau_dev(dev); - int head_offset = head * NV_PRMDIO_SIZE, i; - - nv_wr08(device, NV_PRMDIO_PIXEL_MASK + head_offset, - NV_PRMDIO_PIXEL_MASK_MASK); - nv_wr08(device, NV_PRMDIO_READ_MODE_ADDRESS + head_offset, 0x0); - - for (i = 0; i < 768; i++) { - state->crtc_reg[head].DAC[i] = nv_rd08(device, - NV_PRMDIO_PALETTE_DATA + head_offset); - } - - NVSetEnablePalette(dev, head, false); -} - -void -nouveau_hw_load_state_palette(struct drm_device *dev, int head, - struct nv04_mode_state *state) -{ - struct nouveau_device *device = nouveau_dev(dev); - int head_offset = head * NV_PRMDIO_SIZE, i; - - nv_wr08(device, NV_PRMDIO_PIXEL_MASK + head_offset, - NV_PRMDIO_PIXEL_MASK_MASK); - nv_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS + head_offset, 0x0); - - for (i = 0; i < 768; i++) { - nv_wr08(device, NV_PRMDIO_PALETTE_DATA + head_offset, - state->crtc_reg[head].DAC[i]); - } - - NVSetEnablePalette(dev, head, false); -} - -void nouveau_hw_save_state(struct drm_device *dev, int head, - struct nv04_mode_state *state) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - - if (nv_device(drm->device)->chipset == 0x11) - /* NB: no attempt is made to restore the bad pll later on */ - nouveau_hw_fix_bad_vpll(dev, head); - nv_save_state_ramdac(dev, head, state); - nv_save_state_vga(dev, head, state); - nv_save_state_palette(dev, head, state); - nv_save_state_ext(dev, head, state); -} - -void nouveau_hw_load_state(struct drm_device *dev, int head, - struct nv04_mode_state *state) -{ - NVVgaProtect(dev, head, true); - nv_load_state_ramdac(dev, head, state); - nv_load_state_ext(dev, head, state); - nouveau_hw_load_state_palette(dev, head, state); - nv_load_state_vga(dev, head, state); - NVVgaProtect(dev, head, false); -} diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.h b/drivers/gpu/drm/nouveau/nouveau_hw.h deleted file mode 100644 index 7dff102..0000000 --- a/drivers/gpu/drm/nouveau/nouveau_hw.h +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright 2008 Stuart Bennett - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF - * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef __NOUVEAU_HW_H__ -#define __NOUVEAU_HW_H__ - -#include -#include "nv04_display.h" - -#include - -#define MASK(field) ( \ - (0xffffffff >> (31 - ((1 ? field) - (0 ? field)))) << (0 ? field)) - -#define XLATE(src, srclowbit, outfield) ( \ - (((src) >> (srclowbit)) << (0 ? outfield)) & MASK(outfield)) - -void NVWriteVgaSeq(struct drm_device *, int head, uint8_t index, uint8_t value); -uint8_t NVReadVgaSeq(struct drm_device *, int head, uint8_t index); -void NVWriteVgaGr(struct drm_device *, int head, uint8_t index, uint8_t value); -uint8_t NVReadVgaGr(struct drm_device *, int head, uint8_t index); -void NVSetOwner(struct drm_device *, int owner); -void NVBlankScreen(struct drm_device *, int head, bool blank); -int nouveau_hw_get_pllvals(struct drm_device *, enum nvbios_pll_type plltype, - struct nouveau_pll_vals *pllvals); -int nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pllvals); -int nouveau_hw_get_clock(struct drm_device *, enum nvbios_pll_type plltype); -void nouveau_hw_save_vga_fonts(struct drm_device *, bool save); -void nouveau_hw_save_state(struct drm_device *, int head, - struct nv04_mode_state *state); -void nouveau_hw_load_state(struct drm_device *, int head, - struct nv04_mode_state *state); -void nouveau_hw_load_state_palette(struct drm_device *, int head, - struct nv04_mode_state *state); - -/* nouveau_calc.c */ -extern void nouveau_calc_arb(struct drm_device *, int vclk, int bpp, - int *burst, int *lwm); - -static inline uint32_t NVReadCRTC(struct drm_device *dev, - int head, uint32_t reg) -{ - struct nouveau_device *device = nouveau_dev(dev); - uint32_t val; - if (head) - reg += NV_PCRTC0_SIZE; - val = nv_rd32(device, reg); - return val; -} - -static inline void NVWriteCRTC(struct drm_device *dev, - int head, uint32_t reg, uint32_t val) -{ - struct nouveau_device *device = nouveau_dev(dev); - if (head) - reg += NV_PCRTC0_SIZE; - nv_wr32(device, reg, val); -} - -static inline uint32_t NVReadRAMDAC(struct drm_device *dev, - int head, uint32_t reg) -{ - struct nouveau_device *device = nouveau_dev(dev); - uint32_t val; - if (head) - reg += NV_PRAMDAC0_SIZE; - val = nv_rd32(device, reg); - return val; -} - -static inline void NVWriteRAMDAC(struct drm_device *dev, - int head, uint32_t reg, uint32_t val) -{ - struct nouveau_device *device = nouveau_dev(dev); - if (head) - reg += NV_PRAMDAC0_SIZE; - nv_wr32(device, reg, val); -} - -static inline uint8_t nv_read_tmds(struct drm_device *dev, - int or, int dl, uint8_t address) -{ - int ramdac = (or & DCB_OUTPUT_C) >> 2; - - NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8, - NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | address); - return NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8); -} - -static inline void nv_write_tmds(struct drm_device *dev, - int or, int dl, uint8_t address, - uint8_t data) -{ - int ramdac = (or & DCB_OUTPUT_C) >> 2; - - NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8, data); - NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8, address); -} - -static inline void NVWriteVgaCrtc(struct drm_device *dev, - int head, uint8_t index, uint8_t value) -{ - struct nouveau_device *device = nouveau_dev(dev); - nv_wr08(device, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index); - nv_wr08(device, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE, value); -} - -static inline uint8_t NVReadVgaCrtc(struct drm_device *dev, - int head, uint8_t index) -{ - struct nouveau_device *device = nouveau_dev(dev); - uint8_t val; - nv_wr08(device, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index); - val = nv_rd08(device, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE); - return val; -} - -/* CR57 and CR58 are a fun pair of regs. CR57 provides an index (0-0xf) for CR58 - * I suspect they in fact do nothing, but are merely a way to carry useful - * per-head variables around - * - * Known uses: - * CR57 CR58 - * 0x00 index to the appropriate dcb entry (or 7f for inactive) - * 0x02 dcb entry's "or" value (or 00 for inactive) - * 0x03 bit0 set for dual link (LVDS, possibly elsewhere too) - * 0x08 or 0x09 pxclk in MHz - * 0x0f laptop panel info - low nibble for PEXTDEV_BOOT_0 strap - * high nibble for xlat strap value - */ - -static inline void -NVWriteVgaCrtc5758(struct drm_device *dev, int head, uint8_t index, uint8_t value) -{ - NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index); - NVWriteVgaCrtc(dev, head, NV_CIO_CRE_58, value); -} - -static inline uint8_t NVReadVgaCrtc5758(struct drm_device *dev, int head, uint8_t index) -{ - NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index); - return NVReadVgaCrtc(dev, head, NV_CIO_CRE_58); -} - -static inline uint8_t NVReadPRMVIO(struct drm_device *dev, - int head, uint32_t reg) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - uint8_t val; - - /* Only NV4x have two pvio ranges; other twoHeads cards MUST call - * NVSetOwner for the relevant head to be programmed */ - if (head && nv_device(drm->device)->card_type == NV_40) - reg += NV_PRMVIO_SIZE; - - val = nv_rd08(device, reg); - return val; -} - -static inline void NVWritePRMVIO(struct drm_device *dev, - int head, uint32_t reg, uint8_t value) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - - /* Only NV4x have two pvio ranges; other twoHeads cards MUST call - * NVSetOwner for the relevant head to be programmed */ - if (head && nv_device(drm->device)->card_type == NV_40) - reg += NV_PRMVIO_SIZE; - - nv_wr08(device, reg, value); -} - -static inline void NVSetEnablePalette(struct drm_device *dev, int head, bool enable) -{ - struct nouveau_device *device = nouveau_dev(dev); - nv_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); - nv_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, enable ? 0 : 0x20); -} - -static inline bool NVGetEnablePalette(struct drm_device *dev, int head) -{ - struct nouveau_device *device = nouveau_dev(dev); - nv_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); - return !(nv_rd08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE) & 0x20); -} - -static inline void NVWriteVgaAttr(struct drm_device *dev, - int head, uint8_t index, uint8_t value) -{ - struct nouveau_device *device = nouveau_dev(dev); - if (NVGetEnablePalette(dev, head)) - index &= ~0x20; - else - index |= 0x20; - - nv_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); - nv_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index); - nv_wr08(device, NV_PRMCIO_AR__WRITE + head * NV_PRMCIO_SIZE, value); -} - -static inline uint8_t NVReadVgaAttr(struct drm_device *dev, - int head, uint8_t index) -{ - struct nouveau_device *device = nouveau_dev(dev); - uint8_t val; - if (NVGetEnablePalette(dev, head)) - index &= ~0x20; - else - index |= 0x20; - - nv_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); - nv_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index); - val = nv_rd08(device, NV_PRMCIO_AR__READ + head * NV_PRMCIO_SIZE); - return val; -} - -static inline void NVVgaSeqReset(struct drm_device *dev, int head, bool start) -{ - NVWriteVgaSeq(dev, head, NV_VIO_SR_RESET_INDEX, start ? 0x1 : 0x3); -} - -static inline void NVVgaProtect(struct drm_device *dev, int head, bool protect) -{ - uint8_t seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX); - - if (protect) { - NVVgaSeqReset(dev, head, true); - NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20); - } else { - /* Reenable sequencer, then turn on screen */ - NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20); /* reenable display */ - NVVgaSeqReset(dev, head, false); - } - NVSetEnablePalette(dev, head, protect); -} - -static inline bool -nv_heads_tied(struct drm_device *dev) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - - if (nv_device(drm->device)->chipset == 0x11) - return !!(nv_rd32(device, NV_PBUS_DEBUG_1) & (1 << 28)); - - return NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44) & 0x4; -} - -/* makes cr0-7 on the specified head read-only */ -static inline bool -nv_lock_vga_crtc_base(struct drm_device *dev, int head, bool lock) -{ - uint8_t cr11 = NVReadVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX); - bool waslocked = cr11 & 0x80; - - if (lock) - cr11 |= 0x80; - else - cr11 &= ~0x80; - NVWriteVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX, cr11); - - return waslocked; -} - -static inline void -nv_lock_vga_crtc_shadow(struct drm_device *dev, int head, int lock) -{ - /* shadow lock: connects 0x60?3d? regs to "real" 0x3d? regs - * bit7: unlocks HDT, HBS, HBE, HRS, HRE, HEB - * bit6: seems to have some effect on CR09 (double scan, VBS_9) - * bit5: unlocks HDE - * bit4: unlocks VDE - * bit3: unlocks VDT, OVL, VRS, ?VRE?, VBS, VBE, LSR, EBR - * bit2: same as bit 1 of 0x60?804 - * bit0: same as bit 0 of 0x60?804 - */ - - uint8_t cr21 = lock; - - if (lock < 0) - /* 0xfa is generic "unlock all" mask */ - cr21 = NVReadVgaCrtc(dev, head, NV_CIO_CRE_21) | 0xfa; - - NVWriteVgaCrtc(dev, head, NV_CIO_CRE_21, cr21); -} - -/* renders the extended crtc regs (cr19+) on all crtcs impervious: - * immutable and unreadable - */ -static inline bool -NVLockVgaCrtcs(struct drm_device *dev, bool lock) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - bool waslocked = !NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX); - - NVWriteVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX, - lock ? NV_CIO_SR_LOCK_VALUE : NV_CIO_SR_UNLOCK_RW_VALUE); - /* NV11 has independently lockable extended crtcs, except when tied */ - if (nv_device(drm->device)->chipset == 0x11 && !nv_heads_tied(dev)) - NVWriteVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX, - lock ? NV_CIO_SR_LOCK_VALUE : - NV_CIO_SR_UNLOCK_RW_VALUE); - - return waslocked; -} - -/* nv04 cursor max dimensions of 32x32 (A1R5G5B5) */ -#define NV04_CURSOR_SIZE 32 -/* limit nv10 cursors to 64x64 (ARGB8) (we could go to 64x255) */ -#define NV10_CURSOR_SIZE 64 - -static inline int nv_cursor_width(struct drm_device *dev) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - - return nv_device(drm->device)->card_type >= NV_10 ? NV10_CURSOR_SIZE : NV04_CURSOR_SIZE; -} - -static inline void -nv_fix_nv40_hw_cursor(struct drm_device *dev, int head) -{ - /* on some nv40 (such as the "true" (in the NV_PFB_BOOT_0 sense) nv40, - * the gf6800gt) a hardware bug requires a write to PRAMDAC_CURSOR_POS - * for changes to the CRTC CURCTL regs to take effect, whether changing - * the pixmap location, or just showing/hiding the cursor - */ - uint32_t curpos = NVReadRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS, curpos); -} - -static inline void -nv_set_crtc_base(struct drm_device *dev, int head, uint32_t offset) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - - NVWriteCRTC(dev, head, NV_PCRTC_START, offset); - - if (nv_device(drm->device)->card_type == NV_04) { - /* - * Hilarious, the 24th bit doesn't want to stick to - * PCRTC_START... - */ - int cre_heb = NVReadVgaCrtc(dev, head, NV_CIO_CRE_HEB__INDEX); - - NVWriteVgaCrtc(dev, head, NV_CIO_CRE_HEB__INDEX, - (cre_heb & ~0x40) | ((offset >> 18) & 0x40)); - } -} - -static inline void -nv_show_cursor(struct drm_device *dev, int head, bool show) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - uint8_t *curctl1 = - &nv04_display(dev)->mode_reg.crtc_reg[head].CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX]; - - if (show) - *curctl1 |= MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE); - else - *curctl1 &= ~MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE); - NVWriteVgaCrtc(dev, head, NV_CIO_CRE_HCUR_ADDR1_INDEX, *curctl1); - - if (nv_device(drm->device)->card_type == NV_40) - nv_fix_nv40_hw_cursor(dev, head); -} - -static inline uint32_t -nv_pitch_align(struct drm_device *dev, uint32_t width, int bpp) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - int mask; - - if (bpp == 15) - bpp = 16; - if (bpp == 24) - bpp = 8; - - /* Alignment requirements taken from the Haiku driver */ - if (nv_device(drm->device)->card_type == NV_04) - mask = 128 / bpp - 1; - else - mask = 512 / bpp - 1; - - return (width + mask) & ~mask; -} - -#endif /* __NOUVEAU_HW_H__ */ diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c deleted file mode 100644 index 6578cd2..0000000 --- a/drivers/gpu/drm/nouveau/nv04_crtc.c +++ /dev/null @@ -1,1073 +0,0 @@ -/* - * Copyright 1993-2003 NVIDIA, Corporation - * Copyright 2006 Dave Airlie - * Copyright 2007 Maarten Maathuis - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include -#include - -#include "nouveau_drm.h" -#include "nouveau_reg.h" -#include "nouveau_bo.h" -#include "nouveau_gem.h" -#include "nouveau_encoder.h" -#include "nouveau_connector.h" -#include "nouveau_crtc.h" -#include "nouveau_hw.h" -#include "nvreg.h" -#include "nouveau_fbcon.h" -#include "nv04_display.h" - -#include -#include - -static int -nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb); - -static void -crtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index) -{ - NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index, - crtcstate->CRTC[index]); -} - -static void nv_crtc_set_digital_vibrance(struct drm_crtc *crtc, int level) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct drm_device *dev = crtc->dev; - struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; - - regp->CRTC[NV_CIO_CRE_CSB] = nv_crtc->saturation = level; - if (nv_crtc->saturation && nv_gf4_disp_arch(crtc->dev)) { - regp->CRTC[NV_CIO_CRE_CSB] = 0x80; - regp->CRTC[NV_CIO_CRE_5B] = nv_crtc->saturation << 2; - crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_5B); - } - crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_CSB); -} - -static void nv_crtc_set_image_sharpening(struct drm_crtc *crtc, int level) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct drm_device *dev = crtc->dev; - struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; - - nv_crtc->sharpness = level; - if (level < 0) /* blur is in hw range 0x3f -> 0x20 */ - level += 0x40; - regp->ramdac_634 = level; - NVWriteRAMDAC(crtc->dev, nv_crtc->index, NV_PRAMDAC_634, regp->ramdac_634); -} - -#define PLLSEL_VPLL1_MASK \ - (NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL \ - | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2) -#define PLLSEL_VPLL2_MASK \ - (NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 \ - | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2) -#define PLLSEL_TV_MASK \ - (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \ - | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 \ - | NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \ - | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2) - -/* NV4x 0x40.. pll notes: - * gpu pll: 0x4000 + 0x4004 - * ?gpu? pll: 0x4008 + 0x400c - * vpll1: 0x4010 + 0x4014 - * vpll2: 0x4018 + 0x401c - * mpll: 0x4020 + 0x4024 - * mpll: 0x4038 + 0x403c - * - * the first register of each pair has some unknown details: - * bits 0-7: redirected values from elsewhere? (similar to PLL_SETUP_CONTROL?) - * bits 20-23: (mpll) something to do with post divider? - * bits 28-31: related to single stage mode? (bit 8/12) - */ - -static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mode * mode, int dot_clock) -{ - struct drm_device *dev = crtc->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_bios *bios = nouveau_bios(drm->device); - struct nouveau_clock *clk = nouveau_clock(drm->device); - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; - struct nv04_crtc_reg *regp = &state->crtc_reg[nv_crtc->index]; - struct nouveau_pll_vals *pv = ®p->pllvals; - struct nvbios_pll pll_lim; - - if (nvbios_pll_parse(bios, nv_crtc->index ? PLL_VPLL1 : PLL_VPLL0, - &pll_lim)) - return; - - /* NM2 == 0 is used to determine single stage mode on two stage plls */ - pv->NM2 = 0; - - /* for newer nv4x the blob uses only the first stage of the vpll below a - * certain clock. for a certain nv4b this is 150MHz. since the max - * output frequency of the first stage for this card is 300MHz, it is - * assumed the threshold is given by vco1 maxfreq/2 - */ - /* for early nv4x, specifically nv40 and *some* nv43 (devids 0 and 6, - * not 8, others unknown), the blob always uses both plls. no problem - * has yet been observed in allowing the use a single stage pll on all - * nv43 however. the behaviour of single stage use is untested on nv40 - */ - if (nv_device(drm->device)->chipset > 0x40 && dot_clock <= (pll_lim.vco1.max_freq / 2)) - memset(&pll_lim.vco2, 0, sizeof(pll_lim.vco2)); - - - if (!clk->pll_calc(clk, &pll_lim, dot_clock, pv)) - return; - - state->pllsel &= PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK; - - /* The blob uses this always, so let's do the same */ - if (nv_device(drm->device)->card_type == NV_40) - state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE; - /* again nv40 and some nv43 act more like nv3x as described above */ - if (nv_device(drm->device)->chipset < 0x41) - state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL | - NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL; - state->pllsel |= nv_crtc->index ? PLLSEL_VPLL2_MASK : PLLSEL_VPLL1_MASK; - - if (pv->NM2) - NV_DEBUG(drm, "vpll: n1 %d n2 %d m1 %d m2 %d log2p %d\n", - pv->N1, pv->N2, pv->M1, pv->M2, pv->log2P); - else - NV_DEBUG(drm, "vpll: n %d m %d log2p %d\n", - pv->N1, pv->M1, pv->log2P); - - nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset); -} - -static void -nv_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct drm_device *dev = crtc->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - unsigned char seq1 = 0, crtc17 = 0; - unsigned char crtc1A; - - NV_DEBUG(drm, "Setting dpms mode %d on CRTC %d\n", mode, - nv_crtc->index); - - if (nv_crtc->last_dpms == mode) /* Don't do unnecessary mode changes. */ - return; - - nv_crtc->last_dpms = mode; - - if (nv_two_heads(dev)) - NVSetOwner(dev, nv_crtc->index); - - /* nv4ref indicates these two RPC1 bits inhibit h/v sync */ - crtc1A = NVReadVgaCrtc(dev, nv_crtc->index, - NV_CIO_CRE_RPC1_INDEX) & ~0xC0; - switch (mode) { - case DRM_MODE_DPMS_STANDBY: - /* Screen: Off; HSync: Off, VSync: On -- Not Supported */ - seq1 = 0x20; - crtc17 = 0x80; - crtc1A |= 0x80; - break; - case DRM_MODE_DPMS_SUSPEND: - /* Screen: Off; HSync: On, VSync: Off -- Not Supported */ - seq1 = 0x20; - crtc17 = 0x80; - crtc1A |= 0x40; - break; - case DRM_MODE_DPMS_OFF: - /* Screen: Off; HSync: Off, VSync: Off */ - seq1 = 0x20; - crtc17 = 0x00; - crtc1A |= 0xC0; - break; - case DRM_MODE_DPMS_ON: - default: - /* Screen: On; HSync: On, VSync: On */ - seq1 = 0x00; - crtc17 = 0x80; - break; - } - - NVVgaSeqReset(dev, nv_crtc->index, true); - /* Each head has it's own sequencer, so we can turn it off when we want */ - seq1 |= (NVReadVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX) & ~0x20); - NVWriteVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX, seq1); - crtc17 |= (NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX) & ~0x80); - mdelay(10); - NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX, crtc17); - NVVgaSeqReset(dev, nv_crtc->index, false); - - NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RPC1_INDEX, crtc1A); -} - -static bool -nv_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - -static void -nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode) -{ - struct drm_device *dev = crtc->dev; - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; - struct drm_framebuffer *fb = crtc->fb; - - /* Calculate our timings */ - int horizDisplay = (mode->crtc_hdisplay >> 3) - 1; - int horizStart = (mode->crtc_hsync_start >> 3) + 1; - int horizEnd = (mode->crtc_hsync_end >> 3) + 1; - int horizTotal = (mode->crtc_htotal >> 3) - 5; - int horizBlankStart = (mode->crtc_hdisplay >> 3) - 1; - int horizBlankEnd = (mode->crtc_htotal >> 3) - 1; - int vertDisplay = mode->crtc_vdisplay - 1; - int vertStart = mode->crtc_vsync_start - 1; - int vertEnd = mode->crtc_vsync_end - 1; - int vertTotal = mode->crtc_vtotal - 2; - int vertBlankStart = mode->crtc_vdisplay - 1; - int vertBlankEnd = mode->crtc_vtotal - 1; - - struct drm_encoder *encoder; - bool fp_output = false; - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - - if (encoder->crtc == crtc && - (nv_encoder->dcb->type == DCB_OUTPUT_LVDS || - nv_encoder->dcb->type == DCB_OUTPUT_TMDS)) - fp_output = true; - } - - if (fp_output) { - vertStart = vertTotal - 3; - vertEnd = vertTotal - 2; - vertBlankStart = vertStart; - horizStart = horizTotal - 5; - horizEnd = horizTotal - 2; - horizBlankEnd = horizTotal + 4; -#if 0 - if (dev->overlayAdaptor && nv_device(drm->device)->card_type >= NV_10) - /* This reportedly works around some video overlay bandwidth problems */ - horizTotal += 2; -#endif - } - - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - vertTotal |= 1; - -#if 0 - ErrorF("horizDisplay: 0x%X \n", horizDisplay); - ErrorF("horizStart: 0x%X \n", horizStart); - ErrorF("horizEnd: 0x%X \n", horizEnd); - ErrorF("horizTotal: 0x%X \n", horizTotal); - ErrorF("horizBlankStart: 0x%X \n", horizBlankStart); - ErrorF("horizBlankEnd: 0x%X \n", horizBlankEnd); - ErrorF("vertDisplay: 0x%X \n", vertDisplay); - ErrorF("vertStart: 0x%X \n", vertStart); - ErrorF("vertEnd: 0x%X \n", vertEnd); - ErrorF("vertTotal: 0x%X \n", vertTotal); - ErrorF("vertBlankStart: 0x%X \n", vertBlankStart); - ErrorF("vertBlankEnd: 0x%X \n", vertBlankEnd); -#endif - - /* - * compute correct Hsync & Vsync polarity - */ - if ((mode->flags & (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC)) - && (mode->flags & (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC))) { - - regp->MiscOutReg = 0x23; - if (mode->flags & DRM_MODE_FLAG_NHSYNC) - regp->MiscOutReg |= 0x40; - if (mode->flags & DRM_MODE_FLAG_NVSYNC) - regp->MiscOutReg |= 0x80; - } else { - int vdisplay = mode->vdisplay; - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) - vdisplay *= 2; - if (mode->vscan > 1) - vdisplay *= mode->vscan; - if (vdisplay < 400) - regp->MiscOutReg = 0xA3; /* +hsync -vsync */ - else if (vdisplay < 480) - regp->MiscOutReg = 0x63; /* -hsync +vsync */ - else if (vdisplay < 768) - regp->MiscOutReg = 0xE3; /* -hsync -vsync */ - else - regp->MiscOutReg = 0x23; /* +hsync +vsync */ - } - - regp->MiscOutReg |= (mode->clock_index & 0x03) << 2; - - /* - * Time Sequencer - */ - regp->Sequencer[NV_VIO_SR_RESET_INDEX] = 0x00; - /* 0x20 disables the sequencer */ - if (mode->flags & DRM_MODE_FLAG_CLKDIV2) - regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x29; - else - regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x21; - regp->Sequencer[NV_VIO_SR_PLANE_MASK_INDEX] = 0x0F; - regp->Sequencer[NV_VIO_SR_CHAR_MAP_INDEX] = 0x00; - regp->Sequencer[NV_VIO_SR_MEM_MODE_INDEX] = 0x0E; - - /* - * CRTC - */ - regp->CRTC[NV_CIO_CR_HDT_INDEX] = horizTotal; - regp->CRTC[NV_CIO_CR_HDE_INDEX] = horizDisplay; - regp->CRTC[NV_CIO_CR_HBS_INDEX] = horizBlankStart; - regp->CRTC[NV_CIO_CR_HBE_INDEX] = (1 << 7) | - XLATE(horizBlankEnd, 0, NV_CIO_CR_HBE_4_0); - regp->CRTC[NV_CIO_CR_HRS_INDEX] = horizStart; - regp->CRTC[NV_CIO_CR_HRE_INDEX] = XLATE(horizBlankEnd, 5, NV_CIO_CR_HRE_HBE_5) | - XLATE(horizEnd, 0, NV_CIO_CR_HRE_4_0); - regp->CRTC[NV_CIO_CR_VDT_INDEX] = vertTotal; - regp->CRTC[NV_CIO_CR_OVL_INDEX] = XLATE(vertStart, 9, NV_CIO_CR_OVL_VRS_9) | - XLATE(vertDisplay, 9, NV_CIO_CR_OVL_VDE_9) | - XLATE(vertTotal, 9, NV_CIO_CR_OVL_VDT_9) | - (1 << 4) | - XLATE(vertBlankStart, 8, NV_CIO_CR_OVL_VBS_8) | - XLATE(vertStart, 8, NV_CIO_CR_OVL_VRS_8) | - XLATE(vertDisplay, 8, NV_CIO_CR_OVL_VDE_8) | - XLATE(vertTotal, 8, NV_CIO_CR_OVL_VDT_8); - regp->CRTC[NV_CIO_CR_RSAL_INDEX] = 0x00; - regp->CRTC[NV_CIO_CR_CELL_HT_INDEX] = ((mode->flags & DRM_MODE_FLAG_DBLSCAN) ? MASK(NV_CIO_CR_CELL_HT_SCANDBL) : 0) | - 1 << 6 | - XLATE(vertBlankStart, 9, NV_CIO_CR_CELL_HT_VBS_9); - regp->CRTC[NV_CIO_CR_CURS_ST_INDEX] = 0x00; - regp->CRTC[NV_CIO_CR_CURS_END_INDEX] = 0x00; - regp->CRTC[NV_CIO_CR_SA_HI_INDEX] = 0x00; - regp->CRTC[NV_CIO_CR_SA_LO_INDEX] = 0x00; - regp->CRTC[NV_CIO_CR_TCOFF_HI_INDEX] = 0x00; - regp->CRTC[NV_CIO_CR_TCOFF_LO_INDEX] = 0x00; - regp->CRTC[NV_CIO_CR_VRS_INDEX] = vertStart; - regp->CRTC[NV_CIO_CR_VRE_INDEX] = 1 << 5 | XLATE(vertEnd, 0, NV_CIO_CR_VRE_3_0); - regp->CRTC[NV_CIO_CR_VDE_INDEX] = vertDisplay; - /* framebuffer can be larger than crtc scanout area. */ - regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = fb->pitches[0] / 8; - regp->CRTC[NV_CIO_CR_ULINE_INDEX] = 0x00; - regp->CRTC[NV_CIO_CR_VBS_INDEX] = vertBlankStart; - regp->CRTC[NV_CIO_CR_VBE_INDEX] = vertBlankEnd; - regp->CRTC[NV_CIO_CR_MODE_INDEX] = 0x43; - regp->CRTC[NV_CIO_CR_LCOMP_INDEX] = 0xff; - - /* - * Some extended CRTC registers (they are not saved with the rest of the vga regs). - */ - - /* framebuffer can be larger than crtc scanout area. */ - regp->CRTC[NV_CIO_CRE_RPC0_INDEX] = - XLATE(fb->pitches[0] / 8, 8, NV_CIO_CRE_RPC0_OFFSET_10_8); - regp->CRTC[NV_CIO_CRE_42] = - XLATE(fb->pitches[0] / 8, 11, NV_CIO_CRE_42_OFFSET_11); - regp->CRTC[NV_CIO_CRE_RPC1_INDEX] = mode->crtc_hdisplay < 1280 ? - MASK(NV_CIO_CRE_RPC1_LARGE) : 0x00; - regp->CRTC[NV_CIO_CRE_LSR_INDEX] = XLATE(horizBlankEnd, 6, NV_CIO_CRE_LSR_HBE_6) | - XLATE(vertBlankStart, 10, NV_CIO_CRE_LSR_VBS_10) | - XLATE(vertStart, 10, NV_CIO_CRE_LSR_VRS_10) | - XLATE(vertDisplay, 10, NV_CIO_CRE_LSR_VDE_10) | - XLATE(vertTotal, 10, NV_CIO_CRE_LSR_VDT_10); - regp->CRTC[NV_CIO_CRE_HEB__INDEX] = XLATE(horizStart, 8, NV_CIO_CRE_HEB_HRS_8) | - XLATE(horizBlankStart, 8, NV_CIO_CRE_HEB_HBS_8) | - XLATE(horizDisplay, 8, NV_CIO_CRE_HEB_HDE_8) | - XLATE(horizTotal, 8, NV_CIO_CRE_HEB_HDT_8); - regp->CRTC[NV_CIO_CRE_EBR_INDEX] = XLATE(vertBlankStart, 11, NV_CIO_CRE_EBR_VBS_11) | - XLATE(vertStart, 11, NV_CIO_CRE_EBR_VRS_11) | - XLATE(vertDisplay, 11, NV_CIO_CRE_EBR_VDE_11) | - XLATE(vertTotal, 11, NV_CIO_CRE_EBR_VDT_11); - - if (mode->flags & DRM_MODE_FLAG_INTERLACE) { - horizTotal = (horizTotal >> 1) & ~1; - regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = horizTotal; - regp->CRTC[NV_CIO_CRE_HEB__INDEX] |= XLATE(horizTotal, 8, NV_CIO_CRE_HEB_ILC_8); - } else - regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = 0xff; /* interlace off */ - - /* - * Graphics Display Controller - */ - regp->Graphics[NV_VIO_GX_SR_INDEX] = 0x00; - regp->Graphics[NV_VIO_GX_SREN_INDEX] = 0x00; - regp->Graphics[NV_VIO_GX_CCOMP_INDEX] = 0x00; - regp->Graphics[NV_VIO_GX_ROP_INDEX] = 0x00; - regp->Graphics[NV_VIO_GX_READ_MAP_INDEX] = 0x00; - regp->Graphics[NV_VIO_GX_MODE_INDEX] = 0x40; /* 256 color mode */ - regp->Graphics[NV_VIO_GX_MISC_INDEX] = 0x05; /* map 64k mem + graphic mode */ - regp->Graphics[NV_VIO_GX_DONT_CARE_INDEX] = 0x0F; - regp->Graphics[NV_VIO_GX_BIT_MASK_INDEX] = 0xFF; - - regp->Attribute[0] = 0x00; /* standard colormap translation */ - regp->Attribute[1] = 0x01; - regp->Attribute[2] = 0x02; - regp->Attribute[3] = 0x03; - regp->Attribute[4] = 0x04; - regp->Attribute[5] = 0x05; - regp->Attribute[6] = 0x06; - regp->Attribute[7] = 0x07; - regp->Attribute[8] = 0x08; - regp->Attribute[9] = 0x09; - regp->Attribute[10] = 0x0A; - regp->Attribute[11] = 0x0B; - regp->Attribute[12] = 0x0C; - regp->Attribute[13] = 0x0D; - regp->Attribute[14] = 0x0E; - regp->Attribute[15] = 0x0F; - regp->Attribute[NV_CIO_AR_MODE_INDEX] = 0x01; /* Enable graphic mode */ - /* Non-vga */ - regp->Attribute[NV_CIO_AR_OSCAN_INDEX] = 0x00; - regp->Attribute[NV_CIO_AR_PLANE_INDEX] = 0x0F; /* enable all color planes */ - regp->Attribute[NV_CIO_AR_HPP_INDEX] = 0x00; - regp->Attribute[NV_CIO_AR_CSEL_INDEX] = 0x00; -} - -/** - * Sets up registers for the given mode/adjusted_mode pair. - * - * The clocks, CRTCs and outputs attached to this CRTC must be off. - * - * This shouldn't enable any clocks, CRTCs, or outputs, but they should - * be easily turned on/off after this. - */ -static void -nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode) -{ - struct drm_device *dev = crtc->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; - struct nv04_crtc_reg *savep = &nv04_display(dev)->saved_reg.crtc_reg[nv_crtc->index]; - struct drm_encoder *encoder; - bool lvds_output = false, tmds_output = false, tv_output = false, - off_chip_digital = false; - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - bool digital = false; - - if (encoder->crtc != crtc) - continue; - - if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) - digital = lvds_output = true; - if (nv_encoder->dcb->type == DCB_OUTPUT_TV) - tv_output = true; - if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS) - digital = tmds_output = true; - if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && digital) - off_chip_digital = true; - } - - /* Registers not directly related to the (s)vga mode */ - - /* What is the meaning of this register? */ - /* A few popular values are 0x18, 0x1c, 0x38, 0x3c */ - regp->CRTC[NV_CIO_CRE_ENH_INDEX] = savep->CRTC[NV_CIO_CRE_ENH_INDEX] & ~(1<<5); - - regp->crtc_eng_ctrl = 0; - /* Except for rare conditions I2C is enabled on the primary crtc */ - if (nv_crtc->index == 0) - regp->crtc_eng_ctrl |= NV_CRTC_FSEL_I2C; -#if 0 - /* Set overlay to desired crtc. */ - if (dev->overlayAdaptor) { - NVPortPrivPtr pPriv = GET_OVERLAY_PRIVATE(dev); - if (pPriv->overlayCRTC == nv_crtc->index) - regp->crtc_eng_ctrl |= NV_CRTC_FSEL_OVERLAY; - } -#endif - - /* ADDRESS_SPACE_PNVM is the same as setting HCUR_ASI */ - regp->cursor_cfg = NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 | - NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 | - NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM; - if (nv_device(drm->device)->chipset >= 0x11) - regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32; - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) - regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE; - - /* Unblock some timings */ - regp->CRTC[NV_CIO_CRE_53] = 0; - regp->CRTC[NV_CIO_CRE_54] = 0; - - /* 0x00 is disabled, 0x11 is lvds, 0x22 crt and 0x88 tmds */ - if (lvds_output) - regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x11; - else if (tmds_output) - regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x88; - else - regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x22; - - /* These values seem to vary */ - /* This register seems to be used by the bios to make certain decisions on some G70 cards? */ - regp->CRTC[NV_CIO_CRE_SCRATCH4__INDEX] = savep->CRTC[NV_CIO_CRE_SCRATCH4__INDEX]; - - nv_crtc_set_digital_vibrance(crtc, nv_crtc->saturation); - - /* probably a scratch reg, but kept for cargo-cult purposes: - * bit0: crtc0?, head A - * bit6: lvds, head A - * bit7: (only in X), head A - */ - if (nv_crtc->index == 0) - regp->CRTC[NV_CIO_CRE_4B] = savep->CRTC[NV_CIO_CRE_4B] | 0x80; - - /* The blob seems to take the current value from crtc 0, add 4 to that - * and reuse the old value for crtc 1 */ - regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] = nv04_display(dev)->saved_reg.crtc_reg[0].CRTC[NV_CIO_CRE_TVOUT_LATENCY]; - if (!nv_crtc->index) - regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] += 4; - - /* the blob sometimes sets |= 0x10 (which is the same as setting |= - * 1 << 30 on 0x60.830), for no apparent reason */ - regp->CRTC[NV_CIO_CRE_59] = off_chip_digital; - - if (nv_device(drm->device)->card_type >= NV_30) - regp->CRTC[0x9f] = off_chip_digital ? 0x11 : 0x1; - - regp->crtc_830 = mode->crtc_vdisplay - 3; - regp->crtc_834 = mode->crtc_vdisplay - 1; - - if (nv_device(drm->device)->card_type == NV_40) - /* This is what the blob does */ - regp->crtc_850 = NVReadCRTC(dev, 0, NV_PCRTC_850); - - if (nv_device(drm->device)->card_type >= NV_30) - regp->gpio_ext = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT); - - if (nv_device(drm->device)->card_type >= NV_10) - regp->crtc_cfg = NV10_PCRTC_CONFIG_START_ADDRESS_HSYNC; - else - regp->crtc_cfg = NV04_PCRTC_CONFIG_START_ADDRESS_HSYNC; - - /* Some misc regs */ - if (nv_device(drm->device)->card_type == NV_40) { - regp->CRTC[NV_CIO_CRE_85] = 0xFF; - regp->CRTC[NV_CIO_CRE_86] = 0x1; - } - - regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (crtc->fb->depth + 1) / 8; - /* Enable slaved mode (called MODE_TV in nv4ref.h) */ - if (lvds_output || tmds_output || tv_output) - regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (1 << 7); - - /* Generic PRAMDAC regs */ - - if (nv_device(drm->device)->card_type >= NV_10) - /* Only bit that bios and blob set. */ - regp->nv10_cursync = (1 << 25); - - regp->ramdac_gen_ctrl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | - NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL | - NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON; - if (crtc->fb->depth == 16) - regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; - if (nv_device(drm->device)->chipset >= 0x11) - regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG; - - regp->ramdac_630 = 0; /* turn off green mode (tv test pattern?) */ - regp->tv_setup = 0; - - nv_crtc_set_image_sharpening(crtc, nv_crtc->sharpness); - - /* Some values the blob sets */ - regp->ramdac_8c0 = 0x100; - regp->ramdac_a20 = 0x0; - regp->ramdac_a24 = 0xfffff; - regp->ramdac_a34 = 0x1; -} - -/** - * Sets up registers for the given mode/adjusted_mode pair. - * - * The clocks, CRTCs and outputs attached to this CRTC must be off. - * - * This shouldn't enable any clocks, CRTCs, or outputs, but they should - * be easily turned on/off after this. - */ -static int -nv_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, struct drm_framebuffer *old_fb) -{ - struct drm_device *dev = crtc->dev; - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct nouveau_drm *drm = nouveau_drm(dev); - - NV_DEBUG(drm, "CTRC mode on CRTC %d:\n", nv_crtc->index); - drm_mode_debug_printmodeline(adjusted_mode); - - /* unlock must come after turning off FP_TG_CONTROL in output_prepare */ - nv_lock_vga_crtc_shadow(dev, nv_crtc->index, -1); - - nv_crtc_mode_set_vga(crtc, adjusted_mode); - /* calculated in nv04_dfp_prepare, nv40 needs it written before calculating PLLs */ - if (nv_device(drm->device)->card_type == NV_40) - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, nv04_display(dev)->mode_reg.sel_clk); - nv_crtc_mode_set_regs(crtc, adjusted_mode); - nv_crtc_calc_state_ext(crtc, mode, adjusted_mode->clock); - return 0; -} - -static void nv_crtc_save(struct drm_crtc *crtc) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct drm_device *dev = crtc->dev; - struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; - struct nv04_crtc_reg *crtc_state = &state->crtc_reg[nv_crtc->index]; - struct nv04_mode_state *saved = &nv04_display(dev)->saved_reg; - struct nv04_crtc_reg *crtc_saved = &saved->crtc_reg[nv_crtc->index]; - - if (nv_two_heads(crtc->dev)) - NVSetOwner(crtc->dev, nv_crtc->index); - - nouveau_hw_save_state(crtc->dev, nv_crtc->index, saved); - - /* init some state to saved value */ - state->sel_clk = saved->sel_clk & ~(0x5 << 16); - crtc_state->CRTC[NV_CIO_CRE_LCD__INDEX] = crtc_saved->CRTC[NV_CIO_CRE_LCD__INDEX]; - state->pllsel = saved->pllsel & ~(PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK); - crtc_state->gpio_ext = crtc_saved->gpio_ext; -} - -static void nv_crtc_restore(struct drm_crtc *crtc) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct drm_device *dev = crtc->dev; - int head = nv_crtc->index; - uint8_t saved_cr21 = nv04_display(dev)->saved_reg.crtc_reg[head].CRTC[NV_CIO_CRE_21]; - - if (nv_two_heads(crtc->dev)) - NVSetOwner(crtc->dev, head); - - nouveau_hw_load_state(crtc->dev, head, &nv04_display(dev)->saved_reg); - nv_lock_vga_crtc_shadow(crtc->dev, head, saved_cr21); - - nv_crtc->last_dpms = NV_DPMS_CLEARED; -} - -static void nv_crtc_prepare(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct drm_crtc_helper_funcs *funcs = crtc->helper_private; - - if (nv_two_heads(dev)) - NVSetOwner(dev, nv_crtc->index); - - drm_vblank_pre_modeset(dev, nv_crtc->index); - funcs->dpms(crtc, DRM_MODE_DPMS_OFF); - - NVBlankScreen(dev, nv_crtc->index, true); - - /* Some more preparation. */ - NVWriteCRTC(dev, nv_crtc->index, NV_PCRTC_CONFIG, NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA); - if (nv_device(drm->device)->card_type == NV_40) { - uint32_t reg900 = NVReadRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900); - NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900, reg900 & ~0x10000); - } -} - -static void nv_crtc_commit(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct drm_crtc_helper_funcs *funcs = crtc->helper_private; - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - - nouveau_hw_load_state(dev, nv_crtc->index, &nv04_display(dev)->mode_reg); - nv04_crtc_mode_set_base(crtc, crtc->x, crtc->y, NULL); - -#ifdef __BIG_ENDIAN - /* turn on LFB swapping */ - { - uint8_t tmp = NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR); - tmp |= MASK(NV_CIO_CRE_RCR_ENDIAN_BIG); - NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR, tmp); - } -#endif - - funcs->dpms(crtc, DRM_MODE_DPMS_ON); - drm_vblank_post_modeset(dev, nv_crtc->index); -} - -static void nv_crtc_destroy(struct drm_crtc *crtc) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - - if (!nv_crtc) - return; - - drm_crtc_cleanup(crtc); - - nouveau_bo_unmap(nv_crtc->cursor.nvbo); - nouveau_bo_unpin(nv_crtc->cursor.nvbo); - nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); - kfree(nv_crtc); -} - -static void -nv_crtc_gamma_load(struct drm_crtc *crtc) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct drm_device *dev = nv_crtc->base.dev; - struct rgb { uint8_t r, g, b; } __attribute__((packed)) *rgbs; - int i; - - rgbs = (struct rgb *)nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index].DAC; - for (i = 0; i < 256; i++) { - rgbs[i].r = nv_crtc->lut.r[i] >> 8; - rgbs[i].g = nv_crtc->lut.g[i] >> 8; - rgbs[i].b = nv_crtc->lut.b[i] >> 8; - } - - nouveau_hw_load_state_palette(dev, nv_crtc->index, &nv04_display(dev)->mode_reg); -} - -static void -nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t start, - uint32_t size) -{ - int end = (start + size > 256) ? 256 : start + size, i; - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - - for (i = start; i < end; i++) { - nv_crtc->lut.r[i] = r[i]; - nv_crtc->lut.g[i] = g[i]; - nv_crtc->lut.b[i] = b[i]; - } - - /* We need to know the depth before we upload, but it's possible to - * get called before a framebuffer is bound. If this is the case, - * mark the lut values as dirty by setting depth==0, and it'll be - * uploaded on the first mode_set_base() - */ - if (!nv_crtc->base.fb) { - nv_crtc->lut.depth = 0; - return; - } - - nv_crtc_gamma_load(crtc); -} - -static int -nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, - struct drm_framebuffer *passed_fb, - int x, int y, bool atomic) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct drm_device *dev = crtc->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; - struct drm_framebuffer *drm_fb; - struct nouveau_framebuffer *fb; - int arb_burst, arb_lwm; - int ret; - - NV_DEBUG(drm, "index %d\n", nv_crtc->index); - - /* no fb bound */ - if (!atomic && !crtc->fb) { - NV_DEBUG(drm, "No FB bound\n"); - return 0; - } - - - /* If atomic, we want to switch to the fb we were passed, so - * now we update pointers to do that. (We don't pin; just - * assume we're already pinned and update the base address.) - */ - if (atomic) { - drm_fb = passed_fb; - fb = nouveau_framebuffer(passed_fb); - } else { - drm_fb = crtc->fb; - fb = nouveau_framebuffer(crtc->fb); - /* If not atomic, we can go ahead and pin, and unpin the - * old fb we were passed. - */ - ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM); - if (ret) - return ret; - - if (passed_fb) { - struct nouveau_framebuffer *ofb = nouveau_framebuffer(passed_fb); - nouveau_bo_unpin(ofb->nvbo); - } - } - - nv_crtc->fb.offset = fb->nvbo->bo.offset; - - if (nv_crtc->lut.depth != drm_fb->depth) { - nv_crtc->lut.depth = drm_fb->depth; - nv_crtc_gamma_load(crtc); - } - - /* Update the framebuffer format. */ - regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] &= ~3; - regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (crtc->fb->depth + 1) / 8; - regp->ramdac_gen_ctrl &= ~NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; - if (crtc->fb->depth == 16) - regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; - crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_PIXEL_INDEX); - NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_GENERAL_CONTROL, - regp->ramdac_gen_ctrl); - - regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = drm_fb->pitches[0] >> 3; - regp->CRTC[NV_CIO_CRE_RPC0_INDEX] = - XLATE(drm_fb->pitches[0] >> 3, 8, NV_CIO_CRE_RPC0_OFFSET_10_8); - regp->CRTC[NV_CIO_CRE_42] = - XLATE(drm_fb->pitches[0] / 8, 11, NV_CIO_CRE_42_OFFSET_11); - crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_RPC0_INDEX); - crtc_wr_cio_state(crtc, regp, NV_CIO_CR_OFFSET_INDEX); - crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_42); - - /* Update the framebuffer location. */ - regp->fb_start = nv_crtc->fb.offset & ~3; - regp->fb_start += (y * drm_fb->pitches[0]) + (x * drm_fb->bits_per_pixel / 8); - nv_set_crtc_base(dev, nv_crtc->index, regp->fb_start); - - /* Update the arbitration parameters. */ - nouveau_calc_arb(dev, crtc->mode.clock, drm_fb->bits_per_pixel, - &arb_burst, &arb_lwm); - - regp->CRTC[NV_CIO_CRE_FF_INDEX] = arb_burst; - regp->CRTC[NV_CIO_CRE_FFLWM__INDEX] = arb_lwm & 0xff; - crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FF_INDEX); - crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FFLWM__INDEX); - - if (nv_device(drm->device)->card_type >= NV_20) { - regp->CRTC[NV_CIO_CRE_47] = arb_lwm >> 8; - crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_47); - } - - return 0; -} - -static int -nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) -{ - return nv04_crtc_do_mode_set_base(crtc, old_fb, x, y, false); -} - -static int -nv04_crtc_mode_set_base_atomic(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int x, int y, enum mode_set_atomic state) -{ - struct nouveau_drm *drm = nouveau_drm(crtc->dev); - struct drm_device *dev = drm->dev; - - if (state == ENTER_ATOMIC_MODE_SET) - nouveau_fbcon_save_disable_accel(dev); - else - nouveau_fbcon_restore_accel(dev); - - return nv04_crtc_do_mode_set_base(crtc, fb, x, y, true); -} - -static void nv04_cursor_upload(struct drm_device *dev, struct nouveau_bo *src, - struct nouveau_bo *dst) -{ - int width = nv_cursor_width(dev); - uint32_t pixel; - int i, j; - - for (i = 0; i < width; i++) { - for (j = 0; j < width; j++) { - pixel = nouveau_bo_rd32(src, i*64 + j); - - nouveau_bo_wr16(dst, i*width + j, (pixel & 0x80000000) >> 16 - | (pixel & 0xf80000) >> 9 - | (pixel & 0xf800) >> 6 - | (pixel & 0xf8) >> 3); - } - } -} - -static void nv11_cursor_upload(struct drm_device *dev, struct nouveau_bo *src, - struct nouveau_bo *dst) -{ - uint32_t pixel; - int alpha, i; - - /* nv11+ supports premultiplied (PM), or non-premultiplied (NPM) alpha - * cursors (though NPM in combination with fp dithering may not work on - * nv11, from "nv" driver history) - * NPM mode needs NV_PCRTC_CURSOR_CONFIG_ALPHA_BLEND set and is what the - * blob uses, however we get given PM cursors so we use PM mode - */ - for (i = 0; i < 64 * 64; i++) { - pixel = nouveau_bo_rd32(src, i); - - /* hw gets unhappy if alpha <= rgb values. for a PM image "less - * than" shouldn't happen; fix "equal to" case by adding one to - * alpha channel (slightly inaccurate, but so is attempting to - * get back to NPM images, due to limits of integer precision) - */ - alpha = pixel >> 24; - if (alpha > 0 && alpha < 255) - pixel = (pixel & 0x00ffffff) | ((alpha + 1) << 24); - -#ifdef __BIG_ENDIAN - { - struct nouveau_drm *drm = nouveau_drm(dev); - - if (nv_device(drm->device)->chipset == 0x11) { - pixel = ((pixel & 0x000000ff) << 24) | - ((pixel & 0x0000ff00) << 8) | - ((pixel & 0x00ff0000) >> 8) | - ((pixel & 0xff000000) >> 24); - } - } -#endif - - nouveau_bo_wr32(dst, i, pixel); - } -} - -static int -nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, - uint32_t buffer_handle, uint32_t width, uint32_t height) -{ - struct nouveau_drm *drm = nouveau_drm(crtc->dev); - struct drm_device *dev = drm->dev; - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct nouveau_bo *cursor = NULL; - struct drm_gem_object *gem; - int ret = 0; - - if (!buffer_handle) { - nv_crtc->cursor.hide(nv_crtc, true); - return 0; - } - - if (width != 64 || height != 64) - return -EINVAL; - - gem = drm_gem_object_lookup(dev, file_priv, buffer_handle); - if (!gem) - return -ENOENT; - cursor = nouveau_gem_object(gem); - - ret = nouveau_bo_map(cursor); - if (ret) - goto out; - - if (nv_device(drm->device)->chipset >= 0x11) - nv11_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo); - else - nv04_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo); - - nouveau_bo_unmap(cursor); - nv_crtc->cursor.offset = nv_crtc->cursor.nvbo->bo.offset; - nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset); - nv_crtc->cursor.show(nv_crtc, true); -out: - drm_gem_object_unreference_unlocked(gem); - return ret; -} - -static int -nv04_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - - nv_crtc->cursor.set_pos(nv_crtc, x, y); - return 0; -} - -static const struct drm_crtc_funcs nv04_crtc_funcs = { - .save = nv_crtc_save, - .restore = nv_crtc_restore, - .cursor_set = nv04_crtc_cursor_set, - .cursor_move = nv04_crtc_cursor_move, - .gamma_set = nv_crtc_gamma_set, - .set_config = drm_crtc_helper_set_config, - .page_flip = nouveau_crtc_page_flip, - .destroy = nv_crtc_destroy, -}; - -static const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = { - .dpms = nv_crtc_dpms, - .prepare = nv_crtc_prepare, - .commit = nv_crtc_commit, - .mode_fixup = nv_crtc_mode_fixup, - .mode_set = nv_crtc_mode_set, - .mode_set_base = nv04_crtc_mode_set_base, - .mode_set_base_atomic = nv04_crtc_mode_set_base_atomic, - .load_lut = nv_crtc_gamma_load, -}; - -int -nv04_crtc_create(struct drm_device *dev, int crtc_num) -{ - struct nouveau_crtc *nv_crtc; - int ret, i; - - nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL); - if (!nv_crtc) - return -ENOMEM; - - for (i = 0; i < 256; i++) { - nv_crtc->lut.r[i] = i << 8; - nv_crtc->lut.g[i] = i << 8; - nv_crtc->lut.b[i] = i << 8; - } - nv_crtc->lut.depth = 0; - - nv_crtc->index = crtc_num; - nv_crtc->last_dpms = NV_DPMS_CLEARED; - - drm_crtc_init(dev, &nv_crtc->base, &nv04_crtc_funcs); - drm_crtc_helper_add(&nv_crtc->base, &nv04_crtc_helper_funcs); - drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256); - - ret = nouveau_bo_new(dev, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &nv_crtc->cursor.nvbo); - if (!ret) { - ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); - if (!ret) { - ret = nouveau_bo_map(nv_crtc->cursor.nvbo); - if (ret) - nouveau_bo_unpin(nv_crtc->cursor.nvbo); - } - if (ret) - nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); - } - - nv04_cursor_init(nv_crtc); - - return 0; -} - diff --git a/drivers/gpu/drm/nouveau/nv04_cursor.c b/drivers/gpu/drm/nouveau/nv04_cursor.c deleted file mode 100644 index fe86f0d..0000000 --- a/drivers/gpu/drm/nouveau/nv04_cursor.c +++ /dev/null @@ -1,71 +0,0 @@ -#include -#include -#include "nouveau_drm.h" -#include "nouveau_reg.h" -#include "nouveau_crtc.h" -#include "nouveau_hw.h" - -static void -nv04_cursor_show(struct nouveau_crtc *nv_crtc, bool update) -{ - nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, true); -} - -static void -nv04_cursor_hide(struct nouveau_crtc *nv_crtc, bool update) -{ - nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, false); -} - -static void -nv04_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y) -{ - nv_crtc->cursor_saved_x = x; nv_crtc->cursor_saved_y = y; - NVWriteRAMDAC(nv_crtc->base.dev, nv_crtc->index, - NV_PRAMDAC_CU_START_POS, - XLATE(y, 0, NV_PRAMDAC_CU_START_POS_Y) | - XLATE(x, 0, NV_PRAMDAC_CU_START_POS_X)); -} - -static void -crtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index) -{ - NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index, - crtcstate->CRTC[index]); -} - -static void -nv04_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset) -{ - struct drm_device *dev = nv_crtc->base.dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; - struct drm_crtc *crtc = &nv_crtc->base; - - regp->CRTC[NV_CIO_CRE_HCUR_ADDR0_INDEX] = - MASK(NV_CIO_CRE_HCUR_ASI) | - XLATE(offset, 17, NV_CIO_CRE_HCUR_ADDR0_ADR); - regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] = - XLATE(offset, 11, NV_CIO_CRE_HCUR_ADDR1_ADR); - if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN) - regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] |= - MASK(NV_CIO_CRE_HCUR_ADDR1_CUR_DBL); - regp->CRTC[NV_CIO_CRE_HCUR_ADDR2_INDEX] = offset >> 24; - - crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); - crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX); - crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX); - if (nv_device(drm->device)->card_type == NV_40) - nv_fix_nv40_hw_cursor(dev, nv_crtc->index); -} - -int -nv04_cursor_init(struct nouveau_crtc *crtc) -{ - crtc->cursor.set_offset = nv04_cursor_set_offset; - crtc->cursor.set_pos = nv04_cursor_set_pos; - crtc->cursor.hide = nv04_cursor_hide; - crtc->cursor.show = nv04_cursor_show; - return 0; -} - diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c deleted file mode 100644 index 64f7020..0000000 --- a/drivers/gpu/drm/nouveau/nv04_dac.c +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Copyright 2003 NVIDIA, Corporation - * Copyright 2006 Dave Airlie - * Copyright 2007 Maarten Maathuis - * Copyright 2007-2009 Stuart Bennett - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include -#include - -#include "nouveau_drm.h" -#include "nouveau_encoder.h" -#include "nouveau_connector.h" -#include "nouveau_crtc.h" -#include "nouveau_hw.h" -#include "nvreg.h" - -#include -#include -#include - -int nv04_dac_output_offset(struct drm_encoder *encoder) -{ - struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; - int offset = 0; - - if (dcb->or & (8 | DCB_OUTPUT_C)) - offset += 0x68; - if (dcb->or & (8 | DCB_OUTPUT_B)) - offset += 0x2000; - - return offset; -} - -/* - * arbitrary limit to number of sense oscillations tolerated in one sample - * period (observed to be at least 13 in "nvidia") - */ -#define MAX_HBLANK_OSC 20 - -/* - * arbitrary limit to number of conflicting sample pairs to tolerate at a - * voltage step (observed to be at least 5 in "nvidia") - */ -#define MAX_SAMPLE_PAIRS 10 - -static int sample_load_twice(struct drm_device *dev, bool sense[2]) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_timer *ptimer = nouveau_timer(device); - int i; - - for (i = 0; i < 2; i++) { - bool sense_a, sense_b, sense_b_prime; - int j = 0; - - /* - * wait for bit 0 clear -- out of hblank -- (say reg value 0x4), - * then wait for transition 0x4->0x5->0x4: enter hblank, leave - * hblank again - * use a 10ms timeout (guards against crtc being inactive, in - * which case blank state would never change) - */ - if (!nouveau_timer_wait_eq(ptimer, 10000000, - NV_PRMCIO_INP0__COLOR, - 0x00000001, 0x00000000)) - return -EBUSY; - if (!nouveau_timer_wait_eq(ptimer, 10000000, - NV_PRMCIO_INP0__COLOR, - 0x00000001, 0x00000001)) - return -EBUSY; - if (!nouveau_timer_wait_eq(ptimer, 10000000, - NV_PRMCIO_INP0__COLOR, - 0x00000001, 0x00000000)) - return -EBUSY; - - udelay(100); - /* when level triggers, sense is _LO_ */ - sense_a = nv_rd08(device, NV_PRMCIO_INP0) & 0x10; - - /* take another reading until it agrees with sense_a... */ - do { - udelay(100); - sense_b = nv_rd08(device, NV_PRMCIO_INP0) & 0x10; - if (sense_a != sense_b) { - sense_b_prime = - nv_rd08(device, NV_PRMCIO_INP0) & 0x10; - if (sense_b == sense_b_prime) { - /* ... unless two consecutive subsequent - * samples agree; sense_a is replaced */ - sense_a = sense_b; - /* force mis-match so we loop */ - sense_b = !sense_a; - } - } - } while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC); - - if (j == MAX_HBLANK_OSC) - /* with so much oscillation, default to sense:LO */ - sense[i] = false; - else - sense[i] = sense_a; - } - - return 0; -} - -static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder, - struct drm_connector *connector) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - uint8_t saved_seq1, saved_pi, saved_rpc1, saved_cr_mode; - uint8_t saved_palette0[3], saved_palette_mask; - uint32_t saved_rtest_ctrl, saved_rgen_ctrl; - int i; - uint8_t blue; - bool sense = true; - - /* - * for this detection to work, there needs to be a mode set up on the - * CRTC. this is presumed to be the case - */ - - if (nv_two_heads(dev)) - /* only implemented for head A for now */ - NVSetOwner(dev, 0); - - saved_cr_mode = NVReadVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX); - NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode | 0x80); - - saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX); - NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20); - - saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL); - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, - saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); - - msleep(10); - - saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX); - NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, - saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT))); - saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX); - NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0); - - nv_wr08(device, NV_PRMDIO_READ_MODE_ADDRESS, 0x0); - for (i = 0; i < 3; i++) - saved_palette0[i] = nv_rd08(device, NV_PRMDIO_PALETTE_DATA); - saved_palette_mask = nv_rd08(device, NV_PRMDIO_PIXEL_MASK); - nv_wr08(device, NV_PRMDIO_PIXEL_MASK, 0); - - saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL); - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, - (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | - NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) | - NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON); - - blue = 8; /* start of test range */ - - do { - bool sense_pair[2]; - - nv_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); - nv_wr08(device, NV_PRMDIO_PALETTE_DATA, 0); - nv_wr08(device, NV_PRMDIO_PALETTE_DATA, 0); - /* testing blue won't find monochrome monitors. I don't care */ - nv_wr08(device, NV_PRMDIO_PALETTE_DATA, blue); - - i = 0; - /* take sample pairs until both samples in the pair agree */ - do { - if (sample_load_twice(dev, sense_pair)) - goto out; - } while ((sense_pair[0] != sense_pair[1]) && - ++i < MAX_SAMPLE_PAIRS); - - if (i == MAX_SAMPLE_PAIRS) - /* too much oscillation defaults to LO */ - sense = false; - else - sense = sense_pair[0]; - - /* - * if sense goes LO before blue ramps to 0x18, monitor is not connected. - * ergo, if blue gets to 0x18, monitor must be connected - */ - } while (++blue < 0x18 && sense); - -out: - nv_wr08(device, NV_PRMDIO_PIXEL_MASK, saved_palette_mask); - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl); - nv_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); - for (i = 0; i < 3; i++) - nv_wr08(device, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]); - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl); - NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi); - NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1); - NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1); - NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode); - - if (blue == 0x18) { - NV_DEBUG(drm, "Load detected on head A\n"); - return connector_status_connected; - } - - return connector_status_disconnected; -} - -uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_gpio *gpio = nouveau_gpio(device); - struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; - uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder); - uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, - saved_rtest_ctrl, saved_gpio0 = 0, saved_gpio1 = 0, temp, routput; - int head; - -#define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20) - if (dcb->type == DCB_OUTPUT_TV) { - testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0); - - if (drm->vbios.tvdactestval) - testval = drm->vbios.tvdactestval; - } else { - testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */ - - if (drm->vbios.dactestval) - testval = drm->vbios.dactestval; - } - - saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, - saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); - - saved_powerctrl_2 = nv_rd32(device, NV_PBUS_POWERCTRL_2); - - nv_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff); - if (regoffset == 0x68) { - saved_powerctrl_4 = nv_rd32(device, NV_PBUS_POWERCTRL_4); - nv_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf); - } - - if (gpio) { - saved_gpio1 = gpio->get(gpio, 0, DCB_GPIO_TVDAC1, 0xff); - saved_gpio0 = gpio->get(gpio, 0, DCB_GPIO_TVDAC0, 0xff); - gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, dcb->type == DCB_OUTPUT_TV); - gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, dcb->type == DCB_OUTPUT_TV); - } - - msleep(4); - - saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); - head = (saved_routput & 0x100) >> 8; - - /* if there's a spare crtc, using it will minimise flicker */ - if (!(NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX) & 0xC0)) - head ^= 1; - - /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */ - routput = (saved_routput & 0xfffffece) | head << 8; - - if (nv_device(drm->device)->card_type >= NV_40) { - if (dcb->type == DCB_OUTPUT_TV) - routput |= 0x1a << 16; - else - routput &= ~(0x1a << 16); - } - - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput); - msleep(1); - - temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1); - - NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, - NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval); - temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, - temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); - msleep(5); - - sample = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); - /* do it again just in case it's a residual current */ - sample &= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); - - temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, - temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0); - - /* bios does something more complex for restoring, but I think this is good enough */ - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput); - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl); - if (regoffset == 0x68) - nv_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4); - nv_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2); - - if (gpio) { - gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, saved_gpio1); - gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, saved_gpio0); - } - - return sample; -} - -static enum drm_connector_status -nv17_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) -{ - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; - - if (nv04_dac_in_use(encoder)) - return connector_status_disconnected; - - if (nv17_dac_sample_load(encoder) & - NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI) { - NV_DEBUG(drm, "Load detected on output %c\n", - '@' + ffs(dcb->or)); - return connector_status_connected; - } else { - return connector_status_disconnected; - } -} - -static bool nv04_dac_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - if (nv04_dac_in_use(encoder)) - return false; - - return true; -} - -static void nv04_dac_prepare(struct drm_encoder *encoder) -{ - struct drm_encoder_helper_funcs *helper = encoder->helper_private; - struct drm_device *dev = encoder->dev; - int head = nouveau_crtc(encoder->crtc)->index; - - helper->dpms(encoder, DRM_MODE_DPMS_OFF); - - nv04_dfp_disable(dev, head); -} - -static void nv04_dac_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - int head = nouveau_crtc(encoder->crtc)->index; - - if (nv_gf4_disp_arch(dev)) { - struct drm_encoder *rebind; - uint32_t dac_offset = nv04_dac_output_offset(encoder); - uint32_t otherdac; - - /* bit 16-19 are bits that are set on some G70 cards, - * but don't seem to have much effect */ - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, - head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK); - /* force any other vga encoders to bind to the other crtc */ - list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) { - if (rebind == encoder - || nouveau_encoder(rebind)->dcb->type != DCB_OUTPUT_ANALOG) - continue; - - dac_offset = nv04_dac_output_offset(rebind); - otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset); - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, - (otherdac & ~0x0100) | (head ^ 1) << 8); - } - } - - /* This could use refinement for flatpanels, but it should work this way */ - if (nv_device(drm->device)->chipset < 0x44) - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); - else - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); -} - -static void nv04_dac_commit(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); - struct drm_encoder_helper_funcs *helper = encoder->helper_private; - - helper->dpms(encoder, DRM_MODE_DPMS_ON); - - NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n", - drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), - nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); -} - -void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable) -{ - struct drm_device *dev = encoder->dev; - struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; - - if (nv_gf4_disp_arch(dev)) { - uint32_t *dac_users = &nv04_display(dev)->dac_users[ffs(dcb->or) - 1]; - int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder); - uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off); - - if (enable) { - *dac_users |= 1 << dcb->index; - NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK); - - } else { - *dac_users &= ~(1 << dcb->index); - if (!*dac_users) - NVWriteRAMDAC(dev, 0, dacclk_off, - dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK); - } - } -} - -/* Check if the DAC corresponding to 'encoder' is being used by - * someone else. */ -bool nv04_dac_in_use(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; - - return nv_gf4_disp_arch(encoder->dev) && - (nv04_display(dev)->dac_users[ffs(dcb->or) - 1] & ~(1 << dcb->index)); -} - -static void nv04_dac_dpms(struct drm_encoder *encoder, int mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - - if (nv_encoder->last_dpms == mode) - return; - nv_encoder->last_dpms = mode; - - NV_DEBUG(drm, "Setting dpms mode %d on vga encoder (output %d)\n", - mode, nv_encoder->dcb->index); - - nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); -} - -static void nv04_dac_save(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - - if (nv_gf4_disp_arch(dev)) - nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + - nv04_dac_output_offset(encoder)); -} - -static void nv04_dac_restore(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - - if (nv_gf4_disp_arch(dev)) - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder), - nv_encoder->restore.output); - - nv_encoder->last_dpms = NV_DPMS_CLEARED; -} - -static void nv04_dac_destroy(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - - drm_encoder_cleanup(encoder); - kfree(nv_encoder); -} - -static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = { - .dpms = nv04_dac_dpms, - .save = nv04_dac_save, - .restore = nv04_dac_restore, - .mode_fixup = nv04_dac_mode_fixup, - .prepare = nv04_dac_prepare, - .commit = nv04_dac_commit, - .mode_set = nv04_dac_mode_set, - .detect = nv04_dac_detect -}; - -static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = { - .dpms = nv04_dac_dpms, - .save = nv04_dac_save, - .restore = nv04_dac_restore, - .mode_fixup = nv04_dac_mode_fixup, - .prepare = nv04_dac_prepare, - .commit = nv04_dac_commit, - .mode_set = nv04_dac_mode_set, - .detect = nv17_dac_detect -}; - -static const struct drm_encoder_funcs nv04_dac_funcs = { - .destroy = nv04_dac_destroy, -}; - -int -nv04_dac_create(struct drm_connector *connector, struct dcb_output *entry) -{ - const struct drm_encoder_helper_funcs *helper; - struct nouveau_encoder *nv_encoder = NULL; - struct drm_device *dev = connector->dev; - struct drm_encoder *encoder; - - nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); - if (!nv_encoder) - return -ENOMEM; - - encoder = to_drm_encoder(nv_encoder); - - nv_encoder->dcb = entry; - nv_encoder->or = ffs(entry->or) - 1; - - if (nv_gf4_disp_arch(dev)) - helper = &nv17_dac_helper_funcs; - else - helper = &nv04_dac_helper_funcs; - - drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC); - drm_encoder_helper_add(encoder, helper); - - encoder->possible_crtcs = entry->heads; - encoder->possible_clones = 0; - - drm_mode_connector_attach_encoder(connector, encoder); - return 0; -} diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c deleted file mode 100644 index 7e24cdf..0000000 --- a/drivers/gpu/drm/nouveau/nv04_dfp.c +++ /dev/null @@ -1,720 +0,0 @@ -/* - * Copyright 2003 NVIDIA, Corporation - * Copyright 2006 Dave Airlie - * Copyright 2007 Maarten Maathuis - * Copyright 2007-2009 Stuart Bennett - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include -#include - -#include "nouveau_drm.h" -#include "nouveau_reg.h" -#include "nouveau_encoder.h" -#include "nouveau_connector.h" -#include "nouveau_crtc.h" -#include "nouveau_hw.h" -#include "nvreg.h" - -#include - -#include - -#define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \ - NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \ - NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS) -#define FP_TG_CONTROL_OFF (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE | \ - NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE | \ - NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE) - -static inline bool is_fpc_off(uint32_t fpc) -{ - return ((fpc & (FP_TG_CONTROL_ON | FP_TG_CONTROL_OFF)) == - FP_TG_CONTROL_OFF); -} - -int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_output *dcbent) -{ - /* special case of nv_read_tmds to find crtc associated with an output. - * this does not give a correct answer for off-chip dvi, but there's no - * use for such an answer anyway - */ - int ramdac = (dcbent->or & DCB_OUTPUT_C) >> 2; - - NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL, - NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | 0x4); - return ((NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA) & 0x8) >> 3) ^ ramdac; -} - -void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_output *dcbent, - int head, bool dl) -{ - /* The BIOS scripts don't do this for us, sadly - * Luckily we do know the values ;-) - * - * head < 0 indicates we wish to force a setting with the overrideval - * (for VT restore etc.) - */ - - int ramdac = (dcbent->or & DCB_OUTPUT_C) >> 2; - uint8_t tmds04 = 0x80; - - if (head != ramdac) - tmds04 = 0x88; - - if (dcbent->type == DCB_OUTPUT_LVDS) - tmds04 |= 0x01; - - nv_write_tmds(dev, dcbent->or, 0, 0x04, tmds04); - - if (dl) /* dual link */ - nv_write_tmds(dev, dcbent->or, 1, 0x04, tmds04 ^ 0x08); -} - -void nv04_dfp_disable(struct drm_device *dev, int head) -{ - struct nv04_crtc_reg *crtcstate = nv04_display(dev)->mode_reg.crtc_reg; - - if (NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL) & - FP_TG_CONTROL_ON) { - /* digital remnants must be cleaned before new crtc - * values programmed. delay is time for the vga stuff - * to realise it's in control again - */ - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, - FP_TG_CONTROL_OFF); - msleep(50); - } - /* don't inadvertently turn it on when state written later */ - crtcstate[head].fp_control = FP_TG_CONTROL_OFF; - crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] &= - ~NV_CIO_CRE_LCD_ROUTE_MASK; -} - -void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode) -{ - struct drm_device *dev = encoder->dev; - struct drm_crtc *crtc; - struct nouveau_crtc *nv_crtc; - uint32_t *fpc; - - if (mode == DRM_MODE_DPMS_ON) { - nv_crtc = nouveau_crtc(encoder->crtc); - fpc = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index].fp_control; - - if (is_fpc_off(*fpc)) { - /* using saved value is ok, as (is_digital && dpms_on && - * fp_control==OFF) is (at present) *only* true when - * fpc's most recent change was by below "off" code - */ - *fpc = nv_crtc->dpms_saved_fp_control; - } - - nv_crtc->fp_users |= 1 << nouveau_encoder(encoder)->dcb->index; - NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_FP_TG_CONTROL, *fpc); - } else { - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - nv_crtc = nouveau_crtc(crtc); - fpc = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index].fp_control; - - nv_crtc->fp_users &= ~(1 << nouveau_encoder(encoder)->dcb->index); - if (!is_fpc_off(*fpc) && !nv_crtc->fp_users) { - nv_crtc->dpms_saved_fp_control = *fpc; - /* cut the FP output */ - *fpc &= ~FP_TG_CONTROL_ON; - *fpc |= FP_TG_CONTROL_OFF; - NVWriteRAMDAC(dev, nv_crtc->index, - NV_PRAMDAC_FP_TG_CONTROL, *fpc); - } - } - } -} - -static struct drm_encoder *get_tmds_slave(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; - struct drm_encoder *slave; - - if (dcb->type != DCB_OUTPUT_TMDS || dcb->location == DCB_LOC_ON_CHIP) - return NULL; - - /* Some BIOSes (e.g. the one in a Quadro FX1000) report several - * TMDS transmitters at the same I2C address, in the same I2C - * bus. This can still work because in that case one of them is - * always hard-wired to a reasonable configuration using straps, - * and the other one needs to be programmed. - * - * I don't think there's a way to know which is which, even the - * blob programs the one exposed via I2C for *both* heads, so - * let's do the same. - */ - list_for_each_entry(slave, &dev->mode_config.encoder_list, head) { - struct dcb_output *slave_dcb = nouveau_encoder(slave)->dcb; - - if (slave_dcb->type == DCB_OUTPUT_TMDS && get_slave_funcs(slave) && - slave_dcb->tmdsconf.slave_addr == dcb->tmdsconf.slave_addr) - return slave; - } - - return NULL; -} - -static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder); - - if (!nv_connector->native_mode || - nv_connector->scaling_mode == DRM_MODE_SCALE_NONE || - mode->hdisplay > nv_connector->native_mode->hdisplay || - mode->vdisplay > nv_connector->native_mode->vdisplay) { - nv_encoder->mode = *adjusted_mode; - - } else { - nv_encoder->mode = *nv_connector->native_mode; - adjusted_mode->clock = nv_connector->native_mode->clock; - } - - return true; -} - -static void nv04_dfp_prepare_sel_clk(struct drm_device *dev, - struct nouveau_encoder *nv_encoder, int head) -{ - struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; - uint32_t bits1618 = nv_encoder->dcb->or & DCB_OUTPUT_A ? 0x10000 : 0x40000; - - if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP) - return; - - /* SEL_CLK is only used on the primary ramdac - * It toggles spread spectrum PLL output and sets the bindings of PLLs - * to heads on digital outputs - */ - if (head) - state->sel_clk |= bits1618; - else - state->sel_clk &= ~bits1618; - - /* nv30: - * bit 0 NVClk spread spectrum on/off - * bit 2 MemClk spread spectrum on/off - * bit 4 PixClk1 spread spectrum on/off toggle - * bit 6 PixClk2 spread spectrum on/off toggle - * - * nv40 (observations from bios behaviour and mmio traces): - * bits 4&6 as for nv30 - * bits 5&7 head dependent as for bits 4&6, but do not appear with 4&6; - * maybe a different spread mode - * bits 8&10 seen on dual-link dvi outputs, purpose unknown (set by POST scripts) - * The logic behind turning spread spectrum on/off in the first place, - * and which bit-pair to use, is unclear on nv40 (for earlier cards, the fp table - * entry has the necessary info) - */ - if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS && nv04_display(dev)->saved_reg.sel_clk & 0xf0) { - int shift = (nv04_display(dev)->saved_reg.sel_clk & 0x50) ? 0 : 1; - - state->sel_clk &= ~0xf0; - state->sel_clk |= (head ? 0x40 : 0x10) << shift; - } -} - -static void nv04_dfp_prepare(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_encoder_helper_funcs *helper = encoder->helper_private; - struct drm_device *dev = encoder->dev; - int head = nouveau_crtc(encoder->crtc)->index; - struct nv04_crtc_reg *crtcstate = nv04_display(dev)->mode_reg.crtc_reg; - uint8_t *cr_lcd = &crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX]; - uint8_t *cr_lcd_oth = &crtcstate[head ^ 1].CRTC[NV_CIO_CRE_LCD__INDEX]; - - helper->dpms(encoder, DRM_MODE_DPMS_OFF); - - nv04_dfp_prepare_sel_clk(dev, nv_encoder, head); - - *cr_lcd = (*cr_lcd & ~NV_CIO_CRE_LCD_ROUTE_MASK) | 0x3; - - if (nv_two_heads(dev)) { - if (nv_encoder->dcb->location == DCB_LOC_ON_CHIP) - *cr_lcd |= head ? 0x0 : 0x8; - else { - *cr_lcd |= (nv_encoder->dcb->or << 4) & 0x30; - if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) - *cr_lcd |= 0x30; - if ((*cr_lcd & 0x30) == (*cr_lcd_oth & 0x30)) { - /* avoid being connected to both crtcs */ - *cr_lcd_oth &= ~0x30; - NVWriteVgaCrtc(dev, head ^ 1, - NV_CIO_CRE_LCD__INDEX, - *cr_lcd_oth); - } - } - } -} - - -static void nv04_dfp_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); - struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; - struct nv04_crtc_reg *savep = &nv04_display(dev)->saved_reg.crtc_reg[nv_crtc->index]; - struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc); - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_display_mode *output_mode = &nv_encoder->mode; - struct drm_connector *connector = &nv_connector->base; - uint32_t mode_ratio, panel_ratio; - - NV_DEBUG(drm, "Output mode on CRTC %d:\n", nv_crtc->index); - drm_mode_debug_printmodeline(output_mode); - - /* Initialize the FP registers in this CRTC. */ - regp->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1; - regp->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1; - if (!nv_gf4_disp_arch(dev) || - (output_mode->hsync_start - output_mode->hdisplay) >= - drm->vbios.digital_min_front_porch) - regp->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay; - else - regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start - drm->vbios.digital_min_front_porch - 1; - regp->fp_horiz_regs[FP_SYNC_START] = output_mode->hsync_start - 1; - regp->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1; - regp->fp_horiz_regs[FP_VALID_START] = output_mode->hskew; - regp->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - 1; - - regp->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1; - regp->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1; - regp->fp_vert_regs[FP_CRTC] = output_mode->vtotal - 5 - 1; - regp->fp_vert_regs[FP_SYNC_START] = output_mode->vsync_start - 1; - regp->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1; - regp->fp_vert_regs[FP_VALID_START] = 0; - regp->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - 1; - - /* bit26: a bit seen on some g7x, no as yet discernable purpose */ - regp->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | - (savep->fp_control & (1 << 26 | NV_PRAMDAC_FP_TG_CONTROL_READ_PROG)); - /* Deal with vsync/hsync polarity */ - /* LVDS screens do set this, but modes with +ve syncs are very rare */ - if (output_mode->flags & DRM_MODE_FLAG_PVSYNC) - regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS; - if (output_mode->flags & DRM_MODE_FLAG_PHSYNC) - regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS; - /* panel scaling first, as native would get set otherwise */ - if (nv_connector->scaling_mode == DRM_MODE_SCALE_NONE || - nv_connector->scaling_mode == DRM_MODE_SCALE_CENTER) /* panel handles it */ - regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER; - else if (adjusted_mode->hdisplay == output_mode->hdisplay && - adjusted_mode->vdisplay == output_mode->vdisplay) /* native mode */ - regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE; - else /* gpu needs to scale */ - regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE; - if (nv_rd32(device, NV_PEXTDEV_BOOT_0) & NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT) - regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12; - if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && - output_mode->clock > 165000) - regp->fp_control |= (2 << 24); - if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) { - bool duallink = false, dummy; - if (nv_connector->edid && - nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { - duallink = (((u8 *)nv_connector->edid)[121] == 2); - } else { - nouveau_bios_parse_lvds_table(dev, output_mode->clock, - &duallink, &dummy); - } - - if (duallink) - regp->fp_control |= (8 << 28); - } else - if (output_mode->clock > 165000) - regp->fp_control |= (8 << 28); - - regp->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND | - NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND | - NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR | - NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR | - NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED | - NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE | - NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE; - - /* We want automatic scaling */ - regp->fp_debug_1 = 0; - /* This can override HTOTAL and VTOTAL */ - regp->fp_debug_2 = 0; - - /* Use 20.12 fixed point format to avoid floats */ - mode_ratio = (1 << 12) * adjusted_mode->hdisplay / adjusted_mode->vdisplay; - panel_ratio = (1 << 12) * output_mode->hdisplay / output_mode->vdisplay; - /* if ratios are equal, SCALE_ASPECT will automatically (and correctly) - * get treated the same as SCALE_FULLSCREEN */ - if (nv_connector->scaling_mode == DRM_MODE_SCALE_ASPECT && - mode_ratio != panel_ratio) { - uint32_t diff, scale; - bool divide_by_2 = nv_gf4_disp_arch(dev); - - if (mode_ratio < panel_ratio) { - /* vertical needs to expand to glass size (automatic) - * horizontal needs to be scaled at vertical scale factor - * to maintain aspect */ - - scale = (1 << 12) * adjusted_mode->vdisplay / output_mode->vdisplay; - regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE | - XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE); - - /* restrict area of screen used, horizontally */ - diff = output_mode->hdisplay - - output_mode->vdisplay * mode_ratio / (1 << 12); - regp->fp_horiz_regs[FP_VALID_START] += diff / 2; - regp->fp_horiz_regs[FP_VALID_END] -= diff / 2; - } - - if (mode_ratio > panel_ratio) { - /* horizontal needs to expand to glass size (automatic) - * vertical needs to be scaled at horizontal scale factor - * to maintain aspect */ - - scale = (1 << 12) * adjusted_mode->hdisplay / output_mode->hdisplay; - regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE | - XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE); - - /* restrict area of screen used, vertically */ - diff = output_mode->vdisplay - - (1 << 12) * output_mode->hdisplay / mode_ratio; - regp->fp_vert_regs[FP_VALID_START] += diff / 2; - regp->fp_vert_regs[FP_VALID_END] -= diff / 2; - } - } - - /* Output property. */ - if ((nv_connector->dithering_mode == DITHERING_MODE_ON) || - (nv_connector->dithering_mode == DITHERING_MODE_AUTO && - encoder->crtc->fb->depth > connector->display_info.bpc * 3)) { - if (nv_device(drm->device)->chipset == 0x11) - regp->dither = savep->dither | 0x00010000; - else { - int i; - regp->dither = savep->dither | 0x00000001; - for (i = 0; i < 3; i++) { - regp->dither_regs[i] = 0xe4e4e4e4; - regp->dither_regs[i + 3] = 0x44444444; - } - } - } else { - if (nv_device(drm->device)->chipset != 0x11) { - /* reset them */ - int i; - for (i = 0; i < 3; i++) { - regp->dither_regs[i] = savep->dither_regs[i]; - regp->dither_regs[i + 3] = savep->dither_regs[i + 3]; - } - } - regp->dither = savep->dither; - } - - regp->fp_margin_color = 0; -} - -static void nv04_dfp_commit(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct drm_encoder_helper_funcs *helper = encoder->helper_private; - struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct dcb_output *dcbe = nv_encoder->dcb; - int head = nouveau_crtc(encoder->crtc)->index; - struct drm_encoder *slave_encoder; - - if (dcbe->type == DCB_OUTPUT_TMDS) - run_tmds_table(dev, dcbe, head, nv_encoder->mode.clock); - else if (dcbe->type == DCB_OUTPUT_LVDS) - call_lvds_script(dev, dcbe, head, LVDS_RESET, nv_encoder->mode.clock); - - /* update fp_control state for any changes made by scripts, - * so correct value is written at DPMS on */ - nv04_display(dev)->mode_reg.crtc_reg[head].fp_control = - NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL); - - /* This could use refinement for flatpanels, but it should work this way */ - if (nv_device(drm->device)->chipset < 0x44) - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); - else - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); - - /* Init external transmitters */ - slave_encoder = get_tmds_slave(encoder); - if (slave_encoder) - get_slave_funcs(slave_encoder)->mode_set( - slave_encoder, &nv_encoder->mode, &nv_encoder->mode); - - helper->dpms(encoder, DRM_MODE_DPMS_ON); - - NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n", - drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), - nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); -} - -static void nv04_dfp_update_backlight(struct drm_encoder *encoder, int mode) -{ -#ifdef __powerpc__ - struct drm_device *dev = encoder->dev; - struct nouveau_device *device = nouveau_dev(dev); - - /* BIOS scripts usually take care of the backlight, thanks - * Apple for your consistency. - */ - if (dev->pci_device == 0x0174 || dev->pci_device == 0x0179 || - dev->pci_device == 0x0189 || dev->pci_device == 0x0329) { - if (mode == DRM_MODE_DPMS_ON) { - nv_mask(device, NV_PBUS_DEBUG_DUALHEAD_CTL, 0, 1 << 31); - nv_mask(device, NV_PCRTC_GPIO_EXT, 3, 1); - } else { - nv_mask(device, NV_PBUS_DEBUG_DUALHEAD_CTL, 1 << 31, 0); - nv_mask(device, NV_PCRTC_GPIO_EXT, 3, 0); - } - } -#endif -} - -static inline bool is_powersaving_dpms(int mode) -{ - return mode != DRM_MODE_DPMS_ON && mode != NV_DPMS_CLEARED; -} - -static void nv04_lvds_dpms(struct drm_encoder *encoder, int mode) -{ - struct drm_device *dev = encoder->dev; - struct drm_crtc *crtc = encoder->crtc; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - bool was_powersaving = is_powersaving_dpms(nv_encoder->last_dpms); - - if (nv_encoder->last_dpms == mode) - return; - nv_encoder->last_dpms = mode; - - NV_DEBUG(drm, "Setting dpms mode %d on lvds encoder (output %d)\n", - mode, nv_encoder->dcb->index); - - if (was_powersaving && is_powersaving_dpms(mode)) - return; - - if (nv_encoder->dcb->lvdsconf.use_power_scripts) { - /* when removing an output, crtc may not be set, but PANEL_OFF - * must still be run - */ - int head = crtc ? nouveau_crtc(crtc)->index : - nv04_dfp_get_bound_head(dev, nv_encoder->dcb); - - if (mode == DRM_MODE_DPMS_ON) { - call_lvds_script(dev, nv_encoder->dcb, head, - LVDS_PANEL_ON, nv_encoder->mode.clock); - } else - /* pxclk of 0 is fine for PANEL_OFF, and for a - * disconnected LVDS encoder there is no native_mode - */ - call_lvds_script(dev, nv_encoder->dcb, head, - LVDS_PANEL_OFF, 0); - } - - nv04_dfp_update_backlight(encoder, mode); - nv04_dfp_update_fp_control(encoder, mode); - - if (mode == DRM_MODE_DPMS_ON) - nv04_dfp_prepare_sel_clk(dev, nv_encoder, nouveau_crtc(crtc)->index); - else { - nv04_display(dev)->mode_reg.sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK); - nv04_display(dev)->mode_reg.sel_clk &= ~0xf0; - } - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, nv04_display(dev)->mode_reg.sel_clk); -} - -static void nv04_tmds_dpms(struct drm_encoder *encoder, int mode) -{ - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - - if (nv_encoder->last_dpms == mode) - return; - nv_encoder->last_dpms = mode; - - NV_DEBUG(drm, "Setting dpms mode %d on tmds encoder (output %d)\n", - mode, nv_encoder->dcb->index); - - nv04_dfp_update_backlight(encoder, mode); - nv04_dfp_update_fp_control(encoder, mode); -} - -static void nv04_dfp_save(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - - if (nv_two_heads(dev)) - nv_encoder->restore.head = - nv04_dfp_get_bound_head(dev, nv_encoder->dcb); -} - -static void nv04_dfp_restore(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - int head = nv_encoder->restore.head; - - if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) { - struct nouveau_connector *connector = - nouveau_encoder_connector_get(nv_encoder); - - if (connector && connector->native_mode) - call_lvds_script(dev, nv_encoder->dcb, head, - LVDS_PANEL_ON, - connector->native_mode->clock); - - } else if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS) { - int clock = nouveau_hw_pllvals_to_clk - (&nv04_display(dev)->saved_reg.crtc_reg[head].pllvals); - - run_tmds_table(dev, nv_encoder->dcb, head, clock); - } - - nv_encoder->last_dpms = NV_DPMS_CLEARED; -} - -static void nv04_dfp_destroy(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - - if (get_slave_funcs(encoder)) - get_slave_funcs(encoder)->destroy(encoder); - - drm_encoder_cleanup(encoder); - kfree(nv_encoder); -} - -static void nv04_tmds_slave_init(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_i2c *i2c = nouveau_i2c(drm->device); - struct nouveau_i2c_port *port = i2c->find(i2c, 2); - struct i2c_board_info info[] = { - { - .type = "sil164", - .addr = (dcb->tmdsconf.slave_addr == 0x7 ? 0x3a : 0x38), - .platform_data = &(struct sil164_encoder_params) { - SIL164_INPUT_EDGE_RISING - } - }, - { } - }; - int type; - - if (!nv_gf4_disp_arch(dev) || !port || - get_tmds_slave(encoder)) - return; - - type = i2c->identify(i2c, 2, "TMDS transmitter", info, NULL); - if (type < 0) - return; - - drm_i2c_encoder_init(dev, to_encoder_slave(encoder), - &port->adapter, &info[type]); -} - -static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = { - .dpms = nv04_lvds_dpms, - .save = nv04_dfp_save, - .restore = nv04_dfp_restore, - .mode_fixup = nv04_dfp_mode_fixup, - .prepare = nv04_dfp_prepare, - .commit = nv04_dfp_commit, - .mode_set = nv04_dfp_mode_set, - .detect = NULL, -}; - -static const struct drm_encoder_helper_funcs nv04_tmds_helper_funcs = { - .dpms = nv04_tmds_dpms, - .save = nv04_dfp_save, - .restore = nv04_dfp_restore, - .mode_fixup = nv04_dfp_mode_fixup, - .prepare = nv04_dfp_prepare, - .commit = nv04_dfp_commit, - .mode_set = nv04_dfp_mode_set, - .detect = NULL, -}; - -static const struct drm_encoder_funcs nv04_dfp_funcs = { - .destroy = nv04_dfp_destroy, -}; - -int -nv04_dfp_create(struct drm_connector *connector, struct dcb_output *entry) -{ - const struct drm_encoder_helper_funcs *helper; - struct nouveau_encoder *nv_encoder = NULL; - struct drm_encoder *encoder; - int type; - - switch (entry->type) { - case DCB_OUTPUT_TMDS: - type = DRM_MODE_ENCODER_TMDS; - helper = &nv04_tmds_helper_funcs; - break; - case DCB_OUTPUT_LVDS: - type = DRM_MODE_ENCODER_LVDS; - helper = &nv04_lvds_helper_funcs; - break; - default: - return -EINVAL; - } - - nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); - if (!nv_encoder) - return -ENOMEM; - - encoder = to_drm_encoder(nv_encoder); - - nv_encoder->dcb = entry; - nv_encoder->or = ffs(entry->or) - 1; - - drm_encoder_init(connector->dev, encoder, &nv04_dfp_funcs, type); - drm_encoder_helper_add(encoder, helper); - - encoder->possible_crtcs = entry->heads; - encoder->possible_clones = 0; - - if (entry->type == DCB_OUTPUT_TMDS && - entry->location != DCB_LOC_ON_CHIP) - nv04_tmds_slave_init(encoder); - - drm_mode_connector_attach_encoder(connector, encoder); - return 0; -} diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c deleted file mode 100644 index ad48444..0000000 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2009 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Author: Ben Skeggs - */ - -#include -#include - -#include -#include - -#include "nouveau_drm.h" -#include "nouveau_reg.h" -#include "nouveau_hw.h" -#include "nouveau_encoder.h" -#include "nouveau_connector.h" - -#include - -int -nv04_display_early_init(struct drm_device *dev) -{ - /* ensure vblank interrupts are off, they can't be enabled until - * drm_vblank has been initialised - */ - NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0); - if (nv_two_heads(dev)) - NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0); - - return 0; -} - -void -nv04_display_late_takedown(struct drm_device *dev) -{ -} - -int -nv04_display_create(struct drm_device *dev) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_i2c *i2c = nouveau_i2c(drm->device); - struct dcb_table *dcb = &drm->vbios.dcb; - struct drm_connector *connector, *ct; - struct drm_encoder *encoder; - struct drm_crtc *crtc; - struct nv04_display *disp; - int i, ret; - - disp = kzalloc(sizeof(*disp), GFP_KERNEL); - if (!disp) - return -ENOMEM; - - nouveau_display(dev)->priv = disp; - nouveau_display(dev)->dtor = nv04_display_destroy; - nouveau_display(dev)->init = nv04_display_init; - nouveau_display(dev)->fini = nv04_display_fini; - - nouveau_hw_save_vga_fonts(dev, 1); - - ret = nouveau_object_new(nv_object(drm), NVDRM_DEVICE, 0xd1500000, - NV04_DISP_CLASS, NULL, 0, &disp->core); - if (ret) - return ret; - - nv04_crtc_create(dev, 0); - if (nv_two_heads(dev)) - nv04_crtc_create(dev, 1); - - for (i = 0; i < dcb->entries; i++) { - struct dcb_output *dcbent = &dcb->entry[i]; - - connector = nouveau_connector_create(dev, dcbent->connector); - if (IS_ERR(connector)) - continue; - - switch (dcbent->type) { - case DCB_OUTPUT_ANALOG: - ret = nv04_dac_create(connector, dcbent); - break; - case DCB_OUTPUT_LVDS: - case DCB_OUTPUT_TMDS: - ret = nv04_dfp_create(connector, dcbent); - break; - case DCB_OUTPUT_TV: - if (dcbent->location == DCB_LOC_ON_CHIP) - ret = nv17_tv_create(connector, dcbent); - else - ret = nv04_tv_create(connector, dcbent); - break; - default: - NV_WARN(drm, "DCB type %d not known\n", dcbent->type); - continue; - } - - if (ret) - continue; - } - - list_for_each_entry_safe(connector, ct, - &dev->mode_config.connector_list, head) { - if (!connector->encoder_ids[0]) { - NV_WARN(drm, "%s has no encoders, removing\n", - drm_get_connector_name(connector)); - connector->funcs->destroy(connector); - } - } - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - nv_encoder->i2c = i2c->find(i2c, nv_encoder->dcb->i2c_index); - } - - /* Save previous state */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - crtc->funcs->save(crtc); - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct drm_encoder_helper_funcs *func = encoder->helper_private; - - func->save(encoder); - } - - return 0; -} - -void -nv04_display_destroy(struct drm_device *dev) -{ - struct nv04_display *disp = nv04_display(dev); - struct drm_encoder *encoder; - struct drm_crtc *crtc; - - /* Turn every CRTC off. */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct drm_mode_set modeset = { - .crtc = crtc, - }; - - drm_mode_set_config_internal(&modeset); - } - - /* Restore state */ - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct drm_encoder_helper_funcs *func = encoder->helper_private; - - func->restore(encoder); - } - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - crtc->funcs->restore(crtc); - - nouveau_hw_save_vga_fonts(dev, 0); - - nouveau_display(dev)->priv = NULL; - kfree(disp); -} - -int -nv04_display_init(struct drm_device *dev) -{ - struct drm_encoder *encoder; - struct drm_crtc *crtc; - - /* meh.. modeset apparently doesn't setup all the regs and depends - * on pre-existing state, for now load the state of the card *before* - * nouveau was loaded, and then do a modeset. - * - * best thing to do probably is to make save/restore routines not - * save/restore "pre-load" state, but more general so we can save - * on suspend too. - */ - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct drm_encoder_helper_funcs *func = encoder->helper_private; - - func->restore(encoder); - } - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - crtc->funcs->restore(crtc); - - return 0; -} - -void -nv04_display_fini(struct drm_device *dev) -{ - /* disable vblank interrupts */ - NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0); - if (nv_two_heads(dev)) - NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0); -} diff --git a/drivers/gpu/drm/nouveau/nv04_display.h b/drivers/gpu/drm/nouveau/nv04_display.h deleted file mode 100644 index a0a031d..0000000 --- a/drivers/gpu/drm/nouveau/nv04_display.h +++ /dev/null @@ -1,185 +0,0 @@ -#ifndef __NV04_DISPLAY_H__ -#define __NV04_DISPLAY_H__ - -#include - -#include "nouveau_display.h" - -enum nv04_fp_display_regs { - FP_DISPLAY_END, - FP_TOTAL, - FP_CRTC, - FP_SYNC_START, - FP_SYNC_END, - FP_VALID_START, - FP_VALID_END -}; - -struct nv04_crtc_reg { - unsigned char MiscOutReg; - uint8_t CRTC[0xa0]; - uint8_t CR58[0x10]; - uint8_t Sequencer[5]; - uint8_t Graphics[9]; - uint8_t Attribute[21]; - unsigned char DAC[768]; - - /* PCRTC regs */ - uint32_t fb_start; - uint32_t crtc_cfg; - uint32_t cursor_cfg; - uint32_t gpio_ext; - uint32_t crtc_830; - uint32_t crtc_834; - uint32_t crtc_850; - uint32_t crtc_eng_ctrl; - - /* PRAMDAC regs */ - uint32_t nv10_cursync; - struct nouveau_pll_vals pllvals; - uint32_t ramdac_gen_ctrl; - uint32_t ramdac_630; - uint32_t ramdac_634; - uint32_t tv_setup; - uint32_t tv_vtotal; - uint32_t tv_vskew; - uint32_t tv_vsync_delay; - uint32_t tv_htotal; - uint32_t tv_hskew; - uint32_t tv_hsync_delay; - uint32_t tv_hsync_delay2; - uint32_t fp_horiz_regs[7]; - uint32_t fp_vert_regs[7]; - uint32_t dither; - uint32_t fp_control; - uint32_t dither_regs[6]; - uint32_t fp_debug_0; - uint32_t fp_debug_1; - uint32_t fp_debug_2; - uint32_t fp_margin_color; - uint32_t ramdac_8c0; - uint32_t ramdac_a20; - uint32_t ramdac_a24; - uint32_t ramdac_a34; - uint32_t ctv_regs[38]; -}; - -struct nv04_output_reg { - uint32_t output; - int head; -}; - -struct nv04_mode_state { - struct nv04_crtc_reg crtc_reg[2]; - uint32_t pllsel; - uint32_t sel_clk; -}; - -struct nv04_display { - struct nv04_mode_state mode_reg; - struct nv04_mode_state saved_reg; - uint32_t saved_vga_font[4][16384]; - uint32_t dac_users[4]; - struct nouveau_object *core; -}; - -static inline struct nv04_display * -nv04_display(struct drm_device *dev) -{ - return nouveau_display(dev)->priv; -} - -/* nv04_display.c */ -int nv04_display_early_init(struct drm_device *); -void nv04_display_late_takedown(struct drm_device *); -int nv04_display_create(struct drm_device *); -void nv04_display_destroy(struct drm_device *); -int nv04_display_init(struct drm_device *); -void nv04_display_fini(struct drm_device *); - -/* nv04_crtc.c */ -int nv04_crtc_create(struct drm_device *, int index); - -/* nv04_dac.c */ -int nv04_dac_create(struct drm_connector *, struct dcb_output *); -uint32_t nv17_dac_sample_load(struct drm_encoder *encoder); -int nv04_dac_output_offset(struct drm_encoder *encoder); -void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable); -bool nv04_dac_in_use(struct drm_encoder *encoder); - -/* nv04_dfp.c */ -int nv04_dfp_create(struct drm_connector *, struct dcb_output *); -int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_output *dcbent); -void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_output *dcbent, - int head, bool dl); -void nv04_dfp_disable(struct drm_device *dev, int head); -void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode); - -/* nv04_tv.c */ -int nv04_tv_identify(struct drm_device *dev, int i2c_index); -int nv04_tv_create(struct drm_connector *, struct dcb_output *); - -/* nv17_tv.c */ -int nv17_tv_create(struct drm_connector *, struct dcb_output *); - -static inline bool -nv_two_heads(struct drm_device *dev) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - const int impl = dev->pci_device & 0x0ff0; - - if (nv_device(drm->device)->card_type >= NV_10 && impl != 0x0100 && - impl != 0x0150 && impl != 0x01a0 && impl != 0x0200) - return true; - - return false; -} - -static inline bool -nv_gf4_disp_arch(struct drm_device *dev) -{ - return nv_two_heads(dev) && (dev->pci_device & 0x0ff0) != 0x0110; -} - -static inline bool -nv_two_reg_pll(struct drm_device *dev) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - const int impl = dev->pci_device & 0x0ff0; - - if (impl == 0x0310 || impl == 0x0340 || nv_device(drm->device)->card_type >= NV_40) - return true; - return false; -} - -static inline bool -nv_match_device(struct drm_device *dev, unsigned device, - unsigned sub_vendor, unsigned sub_device) -{ - return dev->pdev->device == device && - dev->pdev->subsystem_vendor == sub_vendor && - dev->pdev->subsystem_device == sub_device; -} - -#include -#include - -static inline void -nouveau_bios_run_init_table(struct drm_device *dev, u16 table, - struct dcb_output *outp, int crtc) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_bios *bios = nouveau_bios(device); - struct nvbios_init init = { - .subdev = nv_subdev(bios), - .bios = bios, - .offset = table, - .outp = outp, - .crtc = crtc, - .execute = 1, - }; - - nvbios_exec(&init); -} - -#endif diff --git a/drivers/gpu/drm/nouveau/nv04_pm.c b/drivers/gpu/drm/nouveau/nv04_pm.c index 2a0cc9d..27afc0e 100644 --- a/drivers/gpu/drm/nouveau/nv04_pm.c +++ b/drivers/gpu/drm/nouveau/nv04_pm.c @@ -25,7 +25,7 @@ #include #include "nouveau_drm.h" #include "nouveau_reg.h" -#include "nouveau_hw.h" +#include "dispnv04/hw.h" #include "nouveau_pm.h" #include diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c deleted file mode 100644 index 4a69ccd..0000000 --- a/drivers/gpu/drm/nouveau/nv04_tv.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (C) 2009 Francisco Jerez. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include "nouveau_drm.h" -#include "nouveau_reg.h" -#include "nouveau_encoder.h" -#include "nouveau_connector.h" -#include "nouveau_crtc.h" -#include "nouveau_hw.h" -#include - -#include - -#include - -static struct i2c_board_info nv04_tv_encoder_info[] = { - { - I2C_BOARD_INFO("ch7006", 0x75), - .platform_data = &(struct ch7006_encoder_params) { - CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER, - 0, 0, 0, - CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED, - CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC - } - }, - { } -}; - -int nv04_tv_identify(struct drm_device *dev, int i2c_index) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_i2c *i2c = nouveau_i2c(drm->device); - - return i2c->identify(i2c, i2c_index, "TV encoder", - nv04_tv_encoder_info, NULL); -} - - -#define PLLSEL_TV_CRTC1_MASK \ - (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \ - | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1) -#define PLLSEL_TV_CRTC2_MASK \ - (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \ - | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2) - -static void nv04_tv_dpms(struct drm_encoder *encoder, int mode) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; - uint8_t crtc1A; - - NV_DEBUG(drm, "Setting dpms mode %d on TV encoder (output %d)\n", - mode, nv_encoder->dcb->index); - - state->pllsel &= ~(PLLSEL_TV_CRTC1_MASK | PLLSEL_TV_CRTC2_MASK); - - if (mode == DRM_MODE_DPMS_ON) { - int head = nouveau_crtc(encoder->crtc)->index; - crtc1A = NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX); - - state->pllsel |= head ? PLLSEL_TV_CRTC2_MASK : - PLLSEL_TV_CRTC1_MASK; - - /* Inhibit hsync */ - crtc1A |= 0x80; - - NVWriteVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX, crtc1A); - } - - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel); - - get_slave_funcs(encoder)->dpms(encoder, mode); -} - -static void nv04_tv_bind(struct drm_device *dev, int head, bool bind) -{ - struct nv04_crtc_reg *state = &nv04_display(dev)->mode_reg.crtc_reg[head]; - - state->tv_setup = 0; - - if (bind) - state->CRTC[NV_CIO_CRE_49] |= 0x10; - else - state->CRTC[NV_CIO_CRE_49] &= ~0x10; - - NVWriteVgaCrtc(dev, head, NV_CIO_CRE_LCD__INDEX, - state->CRTC[NV_CIO_CRE_LCD__INDEX]); - NVWriteVgaCrtc(dev, head, NV_CIO_CRE_49, - state->CRTC[NV_CIO_CRE_49]); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, - state->tv_setup); -} - -static void nv04_tv_prepare(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - int head = nouveau_crtc(encoder->crtc)->index; - struct drm_encoder_helper_funcs *helper = encoder->helper_private; - - helper->dpms(encoder, DRM_MODE_DPMS_OFF); - - nv04_dfp_disable(dev, head); - - if (nv_two_heads(dev)) - nv04_tv_bind(dev, head ^ 1, false); - - nv04_tv_bind(dev, head, true); -} - -static void nv04_tv_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); - struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; - - regp->tv_htotal = adjusted_mode->htotal; - regp->tv_vtotal = adjusted_mode->vtotal; - - /* These delay the TV signals with respect to the VGA port, - * they might be useful if we ever allow a CRTC to drive - * multiple outputs. - */ - regp->tv_hskew = 1; - regp->tv_hsync_delay = 1; - regp->tv_hsync_delay2 = 64; - regp->tv_vskew = 1; - regp->tv_vsync_delay = 1; - - get_slave_funcs(encoder)->mode_set(encoder, mode, adjusted_mode); -} - -static void nv04_tv_commit(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); - struct drm_encoder_helper_funcs *helper = encoder->helper_private; - - helper->dpms(encoder, DRM_MODE_DPMS_ON); - - NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n", - drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); -} - -static void nv04_tv_destroy(struct drm_encoder *encoder) -{ - get_slave_funcs(encoder)->destroy(encoder); - drm_encoder_cleanup(encoder); - - kfree(encoder->helper_private); - kfree(nouveau_encoder(encoder)); -} - -static const struct drm_encoder_funcs nv04_tv_funcs = { - .destroy = nv04_tv_destroy, -}; - -static const struct drm_encoder_helper_funcs nv04_tv_helper_funcs = { - .dpms = nv04_tv_dpms, - .save = drm_i2c_encoder_save, - .restore = drm_i2c_encoder_restore, - .mode_fixup = drm_i2c_encoder_mode_fixup, - .prepare = nv04_tv_prepare, - .commit = nv04_tv_commit, - .mode_set = nv04_tv_mode_set, - .detect = drm_i2c_encoder_detect, -}; - -int -nv04_tv_create(struct drm_connector *connector, struct dcb_output *entry) -{ - struct nouveau_encoder *nv_encoder; - struct drm_encoder *encoder; - struct drm_device *dev = connector->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_i2c *i2c = nouveau_i2c(drm->device); - struct nouveau_i2c_port *port = i2c->find(i2c, entry->i2c_index); - int type, ret; - - /* Ensure that we can talk to this encoder */ - type = nv04_tv_identify(dev, entry->i2c_index); - if (type < 0) - return type; - - /* Allocate the necessary memory */ - nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); - if (!nv_encoder) - return -ENOMEM; - - /* Initialize the common members */ - encoder = to_drm_encoder(nv_encoder); - - drm_encoder_init(dev, encoder, &nv04_tv_funcs, DRM_MODE_ENCODER_TVDAC); - drm_encoder_helper_add(encoder, &nv04_tv_helper_funcs); - - encoder->possible_crtcs = entry->heads; - encoder->possible_clones = 0; - nv_encoder->dcb = entry; - nv_encoder->or = ffs(entry->or) - 1; - - /* Run the slave-specific initialization */ - ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder), - &port->adapter, &nv04_tv_encoder_info[type]); - if (ret < 0) - goto fail_cleanup; - - /* Attach it to the specified connector. */ - get_slave_funcs(encoder)->create_resources(encoder, connector); - drm_mode_connector_attach_encoder(connector, encoder); - - return 0; - -fail_cleanup: - drm_encoder_cleanup(encoder); - kfree(nv_encoder); - return ret; -} diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c deleted file mode 100644 index 977e42b..0000000 --- a/drivers/gpu/drm/nouveau/nv17_tv.c +++ /dev/null @@ -1,843 +0,0 @@ -/* - * Copyright (C) 2009 Francisco Jerez. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include -#include "nouveau_drm.h" -#include "nouveau_reg.h" -#include "nouveau_encoder.h" -#include "nouveau_connector.h" -#include "nouveau_crtc.h" -#include "nouveau_hw.h" -#include "nv17_tv.h" - -#include - -#include -#include - -MODULE_PARM_DESC(tv_norm, "Default TV norm.\n" - "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, NTSC-M, NTSC-J,\n" - "\t\t\thd480i, hd480p, hd576i, hd576p, hd720p, hd1080i.\n" - "\t\tDefault: PAL\n" - "\t\t*NOTE* Ignored for cards with external TV encoders."); -static char *nouveau_tv_norm; -module_param_named(tv_norm, nouveau_tv_norm, charp, 0400); - -static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_gpio *gpio = nouveau_gpio(drm->device); - uint32_t testval, regoffset = nv04_dac_output_offset(encoder); - uint32_t gpio0, gpio1, fp_htotal, fp_hsync_start, fp_hsync_end, - fp_control, test_ctrl, dacclk, ctv_14, ctv_1c, ctv_6c; - uint32_t sample = 0; - int head; - -#define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20) - testval = RGB_TEST_DATA(0x82, 0xeb, 0x82); - if (drm->vbios.tvdactestval) - testval = drm->vbios.tvdactestval; - - dacclk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); - head = (dacclk & 0x100) >> 8; - - /* Save the previous state. */ - gpio1 = gpio->get(gpio, 0, DCB_GPIO_TVDAC1, 0xff); - gpio0 = gpio->get(gpio, 0, DCB_GPIO_TVDAC0, 0xff); - fp_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL); - fp_hsync_start = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START); - fp_hsync_end = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END); - fp_control = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL); - test_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); - ctv_1c = NVReadRAMDAC(dev, head, 0x680c1c); - ctv_14 = NVReadRAMDAC(dev, head, 0x680c14); - ctv_6c = NVReadRAMDAC(dev, head, 0x680c6c); - - /* Prepare the DAC for load detection. */ - gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, true); - gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, true); - - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, 1343); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, 1047); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, 1183); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, - NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | - NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12 | - NV_PRAMDAC_FP_TG_CONTROL_READ_PROG | - NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | - NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS); - - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, 0); - - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, - (dacclk & ~0xff) | 0x22); - msleep(1); - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, - (dacclk & ~0xff) | 0x21); - - NVWriteRAMDAC(dev, head, 0x680c1c, 1 << 20); - NVWriteRAMDAC(dev, head, 0x680c14, 4 << 16); - - /* Sample pin 0x4 (usually S-video luma). */ - NVWriteRAMDAC(dev, head, 0x680c6c, testval >> 10 & 0x3ff); - msleep(20); - sample |= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset) - & 0x4 << 28; - - /* Sample the remaining pins. */ - NVWriteRAMDAC(dev, head, 0x680c6c, testval & 0x3ff); - msleep(20); - sample |= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset) - & 0xa << 28; - - /* Restore the previous state. */ - NVWriteRAMDAC(dev, head, 0x680c1c, ctv_1c); - NVWriteRAMDAC(dev, head, 0x680c14, ctv_14); - NVWriteRAMDAC(dev, head, 0x680c6c, ctv_6c); - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, dacclk); - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, test_ctrl); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, fp_control); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, fp_hsync_end); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, fp_hsync_start); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, fp_htotal); - gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, gpio1); - gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, gpio0); - - return sample; -} - -static bool -get_tv_detect_quirks(struct drm_device *dev, uint32_t *pin_mask) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_object *device = drm->device; - - /* Zotac FX5200 */ - if (nv_device_match(device, 0x0322, 0x19da, 0x1035) || - nv_device_match(device, 0x0322, 0x19da, 0x2035)) { - *pin_mask = 0xc; - return false; - } - - /* MSI nForce2 IGP */ - if (nv_device_match(device, 0x01f0, 0x1462, 0x5710)) { - *pin_mask = 0xc; - return false; - } - - return true; -} - -static enum drm_connector_status -nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct drm_mode_config *conf = &dev->mode_config; - struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); - struct dcb_output *dcb = tv_enc->base.dcb; - bool reliable = get_tv_detect_quirks(dev, &tv_enc->pin_mask); - - if (nv04_dac_in_use(encoder)) - return connector_status_disconnected; - - if (reliable) { - if (nv_device(drm->device)->chipset == 0x42 || - nv_device(drm->device)->chipset == 0x43) - tv_enc->pin_mask = - nv42_tv_sample_load(encoder) >> 28 & 0xe; - else - tv_enc->pin_mask = - nv17_dac_sample_load(encoder) >> 28 & 0xe; - } - - switch (tv_enc->pin_mask) { - case 0x2: - case 0x4: - tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Composite; - break; - case 0xc: - tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO; - break; - case 0xe: - if (dcb->tvconf.has_component_output) - tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Component; - else - tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SCART; - break; - default: - tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown; - break; - } - - drm_object_property_set_value(&connector->base, - conf->tv_subconnector_property, - tv_enc->subconnector); - - if (!reliable) { - return connector_status_unknown; - } else if (tv_enc->subconnector) { - NV_INFO(drm, "Load detected on output %c\n", - '@' + ffs(dcb->or)); - return connector_status_connected; - } else { - return connector_status_disconnected; - } -} - -static int nv17_tv_get_ld_modes(struct drm_encoder *encoder, - struct drm_connector *connector) -{ - struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); - const struct drm_display_mode *tv_mode; - int n = 0; - - for (tv_mode = nv17_tv_modes; tv_mode->hdisplay; tv_mode++) { - struct drm_display_mode *mode; - - mode = drm_mode_duplicate(encoder->dev, tv_mode); - - mode->clock = tv_norm->tv_enc_mode.vrefresh * - mode->htotal / 1000 * - mode->vtotal / 1000; - - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) - mode->clock *= 2; - - if (mode->hdisplay == tv_norm->tv_enc_mode.hdisplay && - mode->vdisplay == tv_norm->tv_enc_mode.vdisplay) - mode->type |= DRM_MODE_TYPE_PREFERRED; - - drm_mode_probed_add(connector, mode); - n++; - } - - return n; -} - -static int nv17_tv_get_hd_modes(struct drm_encoder *encoder, - struct drm_connector *connector) -{ - struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); - struct drm_display_mode *output_mode = &tv_norm->ctv_enc_mode.mode; - struct drm_display_mode *mode; - const struct { - int hdisplay; - int vdisplay; - } modes[] = { - { 640, 400 }, - { 640, 480 }, - { 720, 480 }, - { 720, 576 }, - { 800, 600 }, - { 1024, 768 }, - { 1280, 720 }, - { 1280, 1024 }, - { 1920, 1080 } - }; - int i, n = 0; - - for (i = 0; i < ARRAY_SIZE(modes); i++) { - if (modes[i].hdisplay > output_mode->hdisplay || - modes[i].vdisplay > output_mode->vdisplay) - continue; - - if (modes[i].hdisplay == output_mode->hdisplay && - modes[i].vdisplay == output_mode->vdisplay) { - mode = drm_mode_duplicate(encoder->dev, output_mode); - mode->type |= DRM_MODE_TYPE_PREFERRED; - - } else { - mode = drm_cvt_mode(encoder->dev, modes[i].hdisplay, - modes[i].vdisplay, 60, false, - (output_mode->flags & - DRM_MODE_FLAG_INTERLACE), false); - } - - /* CVT modes are sometimes unsuitable... */ - if (output_mode->hdisplay <= 720 - || output_mode->hdisplay >= 1920) { - mode->htotal = output_mode->htotal; - mode->hsync_start = (mode->hdisplay + (mode->htotal - - mode->hdisplay) * 9 / 10) & ~7; - mode->hsync_end = mode->hsync_start + 8; - } - - if (output_mode->vdisplay >= 1024) { - mode->vtotal = output_mode->vtotal; - mode->vsync_start = output_mode->vsync_start; - mode->vsync_end = output_mode->vsync_end; - } - - mode->type |= DRM_MODE_TYPE_DRIVER; - drm_mode_probed_add(connector, mode); - n++; - } - - return n; -} - -static int nv17_tv_get_modes(struct drm_encoder *encoder, - struct drm_connector *connector) -{ - struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); - - if (tv_norm->kind == CTV_ENC_MODE) - return nv17_tv_get_hd_modes(encoder, connector); - else - return nv17_tv_get_ld_modes(encoder, connector); -} - -static int nv17_tv_mode_valid(struct drm_encoder *encoder, - struct drm_display_mode *mode) -{ - struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); - - if (tv_norm->kind == CTV_ENC_MODE) { - struct drm_display_mode *output_mode = - &tv_norm->ctv_enc_mode.mode; - - if (mode->clock > 400000) - return MODE_CLOCK_HIGH; - - if (mode->hdisplay > output_mode->hdisplay || - mode->vdisplay > output_mode->vdisplay) - return MODE_BAD; - - if ((mode->flags & DRM_MODE_FLAG_INTERLACE) != - (output_mode->flags & DRM_MODE_FLAG_INTERLACE)) - return MODE_NO_INTERLACE; - - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) - return MODE_NO_DBLESCAN; - - } else { - const int vsync_tolerance = 600; - - if (mode->clock > 70000) - return MODE_CLOCK_HIGH; - - if (abs(drm_mode_vrefresh(mode) * 1000 - - tv_norm->tv_enc_mode.vrefresh) > vsync_tolerance) - return MODE_VSYNC; - - /* The encoder takes care of the actual interlacing */ - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - return MODE_NO_INTERLACE; - } - - return MODE_OK; -} - -static bool nv17_tv_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); - - if (nv04_dac_in_use(encoder)) - return false; - - if (tv_norm->kind == CTV_ENC_MODE) - adjusted_mode->clock = tv_norm->ctv_enc_mode.mode.clock; - else - adjusted_mode->clock = 90000; - - return true; -} - -static void nv17_tv_dpms(struct drm_encoder *encoder, int mode) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_gpio *gpio = nouveau_gpio(drm->device); - struct nv17_tv_state *regs = &to_tv_enc(encoder)->state; - struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); - - if (nouveau_encoder(encoder)->last_dpms == mode) - return; - nouveau_encoder(encoder)->last_dpms = mode; - - NV_INFO(drm, "Setting dpms mode %d on TV encoder (output %d)\n", - mode, nouveau_encoder(encoder)->dcb->index); - - regs->ptv_200 &= ~1; - - if (tv_norm->kind == CTV_ENC_MODE) { - nv04_dfp_update_fp_control(encoder, mode); - - } else { - nv04_dfp_update_fp_control(encoder, DRM_MODE_DPMS_OFF); - - if (mode == DRM_MODE_DPMS_ON) - regs->ptv_200 |= 1; - } - - nv_load_ptv(dev, regs, 200); - - gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, mode == DRM_MODE_DPMS_ON); - gpio->set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, mode == DRM_MODE_DPMS_ON); - - nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); -} - -static void nv17_tv_prepare(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct drm_encoder_helper_funcs *helper = encoder->helper_private; - struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); - int head = nouveau_crtc(encoder->crtc)->index; - uint8_t *cr_lcd = &nv04_display(dev)->mode_reg.crtc_reg[head].CRTC[ - NV_CIO_CRE_LCD__INDEX]; - uint32_t dacclk_off = NV_PRAMDAC_DACCLK + - nv04_dac_output_offset(encoder); - uint32_t dacclk; - - helper->dpms(encoder, DRM_MODE_DPMS_OFF); - - nv04_dfp_disable(dev, head); - - /* Unbind any FP encoders from this head if we need the FP - * stuff enabled. */ - if (tv_norm->kind == CTV_ENC_MODE) { - struct drm_encoder *enc; - - list_for_each_entry(enc, &dev->mode_config.encoder_list, head) { - struct dcb_output *dcb = nouveau_encoder(enc)->dcb; - - if ((dcb->type == DCB_OUTPUT_TMDS || - dcb->type == DCB_OUTPUT_LVDS) && - !enc->crtc && - nv04_dfp_get_bound_head(dev, dcb) == head) { - nv04_dfp_bind_head(dev, dcb, head ^ 1, - drm->vbios.fp.dual_link); - } - } - - } - - if (tv_norm->kind == CTV_ENC_MODE) - *cr_lcd |= 0x1 | (head ? 0x0 : 0x8); - - /* Set the DACCLK register */ - dacclk = (NVReadRAMDAC(dev, 0, dacclk_off) & ~0x30) | 0x1; - - if (nv_device(drm->device)->card_type == NV_40) - dacclk |= 0x1a << 16; - - if (tv_norm->kind == CTV_ENC_MODE) { - dacclk |= 0x20; - - if (head) - dacclk |= 0x100; - else - dacclk &= ~0x100; - - } else { - dacclk |= 0x10; - - } - - NVWriteRAMDAC(dev, 0, dacclk_off, dacclk); -} - -static void nv17_tv_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *drm_mode, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - int head = nouveau_crtc(encoder->crtc)->index; - struct nv04_crtc_reg *regs = &nv04_display(dev)->mode_reg.crtc_reg[head]; - struct nv17_tv_state *tv_regs = &to_tv_enc(encoder)->state; - struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); - int i; - - regs->CRTC[NV_CIO_CRE_53] = 0x40; /* FP_HTIMING */ - regs->CRTC[NV_CIO_CRE_54] = 0; /* FP_VTIMING */ - regs->ramdac_630 = 0x2; /* turn off green mode (tv test pattern?) */ - regs->tv_setup = 1; - regs->ramdac_8c0 = 0x0; - - if (tv_norm->kind == TV_ENC_MODE) { - tv_regs->ptv_200 = 0x13111100; - if (head) - tv_regs->ptv_200 |= 0x10; - - tv_regs->ptv_20c = 0x808010; - tv_regs->ptv_304 = 0x2d00000; - tv_regs->ptv_600 = 0x0; - tv_regs->ptv_60c = 0x0; - tv_regs->ptv_610 = 0x1e00000; - - if (tv_norm->tv_enc_mode.vdisplay == 576) { - tv_regs->ptv_508 = 0x1200000; - tv_regs->ptv_614 = 0x33; - - } else if (tv_norm->tv_enc_mode.vdisplay == 480) { - tv_regs->ptv_508 = 0xf00000; - tv_regs->ptv_614 = 0x13; - } - - if (nv_device(drm->device)->card_type >= NV_30) { - tv_regs->ptv_500 = 0xe8e0; - tv_regs->ptv_504 = 0x1710; - tv_regs->ptv_604 = 0x0; - tv_regs->ptv_608 = 0x0; - } else { - if (tv_norm->tv_enc_mode.vdisplay == 576) { - tv_regs->ptv_604 = 0x20; - tv_regs->ptv_608 = 0x10; - tv_regs->ptv_500 = 0x19710; - tv_regs->ptv_504 = 0x68f0; - - } else if (tv_norm->tv_enc_mode.vdisplay == 480) { - tv_regs->ptv_604 = 0x10; - tv_regs->ptv_608 = 0x20; - tv_regs->ptv_500 = 0x4b90; - tv_regs->ptv_504 = 0x1b480; - } - } - - for (i = 0; i < 0x40; i++) - tv_regs->tv_enc[i] = tv_norm->tv_enc_mode.tv_enc[i]; - - } else { - struct drm_display_mode *output_mode = - &tv_norm->ctv_enc_mode.mode; - - /* The registers in PRAMDAC+0xc00 control some timings and CSC - * parameters for the CTV encoder (It's only used for "HD" TV - * modes, I don't think I have enough working to guess what - * they exactly mean...), it's probably connected at the - * output of the FP encoder, but it also needs the analog - * encoder in its OR enabled and routed to the head it's - * using. It's enabled with the DACCLK register, bits [5:4]. - */ - for (i = 0; i < 38; i++) - regs->ctv_regs[i] = tv_norm->ctv_enc_mode.ctv_regs[i]; - - regs->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1; - regs->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1; - regs->fp_horiz_regs[FP_SYNC_START] = - output_mode->hsync_start - 1; - regs->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1; - regs->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay + - max((output_mode->hdisplay-600)/40 - 1, 1); - - regs->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1; - regs->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1; - regs->fp_vert_regs[FP_SYNC_START] = - output_mode->vsync_start - 1; - regs->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1; - regs->fp_vert_regs[FP_CRTC] = output_mode->vdisplay - 1; - - regs->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | - NV_PRAMDAC_FP_TG_CONTROL_READ_PROG | - NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12; - - if (output_mode->flags & DRM_MODE_FLAG_PVSYNC) - regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS; - if (output_mode->flags & DRM_MODE_FLAG_PHSYNC) - regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS; - - regs->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND | - NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND | - NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR | - NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR | - NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED | - NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE | - NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE; - - regs->fp_debug_2 = 0; - - regs->fp_margin_color = 0x801080; - - } -} - -static void nv17_tv_commit(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_encoder_helper_funcs *helper = encoder->helper_private; - - if (get_tv_norm(encoder)->kind == TV_ENC_MODE) { - nv17_tv_update_rescaler(encoder); - nv17_tv_update_properties(encoder); - } else { - nv17_ctv_update_rescaler(encoder); - } - - nv17_tv_state_load(dev, &to_tv_enc(encoder)->state); - - /* This could use refinement for flatpanels, but it should work */ - if (nv_device(drm->device)->chipset < 0x44) - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + - nv04_dac_output_offset(encoder), - 0xf0000000); - else - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + - nv04_dac_output_offset(encoder), - 0x00100000); - - helper->dpms(encoder, DRM_MODE_DPMS_ON); - - NV_INFO(drm, "Output %s is running on CRTC %d using output %c\n", - drm_get_connector_name( - &nouveau_encoder_connector_get(nv_encoder)->base), - nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); -} - -static void nv17_tv_save(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); - - nouveau_encoder(encoder)->restore.output = - NVReadRAMDAC(dev, 0, - NV_PRAMDAC_DACCLK + - nv04_dac_output_offset(encoder)); - - nv17_tv_state_save(dev, &tv_enc->saved_state); - - tv_enc->state.ptv_200 = tv_enc->saved_state.ptv_200; -} - -static void nv17_tv_restore(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - - NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + - nv04_dac_output_offset(encoder), - nouveau_encoder(encoder)->restore.output); - - nv17_tv_state_load(dev, &to_tv_enc(encoder)->saved_state); - - nouveau_encoder(encoder)->last_dpms = NV_DPMS_CLEARED; -} - -static int nv17_tv_create_resources(struct drm_encoder *encoder, - struct drm_connector *connector) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct drm_mode_config *conf = &dev->mode_config; - struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); - struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; - int num_tv_norms = dcb->tvconf.has_component_output ? NUM_TV_NORMS : - NUM_LD_TV_NORMS; - int i; - - if (nouveau_tv_norm) { - for (i = 0; i < num_tv_norms; i++) { - if (!strcmp(nv17_tv_norm_names[i], nouveau_tv_norm)) { - tv_enc->tv_norm = i; - break; - } - } - - if (i == num_tv_norms) - NV_WARN(drm, "Invalid TV norm setting \"%s\"\n", - nouveau_tv_norm); - } - - drm_mode_create_tv_properties(dev, num_tv_norms, nv17_tv_norm_names); - - drm_object_attach_property(&connector->base, - conf->tv_select_subconnector_property, - tv_enc->select_subconnector); - drm_object_attach_property(&connector->base, - conf->tv_subconnector_property, - tv_enc->subconnector); - drm_object_attach_property(&connector->base, - conf->tv_mode_property, - tv_enc->tv_norm); - drm_object_attach_property(&connector->base, - conf->tv_flicker_reduction_property, - tv_enc->flicker); - drm_object_attach_property(&connector->base, - conf->tv_saturation_property, - tv_enc->saturation); - drm_object_attach_property(&connector->base, - conf->tv_hue_property, - tv_enc->hue); - drm_object_attach_property(&connector->base, - conf->tv_overscan_property, - tv_enc->overscan); - - return 0; -} - -static int nv17_tv_set_property(struct drm_encoder *encoder, - struct drm_connector *connector, - struct drm_property *property, - uint64_t val) -{ - struct drm_mode_config *conf = &encoder->dev->mode_config; - struct drm_crtc *crtc = encoder->crtc; - struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); - struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); - bool modes_changed = false; - - if (property == conf->tv_overscan_property) { - tv_enc->overscan = val; - if (encoder->crtc) { - if (tv_norm->kind == CTV_ENC_MODE) - nv17_ctv_update_rescaler(encoder); - else - nv17_tv_update_rescaler(encoder); - } - - } else if (property == conf->tv_saturation_property) { - if (tv_norm->kind != TV_ENC_MODE) - return -EINVAL; - - tv_enc->saturation = val; - nv17_tv_update_properties(encoder); - - } else if (property == conf->tv_hue_property) { - if (tv_norm->kind != TV_ENC_MODE) - return -EINVAL; - - tv_enc->hue = val; - nv17_tv_update_properties(encoder); - - } else if (property == conf->tv_flicker_reduction_property) { - if (tv_norm->kind != TV_ENC_MODE) - return -EINVAL; - - tv_enc->flicker = val; - if (encoder->crtc) - nv17_tv_update_rescaler(encoder); - - } else if (property == conf->tv_mode_property) { - if (connector->dpms != DRM_MODE_DPMS_OFF) - return -EINVAL; - - tv_enc->tv_norm = val; - - modes_changed = true; - - } else if (property == conf->tv_select_subconnector_property) { - if (tv_norm->kind != TV_ENC_MODE) - return -EINVAL; - - tv_enc->select_subconnector = val; - nv17_tv_update_properties(encoder); - - } else { - return -EINVAL; - } - - if (modes_changed) { - drm_helper_probe_single_connector_modes(connector, 0, 0); - - /* Disable the crtc to ensure a full modeset is - * performed whenever it's turned on again. */ - if (crtc) { - struct drm_mode_set modeset = { - .crtc = crtc, - }; - - drm_mode_set_config_internal(&modeset); - } - } - - return 0; -} - -static void nv17_tv_destroy(struct drm_encoder *encoder) -{ - struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); - - drm_encoder_cleanup(encoder); - kfree(tv_enc); -} - -static struct drm_encoder_helper_funcs nv17_tv_helper_funcs = { - .dpms = nv17_tv_dpms, - .save = nv17_tv_save, - .restore = nv17_tv_restore, - .mode_fixup = nv17_tv_mode_fixup, - .prepare = nv17_tv_prepare, - .commit = nv17_tv_commit, - .mode_set = nv17_tv_mode_set, - .detect = nv17_tv_detect, -}; - -static struct drm_encoder_slave_funcs nv17_tv_slave_funcs = { - .get_modes = nv17_tv_get_modes, - .mode_valid = nv17_tv_mode_valid, - .create_resources = nv17_tv_create_resources, - .set_property = nv17_tv_set_property, -}; - -static struct drm_encoder_funcs nv17_tv_funcs = { - .destroy = nv17_tv_destroy, -}; - -int -nv17_tv_create(struct drm_connector *connector, struct dcb_output *entry) -{ - struct drm_device *dev = connector->dev; - struct drm_encoder *encoder; - struct nv17_tv_encoder *tv_enc = NULL; - - tv_enc = kzalloc(sizeof(*tv_enc), GFP_KERNEL); - if (!tv_enc) - return -ENOMEM; - - tv_enc->overscan = 50; - tv_enc->flicker = 50; - tv_enc->saturation = 50; - tv_enc->hue = 0; - tv_enc->tv_norm = TV_NORM_PAL; - tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown; - tv_enc->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic; - tv_enc->pin_mask = 0; - - encoder = to_drm_encoder(&tv_enc->base); - - tv_enc->base.dcb = entry; - tv_enc->base.or = ffs(entry->or) - 1; - - drm_encoder_init(dev, encoder, &nv17_tv_funcs, DRM_MODE_ENCODER_TVDAC); - drm_encoder_helper_add(encoder, &nv17_tv_helper_funcs); - to_encoder_slave(encoder)->slave_funcs = &nv17_tv_slave_funcs; - - encoder->possible_crtcs = entry->heads; - encoder->possible_clones = 0; - - nv17_tv_create_resources(encoder, connector); - drm_mode_connector_attach_encoder(connector, encoder); - return 0; -} diff --git a/drivers/gpu/drm/nouveau/nv17_tv.h b/drivers/gpu/drm/nouveau/nv17_tv.h deleted file mode 100644 index 7b33154..0000000 --- a/drivers/gpu/drm/nouveau/nv17_tv.h +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2009 Francisco Jerez. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef __NV17_TV_H__ -#define __NV17_TV_H__ - -struct nv17_tv_state { - uint8_t tv_enc[0x40]; - - uint32_t hfilter[4][7]; - uint32_t hfilter2[4][7]; - uint32_t vfilter[4][7]; - - uint32_t ptv_200; - uint32_t ptv_204; - uint32_t ptv_208; - uint32_t ptv_20c; - uint32_t ptv_304; - uint32_t ptv_500; - uint32_t ptv_504; - uint32_t ptv_508; - uint32_t ptv_600; - uint32_t ptv_604; - uint32_t ptv_608; - uint32_t ptv_60c; - uint32_t ptv_610; - uint32_t ptv_614; -}; - -enum nv17_tv_norm{ - TV_NORM_PAL, - TV_NORM_PAL_M, - TV_NORM_PAL_N, - TV_NORM_PAL_NC, - TV_NORM_NTSC_M, - TV_NORM_NTSC_J, - NUM_LD_TV_NORMS, - TV_NORM_HD480I = NUM_LD_TV_NORMS, - TV_NORM_HD480P, - TV_NORM_HD576I, - TV_NORM_HD576P, - TV_NORM_HD720P, - TV_NORM_HD1080I, - NUM_TV_NORMS -}; - -struct nv17_tv_encoder { - struct nouveau_encoder base; - - struct nv17_tv_state state; - struct nv17_tv_state saved_state; - - int overscan; - int flicker; - int saturation; - int hue; - enum nv17_tv_norm tv_norm; - int subconnector; - int select_subconnector; - uint32_t pin_mask; -}; -#define to_tv_enc(x) container_of(nouveau_encoder(x), \ - struct nv17_tv_encoder, base) - -extern char *nv17_tv_norm_names[NUM_TV_NORMS]; - -extern struct nv17_tv_norm_params { - enum { - TV_ENC_MODE, - CTV_ENC_MODE, - } kind; - - union { - struct { - int hdisplay; - int vdisplay; - int vrefresh; /* mHz */ - - uint8_t tv_enc[0x40]; - } tv_enc_mode; - - struct { - struct drm_display_mode mode; - - uint32_t ctv_regs[38]; - } ctv_enc_mode; - }; - -} nv17_tv_norms[NUM_TV_NORMS]; -#define get_tv_norm(enc) (&nv17_tv_norms[to_tv_enc(enc)->tv_norm]) - -extern const struct drm_display_mode nv17_tv_modes[]; - -static inline int interpolate(int y0, int y1, int y2, int x) -{ - return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50; -} - -void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state); -void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state); -void nv17_tv_update_properties(struct drm_encoder *encoder); -void nv17_tv_update_rescaler(struct drm_encoder *encoder); -void nv17_ctv_update_rescaler(struct drm_encoder *encoder); - -/* TV hardware access functions */ - -static inline void nv_write_ptv(struct drm_device *dev, uint32_t reg, - uint32_t val) -{ - struct nouveau_device *device = nouveau_dev(dev); - nv_wr32(device, reg, val); -} - -static inline uint32_t nv_read_ptv(struct drm_device *dev, uint32_t reg) -{ - struct nouveau_device *device = nouveau_dev(dev); - return nv_rd32(device, reg); -} - -static inline void nv_write_tv_enc(struct drm_device *dev, uint8_t reg, - uint8_t val) -{ - nv_write_ptv(dev, NV_PTV_TV_INDEX, reg); - nv_write_ptv(dev, NV_PTV_TV_DATA, val); -} - -static inline uint8_t nv_read_tv_enc(struct drm_device *dev, uint8_t reg) -{ - nv_write_ptv(dev, NV_PTV_TV_INDEX, reg); - return nv_read_ptv(dev, NV_PTV_TV_DATA); -} - -#define nv_load_ptv(dev, state, reg) \ - nv_write_ptv(dev, NV_PTV_OFFSET + 0x##reg, state->ptv_##reg) -#define nv_save_ptv(dev, state, reg) \ - state->ptv_##reg = nv_read_ptv(dev, NV_PTV_OFFSET + 0x##reg) -#define nv_load_tv_enc(dev, state, reg) \ - nv_write_tv_enc(dev, 0x##reg, state->tv_enc[0x##reg]) - -#endif diff --git a/drivers/gpu/drm/nouveau/nv17_tv_modes.c b/drivers/gpu/drm/nouveau/nv17_tv_modes.c deleted file mode 100644 index 1cdfe2a..0000000 --- a/drivers/gpu/drm/nouveau/nv17_tv_modes.c +++ /dev/null @@ -1,592 +0,0 @@ -/* - * Copyright (C) 2009 Francisco Jerez. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include -#include "nouveau_drm.h" -#include "nouveau_encoder.h" -#include "nouveau_crtc.h" -#include "nouveau_hw.h" -#include "nv17_tv.h" - -char *nv17_tv_norm_names[NUM_TV_NORMS] = { - [TV_NORM_PAL] = "PAL", - [TV_NORM_PAL_M] = "PAL-M", - [TV_NORM_PAL_N] = "PAL-N", - [TV_NORM_PAL_NC] = "PAL-Nc", - [TV_NORM_NTSC_M] = "NTSC-M", - [TV_NORM_NTSC_J] = "NTSC-J", - [TV_NORM_HD480I] = "hd480i", - [TV_NORM_HD480P] = "hd480p", - [TV_NORM_HD576I] = "hd576i", - [TV_NORM_HD576P] = "hd576p", - [TV_NORM_HD720P] = "hd720p", - [TV_NORM_HD1080I] = "hd1080i" -}; - -/* TV standard specific parameters */ - -struct nv17_tv_norm_params nv17_tv_norms[NUM_TV_NORMS] = { - [TV_NORM_PAL] = { TV_ENC_MODE, { - .tv_enc_mode = { 720, 576, 50000, { - 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18, - 0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3, - 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, - 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3, - 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5, - 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, - 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, - 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0 - } } } }, - - [TV_NORM_PAL_M] = { TV_ENC_MODE, { - .tv_enc_mode = { 720, 480, 59940, { - 0x21, 0xe6, 0xef, 0xe3, 0x0, 0x0, 0xb, 0x18, - 0x7e, 0x44, 0x76, 0x32, 0x25, 0x0, 0x3c, 0x0, - 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, - 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, - 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5, - 0x0, 0x18, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, - 0x0, 0xb4, 0x0, 0x15, 0x40, 0x10, 0x0, 0x9c, - 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 - } } } }, - - [TV_NORM_PAL_N] = { TV_ENC_MODE, { - .tv_enc_mode = { 720, 576, 50000, { - 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18, - 0x7e, 0x40, 0x8a, 0x32, 0x25, 0x0, 0x3c, 0x0, - 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, - 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, - 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5, - 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, - 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, - 0xbd, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 - } } } }, - - [TV_NORM_PAL_NC] = { TV_ENC_MODE, { - .tv_enc_mode = { 720, 576, 50000, { - 0x21, 0xf6, 0x94, 0x46, 0x0, 0x0, 0xb, 0x18, - 0x7e, 0x44, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3, - 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, - 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3, - 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5, - 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, - 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, - 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0 - } } } }, - - [TV_NORM_NTSC_M] = { TV_ENC_MODE, { - .tv_enc_mode = { 720, 480, 59940, { - 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18, - 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x3c, 0x0, - 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, - 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, - 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5, - 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, - 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0x9c, - 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 - } } } }, - - [TV_NORM_NTSC_J] = { TV_ENC_MODE, { - .tv_enc_mode = { 720, 480, 59940, { - 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18, - 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0, - 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, - 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, - 0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5, - 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, - 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4, - 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 - } } } }, - - [TV_NORM_HD480I] = { TV_ENC_MODE, { - .tv_enc_mode = { 720, 480, 59940, { - 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18, - 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0, - 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, - 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, - 0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5, - 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, - 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4, - 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 - } } } }, - - [TV_NORM_HD576I] = { TV_ENC_MODE, { - .tv_enc_mode = { 720, 576, 50000, { - 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18, - 0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3, - 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, - 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3, - 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5, - 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, - 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, - 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0 - } } } }, - - - [TV_NORM_HD480P] = { CTV_ENC_MODE, { - .ctv_enc_mode = { - .mode = { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, - 720, 735, 743, 858, 0, 480, 490, 494, 525, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - .ctv_regs = { 0x3540000, 0x0, 0x0, 0x314, - 0x354003a, 0x40000, 0x6f0344, 0x18100000, - 0x10160004, 0x10060005, 0x1006000c, 0x10060020, - 0x10060021, 0x140e0022, 0x10060202, 0x1802020a, - 0x1810020b, 0x10000fff, 0x10000fff, 0x10000fff, - 0x10000fff, 0x10000fff, 0x10000fff, 0x70, - 0x3ff0000, 0x57, 0x2e001e, 0x258012c, - 0xa0aa04ec, 0x30, 0x80960019, 0x12c0300, - 0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400 - } } } }, - - [TV_NORM_HD576P] = { CTV_ENC_MODE, { - .ctv_enc_mode = { - .mode = { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, - 720, 730, 738, 864, 0, 576, 581, 585, 625, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - .ctv_regs = { 0x3540000, 0x0, 0x0, 0x314, - 0x354003a, 0x40000, 0x6f0344, 0x18100000, - 0x10060001, 0x10060009, 0x10060026, 0x10060027, - 0x140e0028, 0x10060268, 0x1810026d, 0x10000fff, - 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff, - 0x10000fff, 0x10000fff, 0x10000fff, 0x69, - 0x3ff0000, 0x57, 0x2e001e, 0x258012c, - 0xa0aa04ec, 0x30, 0x80960019, 0x12c0300, - 0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400 - } } } }, - - [TV_NORM_HD720P] = { CTV_ENC_MODE, { - .ctv_enc_mode = { - .mode = { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, - 1280, 1349, 1357, 1650, 0, 720, 725, 730, 750, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - .ctv_regs = { 0x1260394, 0x0, 0x0, 0x622, - 0x66b0021, 0x6004a, 0x1210626, 0x8170000, - 0x70004, 0x70016, 0x70017, 0x40f0018, - 0x702e8, 0x81702ed, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfff, 0xfff, 0x0, - 0x2e40001, 0x58, 0x2e001e, 0x258012c, - 0xa0aa04ec, 0x30, 0x810c0039, 0x12c0300, - 0xc0002039, 0x600, 0x32060039, 0x0, 0x0, 0x0 - } } } }, - - [TV_NORM_HD1080I] = { CTV_ENC_MODE, { - .ctv_enc_mode = { - .mode = { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, - 1920, 1961, 2049, 2200, 0, 1080, 1084, 1088, 1125, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC - | DRM_MODE_FLAG_INTERLACE) }, - .ctv_regs = { 0xac0420, 0x44c0478, 0x4a4, 0x4fc0868, - 0x8940028, 0x60054, 0xe80870, 0xbf70000, - 0xbc70004, 0x70005, 0x70012, 0x70013, - 0x40f0014, 0x70230, 0xbf70232, 0xbf70233, - 0x1c70237, 0x70238, 0x70244, 0x70245, - 0x40f0246, 0x70462, 0x1f70464, 0x0, - 0x2e40001, 0x58, 0x2e001e, 0x258012c, - 0xa0aa04ec, 0x30, 0x815f004c, 0x12c0300, - 0xc000204c, 0x600, 0x3206004c, 0x0, 0x0, 0x0 - } } } } -}; - -/* - * The following is some guesswork on how the TV encoder flicker - * filter/rescaler works: - * - * It seems to use some sort of resampling filter, it is controlled - * through the registers at NV_PTV_HFILTER and NV_PTV_VFILTER, they - * control the horizontal and vertical stage respectively, there is - * also NV_PTV_HFILTER2 the blob fills identically to NV_PTV_HFILTER, - * but they seem to do nothing. A rough guess might be that they could - * be used to independently control the filtering of each interlaced - * field, but I don't know how they are enabled. The whole filtering - * process seems to be disabled with bits 26:27 of PTV_200, but we - * aren't doing that. - * - * The layout of both register sets is the same: - * - * A: [BASE+0x18]...[BASE+0x0] [BASE+0x58]..[BASE+0x40] - * B: [BASE+0x34]...[BASE+0x1c] [BASE+0x74]..[BASE+0x5c] - * - * Each coefficient is stored in bits [31],[15:9] in two's complement - * format. They seem to be some kind of weights used in a low-pass - * filter. Both A and B coefficients are applied to the 14 nearest - * samples on each side (Listed from nearest to furthermost. They - * roughly cover 2 framebuffer pixels on each side). They are - * probably multiplied with some more hardwired weights before being - * used: B-coefficients are applied the same on both sides, - * A-coefficients are inverted before being applied to the opposite - * side. - * - * After all the hassle, I got the following formula by empirical - * means... - */ - -#define calc_overscan(o) interpolate(0x100, 0xe1, 0xc1, o) - -#define id1 (1LL << 8) -#define id2 (1LL << 16) -#define id3 (1LL << 24) -#define id4 (1LL << 32) -#define id5 (1LL << 48) - -static struct filter_params{ - int64_t k1; - int64_t ki; - int64_t ki2; - int64_t ki3; - int64_t kr; - int64_t kir; - int64_t ki2r; - int64_t ki3r; - int64_t kf; - int64_t kif; - int64_t ki2f; - int64_t ki3f; - int64_t krf; - int64_t kirf; - int64_t ki2rf; - int64_t ki3rf; -} fparams[2][4] = { - /* Horizontal filter parameters */ - { - {64.311690 * id5, -39.516924 * id5, 6.586143 * id5, 0.000002 * id5, - 0.051285 * id4, 26.168746 * id4, -4.361449 * id4, -0.000001 * id4, - 9.308169 * id3, 78.180965 * id3, -13.030158 * id3, -0.000001 * id3, - -8.801540 * id1, -46.572890 * id1, 7.762145 * id1, -0.000000 * id1}, - {-44.565569 * id5, -68.081246 * id5, 39.812074 * id5, -4.009316 * id5, - 29.832207 * id4, 50.047322 * id4, -25.380017 * id4, 2.546422 * id4, - 104.605622 * id3, 141.908641 * id3, -74.322319 * id3, 7.484316 * id3, - -37.081621 * id1, -90.397510 * id1, 42.784229 * id1, -4.289952 * id1}, - {-56.793244 * id5, 31.153584 * id5, -5.192247 * id5, -0.000003 * id5, - 33.541131 * id4, -34.149302 * id4, 5.691537 * id4, 0.000002 * id4, - 87.196610 * id3, -88.995169 * id3, 14.832456 * id3, 0.000012 * id3, - 17.288138 * id1, 71.864786 * id1, -11.977408 * id1, -0.000009 * id1}, - {51.787796 * id5, 21.211771 * id5, -18.993730 * id5, 1.853310 * id5, - -41.470726 * id4, -17.775823 * id4, 13.057821 * id4, -1.15823 * id4, - -154.235673 * id3, -44.878641 * id3, 40.656077 * id3, -3.695595 * id3, - 112.201065 * id1, 39.992155 * id1, -25.155714 * id1, 2.113984 * id1}, - }, - - /* Vertical filter parameters */ - { - {67.601979 * id5, 0.428319 * id5, -0.071318 * id5, -0.000012 * id5, - -3.402339 * id4, 0.000209 * id4, -0.000092 * id4, 0.000010 * id4, - -9.180996 * id3, 6.111270 * id3, -1.024457 * id3, 0.001043 * id3, - 6.060315 * id1, -0.017425 * id1, 0.007830 * id1, -0.000869 * id1}, - {6.755647 * id5, 5.841348 * id5, 1.469734 * id5, -0.149656 * id5, - 8.293120 * id4, -1.192888 * id4, -0.947652 * id4, 0.094507 * id4, - 37.526655 * id3, 10.257875 * id3, -10.823275 * id3, 1.081497 * id3, - -2.361928 * id1, -2.059432 * id1, 1.840671 * id1, -0.168100 * id1}, - {-14.780391 * id5, -16.042148 * id5, 2.673692 * id5, -0.000000 * id5, - 39.541978 * id4, 5.680053 * id4, -0.946676 * id4, 0.000000 * id4, - 152.994486 * id3, 12.625439 * id3, -2.119579 * id3, 0.002708 * id3, - -38.125089 * id1, -0.855880 * id1, 0.155359 * id1, -0.002245 * id1}, - {-27.476193 * id5, -1.454976 * id5, 1.286557 * id5, 0.025346 * id5, - 20.687300 * id4, 3.014003 * id4, -0.557786 * id4, -0.01311 * id4, - 60.008737 * id3, -0.738273 * id3, 5.408217 * id3, -0.796798 * id3, - -17.296835 * id1, 4.438577 * id1, -2.809420 * id1, 0.385491 * id1}, - } -}; - -static void tv_setup_filter(struct drm_encoder *encoder) -{ - struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); - struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); - struct drm_display_mode *mode = &encoder->crtc->mode; - uint32_t (*filters[])[4][7] = {&tv_enc->state.hfilter, - &tv_enc->state.vfilter}; - int i, j, k; - int32_t overscan = calc_overscan(tv_enc->overscan); - int64_t flicker = (tv_enc->flicker - 50) * (id3 / 100); - uint64_t rs[] = {mode->hdisplay * id3, - mode->vdisplay * id3}; - - do_div(rs[0], overscan * tv_norm->tv_enc_mode.hdisplay); - do_div(rs[1], overscan * tv_norm->tv_enc_mode.vdisplay); - - for (k = 0; k < 2; k++) { - rs[k] = max((int64_t)rs[k], id2); - - for (j = 0; j < 4; j++) { - struct filter_params *p = &fparams[k][j]; - - for (i = 0; i < 7; i++) { - int64_t c = (p->k1 + p->ki*i + p->ki2*i*i + - p->ki3*i*i*i) - + (p->kr + p->kir*i + p->ki2r*i*i + - p->ki3r*i*i*i) * rs[k] - + (p->kf + p->kif*i + p->ki2f*i*i + - p->ki3f*i*i*i) * flicker - + (p->krf + p->kirf*i + p->ki2rf*i*i + - p->ki3rf*i*i*i) * flicker * rs[k]; - - (*filters[k])[j][i] = (c + id5/2) >> 39 - & (0x1 << 31 | 0x7f << 9); - } - } - } -} - -/* Hardware state saving/restoring */ - -static void tv_save_filter(struct drm_device *dev, uint32_t base, - uint32_t regs[4][7]) -{ - int i, j; - uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c }; - - for (i = 0; i < 4; i++) { - for (j = 0; j < 7; j++) - regs[i][j] = nv_read_ptv(dev, offsets[i]+4*j); - } -} - -static void tv_load_filter(struct drm_device *dev, uint32_t base, - uint32_t regs[4][7]) -{ - int i, j; - uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c }; - - for (i = 0; i < 4; i++) { - for (j = 0; j < 7; j++) - nv_write_ptv(dev, offsets[i]+4*j, regs[i][j]); - } -} - -void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state) -{ - int i; - - for (i = 0; i < 0x40; i++) - state->tv_enc[i] = nv_read_tv_enc(dev, i); - - tv_save_filter(dev, NV_PTV_HFILTER, state->hfilter); - tv_save_filter(dev, NV_PTV_HFILTER2, state->hfilter2); - tv_save_filter(dev, NV_PTV_VFILTER, state->vfilter); - - nv_save_ptv(dev, state, 200); - nv_save_ptv(dev, state, 204); - nv_save_ptv(dev, state, 208); - nv_save_ptv(dev, state, 20c); - nv_save_ptv(dev, state, 304); - nv_save_ptv(dev, state, 500); - nv_save_ptv(dev, state, 504); - nv_save_ptv(dev, state, 508); - nv_save_ptv(dev, state, 600); - nv_save_ptv(dev, state, 604); - nv_save_ptv(dev, state, 608); - nv_save_ptv(dev, state, 60c); - nv_save_ptv(dev, state, 610); - nv_save_ptv(dev, state, 614); -} - -void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state) -{ - int i; - - for (i = 0; i < 0x40; i++) - nv_write_tv_enc(dev, i, state->tv_enc[i]); - - tv_load_filter(dev, NV_PTV_HFILTER, state->hfilter); - tv_load_filter(dev, NV_PTV_HFILTER2, state->hfilter2); - tv_load_filter(dev, NV_PTV_VFILTER, state->vfilter); - - nv_load_ptv(dev, state, 200); - nv_load_ptv(dev, state, 204); - nv_load_ptv(dev, state, 208); - nv_load_ptv(dev, state, 20c); - nv_load_ptv(dev, state, 304); - nv_load_ptv(dev, state, 500); - nv_load_ptv(dev, state, 504); - nv_load_ptv(dev, state, 508); - nv_load_ptv(dev, state, 600); - nv_load_ptv(dev, state, 604); - nv_load_ptv(dev, state, 608); - nv_load_ptv(dev, state, 60c); - nv_load_ptv(dev, state, 610); - nv_load_ptv(dev, state, 614); - - /* This is required for some settings to kick in. */ - nv_write_tv_enc(dev, 0x3e, 1); - nv_write_tv_enc(dev, 0x3e, 0); -} - -/* Timings similar to the ones the blob sets */ - -const struct drm_display_mode nv17_tv_modes[] = { - { DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 0, - 320, 344, 392, 560, 0, 200, 200, 202, 220, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC - | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) }, - { DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 0, - 320, 344, 392, 560, 0, 240, 240, 246, 263, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC - | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) }, - { DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 0, - 400, 432, 496, 640, 0, 300, 300, 303, 314, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC - | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) }, - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 0, - 640, 672, 768, 880, 0, 480, 480, 492, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 0, - 720, 752, 872, 960, 0, 480, 480, 493, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 0, - 720, 776, 856, 960, 0, 576, 576, 588, 597, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 0, - 800, 840, 920, 1040, 0, 600, 600, 604, 618, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 0, - 1024, 1064, 1200, 1344, 0, 768, 768, 777, 806, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - {} -}; - -void nv17_tv_update_properties(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); - struct nv17_tv_state *regs = &tv_enc->state; - struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); - int subconnector = tv_enc->select_subconnector ? - tv_enc->select_subconnector : - tv_enc->subconnector; - - switch (subconnector) { - case DRM_MODE_SUBCONNECTOR_Composite: - { - regs->ptv_204 = 0x2; - - /* The composite connector may be found on either pin. */ - if (tv_enc->pin_mask & 0x4) - regs->ptv_204 |= 0x010000; - else if (tv_enc->pin_mask & 0x2) - regs->ptv_204 |= 0x100000; - else - regs->ptv_204 |= 0x110000; - - regs->tv_enc[0x7] = 0x10; - break; - } - case DRM_MODE_SUBCONNECTOR_SVIDEO: - regs->ptv_204 = 0x11012; - regs->tv_enc[0x7] = 0x18; - break; - - case DRM_MODE_SUBCONNECTOR_Component: - regs->ptv_204 = 0x111333; - regs->tv_enc[0x7] = 0x14; - break; - - case DRM_MODE_SUBCONNECTOR_SCART: - regs->ptv_204 = 0x111012; - regs->tv_enc[0x7] = 0x18; - break; - } - - regs->tv_enc[0x20] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x20], - 255, tv_enc->saturation); - regs->tv_enc[0x22] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x22], - 255, tv_enc->saturation); - regs->tv_enc[0x25] = tv_enc->hue * 255 / 100; - - nv_load_ptv(dev, regs, 204); - nv_load_tv_enc(dev, regs, 7); - nv_load_tv_enc(dev, regs, 20); - nv_load_tv_enc(dev, regs, 22); - nv_load_tv_enc(dev, regs, 25); -} - -void nv17_tv_update_rescaler(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); - struct nv17_tv_state *regs = &tv_enc->state; - - regs->ptv_208 = 0x40 | (calc_overscan(tv_enc->overscan) << 8); - - tv_setup_filter(encoder); - - nv_load_ptv(dev, regs, 208); - tv_load_filter(dev, NV_PTV_HFILTER, regs->hfilter); - tv_load_filter(dev, NV_PTV_HFILTER2, regs->hfilter2); - tv_load_filter(dev, NV_PTV_VFILTER, regs->vfilter); -} - -void nv17_ctv_update_rescaler(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); - int head = nouveau_crtc(encoder->crtc)->index; - struct nv04_crtc_reg *regs = &nv04_display(dev)->mode_reg.crtc_reg[head]; - struct drm_display_mode *crtc_mode = &encoder->crtc->mode; - struct drm_display_mode *output_mode = - &get_tv_norm(encoder)->ctv_enc_mode.mode; - int overscan, hmargin, vmargin, hratio, vratio; - - /* The rescaler doesn't do the right thing for interlaced modes. */ - if (output_mode->flags & DRM_MODE_FLAG_INTERLACE) - overscan = 100; - else - overscan = tv_enc->overscan; - - hmargin = (output_mode->hdisplay - crtc_mode->hdisplay) / 2; - vmargin = (output_mode->vdisplay - crtc_mode->vdisplay) / 2; - - hmargin = interpolate(0, min(hmargin, output_mode->hdisplay/20), - hmargin, overscan); - vmargin = interpolate(0, min(vmargin, output_mode->vdisplay/20), - vmargin, overscan); - - hratio = crtc_mode->hdisplay * 0x800 / - (output_mode->hdisplay - 2*hmargin); - vratio = crtc_mode->vdisplay * 0x800 / - (output_mode->vdisplay - 2*vmargin) & ~3; - - regs->fp_horiz_regs[FP_VALID_START] = hmargin; - regs->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - hmargin - 1; - regs->fp_vert_regs[FP_VALID_START] = vmargin; - regs->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - vmargin - 1; - - regs->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE | - XLATE(vratio, 0, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE) | - NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE | - XLATE(hratio, 0, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE); - - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_START, - regs->fp_horiz_regs[FP_VALID_START]); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_END, - regs->fp_horiz_regs[FP_VALID_END]); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_START, - regs->fp_vert_regs[FP_VALID_START]); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_END, - regs->fp_vert_regs[FP_VALID_END]); - NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regs->fp_debug_1); -} diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c index 3382064..3af5bcd 100644 --- a/drivers/gpu/drm/nouveau/nv40_pm.c +++ b/drivers/gpu/drm/nouveau/nv40_pm.c @@ -26,7 +26,7 @@ #include "nouveau_drm.h" #include "nouveau_bios.h" #include "nouveau_pm.h" -#include "nouveau_hw.h" +#include "dispnv04/hw.h" #include #include diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 8bd5d27..69620e3 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -25,7 +25,7 @@ #include #include "nouveau_drm.h" #include "nouveau_bios.h" -#include "nouveau_hw.h" +#include "dispnv04/hw.h" #include "nouveau_pm.h" #include "nouveau_hwsq.h" diff --git a/drivers/gpu/drm/nouveau/nvreg.h b/drivers/gpu/drm/nouveau/nvreg.h deleted file mode 100644 index bbfb1a6..0000000 --- a/drivers/gpu/drm/nouveau/nvreg.h +++ /dev/null @@ -1,517 +0,0 @@ -/* $XConsortium: nvreg.h /main/2 1996/10/28 05:13:41 kaleb $ */ -/* - * Copyright 1996-1997 David J. McKay - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * DAVID J. MCKAY BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF - * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/nv/nvreg.h,v 1.6 2002/01/25 21:56:06 tsi Exp $ */ - -#ifndef __NVREG_H_ -#define __NVREG_H_ - -#define NV_PMC_OFFSET 0x00000000 -#define NV_PMC_SIZE 0x00001000 - -#define NV_PBUS_OFFSET 0x00001000 -#define NV_PBUS_SIZE 0x00001000 - -#define NV_PFIFO_OFFSET 0x00002000 -#define NV_PFIFO_SIZE 0x00002000 - -#define NV_HDIAG_OFFSET 0x00005000 -#define NV_HDIAG_SIZE 0x00001000 - -#define NV_PRAM_OFFSET 0x00006000 -#define NV_PRAM_SIZE 0x00001000 - -#define NV_PVIDEO_OFFSET 0x00008000 -#define NV_PVIDEO_SIZE 0x00001000 - -#define NV_PTIMER_OFFSET 0x00009000 -#define NV_PTIMER_SIZE 0x00001000 - -#define NV_PPM_OFFSET 0x0000A000 -#define NV_PPM_SIZE 0x00001000 - -#define NV_PTV_OFFSET 0x0000D000 -#define NV_PTV_SIZE 0x00001000 - -#define NV_PRMVGA_OFFSET 0x000A0000 -#define NV_PRMVGA_SIZE 0x00020000 - -#define NV_PRMVIO0_OFFSET 0x000C0000 -#define NV_PRMVIO_SIZE 0x00002000 -#define NV_PRMVIO1_OFFSET 0x000C2000 - -#define NV_PFB_OFFSET 0x00100000 -#define NV_PFB_SIZE 0x00001000 - -#define NV_PEXTDEV_OFFSET 0x00101000 -#define NV_PEXTDEV_SIZE 0x00001000 - -#define NV_PME_OFFSET 0x00200000 -#define NV_PME_SIZE 0x00001000 - -#define NV_PROM_OFFSET 0x00300000 -#define NV_PROM_SIZE 0x00010000 - -#define NV_PGRAPH_OFFSET 0x00400000 -#define NV_PGRAPH_SIZE 0x00010000 - -#define NV_PCRTC0_OFFSET 0x00600000 -#define NV_PCRTC0_SIZE 0x00002000 /* empirical */ - -#define NV_PRMCIO0_OFFSET 0x00601000 -#define NV_PRMCIO_SIZE 0x00002000 -#define NV_PRMCIO1_OFFSET 0x00603000 - -#define NV50_DISPLAY_OFFSET 0x00610000 -#define NV50_DISPLAY_SIZE 0x0000FFFF - -#define NV_PRAMDAC0_OFFSET 0x00680000 -#define NV_PRAMDAC0_SIZE 0x00002000 - -#define NV_PRMDIO0_OFFSET 0x00681000 -#define NV_PRMDIO_SIZE 0x00002000 -#define NV_PRMDIO1_OFFSET 0x00683000 - -#define NV_PRAMIN_OFFSET 0x00700000 -#define NV_PRAMIN_SIZE 0x00100000 - -#define NV_FIFO_OFFSET 0x00800000 -#define NV_FIFO_SIZE 0x00800000 - -#define NV_PMC_BOOT_0 0x00000000 -#define NV_PMC_ENABLE 0x00000200 - -#define NV_VIO_VSE2 0x000003c3 -#define NV_VIO_SRX 0x000003c4 - -#define NV_CIO_CRX__COLOR 0x000003d4 -#define NV_CIO_CR__COLOR 0x000003d5 - -#define NV_PBUS_DEBUG_1 0x00001084 -#define NV_PBUS_DEBUG_4 0x00001098 -#define NV_PBUS_DEBUG_DUALHEAD_CTL 0x000010f0 -#define NV_PBUS_POWERCTRL_1 0x00001584 -#define NV_PBUS_POWERCTRL_2 0x00001588 -#define NV_PBUS_POWERCTRL_4 0x00001590 -#define NV_PBUS_PCI_NV_19 0x0000184C -#define NV_PBUS_PCI_NV_20 0x00001850 -# define NV_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED (0 << 0) -# define NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED (1 << 0) - -#define NV_PFIFO_RAMHT 0x00002210 - -#define NV_PTV_TV_INDEX 0x0000d220 -#define NV_PTV_TV_DATA 0x0000d224 -#define NV_PTV_HFILTER 0x0000d310 -#define NV_PTV_HFILTER2 0x0000d390 -#define NV_PTV_VFILTER 0x0000d510 - -#define NV_PRMVIO_MISC__WRITE 0x000c03c2 -#define NV_PRMVIO_SRX 0x000c03c4 -#define NV_PRMVIO_SR 0x000c03c5 -# define NV_VIO_SR_RESET_INDEX 0x00 -# define NV_VIO_SR_CLOCK_INDEX 0x01 -# define NV_VIO_SR_PLANE_MASK_INDEX 0x02 -# define NV_VIO_SR_CHAR_MAP_INDEX 0x03 -# define NV_VIO_SR_MEM_MODE_INDEX 0x04 -#define NV_PRMVIO_MISC__READ 0x000c03cc -#define NV_PRMVIO_GRX 0x000c03ce -#define NV_PRMVIO_GX 0x000c03cf -# define NV_VIO_GX_SR_INDEX 0x00 -# define NV_VIO_GX_SREN_INDEX 0x01 -# define NV_VIO_GX_CCOMP_INDEX 0x02 -# define NV_VIO_GX_ROP_INDEX 0x03 -# define NV_VIO_GX_READ_MAP_INDEX 0x04 -# define NV_VIO_GX_MODE_INDEX 0x05 -# define NV_VIO_GX_MISC_INDEX 0x06 -# define NV_VIO_GX_DONT_CARE_INDEX 0x07 -# define NV_VIO_GX_BIT_MASK_INDEX 0x08 - -#define NV_PCRTC_INTR_0 0x00600100 -# define NV_PCRTC_INTR_0_VBLANK (1 << 0) -#define NV_PCRTC_INTR_EN_0 0x00600140 -#define NV_PCRTC_START 0x00600800 -#define NV_PCRTC_CONFIG 0x00600804 -# define NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA (1 << 0) -# define NV04_PCRTC_CONFIG_START_ADDRESS_HSYNC (4 << 0) -# define NV10_PCRTC_CONFIG_START_ADDRESS_HSYNC (2 << 0) -#define NV_PCRTC_CURSOR_CONFIG 0x00600810 -# define NV_PCRTC_CURSOR_CONFIG_ENABLE_ENABLE (1 << 0) -# define NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE (1 << 4) -# define NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM (1 << 8) -# define NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32 (1 << 12) -# define NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 (1 << 16) -# define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_32 (2 << 24) -# define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 (4 << 24) -# define NV_PCRTC_CURSOR_CONFIG_CUR_BLEND_ALPHA (1 << 28) - -/* note: PCRTC_GPIO is not available on nv10, and in fact aliases 0x600810 */ -#define NV_PCRTC_GPIO 0x00600818 -#define NV_PCRTC_GPIO_EXT 0x0060081c -#define NV_PCRTC_830 0x00600830 -#define NV_PCRTC_834 0x00600834 -#define NV_PCRTC_850 0x00600850 -#define NV_PCRTC_ENGINE_CTRL 0x00600860 -# define NV_CRTC_FSEL_I2C (1 << 4) -# define NV_CRTC_FSEL_OVERLAY (1 << 12) - -#define NV_PRMCIO_ARX 0x006013c0 -#define NV_PRMCIO_AR__WRITE 0x006013c0 -#define NV_PRMCIO_AR__READ 0x006013c1 -# define NV_CIO_AR_MODE_INDEX 0x10 -# define NV_CIO_AR_OSCAN_INDEX 0x11 -# define NV_CIO_AR_PLANE_INDEX 0x12 -# define NV_CIO_AR_HPP_INDEX 0x13 -# define NV_CIO_AR_CSEL_INDEX 0x14 -#define NV_PRMCIO_INP0 0x006013c2 -#define NV_PRMCIO_CRX__COLOR 0x006013d4 -#define NV_PRMCIO_CR__COLOR 0x006013d5 - /* Standard VGA CRTC registers */ -# define NV_CIO_CR_HDT_INDEX 0x00 /* horizontal display total */ -# define NV_CIO_CR_HDE_INDEX 0x01 /* horizontal display end */ -# define NV_CIO_CR_HBS_INDEX 0x02 /* horizontal blanking start */ -# define NV_CIO_CR_HBE_INDEX 0x03 /* horizontal blanking end */ -# define NV_CIO_CR_HBE_4_0 4:0 -# define NV_CIO_CR_HRS_INDEX 0x04 /* horizontal retrace start */ -# define NV_CIO_CR_HRE_INDEX 0x05 /* horizontal retrace end */ -# define NV_CIO_CR_HRE_4_0 4:0 -# define NV_CIO_CR_HRE_HBE_5 7:7 -# define NV_CIO_CR_VDT_INDEX 0x06 /* vertical display total */ -# define NV_CIO_CR_OVL_INDEX 0x07 /* overflow bits */ -# define NV_CIO_CR_OVL_VDT_8 0:0 -# define NV_CIO_CR_OVL_VDE_8 1:1 -# define NV_CIO_CR_OVL_VRS_8 2:2 -# define NV_CIO_CR_OVL_VBS_8 3:3 -# define NV_CIO_CR_OVL_VDT_9 5:5 -# define NV_CIO_CR_OVL_VDE_9 6:6 -# define NV_CIO_CR_OVL_VRS_9 7:7 -# define NV_CIO_CR_RSAL_INDEX 0x08 /* normally "preset row scan" */ -# define NV_CIO_CR_CELL_HT_INDEX 0x09 /* cell height?! normally "max scan line" */ -# define NV_CIO_CR_CELL_HT_VBS_9 5:5 -# define NV_CIO_CR_CELL_HT_SCANDBL 7:7 -# define NV_CIO_CR_CURS_ST_INDEX 0x0a /* cursor start */ -# define NV_CIO_CR_CURS_END_INDEX 0x0b /* cursor end */ -# define NV_CIO_CR_SA_HI_INDEX 0x0c /* screen start address high */ -# define NV_CIO_CR_SA_LO_INDEX 0x0d /* screen start address low */ -# define NV_CIO_CR_TCOFF_HI_INDEX 0x0e /* cursor offset high */ -# define NV_CIO_CR_TCOFF_LO_INDEX 0x0f /* cursor offset low */ -# define NV_CIO_CR_VRS_INDEX 0x10 /* vertical retrace start */ -# define NV_CIO_CR_VRE_INDEX 0x11 /* vertical retrace end */ -# define NV_CIO_CR_VRE_3_0 3:0 -# define NV_CIO_CR_VDE_INDEX 0x12 /* vertical display end */ -# define NV_CIO_CR_OFFSET_INDEX 0x13 /* sets screen pitch */ -# define NV_CIO_CR_ULINE_INDEX 0x14 /* underline location */ -# define NV_CIO_CR_VBS_INDEX 0x15 /* vertical blank start */ -# define NV_CIO_CR_VBE_INDEX 0x16 /* vertical blank end */ -# define NV_CIO_CR_MODE_INDEX 0x17 /* crtc mode control */ -# define NV_CIO_CR_LCOMP_INDEX 0x18 /* line compare */ - /* Extended VGA CRTC registers */ -# define NV_CIO_CRE_RPC0_INDEX 0x19 /* repaint control 0 */ -# define NV_CIO_CRE_RPC0_OFFSET_10_8 7:5 -# define NV_CIO_CRE_RPC1_INDEX 0x1a /* repaint control 1 */ -# define NV_CIO_CRE_RPC1_LARGE 2:2 -# define NV_CIO_CRE_FF_INDEX 0x1b /* fifo control */ -# define NV_CIO_CRE_ENH_INDEX 0x1c /* enhanced? */ -# define NV_CIO_SR_LOCK_INDEX 0x1f /* crtc lock */ -# define NV_CIO_SR_UNLOCK_RW_VALUE 0x57 -# define NV_CIO_SR_LOCK_VALUE 0x99 -# define NV_CIO_CRE_FFLWM__INDEX 0x20 /* fifo low water mark */ -# define NV_CIO_CRE_21 0x21 /* vga shadow crtc lock */ -# define NV_CIO_CRE_LSR_INDEX 0x25 /* ? */ -# define NV_CIO_CRE_LSR_VDT_10 0:0 -# define NV_CIO_CRE_LSR_VDE_10 1:1 -# define NV_CIO_CRE_LSR_VRS_10 2:2 -# define NV_CIO_CRE_LSR_VBS_10 3:3 -# define NV_CIO_CRE_LSR_HBE_6 4:4 -# define NV_CIO_CR_ARX_INDEX 0x26 /* attribute index -- ro copy of 0x60.3c0 */ -# define NV_CIO_CRE_CHIP_ID_INDEX 0x27 /* chip revision */ -# define NV_CIO_CRE_PIXEL_INDEX 0x28 -# define NV_CIO_CRE_PIXEL_FORMAT 1:0 -# define NV_CIO_CRE_HEB__INDEX 0x2d /* horizontal extra bits? */ -# define NV_CIO_CRE_HEB_HDT_8 0:0 -# define NV_CIO_CRE_HEB_HDE_8 1:1 -# define NV_CIO_CRE_HEB_HBS_8 2:2 -# define NV_CIO_CRE_HEB_HRS_8 3:3 -# define NV_CIO_CRE_HEB_ILC_8 4:4 -# define NV_CIO_CRE_2E 0x2e /* some scratch or dummy reg to force writes to sink in */ -# define NV_CIO_CRE_HCUR_ADDR2_INDEX 0x2f /* cursor */ -# define NV_CIO_CRE_HCUR_ADDR0_INDEX 0x30 /* pixmap */ -# define NV_CIO_CRE_HCUR_ADDR0_ADR 6:0 -# define NV_CIO_CRE_HCUR_ASI 7:7 -# define NV_CIO_CRE_HCUR_ADDR1_INDEX 0x31 /* address */ -# define NV_CIO_CRE_HCUR_ADDR1_ENABLE 0:0 -# define NV_CIO_CRE_HCUR_ADDR1_CUR_DBL 1:1 -# define NV_CIO_CRE_HCUR_ADDR1_ADR 7:2 -# define NV_CIO_CRE_LCD__INDEX 0x33 -# define NV_CIO_CRE_LCD_LCD_SELECT 0:0 -# define NV_CIO_CRE_LCD_ROUTE_MASK 0x3b -# define NV_CIO_CRE_DDC0_STATUS__INDEX 0x36 -# define NV_CIO_CRE_DDC0_WR__INDEX 0x37 -# define NV_CIO_CRE_ILACE__INDEX 0x39 /* interlace */ -# define NV_CIO_CRE_SCRATCH3__INDEX 0x3b -# define NV_CIO_CRE_SCRATCH4__INDEX 0x3c -# define NV_CIO_CRE_DDC_STATUS__INDEX 0x3e -# define NV_CIO_CRE_DDC_WR__INDEX 0x3f -# define NV_CIO_CRE_EBR_INDEX 0x41 /* extra bits ? (vertical) */ -# define NV_CIO_CRE_EBR_VDT_11 0:0 -# define NV_CIO_CRE_EBR_VDE_11 2:2 -# define NV_CIO_CRE_EBR_VRS_11 4:4 -# define NV_CIO_CRE_EBR_VBS_11 6:6 -# define NV_CIO_CRE_42 0x42 -# define NV_CIO_CRE_42_OFFSET_11 6:6 -# define NV_CIO_CRE_43 0x43 -# define NV_CIO_CRE_44 0x44 /* head control */ -# define NV_CIO_CRE_CSB 0x45 /* colour saturation boost */ -# define NV_CIO_CRE_RCR 0x46 -# define NV_CIO_CRE_RCR_ENDIAN_BIG 7:7 -# define NV_CIO_CRE_47 0x47 /* extended fifo lwm, used on nv30+ */ -# define NV_CIO_CRE_49 0x49 -# define NV_CIO_CRE_4B 0x4b /* given patterns in 0x[2-3][a-c] regs, probably scratch 6 */ -# define NV_CIO_CRE_TVOUT_LATENCY 0x52 -# define NV_CIO_CRE_53 0x53 /* `fp_htiming' according to Haiku */ -# define NV_CIO_CRE_54 0x54 /* `fp_vtiming' according to Haiku */ -# define NV_CIO_CRE_57 0x57 /* index reg for cr58 */ -# define NV_CIO_CRE_58 0x58 /* data reg for cr57 */ -# define NV_CIO_CRE_59 0x59 /* related to on/off-chip-ness of digital outputs */ -# define NV_CIO_CRE_5B 0x5B /* newer colour saturation reg */ -# define NV_CIO_CRE_85 0x85 -# define NV_CIO_CRE_86 0x86 -#define NV_PRMCIO_INP0__COLOR 0x006013da - -#define NV_PRAMDAC_CU_START_POS 0x00680300 -# define NV_PRAMDAC_CU_START_POS_X 15:0 -# define NV_PRAMDAC_CU_START_POS_Y 31:16 -#define NV_RAMDAC_NV10_CURSYNC 0x00680404 - -#define NV_PRAMDAC_NVPLL_COEFF 0x00680500 -#define NV_PRAMDAC_MPLL_COEFF 0x00680504 -#define NV_PRAMDAC_VPLL_COEFF 0x00680508 -# define NV30_RAMDAC_ENABLE_VCO2 (8 << 4) - -#define NV_PRAMDAC_PLL_COEFF_SELECT 0x0068050c -# define NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE (4 << 0) -# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL (1 << 8) -# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL (2 << 8) -# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL (4 << 8) -# define NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 (8 << 8) -# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 (1 << 16) -# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 (2 << 16) -# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 (4 << 16) -# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2 (8 << 16) -# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_CLK_SOURCE_VIP (1 << 20) -# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2 (1 << 28) -# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2 (2 << 28) - -#define NV_PRAMDAC_PLL_SETUP_CONTROL 0x00680510 -#define NV_RAMDAC_VPLL2 0x00680520 -#define NV_PRAMDAC_SEL_CLK 0x00680524 -#define NV_RAMDAC_DITHER_NV11 0x00680528 -#define NV_PRAMDAC_DACCLK 0x0068052c -# define NV_PRAMDAC_DACCLK_SEL_DACCLK (1 << 0) - -#define NV_RAMDAC_NVPLL_B 0x00680570 -#define NV_RAMDAC_MPLL_B 0x00680574 -#define NV_RAMDAC_VPLL_B 0x00680578 -#define NV_RAMDAC_VPLL2_B 0x0068057c -# define NV31_RAMDAC_ENABLE_VCO2 (8 << 28) -#define NV_PRAMDAC_580 0x00680580 -# define NV_RAMDAC_580_VPLL1_ACTIVE (1 << 8) -# define NV_RAMDAC_580_VPLL2_ACTIVE (1 << 28) - -#define NV_PRAMDAC_GENERAL_CONTROL 0x00680600 -# define NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON (3 << 4) -# define NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL (1 << 8) -# define NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL (1 << 12) -# define NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM (2 << 16) -# define NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS (1 << 20) -# define NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG (2 << 28) -#define NV_PRAMDAC_TEST_CONTROL 0x00680608 -# define NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED (1 << 12) -# define NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF (1 << 16) -# define NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI (1 << 28) -#define NV_PRAMDAC_TESTPOINT_DATA 0x00680610 -# define NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK (8 << 28) -#define NV_PRAMDAC_630 0x00680630 -#define NV_PRAMDAC_634 0x00680634 - -#define NV_PRAMDAC_TV_SETUP 0x00680700 -#define NV_PRAMDAC_TV_VTOTAL 0x00680720 -#define NV_PRAMDAC_TV_VSKEW 0x00680724 -#define NV_PRAMDAC_TV_VSYNC_DELAY 0x00680728 -#define NV_PRAMDAC_TV_HTOTAL 0x0068072c -#define NV_PRAMDAC_TV_HSKEW 0x00680730 -#define NV_PRAMDAC_TV_HSYNC_DELAY 0x00680734 -#define NV_PRAMDAC_TV_HSYNC_DELAY2 0x00680738 - -#define NV_PRAMDAC_TV_SETUP 0x00680700 - -#define NV_PRAMDAC_FP_VDISPLAY_END 0x00680800 -#define NV_PRAMDAC_FP_VTOTAL 0x00680804 -#define NV_PRAMDAC_FP_VCRTC 0x00680808 -#define NV_PRAMDAC_FP_VSYNC_START 0x0068080c -#define NV_PRAMDAC_FP_VSYNC_END 0x00680810 -#define NV_PRAMDAC_FP_VVALID_START 0x00680814 -#define NV_PRAMDAC_FP_VVALID_END 0x00680818 -#define NV_PRAMDAC_FP_HDISPLAY_END 0x00680820 -#define NV_PRAMDAC_FP_HTOTAL 0x00680824 -#define NV_PRAMDAC_FP_HCRTC 0x00680828 -#define NV_PRAMDAC_FP_HSYNC_START 0x0068082c -#define NV_PRAMDAC_FP_HSYNC_END 0x00680830 -#define NV_PRAMDAC_FP_HVALID_START 0x00680834 -#define NV_PRAMDAC_FP_HVALID_END 0x00680838 - -#define NV_RAMDAC_FP_DITHER 0x0068083c -#define NV_PRAMDAC_FP_TG_CONTROL 0x00680848 -# define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS (1 << 0) -# define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE (2 << 0) -# define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS (1 << 4) -# define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE (2 << 4) -# define NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE (0 << 8) -# define NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER (1 << 8) -# define NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE (2 << 8) -# define NV_PRAMDAC_FP_TG_CONTROL_READ_PROG (1 << 20) -# define NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12 (1 << 24) -# define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS (1 << 28) -# define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE (2 << 28) -#define NV_PRAMDAC_FP_MARGIN_COLOR 0x0068084c -#define NV_PRAMDAC_850 0x00680850 -#define NV_PRAMDAC_85C 0x0068085c -#define NV_PRAMDAC_FP_DEBUG_0 0x00680880 -# define NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE (1 << 0) -# define NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE (1 << 4) -/* This doesn't seem to be essential for tmds, but still often set */ -# define NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED (8 << 4) -# define NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR (1 << 8) -# define NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR (1 << 12) -# define NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND (1 << 20) -# define NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND (1 << 24) -# define NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK (1 << 28) -#define NV_PRAMDAC_FP_DEBUG_1 0x00680884 -# define NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE 11:0 -# define NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE (1 << 12) -# define NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE 27:16 -# define NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE (1 << 28) -#define NV_PRAMDAC_FP_DEBUG_2 0x00680888 -#define NV_PRAMDAC_FP_DEBUG_3 0x0068088C - -/* see NV_PRAMDAC_INDIR_TMDS in rules.xml */ -#define NV_PRAMDAC_FP_TMDS_CONTROL 0x006808b0 -# define NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE (1 << 16) -#define NV_PRAMDAC_FP_TMDS_DATA 0x006808b4 - -#define NV_PRAMDAC_8C0 0x006808c0 - -/* Some kind of switch */ -#define NV_PRAMDAC_900 0x00680900 -#define NV_PRAMDAC_A20 0x00680A20 -#define NV_PRAMDAC_A24 0x00680A24 -#define NV_PRAMDAC_A34 0x00680A34 - -#define NV_PRAMDAC_CTV 0x00680c00 - -/* names fabricated from NV_USER_DAC info */ -#define NV_PRMDIO_PIXEL_MASK 0x006813c6 -# define NV_PRMDIO_PIXEL_MASK_MASK 0xff -#define NV_PRMDIO_READ_MODE_ADDRESS 0x006813c7 -#define NV_PRMDIO_WRITE_MODE_ADDRESS 0x006813c8 -#define NV_PRMDIO_PALETTE_DATA 0x006813c9 - -#define NV_PGRAPH_DEBUG_0 0x00400080 -#define NV_PGRAPH_DEBUG_1 0x00400084 -#define NV_PGRAPH_DEBUG_2_NV04 0x00400088 -#define NV_PGRAPH_DEBUG_2 0x00400620 -#define NV_PGRAPH_DEBUG_3 0x0040008c -#define NV_PGRAPH_DEBUG_4 0x00400090 -#define NV_PGRAPH_INTR 0x00400100 -#define NV_PGRAPH_INTR_EN 0x00400140 -#define NV_PGRAPH_CTX_CONTROL 0x00400144 -#define NV_PGRAPH_CTX_CONTROL_NV04 0x00400170 -#define NV_PGRAPH_ABS_UCLIP_XMIN 0x0040053C -#define NV_PGRAPH_ABS_UCLIP_YMIN 0x00400540 -#define NV_PGRAPH_ABS_UCLIP_XMAX 0x00400544 -#define NV_PGRAPH_ABS_UCLIP_YMAX 0x00400548 -#define NV_PGRAPH_BETA_AND 0x00400608 -#define NV_PGRAPH_LIMIT_VIOL_PIX 0x00400610 -#define NV_PGRAPH_BOFFSET0 0x00400640 -#define NV_PGRAPH_BOFFSET1 0x00400644 -#define NV_PGRAPH_BOFFSET2 0x00400648 -#define NV_PGRAPH_BLIMIT0 0x00400684 -#define NV_PGRAPH_BLIMIT1 0x00400688 -#define NV_PGRAPH_BLIMIT2 0x0040068c -#define NV_PGRAPH_STATUS 0x00400700 -#define NV_PGRAPH_SURFACE 0x00400710 -#define NV_PGRAPH_STATE 0x00400714 -#define NV_PGRAPH_FIFO 0x00400720 -#define NV_PGRAPH_PATTERN_SHAPE 0x00400810 -#define NV_PGRAPH_TILE 0x00400b00 - -#define NV_PVIDEO_INTR_EN 0x00008140 -#define NV_PVIDEO_BUFFER 0x00008700 -#define NV_PVIDEO_STOP 0x00008704 -#define NV_PVIDEO_UVPLANE_BASE(buff) (0x00008800+(buff)*4) -#define NV_PVIDEO_UVPLANE_LIMIT(buff) (0x00008808+(buff)*4) -#define NV_PVIDEO_UVPLANE_OFFSET_BUFF(buff) (0x00008820+(buff)*4) -#define NV_PVIDEO_BASE(buff) (0x00008900+(buff)*4) -#define NV_PVIDEO_LIMIT(buff) (0x00008908+(buff)*4) -#define NV_PVIDEO_LUMINANCE(buff) (0x00008910+(buff)*4) -#define NV_PVIDEO_CHROMINANCE(buff) (0x00008918+(buff)*4) -#define NV_PVIDEO_OFFSET_BUFF(buff) (0x00008920+(buff)*4) -#define NV_PVIDEO_SIZE_IN(buff) (0x00008928+(buff)*4) -#define NV_PVIDEO_POINT_IN(buff) (0x00008930+(buff)*4) -#define NV_PVIDEO_DS_DX(buff) (0x00008938+(buff)*4) -#define NV_PVIDEO_DT_DY(buff) (0x00008940+(buff)*4) -#define NV_PVIDEO_POINT_OUT(buff) (0x00008948+(buff)*4) -#define NV_PVIDEO_SIZE_OUT(buff) (0x00008950+(buff)*4) -#define NV_PVIDEO_FORMAT(buff) (0x00008958+(buff)*4) -# define NV_PVIDEO_FORMAT_PLANAR (1 << 0) -# define NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8 (1 << 16) -# define NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY (1 << 20) -# define NV_PVIDEO_FORMAT_MATRIX_ITURBT709 (1 << 24) -#define NV_PVIDEO_COLOR_KEY 0x00008B00 - -/* NV04 overlay defines from VIDIX & Haiku */ -#define NV_PVIDEO_INTR_EN_0 0x00680140 -#define NV_PVIDEO_STEP_SIZE 0x00680200 -#define NV_PVIDEO_CONTROL_Y 0x00680204 -#define NV_PVIDEO_CONTROL_X 0x00680208 -#define NV_PVIDEO_BUFF0_START_ADDRESS 0x0068020c -#define NV_PVIDEO_BUFF0_PITCH_LENGTH 0x00680214 -#define NV_PVIDEO_BUFF0_OFFSET 0x0068021c -#define NV_PVIDEO_BUFF1_START_ADDRESS 0x00680210 -#define NV_PVIDEO_BUFF1_PITCH_LENGTH 0x00680218 -#define NV_PVIDEO_BUFF1_OFFSET 0x00680220 -#define NV_PVIDEO_OE_STATE 0x00680224 -#define NV_PVIDEO_SU_STATE 0x00680228 -#define NV_PVIDEO_RM_STATE 0x0068022c -#define NV_PVIDEO_WINDOW_START 0x00680230 -#define NV_PVIDEO_WINDOW_SIZE 0x00680234 -#define NV_PVIDEO_FIFO_THRES_SIZE 0x00680238 -#define NV_PVIDEO_FIFO_BURST_LENGTH 0x0068023c -#define NV_PVIDEO_KEY 0x00680240 -#define NV_PVIDEO_OVERLAY 0x00680244 -#define NV_PVIDEO_RED_CSC_OFFSET 0x00680280 -#define NV_PVIDEO_GREEN_CSC_OFFSET 0x00680284 -#define NV_PVIDEO_BLUE_CSC_OFFSET 0x00680288 -#define NV_PVIDEO_CSC_ADJUST 0x0068028c - -#endif -- cgit v0.10.2