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