blob: ca86c017dea6f17f89e43a966c932c61b38e6155 [file] [log] [blame]
/*--------------------------------------------------------------------------
Copyright (c) 2009, Code Aurora Forum. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Code Aurora nor
the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef T_WINNT
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#else
#include "qdspext.h"
#include "qdsprtos.h"
#define QDSP_mpuVDecCmdQueue 4
#define QDSP_mpuVDecPktQueue 5
#endif //T_WINNT
#include <errno.h>
#include <pthread.h>
#include "adsp.h"
#include "qtv_msg.h"
#include "qutility.h"
#if PROFILE_DECODER
QPERF_INIT(dsp_decode);
#endif
#define DEBUG 0 // TEST
#if DEBUG // TEST
static pthread_mutex_t logMTX;
static FILE *logFD = NULL;
static const char *logFN = "/data/adsp_log.txt";
#endif
struct adsp_module {
int fd;
int cpu_dma_fd;
unsigned long cpu_dma_freq;
volatile int dead;
volatile int init_done;
void *ctxt;
adsp_msg_frame_done_func frame_done;
adsp_msg_buffer_done_func buffer_done;
pthread_t thr;
};
#ifndef T_WINNT
void convertFrameInfoToFrameDetails(struct Vdec_FrameDetailsType *Frame_details,
struct vdec_frame_info *pFrame)
{
int i;
if (NULL == Frame_details || NULL == pFrame) {
QTV_MSG_PRIO2(QTVDIAG_GENERAL, QTVDIAG_PRIO_LOW,
"convertFrameInfoToFrameDetails() Frame Details or vdec-frame NULL "
"Frame Details: 0x%x, pFrame: 0x%x\n",
Frame_details, pFrame);
}
Frame_details->status = (Vdec_StatusType) pFrame->status; //TBD
Frame_details->userData1 = pFrame->data1;
Frame_details->userData2 = pFrame->data2;
Frame_details->timestamp =
(unsigned long long)((unsigned long long)pFrame->
timestamp_hi << 32 | (unsigned long long)
pFrame->timestamp_lo & 0x0FFFFFFFFLL);
Frame_details->calculatedTimeStamp =
(unsigned long long)((unsigned long long)pFrame->
cal_timestamp_hi << 32 | (unsigned long long)
pFrame->cal_timestamp_lo & 0x0FFFFFFFFLL);
Frame_details->nDecPicWidth = pFrame->dec_width;
Frame_details->nDecPicHeight = pFrame->dec_height;
Frame_details->cwin.x1 = pFrame->cwin.x1;
Frame_details->cwin.y1 = pFrame->cwin.y1;
Frame_details->cwin.x2 = pFrame->cwin.x2;
Frame_details->cwin.y2 = pFrame->cwin.y2;
for (i = 0; i < MAX_FIELDS; i++) {
Frame_details->ePicType[i] =
(Vdec_PictureType) pFrame->picture_type[i];
}
Frame_details->ePicFormat = (Vdec_PictureFormat) pFrame->picture_format;
Frame_details->nVC1RangeY = pFrame->vc1_rangeY;
Frame_details->nVC1RangeUV = pFrame->vc1_rangeUV;
Frame_details->ePicResolution =
(Vdec_PictureRes) pFrame->picture_resolution;
Frame_details->nRepeatProgFrames = pFrame->frame_disp_repeat;
Frame_details->bRepeatFirstField = pFrame->repeat_first_field;
Frame_details->bTopFieldFirst = pFrame->top_field_first;
Frame_details->bFrameInterpFlag = pFrame->interframe_interp;
Frame_details->panScan.numWindows = pFrame->panscan.num;
for (i = 0; i < MAX_VC1_PAN_SCAN_WINDOWS; i++) {
Frame_details->panScan.winHeight[i] = pFrame->panscan.width[i];
Frame_details->panScan.winHeight[i] = pFrame->panscan.height[i];
Frame_details->panScan.winHorOffset[i] =
pFrame->panscan.xoffset[i];
Frame_details->panScan.winVerOffset[i] =
pFrame->panscan.yoffset[i];
}
Frame_details->nPercentConcealedMacroblocks =
pFrame->concealed_macblk_num;
Frame_details->flags = pFrame->flags;
Frame_details->performanceStats = pFrame->performance_stats;
}
void *adsp_thread(void *_mod)
{
struct adsp_module *mod = _mod;
int n;
struct vdec_msg vdecMsg;
struct Vdec_FrameDetailsType vdec_frame;
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_LOW,
"adsp_thread: event thread start\n");
while (!mod->dead) {
if (!mod->init_done)
continue;
QTV_MSG_PRIO1(QTVDIAG_GENERAL, QTVDIAG_PRIO_MED,
"adsp_thread: Calling IOCTL_GETMSG fd %d\n",
mod->fd);
if (ioctl(mod->fd, VDEC_IOCTL_GETMSG, &vdecMsg) < 0) {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_HIGH,
"adsp_thread:VDEC_IOCTL_GETMSG failed\n");
continue;
}
QTV_MSG_PRIO1(QTVDIAG_GENERAL, QTVDIAG_PRIO_MED,
"adsp_thread: %d\n", vdecMsg.id);
if (vdecMsg.id == VDEC_MSG_REUSEINPUTBUFFER) {
QTV_MSG_PRIO1(QTVDIAG_GENERAL, QTVDIAG_PRIO_MED,
"adsp_thread: Reuse input buffer %d\n",
vdecMsg.buf_id);
mod->buffer_done(mod->ctxt, (void *)vdecMsg.buf_id);
} else if (vdecMsg.id == VDEC_MSG_FRAMEDONE) {
QTV_MSG_PRIO1(QTVDIAG_GENERAL, QTVDIAG_PRIO_MED,
"adsp_thread: Frame done %x\n",
vdecMsg.vfr_info);
convertFrameInfoToFrameDetails(&vdec_frame,
&vdecMsg.vfr_info);
#if PROFILE_DECODER
if (vdec_frame.status != VDEC_FLUSH_DONE) {
QPERF_END_AND_START(dsp_decode);
}
#endif
mod->frame_done(mod->ctxt, &vdec_frame,
vdecMsg.vfr_info.data2,
vdecMsg.vfr_info.offset);
} else {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_MED,
"adsp_thread:VDEC_IOCTL_GETMSG Unknown Msg ID\n");
}
}
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_LOW,
"h264: event thread stop\n");
return 0;
}
#endif
void adsp_close(struct adsp_module *mod)
{
#if PROFILE_DECODER
QPERF_TERMINATE(dsp_decode);
QPERF_RESET(dsp_decode);
#endif
#ifndef T_WINNT
int ret;
int thread_ret = 0;
if (NULL == mod) {
QTV_MSG_PRIO1(QTVDIAG_GENERAL, QTVDIAG_PRIO_ERROR,
"adsp_close() mod NULL: 0x%x\n", mod);
return ;
}
mod->dead = 1;
if (ioctl(mod->fd, VDEC_IOCTL_CLOSE, NULL) < 0) {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_ERROR,
"VDEC_IOCTL_CLOSE failed\n");
return;
}
QTV_MSG_PRIO1(QTVDIAG_GENERAL, QTVDIAG_PRIO_LOW, "adsp_close: 0x%x",
(unsigned)mod);
/*Wait on the adsp event thread completion */
ret = pthread_join(mod->thr, (void **)&thread_ret);
if (ret != 0) {
QTV_MSG_PRIO1(QTVDIAG_GENERAL, QTVDIAG_PRIO_ERROR,
"*************adsp_close: Could not join on the adsp event thread err=%d!!\n",
ret);
}
/*Wait on the adsp event thread completion */
ret = close(mod->fd);
if (ret < 0) {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_ERROR,
"*************adsp_close ERROR!");
}
if(mod->cpu_dma_fd > 0) {
if(write(mod->cpu_dma_fd, &mod->cpu_dma_freq, sizeof(mod->cpu_dma_freq)) < 0) {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_ERROR,
"ERROR - adsp: Request cpu_dma_freq write failed\n");
}
close(mod->cpu_dma_fd);
mod->cpu_dma_fd = 0;
}
QTV_MSG_PRIO2(QTVDIAG_GENERAL, QTVDIAG_PRIO_LOW,
"adsp_close returned %d, fd: %d", ret, mod->fd);
#if DEBUG
if (logFD) {
pthread_mutex_lock(&logMTX);
fclose(logFD);
logFD = NULL;
pthread_mutex_destroy(&logMTX);
}
#endif
//sleep(1); /* XXX need better way to stop thread XXX */
free(mod);
mod = NULL;
#endif //T_WINNT
}
struct adsp_module *adsp_open(const char *name, struct adsp_open_info info,
void *context, int32 vdec_fd)
{
QTV_MSG_PRIO1(QTVDIAG_GENERAL, QTVDIAG_PRIO_LOW, "adsp_open: %s", name);
int fds[2], r;
struct adsp_module *mod;
unsigned long cpu_dma_freq = 1000;
mod = calloc(1, sizeof(*mod));
if (!mod)
return 0;
mod->ctxt = context;
mod->frame_done = info.frame_done;
mod->buffer_done = info.buffer_done;
#ifndef T_WINNT
mod->dead = 0;
mod->init_done = 0;
r = pthread_create(&mod->thr, 0, adsp_thread, mod);
if (r < 0) {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_ERROR,
"Error - unable to create adsp_thread\n");
goto fail_thread;
}
mod->fd = vdec_fd;
if(mod->fd < 0) {
mod->fd = open("/dev/vdec", O_RDWR);
if (mod->fd < 0) {
QTV_MSG_PRIO3(QTVDIAG_GENERAL, QTVDIAG_PRIO_FATAL,
"adsp: cannot open '%s', fd: %d (%s)\n", name,
mod->fd, strerror(errno));
goto fail_open;
}
}
mod->cpu_dma_fd = open("/dev/cpu_dma_latency", O_RDWR);
if(mod->cpu_dma_fd < 0) {
QTV_MSG_PRIO2(QTVDIAG_GENERAL, QTVDIAG_PRIO_FATAL,
"adsp: cannot open cpu_dma_latency, fd: %d (%s)\n",
mod->fd, strerror(errno));
} else {
if(read(mod->cpu_dma_fd, &mod->cpu_dma_freq, sizeof(mod->cpu_dma_freq)) < 0) {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_MED,
"ERROR - adsp: Request cpu_dma_freq read failed\n");
}
if(write(mod->cpu_dma_fd, &cpu_dma_freq, sizeof(cpu_dma_freq)) < 0) {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_MED,
"ERROR - adsp: Request cpu_dma_freq write failed\n");
}
}
#if DEBUG
if (pthread_mutex_init(&logMTX, NULL) == 0) {
if (pthread_mutex_lock(&logMTX) == 0) {
if (!logFD) {
logFD = fopen(logFN, "a");
}
if (logFD) {
fprintf(logFD, "\n");
pthread_mutex_unlock(&logMTX);
}
}
if (!logFD) {
pthread_mutex_destroy(&logMTX);
}
}
if (!logFD) {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_ERROR,
"adsp: unable to log adsp writes\n");
}
#endif
#endif
return mod;
fail_open:
mod->dead = 1;
fail_thread:
free(mod);
return 0;
}
int adsp_set_buffers(struct adsp_module *mod, struct adsp_buffer_info bufinfo)
{
struct vdec_buffer mem;
int r;
if (NULL == mod) {
QTV_MSG_PRIO1(QTVDIAG_GENERAL, QTVDIAG_PRIO_LOW,
"adsp_set_buffers() mod NULL: 0x%x\n", mod);
return -1;
}
mem.pmem_id = bufinfo.buf.pmem_id;
mem.buf.buf_type = bufinfo.buf_type;
mem.buf.num_buf = bufinfo.numbuf;
mem.buf.islast = bufinfo.is_last;
mem.buf.region.src_id = 0x0106e429;
mem.buf.region.offset = bufinfo.buf.offset;
mem.buf.region.size = bufinfo.buf.size;
if (ioctl(mod->fd, VDEC_IOCTL_SETBUFFERS, &mem) < 0) {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_ERROR,
"VDEC_IOCTL_SETBUFFERS failed\n");
mod->dead = 1;
return -1;
}
mod->init_done = 1;
return 0;
}
int adsp_free_buffers(struct adsp_module *mod, struct adsp_buffer_info bufinfo)
{
struct vdec_buffer mem;
int r;
if (NULL == mod) {
QTV_MSG_PRIO1(QTVDIAG_GENERAL, QTVDIAG_PRIO_LOW,
"adsp_set_buffers() mod NULL: 0x%x\n", mod);
return -1;
}
mem.pmem_id = bufinfo.buf.pmem_id;
mem.buf.buf_type = bufinfo.buf_type;
mem.buf.num_buf = bufinfo.numbuf;
mem.buf.islast = bufinfo.is_last;
mem.buf.region.src_id = 0x0106e429;
mem.buf.region.offset = bufinfo.buf.offset;
mem.buf.region.size = bufinfo.buf.size;
if (ioctl(mod->fd, VDEC_IOCTL_FREEBUFFERS, &mem) < 0) {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_ERROR,
"VDEC_IOCTL_SETBUFFERS failed\n");
return -1;
}
return 0;
}
int adsp_init(struct adsp_module *mod, struct adsp_init *init)
{
struct vdec_init vi;
struct vdec_buf_req buf;
struct vdec_version ver;
if (NULL == mod) {
QTV_MSG_PRIO1(QTVDIAG_GENERAL, QTVDIAG_PRIO_LOW,
"adsp_init() mod NULL: 0x%x\n", mod);
return -1;
}
/* Get the driver version */
if (ioctl(mod->fd, VDEC_IOCTL_GETVERSION, &ver) < 0) {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_ERROR,
"VDEC_IOCTL_GETVERSION failed setting to default version\n");
ver.major = 1;
ver.minor = 0;
}
vi.sps_cfg.cfg.width = init->width;
vi.sps_cfg.cfg.height = init->height;
vi.sps_cfg.cfg.order = init->order;
vi.sps_cfg.cfg.notify_enable = init->notify_enable;
vi.sps_cfg.cfg.fourcc = init->fourcc;
vi.sps_cfg.cfg.vc1_rowbase = init->vc1_rowbase;
vi.sps_cfg.cfg.h264_startcode_detect = init->h264_startcode_detect;
vi.sps_cfg.cfg.h264_nal_len_size = init->h264_nal_len_size;
vi.sps_cfg.cfg.postproc_flag = init->postproc_flag;
vi.sps_cfg.cfg.fruc_enable = init->fruc_enable;
vi.sps_cfg.cfg.color_format = init->color_format;
vi.sps_cfg.seq.header = init->seq_header;
vi.sps_cfg.seq.len = init->seq_len;
vi.buf_req = &buf;
/* set the color format based on version */
if (ver.major < 2 && init->color_format != 0) {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_ERROR,
"VDEC_IOCTL_INITIALIZE wrong value for reserved field\n");
vi.sps_cfg.cfg.color_format = 0;
}
if (ioctl(mod->fd, VDEC_IOCTL_INITIALIZE, &vi) < 0) {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_ERROR,
"VDEC_IOCTL_INITIALIZE failed\n");
mod->dead = 1;
return -1;
}
init->buf_req->input.bufnum_min = vi.buf_req->input.num_min_buffers;
init->buf_req->input.bufnum_max = vi.buf_req->input.num_max_buffers;
init->buf_req->input.bufsize = vi.buf_req->input.bufsize;
init->buf_req->output.bufnum_min = vi.buf_req->output.num_min_buffers;
init->buf_req->output.bufnum_max = vi.buf_req->output.num_max_buffers;
init->buf_req->output.bufsize = vi.buf_req->output.bufsize;
init->buf_req->dec_req1.bufnum_min =
vi.buf_req->dec_req1.num_min_buffers;
init->buf_req->dec_req1.bufnum_max =
vi.buf_req->dec_req1.num_max_buffers;
init->buf_req->dec_req1.bufsize = vi.buf_req->dec_req1.bufsize;
init->buf_req->dec_req2.bufnum_min =
vi.buf_req->dec_req2.num_min_buffers;
init->buf_req->dec_req2.bufnum_max =
vi.buf_req->dec_req2.num_max_buffers;
init->buf_req->dec_req2.bufsize = vi.buf_req->dec_req2.bufsize;
return 0;
}
int adsp_post_input_buffer(struct adsp_module *mod, struct adsp_input_buf input,
unsigned int eos)
{
struct vdec_input_buf ip;
struct vdec_queue_status stat;
if (NULL == mod) {
QTV_MSG_PRIO1(QTVDIAG_GENERAL, QTVDIAG_PRIO_LOW,
"adsp_post_input_buffer() mod NULL: 0x%x\n", mod);
return -1;
}
ip.pmem_id = input.pmem_id;
ip.buffer.avsync_state = input.avsync_state;
ip.buffer.data = input.data;
ip.buffer.offset = input.offset;
ip.buffer.size = input.size;
ip.buffer.timestamp_lo = input.timestamp_lo;
ip.buffer.timestamp_hi = input.timestamp_hi;
ip.buffer.flags = input.flags;
ip.queue_status = &stat;
#if PROFILE_DECODER
QPERF_START(dsp_decode);
#endif
if (eos) {
if (ioctl(mod->fd, VDEC_IOCTL_EOS, NULL) < 0) {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_ERROR,
"adsp:VDEC_IOCTL_EOS failed\n");
return -1;
}
} else {
//usleep(1000000);
if (ioctl(mod->fd, VDEC_IOCTL_QUEUE, &ip) < 0) {
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_ERROR,
"adsp:VDEC_IOCTL_QUEUE failed\n");
return -1;
}
}
return 0;
}
int adsp_release_frame(struct adsp_module *mod, unsigned int *buf)
{
if (NULL == mod) {
QTV_MSG_PRIO1(QTVDIAG_GENERAL, QTVDIAG_PRIO_LOW,
"adsp_release_frame() mod NULL: 0x%x\n", mod);
return -1;
}
return ioctl(mod->fd, VDEC_IOCTL_REUSEFRAMEBUFFER, buf);
}
int adsp_flush(struct adsp_module *mod, unsigned int port)
{
if (NULL == mod) {
QTV_MSG_PRIO1(QTVDIAG_GENERAL, QTVDIAG_PRIO_LOW,
"adsp_flush() mod NULL: 0x%x\n", mod);
return -1;
}
QTV_MSG_PRIO(QTVDIAG_GENERAL, QTVDIAG_PRIO_LOW,
"adsp_flush() Before Flush \n");
return ioctl(mod->fd, VDEC_IOCTL_FLUSH, &port);
}