Commit | Line | Data |
---|---|---|
6ee73861 BS |
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 | ||
126b5440 | 30 | void |
6ee73861 BS |
31 | nv04_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)) { | |
846975a9 | 42 | nouveau_fbcon_gpu_lockup(info); |
6ee73861 BS |
43 | } |
44 | ||
45 | if (info->flags & FBINFO_HWACCEL_DISABLED) { | |
46 | cfb_copyarea(info, region); | |
47 | return; | |
48 | } | |
49 | ||
50 | BEGIN_RING(chan, NvSubImageBlit, 0x0300, 3); | |
51 | OUT_RING(chan, (region->sy << 16) | region->sx); | |
52 | OUT_RING(chan, (region->dy << 16) | region->dx); | |
53 | OUT_RING(chan, (region->height << 16) | region->width); | |
54 | FIRE_RING(chan); | |
55 | } | |
56 | ||
126b5440 | 57 | void |
6ee73861 BS |
58 | nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect) |
59 | { | |
60 | struct nouveau_fbcon_par *par = info->par; | |
61 | struct drm_device *dev = par->dev; | |
62 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
63 | struct nouveau_channel *chan = dev_priv->channel; | |
6ee73861 BS |
64 | |
65 | if (info->state != FBINFO_STATE_RUNNING) | |
66 | return; | |
67 | ||
68 | if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 7)) { | |
846975a9 | 69 | nouveau_fbcon_gpu_lockup(info); |
6ee73861 BS |
70 | } |
71 | ||
72 | if (info->flags & FBINFO_HWACCEL_DISABLED) { | |
73 | cfb_fillrect(info, rect); | |
74 | return; | |
75 | } | |
76 | ||
77 | BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1); | |
78 | OUT_RING(chan, (rect->rop != ROP_COPY) ? 1 : 3); | |
79 | BEGIN_RING(chan, NvSubGdiRect, 0x03fc, 1); | |
bf5302b9 BS |
80 | if (info->fix.visual == FB_VISUAL_TRUECOLOR || |
81 | info->fix.visual == FB_VISUAL_DIRECTCOLOR) | |
82 | OUT_RING(chan, ((uint32_t *)info->pseudo_palette)[rect->color]); | |
83 | else | |
84 | OUT_RING(chan, rect->color); | |
6ee73861 BS |
85 | BEGIN_RING(chan, NvSubGdiRect, 0x0400, 2); |
86 | OUT_RING(chan, (rect->dx << 16) | rect->dy); | |
87 | OUT_RING(chan, (rect->width << 16) | rect->height); | |
88 | FIRE_RING(chan); | |
89 | } | |
90 | ||
126b5440 | 91 | void |
6ee73861 BS |
92 | nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) |
93 | { | |
94 | struct nouveau_fbcon_par *par = info->par; | |
95 | struct drm_device *dev = par->dev; | |
96 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
97 | struct nouveau_channel *chan = dev_priv->channel; | |
98 | uint32_t fg; | |
99 | uint32_t bg; | |
100 | uint32_t dsize; | |
101 | uint32_t width; | |
102 | uint32_t *data = (uint32_t *)image->data; | |
103 | ||
104 | if (info->state != FBINFO_STATE_RUNNING) | |
105 | return; | |
106 | ||
107 | if (image->depth != 1) { | |
108 | cfb_imageblit(info, image); | |
109 | return; | |
110 | } | |
111 | ||
112 | if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 8)) { | |
846975a9 | 113 | nouveau_fbcon_gpu_lockup(info); |
6ee73861 BS |
114 | } |
115 | ||
116 | if (info->flags & FBINFO_HWACCEL_DISABLED) { | |
117 | cfb_imageblit(info, image); | |
118 | return; | |
119 | } | |
120 | ||
3bfc7d22 | 121 | width = ALIGN(image->width, 32); |
6ee73861 BS |
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)) { | |
846975a9 | 147 | nouveau_fbcon_gpu_lockup(info); |
6ee73861 BS |
148 | cfb_imageblit(info, image); |
149 | return; | |
150 | } | |
151 | ||
152 | BEGIN_RING(chan, NvSubGdiRect, 0x0c00, iter_len); | |
153 | OUT_RINGp(chan, data, iter_len); | |
154 | data += iter_len; | |
155 | dsize -= iter_len; | |
156 | } | |
157 | ||
158 | FIRE_RING(chan); | |
159 | } | |
160 | ||
161 | static int | |
162 | nv04_fbcon_grobj_new(struct drm_device *dev, int class, uint32_t handle) | |
163 | { | |
164 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
165 | struct nouveau_gpuobj *obj = NULL; | |
166 | int ret; | |
167 | ||
168 | ret = nouveau_gpuobj_gr_new(dev_priv->channel, class, &obj); | |
169 | if (ret) | |
170 | return ret; | |
171 | ||
172 | ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, handle, obj, NULL); | |
173 | if (ret) | |
174 | return ret; | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
179 | int | |
180 | nv04_fbcon_accel_init(struct fb_info *info) | |
181 | { | |
182 | struct nouveau_fbcon_par *par = info->par; | |
183 | struct drm_device *dev = par->dev; | |
184 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
185 | struct nouveau_channel *chan = dev_priv->channel; | |
f03a314b | 186 | const int sub = NvSubCtxSurf2D; |
6ee73861 BS |
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)) { | |
846975a9 | 245 | nouveau_fbcon_gpu_lockup(info); |
6ee73861 BS |
246 | return 0; |
247 | } | |
248 | ||
f03a314b | 249 | BEGIN_RING(chan, sub, 0x0000, 1); |
6ee73861 | 250 | OUT_RING(chan, NvCtxSurf2D); |
f03a314b | 251 | BEGIN_RING(chan, sub, 0x0184, 2); |
6ee73861 BS |
252 | OUT_RING(chan, NvDmaFB); |
253 | OUT_RING(chan, NvDmaFB); | |
f03a314b | 254 | BEGIN_RING(chan, sub, 0x0300, 4); |
6ee73861 BS |
255 | OUT_RING(chan, surface_fmt); |
256 | OUT_RING(chan, info->fix.line_length | (info->fix.line_length << 16)); | |
257 | OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base); | |
258 | OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base); | |
259 | ||
f03a314b | 260 | BEGIN_RING(chan, sub, 0x0000, 1); |
6ee73861 | 261 | OUT_RING(chan, NvRop); |
f03a314b | 262 | BEGIN_RING(chan, sub, 0x0300, 1); |
6ee73861 BS |
263 | OUT_RING(chan, 0x55); |
264 | ||
f03a314b | 265 | BEGIN_RING(chan, sub, 0x0000, 1); |
6ee73861 | 266 | OUT_RING(chan, NvImagePatt); |
f03a314b | 267 | BEGIN_RING(chan, sub, 0x0300, 8); |
6ee73861 BS |
268 | OUT_RING(chan, pattern_fmt); |
269 | #ifdef __BIG_ENDIAN | |
270 | OUT_RING(chan, 2); | |
271 | #else | |
272 | OUT_RING(chan, 1); | |
273 | #endif | |
274 | OUT_RING(chan, 0); | |
275 | OUT_RING(chan, 1); | |
276 | OUT_RING(chan, ~0); | |
277 | OUT_RING(chan, ~0); | |
278 | OUT_RING(chan, ~0); | |
279 | OUT_RING(chan, ~0); | |
280 | ||
f03a314b | 281 | BEGIN_RING(chan, sub, 0x0000, 1); |
6ee73861 | 282 | OUT_RING(chan, NvClipRect); |
f03a314b | 283 | BEGIN_RING(chan, sub, 0x0300, 2); |
6ee73861 BS |
284 | OUT_RING(chan, 0); |
285 | OUT_RING(chan, (info->var.yres_virtual << 16) | info->var.xres_virtual); | |
286 | ||
287 | BEGIN_RING(chan, NvSubImageBlit, 0x0000, 1); | |
288 | OUT_RING(chan, NvImageBlit); | |
289 | BEGIN_RING(chan, NvSubImageBlit, 0x019c, 1); | |
290 | OUT_RING(chan, NvCtxSurf2D); | |
291 | BEGIN_RING(chan, NvSubImageBlit, 0x02fc, 1); | |
292 | OUT_RING(chan, 3); | |
293 | ||
294 | BEGIN_RING(chan, NvSubGdiRect, 0x0000, 1); | |
295 | OUT_RING(chan, NvGdiRect); | |
296 | BEGIN_RING(chan, NvSubGdiRect, 0x0198, 1); | |
297 | OUT_RING(chan, NvCtxSurf2D); | |
298 | BEGIN_RING(chan, NvSubGdiRect, 0x0188, 2); | |
299 | OUT_RING(chan, NvImagePatt); | |
300 | OUT_RING(chan, NvRop); | |
301 | BEGIN_RING(chan, NvSubGdiRect, 0x0304, 1); | |
302 | OUT_RING(chan, 1); | |
303 | BEGIN_RING(chan, NvSubGdiRect, 0x0300, 1); | |
304 | OUT_RING(chan, rect_fmt); | |
305 | BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1); | |
306 | OUT_RING(chan, 3); | |
307 | ||
308 | FIRE_RING(chan); | |
309 | ||
6ee73861 BS |
310 | return 0; |
311 | } | |
312 |