aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/cdv/imgv/psb_msvdx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/cdv/imgv/psb_msvdx.c')
-rw-r--r--drivers/staging/cdv/imgv/psb_msvdx.c1451
1 files changed, 1451 insertions, 0 deletions
diff --git a/drivers/staging/cdv/imgv/psb_msvdx.c b/drivers/staging/cdv/imgv/psb_msvdx.c
new file mode 100644
index 000000000000..8a50f4b5d9ed
--- /dev/null
+++ b/drivers/staging/cdv/imgv/psb_msvdx.c
@@ -0,0 +1,1451 @@
+/**************************************************************************
+ * MSVDX I/O operations and IRQ handling
+ *
+ * Copyright (c) 2011 Intel Corporation, Hillsboro, OR, USA
+ * Copyright (c) Imagination Technologies Limited, UK
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include <drm/drmP.h>
+#include "psb_drm.h"
+#include "psb_drv.h"
+#include "psb_msvdx.h"
+#include "psb_powermgmt.h"
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#ifndef list_first_entry
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+#endif
+
+#define DRM_MPEG2_DELAY 125
+
+static int psb_msvdx_send(struct drm_device *dev, void *cmd,
+ unsigned long cmd_size);
+
+static int psb_msvdx_dequeue_send(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct psb_msvdx_cmd_queue *msvdx_cmd = NULL;
+ int ret = 0;
+ struct msvdx_private *msvdx_priv = dev_priv->msvdx_private;
+
+ if (list_empty(&msvdx_priv->msvdx_queue)) {
+ PSB_DEBUG_GENERAL("MSVDXQUE: msvdx list empty.\n");
+ msvdx_priv->msvdx_busy = 0;
+ return -EINVAL;
+ }
+ msvdx_cmd = list_first_entry(&msvdx_priv->msvdx_queue,
+ struct psb_msvdx_cmd_queue, head);
+ PSB_DEBUG_GENERAL("MSVDXQUE: Queue has id %08x\n", msvdx_cmd->sequence);
+ ret = psb_msvdx_send(dev, msvdx_cmd->cmd, msvdx_cmd->cmd_size);
+ if (ret) {
+ DRM_ERROR("MSVDXQUE: psb_msvdx_send failed\n");
+ ret = -EINVAL;
+ }
+ list_del(&msvdx_cmd->head);
+ kfree(msvdx_cmd->cmd);
+ kfree(msvdx_cmd);
+
+ return ret;
+}
+
+static int psb_msvdx_map_command(struct drm_device *dev,
+ struct ttm_buffer_object *cmd_buffer,
+ unsigned long cmd_offset, unsigned long cmd_size,
+ void **msvdx_cmd, uint32_t sequence, int copy_cmd)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct msvdx_private *msvdx_priv = dev_priv->msvdx_private;
+ int ret = 0;
+ unsigned long cmd_page_offset = cmd_offset & ~PAGE_MASK;
+ unsigned long cmd_size_remaining;
+ struct ttm_bo_kmap_obj cmd_kmap, regio_kmap;
+ void *cmd, *cmd_copy, *cmd_start;
+ bool is_iomem;
+ int i;
+ struct HOST_BE_OPP_PARAMS * oppParam;
+ drm_psb_msvdx_frame_info_t * current_frame = NULL;
+ int first_empty = -1;
+
+
+ /* command buffers may not exceed page boundary */
+ if (cmd_size + cmd_page_offset > PAGE_SIZE)
+ return -EINVAL;
+
+ ret = ttm_bo_kmap(cmd_buffer, cmd_offset >> PAGE_SHIFT, 1, &cmd_kmap);
+ if (ret) {
+ DRM_ERROR("MSVDXQUE:ret:%d\n", ret);
+ return ret;
+ }
+
+ cmd_start = (void *)ttm_kmap_obj_virtual(&cmd_kmap, &is_iomem)
+ + cmd_page_offset;
+ cmd = cmd_start;
+ cmd_size_remaining = cmd_size;
+
+ while (cmd_size_remaining > 0) {
+ uint32_t cur_cmd_size = MEMIO_READ_FIELD(cmd, FWRK_GENMSG_SIZE);
+ uint32_t cur_cmd_id = MEMIO_READ_FIELD(cmd, FWRK_GENMSG_ID);
+ uint32_t mmu_ptd = 0, msvdx_mmu_invalid = 0;
+ struct psb_msvdx_deblock_queue *msvdx_deblock;
+ unsigned long irq_flags;
+
+ PSB_DEBUG_GENERAL("cmd start at %08x cur_cmd_size = %d"
+ " cur_cmd_id = %02x fence = %08x\n",
+ (uint32_t) cmd, cur_cmd_size, cur_cmd_id, sequence);
+ if ((cur_cmd_size % sizeof(uint32_t))
+ || (cur_cmd_size > cmd_size_remaining)) {
+ ret = -EINVAL;
+ DRM_ERROR("MSVDX: ret:%d\n", ret);
+ goto out;
+ }
+
+ switch (cur_cmd_id) {
+ case VA_MSGID_RENDER:
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: send render message. \n");
+
+ /* Fence ID */
+ if((IS_CDV(dev)) && IS_FW_UPDATED)
+ MEMIO_WRITE_FIELD(cmd, FW_VA_DECODE_MSG_ID, sequence);
+ else
+ MEMIO_WRITE_FIELD(cmd, FW_VA_RENDER_FENCE_VALUE, sequence);
+
+ mmu_ptd = psb_get_default_pd_addr(dev_priv->mmu);
+ msvdx_mmu_invalid = atomic_cmpxchg(&dev_priv->msvdx_mmu_invaldc,
+ 1, 0);
+ if (msvdx_mmu_invalid == 1) {
+ if(!(IS_CDV(dev) && IS_FW_UPDATED))
+ mmu_ptd |= 1;
+ else {
+ uint32_t flags;
+ flags = MEMIO_READ_FIELD(cmd, FW_DEVA_DECODE_FLAGS);
+ flags |= FW_DEVA_INVALIDATE_MMU;
+ MEMIO_WRITE_FIELD(cmd, FW_DEVA_DECODE_FLAGS, flags);
+ }
+
+ PSB_DEBUG_GENERAL("MSVDX:Set MMU invalidate\n");
+ }
+
+ /* PTD */
+ if((IS_CDV(dev) ) && IS_FW_UPDATED) {
+ uint32_t context_id;
+ context_id = MEMIO_READ_FIELD(cmd, FW_VA_DECODE_MMUPTD);
+ mmu_ptd = mmu_ptd | (context_id & 0xff);
+ MEMIO_WRITE_FIELD(cmd, FW_VA_DECODE_MMUPTD, mmu_ptd);
+ }
+ else
+ MEMIO_WRITE_FIELD(cmd, FW_VA_RENDER_MMUPTD, mmu_ptd);
+ break;
+
+ case VA_MSGID_OOLD:
+ MEMIO_WRITE_FIELD(cmd, FW_DXVA_OOLD_FENCE_VALUE,
+ sequence);
+ mmu_ptd = psb_get_default_pd_addr(dev_priv->mmu);
+ msvdx_mmu_invalid = atomic_cmpxchg(&dev_priv->msvdx_mmu_invaldc,
+ 1, 0);
+ if (msvdx_mmu_invalid == 1) {
+ mmu_ptd |= 1;
+ PSB_DEBUG_GENERAL("MSVDX:Set MMU invalidate\n");
+ }
+
+ /* PTD */
+ MEMIO_WRITE_FIELD(cmd, FW_DXVA_OOLD_MMUPTD, mmu_ptd);
+
+ PSB_DEBUG_GENERAL("MSVDX:Get oold cmd\n");
+
+ break;
+
+ case VA_MSGID_OOLD_MFLD:
+ case VA_MSGID_DEBLOCK_MFLD: {
+ FW_VA_DEBLOCK_MSG * deblock_msg;
+
+ PSB_DEBUG_GENERAL("MSVDX:Get deblock cmd for medfield\n");
+
+ deblock_msg = (FW_VA_DEBLOCK_MSG *)cmd;
+
+ mmu_ptd = psb_get_default_pd_addr(dev_priv->mmu);
+ msvdx_mmu_invalid = atomic_cmpxchg(&dev_priv->msvdx_mmu_invaldc,
+ 1, 0);
+ if (msvdx_mmu_invalid == 1) {
+ uint32_t flags;
+ flags = deblock_msg->flags;
+ flags |= FW_DEVA_INVALIDATE_MMU;
+ deblock_msg->flags = flags;
+
+ PSB_DEBUG_GENERAL("MSVDX:Set MMU invalidate\n");
+ }
+
+
+ deblock_msg->header.bits.msg_type = cur_cmd_id - VA_MSGID_DEBLOCK_MFLD + VA_MSGID_DEBLOCK; /* patch to right cmd type */
+ deblock_msg->header.bits.msg_fence = (uint16_t)(sequence & 0xffff);
+ deblock_msg->mmu_context.bits.mmu_ptd = (mmu_ptd >> 8);
+
+ }
+ break;
+
+ case VA_MSGID_DEBLOCK:
+ msvdx_priv->deblock_enabled = 1;
+ /* Fence ID */
+ MEMIO_WRITE_FIELD(cmd, FW_DXVA_DEBLOCK_FENCE_VALUE,
+ sequence);
+ mmu_ptd = psb_get_default_pd_addr(dev_priv->mmu);
+ msvdx_mmu_invalid = atomic_cmpxchg(&dev_priv->msvdx_mmu_invaldc,
+ 1, 0);
+ if (msvdx_mmu_invalid == 1) {
+ mmu_ptd |= 1;
+ PSB_DEBUG_GENERAL("MSVDX:Set MMU invalidate\n");
+ }
+
+ /* PTD */
+ MEMIO_WRITE_FIELD(cmd,
+ FW_DXVA_DEBLOCK_MMUPTD,
+ mmu_ptd);
+
+ /* printk("Got deblock msg\n"); */
+ /* Deblock message is followed by 32 */
+ /* bytes of deblock params */
+ msvdx_deblock = kmalloc(
+ sizeof(struct psb_msvdx_deblock_queue),
+ GFP_KERNEL);
+
+ if (msvdx_deblock == NULL) {
+ DRM_ERROR("DEBLOCK QUE: Out of memory...\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(&msvdx_deblock->dbParams, cmd + 16, sizeof(struct DEBLOCKPARAMS));
+
+ ret = ttm_bo_kmap(
+ (struct ttm_buffer_object *)
+ msvdx_deblock->dbParams.handle,
+ 0,
+ (msvdx_deblock->dbParams.buffer_size +
+ PAGE_SIZE - 1) >> PAGE_SHIFT,
+ &regio_kmap);
+
+ /* printk("deblock regio buffer size is 0x%x\n",
+ msvdx_deblock->dbParams.buffer_size); */
+
+ if (likely(!ret)) {
+ msvdx_deblock->dbParams.pPicparams = kmalloc(
+ msvdx_deblock->dbParams.buffer_size,
+ GFP_KERNEL);
+
+ if (msvdx_deblock->dbParams.pPicparams != NULL)
+ memcpy(
+ msvdx_deblock->dbParams.pPicparams,
+ regio_kmap.virtual,
+ msvdx_deblock->dbParams.buffer_size);
+ ttm_bo_kunmap(&regio_kmap);
+ }
+ spin_lock_irqsave(&msvdx_priv->msvdx_lock, irq_flags);
+ list_add_tail(&msvdx_deblock->head,
+ &msvdx_priv->deblock_queue);
+ spin_unlock_irqrestore(&msvdx_priv->msvdx_lock,
+ irq_flags);
+
+ cmd += sizeof(struct DEBLOCKPARAMS);
+ cmd_size_remaining -= sizeof(struct DEBLOCKPARAMS);
+ break;
+
+ case VA_MSGID_HOST_BE_OPP:
+
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: send host_be_opp message. \n");
+
+ msvdx_priv->host_be_opp_enabled = 1;
+ /* Fence ID */
+ MEMIO_WRITE_FIELD(cmd, FW_VA_HOST_BE_OPP_FENCE_VALUE,
+ sequence);
+ mmu_ptd = psb_get_default_pd_addr(dev_priv->mmu);
+ msvdx_mmu_invalid = atomic_cmpxchg(&dev_priv->msvdx_mmu_invaldc,
+ 1, 0);
+ if (msvdx_mmu_invalid == 1) {
+ mmu_ptd |= 1;
+ PSB_DEBUG_GENERAL("MSVDX:Set MMU invalidate\n");
+ }
+
+ /* PTD */
+ MEMIO_WRITE_FIELD(cmd,
+ FW_VA_HOST_BE_OPP_MMUPTD,
+ mmu_ptd);
+
+ /* HostBeOpp message is followed by 32 bytes of host_be_opp params */
+ oppParam = kmalloc(
+ sizeof(struct HOST_BE_OPP_PARAMS),
+ GFP_KERNEL);
+
+ if (oppParam == NULL) {
+ DRM_ERROR("DEBLOCK QUE: Out of memory...\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(oppParam, cmd + 16, sizeof(struct HOST_BE_OPP_PARAMS));
+ /*get the right frame_info struct for current surface*/
+ for (i = 0; i < MAX_DECODE_BUFFERS; i++) {
+ if (msvdx_priv->frame_info[i].handle == oppParam->handle) {
+ current_frame = &(msvdx_priv->frame_info[i]);
+ break;
+ }
+ if ((first_empty == -1) && (msvdx_priv->frame_info[i].handle == 0))
+ first_empty = i;
+ }
+
+ /*if didn't find the struct for current surface, use the earliest empty one*/
+ if (!current_frame) {
+ if (first_empty == -1) {
+ DRM_ERROR("failed find the struct for current surface and also there is no empty one.\n");
+ ret = -EFAULT;
+ goto out;
+ }
+ current_frame = &(msvdx_priv->frame_info[first_empty]);
+ }
+
+ memset(current_frame, 0, sizeof(drm_psb_msvdx_frame_info_t));
+ current_frame->handle = oppParam->handle;
+ current_frame->buffer_stride = oppParam->buffer_stride;
+ current_frame->buffer_size = oppParam->buffer_size;
+ current_frame->picture_width_mb = oppParam->picture_width_mb;
+ current_frame->size_mb = oppParam->size_mb;
+ current_frame->fence = sequence;
+
+
+ cmd += sizeof(struct HOST_BE_OPP_PARAMS);
+ cmd_size_remaining -= sizeof(struct HOST_BE_OPP_PARAMS);
+
+ break;
+
+ default:
+ /* Msg not supported */
+ ret = -EINVAL;
+ PSB_DEBUG_GENERAL("MSVDX: ret:%d\n", ret);
+ goto out;
+ }
+
+ cmd += cur_cmd_size;
+ cmd_size_remaining -= cur_cmd_size;
+ }
+
+ if (copy_cmd) {
+ PSB_DEBUG_GENERAL("MSVDXQUE:copying command\n");
+
+ cmd_copy = kzalloc(cmd_size, GFP_KERNEL);
+ if (cmd_copy == NULL) {
+ ret = -ENOMEM;
+ DRM_ERROR("MSVDX: fail to callc,ret=:%d\n", ret);
+ goto out;
+ }
+ memcpy(cmd_copy, cmd_start, cmd_size);
+ *msvdx_cmd = cmd_copy;
+ } else {
+ PSB_DEBUG_GENERAL("MSVDXQUE:did NOT copy command\n");
+ ret = psb_msvdx_send(dev, cmd_start, cmd_size);
+ if (ret) {
+ DRM_ERROR("MSVDXQUE: psb_msvdx_send failed\n");
+ ret = -EINVAL;
+ }
+ }
+
+out:
+ ttm_bo_kunmap(&cmd_kmap);
+
+ return ret;
+}
+
+int psb_submit_video_cmdbuf(struct drm_device *dev,
+ struct ttm_buffer_object *cmd_buffer,
+ unsigned long cmd_offset, unsigned long cmd_size,
+ struct ttm_fence_object *fence)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ uint32_t sequence = dev_priv->sequence[PSB_ENGINE_VIDEO];
+ unsigned long irq_flags;
+ int ret = 0;
+ struct msvdx_private *msvdx_priv = dev_priv->msvdx_private;
+ int offset = 0;
+ /* psb_schedule_watchdog(dev_priv); */
+
+ spin_lock_irqsave(&msvdx_priv->msvdx_lock, irq_flags);
+
+ /* expected msvdx_needs_reset is set after previous session exited
+ * but msvdx_hw_busy is always 1, and caused powerdown not excuted
+ * so reload the firmware for every new context
+ */
+
+ dev_priv->last_msvdx_ctx = dev_priv->msvdx_ctx;
+
+ if (msvdx_priv->msvdx_needs_reset) {
+ spin_unlock_irqrestore(&msvdx_priv->msvdx_lock, irq_flags);
+ PSB_DEBUG_GENERAL("MSVDX: will reset msvdx\n");
+ if (psb_msvdx_reset(dev_priv)) {
+ ret = -EBUSY;
+ DRM_ERROR("MSVDX: Reset failed\n");
+ return ret;
+ }
+ msvdx_priv->msvdx_needs_reset = 0;
+ msvdx_priv->msvdx_busy = 0;
+
+ if(psb_msvdx_init(dev)) {
+ ret = -EIO;
+ DRM_ERROR("MSVDX: failed in MSVDX Initialization\n");
+ return ret;
+ }
+
+ /* restore vec local mem if needed */
+ if (msvdx_priv->vec_local_mem_saved &&
+ msvdx_priv->vec_local_mem_data) {
+ for (offset = 0; offset < VEC_LOCAL_MEM_BYTE_SIZE / 4; ++offset)
+ PSB_WMSVDX32(msvdx_priv->vec_local_mem_data[offset],
+ VEC_LOCAL_MEM_OFFSET + offset * 4);
+
+ msvdx_priv->vec_local_mem_saved = 0;
+ }
+
+ spin_lock_irqsave(&msvdx_priv->msvdx_lock, irq_flags);
+ }
+
+ if (!msvdx_priv->msvdx_fw_loaded) {
+ spin_unlock_irqrestore(&msvdx_priv->msvdx_lock, irq_flags);
+ PSB_DEBUG_GENERAL("MSVDX:reload FW to MTX\n");
+
+ ret = psb_setup_fw(dev);
+ if (ret) {
+ DRM_ERROR("MSVDX:fail to load FW\n");
+ /* FIXME: find a proper return value */
+ return -EFAULT;
+ }
+ msvdx_priv->msvdx_fw_loaded = 1;
+
+ PSB_DEBUG_GENERAL("MSVDX: load firmware successfully\n");
+ spin_lock_irqsave(&msvdx_priv->msvdx_lock, irq_flags);
+ }
+
+ if (!msvdx_priv->msvdx_busy) {
+ msvdx_priv->msvdx_busy = 1;
+ spin_unlock_irqrestore(&msvdx_priv->msvdx_lock, irq_flags);
+ PSB_DEBUG_GENERAL("MSVDX: commit command to HW,seq=0x%08x\n",
+ sequence);
+ ret = psb_msvdx_map_command(dev, cmd_buffer, cmd_offset,
+ cmd_size, NULL, sequence, 0);
+ if (ret) {
+ DRM_ERROR("MSVDXQUE: Failed to extract cmd\n");
+ return ret;
+ }
+ } else {
+ struct psb_msvdx_cmd_queue *msvdx_cmd;
+ void *cmd = NULL;
+
+ spin_unlock_irqrestore(&msvdx_priv->msvdx_lock, irq_flags);
+ /* queue the command to be sent when the h/w is ready */
+ PSB_DEBUG_GENERAL("MSVDXQUE: queueing sequence:%08x..\n",
+ sequence);
+ msvdx_cmd = kzalloc(sizeof(struct psb_msvdx_cmd_queue),
+ GFP_KERNEL);
+ if (msvdx_cmd == NULL) {
+ DRM_ERROR("MSVDXQUE: Out of memory...\n");
+ return -ENOMEM;
+ }
+
+ ret = psb_msvdx_map_command(dev, cmd_buffer, cmd_offset,
+ cmd_size, &cmd, sequence, 1);
+ if (ret) {
+ DRM_ERROR("MSVDXQUE: Failed to extract cmd\n");
+ kfree(msvdx_cmd
+ );
+ return ret;
+ }
+ msvdx_cmd->cmd = cmd;
+ msvdx_cmd->cmd_size = cmd_size;
+ msvdx_cmd->sequence = sequence;
+ spin_lock_irqsave(&msvdx_priv->msvdx_lock, irq_flags);
+ list_add_tail(&msvdx_cmd->head, &msvdx_priv->msvdx_queue);
+ if (!msvdx_priv->msvdx_busy) {
+ msvdx_priv->msvdx_busy = 1;
+ PSB_DEBUG_GENERAL("MSVDXQUE: Need immediate dequeue\n");
+ psb_msvdx_dequeue_send(dev);
+ }
+ spin_unlock_irqrestore(&msvdx_priv->msvdx_lock, irq_flags);
+ }
+
+ return ret;
+}
+
+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);
+ spin_lock(&cmd_buffer->bdev->fence_lock);
+ if (cmd_buffer->sync_obj != NULL)
+ ttm_fence_sync_obj_unref(&cmd_buffer->sync_obj);
+ spin_unlock(&cmd_buffer->bdev->fence_lock);
+
+ return 0;
+}
+
+
+static int psb_msvdx_send(struct drm_device *dev, void *cmd,
+ unsigned long cmd_size)
+{
+ int ret = 0;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct psb_video_ctx *msvdx_ctx = NULL;
+ int ctx_type;
+
+ while (cmd_size > 0) {
+ uint32_t cur_cmd_size = MEMIO_READ_FIELD(cmd, FWRK_GENMSG_SIZE);
+ uint32_t cur_cmd_id = MEMIO_READ_FIELD(cmd, FWRK_GENMSG_ID);
+ if (cur_cmd_size > cmd_size) {
+ ret = -EINVAL;
+ DRM_ERROR("MSVDX:cmd_size %lu cur_cmd_size %lu\n",
+ cmd_size, (unsigned long)cur_cmd_size);
+ goto out;
+ }
+
+ /* Send the message to h/w */
+ ret = psb_mtx_send(dev_priv, cmd);
+ if (ret) {
+ PSB_DEBUG_GENERAL("MSVDX: ret:%d\n", ret);
+ goto out;
+ }
+ cmd += cur_cmd_size;
+ cmd_size -= cur_cmd_size;
+ if (cur_cmd_id == VA_MSGID_DEBLOCK && (!IS_CDV(dev) && IS_FW_UPDATED)) {
+ cmd += sizeof(struct DEBLOCKPARAMS);
+ cmd_size -= sizeof(struct DEBLOCKPARAMS);
+ }
+ if (cur_cmd_id == VA_MSGID_HOST_BE_OPP) {
+ cmd += sizeof(struct HOST_BE_OPP_PARAMS);
+ cmd_size -= sizeof(struct HOST_BE_OPP_PARAMS);
+ }
+ if(cmd_size && IS_CDV(dev)) {
+ msvdx_ctx = dev_priv->msvdx_ctx;
+ /* Get the Profile for the current video context */
+ ctx_type = msvdx_ctx->ctx_type >> 8;
+ if (ctx_type == VAProfileMPEG2Simple || ctx_type == VAProfileMPEG2Main)
+ udelay(DRM_MPEG2_DELAY);
+ else
+ udelay(drm_msvdx_delay);
+ }
+ }
+
+out:
+ PSB_DEBUG_GENERAL("MSVDX: ret:%d\n", ret);
+ return ret;
+}
+
+int psb_mtx_send(struct drm_psb_private *dev_priv, const void *msg)
+{
+ static uint32_t pad_msg[FWRK_PADMSG_SIZE];
+ const uint32_t *p_msg = (uint32_t *) msg;
+ uint32_t msg_num, words_free, ridx, widx, buf_size, buf_offset;
+ int ret = 0;
+
+ PSB_DEBUG_GENERAL("MSVDX: psb_mtx_send\n");
+
+ /* we need clocks enabled before we touch VEC local ram */
+ PSB_WMSVDX32(clk_enable_all, MSVDX_MAN_CLK_ENABLE);
+
+ msg_num = (MEMIO_READ_FIELD(msg, FWRK_GENMSG_SIZE) + 3) / 4;
+
+/*
+ {
+ int i;
+ printk("MSVDX: psb_mtx_send is %dDW\n", msg_num);
+
+ for(i = 0; i < msg_num; i++)
+ printk("0x%08x ", p_msg[i]);
+ printk("\n");
+ }
+*/
+ buf_size = PSB_RMSVDX32(MSVDX_COMMS_TO_MTX_BUF_SIZE) & ((1 << 16) - 1);
+
+ if (msg_num > buf_size) {
+ ret = -EINVAL;
+ DRM_ERROR("MSVDX: message exceed maximum,ret:%d\n", ret);
+ goto out;
+ }
+
+ ridx = PSB_RMSVDX32(MSVDX_COMMS_TO_MTX_RD_INDEX);
+ widx = PSB_RMSVDX32(MSVDX_COMMS_TO_MTX_WRT_INDEX);
+
+
+ buf_size = PSB_RMSVDX32(MSVDX_COMMS_TO_MTX_BUF_SIZE) & ((1 << 16) - 1);
+ /*0x2000 is VEC Local Ram offset*/
+ buf_offset =
+ (PSB_RMSVDX32(MSVDX_COMMS_TO_MTX_BUF_SIZE) >> 16) + 0x2000;
+
+ /* message would wrap, need to send a pad message */
+ if (widx + msg_num > buf_size) {
+ /* Shouldn't happen for a PAD message itself */
+ BUG_ON(MEMIO_READ_FIELD(msg, FWRK_GENMSG_ID)
+ == FWRK_MSGID_PADDING);
+
+ /* if the read pointer is at zero then we must wait for it to
+ * change otherwise the write pointer will equal the read
+ * pointer,which should only happen when the buffer is empty
+ *
+ * This will only happens if we try to overfill the queue,
+ * queue management should make
+ * sure this never happens in the first place.
+ */
+ BUG_ON(0 == ridx);
+ if (0 == ridx) {
+ ret = -EINVAL;
+ DRM_ERROR("MSVDX: RIndex=0, ret:%d\n", ret);
+ goto out;
+ }
+
+ /* Send a pad message */
+ MEMIO_WRITE_FIELD(pad_msg, FWRK_GENMSG_SIZE,
+ (buf_size - widx) << 2);
+ MEMIO_WRITE_FIELD(pad_msg, FWRK_GENMSG_ID,
+ FWRK_MSGID_PADDING);
+ psb_mtx_send(dev_priv, pad_msg);
+ widx = PSB_RMSVDX32(MSVDX_COMMS_TO_MTX_WRT_INDEX);
+ }
+
+ if (widx >= ridx)
+ words_free = buf_size - (widx - ridx) - 1;
+ else
+ words_free = ridx - widx - 1;
+
+ BUG_ON(msg_num > words_free);
+ if (msg_num > words_free) {
+ ret = -EINVAL;
+ DRM_ERROR("MSVDX: msg_num > words_free, ret:%d\n", ret);
+ goto out;
+ }
+ while (msg_num > 0) {
+ PSB_WMSVDX32(*p_msg++, buf_offset + (widx << 2));
+ msg_num--;
+ widx++;
+ if (buf_size == widx)
+ widx = 0;
+ }
+
+ PSB_WMSVDX32(widx, MSVDX_COMMS_TO_MTX_WRT_INDEX);
+
+ /* Make sure clocks are enabled before we kick */
+ PSB_WMSVDX32(clk_enable_all, MSVDX_MAN_CLK_ENABLE);
+
+ PSB_WMSVDX32(clk_enable_all, MSVDX_MAN_CLK_ENABLE);
+
+ /* signal an interrupt to let the mtx know there is a new message */
+ /* PSB_WMSVDX32(1, MSVDX_MTX_KICKI); */
+ PSB_WMSVDX32(1, MSVDX_MTX_KICK);
+
+ /* Read MSVDX Register several times in case Idle signal assert */
+ PSB_RMSVDX32(MSVDX_INTERRUPT_STATUS);
+ PSB_RMSVDX32(MSVDX_INTERRUPT_STATUS);
+ PSB_RMSVDX32(MSVDX_INTERRUPT_STATUS);
+ PSB_RMSVDX32(MSVDX_INTERRUPT_STATUS);
+
+
+out:
+ return ret;
+}
+
+static int psb_msvdx_towpass_deblock(struct drm_device *dev,
+ uint32_t *pPicparams)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *)dev->dev_private;
+ uint32_t cmd_size, cmd_count = 0;
+ uint32_t cmd_id, reg, value, wait, reg_value, read = 0, ret = 0;
+
+ cmd_size = *pPicparams++;
+ PSB_DEBUG_GENERAL("MSVDX DEBLOCK: deblock get cmd size %d\n", cmd_size);
+ /* printk("MSVDX DEBLOCK: deblock get cmd size %d\n", cmd_size); */
+
+ do {
+ cmd_id = (*pPicparams) & 0xf0000000;
+ reg = (*pPicparams++) & 0x0fffffff;
+ switch (cmd_id) {
+ case MSVDX_DEBLOCK_REG_SET: {
+ value = *pPicparams++;
+ PSB_WMSVDX32(value, reg);
+ cmd_count += 2;
+ break;
+ }
+ case MSVDX_DEBLOCK_REG_GET: {
+ read = PSB_RMSVDX32(reg);
+ cmd_count += 1;
+ break;
+ }
+ case MSVDX_DEBLOCK_REG_POLLn: {
+ value = *pPicparams++;
+ wait = 0;
+
+ do {
+ reg_value = PSB_RMSVDX32(reg);
+ } while ((wait++ < 20000) && (value > reg_value));
+
+ if (wait >= 20000) {
+ ret = 1;
+ PSB_DEBUG_GENERAL(
+ "MSVDX DEBLOCK: polln cmd space time out!\n");
+ goto finish_deblock;
+ }
+ cmd_count += 2;
+ break;
+ }
+ case MSVDX_DEBLOCK_REG_POLLx: {
+ wait = 0;
+
+ do {
+ reg_value = PSB_RMSVDX32(reg);
+ } while ((wait++ < 20000) && (read > reg_value));
+
+ if (wait >= 20000) {
+ ret = 1;
+ PSB_DEBUG_GENERAL(
+ "MSVDX DEBLOCK: pollx cmd space time out!\n");
+ goto finish_deblock;
+ }
+
+ cmd_count += 1;
+ break;
+ }
+ default:
+ ret = 1;
+ PSB_DEBUG_GENERAL(
+ "MSVDX DEBLOCK: get error cmd_id: 0x%x!\n",
+ cmd_id);
+ PSB_DEBUG_GENERAL(
+ "MSVDX DEBLOCK: execute cmd num is %d\n",
+ cmd_count);
+ /* printk("MSVDX DEBLOCK: get error cmd_id: 0x%x!\n",
+ cmd_id); */
+ /* printk("MSVDX DEBLOCK: execute cmd num is %d\n",
+ cmd_count); */
+ goto finish_deblock;
+ }
+ } while (cmd_count < cmd_size);
+
+
+finish_deblock:
+ PSB_DEBUG_GENERAL("MSVDX DEBLOCK: execute cmd num is %d\n", cmd_count);
+ return ret;
+}
+
+/*
+ * MSVDX MTX interrupt
+ */
+static void psb_msvdx_mtx_interrupt(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *)dev->dev_private;
+ static uint32_t buf[128]; /* message buffer */
+ uint32_t ridx, widx, buf_size, buf_offset;
+ uint32_t num, ofs; /* message num and offset */
+ struct msvdx_private *msvdx_priv = dev_priv->msvdx_private;
+ int i;
+
+ PSB_DEBUG_GENERAL("MSVDX:Got a MSVDX MTX interrupt\n");
+
+ /* Are clocks enabled - If not enable before
+ * attempting to read from VLR
+ */
+ if (PSB_RMSVDX32(MSVDX_MAN_CLK_ENABLE) != (clk_enable_all)) {
+ PSB_DEBUG_GENERAL("MSVDX:Clocks disabled when Interupt set\n");
+ PSB_WMSVDX32(clk_enable_all, MSVDX_MAN_CLK_ENABLE);
+ }
+
+loop: /* just for coding style check */
+ ridx = PSB_RMSVDX32(MSVDX_COMMS_TO_HOST_RD_INDEX);
+ widx = PSB_RMSVDX32(MSVDX_COMMS_TO_HOST_WRT_INDEX);
+
+ /* Get out of here if nothing */
+ if (ridx == widx)
+ goto done;
+
+ buf_size = PSB_RMSVDX32(MSVDX_COMMS_TO_HOST_BUF_SIZE) & ((1 << 16) - 1);
+ /*0x2000 is VEC Local Ram offset*/
+ buf_offset =
+ (PSB_RMSVDX32(MSVDX_COMMS_TO_HOST_BUF_SIZE) >> 16) + 0x2000;
+
+ ofs = 0;
+ buf[ofs] = PSB_RMSVDX32(buf_offset + (ridx << 2));
+
+ /* round to nearest word */
+ num = (MEMIO_READ_FIELD(buf, FWRK_GENMSG_SIZE) + 3) / 4;
+
+ /* ASSERT(num <= sizeof(buf) / sizeof(uint32_t)); */
+
+ if (++ridx >= buf_size)
+ ridx = 0;
+
+ for (ofs++; ofs < num; ofs++) {
+ buf[ofs] = PSB_RMSVDX32(buf_offset + (ridx << 2));
+
+ if (++ridx >= buf_size)
+ ridx = 0;
+ }
+
+ /* Update the Read index */
+ PSB_WMSVDX32(ridx, MSVDX_COMMS_TO_HOST_RD_INDEX);
+
+ if (msvdx_priv->msvdx_needs_reset)
+ goto loop;
+
+ switch (MEMIO_READ_FIELD(buf, FWRK_GENMSG_ID)) {
+ case VA_MSGID_CMD_HW_PANIC:
+ case VA_MSGID_CMD_FAILED: {
+ /* For VXD385 firmware, fence value is not validate here */
+ uint32_t msg_id = MEMIO_READ_FIELD(buf, FWRK_GENMSG_ID);
+ uint32_t diff = 0;
+ uint32_t fence, fault = 0;
+ drm_psb_msvdx_frame_info_t *failed_frame = NULL;
+
+ if (msg_id == VA_MSGID_CMD_HW_PANIC)
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: get panic message.\n");
+ else
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: get failed message.\n");
+
+ if (msg_id == VA_MSGID_CMD_HW_PANIC) {
+ fence = MEMIO_READ_FIELD(buf,
+ FW_VA_HW_PANIC_FENCE_VALUE);
+ printk("jiangfei debug: fence in panic message is %d.\n", fence);
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: PANIC MESSAGE fence is %d.\n", MEMIO_READ_FIELD(buf, FW_VA_HW_PANIC_FENCE_VALUE));
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: PANIC MESSAGE first mb num is %d.\n", MEMIO_READ_FIELD(buf, FW_VA_HW_PANIC_FIRST_MB_NUM));
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: PANIC MESSAGE fault mb num is %d.\n", MEMIO_READ_FIELD(buf, FW_VA_HW_PANIC_FAULT_MB_NUM));
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: PANIC MESSAGE fe status is 0x%x.\n", MEMIO_READ_FIELD(buf, FW_VA_HW_PANIC_FESTATUS));
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: PANIC MESSAGE be status is 0x%x.\n", MEMIO_READ_FIELD(buf, FW_VA_HW_PANIC_BESTATUS));
+ } else {
+ fence = MEMIO_READ_FIELD(buf,
+ FW_VA_CMD_FAILED_FENCE_VALUE);
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: FAILED MESSAGE fence is %d.\n", MEMIO_READ_FIELD(buf, FW_VA_HW_PANIC_FIRST_MB_NUM));
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: FAILED MESSAGE flag is %d.\n", MEMIO_READ_FIELD(buf, FW_VA_CMD_FAILED_FLAGS));
+ }
+
+ if(IS_CDV(dev) && IS_FW_UPDATED) {
+ fence = MEMIO_READ_FIELD(buf, FW_DEVA_CMD_FAILED_MSG_ID);
+ fault = MEMIO_READ_FIELD(buf, FW_DEVA_CMD_FAILED_FLAGS);
+ }
+
+ if (msg_id == VA_MSGID_CMD_HW_PANIC)
+ PSB_DEBUG_GENERAL("MSVDX: VA_MSGID_CMD_HW_PANIC:"
+ "Fault detected"
+ " - Fence: %08x"
+ " - Flags: %08x"
+ " - resetting and ignoring error\n",
+ fence, fault);
+ else
+ PSB_DEBUG_GENERAL("MSVDX: VA_MSGID_CMD_FAILED:"
+ "Fault detected"
+ " - Fence: %08x"
+ " - Flags: %08x"
+ " - resetting and ignoring error\n",
+ fence, fault);
+
+ msvdx_priv->msvdx_needs_reset = 1;
+
+ if (msg_id == VA_MSGID_CMD_HW_PANIC) {
+ diff = msvdx_priv->msvdx_current_sequence
+ - dev_priv->sequence[PSB_ENGINE_VIDEO];
+
+ if (diff > 0x0FFFFFFF)
+ msvdx_priv->msvdx_current_sequence++;
+
+ PSB_DEBUG_GENERAL("MSVDX: Fence ID missing, "
+ "assuming %08x\n",
+ msvdx_priv->msvdx_current_sequence);
+ } else {
+ msvdx_priv->msvdx_current_sequence = fence;
+ }
+
+ psb_fence_error(dev, PSB_ENGINE_VIDEO,
+ msvdx_priv->msvdx_current_sequence,
+ _PSB_FENCE_TYPE_EXE, DRM_CMD_FAILED);
+
+ /* Flush the command queue */
+ psb_msvdx_flush_cmd_queue(dev);
+
+ if (msvdx_priv->host_be_opp_enabled) {
+ /*get the frame_info struct for error concealment frame*/
+ for (i = 0; i < MAX_DECODE_BUFFERS; i++) {
+ /*by default the fence is 0, so there is problem here???*/
+ if (msvdx_priv->frame_info[i].fence == fence) {
+ failed_frame = &msvdx_priv->frame_info[i];
+ break;
+ }
+ }
+ if (!failed_frame) {
+ DRM_ERROR("MSVDX: didn't find frame_info which matched the fence %d in failed/panic message\n", fence);
+ goto done;
+ }
+
+ failed_frame->fw_status = 1; /* set ERROR flag */
+ } else
+ msvdx_priv->fw_status = 1; /* set ERROR flag */
+
+ goto done;
+ }
+ case VA_MSGID_CMD_COMPLETED: {
+ uint32_t fence;
+ uint32_t flags = MEMIO_READ_FIELD(buf, FW_VA_CMD_COMPLETED_FLAGS);
+ /* uint32_t last_mb = MEMIO_READ_FIELD(buf,
+ FW_VA_CMD_COMPLETED_LASTMB);
+ */
+
+ if(IS_CDV(dev) && IS_FW_UPDATED)
+ fence = MEMIO_READ_FIELD(buf, FW_VA_CMD_COMPLETED_MSG_ID);
+ else
+ fence = MEMIO_READ_FIELD(buf, FW_VA_CMD_COMPLETED_FENCE_VALUE);
+
+ PSB_DEBUG_GENERAL("MSVDX:VA_MSGID_CMD_COMPLETED: "
+ "FenceID: %08x, flags: 0x%x\n",
+ fence, flags);
+
+ msvdx_priv->msvdx_current_sequence = fence;
+ msvdx_priv->ref_pic_fence = fence;
+
+ psb_fence_handler(dev, PSB_ENGINE_VIDEO);
+
+ if (flags & FW_VA_RENDER_HOST_INT) {
+ /*Now send the next command from the msvdx cmd queue */
+ psb_msvdx_dequeue_send(dev);
+ goto done;
+ }
+
+ break;
+ }
+ case VA_MSGID_CMD_COMPLETED_BATCH: {
+ uint32_t fence = MEMIO_READ_FIELD(buf,
+ FW_VA_CMD_COMPLETED_FENCE_VALUE);
+ uint32_t tickcnt = MEMIO_READ_FIELD(buf,
+ FW_VA_CMD_COMPLETED_NO_TICKS);
+ (void)tickcnt;
+ /* we have the fence value in the message */
+ PSB_DEBUG_GENERAL("MSVDX:VA_MSGID_CMD_COMPLETED_BATCH:"
+ " FenceID: %08x, TickCount: %08x\n",
+ fence, tickcnt);
+ msvdx_priv->msvdx_current_sequence = fence;
+
+ break;
+ }
+ case VA_MSGID_ACK:
+ PSB_DEBUG_GENERAL("MSVDX: VA_MSGID_ACK\n");
+ break;
+
+ case VA_MSGID_TEST1:
+ PSB_DEBUG_GENERAL("MSVDX: VA_MSGID_TEST1\n");
+ break;
+
+ /* Penwell deblock is not implemented here */
+ case VA_MSGID_DEBLOCK_REQUIRED: {
+ uint32_t ctxid = MEMIO_READ_FIELD(buf,
+ FW_VA_DEBLOCK_REQUIRED_CONTEXT);
+ struct psb_msvdx_deblock_queue *msvdx_deblock;
+ uint32_t fence = MEMIO_READ_FIELD(buf,
+ FW_VA_DEBLOCK_REQUIRED_FENCE_VALUE);
+
+ PSB_DEBUG_GENERAL("MSVDX: VA_MSGID_DEBLOCK_REQUIRED"
+ " Context=%08x\n", ctxid);
+
+
+ /*deblock and on-be-opp use the same message, there is difficulty to distinguish them*/
+ /*now I just let user space use cpu copy error mb*/
+ if ((msvdx_priv->deblock_enabled == 1) && (msvdx_priv->host_be_opp_enabled == 1)) {
+ DRM_ERROR("MSVDX: should not support both deblock and host_be_opp message. \n");
+ goto done;
+ } else if (msvdx_priv->deblock_enabled == 1) {
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: get deblock required message for deblock operation.\n");
+ if (list_empty(&msvdx_priv->deblock_queue)) {
+ PSB_DEBUG_GENERAL(
+ "DEBLOCKQUE: deblock param list is empty\n");
+ PSB_WMSVDX32(0, MSVDX_CMDS_END_SLICE_PICTURE);
+ PSB_WMSVDX32(1, MSVDX_CMDS_END_SLICE_PICTURE);
+ goto done;
+ }
+ msvdx_deblock = list_first_entry(&msvdx_priv->deblock_queue,
+ struct psb_msvdx_deblock_queue, head);
+
+ if (0) {
+ PSB_DEBUG_GENERAL("MSVDX DEBLOCK: by pass \n");
+ /* try to unblock rendec */
+ PSB_WMSVDX32(0, MSVDX_CMDS_END_SLICE_PICTURE);
+ PSB_WMSVDX32(1, MSVDX_CMDS_END_SLICE_PICTURE);
+ kfree(msvdx_deblock->dbParams.pPicparams);
+ list_del(&msvdx_deblock->head);
+ goto done;
+ }
+
+
+ if (ctxid != msvdx_deblock->dbParams.ctxid) {
+ PSB_DEBUG_GENERAL("MSVDX DEBLOCK: wrong ctxid, may "
+ "caused by multiple context since "
+ "it's not supported yet\n");
+ /* try to unblock rendec */
+ PSB_WMSVDX32(0, MSVDX_CMDS_END_SLICE_PICTURE);
+ PSB_WMSVDX32(1, MSVDX_CMDS_END_SLICE_PICTURE);
+ kfree(msvdx_deblock->dbParams.pPicparams);
+ list_del(&msvdx_deblock->head);
+ goto done;
+ }
+
+ if (msvdx_deblock->dbParams.pPicparams) {
+ PSB_DEBUG_GENERAL("MSVDX DEBLOCK: start deblocking\n");
+ /* printk("MSVDX DEBLOCK: start deblocking\n"); */
+
+ if (psb_msvdx_towpass_deblock(dev,
+ msvdx_deblock->dbParams.pPicparams)) {
+
+ PSB_DEBUG_GENERAL(
+ "MSVDX DEBLOCK: deblock fail!\n");
+ PSB_WMSVDX32(0, MSVDX_CMDS_END_SLICE_PICTURE);
+ PSB_WMSVDX32(1, MSVDX_CMDS_END_SLICE_PICTURE);
+ }
+ kfree(msvdx_deblock->dbParams.pPicparams);
+ } else {
+ PSB_DEBUG_GENERAL("MSVDX DEBLOCK: deblock abort!\n");
+ /* printk("MSVDX DEBLOCK: deblock abort!\n"); */
+ PSB_WMSVDX32(0, MSVDX_CMDS_END_SLICE_PICTURE);
+ PSB_WMSVDX32(1, MSVDX_CMDS_END_SLICE_PICTURE);
+ }
+
+ list_del(&msvdx_deblock->head);
+ kfree(msvdx_deblock);
+ } else if (msvdx_priv->host_be_opp_enabled == 1) {
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: get deblock required message for error concealment.\n");
+
+ /* try to unblock rendec */
+ PSB_WMSVDX32(0, MSVDX_CMDS_END_SLICE_PICTURE);
+ PSB_WMSVDX32(1, MSVDX_CMDS_END_SLICE_PICTURE);
+
+ /*do error concealment with hw*/
+ msvdx_priv->ec_fence = fence;
+ schedule_work(&msvdx_priv->ec_work);
+ }
+
+ break;
+ }
+
+ case VA_MSGID_CMD_CONTIGUITY_WARNING: {
+ drm_psb_msvdx_frame_info_t *ec_frame = NULL;
+ drm_psb_msvdx_decode_status_t *fault_region = NULL;
+
+ /*get erro info*/
+ uint32_t fence = MEMIO_READ_FIELD(buf,
+ FW_VA_CONTIGUITY_WARNING_FENCE_VALUE);
+ uint32_t ui32Start = MEMIO_READ_FIELD(buf,
+ FW_VA_CONTIGUITY_WARNING_BEGIN_MB_NUM);
+ uint32_t ui32End = MEMIO_READ_FIELD(buf,
+ FW_VA_CONTIGUITY_WARNING_END_MB_NUM);
+ PSB_DEBUG_GENERAL("MSVDX: VA_MSGID_CMD_CONTIGUITY_WARNING\n");
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: get contiguity warning message.\n");
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: CONTIGUITY_WARNING MESSAGE fence is %d.\n", MEMIO_READ_FIELD(buf, FW_VA_CONTIGUITY_WARNING_FENCE_VALUE));
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: CONTIGUITY_WARNING MESSAGE end mb num is %d.\n", MEMIO_READ_FIELD(buf, FW_VA_CONTIGUITY_WARNING_END_MB_NUM));
+ PSB_DEBUG_MSVDX("MSVDX_DEBUG: CONTIGUITY_WARNING MESSAGE begin mb num is %d.\n", MEMIO_READ_FIELD(buf, FW_VA_CONTIGUITY_WARNING_BEGIN_MB_NUM));
+
+ /*get the frame_info struct for error concealment frame*/
+ for (i = 0; i < MAX_DECODE_BUFFERS; i++) {
+ if (msvdx_priv->frame_info[i].fence == fence) {
+ ec_frame = &msvdx_priv->frame_info[i];
+ break;
+ }
+ }
+ if (!ec_frame) {
+ DRM_ERROR("MSVDX: didn't find frame_info which matched the fence %d when get contiguity warning.\n", fence);
+ goto done;
+ }
+ else if (msvdx_priv->host_be_opp_enabled){
+ ec_frame->fw_status = 1;
+ fault_region = &ec_frame->decode_status;
+ if (ui32Start == 0xffff) {
+ if (fault_region->num_error_slice == 0) {
+ fault_region->start_error_mb_list[fault_region->num_error_slice] = 0;
+ fault_region->end_error_mb_list[fault_region->num_error_slice] = ui32End;
+ fault_region->num_error_slice++;
+ }
+ else if (fault_region->end_error_mb_list[fault_region->num_error_slice - 1] == 0xffff) {
+ fault_region->end_error_mb_list[fault_region->num_error_slice - 1] = ui32End;
+ }
+ }
+ else if (ui32Start < ui32End) {
+ fault_region->start_error_mb_list[fault_region->num_error_slice] = ui32Start;
+ fault_region->end_error_mb_list[fault_region->num_error_slice] = ui32End;
+ fault_region->num_error_slice++;
+ }
+ else {
+ DRM_ERROR("msvdx error: start mb counter should not be larger than end mb counter.\n");
+ goto done;
+ }
+ }
+
+ break;
+
+ }
+ default:
+ DRM_ERROR("ERROR: msvdx Unknown message from MTX, ID:0x%08x\n", MEMIO_READ_FIELD(buf, FWRK_GENMSG_ID));
+ goto done;
+ }
+
+done:
+ if (ridx != widx) {
+ PSB_DEBUG_GENERAL("MSVDX Interrupt: there are more message to be read\n");
+ goto loop;
+ }
+ /* we get a frame/slice done, try to save some power*/
+ if (drm_msvdx_pmpolicy != PSB_PMPOLICY_NOPM)
+ schedule_delayed_work(&dev_priv->scheduler.msvdx_suspend_wq, 0);
+
+ DRM_MEMORYBARRIER(); /* TBD check this... */
+}
+
+
+/*
+ * MSVDX interrupt.
+ */
+IMG_BOOL psb_msvdx_interrupt(IMG_VOID *pvData)
+{
+ struct drm_device *dev;
+ struct drm_psb_private *dev_priv;
+ struct msvdx_private *msvdx_priv;
+ uint32_t msvdx_stat;
+
+ if (pvData == IMG_NULL) {
+ DRM_ERROR("ERROR: msvdx %s, Invalid params\n", __func__);
+ return IMG_FALSE;
+ }
+
+ dev = (struct drm_device *)pvData;
+
+
+ dev_priv = (struct drm_psb_private *) dev->dev_private;
+ msvdx_priv = dev_priv->msvdx_private;
+
+ msvdx_priv->msvdx_hw_busy = REG_READ(0x20D0) & (0x1 << 9);
+
+ msvdx_stat = PSB_RMSVDX32(MSVDX_INTERRUPT_STATUS);
+
+ if (msvdx_stat & MSVDX_INTERRUPT_STATUS_CR_MMU_FAULT_IRQ_MASK) {
+ /*Ideally we should we should never get to this */
+ PSB_DEBUG_IRQ("MSVDX:MMU Fault:0x%x\n", msvdx_stat);
+
+ /* Pause MMU */
+ PSB_WMSVDX32(MSVDX_MMU_CONTROL0_CR_MMU_PAUSE_MASK,
+ MSVDX_MMU_CONTROL0);
+ DRM_WRITEMEMORYBARRIER();
+
+ /* Clear this interupt bit only */
+ PSB_WMSVDX32(MSVDX_INTERRUPT_STATUS_CR_MMU_FAULT_IRQ_MASK,
+ MSVDX_INTERRUPT_CLEAR);
+ PSB_RMSVDX32(MSVDX_INTERRUPT_CLEAR);
+ DRM_READMEMORYBARRIER();
+
+ msvdx_priv->msvdx_needs_reset = 1;
+ } else if (msvdx_stat & MSVDX_INTERRUPT_STATUS_CR_MTX_IRQ_MASK) {
+ PSB_DEBUG_IRQ
+ ("MSVDX: msvdx_stat: 0x%x(MTX)\n", msvdx_stat);
+
+ /* Clear all interupt bits */
+ PSB_WMSVDX32(0xffff, MSVDX_INTERRUPT_CLEAR);
+ PSB_RMSVDX32(MSVDX_INTERRUPT_CLEAR);
+ DRM_READMEMORYBARRIER();
+
+ psb_msvdx_mtx_interrupt(dev);
+ }
+
+ return IMG_TRUE;
+}
+
+
+void psb_msvdx_lockup(struct drm_psb_private *dev_priv,
+ int *msvdx_lockup, int *msvdx_idle)
+{
+ int diff;
+ struct msvdx_private *msvdx_priv = dev_priv->msvdx_private;
+
+ *msvdx_lockup = 0;
+ *msvdx_idle = 1;
+
+#if 0
+ PSB_DEBUG_GENERAL("MSVDXTimer: current_sequence:%d "
+ "last_sequence:%d and last_submitted_sequence :%d\n",
+ msvdx_priv->msvdx_current_sequence,
+ msvdx_priv->msvdx_last_sequence,
+ dev_priv->sequence[PSB_ENGINE_VIDEO]);
+#endif
+
+ diff = msvdx_priv->msvdx_current_sequence -
+ dev_priv->sequence[PSB_ENGINE_VIDEO];
+
+ if (diff > 0x0FFFFFFF) {
+ if (msvdx_priv->msvdx_current_sequence ==
+ msvdx_priv->msvdx_last_sequence) {
+ DRM_ERROR("MSVDXTimer:locked-up for sequence:%d\n",
+ msvdx_priv->msvdx_current_sequence);
+ *msvdx_lockup = 1;
+ } else {
+ PSB_DEBUG_GENERAL("MSVDXTimer: "
+ "msvdx responded fine so far\n");
+ msvdx_priv->msvdx_last_sequence =
+ msvdx_priv->msvdx_current_sequence;
+ *msvdx_idle = 0;
+ }
+ }
+}
+
+int psb_check_msvdx_idle(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *)dev->dev_private;
+ struct msvdx_private *msvdx_priv = dev_priv->msvdx_private;
+ /* drm_psb_msvdx_frame_info_t *current_frame = NULL; */
+
+ if (msvdx_priv->msvdx_fw_loaded == 0)
+ return 0;
+
+ if (msvdx_priv->msvdx_busy) {
+ PSB_DEBUG_PM("MSVDX: psb_check_msvdx_idle returns busy\n");
+ return -EBUSY;
+ }
+/*
+ if (msvdx_priv->msvdx_hw_busy) {
+ PSB_DEBUG_PM("MSVDX: %s, HW is busy\n", __func__);
+ return -EBUSY;
+ }
+*/
+ return 0;
+}
+
+
+int psb_remove_videoctx(struct drm_psb_private *dev_priv, struct file *filp)
+{
+ struct psb_video_ctx *pos, *n;
+
+ list_for_each_entry_safe(pos, n, &dev_priv->video_ctx, head) {
+ if (pos->filp == filp) {
+ PSB_DEBUG_GENERAL("Video:remove context profile %d,"
+ " entrypoint %d",
+ (pos->ctx_type >> 8),
+ (pos->ctx_type & 0xff));
+
+ if (dev_priv->msvdx_ctx == pos)
+ dev_priv->msvdx_ctx = NULL;
+ if (dev_priv->last_msvdx_ctx == pos)
+ dev_priv->last_msvdx_ctx = NULL;
+
+ list_del(&pos->head);
+ kfree(pos);
+ }
+ }
+ return 0;
+}
+
+
+int lnc_video_getparam(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_lnc_video_getparam_arg *arg = data;
+ int ret = 0;
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *)file_priv->minor->dev->dev_private;
+ drm_psb_msvdx_frame_info_t *current_frame = NULL;
+ uint32_t handle, i;
+
+ void *rar_handler;
+ uint32_t offset = 0;
+ uint32_t device_info = 0;
+ uint32_t ctx_type = 0;
+ struct psb_video_ctx *video_ctx = NULL;
+ uint32_t rar_ci_info[2];
+ struct msvdx_private *msvdx_priv = dev_priv->msvdx_private;
+
+ switch (arg->key) {
+ case LNC_VIDEO_GETPARAM_RAR_INFO:
+ rar_ci_info[0] = dev_priv->rar_region_start;
+ rar_ci_info[1] = dev_priv->rar_region_size;
+ ret = copy_to_user((void __user *) ((unsigned long)arg->value),
+ &rar_ci_info[0],
+ sizeof(rar_ci_info));
+ break;
+ case LNC_VIDEO_GETPARAM_CI_INFO:
+ rar_ci_info[0] = dev_priv->ci_region_start;
+ rar_ci_info[1] = dev_priv->ci_region_size;
+ ret = copy_to_user((void __user *) ((unsigned long)arg->value),
+ &rar_ci_info[0],
+ sizeof(rar_ci_info));
+ break;
+ case LNC_VIDEO_GETPARAM_RAR_HANDLER_OFFSET:
+ ret = copy_from_user(&rar_handler,
+ (void __user *)((unsigned long)arg->arg),
+ sizeof(rar_handler));
+ if (ret)
+ break;
+
+ ret = copy_to_user((void __user *)((unsigned long)arg->value),
+ &offset,
+ sizeof(offset));
+ break;
+ case LNC_VIDEO_FRAME_SKIP:
+ if(IS_CDV(dev)) /* CDV should not call it */
+ ret = -EFAULT;
+ break;
+ case LNC_VIDEO_DEVICE_INFO:
+ device_info = 0xffff & dev_priv->video_device_fuse;
+ device_info |= (0xffff & dev->pci_device) << 16;
+
+ ret = copy_to_user((void __user *) ((unsigned long)arg->value),
+ &device_info, sizeof(device_info));
+ break;
+ case IMG_VIDEO_NEW_CONTEXT:
+ /* add video decode/encode context */
+ ret = copy_from_user(&ctx_type, (void __user *) ((unsigned long)arg->value),
+ sizeof(ctx_type));
+ /* Failure in copy */
+ if (ret) {
+ DRM_ERROR("lnc_video_nex_context copy_from_user error.\n");
+ break;
+ }
+
+ video_ctx = kmalloc(sizeof(struct psb_video_ctx), GFP_KERNEL);
+ if (video_ctx == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
+ INIT_LIST_HEAD(&video_ctx->head);
+ video_ctx->ctx_type = ctx_type;
+ video_ctx->filp = file_priv->filp;
+ list_add(&video_ctx->head, &dev_priv->video_ctx);
+ PSB_DEBUG_GENERAL("Video:add context profile %d, entrypoint %d",
+ (ctx_type >> 8), (ctx_type & 0xff));
+ break;
+
+ case IMG_VIDEO_RM_CONTEXT:
+ psb_remove_videoctx(dev_priv, file_priv->filp);
+ break;
+ case IMG_VIDEO_DECODE_STATUS:
+ if (msvdx_priv->host_be_opp_enabled) {
+ /*get the right frame_info struct for current surface*/
+ ret = copy_from_user(&handle,
+ (void __user *)((unsigned long)arg->arg), 4);
+ if (ret) {
+ DRM_ERROR("MSVDX in lnc_video_getparam, copy_from_user failed.\n");
+ break;
+ }
+
+ for (i = 0; i < MAX_DECODE_BUFFERS; i++) {
+ if (msvdx_priv->frame_info[i].handle == handle) {
+ current_frame = &msvdx_priv->frame_info[i];
+ break;
+ }
+ }
+ if (!current_frame) {
+ DRM_ERROR("MSVDX: didn't find frame_info which matched the surface_id. \n");
+ return -EFAULT;
+ }
+ ret = copy_to_user((void __user *) ((unsigned long)arg->value),
+ &current_frame->fw_status, sizeof(current_frame->fw_status));
+ } else
+ ret = copy_to_user((void __user *) ((unsigned long)arg->value),
+ &msvdx_priv->fw_status, sizeof(msvdx_priv->fw_status));
+ break;
+
+ case IMG_VIDEO_MB_ERROR:
+ /*get the right frame_info struct for current surface*/
+ ret = copy_from_user(&handle,
+ (void __user *)((unsigned long)arg->arg), 4);
+ if (ret)
+ break;
+
+ for (i = 0; i < MAX_DECODE_BUFFERS; i++) {
+ if (msvdx_priv->frame_info[i].handle == handle) {
+ current_frame = &msvdx_priv->frame_info[i];
+ break;
+ }
+ }
+ if (!current_frame) {
+ DRM_ERROR("MSVDX: didn't find frame_info which matched the surface_id. \n");
+ return -EFAULT;
+ }
+ ret = copy_to_user((void __user *) ((unsigned long)arg->value),
+ &(current_frame->decode_status), sizeof(drm_psb_msvdx_decode_status_t));
+ if (ret) {
+ DRM_ERROR("lnc_video_getparam copy_to_user error.\n");
+ return -EFAULT;
+ }
+ break;
+
+
+ default:
+ ret = -EFAULT;
+ break;
+ }
+
+ if (ret)
+ return -EFAULT;
+
+ return 0;
+}
+
+inline int psb_try_power_down_msvdx(struct drm_device *dev)
+{
+ ospm_apm_power_down_msvdx(dev);
+ return 0;
+}
+
+int psb_msvdx_save_context(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *)dev->dev_private;
+ struct msvdx_private *msvdx_priv = dev_priv->msvdx_private;
+ int offset = 0;
+
+ msvdx_priv->msvdx_needs_reset = 1;
+
+ if (msvdx_priv->vec_local_mem_data) {
+ for (offset = 0; offset < VEC_LOCAL_MEM_BYTE_SIZE / 4; ++offset)
+ msvdx_priv->vec_local_mem_data[offset] =
+ PSB_RMSVDX32(VEC_LOCAL_MEM_OFFSET + offset * 4);
+
+ msvdx_priv->vec_local_mem_saved = 1;
+ }
+ return 0;
+}
+
+int psb_msvdx_restore_context(struct drm_device *dev)
+{
+ return 0;
+}