18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 2003 NVIDIA, Corporation 38c2ecf20Sopenharmony_ci * Copyright 2006 Dave Airlie 48c2ecf20Sopenharmony_ci * Copyright 2007 Maarten Maathuis 58c2ecf20Sopenharmony_ci * Copyright 2007-2009 Stuart Bennett 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 88c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 98c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 108c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 118c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 128c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next 158c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 168c2ecf20Sopenharmony_ci * Software. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 198c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 208c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 218c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 228c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 238c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 248c2ecf20Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "nouveau_drv.h" 308c2ecf20Sopenharmony_ci#include "nouveau_encoder.h" 318c2ecf20Sopenharmony_ci#include "nouveau_connector.h" 328c2ecf20Sopenharmony_ci#include "nouveau_crtc.h" 338c2ecf20Sopenharmony_ci#include "hw.h" 348c2ecf20Sopenharmony_ci#include "nvreg.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include <subdev/bios/gpio.h> 378c2ecf20Sopenharmony_ci#include <subdev/gpio.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include <nvif/timer.h> 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ciint nv04_dac_output_offset(struct drm_encoder *encoder) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; 448c2ecf20Sopenharmony_ci int offset = 0; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (dcb->or & (8 | DCB_OUTPUT_C)) 478c2ecf20Sopenharmony_ci offset += 0x68; 488c2ecf20Sopenharmony_ci if (dcb->or & (8 | DCB_OUTPUT_B)) 498c2ecf20Sopenharmony_ci offset += 0x2000; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return offset; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 558c2ecf20Sopenharmony_ci * arbitrary limit to number of sense oscillations tolerated in one sample 568c2ecf20Sopenharmony_ci * period (observed to be at least 13 in "nvidia") 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci#define MAX_HBLANK_OSC 20 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * arbitrary limit to number of conflicting sample pairs to tolerate at a 628c2ecf20Sopenharmony_ci * voltage step (observed to be at least 5 in "nvidia") 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci#define MAX_SAMPLE_PAIRS 10 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int sample_load_twice(struct drm_device *dev, bool sense[2]) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 698c2ecf20Sopenharmony_ci struct nvif_object *device = &drm->client.device.object; 708c2ecf20Sopenharmony_ci int i; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 738c2ecf20Sopenharmony_ci bool sense_a, sense_b, sense_b_prime; 748c2ecf20Sopenharmony_ci int j = 0; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* 778c2ecf20Sopenharmony_ci * wait for bit 0 clear -- out of hblank -- (say reg value 0x4), 788c2ecf20Sopenharmony_ci * then wait for transition 0x4->0x5->0x4: enter hblank, leave 798c2ecf20Sopenharmony_ci * hblank again 808c2ecf20Sopenharmony_ci * use a 10ms timeout (guards against crtc being inactive, in 818c2ecf20Sopenharmony_ci * which case blank state would never change) 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci if (nvif_msec(&drm->client.device, 10, 848c2ecf20Sopenharmony_ci if (!(nvif_rd32(device, NV_PRMCIO_INP0__COLOR) & 1)) 858c2ecf20Sopenharmony_ci break; 868c2ecf20Sopenharmony_ci ) < 0) 878c2ecf20Sopenharmony_ci return -EBUSY; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (nvif_msec(&drm->client.device, 10, 908c2ecf20Sopenharmony_ci if ( (nvif_rd32(device, NV_PRMCIO_INP0__COLOR) & 1)) 918c2ecf20Sopenharmony_ci break; 928c2ecf20Sopenharmony_ci ) < 0) 938c2ecf20Sopenharmony_ci return -EBUSY; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (nvif_msec(&drm->client.device, 10, 968c2ecf20Sopenharmony_ci if (!(nvif_rd32(device, NV_PRMCIO_INP0__COLOR) & 1)) 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci ) < 0) 998c2ecf20Sopenharmony_ci return -EBUSY; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci udelay(100); 1028c2ecf20Sopenharmony_ci /* when level triggers, sense is _LO_ */ 1038c2ecf20Sopenharmony_ci sense_a = nvif_rd08(device, NV_PRMCIO_INP0) & 0x10; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* take another reading until it agrees with sense_a... */ 1068c2ecf20Sopenharmony_ci do { 1078c2ecf20Sopenharmony_ci udelay(100); 1088c2ecf20Sopenharmony_ci sense_b = nvif_rd08(device, NV_PRMCIO_INP0) & 0x10; 1098c2ecf20Sopenharmony_ci if (sense_a != sense_b) { 1108c2ecf20Sopenharmony_ci sense_b_prime = 1118c2ecf20Sopenharmony_ci nvif_rd08(device, NV_PRMCIO_INP0) & 0x10; 1128c2ecf20Sopenharmony_ci if (sense_b == sense_b_prime) { 1138c2ecf20Sopenharmony_ci /* ... unless two consecutive subsequent 1148c2ecf20Sopenharmony_ci * samples agree; sense_a is replaced */ 1158c2ecf20Sopenharmony_ci sense_a = sense_b; 1168c2ecf20Sopenharmony_ci /* force mis-match so we loop */ 1178c2ecf20Sopenharmony_ci sense_b = !sense_a; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci } while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (j == MAX_HBLANK_OSC) 1238c2ecf20Sopenharmony_ci /* with so much oscillation, default to sense:LO */ 1248c2ecf20Sopenharmony_ci sense[i] = false; 1258c2ecf20Sopenharmony_ci else 1268c2ecf20Sopenharmony_ci sense[i] = sense_a; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder, 1338c2ecf20Sopenharmony_ci struct drm_connector *connector) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 1368c2ecf20Sopenharmony_ci struct nvif_object *device = &nouveau_drm(dev)->client.device.object; 1378c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 1388c2ecf20Sopenharmony_ci uint8_t saved_seq1, saved_pi, saved_rpc1, saved_cr_mode; 1398c2ecf20Sopenharmony_ci uint8_t saved_palette0[3], saved_palette_mask; 1408c2ecf20Sopenharmony_ci uint32_t saved_rtest_ctrl, saved_rgen_ctrl; 1418c2ecf20Sopenharmony_ci int i; 1428c2ecf20Sopenharmony_ci uint8_t blue; 1438c2ecf20Sopenharmony_ci bool sense = true; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* 1468c2ecf20Sopenharmony_ci * for this detection to work, there needs to be a mode set up on the 1478c2ecf20Sopenharmony_ci * CRTC. this is presumed to be the case 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (nv_two_heads(dev)) 1518c2ecf20Sopenharmony_ci /* only implemented for head A for now */ 1528c2ecf20Sopenharmony_ci NVSetOwner(dev, 0); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci saved_cr_mode = NVReadVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX); 1558c2ecf20Sopenharmony_ci NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode | 0x80); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX); 1588c2ecf20Sopenharmony_ci NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL); 1618c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, 1628c2ecf20Sopenharmony_ci saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci msleep(10); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX); 1678c2ecf20Sopenharmony_ci NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, 1688c2ecf20Sopenharmony_ci saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT))); 1698c2ecf20Sopenharmony_ci saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX); 1708c2ecf20Sopenharmony_ci NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_READ_MODE_ADDRESS, 0x0); 1738c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) 1748c2ecf20Sopenharmony_ci saved_palette0[i] = nvif_rd08(device, NV_PRMDIO_PALETTE_DATA); 1758c2ecf20Sopenharmony_ci saved_palette_mask = nvif_rd08(device, NV_PRMDIO_PIXEL_MASK); 1768c2ecf20Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_PIXEL_MASK, 0); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL); 1798c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, 1808c2ecf20Sopenharmony_ci (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | 1818c2ecf20Sopenharmony_ci NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) | 1828c2ecf20Sopenharmony_ci NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci blue = 8; /* start of test range */ 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci do { 1878c2ecf20Sopenharmony_ci bool sense_pair[2]; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); 1908c2ecf20Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, 0); 1918c2ecf20Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, 0); 1928c2ecf20Sopenharmony_ci /* testing blue won't find monochrome monitors. I don't care */ 1938c2ecf20Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, blue); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci i = 0; 1968c2ecf20Sopenharmony_ci /* take sample pairs until both samples in the pair agree */ 1978c2ecf20Sopenharmony_ci do { 1988c2ecf20Sopenharmony_ci if (sample_load_twice(dev, sense_pair)) 1998c2ecf20Sopenharmony_ci goto out; 2008c2ecf20Sopenharmony_ci } while ((sense_pair[0] != sense_pair[1]) && 2018c2ecf20Sopenharmony_ci ++i < MAX_SAMPLE_PAIRS); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (i == MAX_SAMPLE_PAIRS) 2048c2ecf20Sopenharmony_ci /* too much oscillation defaults to LO */ 2058c2ecf20Sopenharmony_ci sense = false; 2068c2ecf20Sopenharmony_ci else 2078c2ecf20Sopenharmony_ci sense = sense_pair[0]; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* 2108c2ecf20Sopenharmony_ci * if sense goes LO before blue ramps to 0x18, monitor is not connected. 2118c2ecf20Sopenharmony_ci * ergo, if blue gets to 0x18, monitor must be connected 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci } while (++blue < 0x18 && sense); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ciout: 2168c2ecf20Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_PIXEL_MASK, saved_palette_mask); 2178c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl); 2188c2ecf20Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); 2198c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) 2208c2ecf20Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]); 2218c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl); 2228c2ecf20Sopenharmony_ci NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi); 2238c2ecf20Sopenharmony_ci NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1); 2248c2ecf20Sopenharmony_ci NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1); 2258c2ecf20Sopenharmony_ci NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (blue == 0x18) { 2288c2ecf20Sopenharmony_ci NV_DEBUG(drm, "Load detected on head A\n"); 2298c2ecf20Sopenharmony_ci return connector_status_connected; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return connector_status_disconnected; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ciuint32_t nv17_dac_sample_load(struct drm_encoder *encoder) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 2388c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 2398c2ecf20Sopenharmony_ci struct nvif_object *device = &nouveau_drm(dev)->client.device.object; 2408c2ecf20Sopenharmony_ci struct nvkm_gpio *gpio = nvxx_gpio(&drm->client.device); 2418c2ecf20Sopenharmony_ci struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; 2428c2ecf20Sopenharmony_ci uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder); 2438c2ecf20Sopenharmony_ci uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, 2448c2ecf20Sopenharmony_ci saved_rtest_ctrl, saved_gpio0 = 0, saved_gpio1 = 0, temp, routput; 2458c2ecf20Sopenharmony_ci int head; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci#define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20) 2488c2ecf20Sopenharmony_ci if (dcb->type == DCB_OUTPUT_TV) { 2498c2ecf20Sopenharmony_ci testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (drm->vbios.tvdactestval) 2528c2ecf20Sopenharmony_ci testval = drm->vbios.tvdactestval; 2538c2ecf20Sopenharmony_ci } else { 2548c2ecf20Sopenharmony_ci testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */ 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (drm->vbios.dactestval) 2578c2ecf20Sopenharmony_ci testval = drm->vbios.dactestval; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); 2618c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, 2628c2ecf20Sopenharmony_ci saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci saved_powerctrl_2 = nvif_rd32(device, NV_PBUS_POWERCTRL_2); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci nvif_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff); 2678c2ecf20Sopenharmony_ci if (regoffset == 0x68) { 2688c2ecf20Sopenharmony_ci saved_powerctrl_4 = nvif_rd32(device, NV_PBUS_POWERCTRL_4); 2698c2ecf20Sopenharmony_ci nvif_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf); 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (gpio) { 2738c2ecf20Sopenharmony_ci saved_gpio1 = nvkm_gpio_get(gpio, 0, DCB_GPIO_TVDAC1, 0xff); 2748c2ecf20Sopenharmony_ci saved_gpio0 = nvkm_gpio_get(gpio, 0, DCB_GPIO_TVDAC0, 0xff); 2758c2ecf20Sopenharmony_ci nvkm_gpio_set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, dcb->type == DCB_OUTPUT_TV); 2768c2ecf20Sopenharmony_ci nvkm_gpio_set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, dcb->type == DCB_OUTPUT_TV); 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci msleep(4); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); 2828c2ecf20Sopenharmony_ci head = (saved_routput & 0x100) >> 8; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* if there's a spare crtc, using it will minimise flicker */ 2858c2ecf20Sopenharmony_ci if (!(NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX) & 0xC0)) 2868c2ecf20Sopenharmony_ci head ^= 1; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */ 2898c2ecf20Sopenharmony_ci routput = (saved_routput & 0xfffffece) | head << 8; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CURIE) { 2928c2ecf20Sopenharmony_ci if (dcb->type == DCB_OUTPUT_TV) 2938c2ecf20Sopenharmony_ci routput |= 0x1a << 16; 2948c2ecf20Sopenharmony_ci else 2958c2ecf20Sopenharmony_ci routput &= ~(0x1a << 16); 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput); 2998c2ecf20Sopenharmony_ci msleep(1); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); 3028c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 3058c2ecf20Sopenharmony_ci NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval); 3068c2ecf20Sopenharmony_ci temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); 3078c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, 3088c2ecf20Sopenharmony_ci temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); 3098c2ecf20Sopenharmony_ci msleep(5); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci sample = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); 3128c2ecf20Sopenharmony_ci /* do it again just in case it's a residual current */ 3138c2ecf20Sopenharmony_ci sample &= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); 3168c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, 3178c2ecf20Sopenharmony_ci temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); 3188c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* bios does something more complex for restoring, but I think this is good enough */ 3218c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput); 3228c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl); 3238c2ecf20Sopenharmony_ci if (regoffset == 0x68) 3248c2ecf20Sopenharmony_ci nvif_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4); 3258c2ecf20Sopenharmony_ci nvif_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (gpio) { 3288c2ecf20Sopenharmony_ci nvkm_gpio_set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, saved_gpio1); 3298c2ecf20Sopenharmony_ci nvkm_gpio_set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, saved_gpio0); 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return sample; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic enum drm_connector_status 3368c2ecf20Sopenharmony_cinv17_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(encoder->dev); 3398c2ecf20Sopenharmony_ci struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (nv04_dac_in_use(encoder)) 3428c2ecf20Sopenharmony_ci return connector_status_disconnected; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (nv17_dac_sample_load(encoder) & 3458c2ecf20Sopenharmony_ci NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI) { 3468c2ecf20Sopenharmony_ci NV_DEBUG(drm, "Load detected on output %c\n", 3478c2ecf20Sopenharmony_ci '@' + ffs(dcb->or)); 3488c2ecf20Sopenharmony_ci return connector_status_connected; 3498c2ecf20Sopenharmony_ci } else { 3508c2ecf20Sopenharmony_ci return connector_status_disconnected; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic bool nv04_dac_mode_fixup(struct drm_encoder *encoder, 3558c2ecf20Sopenharmony_ci const struct drm_display_mode *mode, 3568c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci if (nv04_dac_in_use(encoder)) 3598c2ecf20Sopenharmony_ci return false; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return true; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic void nv04_dac_prepare(struct drm_encoder *encoder) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci const struct drm_encoder_helper_funcs *helper = encoder->helper_private; 3678c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 3688c2ecf20Sopenharmony_ci int head = nouveau_crtc(encoder->crtc)->index; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci helper->dpms(encoder, DRM_MODE_DPMS_OFF); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci nv04_dfp_disable(dev, head); 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic void nv04_dac_mode_set(struct drm_encoder *encoder, 3768c2ecf20Sopenharmony_ci struct drm_display_mode *mode, 3778c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 3808c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 3818c2ecf20Sopenharmony_ci int head = nouveau_crtc(encoder->crtc)->index; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (nv_gf4_disp_arch(dev)) { 3848c2ecf20Sopenharmony_ci struct drm_encoder *rebind; 3858c2ecf20Sopenharmony_ci uint32_t dac_offset = nv04_dac_output_offset(encoder); 3868c2ecf20Sopenharmony_ci uint32_t otherdac; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* bit 16-19 are bits that are set on some G70 cards, 3898c2ecf20Sopenharmony_ci * but don't seem to have much effect */ 3908c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, 3918c2ecf20Sopenharmony_ci head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK); 3928c2ecf20Sopenharmony_ci /* force any other vga encoders to bind to the other crtc */ 3938c2ecf20Sopenharmony_ci list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) { 3948c2ecf20Sopenharmony_ci if (rebind == encoder 3958c2ecf20Sopenharmony_ci || nouveau_encoder(rebind)->dcb->type != DCB_OUTPUT_ANALOG) 3968c2ecf20Sopenharmony_ci continue; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci dac_offset = nv04_dac_output_offset(rebind); 3998c2ecf20Sopenharmony_ci otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset); 4008c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, 4018c2ecf20Sopenharmony_ci (otherdac & ~0x0100) | (head ^ 1) << 8); 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* This could use refinement for flatpanels, but it should work this way */ 4068c2ecf20Sopenharmony_ci if (drm->client.device.info.chipset < 0x44) 4078c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); 4088c2ecf20Sopenharmony_ci else 4098c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic void nv04_dac_commit(struct drm_encoder *encoder) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 4158c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(encoder->dev); 4168c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); 4178c2ecf20Sopenharmony_ci const struct drm_encoder_helper_funcs *helper = encoder->helper_private; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci helper->dpms(encoder, DRM_MODE_DPMS_ON); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n", 4228c2ecf20Sopenharmony_ci nv04_encoder_get_connector(nv_encoder)->base.name, 4238c2ecf20Sopenharmony_ci nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_civoid nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 4298c2ecf20Sopenharmony_ci struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (nv_gf4_disp_arch(dev)) { 4328c2ecf20Sopenharmony_ci uint32_t *dac_users = &nv04_display(dev)->dac_users[ffs(dcb->or) - 1]; 4338c2ecf20Sopenharmony_ci int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder); 4348c2ecf20Sopenharmony_ci uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (enable) { 4378c2ecf20Sopenharmony_ci *dac_users |= 1 << dcb->index; 4388c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci } else { 4418c2ecf20Sopenharmony_ci *dac_users &= ~(1 << dcb->index); 4428c2ecf20Sopenharmony_ci if (!*dac_users) 4438c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, dacclk_off, 4448c2ecf20Sopenharmony_ci dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK); 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci/* Check if the DAC corresponding to 'encoder' is being used by 4508c2ecf20Sopenharmony_ci * someone else. */ 4518c2ecf20Sopenharmony_cibool nv04_dac_in_use(struct drm_encoder *encoder) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 4548c2ecf20Sopenharmony_ci struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci return nv_gf4_disp_arch(encoder->dev) && 4578c2ecf20Sopenharmony_ci (nv04_display(dev)->dac_users[ffs(dcb->or) - 1] & ~(1 << dcb->index)); 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic void nv04_dac_dpms(struct drm_encoder *encoder, int mode) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 4638c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(encoder->dev); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (nv_encoder->last_dpms == mode) 4668c2ecf20Sopenharmony_ci return; 4678c2ecf20Sopenharmony_ci nv_encoder->last_dpms = mode; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci NV_DEBUG(drm, "Setting dpms mode %d on vga encoder (output %d)\n", 4708c2ecf20Sopenharmony_ci mode, nv_encoder->dcb->index); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic void nv04_dac_save(struct drm_encoder *encoder) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 4788c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (nv_gf4_disp_arch(dev)) 4818c2ecf20Sopenharmony_ci nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + 4828c2ecf20Sopenharmony_ci nv04_dac_output_offset(encoder)); 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic void nv04_dac_restore(struct drm_encoder *encoder) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 4888c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (nv_gf4_disp_arch(dev)) 4918c2ecf20Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder), 4928c2ecf20Sopenharmony_ci nv_encoder->restore.output); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci nv_encoder->last_dpms = NV_DPMS_CLEARED; 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic void nv04_dac_destroy(struct drm_encoder *encoder) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci drm_encoder_cleanup(encoder); 5028c2ecf20Sopenharmony_ci kfree(nv_encoder); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = { 5068c2ecf20Sopenharmony_ci .dpms = nv04_dac_dpms, 5078c2ecf20Sopenharmony_ci .mode_fixup = nv04_dac_mode_fixup, 5088c2ecf20Sopenharmony_ci .prepare = nv04_dac_prepare, 5098c2ecf20Sopenharmony_ci .commit = nv04_dac_commit, 5108c2ecf20Sopenharmony_ci .mode_set = nv04_dac_mode_set, 5118c2ecf20Sopenharmony_ci .detect = nv04_dac_detect 5128c2ecf20Sopenharmony_ci}; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = { 5158c2ecf20Sopenharmony_ci .dpms = nv04_dac_dpms, 5168c2ecf20Sopenharmony_ci .mode_fixup = nv04_dac_mode_fixup, 5178c2ecf20Sopenharmony_ci .prepare = nv04_dac_prepare, 5188c2ecf20Sopenharmony_ci .commit = nv04_dac_commit, 5198c2ecf20Sopenharmony_ci .mode_set = nv04_dac_mode_set, 5208c2ecf20Sopenharmony_ci .detect = nv17_dac_detect 5218c2ecf20Sopenharmony_ci}; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic const struct drm_encoder_funcs nv04_dac_funcs = { 5248c2ecf20Sopenharmony_ci .destroy = nv04_dac_destroy, 5258c2ecf20Sopenharmony_ci}; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ciint 5288c2ecf20Sopenharmony_cinv04_dac_create(struct drm_connector *connector, struct dcb_output *entry) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci const struct drm_encoder_helper_funcs *helper; 5318c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = NULL; 5328c2ecf20Sopenharmony_ci struct drm_device *dev = connector->dev; 5338c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); 5368c2ecf20Sopenharmony_ci if (!nv_encoder) 5378c2ecf20Sopenharmony_ci return -ENOMEM; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci encoder = to_drm_encoder(nv_encoder); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci nv_encoder->dcb = entry; 5428c2ecf20Sopenharmony_ci nv_encoder->or = ffs(entry->or) - 1; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci nv_encoder->enc_save = nv04_dac_save; 5458c2ecf20Sopenharmony_ci nv_encoder->enc_restore = nv04_dac_restore; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (nv_gf4_disp_arch(dev)) 5488c2ecf20Sopenharmony_ci helper = &nv17_dac_helper_funcs; 5498c2ecf20Sopenharmony_ci else 5508c2ecf20Sopenharmony_ci helper = &nv04_dac_helper_funcs; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC, 5538c2ecf20Sopenharmony_ci NULL); 5548c2ecf20Sopenharmony_ci drm_encoder_helper_add(encoder, helper); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci encoder->possible_crtcs = entry->heads; 5578c2ecf20Sopenharmony_ci encoder->possible_clones = 0; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci drm_connector_attach_encoder(connector, encoder); 5608c2ecf20Sopenharmony_ci return 0; 5618c2ecf20Sopenharmony_ci} 562