diff options
author | Stuart Bennett <stuart@freedesktop.org> | 2009-04-07 14:37:05 +0100 |
---|---|---|
committer | Stuart Bennett <stuart@freedesktop.org> | 2009-05-06 14:27:43 +0100 |
commit | 6c209a87d2d90b39fddc446378b370bd7fb81f6b (patch) | |
tree | 533373527cf35f07a6d8d8b146d2ca4a48162681 /src | |
parent | 3971dda57004894d5d4fc9420aa00da400815af9 (diff) |
randr12: pre-nv17 load detection
Sampling heuristic as close to nvidia's as mmiotrace-based inference admits
Works on both nv05 and nv11
Diffstat (limited to 'src')
-rw-r--r-- | src/nv_output.c | 190 | ||||
-rw-r--r-- | src/nvreg.h | 5 |
2 files changed, 188 insertions, 7 deletions
diff --git a/src/nv_output.c b/src/nv_output.c index b76922f..68776e6 100644 --- a/src/nv_output.c +++ b/src/nv_output.c @@ -2,7 +2,7 @@ * Copyright 2003 NVIDIA, Corporation * Copyright 2006 Dave Airlie * Copyright 2007 Maarten Maathuis - * Copyright 2007-2008 Stuart Bennett + * 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"), @@ -25,6 +25,7 @@ */ #include <X11/Xatom.h> +#include <X11/Xos.h> /* X_GETTIMEOFDAY */ #include "nv_include.h" #define MULTIPLE_ENCODERS(e) (e & (e - 1)) @@ -57,8 +58,182 @@ static int nv_get_digital_bound_head(NVPtr pNv, int or) return (((NVReadRAMDAC(pNv, ramdac, NV_PRAMDAC_FP_TMDS_DATA) & 0x8) >> 3) ^ ramdac); } +#define WAIT_FOR(cond, timeout_us) __extension__ ({ \ + struct timeval begin, cur; \ + long d_secs, d_usecs, diff = 0; \ + \ + X_GETTIMEOFDAY(&begin); \ + while (!(cond) && diff < timeout_us) { \ + X_GETTIMEOFDAY(&cur); \ + d_secs = cur.tv_sec - begin.tv_sec; \ + d_usecs = cur.tv_usec - begin.tv_usec; \ + diff = d_secs * 1000000 + d_usecs; \ + }; \ + diff >= timeout_us ? -EAGAIN : 0; \ +}) + +/* + * 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(NVPtr pNv, bool sense[2]) +{ + 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 (WAIT_FOR(!(VGA_RD08(pNv->REGS, NV_PRMCIO_INP0__COLOR) & 1), 10000)) + return -EWOULDBLOCK; + if (WAIT_FOR(VGA_RD08(pNv->REGS, NV_PRMCIO_INP0__COLOR) & 1, 10000)) + return -EWOULDBLOCK; + if (WAIT_FOR(!(VGA_RD08(pNv->REGS, NV_PRMCIO_INP0__COLOR) & 1), 10000)) + return -EWOULDBLOCK; + + WAIT_FOR(0, 100); /* faster than usleep(100) */ + /* when level triggers, sense is _LO_ */ + sense_a = VGA_RD08(pNv->REGS, NV_PRMCIO_INP0) & 0x10; + + /* take another reading until it agrees with sense_a... */ + do { + WAIT_FOR(0, 100); + sense_b = VGA_RD08(pNv->REGS, NV_PRMCIO_INP0) & 0x10; + if (sense_a != sense_b) { + sense_b_prime = VGA_RD08(pNv->REGS, 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 bool nv_legacy_load_detect(ScrnInfoPtr pScrn) +{ + NVPtr pNv = NVPTR(pScrn); + uint8_t saved_seq1, saved_pi, saved_rpc1; + 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 (pNv->twoHeads) + /* only implemented for head A for now */ + NVSetOwner(pNv, 0); + + saved_seq1 = NVReadVgaSeq(pNv, 0, NV_VIO_SR_CLOCK_INDEX); + NVWriteVgaSeq(pNv, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20); + + saved_rtest_ctrl = NVReadRAMDAC(pNv, 0, NV_PRAMDAC_TEST_CONTROL); + NVWriteRAMDAC(pNv, 0, NV_PRAMDAC_TEST_CONTROL, + saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); + + usleep(10000); + + saved_pi = NVReadVgaCrtc(pNv, 0, NV_CIO_CRE_PIXEL_INDEX); + NVWriteVgaCrtc(pNv, 0, NV_CIO_CRE_PIXEL_INDEX, + saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT))); + saved_rpc1 = NVReadVgaCrtc(pNv, 0, NV_CIO_CRE_RPC1_INDEX); + NVWriteVgaCrtc(pNv, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0); + + VGA_WR08(pNv->REGS, NV_PRMDIO_READ_MODE_ADDRESS, 0x0); + for (i = 0; i < 3; i++) + saved_palette0[i] = NV_RD08(pNv->REGS, NV_PRMDIO_PALETTE_DATA); + saved_palette_mask = NV_RD08(pNv->REGS, NV_PRMDIO_PIXEL_MASK); + VGA_WR08(pNv->REGS, NV_PRMDIO_PIXEL_MASK, 0); + + saved_rgen_ctrl = NVReadRAMDAC(pNv, 0, NV_PRAMDAC_GENERAL_CONTROL); + NVWriteRAMDAC(pNv, 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]; + + VGA_WR08(pNv->REGS, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); + NV_WR08(pNv->REGS, NV_PRMDIO_PALETTE_DATA, 0); + NV_WR08(pNv->REGS, NV_PRMDIO_PALETTE_DATA, 0); + /* testing blue won't find monochrome monitors. I don't care */ + NV_WR08(pNv->REGS, NV_PRMDIO_PALETTE_DATA, blue); + + i = 0; + /* take sample pairs until both samples in the pair agree */ + do { + if (sample_load_twice(pNv, 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: + VGA_WR08(pNv->REGS, NV_PRMDIO_PIXEL_MASK, saved_palette_mask); + NVWriteRAMDAC(pNv, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl); + VGA_WR08(pNv->REGS, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); + for (i = 0; i < 3; i++) + NV_WR08(pNv->REGS, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]); + NVWriteRAMDAC(pNv, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl); + NVWriteVgaCrtc(pNv, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi); + NVWriteVgaCrtc(pNv, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1); + NVWriteVgaSeq(pNv, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1); + + if (blue == 0x18) { + NV_TRACE(pScrn, "Load detected on head A\n"); + return true; + } + + return false; +} + static bool -nv_load_detect(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder) +nv_nv17_load_detect(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder) { NVPtr pNv = NVPTR(pScrn); uint32_t testval, regoffset = nv_output_ramdac_offset(nv_encoder); @@ -216,11 +391,12 @@ nv_output_detect(xf86OutputPtr output) xf86CheckBoolOption(output->conf_monitor->mon_option_lst, "Enable", FALSE)) ret = XF86OutputStatusConnected; - /* we don't have a load det function for early cards */ - else if (!pNv->gf4_disp_arch) - ret = XF86OutputStatusUnknown; - else if (nv_load_detect(pScrn, det_encoder)) - ret = XF86OutputStatusConnected; + else if (pNv->gf4_disp_arch) { + if (nv_nv17_load_detect(pScrn, det_encoder)) + ret = XF86OutputStatusConnected; + } else + if (nv_legacy_load_detect(pScrn)) + ret = XF86OutputStatusConnected; } else if ((det_encoder = find_encoder_by_type(OUTPUT_LVDS))) { if (det_encoder->dcb->lvdsconf.use_straps_for_mode) { if (nouveau_bios_fp_mode(pScrn, NULL)) diff --git a/src/nvreg.h b/src/nvreg.h index 30c0f01..ef750c2 100644 --- a/src/nvreg.h +++ b/src/nvreg.h @@ -195,6 +195,7 @@ # 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 */ @@ -257,6 +258,7 @@ # 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 @@ -339,6 +341,9 @@ # 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_TERMINATION_75OHM (2 << 16) +# define NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS (1 << 20) #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) |