162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright 2003 NVIDIA, Corporation 362306a36Sopenharmony_ci * Copyright 2006 Dave Airlie 462306a36Sopenharmony_ci * Copyright 2007 Maarten Maathuis 562306a36Sopenharmony_ci * Copyright 2007-2009 Stuart Bennett 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 862306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 962306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 1062306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 1162306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 1262306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the next 1562306a36Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 1662306a36Sopenharmony_ci * Software. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1962306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2062306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 2162306a36Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2262306a36Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2362306a36Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 2462306a36Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <drm/drm_modeset_helper_vtables.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "nouveau_drv.h" 3062306a36Sopenharmony_ci#include "nouveau_encoder.h" 3162306a36Sopenharmony_ci#include "nouveau_connector.h" 3262306a36Sopenharmony_ci#include "nouveau_crtc.h" 3362306a36Sopenharmony_ci#include "hw.h" 3462306a36Sopenharmony_ci#include "nvreg.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <subdev/bios/gpio.h> 3762306a36Sopenharmony_ci#include <subdev/gpio.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include <nvif/timer.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ciint nv04_dac_output_offset(struct drm_encoder *encoder) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; 4462306a36Sopenharmony_ci int offset = 0; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (dcb->or & (8 | DCB_OUTPUT_C)) 4762306a36Sopenharmony_ci offset += 0x68; 4862306a36Sopenharmony_ci if (dcb->or & (8 | DCB_OUTPUT_B)) 4962306a36Sopenharmony_ci offset += 0x2000; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci return offset; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * arbitrary limit to number of sense oscillations tolerated in one sample 5662306a36Sopenharmony_ci * period (observed to be at least 13 in "nvidia") 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci#define MAX_HBLANK_OSC 20 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * arbitrary limit to number of conflicting sample pairs to tolerate at a 6262306a36Sopenharmony_ci * voltage step (observed to be at least 5 in "nvidia") 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci#define MAX_SAMPLE_PAIRS 10 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int sample_load_twice(struct drm_device *dev, bool sense[2]) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 6962306a36Sopenharmony_ci struct nvif_object *device = &drm->client.device.object; 7062306a36Sopenharmony_ci int i; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 7362306a36Sopenharmony_ci bool sense_a, sense_b, sense_b_prime; 7462306a36Sopenharmony_ci int j = 0; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * wait for bit 0 clear -- out of hblank -- (say reg value 0x4), 7862306a36Sopenharmony_ci * then wait for transition 0x4->0x5->0x4: enter hblank, leave 7962306a36Sopenharmony_ci * hblank again 8062306a36Sopenharmony_ci * use a 10ms timeout (guards against crtc being inactive, in 8162306a36Sopenharmony_ci * which case blank state would never change) 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci if (nvif_msec(&drm->client.device, 10, 8462306a36Sopenharmony_ci if (!(nvif_rd32(device, NV_PRMCIO_INP0__COLOR) & 1)) 8562306a36Sopenharmony_ci break; 8662306a36Sopenharmony_ci ) < 0) 8762306a36Sopenharmony_ci return -EBUSY; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (nvif_msec(&drm->client.device, 10, 9062306a36Sopenharmony_ci if ( (nvif_rd32(device, NV_PRMCIO_INP0__COLOR) & 1)) 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci ) < 0) 9362306a36Sopenharmony_ci return -EBUSY; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (nvif_msec(&drm->client.device, 10, 9662306a36Sopenharmony_ci if (!(nvif_rd32(device, NV_PRMCIO_INP0__COLOR) & 1)) 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci ) < 0) 9962306a36Sopenharmony_ci return -EBUSY; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci udelay(100); 10262306a36Sopenharmony_ci /* when level triggers, sense is _LO_ */ 10362306a36Sopenharmony_ci sense_a = nvif_rd08(device, NV_PRMCIO_INP0) & 0x10; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* take another reading until it agrees with sense_a... */ 10662306a36Sopenharmony_ci do { 10762306a36Sopenharmony_ci udelay(100); 10862306a36Sopenharmony_ci sense_b = nvif_rd08(device, NV_PRMCIO_INP0) & 0x10; 10962306a36Sopenharmony_ci if (sense_a != sense_b) { 11062306a36Sopenharmony_ci sense_b_prime = 11162306a36Sopenharmony_ci nvif_rd08(device, NV_PRMCIO_INP0) & 0x10; 11262306a36Sopenharmony_ci if (sense_b == sense_b_prime) { 11362306a36Sopenharmony_ci /* ... unless two consecutive subsequent 11462306a36Sopenharmony_ci * samples agree; sense_a is replaced */ 11562306a36Sopenharmony_ci sense_a = sense_b; 11662306a36Sopenharmony_ci /* force mis-match so we loop */ 11762306a36Sopenharmony_ci sense_b = !sense_a; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci } while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (j == MAX_HBLANK_OSC) 12362306a36Sopenharmony_ci /* with so much oscillation, default to sense:LO */ 12462306a36Sopenharmony_ci sense[i] = false; 12562306a36Sopenharmony_ci else 12662306a36Sopenharmony_ci sense[i] = sense_a; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder, 13362306a36Sopenharmony_ci struct drm_connector *connector) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 13662306a36Sopenharmony_ci struct nvif_object *device = &nouveau_drm(dev)->client.device.object; 13762306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 13862306a36Sopenharmony_ci uint8_t saved_seq1, saved_pi, saved_rpc1, saved_cr_mode; 13962306a36Sopenharmony_ci uint8_t saved_palette0[3], saved_palette_mask; 14062306a36Sopenharmony_ci uint32_t saved_rtest_ctrl, saved_rgen_ctrl; 14162306a36Sopenharmony_ci int i; 14262306a36Sopenharmony_ci uint8_t blue; 14362306a36Sopenharmony_ci bool sense = true; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* 14662306a36Sopenharmony_ci * for this detection to work, there needs to be a mode set up on the 14762306a36Sopenharmony_ci * CRTC. this is presumed to be the case 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (nv_two_heads(dev)) 15162306a36Sopenharmony_ci /* only implemented for head A for now */ 15262306a36Sopenharmony_ci NVSetOwner(dev, 0); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci saved_cr_mode = NVReadVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX); 15562306a36Sopenharmony_ci NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode | 0x80); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX); 15862306a36Sopenharmony_ci NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL); 16162306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, 16262306a36Sopenharmony_ci saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci msleep(10); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX); 16762306a36Sopenharmony_ci NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, 16862306a36Sopenharmony_ci saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT))); 16962306a36Sopenharmony_ci saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX); 17062306a36Sopenharmony_ci NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_READ_MODE_ADDRESS, 0x0); 17362306a36Sopenharmony_ci for (i = 0; i < 3; i++) 17462306a36Sopenharmony_ci saved_palette0[i] = nvif_rd08(device, NV_PRMDIO_PALETTE_DATA); 17562306a36Sopenharmony_ci saved_palette_mask = nvif_rd08(device, NV_PRMDIO_PIXEL_MASK); 17662306a36Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_PIXEL_MASK, 0); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL); 17962306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, 18062306a36Sopenharmony_ci (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | 18162306a36Sopenharmony_ci NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) | 18262306a36Sopenharmony_ci NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci blue = 8; /* start of test range */ 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci do { 18762306a36Sopenharmony_ci bool sense_pair[2]; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); 19062306a36Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, 0); 19162306a36Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, 0); 19262306a36Sopenharmony_ci /* testing blue won't find monochrome monitors. I don't care */ 19362306a36Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, blue); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci i = 0; 19662306a36Sopenharmony_ci /* take sample pairs until both samples in the pair agree */ 19762306a36Sopenharmony_ci do { 19862306a36Sopenharmony_ci if (sample_load_twice(dev, sense_pair)) 19962306a36Sopenharmony_ci goto out; 20062306a36Sopenharmony_ci } while ((sense_pair[0] != sense_pair[1]) && 20162306a36Sopenharmony_ci ++i < MAX_SAMPLE_PAIRS); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (i == MAX_SAMPLE_PAIRS) 20462306a36Sopenharmony_ci /* too much oscillation defaults to LO */ 20562306a36Sopenharmony_ci sense = false; 20662306a36Sopenharmony_ci else 20762306a36Sopenharmony_ci sense = sense_pair[0]; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* 21062306a36Sopenharmony_ci * if sense goes LO before blue ramps to 0x18, monitor is not connected. 21162306a36Sopenharmony_ci * ergo, if blue gets to 0x18, monitor must be connected 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ci } while (++blue < 0x18 && sense); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ciout: 21662306a36Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_PIXEL_MASK, saved_palette_mask); 21762306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl); 21862306a36Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); 21962306a36Sopenharmony_ci for (i = 0; i < 3; i++) 22062306a36Sopenharmony_ci nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]); 22162306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl); 22262306a36Sopenharmony_ci NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi); 22362306a36Sopenharmony_ci NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1); 22462306a36Sopenharmony_ci NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1); 22562306a36Sopenharmony_ci NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (blue == 0x18) { 22862306a36Sopenharmony_ci NV_DEBUG(drm, "Load detected on head A\n"); 22962306a36Sopenharmony_ci return connector_status_connected; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return connector_status_disconnected; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ciuint32_t nv17_dac_sample_load(struct drm_encoder *encoder) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 23862306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 23962306a36Sopenharmony_ci struct nvif_object *device = &nouveau_drm(dev)->client.device.object; 24062306a36Sopenharmony_ci struct nvkm_gpio *gpio = nvxx_gpio(&drm->client.device); 24162306a36Sopenharmony_ci struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; 24262306a36Sopenharmony_ci uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder); 24362306a36Sopenharmony_ci uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, 24462306a36Sopenharmony_ci saved_rtest_ctrl, saved_gpio0 = 0, saved_gpio1 = 0, temp, routput; 24562306a36Sopenharmony_ci int head; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci#define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20) 24862306a36Sopenharmony_ci if (dcb->type == DCB_OUTPUT_TV) { 24962306a36Sopenharmony_ci testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (drm->vbios.tvdactestval) 25262306a36Sopenharmony_ci testval = drm->vbios.tvdactestval; 25362306a36Sopenharmony_ci } else { 25462306a36Sopenharmony_ci testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */ 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (drm->vbios.dactestval) 25762306a36Sopenharmony_ci testval = drm->vbios.dactestval; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); 26162306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, 26262306a36Sopenharmony_ci saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci saved_powerctrl_2 = nvif_rd32(device, NV_PBUS_POWERCTRL_2); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci nvif_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff); 26762306a36Sopenharmony_ci if (regoffset == 0x68) { 26862306a36Sopenharmony_ci saved_powerctrl_4 = nvif_rd32(device, NV_PBUS_POWERCTRL_4); 26962306a36Sopenharmony_ci nvif_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf); 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (gpio) { 27362306a36Sopenharmony_ci saved_gpio1 = nvkm_gpio_get(gpio, 0, DCB_GPIO_TVDAC1, 0xff); 27462306a36Sopenharmony_ci saved_gpio0 = nvkm_gpio_get(gpio, 0, DCB_GPIO_TVDAC0, 0xff); 27562306a36Sopenharmony_ci nvkm_gpio_set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, dcb->type == DCB_OUTPUT_TV); 27662306a36Sopenharmony_ci nvkm_gpio_set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, dcb->type == DCB_OUTPUT_TV); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci msleep(4); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); 28262306a36Sopenharmony_ci head = (saved_routput & 0x100) >> 8; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* if there's a spare crtc, using it will minimise flicker */ 28562306a36Sopenharmony_ci if (!(NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX) & 0xC0)) 28662306a36Sopenharmony_ci head ^= 1; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */ 28962306a36Sopenharmony_ci routput = (saved_routput & 0xfffffece) | head << 8; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CURIE) { 29262306a36Sopenharmony_ci if (dcb->type == DCB_OUTPUT_TV) 29362306a36Sopenharmony_ci routput |= 0x1a << 16; 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci routput &= ~(0x1a << 16); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput); 29962306a36Sopenharmony_ci msleep(1); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); 30262306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 30562306a36Sopenharmony_ci NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval); 30662306a36Sopenharmony_ci temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); 30762306a36Sopenharmony_ci NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, 30862306a36Sopenharmony_ci temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); 30962306a36Sopenharmony_ci msleep(5); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci sample = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); 31262306a36Sopenharmony_ci /* do it again just in case it's a residual current */ 31362306a36Sopenharmony_ci sample &= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); 31662306a36Sopenharmony_ci NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, 31762306a36Sopenharmony_ci temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); 31862306a36Sopenharmony_ci NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* bios does something more complex for restoring, but I think this is good enough */ 32162306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput); 32262306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl); 32362306a36Sopenharmony_ci if (regoffset == 0x68) 32462306a36Sopenharmony_ci nvif_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4); 32562306a36Sopenharmony_ci nvif_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (gpio) { 32862306a36Sopenharmony_ci nvkm_gpio_set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, saved_gpio1); 32962306a36Sopenharmony_ci nvkm_gpio_set(gpio, 0, DCB_GPIO_TVDAC0, 0xff, saved_gpio0); 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return sample; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic enum drm_connector_status 33662306a36Sopenharmony_cinv17_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(encoder->dev); 33962306a36Sopenharmony_ci struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (nv04_dac_in_use(encoder)) 34262306a36Sopenharmony_ci return connector_status_disconnected; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (nv17_dac_sample_load(encoder) & 34562306a36Sopenharmony_ci NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI) { 34662306a36Sopenharmony_ci NV_DEBUG(drm, "Load detected on output %c\n", 34762306a36Sopenharmony_ci '@' + ffs(dcb->or)); 34862306a36Sopenharmony_ci return connector_status_connected; 34962306a36Sopenharmony_ci } else { 35062306a36Sopenharmony_ci return connector_status_disconnected; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic bool nv04_dac_mode_fixup(struct drm_encoder *encoder, 35562306a36Sopenharmony_ci const struct drm_display_mode *mode, 35662306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci if (nv04_dac_in_use(encoder)) 35962306a36Sopenharmony_ci return false; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return true; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic void nv04_dac_prepare(struct drm_encoder *encoder) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci const struct drm_encoder_helper_funcs *helper = encoder->helper_private; 36762306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 36862306a36Sopenharmony_ci int head = nouveau_crtc(encoder->crtc)->index; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci helper->dpms(encoder, DRM_MODE_DPMS_OFF); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci nv04_dfp_disable(dev, head); 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic void nv04_dac_mode_set(struct drm_encoder *encoder, 37662306a36Sopenharmony_ci struct drm_display_mode *mode, 37762306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 38062306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 38162306a36Sopenharmony_ci int head = nouveau_crtc(encoder->crtc)->index; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (nv_gf4_disp_arch(dev)) { 38462306a36Sopenharmony_ci struct drm_encoder *rebind; 38562306a36Sopenharmony_ci uint32_t dac_offset = nv04_dac_output_offset(encoder); 38662306a36Sopenharmony_ci uint32_t otherdac; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* bit 16-19 are bits that are set on some G70 cards, 38962306a36Sopenharmony_ci * but don't seem to have much effect */ 39062306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, 39162306a36Sopenharmony_ci head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK); 39262306a36Sopenharmony_ci /* force any other vga encoders to bind to the other crtc */ 39362306a36Sopenharmony_ci list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) { 39462306a36Sopenharmony_ci if (rebind == encoder 39562306a36Sopenharmony_ci || nouveau_encoder(rebind)->dcb->type != DCB_OUTPUT_ANALOG) 39662306a36Sopenharmony_ci continue; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci dac_offset = nv04_dac_output_offset(rebind); 39962306a36Sopenharmony_ci otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset); 40062306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, 40162306a36Sopenharmony_ci (otherdac & ~0x0100) | (head ^ 1) << 8); 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* This could use refinement for flatpanels, but it should work this way */ 40662306a36Sopenharmony_ci if (drm->client.device.info.chipset < 0x44) 40762306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); 40862306a36Sopenharmony_ci else 40962306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic void nv04_dac_commit(struct drm_encoder *encoder) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 41562306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(encoder->dev); 41662306a36Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); 41762306a36Sopenharmony_ci const struct drm_encoder_helper_funcs *helper = encoder->helper_private; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci helper->dpms(encoder, DRM_MODE_DPMS_ON); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n", 42262306a36Sopenharmony_ci nv04_encoder_get_connector(nv_encoder)->base.name, 42362306a36Sopenharmony_ci nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_civoid nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 42962306a36Sopenharmony_ci struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (nv_gf4_disp_arch(dev)) { 43262306a36Sopenharmony_ci uint32_t *dac_users = &nv04_display(dev)->dac_users[ffs(dcb->or) - 1]; 43362306a36Sopenharmony_ci int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder); 43462306a36Sopenharmony_ci uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (enable) { 43762306a36Sopenharmony_ci *dac_users |= 1 << dcb->index; 43862306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci } else { 44162306a36Sopenharmony_ci *dac_users &= ~(1 << dcb->index); 44262306a36Sopenharmony_ci if (!*dac_users) 44362306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, dacclk_off, 44462306a36Sopenharmony_ci dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK); 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/* Check if the DAC corresponding to 'encoder' is being used by 45062306a36Sopenharmony_ci * someone else. */ 45162306a36Sopenharmony_cibool nv04_dac_in_use(struct drm_encoder *encoder) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 45462306a36Sopenharmony_ci struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci return nv_gf4_disp_arch(encoder->dev) && 45762306a36Sopenharmony_ci (nv04_display(dev)->dac_users[ffs(dcb->or) - 1] & ~(1 << dcb->index)); 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic void nv04_dac_dpms(struct drm_encoder *encoder, int mode) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 46362306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(encoder->dev); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (nv_encoder->last_dpms == mode) 46662306a36Sopenharmony_ci return; 46762306a36Sopenharmony_ci nv_encoder->last_dpms = mode; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci NV_DEBUG(drm, "Setting dpms mode %d on vga encoder (output %d)\n", 47062306a36Sopenharmony_ci mode, nv_encoder->dcb->index); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic void nv04_dac_save(struct drm_encoder *encoder) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 47862306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (nv_gf4_disp_arch(dev)) 48162306a36Sopenharmony_ci nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + 48262306a36Sopenharmony_ci nv04_dac_output_offset(encoder)); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic void nv04_dac_restore(struct drm_encoder *encoder) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 48862306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (nv_gf4_disp_arch(dev)) 49162306a36Sopenharmony_ci NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder), 49262306a36Sopenharmony_ci nv_encoder->restore.output); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci nv_encoder->last_dpms = NV_DPMS_CLEARED; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic void nv04_dac_destroy(struct drm_encoder *encoder) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci drm_encoder_cleanup(encoder); 50262306a36Sopenharmony_ci kfree(nv_encoder); 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = { 50662306a36Sopenharmony_ci .dpms = nv04_dac_dpms, 50762306a36Sopenharmony_ci .mode_fixup = nv04_dac_mode_fixup, 50862306a36Sopenharmony_ci .prepare = nv04_dac_prepare, 50962306a36Sopenharmony_ci .commit = nv04_dac_commit, 51062306a36Sopenharmony_ci .mode_set = nv04_dac_mode_set, 51162306a36Sopenharmony_ci .detect = nv04_dac_detect 51262306a36Sopenharmony_ci}; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = { 51562306a36Sopenharmony_ci .dpms = nv04_dac_dpms, 51662306a36Sopenharmony_ci .mode_fixup = nv04_dac_mode_fixup, 51762306a36Sopenharmony_ci .prepare = nv04_dac_prepare, 51862306a36Sopenharmony_ci .commit = nv04_dac_commit, 51962306a36Sopenharmony_ci .mode_set = nv04_dac_mode_set, 52062306a36Sopenharmony_ci .detect = nv17_dac_detect 52162306a36Sopenharmony_ci}; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic const struct drm_encoder_funcs nv04_dac_funcs = { 52462306a36Sopenharmony_ci .destroy = nv04_dac_destroy, 52562306a36Sopenharmony_ci}; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ciint 52862306a36Sopenharmony_cinv04_dac_create(struct drm_connector *connector, struct dcb_output *entry) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci const struct drm_encoder_helper_funcs *helper; 53162306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder = NULL; 53262306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 53362306a36Sopenharmony_ci struct drm_encoder *encoder; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); 53662306a36Sopenharmony_ci if (!nv_encoder) 53762306a36Sopenharmony_ci return -ENOMEM; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci encoder = to_drm_encoder(nv_encoder); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci nv_encoder->dcb = entry; 54262306a36Sopenharmony_ci nv_encoder->or = ffs(entry->or) - 1; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci nv_encoder->enc_save = nv04_dac_save; 54562306a36Sopenharmony_ci nv_encoder->enc_restore = nv04_dac_restore; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (nv_gf4_disp_arch(dev)) 54862306a36Sopenharmony_ci helper = &nv17_dac_helper_funcs; 54962306a36Sopenharmony_ci else 55062306a36Sopenharmony_ci helper = &nv04_dac_helper_funcs; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC, 55362306a36Sopenharmony_ci NULL); 55462306a36Sopenharmony_ci drm_encoder_helper_add(encoder, helper); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci encoder->possible_crtcs = entry->heads; 55762306a36Sopenharmony_ci encoder->possible_clones = 0; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci drm_connector_attach_encoder(connector, encoder); 56062306a36Sopenharmony_ci return 0; 56162306a36Sopenharmony_ci} 562