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