• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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>
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  */
psb_spank(struct drm_psb_private * dev_priv)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  */
psb_2d_wait_available(struct drm_psb_private * dev_priv,unsigned size)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  */
psbfb_2d_submit(struct drm_psb_private * dev_priv,uint32_t * cmdbuf,unsigned size)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;
113 	unsigned long flags;
114 
115 	spin_lock_irqsave(&dev_priv->lock_2d, flags);
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)
121 			break;
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 	}
130 	spin_unlock_irqrestore(&dev_priv->lock_2d, flags);
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  */
psb_accel_2d_copy_direction(int xdir,int ydir)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  */
psb_accel_2d_copy(struct drm_psb_private * dev_priv,uint32_t src_offset,uint32_t src_stride,uint32_t src_format,uint32_t dst_offset,uint32_t dst_stride,uint32_t dst_format,uint16_t src_x,uint16_t src_y,uint16_t dst_x,uint16_t dst_y,uint16_t size_x,uint16_t size_y)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  */
psbfb_copyarea_accel(struct fb_info * info,const struct fb_copyarea * a)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;
255 	stride = fb->pitches[0];
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 */
278 		drm_fb_helper_cfb_copyarea(info, a);
279 		return;
280 	}
281 
282 	if (!gma_power_begin(dev, false)) {
283 		drm_fb_helper_cfb_copyarea(info, a);
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  */
psbfb_copyarea(struct fb_info * info,const struct fb_copyarea * region)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))
310 		return drm_fb_helper_cfb_copyarea(info, region);
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  */
psbfb_sync(struct fb_info * info)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;
328 	unsigned long _end = jiffies + HZ;
329 	int busy = 0;
330 	unsigned long flags;
331 
332 	spin_lock_irqsave(&dev_priv->lock_2d, flags);
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:
361 	spin_unlock_irqrestore(&dev_priv->lock_2d, flags);
362 	return (busy) ? -EBUSY : 0;
363 }
364