aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nv04_fbcon.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_fbcon.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_fbcon.c')
-rw-r--r--drivers/gpu/drm/nouveau/nv04_fbcon.c316
1 files changed, 316 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c
new file mode 100644
index 000000000000..09a31071ee58
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c
@@ -0,0 +1,316 @@
1/*
2 * Copyright 2009 Ben Skeggs
3 * Copyright 2008 Stuart Bennett
4 *
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:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25#include "drmP.h"
26#include "nouveau_drv.h"
27#include "nouveau_dma.h"
28#include "nouveau_fbcon.h"
29
30static void
31nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
32{
33 struct nouveau_fbcon_par *par = info->par;
34 struct drm_device *dev = par->dev;
35 struct drm_nouveau_private *dev_priv = dev->dev_private;
36 struct nouveau_channel *chan = dev_priv->channel;
37
38 if (info->state != FBINFO_STATE_RUNNING)
39 return;
40
41 if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 4)) {
42 NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
43 info->flags |= FBINFO_HWACCEL_DISABLED;
44 }
45
46 if (info->flags & FBINFO_HWACCEL_DISABLED) {
47 cfb_copyarea(info, region);
48 return;
49 }
50
51 BEGIN_RING(chan, NvSubImageBlit, 0x0300, 3);
52 OUT_RING(chan, (region->sy << 16) | region->sx);
53 OUT_RING(chan, (region->dy << 16) | region->dx);
54 OUT_RING(chan, (region->height << 16) | region->width);
55 FIRE_RING(chan);
56}
57
58static void
59nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
60{
61 struct nouveau_fbcon_par *par = info->par;
62 struct drm_device *dev = par->dev;
63 struct drm_nouveau_private *dev_priv = dev->dev_private;
64 struct nouveau_channel *chan = dev_priv->channel;
65 uint32_t color = ((uint32_t *) info->pseudo_palette)[rect->color];
66
67 if (info->state != FBINFO_STATE_RUNNING)
68 return;
69
70 if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 7)) {
71 NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
72 info->flags |= FBINFO_HWACCEL_DISABLED;
73 }
74
75 if (info->flags & FBINFO_HWACCEL_DISABLED) {
76 cfb_fillrect(info, rect);
77 return;
78 }
79
80 BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1);
81 OUT_RING(chan, (rect->rop != ROP_COPY) ? 1 : 3);
82 BEGIN_RING(chan, NvSubGdiRect, 0x03fc, 1);
83 OUT_RING(chan, color);
84 BEGIN_RING(chan, NvSubGdiRect, 0x0400, 2);
85 OUT_RING(chan, (rect->dx << 16) | rect->dy);
86 OUT_RING(chan, (rect->width << 16) | rect->height);
87 FIRE_RING(chan);
88}
89
90static void
91nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
92{
93 struct nouveau_fbcon_par *par = info->par;
94 struct drm_device *dev = par->dev;
95 struct drm_nouveau_private *dev_priv = dev->dev_private;
96 struct nouveau_channel *chan = dev_priv->channel;
97 uint32_t fg;
98 uint32_t bg;
99 uint32_t dsize;
100 uint32_t width;
101 uint32_t *data = (uint32_t *)image->data;
102
103 if (info->state != FBINFO_STATE_RUNNING)
104 return;
105
106 if (image->depth != 1) {
107 cfb_imageblit(info, image);
108 return;
109 }
110
111 if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 8)) {
112 NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
113 info->flags |= FBINFO_HWACCEL_DISABLED;
114 }
115
116 if (info->flags & FBINFO_HWACCEL_DISABLED) {
117 cfb_imageblit(info, image);
118 return;
119 }
120
121 width = (image->width + 31) & ~31;
122 dsize = (width * image->height) >> 5;
123
124 if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
125 info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
126 fg = ((uint32_t *) info->pseudo_palette)[image->fg_color];
127 bg = ((uint32_t *) info->pseudo_palette)[image->bg_color];
128 } else {
129 fg = image->fg_color;
130 bg = image->bg_color;
131 }
132
133 BEGIN_RING(chan, NvSubGdiRect, 0x0be4, 7);
134 OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff));
135 OUT_RING(chan, ((image->dy + image->height) << 16) |
136 ((image->dx + image->width) & 0xffff));
137 OUT_RING(chan, bg);
138 OUT_RING(chan, fg);
139 OUT_RING(chan, (image->height << 16) | image->width);
140 OUT_RING(chan, (image->height << 16) | width);
141 OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff));
142
143 while (dsize) {
144 int iter_len = dsize > 128 ? 128 : dsize;
145
146 if (RING_SPACE(chan, iter_len + 1)) {
147 NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
148 info->flags |= FBINFO_HWACCEL_DISABLED;
149 cfb_imageblit(info, image);
150 return;
151 }
152
153 BEGIN_RING(chan, NvSubGdiRect, 0x0c00, iter_len);
154 OUT_RINGp(chan, data, iter_len);
155 data += iter_len;
156 dsize -= iter_len;
157 }
158
159 FIRE_RING(chan);
160}
161
162static int
163nv04_fbcon_grobj_new(struct drm_device *dev, int class, uint32_t handle)
164{
165 struct drm_nouveau_private *dev_priv = dev->dev_private;
166 struct nouveau_gpuobj *obj = NULL;
167 int ret;
168
169 ret = nouveau_gpuobj_gr_new(dev_priv->channel, class, &obj);
170 if (ret)
171 return ret;
172
173 ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, handle, obj, NULL);
174 if (ret)
175 return ret;
176
177 return 0;
178}
179
180int
181nv04_fbcon_accel_init(struct fb_info *info)
182{
183 struct nouveau_fbcon_par *par = info->par;
184 struct drm_device *dev = par->dev;
185 struct drm_nouveau_private *dev_priv = dev->dev_private;
186 struct nouveau_channel *chan = dev_priv->channel;
187 int surface_fmt, pattern_fmt, rect_fmt;
188 int ret;
189
190 switch (info->var.bits_per_pixel) {
191 case 8:
192 surface_fmt = 1;
193 pattern_fmt = 3;
194 rect_fmt = 3;
195 break;
196 case 16:
197 surface_fmt = 4;
198 pattern_fmt = 1;
199 rect_fmt = 1;
200 break;
201 case 32:
202 switch (info->var.transp.length) {
203 case 0: /* depth 24 */
204 case 8: /* depth 32 */
205 break;
206 default:
207 return -EINVAL;
208 }
209
210 surface_fmt = 6;
211 pattern_fmt = 3;
212 rect_fmt = 3;
213 break;
214 default:
215 return -EINVAL;
216 }
217
218 ret = nv04_fbcon_grobj_new(dev, dev_priv->card_type >= NV_10 ?
219 0x0062 : 0x0042, NvCtxSurf2D);
220 if (ret)
221 return ret;
222
223 ret = nv04_fbcon_grobj_new(dev, 0x0019, NvClipRect);
224 if (ret)
225 return ret;
226
227 ret = nv04_fbcon_grobj_new(dev, 0x0043, NvRop);
228 if (ret)
229 return ret;
230
231 ret = nv04_fbcon_grobj_new(dev, 0x0044, NvImagePatt);
232 if (ret)
233 return ret;
234
235 ret = nv04_fbcon_grobj_new(dev, 0x004a, NvGdiRect);
236 if (ret)
237 return ret;
238
239 ret = nv04_fbcon_grobj_new(dev, dev_priv->card_type >= NV_10 ?
240 0x009f : 0x005f, NvImageBlit);
241 if (ret)
242 return ret;
243
244 if (RING_SPACE(chan, 49)) {
245 NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
246 info->flags |= FBINFO_HWACCEL_DISABLED;
247 return 0;
248 }
249
250 BEGIN_RING(chan, 1, 0x0000, 1);
251 OUT_RING(chan, NvCtxSurf2D);
252 BEGIN_RING(chan, 1, 0x0184, 2);
253 OUT_RING(chan, NvDmaFB);
254 OUT_RING(chan, NvDmaFB);
255 BEGIN_RING(chan, 1, 0x0300, 4);
256 OUT_RING(chan, surface_fmt);
257 OUT_RING(chan, info->fix.line_length | (info->fix.line_length << 16));
258 OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base);
259 OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base);
260
261 BEGIN_RING(chan, 1, 0x0000, 1);
262 OUT_RING(chan, NvRop);
263 BEGIN_RING(chan, 1, 0x0300, 1);
264 OUT_RING(chan, 0x55);
265
266 BEGIN_RING(chan, 1, 0x0000, 1);
267 OUT_RING(chan, NvImagePatt);
268 BEGIN_RING(chan, 1, 0x0300, 8);
269 OUT_RING(chan, pattern_fmt);
270#ifdef __BIG_ENDIAN
271 OUT_RING(chan, 2);
272#else
273 OUT_RING(chan, 1);
274#endif
275 OUT_RING(chan, 0);
276 OUT_RING(chan, 1);
277 OUT_RING(chan, ~0);
278 OUT_RING(chan, ~0);
279 OUT_RING(chan, ~0);
280 OUT_RING(chan, ~0);
281
282 BEGIN_RING(chan, 1, 0x0000, 1);
283 OUT_RING(chan, NvClipRect);
284 BEGIN_RING(chan, 1, 0x0300, 2);
285 OUT_RING(chan, 0);
286 OUT_RING(chan, (info->var.yres_virtual << 16) | info->var.xres_virtual);
287
288 BEGIN_RING(chan, NvSubImageBlit, 0x0000, 1);
289 OUT_RING(chan, NvImageBlit);
290 BEGIN_RING(chan, NvSubImageBlit, 0x019c, 1);
291 OUT_RING(chan, NvCtxSurf2D);
292 BEGIN_RING(chan, NvSubImageBlit, 0x02fc, 1);
293 OUT_RING(chan, 3);
294
295 BEGIN_RING(chan, NvSubGdiRect, 0x0000, 1);
296 OUT_RING(chan, NvGdiRect);
297 BEGIN_RING(chan, NvSubGdiRect, 0x0198, 1);
298 OUT_RING(chan, NvCtxSurf2D);
299 BEGIN_RING(chan, NvSubGdiRect, 0x0188, 2);
300 OUT_RING(chan, NvImagePatt);
301 OUT_RING(chan, NvRop);
302 BEGIN_RING(chan, NvSubGdiRect, 0x0304, 1);
303 OUT_RING(chan, 1);
304 BEGIN_RING(chan, NvSubGdiRect, 0x0300, 1);
305 OUT_RING(chan, rect_fmt);
306 BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1);
307 OUT_RING(chan, 3);
308
309 FIRE_RING(chan);
310
311 info->fbops->fb_fillrect = nv04_fbcon_fillrect;
312 info->fbops->fb_copyarea = nv04_fbcon_copyarea;
313 info->fbops->fb_imageblit = nv04_fbcon_imageblit;
314 return 0;
315}
316