blob: 53aff77cfd92940d85770936a969bf258abfa212 [file] [log] [blame]
/* arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c
*
* Verificion code for aDSP VDEC packets from userspace.
*
* Copyright (c) 2008 QUALCOMM Incorporated
* Copyright (C) 2008 Google, Inc.
*
* 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>
#define ADSP_DEBUG_MSGS 0
#if ADSP_DEBUG_MSGS
#define DLOG(fmt,args...) \
do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \
##args); } \
while (0)
#else
#define DLOG(x...) do {} while (0)
#endif
#include <mach/qdsp5/qdsp5vdeccmdi.h>
#include "adsp.h"
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)
{
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);
DLOG("virt %x %x\n", phys_addr, phys_size);
if (adsp_pmem_fixup_kvaddr(module, &phys_addr, &kvaddr, phys_size)) {
DLOG("ah%x al%x sh%x sl%x addr %x size %x\n",
*high, *low, size_high, size_low, phys_addr, phys_size);
return -1;
}
ptr_to_high_low_short(phys_addr, high, low);
DLOG("phys %x %x\n", phys_addr, 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)
{
unsigned short cmd_id = ((unsigned short *)cmd_data)[0];
viddec_cmd_subframe_pkt *pkt;
unsigned long subframe_pkt_addr;
unsigned long subframe_pkt_size;
viddec_cmd_frame_header_packet *frame_header_pkt;
int i, num_addr, skip;
unsigned short *frame_buffer_high, *frame_buffer_low;
unsigned long frame_buffer_size;
unsigned short frame_buffer_size_high, frame_buffer_size_low;
DLOG("cmd_size %d cmd_id %d cmd_data %x\n", cmd_size, cmd_id, cmd_data);
if (cmd_id != VIDDEC_CMD_SUBFRAME_PKT) {
printk(KERN_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;
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))
return -1;
/* deref those ptrs and check if they are a frame header packet */
frame_header_pkt = (viddec_cmd_frame_header_packet *)subframe_pkt_addr;
switch (frame_header_pkt->packet_id) {
case 0xB201: /* h.264 */
num_addr = skip = 8;
break;
case 0x4D01: /* mpeg-4 and h.263 */
num_addr = 3;
skip = 0;
break;
default:
return 0;
}
frame_buffer_high = &frame_header_pkt->frame_buffer_0_high;
frame_buffer_low = &frame_header_pkt->frame_buffer_0_low;
frame_buffer_size = (frame_header_pkt->x_dimension *
frame_header_pkt->y_dimension * 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 (pmem_fixup_high_low(frame_buffer_high, frame_buffer_low,
frame_buffer_size_high,
frame_buffer_size_low,
module,
NULL, NULL))
return -1;
frame_buffer_high += 2;
frame_buffer_low += 2;
}
/* Patch the output buffer. */
frame_buffer_high += 2*skip;
frame_buffer_low += 2*skip;
if (pmem_fixup_high_low(frame_buffer_high, frame_buffer_low,
frame_buffer_size_high,
frame_buffer_size_low, module, NULL, NULL))
return -1;
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:
DLOG("\n");
return verify_vdec_pkt_cmd(module, cmd_data, cmd_size);
default:
printk(KERN_INFO "unknown video queue %u\n", queue_id);
return 0;
}
}