From 2b67015e5911cb7e321da72d7d3d5f0fefedd8e0 Mon Sep 17 00:00:00 2001 From: Stuart Bennett Date: Tue, 7 Oct 2008 02:35:44 +0100 Subject: [PATCH] randr12: reorder nv_output functions roughly into order of use --- src/nv_output.c | 1008 +++++++++++++++++++++++------------------------ 1 file changed, 501 insertions(+), 507 deletions(-) diff --git a/src/nv_output.c b/src/nv_output.c index 900a1be..72acca9 100644 --- a/src/nv_output.c +++ b/src/nv_output.c @@ -32,9 +32,6 @@ if (c->possible_encoders & (1 << i) && \ (e = &pNv->encoders[i])) -static Atom scaling_mode_atom; -static Atom dithering_atom; - static int nv_output_ramdac_offset(struct nouveau_encoder *nv_encoder) { int offset = 0; @@ -47,303 +44,6 @@ static int nv_output_ramdac_offset(struct nouveau_encoder *nv_encoder) return offset; } -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; - NVCrtcRegPtr regp; - xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); - int i; - - if (mode == DPMSModeOn) { - nv_crtc = to_nouveau_crtc(crtc); - regp = &pNv->ModeReg.crtc_reg[nv_crtc->head]; - - nv_crtc->fp_users |= 1 << nv_encoder->dcb->index; - NVWriteRAMDAC(pNv, nv_crtc->head, NV_RAMDAC_FP_CONTROL, regp->fp_control & ~0x20000022); - } else - for (i = 0; i <= pNv->twoHeads; i++) { - nv_crtc = to_nouveau_crtc(xf86_config->crtc[i]); - regp = &pNv->ModeReg.crtc_reg[nv_crtc->head]; - - nv_crtc->fp_users &= ~(1 << nv_encoder->dcb->index); - if (!nv_crtc->fp_users) { - /* cut the FP output */ - regp->fp_control |= 0x20000022; - NVWriteRAMDAC(pNv, nv_crtc->head, NV_RAMDAC_FP_CONTROL, regp->fp_control); - } - } -} - -static void nv_digital_output_prepare_sel_clk(NVPtr pNv, struct nouveau_encoder *nv_encoder, int head); - -static void -lvds_encoder_dpms(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder, xf86CrtcPtr crtc, int mode) -{ - NVPtr pNv = NVPTR(pScrn); - - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "lvds_encoder_dpms is called with mode %d\n", mode); - - if (nv_encoder->last_dpms == mode) - return; - nv_encoder->last_dpms = mode; - - 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 = nv_get_digital_bound_head(pNv, nv_encoder->dcb->or); - int pclk = nv_encoder->native_mode->Clock; - - if (crtc) - head = to_nouveau_crtc(crtc)->head; - - if (mode == DPMSModeOn) - call_lvds_script(pScrn, nv_encoder->dcb, head, LVDS_PANEL_ON, pclk); - else - call_lvds_script(pScrn, nv_encoder->dcb, head, LVDS_PANEL_OFF, pclk); - } - - 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->ModeReg.sel_clk = NVReadRAMDAC(pNv, 0, NV_RAMDAC_SEL_CLK); - pNv->ModeReg.sel_clk &= ~0xf0; - } - NVWriteRAMDAC(pNv, 0, NV_RAMDAC_SEL_CLK, pNv->ModeReg.sel_clk); -} - -static void -vga_encoder_dpms(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder, xf86CrtcPtr crtc, int mode) -{ - NVPtr pNv = NVPTR(pScrn); - - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "vga_encoder_dpms is called with mode %d\n", mode); - - if (nv_encoder->last_dpms == mode) - return; - nv_encoder->last_dpms = mode; - - if (pNv->twoHeads) { - uint32_t outputval = NVReadRAMDAC(pNv, 0, NV_RAMDAC_OUTPUT + nv_output_ramdac_offset(nv_encoder)); - - if (mode == DPMSModeOff) - NVWriteRAMDAC(pNv, 0, NV_RAMDAC_OUTPUT + nv_output_ramdac_offset(nv_encoder), - outputval & ~NV_RAMDAC_OUTPUT_DAC_ENABLE); - else if (mode == DPMSModeOn) - NVWriteRAMDAC(pNv, 0, NV_RAMDAC_OUTPUT + nv_output_ramdac_offset(nv_encoder), - outputval | NV_RAMDAC_OUTPUT_DAC_ENABLE); - } -} - -static void -tmds_encoder_dpms(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder, xf86CrtcPtr crtc, int mode) -{ - NVPtr pNv = NVPTR(pScrn); - - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "tmds_encoder_dpms is called with mode %d\n", mode); - - if (nv_encoder->last_dpms == mode) - return; - nv_encoder->last_dpms = mode; - - dpms_update_fp_control(pScrn, nv_encoder, crtc, mode); - - if (nv_encoder->dcb->location != LOC_ON_CHIP) { - struct nouveau_crtc *nv_crtc; - int i; - - if (mode == DPMSModeOn) { - nv_crtc = to_nouveau_crtc(crtc); - NVWriteVgaCrtc(pNv, nv_crtc->head, NV_VGA_CRTCX_LCD, - pNv->ModeReg.crtc_reg[nv_crtc->head].CRTC[NV_VGA_CRTCX_LCD]); - } else - for (i = 0; i <= pNv->twoHeads; i++) - NVWriteVgaCrtc(pNv, i, NV_VGA_CRTCX_LCD, - NVReadVgaCrtc(pNv, i, NV_VGA_CRTCX_LCD) & ~((nv_encoder->dcb->or << 4) & 0x30)); - } -} - -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; - - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_encoder_save is called.\n"); - - if (pNv->twoHeads && nv_encoder->dcb->type == OUTPUT_ANALOG) - nv_encoder->restore.output = NVReadRAMDAC(pNv, 0, NV_RAMDAC_OUTPUT + nv_output_ramdac_offset(nv_encoder)); - if (nv_encoder->dcb->type == OUTPUT_TMDS || nv_encoder->dcb->type == OUTPUT_LVDS) - nv_encoder->restore.head = nv_get_digital_bound_head(pNv, nv_encoder->dcb->or); -} - -static uint32_t nv_get_clock_from_crtc(ScrnInfoPtr pScrn, RIVA_HW_STATE *state, uint8_t crtc) -{ - NVPtr pNv = NVPTR(pScrn); - struct pll_lims pll_lim; - uint32_t vplla = state->crtc_reg[crtc].vpll_a; - uint32_t vpllb = state->crtc_reg[crtc].vpll_b; - bool nv40_single = pNv->Architecture == 0x40 && - ((!crtc && state->reg580 & NV_RAMDAC_580_VPLL1_ACTIVE) || - (crtc && state->reg580 & NV_RAMDAC_580_VPLL2_ACTIVE)); - - if (!get_pll_limits(pScrn, crtc ? VPLL2 : VPLL1, &pll_lim)) - return 0; - - return nv_decode_pll_highregs(pNv, vplla, vpllb, nv40_single, pll_lim.refclk); -} - -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; - - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_encoder_restore is called.\n"); - - if (pNv->twoHeads && nv_encoder->dcb->type == OUTPUT_ANALOG) - NVWriteRAMDAC(pNv, 0, - NV_RAMDAC_OUTPUT + 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 = nv_get_clock_from_crtc(pScrn, &pNv->SavedReg, head); - - run_tmds_table(pScrn, nv_encoder->dcb, head, clock); - } - - nv_encoder->last_dpms = NV_DPMS_CLEARED; -} - -static int nv_output_mode_valid(xf86OutputPtr output, DisplayModePtr mode) -{ - struct nouveau_encoder *nv_encoder = to_nouveau_connector(output)->detected_encoder; - NVPtr pNv = NVPTR(output->scrn); - - 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 (mode->Clock > (pNv->twoStagePLL ? 400000 : 350000)) - return MODE_CLOCK_HIGH; - if (mode->Clock < 12000) - return MODE_CLOCK_LOW; - } - if (nv_encoder->dcb->type == OUTPUT_LVDS || nv_encoder->dcb->type == OUTPUT_TMDS) - /* 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 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); - ScrnInfoPtr pScrn = output->scrn; - - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_output_mode_fixup is called.\n"); - - /* For internal panels and gpu scaling on DVI we need the native mode */ - if (nv_encoder->dcb->type == OUTPUT_LVDS || - (nv_encoder->dcb->type == OUTPUT_TMDS && 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_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 nouveau_crtc *nv_crtc = to_nouveau_crtc(output->crtc); - - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_output_mode_set is called.\n"); - - if (pNv->twoHeads && nv_encoder->dcb->type == OUTPUT_ANALOG) - /* bit 16-19 are bits that are set on some G70 cards, - * but don't seem to have much effect */ - NVWriteRAMDAC(pNv, 0, NV_RAMDAC_OUTPUT + nv_output_ramdac_offset(nv_encoder), - nv_crtc->head << 8 | NV_RAMDAC_OUTPUT_DAC_ENABLE); - if (nv_encoder->dcb->type == OUTPUT_TMDS) - run_tmds_table(pScrn, nv_encoder->dcb, nv_crtc->head, adjusted_mode->Clock); - else if (nv_encoder->dcb->type == OUTPUT_LVDS) - call_lvds_script(pScrn, nv_encoder->dcb, nv_crtc->head, LVDS_RESET, adjusted_mode->Clock); - - /* This could use refinement for flatpanels, but it should work this way */ - if (pNv->NVArch < 0x44) - NVWriteRAMDAC(pNv, 0, NV_RAMDAC_TEST_CONTROL + nv_output_ramdac_offset(nv_encoder), 0xf0000000); - else - NVWriteRAMDAC(pNv, 0, NV_RAMDAC_TEST_CONTROL + nv_output_ramdac_offset(nv_encoder), 0x00100000); -} - static Bool nv_load_detect(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder) { @@ -489,112 +189,342 @@ nv_output_detect(xf86OutputPtr output) } } - if (ret != XF86OutputStatusDisconnected) - update_output_fields(output, det_encoder); + 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; + ScrnInfoPtr pScrn = output->scrn; + int max_h_active = 0, max_v_active = 0; + int i; + DisplayModePtr mode; + + for (i = 0; i < DET_TIMINGS; i++) { + /* We only look at detailed timings atm */ + if (nv_connector->edid->det_mon[i].type != DT) + continue; + /* Selecting only based on width ok? */ + if (nv_connector->edid->det_mon[i].section.d_timings.h_active > max_h_active) { + max_h_active = nv_connector->edid->det_mon[i].section.d_timings.h_active; + max_v_active = nv_connector->edid->det_mon[i].section.d_timings.v_active; + } + } + if (!(max_h_active && max_v_active)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No EDID detailed timings available, bailing out.\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; + ScrnInfoPtr pScrn = output->scrn; + DisplayModePtr edid_modes; + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_output_get_edid_modes is called.\n"); + + if (!(edid_modes = xf86OutputGetEDIDModes(output))) + return edid_modes; + + if (nv_encoder->dcb->type == OUTPUT_TMDS || nv_encoder->dcb->type == OUTPUT_LVDS) + if (!get_native_mode_from_edid(output, edid_modes)) + return NULL; + + if (nv_encoder->dcb->type == OUTPUT_LVDS) { + static bool dual_link_correction_done = false; + + if (!dual_link_correction_done) { + parse_lvds_manufacturer_table(pScrn, &NVPTR(pScrn)->VBIOS, nv_encoder->native_mode->Clock); + dual_link_correction_done = true; + } + } + + 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; + NVPtr pNv = NVPTR(pScrn); + DisplayModePtr modes; + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_lvds_output_get_modes is called.\n"); + + if ((modes = nv_output_get_edid_modes(output))) + return modes; + + if (!nv_encoder->dcb->lvdsconf.use_straps_for_mode || pNv->VBIOS.fp.native_mode == NULL) + return NULL; + + if (nv_encoder->native_mode) + xfree(nv_encoder->native_mode); + nv_encoder->native_mode = xf86DuplicateMode(pNv->VBIOS.fp.native_mode); + + return xf86DuplicateMode(pNv->VBIOS.fp.native_mode); +} + +static int nv_output_mode_valid(xf86OutputPtr output, DisplayModePtr mode) +{ + struct nouveau_encoder *nv_encoder = to_nouveau_connector(output)->detected_encoder; + NVPtr pNv = NVPTR(output->scrn); + + 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 (mode->Clock > (pNv->twoStagePLL ? 400000 : 350000)) + return MODE_CLOCK_HIGH; + if (mode->Clock < 12000) + return MODE_CLOCK_LOW; + } + if (nv_encoder->dcb->type == OUTPUT_LVDS || nv_encoder->dcb->type == OUTPUT_TMDS) + /* 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; + ScrnInfoPtr pScrn = output->scrn; + NVPtr pNv = NVPTR(output->scrn); + int i; + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_output_destroy is called.\n"); + + 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 Atom scaling_mode_atom; +#define SCALING_MODE_NAME "SCALING_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} +}; + +static Atom dithering_atom; +#define DITHERING_MODE_NAME "DITHERING" + +static void +nv_output_create_resources(xf86OutputPtr output) +{ + struct nouveau_encoder *nv_encoder = to_nouveau_encoder(output); + ScrnInfoPtr pScrn = output->scrn; + INT32 dithering_range[2] = { 0, 1 }; + int error, i; + + /* may be called before encoder is picked, resources will be created + * by update_output_fields() + */ + if (!nv_encoder) + return; + + /* no properties for vga */ + if (nv_encoder->dcb->type == OUTPUT_ANALOG) + return; - return ret; -} + /* + * Setup scaling mode property. + */ + scaling_mode_atom = MakeAtom(SCALING_MODE_NAME, sizeof(SCALING_MODE_NAME) - 1, TRUE); -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; - ScrnInfoPtr pScrn = output->scrn; - int max_h_active = 0, max_v_active = 0; - int i; - DisplayModePtr mode; + error = RRConfigureOutputProperty(output->randr_output, + scaling_mode_atom, TRUE, FALSE, FALSE, + 0, NULL); - for (i = 0; i < DET_TIMINGS; i++) { - /* We only look at detailed timings atm */ - if (nv_connector->edid->det_mon[i].type != DT) - continue; - /* Selecting only based on width ok? */ - if (nv_connector->edid->det_mon[i].section.d_timings.h_active > max_h_active) { - max_h_active = nv_connector->edid->det_mon[i].section.d_timings.h_active; - max_v_active = nv_connector->edid->det_mon[i].section.d_timings.v_active; - } + if (error != 0) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "RRConfigureOutputProperty error, %d\n", error); } - if (!(max_h_active && max_v_active)) { - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No EDID detailed timings available, bailing out.\n"); - return NULL; + + char *existing_scale_name = NULL; + for (i = 0; scaling_mode[i].name; i++) + if (scaling_mode[i].mode == nv_encoder->scaling_mode) + existing_scale_name = scaling_mode[i].name; + + error = RRChangeOutputProperty(output->randr_output, scaling_mode_atom, + XA_STRING, 8, PropModeReplace, + strlen(existing_scale_name), + existing_scale_name, FALSE, TRUE); + + if (error != 0) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to set scaling mode, %d\n", error); } - if (nv_encoder->native_mode) { - xfree(nv_encoder->native_mode); - nv_encoder->native_mode = NULL; + /* + * Setup dithering property. + */ + dithering_atom = MakeAtom(DITHERING_MODE_NAME, sizeof(DITHERING_MODE_NAME) - 1, TRUE); + + error = RRConfigureOutputProperty(output->randr_output, + dithering_atom, TRUE, TRUE, FALSE, + 2, dithering_range); + + if (error != 0) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "RRConfigureOutputProperty error, %d\n", error); } - 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); - } - } + /* promote bool into int32 to make RandR DIX and big endian happy */ + int32_t existing_dither = nv_encoder->dithering; + error = RRChangeOutputProperty(output->randr_output, dithering_atom, + XA_INTEGER, 32, PropModeReplace, 1, + &existing_dither, FALSE, TRUE); + + if (error != 0) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to set dithering mode, %d\n", error); } - return nv_encoder->native_mode; + RRPostPendingProperties(output->randr_output); } -static DisplayModePtr -nv_output_get_edid_modes(xf86OutputPtr output) +static Bool +nv_output_set_property(xf86OutputPtr output, Atom property, + RRPropertyValuePtr value) { - struct nouveau_connector *nv_connector = to_nouveau_connector(output); - struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; - ScrnInfoPtr pScrn = output->scrn; - DisplayModePtr edid_modes; + struct nouveau_encoder *nv_encoder = to_nouveau_encoder(output); - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_output_get_edid_modes is called.\n"); + if (property == scaling_mode_atom) { + int32_t ret; + char *name = NULL; - if (!(edid_modes = xf86OutputGetEDIDModes(output))) - return edid_modes; + if (value->type != XA_STRING || value->format != 8) + return FALSE; - if (nv_encoder->dcb->type == OUTPUT_TMDS || nv_encoder->dcb->type == OUTPUT_LVDS) - if (!get_native_mode_from_edid(output, edid_modes)) - return NULL; + name = (char *) value->data; - if (nv_encoder->dcb->type == OUTPUT_LVDS) { - static bool dual_link_correction_done = false; + /* Match a string to a scaling mode */ + ret = nv_scaling_mode_lookup(name, value->size); + if (ret == SCALE_INVALID) + return FALSE; - if (!dual_link_correction_done) { - parse_lvds_manufacturer_table(pScrn, &NVPTR(pScrn)->VBIOS, nv_encoder->native_mode->Clock); - dual_link_correction_done = true; - } + /* LVDS must always use gpu scaling. */ + if (ret == SCALE_PANEL && nv_encoder->dcb->type == OUTPUT_LVDS) + return FALSE; + + nv_encoder->scaling_mode = ret; + } else if (property == dithering_atom) { + if (value->type != XA_INTEGER || value->format != 32) + return FALSE; + + int32_t val = *(int32_t *) value->data; + + if (val < 0 || val > 1) + return FALSE; + + nv_encoder->dithering = val; } - return edid_modes; + return TRUE; } -static void -nv_output_destroy (xf86OutputPtr output) +static Bool +nv_output_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) { struct nouveau_connector *nv_connector = to_nouveau_connector(output); - struct nouveau_encoder *nv_encoder; + + 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); ScrnInfoPtr pScrn = output->scrn; - NVPtr pNv = NVPTR(output->scrn); - int i; - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_output_destroy is called.\n"); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_output_mode_fixup is called.\n"); - if (!nv_connector) - return; + /* For internal panels and gpu scaling on DVI we need the native mode */ + if (nv_encoder->dcb->type == OUTPUT_LVDS || + (nv_encoder->dcb->type == OUTPUT_TMDS && 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; - 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); + 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) @@ -671,150 +601,238 @@ nv_output_prepare(xf86OutputPtr output) } static void -nv_output_commit(xf86OutputPtr output) +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 nouveau_crtc *nv_crtc = to_nouveau_crtc(output->crtc); + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_output_mode_set is called.\n"); + + if (pNv->twoHeads && nv_encoder->dcb->type == OUTPUT_ANALOG) + /* bit 16-19 are bits that are set on some G70 cards, + * but don't seem to have much effect */ + NVWriteRAMDAC(pNv, 0, NV_RAMDAC_OUTPUT + nv_output_ramdac_offset(nv_encoder), + nv_crtc->head << 8 | NV_RAMDAC_OUTPUT_DAC_ENABLE); + if (nv_encoder->dcb->type == OUTPUT_TMDS) + run_tmds_table(pScrn, nv_encoder->dcb, nv_crtc->head, adjusted_mode->Clock); + else if (nv_encoder->dcb->type == OUTPUT_LVDS) + call_lvds_script(pScrn, nv_encoder->dcb, nv_crtc->head, LVDS_RESET, adjusted_mode->Clock); + + /* This could use refinement for flatpanels, but it should work this way */ + if (pNv->NVArch < 0x44) + NVWriteRAMDAC(pNv, 0, NV_RAMDAC_TEST_CONTROL + nv_output_ramdac_offset(nv_encoder), 0xf0000000); + else + NVWriteRAMDAC(pNv, 0, NV_RAMDAC_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; + xf86CrtcPtr crtc = output->crtc; + struct nouveau_crtc *nv_crtc = to_nouveau_crtc(crtc); + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_output_commit is called.\n"); + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Output %s is running on CRTC %d using output %c\n", output->name, nv_crtc->head, '@' + ffs(nv_encoder->dcb->or)); + + output->funcs->dpms(output, DPMSModeOn); +} + +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; + NVCrtcRegPtr regp; + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); + int i; + + if (mode == DPMSModeOn) { + nv_crtc = to_nouveau_crtc(crtc); + regp = &pNv->ModeReg.crtc_reg[nv_crtc->head]; + + nv_crtc->fp_users |= 1 << nv_encoder->dcb->index; + NVWriteRAMDAC(pNv, nv_crtc->head, NV_RAMDAC_FP_CONTROL, regp->fp_control & ~0x20000022); + } else + for (i = 0; i <= pNv->twoHeads; i++) { + nv_crtc = to_nouveau_crtc(xf86_config->crtc[i]); + regp = &pNv->ModeReg.crtc_reg[nv_crtc->head]; + + nv_crtc->fp_users &= ~(1 << nv_encoder->dcb->index); + if (!nv_crtc->fp_users) { + /* cut the FP output */ + regp->fp_control |= 0x20000022; + NVWriteRAMDAC(pNv, nv_crtc->head, NV_RAMDAC_FP_CONTROL, regp->fp_control); + } + } +} + +static void +lvds_encoder_dpms(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder, xf86CrtcPtr crtc, int mode) { - struct nouveau_encoder *nv_encoder = to_nouveau_encoder(output); - ScrnInfoPtr pScrn = output->scrn; - xf86CrtcPtr crtc = output->crtc; - struct nouveau_crtc *nv_crtc = to_nouveau_crtc(crtc); + NVPtr pNv = NVPTR(pScrn); - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_output_commit is called.\n"); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "lvds_encoder_dpms is called with mode %d\n", mode); - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Output %s is running on CRTC %d using output %c\n", output->name, nv_crtc->head, '@' + ffs(nv_encoder->dcb->or)); + if (nv_encoder->last_dpms == mode) + return; + nv_encoder->last_dpms = mode; - output->funcs->dpms(output, DPMSModeOn); -} + 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 = nv_get_digital_bound_head(pNv, nv_encoder->dcb->or); + int pclk = nv_encoder->native_mode->Clock; -/* - * Several scaling modes exist, let the user choose. - */ -#define SCALING_MODE_NAME "SCALING_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} -}; + if (crtc) + head = to_nouveau_crtc(crtc)->head; -#define DITHERING_MODE_NAME "DITHERING" + if (mode == DPMSModeOn) + call_lvds_script(pScrn, nv_encoder->dcb, head, LVDS_PANEL_ON, pclk); + else + call_lvds_script(pScrn, nv_encoder->dcb, head, LVDS_PANEL_OFF, pclk); + } + + 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->ModeReg.sel_clk = NVReadRAMDAC(pNv, 0, NV_RAMDAC_SEL_CLK); + pNv->ModeReg.sel_clk &= ~0xf0; + } + NVWriteRAMDAC(pNv, 0, NV_RAMDAC_SEL_CLK, pNv->ModeReg.sel_clk); +} static void -nv_output_create_resources(xf86OutputPtr output) +vga_encoder_dpms(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder, xf86CrtcPtr crtc, int mode) { - struct nouveau_encoder *nv_encoder = to_nouveau_encoder(output); - ScrnInfoPtr pScrn = output->scrn; - INT32 dithering_range[2] = { 0, 1 }; - int error, i; + NVPtr pNv = NVPTR(pScrn); - /* may be called before encoder is picked, resources will be created - * by update_output_fields() - */ - if (!nv_encoder) - return; + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "vga_encoder_dpms is called with mode %d\n", mode); - /* no properties for vga */ - if (nv_encoder->dcb->type == OUTPUT_ANALOG) + if (nv_encoder->last_dpms == mode) return; + nv_encoder->last_dpms = mode; - /* - * Setup scaling mode property. - */ - scaling_mode_atom = MakeAtom(SCALING_MODE_NAME, sizeof(SCALING_MODE_NAME) - 1, TRUE); - - error = RRConfigureOutputProperty(output->randr_output, - scaling_mode_atom, TRUE, FALSE, FALSE, - 0, NULL); + if (pNv->twoHeads) { + uint32_t outputval = NVReadRAMDAC(pNv, 0, NV_RAMDAC_OUTPUT + nv_output_ramdac_offset(nv_encoder)); - if (error != 0) { - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, - "RRConfigureOutputProperty error, %d\n", error); + if (mode == DPMSModeOff) + NVWriteRAMDAC(pNv, 0, NV_RAMDAC_OUTPUT + nv_output_ramdac_offset(nv_encoder), + outputval & ~NV_RAMDAC_OUTPUT_DAC_ENABLE); + else if (mode == DPMSModeOn) + NVWriteRAMDAC(pNv, 0, NV_RAMDAC_OUTPUT + nv_output_ramdac_offset(nv_encoder), + outputval | NV_RAMDAC_OUTPUT_DAC_ENABLE); } +} - char *existing_scale_name = NULL; - for (i = 0; scaling_mode[i].name; i++) - if (scaling_mode[i].mode == nv_encoder->scaling_mode) - existing_scale_name = scaling_mode[i].name; +static void +tmds_encoder_dpms(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder, xf86CrtcPtr crtc, int mode) +{ + NVPtr pNv = NVPTR(pScrn); - error = RRChangeOutputProperty(output->randr_output, scaling_mode_atom, - XA_STRING, 8, PropModeReplace, - strlen(existing_scale_name), - existing_scale_name, FALSE, TRUE); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "tmds_encoder_dpms is called with mode %d\n", mode); - if (error != 0) { - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, - "Failed to set scaling mode, %d\n", error); - } + if (nv_encoder->last_dpms == mode) + return; + nv_encoder->last_dpms = mode; - /* - * Setup dithering property. - */ - dithering_atom = MakeAtom(DITHERING_MODE_NAME, sizeof(DITHERING_MODE_NAME) - 1, TRUE); + dpms_update_fp_control(pScrn, nv_encoder, crtc, mode); - error = RRConfigureOutputProperty(output->randr_output, - dithering_atom, TRUE, TRUE, FALSE, - 2, dithering_range); + if (nv_encoder->dcb->location != LOC_ON_CHIP) { + struct nouveau_crtc *nv_crtc; + int i; - if (error != 0) { - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, - "RRConfigureOutputProperty error, %d\n", error); + if (mode == DPMSModeOn) { + nv_crtc = to_nouveau_crtc(crtc); + NVWriteVgaCrtc(pNv, nv_crtc->head, NV_VGA_CRTCX_LCD, + pNv->ModeReg.crtc_reg[nv_crtc->head].CRTC[NV_VGA_CRTCX_LCD]); + } else + for (i = 0; i <= pNv->twoHeads; i++) + NVWriteVgaCrtc(pNv, i, NV_VGA_CRTCX_LCD, + NVReadVgaCrtc(pNv, i, NV_VGA_CRTCX_LCD) & ~((nv_encoder->dcb->or << 4) & 0x30)); } +} - /* promote bool into int32 to make RandR DIX and big endian happy */ - int32_t existing_dither = nv_encoder->dithering; - error = RRChangeOutputProperty(output->randr_output, dithering_atom, - XA_INTEGER, 32, PropModeReplace, 1, - &existing_dither, FALSE, TRUE); +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 }; - if (error != 0) { - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, - "Failed to set dithering mode, %d\n", error); - } + 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); - RRPostPendingProperties(output->randr_output); + 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); } -static Bool -nv_output_set_property(xf86OutputPtr output, Atom property, - RRPropertyValuePtr value) +static uint32_t nv_get_clock_from_crtc(ScrnInfoPtr pScrn, RIVA_HW_STATE *state, uint8_t crtc) { - struct nouveau_encoder *nv_encoder = to_nouveau_encoder(output); + NVPtr pNv = NVPTR(pScrn); + struct pll_lims pll_lim; + uint32_t vplla = state->crtc_reg[crtc].vpll_a; + uint32_t vpllb = state->crtc_reg[crtc].vpll_b; + bool nv40_single = pNv->Architecture == 0x40 && + ((!crtc && state->reg580 & NV_RAMDAC_580_VPLL1_ACTIVE) || + (crtc && state->reg580 & NV_RAMDAC_580_VPLL2_ACTIVE)); - if (property == scaling_mode_atom) { - int32_t ret; - char *name = NULL; + if (!get_pll_limits(pScrn, crtc ? VPLL2 : VPLL1, &pll_lim)) + return 0; - if (value->type != XA_STRING || value->format != 8) - return FALSE; + return nv_decode_pll_highregs(pNv, vplla, vpllb, nv40_single, pll_lim.refclk); +} - name = (char *) value->data; +void nv_encoder_save(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder) +{ + NVPtr pNv = NVPTR(pScrn); - /* Match a string to a scaling mode */ - ret = nv_scaling_mode_lookup(name, value->size); - if (ret == SCALE_INVALID) - return FALSE; + if (!nv_encoder->dcb) /* uninitialised encoder */ + return; - /* LVDS must always use gpu scaling. */ - if (ret == SCALE_PANEL && nv_encoder->dcb->type == OUTPUT_LVDS) - return FALSE; + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_encoder_save is called.\n"); - nv_encoder->scaling_mode = ret; - } else if (property == dithering_atom) { - if (value->type != XA_INTEGER || value->format != 32) - return FALSE; + if (pNv->twoHeads && nv_encoder->dcb->type == OUTPUT_ANALOG) + nv_encoder->restore.output = NVReadRAMDAC(pNv, 0, NV_RAMDAC_OUTPUT + nv_output_ramdac_offset(nv_encoder)); + if (nv_encoder->dcb->type == OUTPUT_TMDS || nv_encoder->dcb->type == OUTPUT_LVDS) + nv_encoder->restore.head = nv_get_digital_bound_head(pNv, nv_encoder->dcb->or); +} - int32_t val = *(int32_t *) value->data; +void nv_encoder_restore(ScrnInfoPtr pScrn, struct nouveau_encoder *nv_encoder) +{ + NVPtr pNv = NVPTR(pScrn); + int head = nv_encoder->restore.head; - if (val < 0 || val > 1) - return FALSE; + if (!nv_encoder->dcb) /* uninitialised encoder */ + return; - nv_encoder->dithering = val; + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_encoder_restore is called.\n"); + + if (pNv->twoHeads && nv_encoder->dcb->type == OUTPUT_ANALOG) + NVWriteRAMDAC(pNv, 0, + NV_RAMDAC_OUTPUT + 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 = nv_get_clock_from_crtc(pScrn, &pNv->SavedReg, head); + + run_tmds_table(pScrn, nv_encoder->dcb, head, clock); } - return TRUE; + nv_encoder->last_dpms = NV_DPMS_CLEARED; } static const xf86OutputFuncsRec nv_output_funcs = { @@ -831,30 +849,6 @@ static const xf86OutputFuncsRec nv_output_funcs = { .set_property = nv_output_set_property, }; -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; - NVPtr pNv = NVPTR(pScrn); - DisplayModePtr modes; - - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "nv_lvds_output_get_modes is called.\n"); - - if ((modes = nv_output_get_edid_modes(output))) - return modes; - - if (!nv_encoder->dcb->lvdsconf.use_straps_for_mode || pNv->VBIOS.fp.native_mode == NULL) - return NULL; - - if (nv_encoder->native_mode) - xfree(nv_encoder->native_mode); - nv_encoder->native_mode = xf86DuplicateMode(pNv->VBIOS.fp.native_mode); - - return xf86DuplicateMode(pNv->VBIOS.fp.native_mode); -} - static const xf86OutputFuncsRec nv_lvds_output_funcs = { .dpms = nv_output_dpms, .mode_valid = nv_output_mode_valid, -- 2.32.0.93.g670b81a890