linux/drivers/gpu/drm/gma500/accel_2d.c
<<
>>
Prefs
   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