Path: blob/master/drivers/media/video/cx18/cx18-mailbox.c
17723 views
/*1* cx18 mailbox functions2*3* Copyright (C) 2007 Hans Verkuil <[email protected]>4* Copyright (C) 2008 Andy Walls <[email protected]>5*6* This program is free software; you can redistribute it and/or modify7* it under the terms of the GNU General Public License as published by8* the Free Software Foundation; either version 2 of the License, or9* (at your option) any later version.10*11* This program is distributed in the hope that it will be useful,12* but WITHOUT ANY WARRANTY; without even the implied warranty of13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14* GNU General Public License for more details.15*16* You should have received a copy of the GNU General Public License17* along with this program; if not, write to the Free Software18* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA19* 02111-1307 USA20*/2122#include <stdarg.h>2324#include "cx18-driver.h"25#include "cx18-io.h"26#include "cx18-scb.h"27#include "cx18-irq.h"28#include "cx18-mailbox.h"29#include "cx18-queue.h"30#include "cx18-streams.h"31#include "cx18-alsa-pcm.h" /* FIXME make configurable */3233static const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" };3435#define API_FAST (1 << 2) /* Short timeout */36#define API_SLOW (1 << 3) /* Additional 300ms timeout */3738struct cx18_api_info {39u32 cmd;40u8 flags; /* Flags, see above */41u8 rpu; /* Processing unit */42const char *name; /* The name of the command */43};4445#define API_ENTRY(rpu, x, f) { (x), (f), (rpu), #x }4647static const struct cx18_api_info api_info[] = {48/* MPEG encoder API */49API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0),50API_ENTRY(CPU, CX18_EPU_DEBUG, 0),51API_ENTRY(CPU, CX18_CREATE_TASK, 0),52API_ENTRY(CPU, CX18_DESTROY_TASK, 0),53API_ENTRY(CPU, CX18_CPU_CAPTURE_START, API_SLOW),54API_ENTRY(CPU, CX18_CPU_CAPTURE_STOP, API_SLOW),55API_ENTRY(CPU, CX18_CPU_CAPTURE_PAUSE, 0),56API_ENTRY(CPU, CX18_CPU_CAPTURE_RESUME, 0),57API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0),58API_ENTRY(CPU, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 0),59API_ENTRY(CPU, CX18_CPU_SET_VIDEO_IN, 0),60API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RATE, 0),61API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RESOLUTION, 0),62API_ENTRY(CPU, CX18_CPU_SET_FILTER_PARAM, 0),63API_ENTRY(CPU, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 0),64API_ENTRY(CPU, CX18_CPU_SET_MEDIAN_CORING, 0),65API_ENTRY(CPU, CX18_CPU_SET_INDEXTABLE, 0),66API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PARAMETERS, 0),67API_ENTRY(CPU, CX18_CPU_SET_VIDEO_MUTE, 0),68API_ENTRY(CPU, CX18_CPU_SET_AUDIO_MUTE, 0),69API_ENTRY(CPU, CX18_CPU_SET_MISC_PARAMETERS, 0),70API_ENTRY(CPU, CX18_CPU_SET_RAW_VBI_PARAM, API_SLOW),71API_ENTRY(CPU, CX18_CPU_SET_CAPTURE_LINE_NO, 0),72API_ENTRY(CPU, CX18_CPU_SET_COPYRIGHT, 0),73API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PID, 0),74API_ENTRY(CPU, CX18_CPU_SET_VIDEO_PID, 0),75API_ENTRY(CPU, CX18_CPU_SET_VER_CROP_LINE, 0),76API_ENTRY(CPU, CX18_CPU_SET_GOP_STRUCTURE, 0),77API_ENTRY(CPU, CX18_CPU_SET_SCENE_CHANGE_DETECTION, 0),78API_ENTRY(CPU, CX18_CPU_SET_ASPECT_RATIO, 0),79API_ENTRY(CPU, CX18_CPU_SET_SKIP_INPUT_FRAME, 0),80API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM, 0),81API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER, 0),82API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0),83API_ENTRY(CPU, CX18_CPU_SET_VFC_PARAM, 0),84API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0),85API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST),86API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW),87API_ENTRY(APU, CX18_APU_START, 0),88API_ENTRY(APU, CX18_APU_STOP, 0),89API_ENTRY(APU, CX18_APU_RESETAI, 0),90API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32, 0),91API_ENTRY(0, 0, 0),92};9394static const struct cx18_api_info *find_api_info(u32 cmd)95{96int i;9798for (i = 0; api_info[i].cmd; i++)99if (api_info[i].cmd == cmd)100return &api_info[i];101return NULL;102}103104/* Call with buf of n*11+1 bytes */105static char *u32arr2hex(u32 data[], int n, char *buf)106{107char *p;108int i;109110for (i = 0, p = buf; i < n; i++, p += 11) {111/* kernel snprintf() appends '\0' always */112snprintf(p, 12, " %#010x", data[i]);113}114*p = '\0';115return buf;116}117118static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name)119{120char argstr[MAX_MB_ARGUMENTS*11+1];121122if (!(cx18_debug & CX18_DBGFLG_API))123return;124125CX18_DEBUG_API("%s: req %#010x ack %#010x cmd %#010x err %#010x args%s"126"\n", name, mb->request, mb->ack, mb->cmd, mb->error,127u32arr2hex(mb->args, MAX_MB_ARGUMENTS, argstr));128}129130131/*132* Functions that run in a work_queue work handling context133*/134135static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl)136{137struct cx18_buffer *buf;138139if (s->dvb == NULL || !s->dvb->enabled || mdl->bytesused == 0)140return;141142/* We ignore mdl and buf readpos accounting here - it doesn't matter */143144/* The likely case */145if (list_is_singular(&mdl->buf_list)) {146buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,147list);148if (buf->bytesused)149dvb_dmx_swfilter(&s->dvb->demux,150buf->buf, buf->bytesused);151return;152}153154list_for_each_entry(buf, &mdl->buf_list, list) {155if (buf->bytesused == 0)156break;157dvb_dmx_swfilter(&s->dvb->demux, buf->buf, buf->bytesused);158}159}160161static void cx18_mdl_send_to_videobuf(struct cx18_stream *s,162struct cx18_mdl *mdl)163{164struct cx18_videobuf_buffer *vb_buf;165struct cx18_buffer *buf;166u8 *p;167u32 offset = 0;168int dispatch = 0;169170if (mdl->bytesused == 0)171return;172173/* Acquire a videobuf buffer, clone to and and release it */174spin_lock(&s->vb_lock);175if (list_empty(&s->vb_capture))176goto out;177178vb_buf = list_first_entry(&s->vb_capture, struct cx18_videobuf_buffer,179vb.queue);180181p = videobuf_to_vmalloc(&vb_buf->vb);182if (!p)183goto out;184185offset = vb_buf->bytes_used;186list_for_each_entry(buf, &mdl->buf_list, list) {187if (buf->bytesused == 0)188break;189190if ((offset + buf->bytesused) <= vb_buf->vb.bsize) {191memcpy(p + offset, buf->buf, buf->bytesused);192offset += buf->bytesused;193vb_buf->bytes_used += buf->bytesused;194}195}196197/* If we've filled the buffer as per the callers res then dispatch it */198if (vb_buf->bytes_used >= (vb_buf->vb.width * vb_buf->vb.height * 2)) {199dispatch = 1;200vb_buf->bytes_used = 0;201}202203if (dispatch) {204vb_buf->vb.ts = ktime_to_timeval(ktime_get());205list_del(&vb_buf->vb.queue);206vb_buf->vb.state = VIDEOBUF_DONE;207wake_up(&vb_buf->vb.done);208}209210mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies);211212out:213spin_unlock(&s->vb_lock);214}215216static void cx18_mdl_send_to_alsa(struct cx18 *cx, struct cx18_stream *s,217struct cx18_mdl *mdl)218{219struct cx18_buffer *buf;220221if (mdl->bytesused == 0)222return;223224/* We ignore mdl and buf readpos accounting here - it doesn't matter */225226/* The likely case */227if (list_is_singular(&mdl->buf_list)) {228buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,229list);230if (buf->bytesused)231cx->pcm_announce_callback(cx->alsa, buf->buf,232buf->bytesused);233return;234}235236list_for_each_entry(buf, &mdl->buf_list, list) {237if (buf->bytesused == 0)238break;239cx->pcm_announce_callback(cx->alsa, buf->buf, buf->bytesused);240}241}242243static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)244{245u32 handle, mdl_ack_count, id;246struct cx18_mailbox *mb;247struct cx18_mdl_ack *mdl_ack;248struct cx18_stream *s;249struct cx18_mdl *mdl;250int i;251252mb = &order->mb;253handle = mb->args[0];254s = cx18_handle_to_stream(cx, handle);255256if (s == NULL) {257CX18_WARN("Got DMA done notification for unknown/inactive"258" handle %d, %s mailbox seq no %d\n", handle,259(order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) ?260"stale" : "good", mb->request);261return;262}263264mdl_ack_count = mb->args[2];265mdl_ack = order->mdl_ack;266for (i = 0; i < mdl_ack_count; i++, mdl_ack++) {267id = mdl_ack->id;268/*269* Simple integrity check for processing a stale (and possibly270* inconsistent mailbox): make sure the MDL id is in the271* valid range for the stream.272*273* We go through the trouble of dealing with stale mailboxes274* because most of the time, the mailbox data is still valid and275* unchanged (and in practice the firmware ping-pongs the276* two mdl_ack buffers so mdl_acks are not stale).277*278* There are occasions when we get a half changed mailbox,279* which this check catches for a handle & id mismatch. If the280* handle and id do correspond, the worst case is that we281* completely lost the old MDL, but pick up the new MDL282* early (but the new mdl_ack is guaranteed to be good in this283* case as the firmware wouldn't point us to a new mdl_ack until284* it's filled in).285*286* cx18_queue_get_mdl() will detect the lost MDLs287* and send them back to q_free for fw rotation eventually.288*/289if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) &&290!(id >= s->mdl_base_idx &&291id < (s->mdl_base_idx + s->buffers))) {292CX18_WARN("Fell behind! Ignoring stale mailbox with "293" inconsistent data. Lost MDL for mailbox "294"seq no %d\n", mb->request);295break;296}297mdl = cx18_queue_get_mdl(s, id, mdl_ack->data_used);298299CX18_DEBUG_HI_DMA("DMA DONE for %s (MDL %d)\n", s->name, id);300if (mdl == NULL) {301CX18_WARN("Could not find MDL %d for stream %s\n",302id, s->name);303continue;304}305306CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n",307s->name, mdl->bytesused);308309if (s->type == CX18_ENC_STREAM_TYPE_TS) {310cx18_mdl_send_to_dvb(s, mdl);311cx18_enqueue(s, mdl, &s->q_free);312} else if (s->type == CX18_ENC_STREAM_TYPE_PCM) {313/* Pass the data to cx18-alsa */314if (cx->pcm_announce_callback != NULL) {315cx18_mdl_send_to_alsa(cx, s, mdl);316cx18_enqueue(s, mdl, &s->q_free);317} else {318cx18_enqueue(s, mdl, &s->q_full);319}320} else if (s->type == CX18_ENC_STREAM_TYPE_YUV) {321cx18_mdl_send_to_videobuf(s, mdl);322cx18_enqueue(s, mdl, &s->q_free);323} else {324cx18_enqueue(s, mdl, &s->q_full);325if (s->type == CX18_ENC_STREAM_TYPE_IDX)326cx18_stream_rotate_idx_mdls(cx);327}328}329/* Put as many MDLs as possible back into fw use */330cx18_stream_load_fw_queue(s);331332wake_up(&cx->dma_waitq);333if (s->id != -1)334wake_up(&s->waitq);335}336337static void epu_debug(struct cx18 *cx, struct cx18_in_work_order *order)338{339char *p;340char *str = order->str;341342CX18_DEBUG_INFO("%x %s\n", order->mb.args[0], str);343p = strchr(str, '.');344if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)345CX18_INFO("FW version: %s\n", p - 1);346}347348static void epu_cmd(struct cx18 *cx, struct cx18_in_work_order *order)349{350switch (order->rpu) {351case CPU:352{353switch (order->mb.cmd) {354case CX18_EPU_DMA_DONE:355epu_dma_done(cx, order);356break;357case CX18_EPU_DEBUG:358epu_debug(cx, order);359break;360default:361CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n",362order->mb.cmd);363break;364}365break;366}367case APU:368CX18_WARN("Unknown APU to EPU mailbox command %#0x\n",369order->mb.cmd);370break;371default:372break;373}374}375376static377void free_in_work_order(struct cx18 *cx, struct cx18_in_work_order *order)378{379atomic_set(&order->pending, 0);380}381382void cx18_in_work_handler(struct work_struct *work)383{384struct cx18_in_work_order *order =385container_of(work, struct cx18_in_work_order, work);386struct cx18 *cx = order->cx;387epu_cmd(cx, order);388free_in_work_order(cx, order);389}390391392/*393* Functions that run in an interrupt handling context394*/395396static void mb_ack_irq(struct cx18 *cx, struct cx18_in_work_order *order)397{398struct cx18_mailbox __iomem *ack_mb;399u32 ack_irq, req;400401switch (order->rpu) {402case APU:403ack_irq = IRQ_EPU_TO_APU_ACK;404ack_mb = &cx->scb->apu2epu_mb;405break;406case CPU:407ack_irq = IRQ_EPU_TO_CPU_ACK;408ack_mb = &cx->scb->cpu2epu_mb;409break;410default:411CX18_WARN("Unhandled RPU (%d) for command %x ack\n",412order->rpu, order->mb.cmd);413return;414}415416req = order->mb.request;417/* Don't ack if the RPU has gotten impatient and timed us out */418if (req != cx18_readl(cx, &ack_mb->request) ||419req == cx18_readl(cx, &ack_mb->ack)) {420CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our "421"incoming %s to EPU mailbox (sequence no. %u) "422"while processing\n",423rpu_str[order->rpu], rpu_str[order->rpu], req);424order->flags |= CX18_F_EWO_MB_STALE_WHILE_PROC;425return;426}427cx18_writel(cx, req, &ack_mb->ack);428cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq);429return;430}431432static int epu_dma_done_irq(struct cx18 *cx, struct cx18_in_work_order *order)433{434u32 handle, mdl_ack_offset, mdl_ack_count;435struct cx18_mailbox *mb;436437mb = &order->mb;438handle = mb->args[0];439mdl_ack_offset = mb->args[1];440mdl_ack_count = mb->args[2];441442if (handle == CX18_INVALID_TASK_HANDLE ||443mdl_ack_count == 0 || mdl_ack_count > CX18_MAX_MDL_ACKS) {444if ((order->flags & CX18_F_EWO_MB_STALE) == 0)445mb_ack_irq(cx, order);446return -1;447}448449cx18_memcpy_fromio(cx, order->mdl_ack, cx->enc_mem + mdl_ack_offset,450sizeof(struct cx18_mdl_ack) * mdl_ack_count);451452if ((order->flags & CX18_F_EWO_MB_STALE) == 0)453mb_ack_irq(cx, order);454return 1;455}456457static458int epu_debug_irq(struct cx18 *cx, struct cx18_in_work_order *order)459{460u32 str_offset;461char *str = order->str;462463str[0] = '\0';464str_offset = order->mb.args[1];465if (str_offset) {466cx18_setup_page(cx, str_offset);467cx18_memcpy_fromio(cx, str, cx->enc_mem + str_offset, 252);468str[252] = '\0';469cx18_setup_page(cx, SCB_OFFSET);470}471472if ((order->flags & CX18_F_EWO_MB_STALE) == 0)473mb_ack_irq(cx, order);474475return str_offset ? 1 : 0;476}477478static inline479int epu_cmd_irq(struct cx18 *cx, struct cx18_in_work_order *order)480{481int ret = -1;482483switch (order->rpu) {484case CPU:485{486switch (order->mb.cmd) {487case CX18_EPU_DMA_DONE:488ret = epu_dma_done_irq(cx, order);489break;490case CX18_EPU_DEBUG:491ret = epu_debug_irq(cx, order);492break;493default:494CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n",495order->mb.cmd);496break;497}498break;499}500case APU:501CX18_WARN("Unknown APU to EPU mailbox command %#0x\n",502order->mb.cmd);503break;504default:505break;506}507return ret;508}509510static inline511struct cx18_in_work_order *alloc_in_work_order_irq(struct cx18 *cx)512{513int i;514struct cx18_in_work_order *order = NULL;515516for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) {517/*518* We only need "pending" atomic to inspect its contents,519* and need not do a check and set because:520* 1. Any work handler thread only clears "pending" and only521* on one, particular work order at a time, per handler thread.522* 2. "pending" is only set here, and we're serialized because523* we're called in an IRQ handler context.524*/525if (atomic_read(&cx->in_work_order[i].pending) == 0) {526order = &cx->in_work_order[i];527atomic_set(&order->pending, 1);528break;529}530}531return order;532}533534void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu)535{536struct cx18_mailbox __iomem *mb;537struct cx18_mailbox *order_mb;538struct cx18_in_work_order *order;539int submit;540541switch (rpu) {542case CPU:543mb = &cx->scb->cpu2epu_mb;544break;545case APU:546mb = &cx->scb->apu2epu_mb;547break;548default:549return;550}551552order = alloc_in_work_order_irq(cx);553if (order == NULL) {554CX18_WARN("Unable to find blank work order form to schedule "555"incoming mailbox command processing\n");556return;557}558559order->flags = 0;560order->rpu = rpu;561order_mb = &order->mb;562563/* mb->cmd and mb->args[0] through mb->args[2] */564cx18_memcpy_fromio(cx, &order_mb->cmd, &mb->cmd, 4 * sizeof(u32));565/* mb->request and mb->ack. N.B. we want to read mb->ack last */566cx18_memcpy_fromio(cx, &order_mb->request, &mb->request,5672 * sizeof(u32));568569if (order_mb->request == order_mb->ack) {570CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our "571"incoming %s to EPU mailbox (sequence no. %u)"572"\n",573rpu_str[rpu], rpu_str[rpu], order_mb->request);574if (cx18_debug & CX18_DBGFLG_WARN)575dump_mb(cx, order_mb, "incoming");576order->flags = CX18_F_EWO_MB_STALE_UPON_RECEIPT;577}578579/*580* Individual EPU command processing is responsible for ack-ing581* a non-stale mailbox as soon as possible582*/583submit = epu_cmd_irq(cx, order);584if (submit > 0) {585queue_work(cx->in_work_queue, &order->work);586}587}588589590/*591* Functions called from a non-interrupt, non work_queue context592*/593594static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])595{596const struct cx18_api_info *info = find_api_info(cmd);597u32 state, irq, req, ack, err;598struct cx18_mailbox __iomem *mb;599u32 __iomem *xpu_state;600wait_queue_head_t *waitq;601struct mutex *mb_lock;602unsigned long int t0, timeout, ret;603int i;604char argstr[MAX_MB_ARGUMENTS*11+1];605DEFINE_WAIT(w);606607if (info == NULL) {608CX18_WARN("unknown cmd %x\n", cmd);609return -EINVAL;610}611612if (cx18_debug & CX18_DBGFLG_API) { /* only call u32arr2hex if needed */613if (cmd == CX18_CPU_DE_SET_MDL) {614if (cx18_debug & CX18_DBGFLG_HIGHVOL)615CX18_DEBUG_HI_API("%s\tcmd %#010x args%s\n",616info->name, cmd,617u32arr2hex(data, args, argstr));618} else619CX18_DEBUG_API("%s\tcmd %#010x args%s\n",620info->name, cmd,621u32arr2hex(data, args, argstr));622}623624switch (info->rpu) {625case APU:626waitq = &cx->mb_apu_waitq;627mb_lock = &cx->epu2apu_mb_lock;628irq = IRQ_EPU_TO_APU;629mb = &cx->scb->epu2apu_mb;630xpu_state = &cx->scb->apu_state;631break;632case CPU:633waitq = &cx->mb_cpu_waitq;634mb_lock = &cx->epu2cpu_mb_lock;635irq = IRQ_EPU_TO_CPU;636mb = &cx->scb->epu2cpu_mb;637xpu_state = &cx->scb->cpu_state;638break;639default:640CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu);641return -EINVAL;642}643644mutex_lock(mb_lock);645/*646* Wait for an in-use mailbox to complete647*648* If the XPU is responding with Ack's, the mailbox shouldn't be in649* a busy state, since we serialize access to it on our end.650*651* If the wait for ack after sending a previous command was interrupted652* by a signal, we may get here and find a busy mailbox. After waiting,653* mark it "not busy" from our end, if the XPU hasn't ack'ed it still.654*/655state = cx18_readl(cx, xpu_state);656req = cx18_readl(cx, &mb->request);657timeout = msecs_to_jiffies(10);658ret = wait_event_timeout(*waitq,659(ack = cx18_readl(cx, &mb->ack)) == req,660timeout);661if (req != ack) {662/* waited long enough, make the mbox "not busy" from our end */663cx18_writel(cx, req, &mb->ack);664CX18_ERR("mbox was found stuck busy when setting up for %s; "665"clearing busy and trying to proceed\n", info->name);666} else if (ret != timeout)667CX18_DEBUG_API("waited %u msecs for busy mbox to be acked\n",668jiffies_to_msecs(timeout-ret));669670/* Build the outgoing mailbox */671req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1;672673cx18_writel(cx, cmd, &mb->cmd);674for (i = 0; i < args; i++)675cx18_writel(cx, data[i], &mb->args[i]);676cx18_writel(cx, 0, &mb->error);677cx18_writel(cx, req, &mb->request);678cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */679680/*681* Notify the XPU and wait for it to send an Ack back682*/683timeout = msecs_to_jiffies((info->flags & API_FAST) ? 10 : 20);684685CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n",686irq, info->name);687688/* So we don't miss the wakeup, prepare to wait before notifying fw */689prepare_to_wait(waitq, &w, TASK_UNINTERRUPTIBLE);690cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq);691692t0 = jiffies;693ack = cx18_readl(cx, &mb->ack);694if (ack != req) {695schedule_timeout(timeout);696ret = jiffies - t0;697ack = cx18_readl(cx, &mb->ack);698} else {699ret = jiffies - t0;700}701702finish_wait(waitq, &w);703704if (req != ack) {705mutex_unlock(mb_lock);706if (ret >= timeout) {707/* Timed out */708CX18_DEBUG_WARN("sending %s timed out waiting %d msecs "709"for RPU acknowledgement\n",710info->name, jiffies_to_msecs(ret));711} else {712CX18_DEBUG_WARN("woken up before mailbox ack was ready "713"after submitting %s to RPU. only "714"waited %d msecs on req %u but awakened"715" with unmatched ack %u\n",716info->name,717jiffies_to_msecs(ret),718req, ack);719}720return -EINVAL;721}722723if (ret >= timeout)724CX18_DEBUG_WARN("failed to be awakened upon RPU acknowledgment "725"sending %s; timed out waiting %d msecs\n",726info->name, jiffies_to_msecs(ret));727else728CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n",729jiffies_to_msecs(ret), info->name);730731/* Collect data returned by the XPU */732for (i = 0; i < MAX_MB_ARGUMENTS; i++)733data[i] = cx18_readl(cx, &mb->args[i]);734err = cx18_readl(cx, &mb->error);735mutex_unlock(mb_lock);736737/*738* Wait for XPU to perform extra actions for the caller in some cases.739* e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all MDLs740* back in a burst shortly thereafter741*/742if (info->flags & API_SLOW)743cx18_msleep_timeout(300, 0);744745if (err)746CX18_DEBUG_API("mailbox error %08x for command %s\n", err,747info->name);748return err ? -EIO : 0;749}750751int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[])752{753return cx18_api_call(cx, cmd, args, data);754}755756static int cx18_set_filter_param(struct cx18_stream *s)757{758struct cx18 *cx = s->cx;759u32 mode;760int ret;761762mode = (cx->filter_mode & 1) ? 2 : (cx->spatial_strength ? 1 : 0);763ret = cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,764s->handle, 1, mode, cx->spatial_strength);765mode = (cx->filter_mode & 2) ? 2 : (cx->temporal_strength ? 1 : 0);766ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,767s->handle, 0, mode, cx->temporal_strength);768ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,769s->handle, 2, cx->filter_mode >> 2, 0);770return ret;771}772773int cx18_api_func(void *priv, u32 cmd, int in, int out,774u32 data[CX2341X_MBOX_MAX_DATA])775{776struct cx18_stream *s = priv;777struct cx18 *cx = s->cx;778779switch (cmd) {780case CX2341X_ENC_SET_OUTPUT_PORT:781return 0;782case CX2341X_ENC_SET_FRAME_RATE:783return cx18_vapi(cx, CX18_CPU_SET_VIDEO_IN, 6,784s->handle, 0, 0, 0, 0, data[0]);785case CX2341X_ENC_SET_FRAME_SIZE:786return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RESOLUTION, 3,787s->handle, data[1], data[0]);788case CX2341X_ENC_SET_STREAM_TYPE:789return cx18_vapi(cx, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 2,790s->handle, data[0]);791case CX2341X_ENC_SET_ASPECT_RATIO:792return cx18_vapi(cx, CX18_CPU_SET_ASPECT_RATIO, 2,793s->handle, data[0]);794795case CX2341X_ENC_SET_GOP_PROPERTIES:796return cx18_vapi(cx, CX18_CPU_SET_GOP_STRUCTURE, 3,797s->handle, data[0], data[1]);798case CX2341X_ENC_SET_GOP_CLOSURE:799return 0;800case CX2341X_ENC_SET_AUDIO_PROPERTIES:801return cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2,802s->handle, data[0]);803case CX2341X_ENC_MUTE_AUDIO:804return cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,805s->handle, data[0]);806case CX2341X_ENC_SET_BIT_RATE:807return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RATE, 5,808s->handle, data[0], data[1], data[2], data[3]);809case CX2341X_ENC_MUTE_VIDEO:810return cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2,811s->handle, data[0]);812case CX2341X_ENC_SET_FRAME_DROP_RATE:813return cx18_vapi(cx, CX18_CPU_SET_SKIP_INPUT_FRAME, 2,814s->handle, data[0]);815case CX2341X_ENC_MISC:816return cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 4,817s->handle, data[0], data[1], data[2]);818case CX2341X_ENC_SET_DNR_FILTER_MODE:819cx->filter_mode = (data[0] & 3) | (data[1] << 2);820return cx18_set_filter_param(s);821case CX2341X_ENC_SET_DNR_FILTER_PROPS:822cx->spatial_strength = data[0];823cx->temporal_strength = data[1];824return cx18_set_filter_param(s);825case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:826return cx18_vapi(cx, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 3,827s->handle, data[0], data[1]);828case CX2341X_ENC_SET_CORING_LEVELS:829return cx18_vapi(cx, CX18_CPU_SET_MEDIAN_CORING, 5,830s->handle, data[0], data[1], data[2], data[3]);831}832CX18_WARN("Unknown cmd %x\n", cmd);833return 0;834}835836int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS],837u32 cmd, int args, ...)838{839va_list ap;840int i;841842va_start(ap, args);843for (i = 0; i < args; i++)844data[i] = va_arg(ap, u32);845va_end(ap);846return cx18_api(cx, cmd, args, data);847}848849int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...)850{851u32 data[MAX_MB_ARGUMENTS];852va_list ap;853int i;854855if (cx == NULL) {856CX18_ERR("cx == NULL (cmd=%x)\n", cmd);857return 0;858}859if (args > MAX_MB_ARGUMENTS) {860CX18_ERR("args too big (cmd=%x)\n", cmd);861args = MAX_MB_ARGUMENTS;862}863va_start(ap, args);864for (i = 0; i < args; i++)865data[i] = va_arg(ap, u32);866va_end(ap);867return cx18_api(cx, cmd, args, data);868}869870871