summaryrefslogtreecommitdiff
path: root/drivers/staging/psb/psb_sgx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/psb/psb_sgx.c')
-rw-r--r--drivers/staging/psb/psb_sgx.c1869
1 files changed, 1869 insertions, 0 deletions
diff --git a/drivers/staging/psb/psb_sgx.c b/drivers/staging/psb/psb_sgx.c
new file mode 100644
index 000000000000..517eed95647b
--- /dev/null
+++ b/drivers/staging/psb/psb_sgx.c
@@ -0,0 +1,1869 @@
+/**************************************************************************
+ * Copyright (c) 2007, Intel Corporation.
+ * All Rights Reserved.
+ * Copyright (c) 2008, Tungsten Graphics, Inc. Cedar Park, TX. USA.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
+ * develop this driver.
+ *
+ **************************************************************************/
+/*
+ */
+
+#include <drm/drmP.h>
+#include "psb_drv.h"
+#include "psb_drm.h"
+#include "psb_reg.h"
+#include "psb_scene.h"
+#include "psb_msvdx.h"
+#include "lnc_topaz.h"
+#include "ttm/ttm_bo_api.h"
+#include "ttm/ttm_execbuf_util.h"
+#include "ttm/ttm_userobj_api.h"
+#include "ttm/ttm_placement_common.h"
+#include "psb_sgx.h"
+
+static inline int psb_same_page(unsigned long offset,
+ unsigned long offset2)
+{
+ return (offset & PAGE_MASK) == (offset2 & PAGE_MASK);
+}
+
+static inline unsigned long psb_offset_end(unsigned long offset,
+ unsigned long end)
+{
+ offset = (offset + PAGE_SIZE) & PAGE_MASK;
+ return (end < offset) ? end : offset;
+}
+
+static void psb_idle_engine(struct drm_device *dev, int engine);
+
+struct psb_dstbuf_cache {
+ unsigned int dst;
+ struct ttm_buffer_object *dst_buf;
+ unsigned long dst_offset;
+ uint32_t *dst_page;
+ unsigned int dst_page_offset;
+ struct ttm_bo_kmap_obj dst_kmap;
+ bool dst_is_iomem;
+};
+
+struct psb_validate_buffer {
+ struct ttm_validate_buffer base;
+ struct psb_validate_req req;
+ int ret;
+ struct psb_validate_arg __user *user_val_arg;
+ uint32_t flags;
+ uint32_t offset;
+ int po_correct;
+};
+
+
+
+#define PSB_REG_GRAN_SHIFT 2
+#define PSB_REG_GRANULARITY (1 << PSB_REG_GRAN_SHIFT)
+#define PSB_MAX_REG 0x1000
+
+static const uint32_t disallowed_ranges[][2] = {
+ {0x0000, 0x0200},
+ {0x0208, 0x0214},
+ {0x021C, 0x0224},
+ {0x0230, 0x0234},
+ {0x0248, 0x024C},
+ {0x0254, 0x0358},
+ {0x0428, 0x0428},
+ {0x0430, 0x043C},
+ {0x0498, 0x04B4},
+ {0x04CC, 0x04D8},
+ {0x04E0, 0x07FC},
+ {0x0804, 0x0A14},
+ {0x0A4C, 0x0A58},
+ {0x0A68, 0x0A80},
+ {0x0AA0, 0x0B1C},
+ {0x0B2C, 0x0CAC},
+ {0x0CB4, PSB_MAX_REG - PSB_REG_GRANULARITY}
+};
+
+static uint32_t psb_disallowed_regs[PSB_MAX_REG /
+ (PSB_REG_GRANULARITY *
+ (sizeof(uint32_t) << 3))];
+
+static inline int psb_disallowed(uint32_t reg)
+{
+ reg >>= PSB_REG_GRAN_SHIFT;
+ return (psb_disallowed_regs[reg >> 5] & (1 << (reg & 31))) != 0;
+}
+
+void psb_init_disallowed(void)
+{
+ int i;
+ uint32_t reg, tmp;
+ static int initialized;
+
+ if (initialized)
+ return;
+
+ initialized = 1;
+ memset(psb_disallowed_regs, 0, sizeof(psb_disallowed_regs));
+
+ for (i = 0;
+ i < (sizeof(disallowed_ranges) / (2 * sizeof(uint32_t)));
+ ++i) {
+ for (reg = disallowed_ranges[i][0];
+ reg <= disallowed_ranges[i][1]; reg += 4) {
+ tmp = reg >> 2;
+ psb_disallowed_regs[tmp >> 5] |= (1 << (tmp & 31));
+ }
+ }
+}
+
+static int psb_memcpy_check(uint32_t *dst, const uint32_t *src,
+ uint32_t size)
+{
+ size >>= 3;
+ while (size--) {
+ if (unlikely((*src >= 0x1000) || psb_disallowed(*src))) {
+ DRM_ERROR("Forbidden SGX register access: "
+ "0x%04x.\n", *src);
+ return -EPERM;
+ }
+ *dst++ = *src++;
+ *dst++ = *src++;
+ }
+ return 0;
+}
+
+int psb_2d_wait_available(struct drm_psb_private *dev_priv,
+ unsigned size)
+{
+ uint32_t avail = PSB_RSGX32(PSB_CR_2D_SOCIF);
+ int ret = 0;
+
+retry:
+ if (avail < size) {
+#if 0
+ /* We'd ideally
+ * like to have an IRQ-driven event here.
+ */
+
+ psb_2D_irq_on(dev_priv);
+ DRM_WAIT_ON(ret, dev_priv->event_2d_queue, DRM_HZ,
+ ((avail =
+ PSB_RSGX32(PSB_CR_2D_SOCIF)) >= size));
+ psb_2D_irq_off(dev_priv);
+ if (ret == 0)
+ return 0;
+ if (ret == -EINTR) {
+ ret = 0;
+ goto retry;
+ }
+#else
+ avail = PSB_RSGX32(PSB_CR_2D_SOCIF);
+ goto retry;
+#endif
+ }
+ return ret;
+}
+
+int psb_2d_submit(struct drm_psb_private *dev_priv, uint32_t *cmdbuf,
+ unsigned size)
+{
+ int ret = 0;
+ int i;
+ unsigned submit_size;
+
+ while (size > 0) {
+ submit_size = (size < 0x60) ? size : 0x60;
+ size -= submit_size;
+ ret = psb_2d_wait_available(dev_priv, submit_size);
+ if (ret)
+ return ret;
+
+ submit_size <<= 2;
+ mutex_lock(&dev_priv->reset_mutex);
+ for (i = 0; i < submit_size; i += 4) {
+ PSB_WSGX32(*cmdbuf++, PSB_SGX_2D_SLAVE_PORT + i);
+ }
+ (void)PSB_RSGX32(PSB_SGX_2D_SLAVE_PORT + i - 4);
+ mutex_unlock(&dev_priv->reset_mutex);
+ }
+ return 0;
+}
+
+int psb_blit_sequence(struct drm_psb_private *dev_priv, uint32_t sequence)
+{
+ uint32_t buffer[8];
+ uint32_t *bufp = buffer;
+ int ret;
+
+ *bufp++ = PSB_2D_FENCE_BH;
+
+ *bufp++ = PSB_2D_DST_SURF_BH |
+ PSB_2D_DST_8888ARGB | (4 << PSB_2D_DST_STRIDE_SHIFT);
+ *bufp++ = dev_priv->comm_mmu_offset - dev_priv->mmu_2d_offset;
+
+ *bufp++ = PSB_2D_BLIT_BH |
+ PSB_2D_ROT_NONE |
+ PSB_2D_COPYORDER_TL2BR |
+ PSB_2D_DSTCK_DISABLE |
+ PSB_2D_SRCCK_DISABLE | PSB_2D_USE_FILL | PSB_2D_ROP3_PATCOPY;
+
+ *bufp++ = sequence << PSB_2D_FILLCOLOUR_SHIFT;
+ *bufp++ = (0 << PSB_2D_DST_XSTART_SHIFT) |
+ (0 << PSB_2D_DST_YSTART_SHIFT);
+ *bufp++ =
+ (1 << PSB_2D_DST_XSIZE_SHIFT) | (1 << PSB_2D_DST_YSIZE_SHIFT);
+
+ *bufp++ = PSB_2D_FLUSH_BH;
+
+ psb_2d_lock(dev_priv);
+ ret = psb_2d_submit(dev_priv, buffer, bufp - buffer);
+ psb_2d_unlock(dev_priv);
+
+ if (!ret)
+ psb_schedule_watchdog(dev_priv);
+ return ret;
+}
+
+int psb_emit_2d_copy_blit(struct drm_device *dev,
+ uint32_t src_offset,
+ uint32_t dst_offset, uint32_t pages,
+ int direction)
+{
+ uint32_t cur_pages;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ uint32_t buf[10];
+ uint32_t *bufp;
+ uint32_t xstart;
+ uint32_t ystart;
+ uint32_t blit_cmd;
+ uint32_t pg_add;
+ int ret = 0;
+
+ if (!dev_priv)
+ return 0;
+
+ if (direction) {
+ pg_add = (pages - 1) << PAGE_SHIFT;
+ src_offset += pg_add;
+ dst_offset += pg_add;
+ }
+
+ blit_cmd = PSB_2D_BLIT_BH |
+ PSB_2D_ROT_NONE |
+ PSB_2D_DSTCK_DISABLE |
+ PSB_2D_SRCCK_DISABLE |
+ PSB_2D_USE_PAT |
+ PSB_2D_ROP3_SRCCOPY |
+ (direction ? PSB_2D_COPYORDER_BR2TL : PSB_2D_COPYORDER_TL2BR);
+ xstart = (direction) ? ((PAGE_SIZE - 1) >> 2) : 0;
+
+ psb_2d_lock(dev_priv);
+ while (pages > 0) {
+ cur_pages = pages;
+ if (cur_pages > 2048)
+ cur_pages = 2048;
+ pages -= cur_pages;
+ ystart = (direction) ? cur_pages - 1 : 0;
+
+ bufp = buf;
+ *bufp++ = PSB_2D_FENCE_BH;
+
+ *bufp++ = PSB_2D_DST_SURF_BH | PSB_2D_DST_8888ARGB |
+ (PAGE_SIZE << PSB_2D_DST_STRIDE_SHIFT);
+ *bufp++ = dst_offset;
+ *bufp++ = PSB_2D_SRC_SURF_BH | PSB_2D_SRC_8888ARGB |
+ (PAGE_SIZE << PSB_2D_SRC_STRIDE_SHIFT);
+ *bufp++ = src_offset;
+ *bufp++ =
+ PSB_2D_SRC_OFF_BH | (xstart <<
+ PSB_2D_SRCOFF_XSTART_SHIFT) |
+ (ystart << PSB_2D_SRCOFF_YSTART_SHIFT);
+ *bufp++ = blit_cmd;
+ *bufp++ = (xstart << PSB_2D_DST_XSTART_SHIFT) |
+ (ystart << PSB_2D_DST_YSTART_SHIFT);
+ *bufp++ = ((PAGE_SIZE >> 2) << PSB_2D_DST_XSIZE_SHIFT) |
+ (cur_pages << PSB_2D_DST_YSIZE_SHIFT);
+
+ ret = psb_2d_submit(dev_priv, buf, bufp - buf);
+ if (ret)
+ goto out;
+ pg_add =
+ (cur_pages << PAGE_SHIFT) * ((direction) ? -1 : 1);
+ src_offset += pg_add;
+ dst_offset += pg_add;
+ }
+out:
+ psb_2d_unlock(dev_priv);
+ return ret;
+}
+
+void psb_init_2d(struct drm_psb_private *dev_priv)
+{
+ spin_lock_init(&dev_priv->sequence_lock);
+ psb_reset(dev_priv, 1);
+ dev_priv->mmu_2d_offset = dev_priv->pg->gatt_start;
+ PSB_WSGX32(dev_priv->mmu_2d_offset, PSB_CR_BIF_TWOD_REQ_BASE);
+ (void) PSB_RSGX32(PSB_CR_BIF_TWOD_REQ_BASE);
+}
+
+int psb_idle_2d(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ unsigned long _end = jiffies + DRM_HZ;
+ int busy = 0;
+
+ /*
+ * First idle the 2D engine.
+ */
+
+ if (dev_priv->engine_lockup_2d)
+ return -EBUSY;
+
+ if ((PSB_RSGX32(PSB_CR_2D_SOCIF) == _PSB_C2_SOCIF_EMPTY) &&
+ ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & _PSB_C2B_STATUS_BUSY) ==
+ 0))
+ goto out;
+
+ do {
+ busy =
+ (PSB_RSGX32(PSB_CR_2D_SOCIF) != _PSB_C2_SOCIF_EMPTY);
+ } while (busy && !time_after_eq(jiffies, _end));
+
+ if (busy)
+ busy =
+ (PSB_RSGX32(PSB_CR_2D_SOCIF) != _PSB_C2_SOCIF_EMPTY);
+ if (busy)
+ goto out;
+
+ do {
+ busy =
+ ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) &
+ _PSB_C2B_STATUS_BUSY)
+ != 0);
+ } while (busy && !time_after_eq(jiffies, _end));
+ if (busy)
+ busy =
+ ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) &
+ _PSB_C2B_STATUS_BUSY)
+ != 0);
+
+out:
+ if (busy)
+ dev_priv->engine_lockup_2d = 1;
+
+ return (busy) ? -EBUSY : 0;
+}
+
+int psb_idle_3d(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+ int ret;
+
+ ret = wait_event_timeout(scheduler->idle_queue,
+ psb_scheduler_finished(dev_priv),
+ DRM_HZ * 10);
+
+ return (ret < 1) ? -EBUSY : 0;
+}
+
+static int psb_check_presumed(struct psb_validate_req *req,
+ struct ttm_buffer_object *bo,
+ struct psb_validate_arg __user *data,
+ int *presumed_ok)
+{
+ struct psb_validate_req __user *user_req = &(data->d.req);
+
+ *presumed_ok = 0;
+
+ if (bo->mem.mem_type == TTM_PL_SYSTEM) {
+ *presumed_ok = 1;
+ return 0;
+ }
+
+ if (unlikely(!(req->presumed_flags & PSB_USE_PRESUMED)))
+ return 0;
+
+ if (bo->offset == req->presumed_gpu_offset) {
+ *presumed_ok = 1;
+ return 0;
+ }
+
+ return __put_user(req->presumed_flags & ~PSB_USE_PRESUMED,
+ &user_req->presumed_flags);
+}
+
+
+static void psb_unreference_buffers(struct psb_context *context)
+{
+ struct ttm_validate_buffer *entry, *next;
+ struct psb_validate_buffer *vbuf;
+ struct list_head *list = &context->validate_list;
+
+ list_for_each_entry_safe(entry, next, list, head) {
+ vbuf =
+ container_of(entry, struct psb_validate_buffer, base);
+ list_del(&entry->head);
+ ttm_bo_unref(&entry->bo);
+ }
+
+ list = &context->kern_validate_list;
+
+ list_for_each_entry_safe(entry, next, list, head) {
+ vbuf =
+ container_of(entry, struct psb_validate_buffer, base);
+ list_del(&entry->head);
+ ttm_bo_unref(&entry->bo);
+ }
+}
+
+
+static int psb_lookup_validate_buffer(struct drm_file *file_priv,
+ uint64_t data,
+ struct psb_validate_buffer *item)
+{
+ struct ttm_object_file *tfile = psb_fpriv(file_priv)->tfile;
+
+ item->user_val_arg =
+ (struct psb_validate_arg __user *) (unsigned long) data;
+
+ if (unlikely(copy_from_user(&item->req, &item->user_val_arg->d.req,
+ sizeof(item->req)) != 0)) {
+ DRM_ERROR("Lookup copy fault.\n");
+ return -EFAULT;
+ }
+
+ item->base.bo =
+ ttm_buffer_object_lookup(tfile, item->req.buffer_handle);
+
+ if (unlikely(item->base.bo == NULL)) {
+ DRM_ERROR("Bo lookup fault.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int psb_reference_buffers(struct drm_file *file_priv,
+ uint64_t data,
+ struct psb_context *context)
+{
+ struct psb_validate_buffer *item;
+ int ret;
+
+ while (likely(data != 0)) {
+ if (unlikely(context->used_buffers >=
+ PSB_NUM_VALIDATE_BUFFERS)) {
+ DRM_ERROR("Too many buffers "
+ "on validate list.\n");
+ ret = -EINVAL;
+ goto out_err0;
+ }
+
+ item = &context->buffers[context->used_buffers];
+
+ ret = psb_lookup_validate_buffer(file_priv, data, item);
+ if (unlikely(ret != 0))
+ goto out_err0;
+
+ item->base.reserved = 0;
+ list_add_tail(&item->base.head, &context->validate_list);
+ context->used_buffers++;
+ data = item->req.next;
+ }
+ return 0;
+
+out_err0:
+ psb_unreference_buffers(context);
+ return ret;
+}
+
+static int
+psb_placement_fence_type(struct ttm_buffer_object *bo,
+ uint64_t set_val_flags,
+ uint64_t clr_val_flags,
+ uint32_t new_fence_class,
+ uint32_t *new_fence_type)
+{
+ int ret;
+ uint32_t n_fence_type;
+ uint32_t set_flags = set_val_flags & 0xFFFFFFFF;
+ uint32_t clr_flags = clr_val_flags & 0xFFFFFFFF;
+ struct ttm_fence_object *old_fence;
+ uint32_t old_fence_type;
+
+ if (unlikely
+ (!(set_val_flags &
+ (PSB_GPU_ACCESS_READ | PSB_GPU_ACCESS_WRITE)))) {
+ DRM_ERROR
+ ("GPU access type (read / write) is not indicated.\n");
+ return -EINVAL;
+ }
+
+ ret = ttm_bo_check_placement(bo, set_flags, clr_flags);
+ if (unlikely(ret != 0))
+ return ret;
+
+ switch (new_fence_class) {
+ case PSB_ENGINE_TA:
+ n_fence_type = _PSB_FENCE_TYPE_EXE |
+ _PSB_FENCE_TYPE_TA_DONE | _PSB_FENCE_TYPE_RASTER_DONE;
+ if (set_val_flags & PSB_BO_FLAG_TA)
+ n_fence_type &= ~_PSB_FENCE_TYPE_RASTER_DONE;
+ if (set_val_flags & PSB_BO_FLAG_COMMAND)
+ n_fence_type &=
+ ~(_PSB_FENCE_TYPE_RASTER_DONE |
+ _PSB_FENCE_TYPE_TA_DONE);
+ if (set_val_flags & PSB_BO_FLAG_SCENE)
+ n_fence_type |= _PSB_FENCE_TYPE_SCENE_DONE;
+ if (set_val_flags & PSB_BO_FLAG_FEEDBACK)
+ n_fence_type |= _PSB_FENCE_TYPE_FEEDBACK;
+ break;
+ default:
+ n_fence_type = _PSB_FENCE_TYPE_EXE;
+ }
+
+ *new_fence_type = n_fence_type;
+ old_fence = (struct ttm_fence_object *) bo->sync_obj;
+ old_fence_type = (uint32_t) (unsigned long) bo->sync_obj_arg;
+
+ if (old_fence && ((new_fence_class != old_fence->fence_class) ||
+ ((n_fence_type ^ old_fence_type) &
+ old_fence_type))) {
+ ret = ttm_bo_wait(bo, 0, 1, 0);
+ if (unlikely(ret != 0))
+ return ret;
+ }
+
+ bo->proposed_flags = (bo->proposed_flags | set_flags)
+ & ~clr_flags & TTM_PL_MASK_MEMTYPE;
+
+ return 0;
+}
+
+int psb_validate_kernel_buffer(struct psb_context *context,
+ struct ttm_buffer_object *bo,
+ uint32_t fence_class,
+ uint64_t set_flags, uint64_t clr_flags)
+{
+ struct psb_validate_buffer *item;
+ uint32_t cur_fence_type;
+ int ret;
+
+ if (unlikely(context->used_buffers >= PSB_NUM_VALIDATE_BUFFERS)) {
+ DRM_ERROR("Out of free validation buffer entries for "
+ "kernel buffer validation.\n");
+ return -ENOMEM;
+ }
+
+ item = &context->buffers[context->used_buffers];
+ item->user_val_arg = NULL;
+ item->base.reserved = 0;
+
+ ret = ttm_bo_reserve(bo, 1, 0, 1, context->val_seq);
+ if (unlikely(ret != 0))
+ goto out_unlock;
+
+ mutex_lock(&bo->mutex);
+ ret = psb_placement_fence_type(bo, set_flags, clr_flags, fence_class,
+ &cur_fence_type);
+ if (unlikely(ret != 0)) {
+ ttm_bo_unreserve(bo);
+ goto out_unlock;
+ }
+
+ item->base.bo = ttm_bo_reference(bo);
+ item->base.new_sync_obj_arg = (void *) (unsigned long) cur_fence_type;
+ item->base.reserved = 1;
+
+ list_add_tail(&item->base.head, &context->kern_validate_list);
+ context->used_buffers++;
+
+ ret = ttm_buffer_object_validate(bo, 1, 0);
+ if (unlikely(ret != 0))
+ goto out_unlock;
+
+ item->offset = bo->offset;
+ item->flags = bo->mem.flags;
+ context->fence_types |= cur_fence_type;
+
+out_unlock:
+ mutex_unlock(&bo->mutex);
+ return ret;
+}
+
+
+static int psb_validate_buffer_list(struct drm_file *file_priv,
+ uint32_t fence_class,
+ struct psb_context *context,
+ int *po_correct)
+{
+ struct psb_validate_buffer *item;
+ struct ttm_buffer_object *bo;
+ int ret;
+ struct psb_validate_req *req;
+ uint32_t fence_types = 0;
+ uint32_t cur_fence_type;
+ struct ttm_validate_buffer *entry;
+ struct list_head *list = &context->validate_list;
+
+ *po_correct = 1;
+
+ list_for_each_entry(entry, list, head) {
+ item =
+ container_of(entry, struct psb_validate_buffer, base);
+ bo = entry->bo;
+ item->ret = 0;
+ req = &item->req;
+
+ mutex_lock(&bo->mutex);
+ ret = psb_placement_fence_type(bo,
+ req->set_flags,
+ req->clear_flags,
+ fence_class,
+ &cur_fence_type);
+ if (unlikely(ret != 0))
+ goto out_err;
+
+ ret = ttm_buffer_object_validate(bo, 1, 0);
+
+ if (unlikely(ret != 0))
+ goto out_err;
+
+ fence_types |= cur_fence_type;
+ entry->new_sync_obj_arg = (void *)
+ (unsigned long) cur_fence_type;
+
+ item->offset = bo->offset;
+ item->flags = bo->mem.flags;
+ mutex_unlock(&bo->mutex);
+
+ ret =
+ psb_check_presumed(&item->req, bo, item->user_val_arg,
+ &item->po_correct);
+ if (unlikely(ret != 0))
+ goto out_err;
+
+ if (unlikely(!item->po_correct))
+ *po_correct = 0;
+
+ item++;
+ }
+
+ context->fence_types |= fence_types;
+
+ return 0;
+out_err:
+ mutex_unlock(&bo->mutex);
+ item->ret = ret;
+ return ret;
+}
+
+
+int
+psb_reg_submit(struct drm_psb_private *dev_priv, uint32_t *regs,
+ unsigned int cmds)
+{
+ int i;
+
+ /*
+ * cmds is 32-bit words.
+ */
+
+ cmds >>= 1;
+ for (i = 0; i < cmds; ++i) {
+ PSB_WSGX32(regs[1], regs[0]);
+ regs += 2;
+ }
+ wmb();
+ return 0;
+}
+
+/*
+ * Security: Block user-space writing to MMU mapping registers.
+ * This is important for security and brings Poulsbo DRM
+ * up to par with the other DRM drivers. Using this,
+ * user-space should not be able to map arbitrary memory
+ * pages to graphics memory, but all user-space processes
+ * basically have access to all buffer objects mapped to
+ * graphics memory.
+ */
+
+int
+psb_submit_copy_cmdbuf(struct drm_device *dev,
+ struct ttm_buffer_object *cmd_buffer,
+ unsigned long cmd_offset,
+ unsigned long cmd_size,
+ int engine, uint32_t *copy_buffer)
+{
+ unsigned long cmd_end = cmd_offset + (cmd_size << 2);
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ unsigned long cmd_page_offset =
+ cmd_offset - (cmd_offset & PAGE_MASK);
+ unsigned long cmd_next;
+ struct ttm_bo_kmap_obj cmd_kmap;
+ uint32_t *cmd_page;
+ unsigned cmds;
+ bool is_iomem;
+ int ret = 0;
+
+ if (cmd_size == 0)
+ return 0;
+
+ if (engine == PSB_ENGINE_2D)
+ psb_2d_lock(dev_priv);
+
+ do {
+ cmd_next = psb_offset_end(cmd_offset, cmd_end);
+ ret = ttm_bo_kmap(cmd_buffer, cmd_offset >> PAGE_SHIFT,
+ 1, &cmd_kmap);
+
+ if (ret) {
+ if (engine == PSB_ENGINE_2D)
+ psb_2d_unlock(dev_priv);
+ return ret;
+ }
+ cmd_page = ttm_kmap_obj_virtual(&cmd_kmap, &is_iomem);
+ cmd_page_offset = (cmd_offset & ~PAGE_MASK) >> 2;
+ cmds = (cmd_next - cmd_offset) >> 2;
+
+ switch (engine) {
+ case PSB_ENGINE_2D:
+ ret =
+ psb_2d_submit(dev_priv,
+ cmd_page + cmd_page_offset,
+ cmds);
+ break;
+ case PSB_ENGINE_RASTERIZER:
+ case PSB_ENGINE_TA:
+ case PSB_ENGINE_HPRAST:
+ PSB_DEBUG_GENERAL("Reg copy.\n");
+ ret = psb_memcpy_check(copy_buffer,
+ cmd_page + cmd_page_offset,
+ cmds * sizeof(uint32_t));
+ copy_buffer += cmds;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ ttm_bo_kunmap(&cmd_kmap);
+ if (ret)
+ break;
+ } while (cmd_offset = cmd_next, cmd_offset != cmd_end);
+
+ if (engine == PSB_ENGINE_2D)
+ psb_2d_unlock(dev_priv);
+
+ return ret;
+}
+
+static void psb_clear_dstbuf_cache(struct psb_dstbuf_cache *dst_cache)
+{
+ if (dst_cache->dst_page) {
+ ttm_bo_kunmap(&dst_cache->dst_kmap);
+ dst_cache->dst_page = NULL;
+ }
+ dst_cache->dst_buf = NULL;
+ dst_cache->dst = ~0;
+}
+
+static int psb_update_dstbuf_cache(struct psb_dstbuf_cache *dst_cache,
+ struct psb_validate_buffer *buffers,
+ unsigned int dst,
+ unsigned long dst_offset)
+{
+ int ret;
+
+ PSB_DEBUG_GENERAL("Destination buffer is %d.\n", dst);
+
+ if (unlikely(dst != dst_cache->dst || NULL == dst_cache->dst_buf)) {
+ psb_clear_dstbuf_cache(dst_cache);
+ dst_cache->dst = dst;
+ dst_cache->dst_buf = buffers[dst].base.bo;
+ }
+
+ if (unlikely
+ (dst_offset > dst_cache->dst_buf->num_pages * PAGE_SIZE)) {
+ DRM_ERROR("Relocation destination out of bounds.\n");
+ return -EINVAL;
+ }
+
+ if (!psb_same_page(dst_cache->dst_offset, dst_offset) ||
+ NULL == dst_cache->dst_page) {
+ if (NULL != dst_cache->dst_page) {
+ ttm_bo_kunmap(&dst_cache->dst_kmap);
+ dst_cache->dst_page = NULL;
+ }
+
+ ret =
+ ttm_bo_kmap(dst_cache->dst_buf,
+ dst_offset >> PAGE_SHIFT, 1,
+ &dst_cache->dst_kmap);
+ if (ret) {
+ DRM_ERROR("Could not map destination buffer for "
+ "relocation.\n");
+ return ret;
+ }
+
+ dst_cache->dst_page =
+ ttm_kmap_obj_virtual(&dst_cache->dst_kmap,
+ &dst_cache->dst_is_iomem);
+ dst_cache->dst_offset = dst_offset & PAGE_MASK;
+ dst_cache->dst_page_offset = dst_cache->dst_offset >> 2;
+ }
+ return 0;
+}
+
+static int psb_apply_reloc(struct drm_psb_private *dev_priv,
+ uint32_t fence_class,
+ const struct drm_psb_reloc *reloc,
+ struct psb_validate_buffer *buffers,
+ int num_buffers,
+ struct psb_dstbuf_cache *dst_cache,
+ int no_wait, int interruptible)
+{
+ uint32_t val;
+ uint32_t background;
+ unsigned int index;
+ int ret;
+ unsigned int shift;
+ unsigned int align_shift;
+ struct ttm_buffer_object *reloc_bo;
+
+
+ PSB_DEBUG_GENERAL("Reloc type %d\n"
+ "\t where 0x%04x\n"
+ "\t buffer 0x%04x\n"
+ "\t mask 0x%08x\n"
+ "\t shift 0x%08x\n"
+ "\t pre_add 0x%08x\n"
+ "\t background 0x%08x\n"
+ "\t dst_buffer 0x%08x\n"
+ "\t arg0 0x%08x\n"
+ "\t arg1 0x%08x\n",
+ reloc->reloc_op,
+ reloc->where,
+ reloc->buffer,
+ reloc->mask,
+ reloc->shift,
+ reloc->pre_add,
+ reloc->background,
+ reloc->dst_buffer, reloc->arg0, reloc->arg1);
+
+ if (unlikely(reloc->buffer >= num_buffers)) {
+ DRM_ERROR("Illegal relocation buffer %d.\n",
+ reloc->buffer);
+ return -EINVAL;
+ }
+
+ if (buffers[reloc->buffer].po_correct)
+ return 0;
+
+ if (unlikely(reloc->dst_buffer >= num_buffers)) {
+ DRM_ERROR
+ ("Illegal destination buffer for relocation %d.\n",
+ reloc->dst_buffer);
+ return -EINVAL;
+ }
+
+ ret =
+ psb_update_dstbuf_cache(dst_cache, buffers, reloc->dst_buffer,
+ reloc->where << 2);
+ if (ret)
+ return ret;
+
+ reloc_bo = buffers[reloc->buffer].base.bo;
+
+ if (unlikely(reloc->pre_add > (reloc_bo->num_pages << PAGE_SHIFT))) {
+ DRM_ERROR("Illegal relocation offset add.\n");
+ return -EINVAL;
+ }
+
+ switch (reloc->reloc_op) {
+ case PSB_RELOC_OP_OFFSET:
+ val = reloc_bo->offset + reloc->pre_add;
+ break;
+ case PSB_RELOC_OP_2D_OFFSET:
+ val = reloc_bo->offset + reloc->pre_add -
+ dev_priv->mmu_2d_offset;
+ if (unlikely(val >= PSB_2D_SIZE)) {
+ DRM_ERROR("2D relocation out of bounds\n");
+ return -EINVAL;
+ }
+ break;
+ case PSB_RELOC_OP_PDS_OFFSET:
+ val =
+ reloc_bo->offset + reloc->pre_add - PSB_MEM_PDS_START;
+ if (unlikely
+ (val >= (PSB_MEM_MMU_START - PSB_MEM_PDS_START))) {
+ DRM_ERROR("PDS relocation out of bounds\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ DRM_ERROR("Unimplemented relocation.\n");
+ return -EINVAL;
+ }
+
+ shift =
+ (reloc->shift & PSB_RELOC_SHIFT_MASK) >> PSB_RELOC_SHIFT_SHIFT;
+ align_shift =
+ (reloc->
+ shift & PSB_RELOC_ALSHIFT_MASK) >> PSB_RELOC_ALSHIFT_SHIFT;
+
+ val = ((val >> align_shift) << shift);
+ index = reloc->where - dst_cache->dst_page_offset;
+
+ background = reloc->background;
+ val = (background & ~reloc->mask) | (val & reloc->mask);
+ dst_cache->dst_page[index] = val;
+
+ PSB_DEBUG_GENERAL("Reloc buffer %d index 0x%08x, value 0x%08x\n",
+ reloc->dst_buffer, index,
+ dst_cache->dst_page[index]);
+
+ return 0;
+}
+
+static int psb_ok_to_map_reloc(struct drm_psb_private *dev_priv,
+ unsigned int num_pages)
+{
+ int ret = 0;
+
+ spin_lock(&dev_priv->reloc_lock);
+ if (dev_priv->rel_mapped_pages + num_pages <= PSB_MAX_RELOC_PAGES) {
+ dev_priv->rel_mapped_pages += num_pages;
+ ret = 1;
+ }
+ spin_unlock(&dev_priv->reloc_lock);
+ return ret;
+}
+
+static int psb_fixup_relocs(struct drm_file *file_priv,
+ uint32_t fence_class,
+ unsigned int num_relocs,
+ unsigned int reloc_offset,
+ uint32_t reloc_handle,
+ struct psb_context *context,
+ int no_wait, int interruptible)
+{
+ struct drm_device *dev = file_priv->minor->dev;
+ struct ttm_object_file *tfile = psb_fpriv(file_priv)->tfile;
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+ struct ttm_buffer_object *reloc_buffer = NULL;
+ unsigned int reloc_num_pages;
+ unsigned int reloc_first_page;
+ unsigned int reloc_last_page;
+ struct psb_dstbuf_cache dst_cache;
+ struct drm_psb_reloc *reloc;
+ struct ttm_bo_kmap_obj reloc_kmap;
+ bool reloc_is_iomem;
+ int count;
+ int ret = 0;
+ int registered = 0;
+ uint32_t num_buffers = context->used_buffers;
+
+ if (num_relocs == 0)
+ return 0;
+
+ memset(&dst_cache, 0, sizeof(dst_cache));
+ memset(&reloc_kmap, 0, sizeof(reloc_kmap));
+
+ reloc_buffer = ttm_buffer_object_lookup(tfile, reloc_handle);
+ if (!reloc_buffer)
+ goto out;
+
+ if (unlikely(atomic_read(&reloc_buffer->reserved) != 1)) {
+ DRM_ERROR("Relocation buffer was not on validate list.\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ reloc_first_page = reloc_offset >> PAGE_SHIFT;
+ reloc_last_page =
+ (reloc_offset +
+ num_relocs * sizeof(struct drm_psb_reloc)) >> PAGE_SHIFT;
+ reloc_num_pages = reloc_last_page - reloc_first_page + 1;
+ reloc_offset &= ~PAGE_MASK;
+
+ if (reloc_num_pages > PSB_MAX_RELOC_PAGES) {
+ DRM_ERROR("Relocation buffer is too large\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ DRM_WAIT_ON(ret, dev_priv->rel_mapped_queue, 3 * DRM_HZ,
+ (registered =
+ psb_ok_to_map_reloc(dev_priv, reloc_num_pages)));
+
+ if (ret == -EINTR) {
+ ret = -ERESTART;
+ goto out;
+ }
+ if (ret) {
+ DRM_ERROR("Error waiting for space to map "
+ "relocation buffer.\n");
+ goto out;
+ }
+
+ ret = ttm_bo_kmap(reloc_buffer, reloc_first_page,
+ reloc_num_pages, &reloc_kmap);
+
+ if (ret) {
+ DRM_ERROR("Could not map relocation buffer.\n"
+ "\tReloc buffer id 0x%08x.\n"
+ "\tReloc first page %d.\n"
+ "\tReloc num pages %d.\n",
+ reloc_handle, reloc_first_page, reloc_num_pages);
+ goto out;
+ }
+
+ reloc = (struct drm_psb_reloc *)
+ ((unsigned long)
+ ttm_kmap_obj_virtual(&reloc_kmap,
+ &reloc_is_iomem) + reloc_offset);
+
+ for (count = 0; count < num_relocs; ++count) {
+ ret = psb_apply_reloc(dev_priv, fence_class,
+ reloc, context->buffers,
+ num_buffers, &dst_cache,
+ no_wait, interruptible);
+ if (ret)
+ goto out1;
+ reloc++;
+ }
+
+out1:
+ ttm_bo_kunmap(&reloc_kmap);
+out:
+ if (registered) {
+ spin_lock(&dev_priv->reloc_lock);
+ dev_priv->rel_mapped_pages -= reloc_num_pages;
+ spin_unlock(&dev_priv->reloc_lock);
+ DRM_WAKEUP(&dev_priv->rel_mapped_queue);
+ }
+
+ psb_clear_dstbuf_cache(&dst_cache);
+ if (reloc_buffer)
+ ttm_bo_unref(&reloc_buffer);
+ return ret;
+}
+
+void psb_fence_or_sync(struct drm_file *file_priv,
+ uint32_t engine,
+ uint32_t fence_types,
+ uint32_t fence_flags,
+ struct list_head *list,
+ struct psb_ttm_fence_rep *fence_arg,
+ struct ttm_fence_object **fence_p)
+{
+ struct drm_device *dev = file_priv->minor->dev;
+ struct drm_psb_private *dev_priv = psb_priv(dev);
+ struct ttm_fence_device *fdev = &dev_priv->fdev;
+ int ret;
+ struct ttm_fence_object *fence;
+ struct ttm_object_file *tfile = psb_fpriv(file_priv)->tfile;
+ uint32_t handle;
+
+ ret = ttm_fence_user_create(fdev, tfile,
+ engine, fence_types,
+ TTM_FENCE_FLAG_EMIT, &fence, &handle);
+ if (ret) {
+
+ /*
+ * Fence creation failed.
+ * Fall back to synchronous operation and idle the engine.
+ */
+
+ psb_idle_engine(dev, engine);
+ if (!(fence_flags & DRM_PSB_FENCE_NO_USER)) {
+
+ /*
+ * Communicate to user-space that
+ * fence creation has failed and that
+ * the engine is idle.
+ */
+
+ fence_arg->handle = ~0;
+ fence_arg->error = ret;
+ }
+
+ ttm_eu_backoff_reservation(list);
+ if (fence_p)
+ *fence_p = NULL;
+ return;
+ }
+
+ ttm_eu_fence_buffer_objects(list, fence);
+ if (!(fence_flags & DRM_PSB_FENCE_NO_USER)) {
+ struct ttm_fence_info info = ttm_fence_get_info(fence);
+ fence_arg->handle = handle;
+ fence_arg->fence_class = ttm_fence_class(fence);
+ fence_arg->fence_type = ttm_fence_types(fence);
+ fence_arg->signaled_types = info.signaled_types;
+ fence_arg->error = 0;
+ } else {
+ ret =
+ ttm_ref_object_base_unref(tfile, handle,
+ ttm_fence_type);
+ BUG_ON(ret);
+ }
+
+ if (fence_p)
+ *fence_p = fence;
+ else if (fence)
+ ttm_fence_object_unref(&fence);
+}
+
+
+
+static int psb_cmdbuf_2d(struct drm_file *priv,
+ struct list_head *validate_list,
+ uint32_t fence_type,
+ struct drm_psb_cmdbuf_arg *arg,
+ struct ttm_buffer_object *cmd_buffer,
+ struct psb_ttm_fence_rep *fence_arg)
+{
+ struct drm_device *dev = priv->minor->dev;
+ int ret;
+
+ ret = psb_submit_copy_cmdbuf(dev, cmd_buffer, arg->cmdbuf_offset,
+ arg->cmdbuf_size, PSB_ENGINE_2D,
+ NULL);
+ if (ret)
+ goto out_unlock;
+
+ psb_fence_or_sync(priv, PSB_ENGINE_2D, fence_type,
+ arg->fence_flags, validate_list, fence_arg,
+ NULL);
+
+ mutex_lock(&cmd_buffer->mutex);
+ if (cmd_buffer->sync_obj != NULL)
+ ttm_fence_sync_obj_unref(&cmd_buffer->sync_obj);
+ mutex_unlock(&cmd_buffer->mutex);
+out_unlock:
+ return ret;
+}
+
+#if 0
+static int psb_dump_page(struct ttm_buffer_object *bo,
+ unsigned int page_offset, unsigned int num)
+{
+ struct ttm_bo_kmap_obj kmobj;
+ int is_iomem;
+ uint32_t *p;
+ int ret;
+ unsigned int i;
+
+ ret = ttm_bo_kmap(bo, page_offset, 1, &kmobj);
+ if (ret)
+ return ret;
+
+ p = ttm_kmap_obj_virtual(&kmobj, &is_iomem);
+ for (i = 0; i < num; ++i)
+ PSB_DEBUG_GENERAL("0x%04x: 0x%08x\n", i, *p++);
+
+ ttm_bo_kunmap(&kmobj);
+ return 0;
+}
+#endif
+
+static void psb_idle_engine(struct drm_device *dev, int engine)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+ uint32_t dummy;
+ unsigned long dummy2;
+
+ switch (engine) {
+ case PSB_ENGINE_2D:
+
+ /*
+ * Make sure we flush 2D properly using a dummy
+ * fence sequence emit.
+ */
+
+ (void) psb_fence_emit_sequence(&dev_priv->fdev,
+ PSB_ENGINE_2D, 0,
+ &dummy, &dummy2);
+ psb_2d_lock(dev_priv);
+ (void) psb_idle_2d(dev);
+ psb_2d_unlock(dev_priv);
+ break;
+ case PSB_ENGINE_TA:
+ case PSB_ENGINE_RASTERIZER:
+ case PSB_ENGINE_HPRAST:
+ (void) psb_idle_3d(dev);
+ break;
+ default:
+
+ /*
+ * FIXME: Insert video engine idle command here.
+ */
+
+ break;
+ }
+}
+
+static int psb_handle_copyback(struct drm_device *dev,
+ struct psb_context *context,
+ int ret)
+{
+ int err = ret;
+ struct ttm_validate_buffer *entry;
+ struct psb_validate_arg arg;
+ struct list_head *list = &context->validate_list;
+
+ if (ret) {
+ ttm_eu_backoff_reservation(list);
+ ttm_eu_backoff_reservation(&context->kern_validate_list);
+ }
+
+
+ if (ret != -EAGAIN && ret != -EINTR && ret != -ERESTART) {
+ list_for_each_entry(entry, list, head) {
+ struct psb_validate_buffer *vbuf =
+ container_of(entry, struct psb_validate_buffer,
+ base);
+ arg.handled = 1;
+ arg.ret = vbuf->ret;
+ if (!arg.ret) {
+ struct ttm_buffer_object *bo = entry->bo;
+ mutex_lock(&bo->mutex);
+ arg.d.rep.gpu_offset = bo->offset;
+ arg.d.rep.placement = bo->mem.flags;
+ arg.d.rep.fence_type_mask =
+ (uint32_t) (unsigned long)
+ entry->new_sync_obj_arg;
+ mutex_unlock(&bo->mutex);
+ }
+
+ if (__copy_to_user(vbuf->user_val_arg,
+ &arg, sizeof(arg)))
+ err = -EFAULT;
+
+ if (arg.ret)
+ break;
+ }
+ }
+
+ return err;
+}
+
+
+static int psb_cmdbuf_video(struct drm_file *priv,
+ struct list_head *validate_list,
+ uint32_t fence_type,
+ struct drm_psb_cmdbuf_arg *arg,
+ struct ttm_buffer_object *cmd_buffer,
+ struct psb_ttm_fence_rep *fence_arg)
+{
+ struct drm_device *dev = priv->minor->dev;
+ struct ttm_fence_object *fence;
+ int ret;
+
+ /*
+ * Check this. Doesn't seem right. Have fencing done AFTER command
+ * submission and make sure drm_psb_idle idles the MSVDX completely.
+ */
+ ret =
+ psb_submit_video_cmdbuf(dev, cmd_buffer, arg->cmdbuf_offset,
+ arg->cmdbuf_size, NULL);
+ if (ret)
+ return ret;
+
+
+ /* DRM_ERROR("Intel: Fix video fencing!!\n"); */
+ psb_fence_or_sync(priv, PSB_ENGINE_VIDEO, fence_type,
+ arg->fence_flags, validate_list, fence_arg,
+ &fence);
+
+
+ ttm_fence_object_unref(&fence);
+ mutex_lock(&cmd_buffer->mutex);
+ if (cmd_buffer->sync_obj != NULL)
+ ttm_fence_sync_obj_unref(&cmd_buffer->sync_obj);
+ mutex_unlock(&cmd_buffer->mutex);
+ return 0;
+}
+
+static int psb_feedback_buf(struct ttm_object_file *tfile,
+ struct psb_context *context,
+ uint32_t feedback_ops,
+ uint32_t handle,
+ uint32_t offset,
+ uint32_t feedback_breakpoints,
+ uint32_t feedback_size,
+ struct psb_feedback_info *feedback)
+{
+ struct ttm_buffer_object *bo;
+ struct page *page;
+ uint32_t page_no;
+ uint32_t page_offset;
+ int ret;
+
+ if (feedback_ops & ~PSB_FEEDBACK_OP_VISTEST) {
+ DRM_ERROR("Illegal feedback op.\n");
+ return -EINVAL;
+ }
+
+ if (feedback_breakpoints != 0) {
+ DRM_ERROR("Feedback breakpoints not implemented yet.\n");
+ return -EINVAL;
+ }
+
+ if (feedback_size < PSB_HW_FEEDBACK_SIZE * sizeof(uint32_t)) {
+ DRM_ERROR("Feedback buffer size too small.\n");
+ return -EINVAL;
+ }
+
+ page_offset = offset & ~PAGE_MASK;
+ if ((PAGE_SIZE - PSB_HW_FEEDBACK_SIZE * sizeof(uint32_t))
+ < page_offset) {
+ DRM_ERROR("Illegal feedback buffer alignment.\n");
+ return -EINVAL;
+ }
+
+ bo = ttm_buffer_object_lookup(tfile, handle);
+ if (unlikely(bo == NULL)) {
+ DRM_ERROR("Failed looking up feedback buffer.\n");
+ return -EINVAL;
+ }
+
+
+ ret = psb_validate_kernel_buffer(context, bo,
+ PSB_ENGINE_TA,
+ TTM_PL_FLAG_SYSTEM |
+ TTM_PL_FLAG_CACHED |
+ PSB_GPU_ACCESS_WRITE |
+ PSB_BO_FLAG_FEEDBACK,
+ TTM_PL_MASK_MEM &
+ ~(TTM_PL_FLAG_SYSTEM |
+ TTM_PL_FLAG_CACHED));
+ if (unlikely(ret != 0))
+ goto out_unref;
+
+ page_no = offset >> PAGE_SHIFT;
+ if (unlikely(page_no >= bo->num_pages)) {
+ ret = -EINVAL;
+ DRM_ERROR("Illegal feedback buffer offset.\n");
+ goto out_unref;
+ }
+
+ if (unlikely(bo->ttm == NULL)) {
+ ret = -EINVAL;
+ DRM_ERROR("Vistest buffer without TTM.\n");
+ goto out_unref;
+ }
+
+ page = ttm_tt_get_page(bo->ttm, page_no);
+ if (unlikely(page == NULL)) {
+ ret = -ENOMEM;
+ goto out_unref;
+ }
+
+ feedback->page = page;
+ feedback->offset = page_offset;
+
+ /*
+ * Note: bo referece transferred.
+ */
+
+ feedback->bo = bo;
+ return 0;
+
+out_unref:
+ ttm_bo_unref(&bo);
+ return ret;
+}
+
+void psb_down_island_power(struct drm_device *dev, int islands)
+{
+ u32 pwr_cnt = 0;
+ pwr_cnt = MSG_READ32(PSB_PUNIT_PORT, PSB_PWRGT_CNT);
+ if (islands & PSB_GRAPHICS_ISLAND)
+ pwr_cnt |= 0x3;
+ if (islands & PSB_VIDEO_ENC_ISLAND)
+ pwr_cnt |= 0x30;
+ if (islands & PSB_VIDEO_DEC_ISLAND)
+ pwr_cnt |= 0xc;
+ MSG_WRITE32(PSB_PUNIT_PORT, PSB_PWRGT_CNT, pwr_cnt);
+}
+void psb_up_island_power(struct drm_device *dev, int islands)
+{
+ u32 pwr_cnt = 0;
+ u32 count = 5;
+ u32 pwr_sts = 0;
+ u32 pwr_mask = 0;
+ pwr_cnt = MSG_READ32(PSB_PUNIT_PORT, PSB_PWRGT_CNT);
+ if (islands & PSB_GRAPHICS_ISLAND) {
+ pwr_cnt &= ~PSB_PWRGT_GFX_MASK;
+ pwr_mask |= PSB_PWRGT_GFX_MASK;
+ }
+ if (islands & PSB_VIDEO_ENC_ISLAND) {
+ pwr_cnt &= ~PSB_PWRGT_VID_ENC_MASK;
+ pwr_mask |= PSB_PWRGT_VID_ENC_MASK;
+ }
+ if (islands & PSB_VIDEO_DEC_ISLAND) {
+ pwr_cnt &= ~PSB_PWRGT_VID_DEC_MASK;
+ pwr_mask |= PSB_PWRGT_VID_DEC_MASK;
+ }
+ MSG_WRITE32(PSB_PUNIT_PORT, PSB_PWRGT_CNT, pwr_cnt);
+ while (count--) {
+ pwr_sts = MSG_READ32(PSB_PUNIT_PORT, PSB_PWRGT_STS);
+ if ((pwr_sts & pwr_mask) == 0)
+ break;
+ else
+ udelay(10);
+ }
+}
+
+static int psb_power_down_sgx(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *)dev->dev_private;
+
+ PSB_DEBUG_PM("power down sgx \n");
+
+#ifdef OSPM_STAT
+ if (dev_priv->graphics_state == PSB_PWR_STATE_D0i0)
+ dev_priv->gfx_d0i0_time += jiffies - dev_priv->gfx_last_mode_change;
+ else
+ PSB_DEBUG_PM("power down:illegal previous power state\n");
+ dev_priv->gfx_last_mode_change = jiffies;
+ dev_priv->gfx_d0i3_cnt++;
+#endif
+
+ dev_priv->saveCLOCKGATING = PSB_RSGX32(PSB_CR_CLKGATECTL);
+ dev_priv->graphics_state = PSB_PWR_STATE_D0i3;
+ psb_down_island_power(dev, PSB_GRAPHICS_ISLAND);
+ return 0;
+}
+static int psb_power_up_sgx(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *)dev->dev_private;
+ if ((dev_priv->graphics_state & PSB_PWR_STATE_MASK) !=
+ PSB_PWR_STATE_D0i3)
+ return -EINVAL;
+
+ PSB_DEBUG_PM("power up sgx \n");
+ if (unlikely(PSB_D_PM & drm_psb_debug))
+ dump_stack();
+ INIT_LIST_HEAD(&dev_priv->resume_buf.head);
+
+ psb_up_island_power(dev, PSB_GRAPHICS_ISLAND);
+
+ /*
+ * The SGX loses it's register contents.
+ * Restore BIF registers. The MMU page tables are
+ * "normal" pages, so their contents should be kept.
+ */
+
+ PSB_WSGX32(dev_priv->saveCLOCKGATING, PSB_CR_CLKGATECTL);
+ PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK0);
+ PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK1);
+ PSB_RSGX32(PSB_CR_BIF_BANK1);
+
+ psb_mmu_set_pd_context(psb_mmu_get_default_pd(dev_priv->mmu), 0);
+ psb_mmu_set_pd_context(dev_priv->pf_pd, 1);
+ psb_mmu_enable_requestor(dev_priv->mmu, _PSB_MMU_ER_MASK);
+
+ /*
+ * 2D Base registers..
+ */
+ psb_init_2d(dev_priv);
+ /*
+ * Persistant 3D base registers and USSE base registers..
+ */
+
+ PSB_WSGX32(PSB_MEM_PDS_START, PSB_CR_PDS_EXEC_BASE);
+ PSB_WSGX32(PSB_MEM_RASTGEOM_START, PSB_CR_BIF_3D_REQ_BASE);
+ PSB_WSGX32(dev_priv->sgx2_irq_mask, PSB_CR_EVENT_HOST_ENABLE2);
+ PSB_WSGX32(dev_priv->sgx_irq_mask, PSB_CR_EVENT_HOST_ENABLE);
+ (void)PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE);
+ /*
+ * Now, re-initialize the 3D engine.
+ */
+ if (dev_priv->xhw_on)
+ psb_xhw_resume(dev_priv, &dev_priv->resume_buf);
+
+ psb_scheduler_ta_mem_check(dev_priv);
+ if (dev_priv->ta_mem && !dev_priv->force_ta_mem_load) {
+ psb_xhw_ta_mem_load(dev_priv, &dev_priv->resume_buf,
+ PSB_TA_MEM_FLAG_TA |
+ PSB_TA_MEM_FLAG_RASTER |
+ PSB_TA_MEM_FLAG_HOSTA |
+ PSB_TA_MEM_FLAG_HOSTD |
+ PSB_TA_MEM_FLAG_INIT,
+ dev_priv->ta_mem->ta_memory->offset,
+ dev_priv->ta_mem->hw_data->offset,
+ dev_priv->ta_mem->hw_cookie);
+ }
+
+#ifdef OSPM_STAT
+ if (dev_priv->graphics_state == PSB_PWR_STATE_D0i3)
+ dev_priv->gfx_d0i3_time += jiffies - dev_priv->gfx_last_mode_change;
+ else
+ PSB_DEBUG_PM("power up:illegal previous power state\n");
+ dev_priv->gfx_last_mode_change = jiffies;
+ dev_priv->gfx_d0i0_cnt++;
+#endif
+
+ dev_priv->graphics_state = PSB_PWR_STATE_D0i0;
+
+ return 0;
+}
+
+int psb_try_power_down_sgx(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *)dev->dev_private;
+ struct psb_scheduler *scheduler = &dev_priv->scheduler;
+ int ret;
+ if (!down_write_trylock(&dev_priv->sgx_sem))
+ return -EBUSY;
+ /*Try lock 2d, because FB driver ususally use 2D engine.*/
+ if (!psb_2d_trylock(dev_priv)) {
+ ret = -EBUSY;
+ goto out_err0;
+ }
+ if ((dev_priv->graphics_state & PSB_PWR_STATE_MASK) !=
+ PSB_PWR_STATE_D0i0) {
+ ret = -EINVAL;
+ goto out_err1;
+ }
+ if ((PSB_RSGX32(PSB_CR_2D_SOCIF) != _PSB_C2_SOCIF_EMPTY) ||
+ ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & _PSB_C2B_STATUS_BUSY) != 0)) {
+ ret = -EBUSY;
+ goto out_err1;
+ }
+ if (!scheduler->idle ||
+ !list_empty(&scheduler->raster_queue) ||
+ !list_empty(&scheduler->ta_queue) ||
+ !list_empty(&scheduler->hp_raster_queue)) {
+ ret = -EBUSY;
+ goto out_err1;
+ }
+ /*flush_scheduled_work();*/
+ ret = psb_power_down_sgx(dev);
+out_err1:
+ psb_2d_atomic_unlock(dev_priv);
+out_err0:
+ up_write(&dev_priv->sgx_sem);
+ return ret;
+}
+/*check power state, if in sleep, wake up*/
+void psb_check_power_state(struct drm_device *dev, int devices)
+{
+ struct pci_dev *pdev = dev->pdev;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ down(&dev_priv->pm_sem);
+ switch (pdev->current_state) {
+ case PCI_D3hot:
+ dev->driver->pci_driver.resume(pdev);
+ break;
+ default:
+
+ if (devices & PSB_DEVICE_SGX) {
+ if ((dev_priv->graphics_state & PSB_PWR_STATE_MASK) ==
+ PSB_PWR_STATE_D0i3) {
+ /*power up sgx*/
+ psb_power_up_sgx(dev);
+ }
+ } else if (devices & PSB_DEVICE_MSVDX) {
+ if ((dev_priv->msvdx_state & PSB_PWR_STATE_MASK) ==
+ PSB_PWR_STATE_D0i3) {
+ psb_power_up_msvdx(dev);
+ } else {
+ dev_priv->msvdx_last_action = jiffies;
+ }
+ }
+ break;
+ }
+ up(&dev_priv->pm_sem);
+}
+
+void psb_init_ospm(struct drm_psb_private *dev_priv)
+{
+ static int init;
+ if (!init) {
+ dev_priv->graphics_state = PSB_PWR_STATE_D0i0;
+ init_rwsem(&dev_priv->sgx_sem);
+ sema_init(&dev_priv->pm_sem, 1);
+#ifdef OSPM_STAT
+ dev_priv->gfx_last_mode_change = jiffies;
+ dev_priv->gfx_d0i0_time = 0;
+ dev_priv->gfx_d0i3_time = 0;
+ dev_priv->gfx_d3_time = 0;
+#endif
+ init = 1;
+ }
+}
+
+int psb_cmdbuf_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_psb_cmdbuf_arg *arg = data;
+ int ret = 0;
+ struct ttm_object_file *tfile = psb_fpriv(file_priv)->tfile;
+ struct ttm_buffer_object *cmd_buffer = NULL;
+ struct ttm_buffer_object *ta_buffer = NULL;
+ struct ttm_buffer_object *oom_buffer = NULL;
+ struct psb_ttm_fence_rep fence_arg;
+ struct drm_psb_scene user_scene;
+ struct psb_scene_pool *pool = NULL;
+ struct psb_scene *scene = NULL;
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *)file_priv->minor->dev->dev_private;
+ int engine;
+ struct psb_feedback_info feedback;
+ int po_correct;
+ struct psb_context *context;
+ unsigned num_buffers;
+
+ num_buffers = PSB_NUM_VALIDATE_BUFFERS;
+
+ ret = ttm_read_lock(&dev_priv->ttm_lock, true);
+ if (unlikely(ret != 0))
+ return ret;
+
+ if ((arg->engine == PSB_ENGINE_2D) || (arg->engine == PSB_ENGINE_TA)
+ || (arg->engine == PSB_ENGINE_RASTERIZER)) {
+ down_read(&dev_priv->sgx_sem);
+ psb_check_power_state(dev, PSB_DEVICE_SGX);
+ }
+
+ ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex);
+ if (unlikely(ret != 0))
+ goto out_err0;
+
+
+ context = &dev_priv->context;
+ context->used_buffers = 0;
+ context->fence_types = 0;
+ BUG_ON(!list_empty(&context->validate_list));
+ BUG_ON(!list_empty(&context->kern_validate_list));
+
+ if (unlikely(context->buffers == NULL)) {
+ context->buffers = vmalloc(PSB_NUM_VALIDATE_BUFFERS *
+ sizeof(*context->buffers));
+ if (unlikely(context->buffers == NULL)) {
+ ret = -ENOMEM;
+ goto out_err1;
+ }
+ }
+
+ ret = psb_reference_buffers(file_priv,
+ arg->buffer_list,
+ context);
+
+ if (unlikely(ret != 0))
+ goto out_err1;
+
+ context->val_seq = atomic_add_return(1, &dev_priv->val_seq);
+
+ ret = ttm_eu_reserve_buffers(&context->validate_list,
+ context->val_seq);
+ if (unlikely(ret != 0)) {
+ goto out_err2;
+ }
+
+ engine = (arg->engine == PSB_ENGINE_RASTERIZER) ?
+ PSB_ENGINE_TA : arg->engine;
+
+ ret = psb_validate_buffer_list(file_priv, engine,
+ context, &po_correct);
+ if (unlikely(ret != 0))
+ goto out_err3;
+
+ if (!po_correct) {
+ ret = psb_fixup_relocs(file_priv, engine, arg->num_relocs,
+ arg->reloc_offset,
+ arg->reloc_handle, context, 0, 1);
+ if (unlikely(ret != 0))
+ goto out_err3;
+
+ }
+
+ cmd_buffer = ttm_buffer_object_lookup(tfile, arg->cmdbuf_handle);
+ if (unlikely(cmd_buffer == NULL)) {
+ ret = -EINVAL;
+ goto out_err4;
+ }
+
+ switch (arg->engine) {
+ case PSB_ENGINE_2D:
+ ret = psb_cmdbuf_2d(file_priv, &context->validate_list,
+ context->fence_types, arg, cmd_buffer,
+ &fence_arg);
+ if (unlikely(ret != 0))
+ goto out_err4;
+ break;
+ case PSB_ENGINE_VIDEO:
+ psb_check_power_state(dev, PSB_DEVICE_MSVDX);
+ ret = psb_cmdbuf_video(file_priv, &context->validate_list,
+ context->fence_types, arg,
+ cmd_buffer, &fence_arg);
+
+ if (unlikely(ret != 0))
+ goto out_err4;
+ break;
+ case LNC_ENGINE_ENCODE:
+ psb_check_power_state(dev, PSB_DEVICE_TOPAZ);
+ ret = lnc_cmdbuf_video(file_priv, &context->validate_list,
+ context->fence_types, arg,
+ cmd_buffer, &fence_arg);
+ if (unlikely(ret != 0))
+ goto out_err4;
+ break;
+ case PSB_ENGINE_RASTERIZER:
+ ret = psb_cmdbuf_raster(file_priv, context,
+ arg, cmd_buffer, &fence_arg);
+ if (unlikely(ret != 0))
+ goto out_err4;
+ break;
+ case PSB_ENGINE_TA:
+ if (arg->ta_handle == arg->cmdbuf_handle) {
+ ta_buffer = ttm_bo_reference(cmd_buffer);
+ } else {
+ ta_buffer =
+ ttm_buffer_object_lookup(tfile,
+ arg->ta_handle);
+ if (!ta_buffer) {
+ ret = -EINVAL;
+ goto out_err4;
+ }
+ }
+ if (arg->oom_size != 0) {
+ if (arg->oom_handle == arg->cmdbuf_handle) {
+ oom_buffer = ttm_bo_reference(cmd_buffer);
+ } else {
+ oom_buffer =
+ ttm_buffer_object_lookup(tfile,
+ arg->
+ oom_handle);
+ if (!oom_buffer) {
+ ret = -EINVAL;
+ goto out_err4;
+ }
+ }
+ }
+
+ ret = copy_from_user(&user_scene, (void __user *)
+ ((unsigned long) arg->scene_arg),
+ sizeof(user_scene));
+ if (ret)
+ goto out_err4;
+
+ if (!user_scene.handle_valid) {
+ pool = psb_scene_pool_alloc(file_priv, 0,
+ user_scene.num_buffers,
+ user_scene.w,
+ user_scene.h);
+ if (!pool) {
+ ret = -ENOMEM;
+ goto out_err0;
+ }
+
+ user_scene.handle = psb_scene_pool_handle(pool);
+ user_scene.handle_valid = 1;
+ ret = copy_to_user((void __user *)
+ ((unsigned long) arg->
+ scene_arg), &user_scene,
+ sizeof(user_scene));
+
+ if (ret)
+ goto out_err4;
+ } else {
+ pool =
+ psb_scene_pool_lookup(file_priv,
+ user_scene.handle, 1);
+ if (!pool) {
+ ret = -EINVAL;
+ goto out_err4;
+ }
+ }
+
+ ret = psb_validate_scene_pool(context, pool,
+ user_scene.w,
+ user_scene.h,
+ arg->ta_flags &
+ PSB_TA_FLAG_LASTPASS, &scene);
+ if (ret)
+ goto out_err4;
+
+ memset(&feedback, 0, sizeof(feedback));
+ if (arg->feedback_ops) {
+ ret = psb_feedback_buf(tfile,
+ context,
+ arg->feedback_ops,
+ arg->feedback_handle,
+ arg->feedback_offset,
+ arg->feedback_breakpoints,
+ arg->feedback_size,
+ &feedback);
+ if (ret)
+ goto out_err4;
+ }
+ ret = psb_cmdbuf_ta(file_priv, context,
+ arg, cmd_buffer, ta_buffer,
+ oom_buffer, scene, &feedback,
+ &fence_arg);
+ if (ret)
+ goto out_err4;
+ break;
+ default:
+ DRM_ERROR
+ ("Unimplemented command submission mechanism (%x).\n",
+ arg->engine);
+ ret = -EINVAL;
+ goto out_err4;
+ }
+
+ if (!(arg->fence_flags & DRM_PSB_FENCE_NO_USER)) {
+ ret = copy_to_user((void __user *)
+ ((unsigned long) arg->fence_arg),
+ &fence_arg, sizeof(fence_arg));
+ }
+
+out_err4:
+ if (scene)
+ psb_scene_unref(&scene);
+ if (pool)
+ psb_scene_pool_unref(&pool);
+ if (cmd_buffer)
+ ttm_bo_unref(&cmd_buffer);
+ if (ta_buffer)
+ ttm_bo_unref(&ta_buffer);
+ if (oom_buffer)
+ ttm_bo_unref(&oom_buffer);
+out_err3:
+ ret = psb_handle_copyback(dev, context, ret);
+out_err2:
+ psb_unreference_buffers(context);
+out_err1:
+ mutex_unlock(&dev_priv->cmdbuf_mutex);
+out_err0:
+ ttm_read_unlock(&dev_priv->ttm_lock);
+ if ((arg->engine == PSB_ENGINE_2D) || (arg->engine == PSB_ENGINE_TA)
+ || (arg->engine == PSB_ENGINE_RASTERIZER))
+ up_read(&dev_priv->sgx_sem);
+ return ret;
+}