Commit | Line | Data |
---|---|---|
4d8d096e AC |
1 | /************************************************************************** |
2 | * Copyright (c) 2007-2011, Intel Corporation. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms and conditions of the GNU General Public License, | |
7 | * version 2, as published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along with | |
15 | * this program; if not, write to the Free Software Foundation, Inc., | |
16 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | |
17 | * | |
18 | * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to | |
19 | * develop this driver. | |
20 | * | |
21 | **************************************************************************/ | |
22 | ||
23 | #include <linux/module.h> | |
24 | #include <linux/kernel.h> | |
25 | #include <linux/errno.h> | |
26 | #include <linux/string.h> | |
27 | #include <linux/mm.h> | |
28 | #include <linux/tty.h> | |
29 | #include <linux/slab.h> | |
30 | #include <linux/delay.h> | |
4d8d096e AC |
31 | #include <linux/init.h> |
32 | #include <linux/console.h> | |
33 | ||
34 | #include <drm/drmP.h> | |
35 | #include <drm/drm.h> | |
36 | #include <drm/drm_crtc.h> | |
37 | ||
38 | #include "psb_drv.h" | |
39 | #include "psb_reg.h" | |
40 | #include "framebuffer.h" | |
41 | ||
42 | /** | |
43 | * psb_spank - reset the 2D engine | |
44 | * @dev_priv: our PSB DRM device | |
45 | * | |
46 | * Soft reset the graphics engine and then reload the necessary registers. | |
47 | * We use this at initialisation time but it will become relevant for | |
48 | * accelerated X later | |
49 | */ | |
50 | void psb_spank(struct drm_psb_private *dev_priv) | |
51 | { | |
52 | PSB_WSGX32(_PSB_CS_RESET_BIF_RESET | _PSB_CS_RESET_DPM_RESET | | |
53 | _PSB_CS_RESET_TA_RESET | _PSB_CS_RESET_USE_RESET | | |
54 | _PSB_CS_RESET_ISP_RESET | _PSB_CS_RESET_TSP_RESET | | |
55 | _PSB_CS_RESET_TWOD_RESET, PSB_CR_SOFT_RESET); | |
56 | PSB_RSGX32(PSB_CR_SOFT_RESET); | |
57 | ||
58 | msleep(1); | |
59 | ||
60 | PSB_WSGX32(0, PSB_CR_SOFT_RESET); | |
61 | wmb(); | |
62 | PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) | _PSB_CB_CTRL_CLEAR_FAULT, | |
63 | PSB_CR_BIF_CTRL); | |
64 | wmb(); | |
65 | (void) PSB_RSGX32(PSB_CR_BIF_CTRL); | |
66 | ||
67 | msleep(1); | |
68 | PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) & ~_PSB_CB_CTRL_CLEAR_FAULT, | |
69 | PSB_CR_BIF_CTRL); | |
70 | (void) PSB_RSGX32(PSB_CR_BIF_CTRL); | |
71 | PSB_WSGX32(dev_priv->gtt.gatt_start, PSB_CR_BIF_TWOD_REQ_BASE); | |
72 | } | |
73 | ||
74 | /** | |
75 | * psb2_2d_wait_available - wait for FIFO room | |
76 | * @dev_priv: our DRM device | |
77 | * @size: size (in dwords) of the command we want to issue | |
78 | * | |
79 | * Wait until there is room to load the FIFO with our data. If the | |
80 | * device is not responding then reset it | |
81 | */ | |
82 | static int psb_2d_wait_available(struct drm_psb_private *dev_priv, | |
83 | unsigned size) | |
84 | { | |
85 | uint32_t avail = PSB_RSGX32(PSB_CR_2D_SOCIF); | |
86 | unsigned long t = jiffies + HZ; | |
87 | ||
88 | while (avail < size) { | |
89 | avail = PSB_RSGX32(PSB_CR_2D_SOCIF); | |
90 | if (time_after(jiffies, t)) { | |
91 | psb_spank(dev_priv); | |
92 | return -EIO; | |
93 | } | |
94 | } | |
95 | return 0; | |
96 | } | |
97 | ||
98 | /** | |
99 | * psb_2d_submit - submit a 2D command | |
100 | * @dev_priv: our DRM device | |
101 | * @cmdbuf: command to issue | |
102 | * @size: length (in dwords) | |
103 | * | |
104 | * Issue one or more 2D commands to the accelerator. This needs to be | |
105 | * serialized later when we add the GEM interfaces for acceleration | |
106 | */ | |
107 | static int psbfb_2d_submit(struct drm_psb_private *dev_priv, uint32_t *cmdbuf, | |
108 | unsigned size) | |
109 | { | |
110 | int ret = 0; | |
111 | int i; | |
112 | unsigned submit_size; | |
9242fe23 | 113 | unsigned long flags; |
4d8d096e | 114 | |
9242fe23 | 115 | spin_lock_irqsave(&dev_priv->lock_2d, flags); |
4d8d096e AC |
116 | while (size > 0) { |
117 | submit_size = (size < 0x60) ? size : 0x60; | |
118 | size -= submit_size; | |
119 | ret = psb_2d_wait_available(dev_priv, submit_size); | |
120 | if (ret) | |
cd009355 | 121 | break; |
4d8d096e AC |
122 | |
123 | submit_size <<= 2; | |
124 | ||
125 | for (i = 0; i < submit_size; i += 4) | |
126 | PSB_WSGX32(*cmdbuf++, PSB_SGX_2D_SLAVE_PORT + i); | |
127 | ||
128 | (void)PSB_RSGX32(PSB_SGX_2D_SLAVE_PORT + i - 4); | |
129 | } | |
9242fe23 | 130 | spin_unlock_irqrestore(&dev_priv->lock_2d, flags); |
4d8d096e AC |
131 | return ret; |
132 | } | |
133 | ||
134 | ||
135 | /** | |
136 | * psb_accel_2d_copy_direction - compute blit order | |
137 | * @xdir: X direction of move | |
138 | * @ydir: Y direction of move | |
139 | * | |
140 | * Compute the correct order setings to ensure that an overlapping blit | |
141 | * correctly copies all the pixels. | |
142 | */ | |
143 | static u32 psb_accel_2d_copy_direction(int xdir, int ydir) | |
144 | { | |
145 | if (xdir < 0) | |
146 | return (ydir < 0) ? PSB_2D_COPYORDER_BR2TL : | |
147 | PSB_2D_COPYORDER_TR2BL; | |
148 | else | |
149 | return (ydir < 0) ? PSB_2D_COPYORDER_BL2TR : | |
150 | PSB_2D_COPYORDER_TL2BR; | |
151 | } | |
152 | ||
153 | /** | |
154 | * psb_accel_2d_copy - accelerated 2D copy | |
155 | * @dev_priv: our DRM device | |
156 | * @src_offset in bytes | |
157 | * @src_stride in bytes | |
158 | * @src_format psb 2D format defines | |
159 | * @dst_offset in bytes | |
160 | * @dst_stride in bytes | |
161 | * @dst_format psb 2D format defines | |
162 | * @src_x offset in pixels | |
163 | * @src_y offset in pixels | |
164 | * @dst_x offset in pixels | |
165 | * @dst_y offset in pixels | |
166 | * @size_x of the copied area | |
167 | * @size_y of the copied area | |
168 | * | |
169 | * Format and issue a 2D accelerated copy command. | |
170 | */ | |
171 | static int psb_accel_2d_copy(struct drm_psb_private *dev_priv, | |
172 | uint32_t src_offset, uint32_t src_stride, | |
173 | uint32_t src_format, uint32_t dst_offset, | |
174 | uint32_t dst_stride, uint32_t dst_format, | |
175 | uint16_t src_x, uint16_t src_y, | |
176 | uint16_t dst_x, uint16_t dst_y, | |
177 | uint16_t size_x, uint16_t size_y) | |
178 | { | |
179 | uint32_t blit_cmd; | |
180 | uint32_t buffer[10]; | |
181 | uint32_t *buf; | |
182 | uint32_t direction; | |
183 | ||
184 | buf = buffer; | |
185 | ||
186 | direction = | |
187 | psb_accel_2d_copy_direction(src_x - dst_x, src_y - dst_y); | |
188 | ||
189 | if (direction == PSB_2D_COPYORDER_BR2TL || | |
190 | direction == PSB_2D_COPYORDER_TR2BL) { | |
191 | src_x += size_x - 1; | |
192 | dst_x += size_x - 1; | |
193 | } | |
194 | if (direction == PSB_2D_COPYORDER_BR2TL || | |
195 | direction == PSB_2D_COPYORDER_BL2TR) { | |
196 | src_y += size_y - 1; | |
197 | dst_y += size_y - 1; | |
198 | } | |
199 | ||
200 | blit_cmd = | |
201 | PSB_2D_BLIT_BH | | |
202 | PSB_2D_ROT_NONE | | |
203 | PSB_2D_DSTCK_DISABLE | | |
204 | PSB_2D_SRCCK_DISABLE | | |
205 | PSB_2D_USE_PAT | PSB_2D_ROP3_SRCCOPY | direction; | |
206 | ||
207 | *buf++ = PSB_2D_FENCE_BH; | |
208 | *buf++ = | |
209 | PSB_2D_DST_SURF_BH | dst_format | (dst_stride << | |
210 | PSB_2D_DST_STRIDE_SHIFT); | |
211 | *buf++ = dst_offset; | |
212 | *buf++ = | |
213 | PSB_2D_SRC_SURF_BH | src_format | (src_stride << | |
214 | PSB_2D_SRC_STRIDE_SHIFT); | |
215 | *buf++ = src_offset; | |
216 | *buf++ = | |
217 | PSB_2D_SRC_OFF_BH | (src_x << PSB_2D_SRCOFF_XSTART_SHIFT) | | |
218 | (src_y << PSB_2D_SRCOFF_YSTART_SHIFT); | |
219 | *buf++ = blit_cmd; | |
220 | *buf++ = | |
221 | (dst_x << PSB_2D_DST_XSTART_SHIFT) | (dst_y << | |
222 | PSB_2D_DST_YSTART_SHIFT); | |
223 | *buf++ = | |
224 | (size_x << PSB_2D_DST_XSIZE_SHIFT) | (size_y << | |
225 | PSB_2D_DST_YSIZE_SHIFT); | |
226 | *buf++ = PSB_2D_FLUSH_BH; | |
227 | ||
228 | return psbfb_2d_submit(dev_priv, buffer, buf - buffer); | |
229 | } | |
230 | ||
231 | /** | |
232 | * psbfb_copyarea_accel - copyarea acceleration for /dev/fb | |
233 | * @info: our framebuffer | |
234 | * @a: copyarea parameters from the framebuffer core | |
235 | * | |
236 | * Perform a 2D copy via the accelerator | |
237 | */ | |
238 | static void psbfb_copyarea_accel(struct fb_info *info, | |
239 | const struct fb_copyarea *a) | |
240 | { | |
241 | struct psb_fbdev *fbdev = info->par; | |
242 | struct psb_framebuffer *psbfb = &fbdev->pfb; | |
243 | struct drm_device *dev = psbfb->base.dev; | |
244 | struct drm_framebuffer *fb = fbdev->psb_fb_helper.fb; | |
245 | struct drm_psb_private *dev_priv = dev->dev_private; | |
246 | uint32_t offset; | |
247 | uint32_t stride; | |
248 | uint32_t src_format; | |
249 | uint32_t dst_format; | |
250 | ||
251 | if (!fb) | |
252 | return; | |
253 | ||
254 | offset = psbfb->gtt->offset; | |
01f2c773 | 255 | stride = fb->pitches[0]; |
4d8d096e AC |
256 | |
257 | switch (fb->depth) { | |
258 | case 8: | |
259 | src_format = PSB_2D_SRC_332RGB; | |
260 | dst_format = PSB_2D_DST_332RGB; | |
261 | break; | |
262 | case 15: | |
263 | src_format = PSB_2D_SRC_555RGB; | |
264 | dst_format = PSB_2D_DST_555RGB; | |
265 | break; | |
266 | case 16: | |
267 | src_format = PSB_2D_SRC_565RGB; | |
268 | dst_format = PSB_2D_DST_565RGB; | |
269 | break; | |
270 | case 24: | |
271 | case 32: | |
272 | /* this is wrong but since we don't do blending its okay */ | |
273 | src_format = PSB_2D_SRC_8888ARGB; | |
274 | dst_format = PSB_2D_DST_8888ARGB; | |
275 | break; | |
276 | default: | |
277 | /* software fallback */ | |
546187c8 | 278 | drm_fb_helper_cfb_copyarea(info, a); |
4d8d096e AC |
279 | return; |
280 | } | |
281 | ||
282 | if (!gma_power_begin(dev, false)) { | |
546187c8 | 283 | drm_fb_helper_cfb_copyarea(info, a); |
4d8d096e AC |
284 | return; |
285 | } | |
286 | psb_accel_2d_copy(dev_priv, | |
287 | offset, stride, src_format, | |
288 | offset, stride, dst_format, | |
289 | a->sx, a->sy, a->dx, a->dy, a->width, a->height); | |
290 | gma_power_end(dev); | |
291 | } | |
292 | ||
293 | /** | |
294 | * psbfb_copyarea - 2D copy interface | |
295 | * @info: our framebuffer | |
296 | * @region: region to copy | |
297 | * | |
298 | * Copy an area of the framebuffer console either by the accelerator | |
299 | * or directly using the cfb helpers according to the request | |
300 | */ | |
301 | void psbfb_copyarea(struct fb_info *info, | |
302 | const struct fb_copyarea *region) | |
303 | { | |
304 | if (unlikely(info->state != FBINFO_STATE_RUNNING)) | |
305 | return; | |
306 | ||
307 | /* Avoid the 8 pixel erratum */ | |
308 | if (region->width == 8 || region->height == 8 || | |
309 | (info->flags & FBINFO_HWACCEL_DISABLED)) | |
546187c8 | 310 | return drm_fb_helper_cfb_copyarea(info, region); |
4d8d096e AC |
311 | |
312 | psbfb_copyarea_accel(info, region); | |
313 | } | |
314 | ||
315 | /** | |
316 | * psbfb_sync - synchronize 2D | |
317 | * @info: our framebuffer | |
318 | * | |
319 | * Wait for the 2D engine to quiesce so that we can do CPU | |
320 | * access to the framebuffer again | |
321 | */ | |
322 | int psbfb_sync(struct fb_info *info) | |
323 | { | |
324 | struct psb_fbdev *fbdev = info->par; | |
325 | struct psb_framebuffer *psbfb = &fbdev->pfb; | |
326 | struct drm_device *dev = psbfb->base.dev; | |
327 | struct drm_psb_private *dev_priv = dev->dev_private; | |
bfd8303a | 328 | unsigned long _end = jiffies + HZ; |
4d8d096e | 329 | int busy = 0; |
9242fe23 | 330 | unsigned long flags; |
4d8d096e | 331 | |
9242fe23 | 332 | spin_lock_irqsave(&dev_priv->lock_2d, flags); |
4d8d096e AC |
333 | /* |
334 | * First idle the 2D engine. | |
335 | */ | |
336 | ||
337 | if ((PSB_RSGX32(PSB_CR_2D_SOCIF) == _PSB_C2_SOCIF_EMPTY) && | |
338 | ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & _PSB_C2B_STATUS_BUSY) == 0)) | |
339 | goto out; | |
340 | ||
341 | do { | |
342 | busy = (PSB_RSGX32(PSB_CR_2D_SOCIF) != _PSB_C2_SOCIF_EMPTY); | |
343 | cpu_relax(); | |
344 | } while (busy && !time_after_eq(jiffies, _end)); | |
345 | ||
346 | if (busy) | |
347 | busy = (PSB_RSGX32(PSB_CR_2D_SOCIF) != _PSB_C2_SOCIF_EMPTY); | |
348 | if (busy) | |
349 | goto out; | |
350 | ||
351 | do { | |
352 | busy = ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & | |
353 | _PSB_C2B_STATUS_BUSY) != 0); | |
354 | cpu_relax(); | |
355 | } while (busy && !time_after_eq(jiffies, _end)); | |
356 | if (busy) | |
357 | busy = ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & | |
358 | _PSB_C2B_STATUS_BUSY) != 0); | |
359 | ||
360 | out: | |
9242fe23 | 361 | spin_unlock_irqrestore(&dev_priv->lock_2d, flags); |
4d8d096e AC |
362 | return (busy) ? -EBUSY : 0; |
363 | } |