diff options
Diffstat (limited to 'src/nv_output.c')
-rw-r--r-- | src/nv_output.c | 1293 |
1 files changed, 0 insertions, 1293 deletions
diff --git a/src/nv_output.c b/src/nv_output.c deleted file mode 100644 index 2573702..0000000 --- a/src/nv_output.c +++ /dev/null @@ -1,1293 +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 <X11/Xatom.h> -#include <X11/Xos.h> /* X_GETTIMEOFDAY */ -#include "nv_include.h" - -#define MULTIPLE_ENCODERS(e) (e & (e - 1)) -#define FOR_EACH_ENCODER_IN_CONNECTOR(i, c, e) for (i = 0; i < pNv->vbios->dcb->entries; i++) \ - if (c->possible_encoders & (1 << i) && \ - (e = &pNv->encoders[i])) - -static int nv_output_ramdac_offset(struct nouveau_encoder *nv_encoder) -{ - int offset = 0; - - if (nv_encoder->dcb->or & (8 | OUTPUT_C)) - offset += 0x68; - if (nv_encoder->dcb->or & (8 | OUTPUT_B)) - offset += 0x2000; - - return offset; -} - -static int nv_get_digital_bound_head(NVPtr pNv, int or) -{ - /* 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 = (or & OUTPUT_C) >> 2; - - NVWriteRAMDAC(pNv, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL, - NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | 0x4); - 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_nv17_load_detect(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder) -{ - NVPtr pNv = NVPTR(pScrn); - uint32_t testval, regoffset = nv_output_ramdac_offset(nv_encoder); - uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, saved_rtest_ctrl, temp; - int head, present = 0; - xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); - -#define RGB_TEST_DATA(r,g,b) (r << 0 | g << 10 | b << 20) - testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */ - if (pNv->vbios->dactestval) - testval = pNv->vbios->dactestval; - - saved_rtest_ctrl = NVReadRAMDAC(pNv, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); - NVWriteRAMDAC(pNv, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, - saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); - - saved_powerctrl_2 = nvReadMC(pNv, NV_PBUS_POWERCTRL_2); - - nvWriteMC(pNv, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff); - if (regoffset == 0x68) { - saved_powerctrl_4 = nvReadMC(pNv, NV_PBUS_POWERCTRL_4); - nvWriteMC(pNv, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf); - } - - usleep(4000); - - saved_routput = NVReadRAMDAC(pNv, 0, NV_PRAMDAC_DACCLK + regoffset); - head = (saved_routput & 0x100) >> 8; - /* if there's a spare crtc, using it will minimise flicker for the case - * where the in-use crtc is in use by an off-chip tmds encoder */ - if (xf86_config->crtc[head]->enabled && !xf86_config->crtc[head ^ 1]->enabled) - head ^= 1; - /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */ - NVWriteRAMDAC(pNv, 0, NV_PRAMDAC_DACCLK + regoffset, - (saved_routput & 0xfffffece) | head << 8); - usleep(1000); - - temp = NVReadRAMDAC(pNv, 0, NV_PRAMDAC_DACCLK + regoffset); - NVWriteRAMDAC(pNv, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1); - - NVWriteRAMDAC(pNv, head, NV_PRAMDAC_TESTPOINT_DATA, - NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval); - temp = NVReadRAMDAC(pNv, head, NV_PRAMDAC_TEST_CONTROL); - NVWriteRAMDAC(pNv, head, NV_PRAMDAC_TEST_CONTROL, - temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); - usleep(1000); - - present = NVReadRAMDAC(pNv, 0, NV_PRAMDAC_TEST_CONTROL + regoffset) & - NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI; - - temp = NVReadRAMDAC(pNv, head, NV_PRAMDAC_TEST_CONTROL); - NVWriteRAMDAC(pNv, head, NV_PRAMDAC_TEST_CONTROL, - temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); - NVWriteRAMDAC(pNv, head, NV_PRAMDAC_TESTPOINT_DATA, 0); - - /* bios does something more complex for restoring, but I think this is good enough */ - NVWriteRAMDAC(pNv, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput); - NVWriteRAMDAC(pNv, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl); - if (regoffset == 0x68) - nvWriteMC(pNv, NV_PBUS_POWERCTRL_4, saved_powerctrl_4); - nvWriteMC(pNv, NV_PBUS_POWERCTRL_2, saved_powerctrl_2); - - if (present) { - NV_TRACE(pScrn, "Load detected on output %c\n", - '@' + ffs(nv_encoder->dcb->or)); - return true; - } - - return false; -} - -static void -update_output_fields(xf86OutputPtr output, struct nouveau_encoder *det_encoder) -{ - struct nouveau_connector *nv_connector = to_nouveau_connector(output); - NVPtr pNv = NVPTR(output->scrn); - - if (nv_connector->detected_encoder == det_encoder) - return; - - nv_connector->detected_encoder = det_encoder; - output->possible_crtcs = det_encoder->dcb->heads; - if (IS_DFP(det_encoder->dcb->type)) { - output->doubleScanAllowed = false; - output->interlaceAllowed = false; - } else { - output->doubleScanAllowed = true; - if (pNv->Architecture == NV_ARCH_20 || - (pNv->Architecture == NV_ARCH_10 && - (pNv->Chipset & 0x0ff0) != CHIPSET_NV10 && - (pNv->Chipset & 0x0ff0) != CHIPSET_NV15)) - /* HW is broken */ - output->interlaceAllowed = false; - else - output->interlaceAllowed = true; - } -} - -static bool edid_sink_connected(xf86OutputPtr output) -{ - struct nouveau_connector *nv_connector = to_nouveau_connector(output); - NVPtr pNv = NVPTR(output->scrn); - bool waslocked = NVLockVgaCrtcs(pNv, false); - bool wastied = nv_heads_tied(pNv); - - if (wastied) - NVSetOwner(pNv, 0); /* necessary? */ - - nv_connector->edid = xf86OutputGetEDID(output, nv_connector->pDDCBus); - - if (wastied) - NVSetOwner(pNv, 0x4); - if (waslocked) - NVLockVgaCrtcs(pNv, true); - - return !!nv_connector->edid; -} - -static xf86OutputStatus -nv_output_detect(xf86OutputPtr output) -{ - struct nouveau_connector *nv_connector = to_nouveau_connector(output); - ScrnInfoPtr pScrn = output->scrn; - NVPtr pNv = NVPTR(pScrn); - struct nouveau_encoder *det_encoder; - xf86OutputStatus ret = XF86OutputStatusDisconnected; - - struct nouveau_encoder *find_encoder_by_type(enum nouveau_encoder_type type) - { - int i; - for (i = 0; i < pNv->vbios->dcb->entries; i++) - if (nv_connector->possible_encoders & (1 << i) && - (type == OUTPUT_ANY || pNv->encoders[i].dcb->type == type)) - return &pNv->encoders[i]; - return NULL; - } - - /* if an LVDS output was ever connected it remains so */ - if (nv_connector->detected_encoder && - nv_connector->detected_encoder->dcb->type == OUTPUT_LVDS) - return XF86OutputStatusConnected; - - if (nv_connector->pDDCBus && edid_sink_connected(output)) { - if (MULTIPLE_ENCODERS(nv_connector->possible_encoders)) { - if (nv_connector->edid->features.input_type) - det_encoder = find_encoder_by_type(OUTPUT_TMDS); - else - det_encoder = find_encoder_by_type(OUTPUT_ANALOG); - } else - det_encoder = find_encoder_by_type(OUTPUT_ANY); - ret = XF86OutputStatusConnected; - } else if ((det_encoder = find_encoder_by_type(OUTPUT_ANALOG))) { - /* bind encoder if enabled in xorg.conf */ - if (output->conf_monitor && - xf86CheckBoolOption(output->conf_monitor->mon_option_lst, - "Enable", FALSE)) - 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)) - ret = XF86OutputStatusConnected; - } else if (!pNv->vbios->fp_no_ddc && - nouveau_bios_embedded_edid(pScrn)) { - nv_connector->edid = xf86InterpretEDID(pScrn->scrnIndex, - nouveau_bios_embedded_edid(pScrn)); - ret = XF86OutputStatusConnected; - } - } - - if (ret != XF86OutputStatusDisconnected) - update_output_fields(output, det_encoder); - - return ret; -} - -static DisplayModePtr -get_native_mode_from_edid(xf86OutputPtr output, DisplayModePtr edid_modes) -{ - struct nouveau_connector *nv_connector = to_nouveau_connector(output); - struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; - int max_h_active = 0, max_v_active = 0; - int i; - DisplayModePtr mode; - - for (i = 0; i < DET_TIMINGS; i++) { - struct detailed_timings *dt = - &nv_connector->edid->det_mon[i].section.d_timings; - - if (nv_connector->edid->det_mon[i].type != DT) - continue; - /* Selecting only based on width ok? */ - if (dt->h_active > max_h_active) { - max_h_active = dt->h_active; - max_v_active = dt->v_active; - } - } - if (!max_h_active || !max_v_active) /* clearly a joke EDID */ - for (i = 0; i < STD_TIMINGS; i++) { - struct std_timings *st = - &nv_connector->edid->timings2[i]; - - if (st->hsize > max_h_active) { - max_h_active = st->hsize; - max_v_active = st->vsize; - } - } - if (!max_h_active || !max_v_active) { - NV_ERROR(output->scrn, "EDID too broken to find native mode\n"); - return NULL; - } - - if (nv_encoder->native_mode) { - xfree(nv_encoder->native_mode); - nv_encoder->native_mode = NULL; - } - - for (mode = edid_modes; mode != NULL; mode = mode->next) { - if (mode->HDisplay == max_h_active && - mode->VDisplay == max_v_active) { - /* Take the preferred mode when it exists. */ - if (mode->type & M_T_PREFERRED) { - nv_encoder->native_mode = xf86DuplicateMode(mode); - break; - } - /* Find the highest refresh mode otherwise. */ - if (!nv_encoder->native_mode || - (mode->VRefresh > nv_encoder->native_mode->VRefresh)) { - if (nv_encoder->native_mode) - xfree(nv_encoder->native_mode); - mode->type |= M_T_PREFERRED; - nv_encoder->native_mode = xf86DuplicateMode(mode); - } - } - } - - return nv_encoder->native_mode; -} - -static DisplayModePtr -nv_output_get_edid_modes(xf86OutputPtr output) -{ - struct nouveau_connector *nv_connector = to_nouveau_connector(output); - struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; - DisplayModePtr edid_modes; - - if (IS_DFP(nv_encoder->dcb->type) && - nv_encoder->scaling_mode != SCALE_PANEL) - /* the digital scaler is not limited to modes given in the EDID, - * so enable the GTF bit in order that the xserver thinks - * continuous timing is available and adds the standard modes - */ - nv_connector->edid->features.msc |= 1; - - xf86OutputSetEDID(output, nv_connector->edid); - if (!(edid_modes = xf86OutputGetEDIDModes(output))) - return edid_modes; - - if (IS_DFP(nv_encoder->dcb->type)) - if (!get_native_mode_from_edid(output, edid_modes)) - return NULL; - if (nv_encoder->dcb->type == OUTPUT_TMDS) - nv_encoder->dual_link = nv_encoder->native_mode->Clock >= 165000; - - return edid_modes; -} - -static DisplayModePtr -nv_lvds_output_get_modes(xf86OutputPtr output) -{ - struct nouveau_connector *nv_connector = to_nouveau_connector(output); - struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; - ScrnInfoPtr pScrn = output->scrn; - DisplayModeRec mode, *ret_mode = NULL; - int clock = 0; /* needs to be zero for straps case */ - bool dl, if_is_24bit = false; - - /* panels only have one mode, and it doesn't change */ - if (nv_encoder->native_mode) - return xf86DuplicateMode(nv_encoder->native_mode); - - if (nv_encoder->dcb->lvdsconf.use_straps_for_mode) { - if (!nouveau_bios_fp_mode(pScrn, &mode)) - return NULL; - - mode.status = MODE_OK; - mode.type = M_T_DRIVER | M_T_PREFERRED; - xf86SetModeDefaultName(&mode); - - nv_encoder->native_mode = xf86DuplicateMode(&mode); - ret_mode = xf86DuplicateMode(&mode); - } else - if ((ret_mode = nv_output_get_edid_modes(output))) - clock = nv_encoder->native_mode->Clock; - - if (nouveau_bios_parse_lvds_table(pScrn, clock, &dl, &if_is_24bit)) { - if (nv_encoder->native_mode) { - xfree(nv_encoder->native_mode); - nv_encoder->native_mode = NULL; - } - return NULL; - } - - /* because of the pre-existing native mode exit above, this will only - * get run at startup (and before create_resources is called in - * mode_fixup), so subsequent user dither settings are not overridden - */ - nv_encoder->dithering |= !if_is_24bit; - nv_encoder->dual_link = dl; - - return ret_mode; -} - -static int nv_output_mode_valid(xf86OutputPtr output, DisplayModePtr mode) -{ - struct nouveau_encoder *nv_encoder = to_nouveau_connector(output)->detected_encoder; - - /* mode_valid can be called by someone doing addmode on an output - * which is disconnected and so without an encoder; avoid crashing - */ - if (!nv_encoder) - return MODE_ERROR; - - if (!output->doubleScanAllowed && mode->Flags & V_DBLSCAN) - return MODE_NO_DBLESCAN; - if (!output->interlaceAllowed && mode->Flags & V_INTERLACE) - return MODE_NO_INTERLACE; - - if (nv_encoder->dcb->type == OUTPUT_ANALOG) { - if (nv_encoder->dcb->crtconf.maxfreq) { - if (mode->Clock > nv_encoder->dcb->crtconf.maxfreq) - return MODE_CLOCK_HIGH; - } else - if (mode->Clock > 350000) - return MODE_CLOCK_HIGH; - } - /* must have a native mode for fp (except in panel scaling case) */ - if (IS_DFP(nv_encoder->dcb->type) && !nv_encoder->native_mode && - nv_encoder->scaling_mode != SCALE_PANEL) - return MODE_NOCLOCK; - if (IS_DFP(nv_encoder->dcb->type) && nv_encoder->native_mode) - /* No modes > panel's native res */ - if (mode->HDisplay > nv_encoder->native_mode->HDisplay || - mode->VDisplay > nv_encoder->native_mode->VDisplay) - return MODE_PANEL; - if (nv_encoder->dcb->type == OUTPUT_TMDS) { - if (nv_encoder->dcb->duallink_possible) { - if (mode->Clock > 330000) /* 2x165 MHz */ - return MODE_CLOCK_HIGH; - } else - if (mode->Clock > 165000) /* 165 MHz */ - return MODE_CLOCK_HIGH; - } - - return MODE_OK; -} - -static void -nv_output_destroy(xf86OutputPtr output) -{ - struct nouveau_connector *nv_connector = to_nouveau_connector(output); - struct nouveau_encoder *nv_encoder; - NVPtr pNv = NVPTR(output->scrn); - int i; - - if (!nv_connector) - return; - - if (nv_connector->edid) - xfree(nv_connector->edid); - FOR_EACH_ENCODER_IN_CONNECTOR(i, nv_connector, nv_encoder) - if (nv_encoder->native_mode) - xfree(nv_encoder->native_mode); - xfree(nv_connector); -} - -static char * get_current_scaling_name(enum scaling_modes mode) -{ - static const struct { - char *name; - enum scaling_modes mode; - } scaling_mode[] = { - { "panel", SCALE_PANEL }, - { "fullscreen", SCALE_FULLSCREEN }, - { "aspect", SCALE_ASPECT }, - { "noscale", SCALE_NOSCALE }, - { NULL, SCALE_INVALID } - }; - int i; - - for (i = 0; scaling_mode[i].name; i++) - if (scaling_mode[i].mode == mode) - return scaling_mode[i].name; - - return NULL; -} - -static int nv_output_create_prop(xf86OutputPtr output, char *name, Atom *atom, - INT32 *rangevals, INT32 cur_val, char *cur_str, Bool do_mode_set) -{ - int ret = -ENOMEM; - Bool range = rangevals ? TRUE : FALSE; - - if ((*atom = MakeAtom(name, strlen(name), TRUE)) == BAD_RESOURCE) - goto fail; - if (RRQueryOutputProperty(output->randr_output, *atom)) - return 0; /* already exists */ - if ((ret = RRConfigureOutputProperty(output->randr_output, *atom, - do_mode_set, range, FALSE, range ? 2 : 0, rangevals))) - goto fail; - if (range) - ret = RRChangeOutputProperty(output->randr_output, *atom, XA_INTEGER, 32, - PropModeReplace, 1, &cur_val, FALSE, do_mode_set); - else - ret = RRChangeOutputProperty(output->randr_output, *atom, XA_STRING, 8, - PropModeReplace, strlen(cur_str), cur_str, - FALSE, do_mode_set); - -fail: - if (ret) - xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, - "Creation of %s property failed: %d\n", name, ret); - - return ret; -} - -static Atom dithering_atom, scaling_mode_atom; -static Atom dv_atom, sharpness_atom; - -static void nv_output_create_resources(xf86OutputPtr output) -{ - struct nouveau_encoder *nv_encoder = to_nouveau_encoder(output); - NVPtr pNv = NVPTR(output->scrn); - - /* may be called before encoder is picked, resources will be created - * by update_output_fields() - */ - if (!nv_encoder) - return; - - if (IS_DFP(nv_encoder->dcb->type)) { - nv_output_create_prop(output, "DITHERING", &dithering_atom, - (INT32 []){ 0, 1 }, nv_encoder->dithering, NULL, TRUE); - nv_output_create_prop(output, "SCALING_MODE", &scaling_mode_atom, - NULL, 0, get_current_scaling_name(nv_encoder->scaling_mode), TRUE); - } - if (pNv->NVArch >= 0x11 && output->crtc) { - struct nouveau_crtc *nv_crtc = to_nouveau_crtc(output->crtc); - INT32 dv_range[2] = { 0, !pNv->gf4_disp_arch ? 3 : 63 }; - /* unsure of correct condition here: blur works on my nv34, but not on my nv31 */ - INT32 is_range[2] = { pNv->NVArch > 0x31 ? -32 : 0, 31 }; - - nv_output_create_prop(output, "DIGITAL_VIBRANCE", &dv_atom, - dv_range, nv_crtc->saturation, NULL, FALSE); - if (pNv->NVArch >= 0x30) - nv_output_create_prop(output, "IMAGE_SHARPENING", &sharpness_atom, - is_range, nv_crtc->sharpness, NULL, FALSE); - } -} - -static Bool -nv_output_set_property(xf86OutputPtr output, Atom property, - RRPropertyValuePtr value) -{ - struct nouveau_encoder *nv_encoder = to_nouveau_encoder(output); - NVPtr pNv = NVPTR(output->scrn); - - if (property == scaling_mode_atom) { - char *name = (char *) value->data; - int32_t val; - - if (value->type != XA_STRING || value->format != 8) - return FALSE; - - /* Match a string to a scaling mode */ - val = nv_scaling_mode_lookup(name, value->size); - if (val == SCALE_INVALID) - return FALSE; - - /* LVDS must always use gpu scaling. */ - if (val == SCALE_PANEL && nv_encoder->dcb->type == OUTPUT_LVDS) - return FALSE; - - nv_encoder->scaling_mode = val; - } else if (property == dithering_atom) { - int32_t val = *(int32_t *) value->data; - - if (value->type != XA_INTEGER || value->format != 32) - return FALSE; - - if (val < 0 || val > 1) - return FALSE; - - nv_encoder->dithering = val; - } else if (property == dv_atom || property == sharpness_atom) { - int32_t val = *(int32_t *) value->data; - - if (value->type != XA_INTEGER || value->format != 32) - return FALSE; - - if (!output->crtc) - return FALSE; - - if (property == dv_atom) { - if (val < 0 || val > (!pNv->gf4_disp_arch ? 3 : 63)) - return FALSE; - - nv_crtc_set_digital_vibrance(output->crtc, val); - } else { - if (val < (pNv->NVArch > 0x31 ? -32 : 0) || val > 31) - return FALSE; - - nv_crtc_set_image_sharpening(output->crtc, val); - } - } - - return TRUE; -} - -static Bool -nv_output_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, - DisplayModePtr adjusted_mode) -{ - struct nouveau_connector *nv_connector = to_nouveau_connector(output); - - if (nv_connector->nv_encoder != nv_connector->detected_encoder) { - nv_connector->nv_encoder = nv_connector->detected_encoder; - if (output->randr_output) { - RRDeleteOutputProperty(output->randr_output, dithering_atom); - RRDeleteOutputProperty(output->randr_output, scaling_mode_atom); - output->funcs->create_resources(output); - } - } - - struct nouveau_encoder *nv_encoder = to_nouveau_encoder(output); - - /* For internal panels and gpu scaling on DVI we need the native mode */ - if (IS_DFP(nv_encoder->dcb->type) && - nv_encoder->scaling_mode != SCALE_PANEL) { - adjusted_mode->HDisplay = nv_encoder->native_mode->HDisplay; - adjusted_mode->HSkew = nv_encoder->native_mode->HSkew; - adjusted_mode->HSyncStart = nv_encoder->native_mode->HSyncStart; - adjusted_mode->HSyncEnd = nv_encoder->native_mode->HSyncEnd; - adjusted_mode->HTotal = nv_encoder->native_mode->HTotal; - adjusted_mode->VDisplay = nv_encoder->native_mode->VDisplay; - adjusted_mode->VScan = nv_encoder->native_mode->VScan; - adjusted_mode->VSyncStart = nv_encoder->native_mode->VSyncStart; - adjusted_mode->VSyncEnd = nv_encoder->native_mode->VSyncEnd; - adjusted_mode->VTotal = nv_encoder->native_mode->VTotal; - adjusted_mode->Clock = nv_encoder->native_mode->Clock; - adjusted_mode->Flags = nv_encoder->native_mode->Flags; - - xf86SetModeCrtc(adjusted_mode, INTERLACE_HALVE_V); - } - - return TRUE; -} - -static void nv_digital_output_prepare_sel_clk(NVPtr pNv, struct nouveau_encoder *nv_encoder, int head) -{ - struct nouveau_mode_state *state = &pNv->set_state; - uint32_t bits1618 = nv_encoder->dcb->or & 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 == OUTPUT_LVDS && pNv->saved_regs.sel_clk & 0xf0) { - int shift = (pNv->saved_regs.sel_clk & 0x50) ? 0 : 1; - - state->sel_clk &= ~0xf0; - state->sel_clk |= (head ? 0x40 : 0x10) << shift; - } -} - -#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 bool is_fpc_off(uint32_t fpc) -{ - return ((fpc & (FP_TG_CONTROL_ON | FP_TG_CONTROL_OFF)) == - FP_TG_CONTROL_OFF); -} - -static void -nv_output_prepare(xf86OutputPtr output) -{ - struct nouveau_encoder *nv_encoder = to_nouveau_encoder(output); - NVPtr pNv = NVPTR(output->scrn); - int head = to_nouveau_crtc(output->crtc)->head; - struct nouveau_crtc_state *crtcstate = pNv->set_state.head; - 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]; - - output->funcs->dpms(output, DPMSModeOff); - - if (nv_encoder->dcb->type == OUTPUT_ANALOG) { - if (NVReadRAMDAC(pNv, 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(pNv, head, NV_PRAMDAC_FP_TG_CONTROL, - FP_TG_CONTROL_OFF); - usleep(50000); - } - /* don't inadvertently turn it on when state written later */ - crtcstate[head].fp_control = FP_TG_CONTROL_OFF; - } - - /* calculate some output specific CRTC regs now, so that they can be - * written in nv_crtc_set_mode - */ - - if (IS_DFP(nv_encoder->dcb->type)) - nv_digital_output_prepare_sel_clk(pNv, nv_encoder, head); - - /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f) - * at LCD__INDEX which we don't alter - */ - if (!(*cr_lcd & 0x44)) { - *cr_lcd = IS_DFP(nv_encoder->dcb->type) ? 0x3 : 0x0; - if (IS_DFP(nv_encoder->dcb->type) && pNv->twoHeads) { - 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 == OUTPUT_LVDS) - *cr_lcd |= 0x30; - if ((*cr_lcd & 0x30) == (*cr_lcd_oth & 0x30)) { - /* avoid being connected to both crtcs */ - *cr_lcd_oth &= ~0x30; - NVWriteVgaCrtc(pNv, head ^ 1, - NV_CIO_CRE_LCD__INDEX, - *cr_lcd_oth); - } - } - } - } -} - -static void -nv_output_mode_set(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode) -{ - struct nouveau_encoder *nv_encoder = to_nouveau_encoder(output); - ScrnInfoPtr pScrn = output->scrn; - NVPtr pNv = NVPTR(pScrn); - struct dcb_entry *dcbe = nv_encoder->dcb; - int head = to_nouveau_crtc(output->crtc)->head; - - NV_TRACE(pScrn, "%s called for encoder %d\n", __func__, dcbe->index); - - if (pNv->gf4_disp_arch && dcbe->type == OUTPUT_ANALOG) { - uint32_t dac_offset = nv_output_ramdac_offset(nv_encoder); - uint32_t otherdac; - int i; - - /* bit 16-19 are bits that are set on some G70 cards, - * but don't seem to have much effect */ - NVWriteRAMDAC(pNv, 0, NV_PRAMDAC_DACCLK + dac_offset, - head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK); - /* force any other vga encoders to bind to the other crtc */ - for (i = 0; i < pNv->vbios->dcb->entries; i++) - if (i != dcbe->index && pNv->encoders[i].dcb && - pNv->encoders[i].dcb->type == OUTPUT_ANALOG) { - dac_offset = nv_output_ramdac_offset(&pNv->encoders[i]); - otherdac = NVReadRAMDAC(pNv, 0, NV_PRAMDAC_DACCLK + dac_offset); - NVWriteRAMDAC(pNv, 0, NV_PRAMDAC_DACCLK + dac_offset, - (otherdac & ~0x100) | (head ^ 1) << 8); - } - } - if (dcbe->type == OUTPUT_TMDS) - run_tmds_table(pScrn, dcbe, head, adjusted_mode->Clock); - else if (dcbe->type == OUTPUT_LVDS) - call_lvds_script(pScrn, dcbe, head, LVDS_RESET, adjusted_mode->Clock); - if (IS_DFP(dcbe->type)) - /* update fp_control state for any changes made by scripts, - * so correct value is written at DPMS on */ - pNv->set_state.head[head].fp_control = - NVReadRAMDAC(pNv, head, NV_PRAMDAC_FP_TG_CONTROL); - - /* This could use refinement for flatpanels, but it should work this way */ - if (pNv->NVArch < 0x44) - NVWriteRAMDAC(pNv, 0, NV_PRAMDAC_TEST_CONTROL + nv_output_ramdac_offset(nv_encoder), 0xf0000000); - else - NVWriteRAMDAC(pNv, 0, NV_PRAMDAC_TEST_CONTROL + nv_output_ramdac_offset(nv_encoder), 0x00100000); -} - -static void -nv_output_commit(xf86OutputPtr output) -{ - struct nouveau_encoder *nv_encoder = to_nouveau_encoder(output); - ScrnInfoPtr pScrn = output->scrn; - struct nouveau_crtc *nv_crtc = to_nouveau_crtc(output->crtc); - - output->funcs->dpms(output, DPMSModeOn); - - NV_TRACE(pScrn, "Output %s is running on CRTC %d using output %c\n", - output->name, nv_crtc->head, '@' + ffs(nv_encoder->dcb->or)); -} - -static void dpms_update_fp_control(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder, xf86CrtcPtr crtc, int mode) -{ - NVPtr pNv = NVPTR(pScrn); - struct nouveau_crtc *nv_crtc; - uint32_t *fpc; - xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); - int i; - - if (mode == DPMSModeOn) { - nv_crtc = to_nouveau_crtc(crtc); - fpc = &nv_crtc->state->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 << nv_encoder->dcb->index; - NVWriteRAMDAC(pNv, nv_crtc->head, NV_PRAMDAC_FP_TG_CONTROL, *fpc); - } else - for (i = 0; i < xf86_config->num_crtc; i++) { - nv_crtc = to_nouveau_crtc(xf86_config->crtc[i]); - fpc = &nv_crtc->state->fp_control; - - nv_crtc->fp_users &= ~(1 << nv_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(pNv, nv_crtc->head, - NV_PRAMDAC_FP_TG_CONTROL, *fpc); - } - } -} - -static bool is_powersaving_dpms(int mode) -{ - return (mode == DPMSModeStandby || mode == DPMSModeSuspend || - mode == DPMSModeOff); -} - -static void -lvds_encoder_dpms(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder, xf86CrtcPtr crtc, int mode) -{ - NVPtr pNv = NVPTR(pScrn); - bool was_powersaving = is_powersaving_dpms(nv_encoder->last_dpms); - - if (nv_encoder->last_dpms == mode) - return; - nv_encoder->last_dpms = mode; - - NV_TRACE(pScrn, "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 ? to_nouveau_crtc(crtc)->head : - nv_get_digital_bound_head(pNv, nv_encoder->dcb->or); - - if (mode == DPMSModeOn) - call_lvds_script(pScrn, nv_encoder->dcb, head, - LVDS_PANEL_ON, nv_encoder->native_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(pScrn, nv_encoder->dcb, head, - LVDS_PANEL_OFF, 0); - } - - dpms_update_fp_control(pScrn, nv_encoder, crtc, mode); - - if (mode == DPMSModeOn) - nv_digital_output_prepare_sel_clk(pNv, nv_encoder, to_nouveau_crtc(crtc)->head); - else { - pNv->set_state.sel_clk = NVReadRAMDAC(pNv, 0, NV_PRAMDAC_SEL_CLK); - pNv->set_state.sel_clk &= ~0xf0; - } - NVWriteRAMDAC(pNv, 0, NV_PRAMDAC_SEL_CLK, pNv->set_state.sel_clk); -} - -static void -vga_encoder_dpms(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder, xf86CrtcPtr crtc, int mode) -{ - NVPtr pNv = NVPTR(pScrn); - - if (nv_encoder->last_dpms == mode) - return; - nv_encoder->last_dpms = mode; - - NV_TRACE(pScrn, "Setting dpms mode %d on vga encoder (output %d)\n", - mode, nv_encoder->dcb->index); - - if (pNv->gf4_disp_arch) { - uint32_t outputval = NVReadRAMDAC(pNv, 0, NV_PRAMDAC_DACCLK + nv_output_ramdac_offset(nv_encoder)); - - if (mode == DPMSModeOff) - NVWriteRAMDAC(pNv, 0, NV_PRAMDAC_DACCLK + nv_output_ramdac_offset(nv_encoder), - outputval & ~NV_PRAMDAC_DACCLK_SEL_DACCLK); - else if (mode == DPMSModeOn) - NVWriteRAMDAC(pNv, 0, NV_PRAMDAC_DACCLK + nv_output_ramdac_offset(nv_encoder), - outputval | NV_PRAMDAC_DACCLK_SEL_DACCLK); - } -} - -static void -tmds_encoder_dpms(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder, xf86CrtcPtr crtc, int mode) -{ - if (nv_encoder->last_dpms == mode) - return; - nv_encoder->last_dpms = mode; - - NV_TRACE(pScrn, "Setting dpms mode %d on tmds encoder (output %d)\n", - mode, nv_encoder->dcb->index); - - dpms_update_fp_control(pScrn, nv_encoder, crtc, mode); -} - -static void nv_output_dpms(xf86OutputPtr output, int mode) -{ - struct nouveau_connector *nv_connector = to_nouveau_connector(output); - struct nouveau_encoder *nv_encoder = to_nouveau_encoder(output); - ScrnInfoPtr pScrn = output->scrn; - xf86CrtcPtr crtc = output->crtc; - NVPtr pNv = NVPTR(pScrn); - int i; - void (* const encoder_dpms[4])(ScrnInfoPtr, struct nouveau_encoder *, xf86CrtcPtr, int) = - /* index matches DCB type */ - { vga_encoder_dpms, NULL, tmds_encoder_dpms, lvds_encoder_dpms }; - - struct nouveau_encoder *nv_encoder_i; - FOR_EACH_ENCODER_IN_CONNECTOR(i, nv_connector, nv_encoder_i) - if (nv_encoder_i != nv_encoder) - encoder_dpms[nv_encoder_i->dcb->type](pScrn, nv_encoder_i, crtc, DPMSModeOff); - - if (nv_encoder) /* may be called before encoder is picked, but iteration above solves it */ - encoder_dpms[nv_encoder->dcb->type](pScrn, nv_encoder, crtc, mode); -} - -void nv_encoder_save(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder) -{ - NVPtr pNv = NVPTR(pScrn); - - if (!nv_encoder->dcb) /* uninitialised encoder */ - return; - - if (pNv->gf4_disp_arch && nv_encoder->dcb->type == OUTPUT_ANALOG) - nv_encoder->restore.output = - NVReadRAMDAC(pNv, 0, NV_PRAMDAC_DACCLK + - nv_output_ramdac_offset(nv_encoder)); - if (pNv->twoHeads && IS_DFP(nv_encoder->dcb->type)) - nv_encoder->restore.head = - nv_get_digital_bound_head(pNv, nv_encoder->dcb->or); -} - -void nv_encoder_restore(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder) -{ - NVPtr pNv = NVPTR(pScrn); - int head = nv_encoder->restore.head; - - if (!nv_encoder->dcb) /* uninitialised encoder */ - return; - - if (pNv->gf4_disp_arch && nv_encoder->dcb->type == OUTPUT_ANALOG) - NVWriteRAMDAC(pNv, 0, - NV_PRAMDAC_DACCLK + nv_output_ramdac_offset(nv_encoder), - nv_encoder->restore.output); - if (nv_encoder->dcb->type == OUTPUT_LVDS) - call_lvds_script(pScrn, nv_encoder->dcb, head, LVDS_PANEL_ON, - nv_encoder->native_mode->Clock); - if (nv_encoder->dcb->type == OUTPUT_TMDS) { - int clock = nouveau_hw_pllvals_to_clk - (&pNv->saved_regs.head[head].pllvals); - - run_tmds_table(pScrn, nv_encoder->dcb, head, clock); - } - - nv_encoder->last_dpms = NV_DPMS_CLEARED; -} - -static const xf86OutputFuncsRec nv_output_funcs = { - .dpms = nv_output_dpms, - .mode_valid = nv_output_mode_valid, - .mode_fixup = nv_output_mode_fixup, - .mode_set = nv_output_mode_set, - .detect = nv_output_detect, - .get_modes = nv_output_get_edid_modes, - .destroy = nv_output_destroy, - .prepare = nv_output_prepare, - .commit = nv_output_commit, - .create_resources = nv_output_create_resources, - .set_property = nv_output_set_property, -}; - -static const xf86OutputFuncsRec nv_lvds_output_funcs = { - .dpms = nv_output_dpms, - .mode_valid = nv_output_mode_valid, - .mode_fixup = nv_output_mode_fixup, - .mode_set = nv_output_mode_set, - .detect = nv_output_detect, - .get_modes = nv_lvds_output_get_modes, - .destroy = nv_output_destroy, - .prepare = nv_output_prepare, - .commit = nv_output_commit, - .create_resources = nv_output_create_resources, - .set_property = nv_output_set_property, -}; - -static void -nv_add_encoder(ScrnInfoPtr pScrn, struct dcb_entry *dcbent) -{ - NVPtr pNv = NVPTR(pScrn); - struct nouveau_encoder *nv_encoder = &pNv->encoders[dcbent->index]; - - nv_encoder->dcb = dcbent; - nv_encoder->last_dpms = NV_DPMS_CLEARED; - nv_encoder->dithering = pNv->FPDither; - if (pNv->FpScale && pNv->gf4_disp_arch) /* GPU Scaling */ - nv_encoder->scaling_mode = SCALE_ASPECT; - else if (nv_encoder->dcb->type == OUTPUT_LVDS) - nv_encoder->scaling_mode = SCALE_NOSCALE; - else - nv_encoder->scaling_mode = SCALE_PANEL; - if (xf86GetOptValString(pNv->Options, OPTION_SCALING_MODE)) { - nv_encoder->scaling_mode = nv_scaling_mode_lookup(xf86GetOptValString(pNv->Options, OPTION_SCALING_MODE), -1); - if (nv_encoder->scaling_mode == SCALE_INVALID) - nv_encoder->scaling_mode = SCALE_ASPECT; /* default */ - } -} - -static void -nv_add_connector(ScrnInfoPtr pScrn, int i2c_index, int encoders, const xf86OutputFuncsRec *output_funcs, char *outputname) -{ - NVPtr pNv = NVPTR(pScrn); - xf86OutputPtr output; - struct nouveau_connector *nv_connector; - - if (!(output = xf86OutputCreate(pScrn, output_funcs, outputname))) - return; - if (!(nv_connector = xcalloc(1, sizeof (struct nouveau_connector)))) { - xf86OutputDestroy(output); - return; - } - - output->driver_private = nv_connector; - - if (i2c_index < 0xf) - NV_I2CInit(pScrn, &nv_connector->pDDCBus, &pNv->vbios->dcb->i2c[i2c_index], xstrdup(outputname)); - nv_connector->possible_encoders = encoders; -} - -void NvSetupOutputs(ScrnInfoPtr pScrn) -{ - NVPtr pNv = NVPTR(pScrn); - struct parsed_dcb *dcb = pNv->vbios->dcb; - uint16_t connectors[0x10] = { 0 }; - int i, vga_count = 0, dvid_count = 0, dvii_count = 0, lvds_count = 0; - - if (!(pNv->encoders = xcalloc(dcb->entries, sizeof (struct nouveau_encoder)))) - return; - - for (i = 0; i < dcb->entries; i++) { - struct dcb_entry *dcbent = &dcb->entry[i]; - - if (dcbent->type == OUTPUT_TV) - continue; - if (dcbent->type > 3) { - NV_WARN(pScrn, "DCB type %d not known\n", dcbent->type); - continue; - } - - connectors[dcbent->i2c_index] |= 1 << i; - - nv_add_encoder(pScrn, dcbent); - } - - for (i = 0; i < dcb->entries; i++) { - struct dcb_entry *dcbent = &dcb->entry[i]; - int i2c_index = dcbent->i2c_index; - uint16_t encoders = connectors[i2c_index]; - char outputname[20]; - xf86OutputFuncsRec const *funcs = &nv_output_funcs; - - if (!encoders) - continue; - - switch (dcbent->type) { - case OUTPUT_ANALOG: - if (!MULTIPLE_ENCODERS(encoders)) - sprintf(outputname, "VGA-%d", vga_count++); - else - sprintf(outputname, "DVI-I-%d", dvii_count++); - break; - case OUTPUT_TMDS: - if (!MULTIPLE_ENCODERS(encoders)) - sprintf(outputname, "DVI-D-%d", dvid_count++); - else - sprintf(outputname, "DVI-I-%d", dvii_count++); - break; - case OUTPUT_LVDS: - sprintf(outputname, "LVDS-%d", lvds_count++); - funcs = &nv_lvds_output_funcs; - /* don't create i2c adapter when lvds ddc not allowed */ - if (dcbent->lvdsconf.use_straps_for_mode || - pNv->vbios->fp_no_ddc) - i2c_index = 0xf; - break; - default: - continue; - } - - nv_add_connector(pScrn, i2c_index, encoders, funcs, outputname); - connectors[i2c_index] = 0; /* avoid connectors being added multiply */ - } -} |