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