aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nv50_sor.c
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2009-12-11 04:24:15 -0500
committerDave Airlie <airlied@redhat.com>2009-12-11 06:29:34 -0500
commit6ee738610f41b59733f63718f0bdbcba7d3a3f12 (patch)
treeeccb9f07671998c50a1bc606a54cd6f82ba43e0a /drivers/gpu/drm/nouveau/nv50_sor.c
parentd1ede145cea25c5b6d2ebb19b167af14e374bb45 (diff)
drm/nouveau: Add DRM driver for NVIDIA GPUs
This adds a drm/kms staging non-API stable driver for GPUs from NVIDIA. This driver is a KMS-based driver and requires a compatible nouveau userspace libdrm and nouveau X.org driver. This driver requires firmware files not available in this kernel tree, interested parties can find them via the nouveau project git archive. This driver is reverse engineered, and is in no way supported by nVidia. Support for nearly the complete range of nvidia hw from nv04->g80 (nv50) is available, and the kms driver should support driving nearly all output types (displayport is under development still) along with supporting suspend/resume. This work is all from the upstream nouveau project found at nouveau.freedesktop.org. The original authors list from nouveau git tree is: Anssi Hannula <anssi.hannula@iki.fi> Ben Skeggs <bskeggs@redhat.com> Francisco Jerez <currojerez@riseup.net> Maarten Maathuis <madman2003@gmail.com> Marcin Koƛcielnicki <koriakin@0x04.net> Matthew Garrett <mjg@redhat.com> Matt Parnell <mparnell@gmail.com> Patrice Mandin <patmandin@gmail.com> Pekka Paalanen <pq@iki.fi> Xavier Chantry <shiningxc@gmail.com> along with project founder Stephane Marchesin <marchesin@icps.u-strasbg.fr> Signed-off-by: Ben Skeggs <bskeggs@redhat.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv50_sor.c')
-rw-r--r--drivers/gpu/drm/nouveau/nv50_sor.c309
1 files changed, 309 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c
new file mode 100644
index 00000000000..8c280463a66
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_sor.c
@@ -0,0 +1,309 @@
1/*
2 * Copyright (C) 2008 Maarten Maathuis.
3 * All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial
15 * portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 */
26
27#include "drmP.h"
28#include "drm_crtc_helper.h"
29
30#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
31#include "nouveau_reg.h"
32#include "nouveau_drv.h"
33#include "nouveau_dma.h"
34#include "nouveau_encoder.h"
35#include "nouveau_connector.h"
36#include "nouveau_crtc.h"
37#include "nv50_display.h"
38
39static void
40nv50_sor_disconnect(struct nouveau_encoder *nv_encoder)
41{
42 struct drm_device *dev = to_drm_encoder(nv_encoder)->dev;
43 struct drm_nouveau_private *dev_priv = dev->dev_private;
44 struct nouveau_channel *evo = dev_priv->evo;
45 int ret;
46
47 NV_DEBUG(dev, "Disconnecting SOR %d\n", nv_encoder->or);
48
49 ret = RING_SPACE(evo, 2);
50 if (ret) {
51 NV_ERROR(dev, "no space while disconnecting SOR\n");
52 return;
53 }
54 BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
55 OUT_RING(evo, 0);
56}
57
58static void
59nv50_sor_dp_link_train(struct drm_encoder *encoder)
60{
61 struct drm_device *dev = encoder->dev;
62 struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
63 struct bit_displayport_encoder_table *dpe;
64 int dpe_headerlen;
65
66 dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
67 if (!dpe) {
68 NV_ERROR(dev, "SOR-%d: no DP encoder table!\n", nv_encoder->or);
69 return;
70 }
71
72 if (dpe->script0) {
73 NV_DEBUG(dev, "SOR-%d: running DP script 0\n", nv_encoder->or);
74 nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0),
75 nv_encoder->dcb);
76 }
77
78 if (!nouveau_dp_link_train(encoder))
79 NV_ERROR(dev, "SOR-%d: link training failed\n", nv_encoder->or);
80
81 if (dpe->script1) {
82 NV_DEBUG(dev, "SOR-%d: running DP script 1\n", nv_encoder->or);
83 nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1),
84 nv_encoder->dcb);
85 }
86}
87
88static void
89nv50_sor_dpms(struct drm_encoder *encoder, int mode)
90{
91 struct drm_device *dev = encoder->dev;
92 struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
93 uint32_t val;
94 int or = nv_encoder->or;
95
96 NV_DEBUG(dev, "or %d mode %d\n", or, mode);
97
98 /* wait for it to be done */
99 if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_CTRL(or),
100 NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING, 0)) {
101 NV_ERROR(dev, "timeout: SOR_DPMS_CTRL_PENDING(%d) == 0\n", or);
102 NV_ERROR(dev, "SOR_DPMS_CTRL(%d) = 0x%08x\n", or,
103 nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or)));
104 }
105
106 val = nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or));
107
108 if (mode == DRM_MODE_DPMS_ON)
109 val |= NV50_PDISPLAY_SOR_DPMS_CTRL_ON;
110 else
111 val &= ~NV50_PDISPLAY_SOR_DPMS_CTRL_ON;
112
113 nv_wr32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or), val |
114 NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING);
115 if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_STATE(or),
116 NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) {
117 NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", or);
118 NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", or,
119 nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(or)));
120 }
121
122 if (nv_encoder->dcb->type == OUTPUT_DP && mode == DRM_MODE_DPMS_ON)
123 nv50_sor_dp_link_train(encoder);
124}
125
126static void
127nv50_sor_save(struct drm_encoder *encoder)
128{
129 NV_ERROR(encoder->dev, "!!\n");
130}
131
132static void
133nv50_sor_restore(struct drm_encoder *encoder)
134{
135 NV_ERROR(encoder->dev, "!!\n");
136}
137
138static bool
139nv50_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
140 struct drm_display_mode *adjusted_mode)
141{
142 struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
143 struct nouveau_connector *connector;
144
145 NV_DEBUG(encoder->dev, "or %d\n", nv_encoder->or);
146
147 connector = nouveau_encoder_connector_get(nv_encoder);
148 if (!connector) {
149 NV_ERROR(encoder->dev, "Encoder has no connector\n");
150 return false;
151 }
152
153 if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
154 connector->native_mode) {
155 int id = adjusted_mode->base.id;
156 *adjusted_mode = *connector->native_mode;
157 adjusted_mode->base.id = id;
158 }
159
160 return true;
161}
162
163static void
164nv50_sor_prepare(struct drm_encoder *encoder)
165{
166}
167
168static void
169nv50_sor_commit(struct drm_encoder *encoder)
170{
171}
172
173static void
174nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
175 struct drm_display_mode *adjusted_mode)
176{
177 struct drm_nouveau_private *dev_priv = encoder->dev->dev_private;
178 struct nouveau_channel *evo = dev_priv->evo;
179 struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
180 struct drm_device *dev = encoder->dev;
181 struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
182 uint32_t mode_ctl = 0;
183 int ret;
184
185 NV_DEBUG(dev, "or %d\n", nv_encoder->or);
186
187 nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON);
188
189 switch (nv_encoder->dcb->type) {
190 case OUTPUT_TMDS:
191 if (nv_encoder->dcb->sorconf.link & 1) {
192 if (adjusted_mode->clock < 165000)
193 mode_ctl = 0x0100;
194 else
195 mode_ctl = 0x0500;
196 } else
197 mode_ctl = 0x0200;
198 break;
199 case OUTPUT_DP:
200 mode_ctl |= 0x00050000;
201 if (nv_encoder->dcb->sorconf.link & 1)
202 mode_ctl |= 0x00000800;
203 else
204 mode_ctl |= 0x00000900;
205 break;
206 default:
207 break;
208 }
209
210 if (crtc->index == 1)
211 mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC1;
212 else
213 mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC0;
214
215 if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
216 mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NHSYNC;
217
218 if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
219 mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC;
220
221 ret = RING_SPACE(evo, 2);
222 if (ret) {
223 NV_ERROR(dev, "no space while connecting SOR\n");
224 return;
225 }
226 BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
227 OUT_RING(evo, mode_ctl);
228}
229
230static const struct drm_encoder_helper_funcs nv50_sor_helper_funcs = {
231 .dpms = nv50_sor_dpms,
232 .save = nv50_sor_save,
233 .restore = nv50_sor_restore,
234 .mode_fixup = nv50_sor_mode_fixup,
235 .prepare = nv50_sor_prepare,
236 .commit = nv50_sor_commit,
237 .mode_set = nv50_sor_mode_set,
238 .detect = NULL
239};
240
241static void
242nv50_sor_destroy(struct drm_encoder *encoder)
243{
244 struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
245
246 if (!encoder)
247 return;
248
249 NV_DEBUG(encoder->dev, "\n");
250
251 drm_encoder_cleanup(encoder);
252
253 kfree(nv_encoder);
254}
255
256static const struct drm_encoder_funcs nv50_sor_encoder_funcs = {
257 .destroy = nv50_sor_destroy,
258};
259
260int
261nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry)
262{
263 struct nouveau_encoder *nv_encoder = NULL;
264 struct drm_encoder *encoder;
265 bool dum;
266 int type;
267
268 NV_DEBUG(dev, "\n");
269
270 switch (entry->type) {
271 case OUTPUT_TMDS:
272 NV_INFO(dev, "Detected a TMDS output\n");
273 type = DRM_MODE_ENCODER_TMDS;
274 break;
275 case OUTPUT_LVDS:
276 NV_INFO(dev, "Detected a LVDS output\n");
277 type = DRM_MODE_ENCODER_LVDS;
278
279 if (nouveau_bios_parse_lvds_table(dev, 0, &dum, &dum)) {
280 NV_ERROR(dev, "Failed parsing LVDS table\n");
281 return -EINVAL;
282 }
283 break;
284 case OUTPUT_DP:
285 NV_INFO(dev, "Detected a DP output\n");
286 type = DRM_MODE_ENCODER_TMDS;
287 break;
288 default:
289 return -EINVAL;
290 }
291
292 nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
293 if (!nv_encoder)
294 return -ENOMEM;
295 encoder = to_drm_encoder(nv_encoder);
296
297 nv_encoder->dcb = entry;
298 nv_encoder->or = ffs(entry->or) - 1;
299
300 nv_encoder->disconnect = nv50_sor_disconnect;
301
302 drm_encoder_init(dev, encoder, &nv50_sor_encoder_funcs, type);
303 drm_encoder_helper_add(encoder, &nv50_sor_helper_funcs);
304
305 encoder->possible_crtcs = entry->heads;
306 encoder->possible_clones = 0;
307
308 return 0;
309}