2
* Copyright 2007 NVIDIA, Corporation
3
* Copyright 2008 Maarten Maathuis
5
* Permission is hereby granted, free of charge, to any person obtaining a
6
* copy of this software and associated documentation files (the "Software"),
7
* to deal in the Software without restriction, including without limitation
8
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
* and/or sell copies of the Software, and to permit persons to whom the
10
* Software is furnished to do so, subject to the following conditions:
12
* The above copyright notice and this permission notice shall be included in
13
* all copies or substantial portions of the Software.
15
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
20
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
#include "nouveau_modeset.h"
25
#include "nouveau_crtc.h"
26
#include "nouveau_output.h"
27
#include "nouveau_connector.h"
30
NV50SorModeValid(nouveauOutputPtr output, DisplayModePtr mode)
34
if (output->type == OUTPUT_LVDS)
37
high_limit = 165000; /* no dual link dvi until we figure it out completely */
39
if (mode->Clock > high_limit)
40
return MODE_CLOCK_HIGH;
42
if (mode->Clock < 25000)
43
return MODE_CLOCK_LOW;
45
if (mode->Flags & V_DBLSCAN)
46
return MODE_NO_DBLESCAN;
48
if (mode->HDisplay > output->native_mode->HDisplay || mode->VDisplay > output->native_mode->VDisplay)
55
NV50SorModeSet(nouveauOutputPtr output, DisplayModePtr mode)
57
ScrnInfoPtr pScrn = output->scrn;
58
xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorModeSet is called.\n");
60
const int sorOff = 0x40 * NV50OrOffset(output);
61
uint32_t mode_ctl = NV50_SOR_MODE_CTRL_OFF;
64
/* Disconnect the SOR */
65
xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disconnecting SOR.\n");
66
NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
70
/* Anyone know a more appropriate name? */
71
DisplayModePtr desired_mode = output->crtc->use_native_mode ? output->crtc->native_mode : mode;
73
if (output->type == OUTPUT_LVDS) {
74
mode_ctl |= NV50_SOR_MODE_CTRL_LVDS;
76
mode_ctl |= NV50_SOR_MODE_CTRL_TMDS;
77
if (desired_mode->Clock > 165000)
78
mode_ctl |= NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK;
82
if (output->crtc->index == 1)
83
mode_ctl |= NV50_SOR_MODE_CTRL_CRTC1;
85
mode_ctl |= NV50_SOR_MODE_CTRL_CRTC0;
87
xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Warning, output has no crtc.\n");
91
if (desired_mode->Flags & V_NHSYNC)
92
mode_ctl |= NV50_SOR_MODE_CTRL_NHSYNC;
94
if (desired_mode->Flags & V_NVSYNC)
95
mode_ctl |= NV50_SOR_MODE_CTRL_NVSYNC;
97
// This wouldn't be necessary, but the server is stupid and calls
98
// nv50_sor_dpms after the output is disconnected, even though the hardware
99
// turns it off automatically.
100
output->SetPowerMode(output, DPMSModeOn);
102
NV50DisplayCommand(pScrn, NV50_SOR0_MODE_CTRL + sorOff, mode_ctl);
104
output->crtc->SetScaleMode(output->crtc, output->scale_mode);
108
NV50SorSetClockMode(nouveauOutputPtr output, int clock)
110
ScrnInfoPtr pScrn = output->scrn;
111
NVPtr pNv = NVPTR(pScrn);
112
const int limit = output->dcb->type == OUTPUT_LVDS ? 112000 : 165000;
114
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
115
"NV50SorSetClockMode is called.\n");
118
ret = nouveau_bios_run_display_table(pScrn, output->dcb, clock);
120
NV_WARN(pScrn, "failed to run output script, may hang!\n");
122
/* just to keep the original behaviour until we can reliably run
123
* the output scripts...
125
if (output->dcb->type == OUTPUT_LVDS)
129
/* 0x70000 was a late addition to nv, mentioned as fixing tmds
130
* initialisation on certain gpu's. */
131
/* I presume it's some kind of clock setting, but what precisely i
134
NVWrite(pNv, NV50_SOR0_CLK_CTRL2 + NV50OrOffset(output) * 0x800,
135
0x70000 | ((clock > limit) ? 0x101 : 0));
139
NV50SorSense(nouveauOutputPtr output)
141
switch (output->type) {
151
NV50SorSetPowerMode(nouveauOutputPtr output, int mode)
153
ScrnInfoPtr pScrn = output->scrn;
154
xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorSetPowerMode is called with mode %d.\n", mode);
156
NVPtr pNv = NVPTR(pScrn);
159
while ((NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800) & NV50_SOR_DPMS_CTRL_PENDING));
161
tmp = NVRead(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800);
162
tmp |= NV50_SOR_DPMS_CTRL_PENDING;
164
if (mode == DPMSModeOn)
165
tmp |= NV50_SOR_DPMS_CTRL_MODE_ON;
167
tmp &= ~NV50_SOR_DPMS_CTRL_MODE_ON;
169
NVWrite(pNv, NV50_SOR0_DPMS_CTRL + NV50OrOffset(output) * 0x800, tmp);
170
while ((NVRead(pNv, NV50_SOR0_DPMS_STATE + NV50OrOffset(output) * 0x800) & NV50_SOR_DPMS_STATE_WAIT));
174
NV50SorDetect(nouveauOutputPtr output)
176
if (output->type == OUTPUT_LVDS) /* assume connected */
182
static nouveauCrtcPtr
183
NV50SorGetCurrentCrtc(nouveauOutputPtr output)
185
ScrnInfoPtr pScrn = output->scrn;
186
xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50SorGetCurrentCrtc is called.\n");
188
NVPtr pNv = NVPTR(pScrn);
189
uint32_t mode_ctrl = NVRead(pNv, NV50_SOR0_MODE_CTRL_VAL + NV50OrOffset(output) * 0x8);
192
* MODE_CTRL values only contain one instance of crtc0 and of crtc1.
193
* This is because we disconnect outputs upon modeset.
194
* Crtc might be off even if we get a positive return.
195
* But we are still associated with that crtc.
197
if (mode_ctrl & NV50_SOR_MODE_CTRL_CRTC0)
199
else if (mode_ctrl & NV50_SOR_MODE_CTRL_CRTC1)
206
NV50SorSetFunctionPointers(nouveauOutputPtr output)
208
output->ModeValid = NV50SorModeValid;
209
output->ModeSet = NV50SorModeSet;
210
output->SetClockMode = NV50SorSetClockMode;
211
output->Sense = NV50SorSense;
212
output->Detect = NV50SorDetect;
213
output->SetPowerMode = NV50SorSetPowerMode;
214
output->GetCurrentCrtc = NV50SorGetCurrentCrtc;