diff options
Diffstat (limited to 'drivers/staging/dream/qdsp5/adsp_video_verify_cmd.c')
-rw-r--r-- | drivers/staging/dream/qdsp5/adsp_video_verify_cmd.c | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/drivers/staging/dream/qdsp5/adsp_video_verify_cmd.c b/drivers/staging/dream/qdsp5/adsp_video_verify_cmd.c new file mode 100644 index 00000000000..53aff77cfd9 --- /dev/null +++ b/drivers/staging/dream/qdsp5/adsp_video_verify_cmd.c @@ -0,0 +1,163 @@ +/* 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; + } +} + |