265 lines
7.3 KiB
C
265 lines
7.3 KiB
C
/* arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c
|
|
*
|
|
* Verificion code for aDSP VDEC packets from userspace.
|
|
*
|
|
* Copyright (C) 2008 Google, Inc.
|
|
* Copyright (c) 2008-2010, 2012 The Linux Foundation. All rights reserved.
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that 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.
|
|
*
|
|
*/
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <mach/qdsp5/qdsp5vdeccmdi.h>
|
|
#include "adsp.h"
|
|
#include <mach/debug_mm.h>
|
|
|
|
#define MAX_FLUSH_SIZE 160
|
|
|
|
static inline void *high_low_short_to_ptr(unsigned short high,
|
|
unsigned short low)
|
|
{
|
|
return (void *)((((unsigned long)high) << 16) | ((unsigned long)low));
|
|
}
|
|
|
|
static inline void ptr_to_high_low_short(void *ptr, unsigned short *high,
|
|
unsigned short *low)
|
|
{
|
|
*high = (unsigned short)((((unsigned long)ptr) >> 16) & 0xffff);
|
|
*low = (unsigned short)((unsigned long)ptr & 0xffff);
|
|
}
|
|
|
|
static int pmem_fixup_high_low(unsigned short *high,
|
|
unsigned short *low,
|
|
unsigned short size_high,
|
|
unsigned short size_low,
|
|
struct msm_adsp_module *module,
|
|
unsigned long *addr, unsigned long *size,
|
|
struct file **filp, unsigned long *offset)
|
|
{
|
|
void *phys_addr;
|
|
unsigned long phys_size;
|
|
unsigned long kvaddr;
|
|
|
|
phys_addr = high_low_short_to_ptr(*high, *low);
|
|
phys_size = (unsigned long)high_low_short_to_ptr(size_high, size_low);
|
|
MM_DBG("virt %x %x\n", (unsigned int)phys_addr,
|
|
(unsigned int)phys_size);
|
|
if (phys_addr) {
|
|
if (adsp_ion_fixup_kvaddr(module, &phys_addr,
|
|
&kvaddr, phys_size, filp, offset)) {
|
|
MM_ERR("ah%x al%x sh%x sl%x addr %x size %x\n",
|
|
*high, *low, size_high,
|
|
size_low, (unsigned int)phys_addr,
|
|
(unsigned int)phys_size);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
ptr_to_high_low_short(phys_addr, high, low);
|
|
MM_DBG("phys %x %x\n", (unsigned int)phys_addr,
|
|
(unsigned int)phys_size);
|
|
if (addr)
|
|
*addr = kvaddr;
|
|
if (size)
|
|
*size = phys_size;
|
|
return 0;
|
|
}
|
|
|
|
static int verify_vdec_pkt_cmd(struct msm_adsp_module *module,
|
|
void *cmd_data, size_t cmd_size)
|
|
{
|
|
void *phys_addr;
|
|
unsigned short cmd_id = ((unsigned short *)cmd_data)[0];
|
|
viddec_cmd_subframe_pkt *pkt;
|
|
unsigned long subframe_pkt_addr;
|
|
unsigned long subframe_pkt_size;
|
|
unsigned short *frame_header_pkt;
|
|
int i, num_addr, col_addr = 0, skip;
|
|
int start_pos = 0, xdim_pos = 1, ydim_pos = 2;
|
|
unsigned short *frame_buffer_high, *frame_buffer_low;
|
|
unsigned long frame_buffer_size;
|
|
unsigned short frame_buffer_size_high, frame_buffer_size_low;
|
|
struct file *filp = NULL;
|
|
unsigned long offset = 0;
|
|
unsigned long Codec_Id = 0;
|
|
|
|
MM_DBG("cmd_size %d cmd_id %d cmd_data %x\n", cmd_size, cmd_id,
|
|
(unsigned int)cmd_data);
|
|
if (cmd_id != VIDDEC_CMD_SUBFRAME_PKT) {
|
|
MM_INFO("adsp_video: unknown video packet %u\n", cmd_id);
|
|
return 0;
|
|
}
|
|
if (cmd_size < sizeof(viddec_cmd_subframe_pkt))
|
|
return -1;
|
|
|
|
pkt = (viddec_cmd_subframe_pkt *)cmd_data;
|
|
phys_addr = high_low_short_to_ptr(pkt->subframe_packet_high,
|
|
pkt->subframe_packet_low);
|
|
|
|
if (pmem_fixup_high_low(&(pkt->subframe_packet_high),
|
|
&(pkt->subframe_packet_low),
|
|
pkt->subframe_packet_size_high,
|
|
pkt->subframe_packet_size_low,
|
|
module,
|
|
&subframe_pkt_addr,
|
|
&subframe_pkt_size,
|
|
&filp, &offset))
|
|
return -1;
|
|
Codec_Id = pkt->codec_selection_word;
|
|
/*Invalidate cache before accessing the cached pmem buffer*/
|
|
if (adsp_ion_do_cache_op(module, phys_addr, (void *)subframe_pkt_addr,
|
|
subframe_pkt_size*2, offset, ION_IOC_INV_CACHES)){
|
|
MM_ERR("Cache operation failed for" \
|
|
" phys addr high %x addr low %x\n",
|
|
pkt->subframe_packet_high, pkt->subframe_packet_low);
|
|
return -EINVAL;
|
|
}
|
|
/* deref those ptrs and check if they are a frame header packet */
|
|
frame_header_pkt = (unsigned short *)subframe_pkt_addr;
|
|
switch (frame_header_pkt[0]) {
|
|
case 0xB201: /* h.264 vld in dsp */
|
|
if (Codec_Id == 0x8) {
|
|
num_addr = 16;
|
|
skip = 0;
|
|
start_pos = 5;
|
|
} else {
|
|
num_addr = 16;
|
|
skip = 0;
|
|
start_pos = 6;
|
|
col_addr = 17;
|
|
}
|
|
break;
|
|
case 0x8201: /* h.264 vld in arm */
|
|
num_addr = 16;
|
|
skip = 0;
|
|
start_pos = 6;
|
|
break;
|
|
case 0x4D01: /* mpeg-4 and h.263 vld in arm */
|
|
num_addr = 3;
|
|
skip = 0;
|
|
start_pos = 5;
|
|
break;
|
|
case 0x9201: /*For Real Decoder*/
|
|
num_addr = 2;
|
|
skip = 0;
|
|
start_pos = 5;
|
|
break;
|
|
case 0xBD01: /* mpeg-4 and h.263 vld in dsp */
|
|
num_addr = 3;
|
|
skip = 0;
|
|
start_pos = 6;
|
|
if (((frame_header_pkt[5] & 0x000c) >> 2) == 0x2) /* B-frame */
|
|
start_pos = 8;
|
|
break;
|
|
case 0x0001: /* wmv */
|
|
num_addr = 2;
|
|
skip = 0;
|
|
start_pos = 5;
|
|
break;
|
|
case 0xC201: /*WMV main profile*/
|
|
num_addr = 3;
|
|
skip = 0;
|
|
start_pos = 6;
|
|
break;
|
|
case 0xDD01: /* VP6 */
|
|
num_addr = 3;
|
|
skip = 0;
|
|
start_pos = 10;
|
|
break;
|
|
case 0xFD01: /* VP8 */
|
|
num_addr = 3;
|
|
skip = 0;
|
|
start_pos = 24;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
frame_buffer_high = &frame_header_pkt[start_pos];
|
|
frame_buffer_low = &frame_header_pkt[start_pos + 1];
|
|
frame_buffer_size = (frame_header_pkt[xdim_pos] *
|
|
frame_header_pkt[ydim_pos] * 3) / 2;
|
|
ptr_to_high_low_short((void *)frame_buffer_size,
|
|
&frame_buffer_size_high,
|
|
&frame_buffer_size_low);
|
|
for (i = 0; i < num_addr; i++) {
|
|
if (frame_buffer_high && frame_buffer_low) {
|
|
if (pmem_fixup_high_low(frame_buffer_high,
|
|
frame_buffer_low,
|
|
frame_buffer_size_high,
|
|
frame_buffer_size_low,
|
|
module,
|
|
NULL, NULL, NULL, NULL))
|
|
return -EINVAL;
|
|
}
|
|
frame_buffer_high += 2;
|
|
frame_buffer_low += 2;
|
|
}
|
|
/* Patch the output buffer. */
|
|
frame_buffer_high += 2*skip;
|
|
frame_buffer_low += 2*skip;
|
|
if (frame_buffer_high && frame_buffer_low) {
|
|
if (pmem_fixup_high_low(frame_buffer_high,
|
|
frame_buffer_low,
|
|
frame_buffer_size_high,
|
|
frame_buffer_size_low,
|
|
module,
|
|
NULL, NULL, NULL, NULL))
|
|
return -EINVAL;
|
|
}
|
|
if (col_addr) {
|
|
frame_buffer_high += 2;
|
|
frame_buffer_low += 2;
|
|
/* Patch the Co-located buffers.*/
|
|
frame_buffer_size = (72 * frame_header_pkt[xdim_pos] *
|
|
frame_header_pkt[ydim_pos]) >> 16;
|
|
ptr_to_high_low_short((void *)frame_buffer_size,
|
|
&frame_buffer_size_high,
|
|
&frame_buffer_size_low);
|
|
for (i = 0; i < col_addr; i++) {
|
|
if (frame_buffer_high && frame_buffer_low) {
|
|
if (pmem_fixup_high_low(frame_buffer_high,
|
|
frame_buffer_low,
|
|
frame_buffer_size_high,
|
|
frame_buffer_size_low,
|
|
module,
|
|
NULL, NULL, NULL, NULL))
|
|
return -EINVAL;
|
|
}
|
|
frame_buffer_high += 2;
|
|
frame_buffer_low += 2;
|
|
}
|
|
}
|
|
/*Flush the cached mem subframe packet before sending to DSP*/
|
|
if (adsp_ion_do_cache_op(module, phys_addr, (void *)subframe_pkt_addr,
|
|
MAX_FLUSH_SIZE, offset, ION_IOC_CLEAN_CACHES)){
|
|
MM_ERR("Cache operation failed for" \
|
|
" phys addr high %x addr low %x\n",
|
|
pkt->subframe_packet_high, pkt->subframe_packet_low);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int adsp_video_verify_cmd(struct msm_adsp_module *module,
|
|
unsigned int queue_id, void *cmd_data,
|
|
size_t cmd_size)
|
|
{
|
|
switch (queue_id) {
|
|
case QDSP_mpuVDecPktQueue:
|
|
return verify_vdec_pkt_cmd(module, cmd_data, cmd_size);
|
|
default:
|
|
MM_INFO("unknown video queue %u\n", queue_id);
|
|
return 0;
|
|
}
|
|
}
|
|
|