aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nv04_display.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/nv04_display.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/nv04_display.c')
-rw-r--r--drivers/gpu/drm/nouveau/nv04_display.c288
1 files changed, 288 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c
new file mode 100644
index 000000000000..b47c757ff48b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_display.c
@@ -0,0 +1,288 @@
1/*
2 * Copyright 2009 Red Hat Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Author: Ben Skeggs
23 */
24
25#include "drmP.h"
26#include "drm.h"
27#include "drm_crtc_helper.h"
28
29#include "nouveau_drv.h"
30#include "nouveau_fb.h"
31#include "nouveau_hw.h"
32#include "nouveau_encoder.h"
33#include "nouveau_connector.h"
34
35#define MULTIPLE_ENCODERS(e) (e & (e - 1))
36
37static void
38nv04_display_store_initial_head_owner(struct drm_device *dev)
39{
40 struct drm_nouveau_private *dev_priv = dev->dev_private;
41
42 if (dev_priv->chipset != 0x11) {
43 dev_priv->crtc_owner = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44);
44 goto ownerknown;
45 }
46
47 /* reading CR44 is broken on nv11, so we attempt to infer it */
48 if (nvReadMC(dev, NV_PBUS_DEBUG_1) & (1 << 28)) /* heads tied, restore both */
49 dev_priv->crtc_owner = 0x4;
50 else {
51 uint8_t slaved_on_A, slaved_on_B;
52 bool tvA = false;
53 bool tvB = false;
54
55 NVLockVgaCrtcs(dev, false);
56
57 slaved_on_B = NVReadVgaCrtc(dev, 1, NV_CIO_CRE_PIXEL_INDEX) &
58 0x80;
59 if (slaved_on_B)
60 tvB = !(NVReadVgaCrtc(dev, 1, NV_CIO_CRE_LCD__INDEX) &
61 MASK(NV_CIO_CRE_LCD_LCD_SELECT));
62
63 slaved_on_A = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX) &
64 0x80;
65 if (slaved_on_A)
66 tvA = !(NVReadVgaCrtc(dev, 0, NV_CIO_CRE_LCD__INDEX) &
67 MASK(NV_CIO_CRE_LCD_LCD_SELECT));
68
69 NVLockVgaCrtcs(dev, true);
70
71 if (slaved_on_A && !tvA)
72 dev_priv->crtc_owner = 0x0;
73 else if (slaved_on_B && !tvB)
74 dev_priv->crtc_owner = 0x3;
75 else if (slaved_on_A)
76 dev_priv->crtc_owner = 0x0;
77 else if (slaved_on_B)
78 dev_priv->crtc_owner = 0x3;
79 else
80 dev_priv->crtc_owner = 0x0;
81 }
82
83ownerknown:
84 NV_INFO(dev, "Initial CRTC_OWNER is %d\n", dev_priv->crtc_owner);
85
86 /* we need to ensure the heads are not tied henceforth, or reading any
87 * 8 bit reg on head B will fail
88 * setting a single arbitrary head solves that */
89 NVSetOwner(dev, 0);
90}
91
92int
93nv04_display_create(struct drm_device *dev)
94{
95 struct drm_nouveau_private *dev_priv = dev->dev_private;
96 struct parsed_dcb *dcb = dev_priv->vbios->dcb;
97 struct drm_encoder *encoder;
98 struct drm_crtc *crtc;
99 uint16_t connector[16] = { 0 };
100 int i, ret;
101
102 NV_DEBUG(dev, "\n");
103
104 if (nv_two_heads(dev))
105 nv04_display_store_initial_head_owner(dev);
106
107 drm_mode_config_init(dev);
108 drm_mode_create_scaling_mode_property(dev);
109 drm_mode_create_dithering_property(dev);
110
111 dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
112
113 dev->mode_config.min_width = 0;
114 dev->mode_config.min_height = 0;
115 switch (dev_priv->card_type) {
116 case NV_04:
117 dev->mode_config.max_width = 2048;
118 dev->mode_config.max_height = 2048;
119 break;
120 default:
121 dev->mode_config.max_width = 4096;
122 dev->mode_config.max_height = 4096;
123 break;
124 }
125
126 dev->mode_config.fb_base = dev_priv->fb_phys;
127
128 nv04_crtc_create(dev, 0);
129 if (nv_two_heads(dev))
130 nv04_crtc_create(dev, 1);
131
132 for (i = 0; i < dcb->entries; i++) {
133 struct dcb_entry *dcbent = &dcb->entry[i];
134
135 switch (dcbent->type) {
136 case OUTPUT_ANALOG:
137 ret = nv04_dac_create(dev, dcbent);
138 break;
139 case OUTPUT_LVDS:
140 case OUTPUT_TMDS:
141 ret = nv04_dfp_create(dev, dcbent);
142 break;
143 case OUTPUT_TV:
144 if (dcbent->location == DCB_LOC_ON_CHIP)
145 ret = nv17_tv_create(dev, dcbent);
146 else
147 ret = nv04_tv_create(dev, dcbent);
148 break;
149 default:
150 NV_WARN(dev, "DCB type %d not known\n", dcbent->type);
151 continue;
152 }
153
154 if (ret)
155 continue;
156
157 connector[dcbent->connector] |= (1 << dcbent->type);
158 }
159
160 for (i = 0; i < dcb->entries; i++) {
161 struct dcb_entry *dcbent = &dcb->entry[i];
162 uint16_t encoders;
163 int type;
164
165 encoders = connector[dcbent->connector];
166 if (!(encoders & (1 << dcbent->type)))
167 continue;
168 connector[dcbent->connector] = 0;
169
170 switch (dcbent->type) {
171 case OUTPUT_ANALOG:
172 if (!MULTIPLE_ENCODERS(encoders))
173 type = DRM_MODE_CONNECTOR_VGA;
174 else
175 type = DRM_MODE_CONNECTOR_DVII;
176 break;
177 case OUTPUT_TMDS:
178 if (!MULTIPLE_ENCODERS(encoders))
179 type = DRM_MODE_CONNECTOR_DVID;
180 else
181 type = DRM_MODE_CONNECTOR_DVII;
182 break;
183 case OUTPUT_LVDS:
184 type = DRM_MODE_CONNECTOR_LVDS;
185#if 0
186 /* don't create i2c adapter when lvds ddc not allowed */
187 if (dcbent->lvdsconf.use_straps_for_mode ||
188 dev_priv->vbios->fp_no_ddc)
189 i2c_index = 0xf;
190#endif
191 break;
192 case OUTPUT_TV:
193 type = DRM_MODE_CONNECTOR_TV;
194 break;
195 default:
196 type = DRM_MODE_CONNECTOR_Unknown;
197 continue;
198 }
199
200 nouveau_connector_create(dev, dcbent->connector, type);
201 }
202
203 /* Save previous state */
204 NVLockVgaCrtcs(dev, false);
205
206 nouveau_hw_save_vga_fonts(dev, 1);
207
208 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
209 crtc->funcs->save(crtc);
210
211 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
212 struct drm_encoder_helper_funcs *func = encoder->helper_private;
213
214 func->save(encoder);
215 }
216
217 return 0;
218}
219
220void
221nv04_display_destroy(struct drm_device *dev)
222{
223 struct drm_encoder *encoder;
224 struct drm_crtc *crtc;
225
226 NV_DEBUG(dev, "\n");
227
228 /* Turn every CRTC off. */
229 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
230 struct drm_mode_set modeset = {
231 .crtc = crtc,
232 };
233
234 crtc->funcs->set_config(&modeset);
235 }
236
237 /* Restore state */
238 NVLockVgaCrtcs(dev, false);
239
240 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
241 struct drm_encoder_helper_funcs *func = encoder->helper_private;
242
243 func->restore(encoder);
244 }
245
246 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
247 crtc->funcs->restore(crtc);
248
249 nouveau_hw_save_vga_fonts(dev, 0);
250
251 drm_mode_config_cleanup(dev);
252}
253
254void
255nv04_display_restore(struct drm_device *dev)
256{
257 struct drm_nouveau_private *dev_priv = dev->dev_private;
258 struct drm_encoder *encoder;
259 struct drm_crtc *crtc;
260
261 NVLockVgaCrtcs(dev, false);
262
263 /* meh.. modeset apparently doesn't setup all the regs and depends
264 * on pre-existing state, for now load the state of the card *before*
265 * nouveau was loaded, and then do a modeset.
266 *
267 * best thing to do probably is to make save/restore routines not
268 * save/restore "pre-load" state, but more general so we can save
269 * on suspend too.
270 */
271 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
272 struct drm_encoder_helper_funcs *func = encoder->helper_private;
273
274 func->restore(encoder);
275 }
276
277 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
278 crtc->funcs->restore(crtc);
279
280 if (nv_two_heads(dev)) {
281 NV_INFO(dev, "Restoring CRTC_OWNER to %d.\n",
282 dev_priv->crtc_owner);
283 NVSetOwner(dev, dev_priv->crtc_owner);
284 }
285
286 NVLockVgaCrtcs(dev, true);
287}
288