/** * file psb_msvdx_ec.c * MSVDX error concealment I/O operations * */ /************************************************************************** * * Copyright (c) 2007 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. * * Authors: * Li Zeng * **************************************************************************/ #ifdef CONFIG_DRM_VXD_BYT #include "vxd_drv.h" #else #include "psb_drv.h" #endif #include "psb_msvdx.h" #include "psb_msvdx_msg.h" #include "psb_msvdx_reg.h" #include "psb_msvdx_ec.h" #define MAX_SIZE_IN_MB (4096 / 16) static inline int psb_msvdx_cmd_port_write(struct drm_psb_private *dev_priv, uint32_t offset, uint32_t value, uint32_t *cmd_space) { uint32_t max_attempts = 0xff; uint32_t attempts = 0; max_attempts = 0xff; while (*cmd_space == 0) { *cmd_space = PSB_RMSVDX32( MSVDX_CORE_CR_MSVDX_COMMAND_SPACE_OFFSET + MSVDX_CORE_BASE); if (*cmd_space) break; PSB_UDELAY(2); attempts++; if (attempts > max_attempts) { printk(KERN_ERR "MSVDX: poll cmd space timeout\n"); return -1; } } PSB_WMSVDX32(value, offset + MSVDX_CMDS_BASE); (*cmd_space)--; /* *printk(KERN_DEBUG "MSVDX: poll cmd space attempts %d\n", attempts); */ return 0; } #define PSB_CMDPORT_WRITE(_dev_priv_, _offset_, _cmd_, _cmd_space_) \ do { \ ret = psb_msvdx_cmd_port_write(_dev_priv_, \ _offset_, _cmd_, &_cmd_space_); \ if (ret) { \ printk(KERN_DEBUG "write cmd fail, abort\n"); \ goto ec_done; \ } \ } while (0); #define PSB_CMDPORT_WRITE_FAST(_dev_priv_, _offset_, _cmd_, _cmd_space_) \ psb_msvdx_cmd_port_write(_dev_priv_, \ _offset_, _cmd_, &_cmd_space_); \ void psb_msvdx_do_concealment(struct work_struct *work) { struct msvdx_private *msvdx_priv = container_of(work, struct msvdx_private, ec_work); struct drm_psb_private *dev_priv = NULL; struct psb_msvdx_ec_ctx *msvdx_ec_ctx = msvdx_priv->cur_msvdx_ec_ctx; drm_psb_msvdx_decode_status_t *fault_region = NULL; struct fw_deblock_msg *deblock_msg = (struct fw_deblock_msg *)(msvdx_ec_ctx->unfenced_cmd + msvdx_ec_ctx->deblock_cmd_offset); uint32_t width_in_mb, height_in_mb, cmd; int conceal_above_row = 0, loop, mb_loop; uint32_t cmd_space = 0; int ret = 0; #ifdef CONFIG_VIDEO_MRFLD if (!power_island_get(OSPM_VIDEO_DEC_ISLAND)) { #else if (!ospm_power_using_video_begin(OSPM_VIDEO_DEC_ISLAND)) { #endif printk(KERN_ERR "MSVDX: fail to power on ved for ec\n"); return; } dev_priv = msvdx_priv->dev_priv; fault_region = &msvdx_ec_ctx->decode_status; /* Concealment should be done in time, * otherwise panic msg will be signaled in msvdx */ preempt_disable(); if (msvdx_ec_ctx->deblock_cmd_offset == PSB_MSVDX_INVALID_OFFSET) { printk(KERN_ERR "invalid msg offset, abort concealment\n"); goto ec_done; } if (fault_region->num_region == 0) { PSB_DEBUG_MSVDX("no fault region\n"); goto ec_done; } width_in_mb = deblock_msg->pic_size.bits.pic_width_mb; height_in_mb = deblock_msg->pic_size.bits.frame_height_mb; { int i; for (i = 0; i < fault_region->num_region; i++) PSB_DEBUG_MSVDX("[region %d] is %d to %d\n", i, fault_region->mb_regions[i].start, fault_region->mb_regions[i].end); PSB_DEBUG_MSVDX("MSVDX: MSGID_DEBLOCK:" " - fence: %08x" " - flags: %08x - slice_field_type: %08x" " - operating_mode: %08x" " - context: %08x - mmu_ptd: %08x" " - frame_height_mb: %08x - pic_width_mb: %08x" " - address_a0: %08x - address_a1: %08x" " - mb_param_address: %08x" " - ext_stride_a: %08x" " - address_b0: %08x - address_b1: %08x" " - alt_output_flags_b: %08x.\n", deblock_msg->header.bits.msg_fence, deblock_msg->flag_type.bits.flags, deblock_msg->flag_type.bits.slice_field_type, deblock_msg->operating_mode, deblock_msg->mmu_context.bits.context, deblock_msg->mmu_context.bits.mmu_ptd, deblock_msg->pic_size.bits.frame_height_mb, deblock_msg->pic_size.bits.pic_width_mb, deblock_msg->address_a0, deblock_msg->address_a1, deblock_msg->mb_param_address, deblock_msg->ext_stride_a, deblock_msg->address_b0, deblock_msg->address_b1, deblock_msg->alt_output_flags_b); PSB_DEBUG_MSVDX("deblock addr_c0 is 0x%08x\n", deblock_msg->address_c0); PSB_DEBUG_MSVDX("deblock addr_c1 is 0x%08x\n", deblock_msg->address_c1); } if (unlikely(!width_in_mb || !height_in_mb || width_in_mb > MAX_SIZE_IN_MB || height_in_mb > MAX_SIZE_IN_MB)) { PSB_DEBUG_MSVDX("wrong pic size\n"); goto ec_done; } cmd = 0; REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS, DISPLAY_PICTURE_SIZE_DISPLAY_PICTURE_HEIGHT, (height_in_mb * 16) - 1); REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS, DISPLAY_PICTURE_SIZE_DISPLAY_PICTURE_WIDTH, (width_in_mb * 16) - 1); PSB_CMDPORT_WRITE(dev_priv, MSVDX_CMDS_DISPLAY_PICTURE_SIZE_OFFSET, cmd, cmd_space); cmd = 0; REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS, CODED_PICTURE_SIZE_CODED_PICTURE_HEIGHT, (height_in_mb * 16) - 1); REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS, CODED_PICTURE_SIZE_CODED_PICTURE_WIDTH, (width_in_mb * 16) - 1); PSB_CMDPORT_WRITE(dev_priv, MSVDX_CMDS_CODED_PICTURE_SIZE_OFFSET, cmd, cmd_space); cmd = deblock_msg->operating_mode; REGIO_WRITE_FIELD(cmd, MSVDX_CMDS_OPERATING_MODE, CHROMA_FORMAT, 1); REGIO_WRITE_FIELD(cmd, MSVDX_CMDS_OPERATING_MODE, ASYNC_MODE, 1); REGIO_WRITE_FIELD(cmd, MSVDX_CMDS_OPERATING_MODE, CODEC_MODE, 3); REGIO_WRITE_FIELD(cmd, MSVDX_CMDS_OPERATING_MODE, CODEC_PROFILE, 1); PSB_CMDPORT_WRITE(dev_priv, MSVDX_CMDS_OPERATING_MODE_OFFSET, cmd, cmd_space); /* dest frame address */ PSB_CMDPORT_WRITE(dev_priv, MSVDX_CMDS_LUMA_RECONSTRUCTED_PICTURE_BASE_ADDRESSES_OFFSET, deblock_msg->address_a0, cmd_space); PSB_CMDPORT_WRITE(dev_priv, MSVDX_CMDS_CHROMA_RECONSTRUCTED_PICTURE_BASE_ADDRESSES_OFFSET, deblock_msg->address_a1, cmd_space); /* conceal frame address */ PSB_CMDPORT_WRITE(dev_priv, MSVDX_CMDS_REFERENCE_PICTURE_BASE_ADDRESSES_OFFSET, deblock_msg->address_b0, cmd_space); PSB_CMDPORT_WRITE(dev_priv, MSVDX_CMDS_REFERENCE_PICTURE_BASE_ADDRESSES_OFFSET + 4, deblock_msg->address_b1, cmd_space); cmd = 0; REGIO_WRITE_FIELD(cmd, MSVDX_CMDS_SLICE_PARAMS, SLICE_FIELD_TYPE, 2); REGIO_WRITE_FIELD(cmd, MSVDX_CMDS_SLICE_PARAMS, SLICE_CODE_TYPE, 1); PSB_CMDPORT_WRITE(dev_priv, MSVDX_CMDS_SLICE_PARAMS_OFFSET, cmd, cmd_space); cmd = deblock_msg->alt_output_flags_b; if ((cmd & 3) != 0) { PSB_DEBUG_MSVDX("MSVDX: conceal to rotate surface\n"); } else { PSB_CMDPORT_WRITE(dev_priv, MSVDX_CMDS_ALTERNATIVE_OUTPUT_PICTURE_ROTATION_OFFSET, cmd, cmd_space); PSB_CMDPORT_WRITE(dev_priv, MSVDX_CMDS_VC1_LUMA_RANGE_MAPPING_BASE_ADDRESS_OFFSET, 0, cmd_space); PSB_CMDPORT_WRITE(dev_priv, MSVDX_CMDS_VC1_CHROMA_RANGE_MAPPING_BASE_ADDRESS_OFFSET, 0, cmd_space); PSB_CMDPORT_WRITE(dev_priv, MSVDX_CMDS_VC1_RANGE_MAPPING_FLAGS_OFFSET, 0, cmd_space); } cmd = deblock_msg->ext_stride_a; PSB_CMDPORT_WRITE(dev_priv, MSVDX_CMDS_EXTENDED_ROW_STRIDE_OFFSET, cmd, cmd_space); for (loop = 0; loop < fault_region->num_region; loop++) { uint32_t start = fault_region->mb_regions[loop].start; uint32_t end = fault_region->mb_regions[loop].end; uint32_t x, y; PSB_DEBUG_MSVDX("MSVDX: region(%d) is %d~%d\n", loop, start, end); if (conceal_above_row) start -= width_in_mb; if (end > (width_in_mb * height_in_mb - 1)) end = (width_in_mb * height_in_mb - 1); if (start > end) start = 0; PSB_DEBUG_MSVDX("MSVDX: modify region(%d) is %d~%d\n", loop, start, end); x = start % width_in_mb; y = start / width_in_mb; for (mb_loop = start; mb_loop <= end; mb_loop++, x++) { if (x >= width_in_mb) { x = 0; y++; } /* PSB_DEBUG_MSVDX("MSVDX: concleament (%d,%d)\n", x, y); */ if ((x == 0) && (mb_loop != start)) PSB_CMDPORT_WRITE_FAST(dev_priv, MSVDX_CMDS_END_SLICE_PICTURE_OFFSET, 0, cmd_space); cmd = 0; REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS_MACROBLOCK_NUMBER, MB_CODE_TYPE, 1); REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS_MACROBLOCK_NUMBER, MB_NO_X, x); REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS_MACROBLOCK_NUMBER, MB_NO_Y, y); PSB_CMDPORT_WRITE_FAST(dev_priv, MSVDX_CMDS_MACROBLOCK_NUMBER_OFFSET, cmd, cmd_space); PSB_CMDPORT_WRITE_FAST(dev_priv, MSVDX_CMDS_MACROBLOCK_RESIDUAL_FORMAT_OFFSET, 0, cmd_space); cmd = 0; REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS_INTER_BLOCK_PREDICTION, REF_INDEX_A_VALID, 1); REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS_INTER_BLOCK_PREDICTION, INTER_PRED_BLOCK_SIZE, 0); REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS_INTER_BLOCK_PREDICTION, REF_INDEX_A, 0); REGIO_WRITE_FIELD_LITE(cmd, MSVDX_CMDS_INTER_BLOCK_PREDICTION, REF_INDEX_B, 0); PSB_CMDPORT_WRITE_FAST(dev_priv, MSVDX_CMDS_INTER_BLOCK_PREDICTION_OFFSET, cmd, cmd_space); PSB_CMDPORT_WRITE_FAST(dev_priv, MSVDX_CMDS_MOTION_VECTOR_OFFSET, 0, cmd_space); } PSB_CMDPORT_WRITE(dev_priv, MSVDX_CMDS_END_SLICE_PICTURE_OFFSET, 0, cmd_space); } ec_done: /* try to unblock rendec */ ret = PSB_CMDPORT_WRITE_FAST(dev_priv, MSVDX_CMDS_END_SLICE_PICTURE_OFFSET, 1, cmd_space); fault_region->num_region = 0; preempt_enable(); #ifdef CONFIG_VIDEO_MRFLD power_island_put(OSPM_VIDEO_DEC_ISLAND); #else ospm_power_using_video_end(OSPM_VIDEO_DEC_ISLAND); #endif printk(KERN_DEBUG "MSVDX: EC done, unlock msvdx ret %d\n", ret); return; } struct psb_msvdx_ec_ctx *psb_msvdx_find_ec_ctx( struct msvdx_private *msvdx_priv, struct ttm_object_file *tfile, void *cmd) { int i, free_idx; struct psb_msvdx_ec_ctx *ec_ctx = NULL; struct fw_deblock_msg *deblock_msg = (struct fw_deblock_msg *)cmd; free_idx = -1; for (i = 0; i < PSB_MAX_EC_INSTANCE; i++) { if (msvdx_priv->msvdx_ec_ctx[i]->tfile == tfile) break; else if (free_idx < 0 && msvdx_priv->msvdx_ec_ctx[i]->tfile == NULL) free_idx = i; } if (i < PSB_MAX_EC_INSTANCE) ec_ctx = msvdx_priv->msvdx_ec_ctx[i]; else if (free_idx >= 0 && cmd) { PSB_DEBUG_MSVDX("acquire ec ctx idx %d for tfile %p\n", free_idx, tfile); ec_ctx = msvdx_priv->msvdx_ec_ctx[free_idx]; memset(ec_ctx, 0, sizeof(*ec_ctx)); ec_ctx->tfile = tfile; ec_ctx->context_id = deblock_msg->mmu_context.bits.context; } else { PSB_DEBUG_MSVDX("Available ec ctx is not found\n"); } return ec_ctx; } void psb_msvdx_update_frame_info(struct msvdx_private *msvdx_priv, struct ttm_object_file *tfile, void *cmd) { int i, free_idx; drm_psb_msvdx_frame_info_t *frame_info; struct fw_deblock_msg *deblock_msg = (struct fw_deblock_msg *)cmd; uint32_t buffer_handle = deblock_msg->mb_param_address; struct psb_msvdx_ec_ctx *ec_ctx; PSB_DEBUG_MSVDX( "update frame info (handle 0x%08x) for error concealment\n", buffer_handle); ec_ctx = psb_msvdx_find_ec_ctx(msvdx_priv, tfile, cmd); if (!ec_ctx) return; free_idx = -1; for (i = 0; i < MAX_DECODE_BUFFERS; i++) { if (buffer_handle == ec_ctx->frame_info[i].handle) break; if (free_idx < 0 && ec_ctx->frame_info[i].handle == 0) free_idx = i; } if (i < MAX_DECODE_BUFFERS) frame_info = &ec_ctx->frame_info[i]; else if (free_idx >= 0) { PSB_DEBUG_MSVDX("acquire frame_info solt idx %d\n", free_idx); frame_info = &ec_ctx->frame_info[free_idx]; } else { PSB_DEBUG_MSVDX("%d solts occupied, abort update frame_info\n", MAX_DECODE_BUFFERS); return; } frame_info->fw_status = 0; frame_info->handle = buffer_handle; frame_info->fence = (deblock_msg->header.bits.msg_fence & (~0xf)); frame_info->decode_status.num_region = 0; ec_ctx->cur_frame_info = frame_info; } void psb_msvdx_backup_cmd(struct msvdx_private *msvdx_priv, struct ttm_object_file *tfile, void *cmd, uint32_t cmd_size, uint32_t deblock_cmd_offset) { struct fw_deblock_msg *deblock_msg = NULL; struct psb_msvdx_ec_ctx *ec_ctx; union msg_header *header; PSB_DEBUG_MSVDX("backup cmd for ved error concealment\n"); ec_ctx = psb_msvdx_find_ec_ctx(msvdx_priv, tfile, NULL); if (!ec_ctx) { PSB_DEBUG_MSVDX("this is not a ec ctx, abort backup cmd\n"); return; } if (deblock_cmd_offset != PSB_MSVDX_INVALID_OFFSET) deblock_msg = (struct fw_deblock_msg *)(cmd + deblock_cmd_offset); if (deblock_msg && ec_ctx->context_id != deblock_msg->mmu_context.bits.context) { PSB_DEBUG_MSVDX("backup cmd but find mis-match context id\n"); return; } ec_ctx->cmd_size = cmd_size; ec_ctx->deblock_cmd_offset = deblock_cmd_offset; memcpy(ec_ctx->unfenced_cmd, cmd, cmd_size); ec_ctx->fence = PSB_MSVDX_INVALID_FENCE; header = (union msg_header *)ec_ctx->unfenced_cmd; if (cmd_size) ec_ctx->fence = header->bits.msg_fence; ec_ctx->fence &= (~0xf); PSB_DEBUG_MSVDX("backup cmd for ved: fence 0x%08x, cmd_size %d\n", ec_ctx->fence, cmd_size); } void psb_msvdx_mtx_message_dump(struct drm_device *dev) { struct drm_psb_private *dev_priv = psb_priv(dev); int i, buf_size, buf_offset; buf_size = PSB_RMSVDX32(MSVDX_COMMS_TO_HOST_BUF_SIZE) & ((1 << 16) - 1); buf_offset = (PSB_RMSVDX32(MSVDX_COMMS_TO_HOST_BUF_SIZE) >> 16) + 0x2000; printk(KERN_DEBUG "Dump to HOST message buffer (offset:size)%04x:%04x\n", buf_offset, buf_size); for (i = 0; i < buf_size; i += 4) { uint32_t reg1, reg2, reg3, reg4; reg1 = PSB_RMSVDX32(buf_offset + i * 4); reg2 = PSB_RMSVDX32(buf_offset + i * 4 + 4); reg3 = PSB_RMSVDX32(buf_offset + i * 4 + 8); reg4 = PSB_RMSVDX32(buf_offset + i * 4 + 12); printk(KERN_DEBUG "0x%04x: 0x%08x 0x%08x 0x%08x 0x%08x\n", (buf_offset + i * 4), reg1, reg2, reg3, reg4); } buf_size = PSB_RMSVDX32(MSVDX_COMMS_TO_MTX_BUF_SIZE) & ((1 << 16) - 1); buf_offset = (PSB_RMSVDX32(MSVDX_COMMS_TO_MTX_BUF_SIZE) >> 16) + 0x2000; printk(KERN_DEBUG "Dump to MTX message buffer (offset:size)%04x:%04x\n", buf_offset, buf_size); for (i = 0; i < buf_size; i += 4) { uint32_t reg1, reg2, reg3, reg4; reg1 = PSB_RMSVDX32(buf_offset + i * 4); reg2 = PSB_RMSVDX32(buf_offset + i * 4 + 4); reg3 = PSB_RMSVDX32(buf_offset + i * 4 + 8); reg4 = PSB_RMSVDX32(buf_offset + i * 4 + 12); printk(KERN_DEBUG "0x%04x: 0x%08x 0x%08x 0x%08x 0x%08x\n", (buf_offset + i * 4), reg1, reg2, reg3, reg4); } buf_size = 12; buf_offset = 0xFD0 + 0x2000; printk(KERN_DEBUG "Comm header (offset:size)%04x:%04x\n", buf_offset, buf_size); for (i = 0; i < buf_size; i += 4) { uint32_t reg1, reg2, reg3, reg4; reg1 = PSB_RMSVDX32(buf_offset + i * 4); reg2 = PSB_RMSVDX32(buf_offset + i * 4 + 4); reg3 = PSB_RMSVDX32(buf_offset + i * 4 + 8); reg4 = PSB_RMSVDX32(buf_offset + i * 4 + 12); printk(KERN_DEBUG "0x%04x: 0x%08x 0x%08x 0x%08x 0x%08x\n", (buf_offset + i * 4), reg1, reg2, reg3, reg4); } printk(KERN_DEBUG "Error status 0x2cc4: 0x%08x\n", PSB_RMSVDX32(0x2cc4)); }