162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * cx18 mailbox functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> 662306a36Sopenharmony_ci * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "cx18-driver.h" 1262306a36Sopenharmony_ci#include "cx18-io.h" 1362306a36Sopenharmony_ci#include "cx18-scb.h" 1462306a36Sopenharmony_ci#include "cx18-irq.h" 1562306a36Sopenharmony_ci#include "cx18-mailbox.h" 1662306a36Sopenharmony_ci#include "cx18-queue.h" 1762306a36Sopenharmony_ci#include "cx18-streams.h" 1862306a36Sopenharmony_ci#include "cx18-alsa-pcm.h" /* FIXME make configurable */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" }; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define API_FAST (1 << 2) /* Short timeout */ 2362306a36Sopenharmony_ci#define API_SLOW (1 << 3) /* Additional 300ms timeout */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct cx18_api_info { 2662306a36Sopenharmony_ci u32 cmd; 2762306a36Sopenharmony_ci u8 flags; /* Flags, see above */ 2862306a36Sopenharmony_ci u8 rpu; /* Processing unit */ 2962306a36Sopenharmony_ci const char *name; /* The name of the command */ 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define API_ENTRY(rpu, x, f) { (x), (f), (rpu), #x } 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic const struct cx18_api_info api_info[] = { 3562306a36Sopenharmony_ci /* MPEG encoder API */ 3662306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0), 3762306a36Sopenharmony_ci API_ENTRY(CPU, CX18_EPU_DEBUG, 0), 3862306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CREATE_TASK, 0), 3962306a36Sopenharmony_ci API_ENTRY(CPU, CX18_DESTROY_TASK, 0), 4062306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_CAPTURE_START, API_SLOW), 4162306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_CAPTURE_STOP, API_SLOW), 4262306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_CAPTURE_PAUSE, 0), 4362306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_CAPTURE_RESUME, 0), 4462306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0), 4562306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 0), 4662306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_VIDEO_IN, 0), 4762306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RATE, 0), 4862306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RESOLUTION, 0), 4962306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_FILTER_PARAM, 0), 5062306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 0), 5162306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_MEDIAN_CORING, 0), 5262306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_INDEXTABLE, 0), 5362306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PARAMETERS, 0), 5462306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_VIDEO_MUTE, 0), 5562306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_AUDIO_MUTE, 0), 5662306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_MISC_PARAMETERS, 0), 5762306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_RAW_VBI_PARAM, API_SLOW), 5862306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_CAPTURE_LINE_NO, 0), 5962306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_COPYRIGHT, 0), 6062306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PID, 0), 6162306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_VIDEO_PID, 0), 6262306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_VER_CROP_LINE, 0), 6362306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_GOP_STRUCTURE, 0), 6462306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_SCENE_CHANGE_DETECTION, 0), 6562306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_ASPECT_RATIO, 0), 6662306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_SKIP_INPUT_FRAME, 0), 6762306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM, 0), 6862306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER, 0), 6962306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0), 7062306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_SET_VFC_PARAM, 0), 7162306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), 7262306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), 7362306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW), 7462306a36Sopenharmony_ci API_ENTRY(APU, CX18_APU_START, 0), 7562306a36Sopenharmony_ci API_ENTRY(APU, CX18_APU_STOP, 0), 7662306a36Sopenharmony_ci API_ENTRY(APU, CX18_APU_RESETAI, 0), 7762306a36Sopenharmony_ci API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32, 0), 7862306a36Sopenharmony_ci API_ENTRY(0, 0, 0), 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic const struct cx18_api_info *find_api_info(u32 cmd) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci int i; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci for (i = 0; api_info[i].cmd; i++) 8662306a36Sopenharmony_ci if (api_info[i].cmd == cmd) 8762306a36Sopenharmony_ci return &api_info[i]; 8862306a36Sopenharmony_ci return NULL; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* Call with buf of n*11+1 bytes */ 9262306a36Sopenharmony_cistatic char *u32arr2hex(u32 data[], int n, char *buf) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci char *p; 9562306a36Sopenharmony_ci int i; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci for (i = 0, p = buf; i < n; i++, p += 11) { 9862306a36Sopenharmony_ci /* kernel snprintf() appends '\0' always */ 9962306a36Sopenharmony_ci snprintf(p, 12, " %#010x", data[i]); 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci *p = '\0'; 10262306a36Sopenharmony_ci return buf; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci char argstr[MAX_MB_ARGUMENTS*11+1]; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (!(cx18_debug & CX18_DBGFLG_API)) 11062306a36Sopenharmony_ci return; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci CX18_DEBUG_API("%s: req %#010x ack %#010x cmd %#010x err %#010x args%s\n", 11362306a36Sopenharmony_ci name, mb->request, mb->ack, mb->cmd, mb->error, 11462306a36Sopenharmony_ci u32arr2hex(mb->args, MAX_MB_ARGUMENTS, argstr)); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* 11962306a36Sopenharmony_ci * Functions that run in a work_queue work handling context 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct cx18_buffer *buf; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (s->dvb == NULL || !s->dvb->enabled || mdl->bytesused == 0) 12762306a36Sopenharmony_ci return; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* We ignore mdl and buf readpos accounting here - it doesn't matter */ 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* The likely case */ 13262306a36Sopenharmony_ci if (list_is_singular(&mdl->buf_list)) { 13362306a36Sopenharmony_ci buf = list_first_entry(&mdl->buf_list, struct cx18_buffer, 13462306a36Sopenharmony_ci list); 13562306a36Sopenharmony_ci if (buf->bytesused) 13662306a36Sopenharmony_ci dvb_dmx_swfilter(&s->dvb->demux, 13762306a36Sopenharmony_ci buf->buf, buf->bytesused); 13862306a36Sopenharmony_ci return; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci list_for_each_entry(buf, &mdl->buf_list, list) { 14262306a36Sopenharmony_ci if (buf->bytesused == 0) 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci dvb_dmx_swfilter(&s->dvb->demux, buf->buf, buf->bytesused); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void cx18_mdl_send_to_vb2(struct cx18_stream *s, struct cx18_mdl *mdl) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct cx18_vb2_buffer *vb_buf; 15162306a36Sopenharmony_ci struct cx18_buffer *buf; 15262306a36Sopenharmony_ci u8 *p; 15362306a36Sopenharmony_ci u32 offset = 0; 15462306a36Sopenharmony_ci int dispatch = 0; 15562306a36Sopenharmony_ci unsigned long bsize; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (mdl->bytesused == 0) 15862306a36Sopenharmony_ci return; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Acquire a vb2 buffer, clone to and release it */ 16162306a36Sopenharmony_ci spin_lock(&s->vb_lock); 16262306a36Sopenharmony_ci if (list_empty(&s->vb_capture)) 16362306a36Sopenharmony_ci goto out; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci vb_buf = list_first_entry(&s->vb_capture, struct cx18_vb2_buffer, 16662306a36Sopenharmony_ci list); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci p = vb2_plane_vaddr(&vb_buf->vb.vb2_buf, 0); 16962306a36Sopenharmony_ci if (!p) 17062306a36Sopenharmony_ci goto out; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci bsize = vb2_get_plane_payload(&vb_buf->vb.vb2_buf, 0); 17362306a36Sopenharmony_ci offset = vb_buf->bytes_used; 17462306a36Sopenharmony_ci list_for_each_entry(buf, &mdl->buf_list, list) { 17562306a36Sopenharmony_ci if (buf->bytesused == 0) 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if ((offset + buf->bytesused) <= bsize) { 17962306a36Sopenharmony_ci memcpy(p + offset, buf->buf, buf->bytesused); 18062306a36Sopenharmony_ci offset += buf->bytesused; 18162306a36Sopenharmony_ci vb_buf->bytes_used += buf->bytesused; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* If we've filled the buffer as per the callers res then dispatch it */ 18662306a36Sopenharmony_ci if (vb_buf->bytes_used >= s->vb_bytes_per_frame) { 18762306a36Sopenharmony_ci dispatch = 1; 18862306a36Sopenharmony_ci vb_buf->bytes_used = 0; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (dispatch) { 19262306a36Sopenharmony_ci vb_buf->vb.vb2_buf.timestamp = ktime_get_ns(); 19362306a36Sopenharmony_ci vb_buf->vb.sequence = s->sequence++; 19462306a36Sopenharmony_ci list_del(&vb_buf->list); 19562306a36Sopenharmony_ci vb2_buffer_done(&vb_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ciout: 20162306a36Sopenharmony_ci spin_unlock(&s->vb_lock); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void cx18_mdl_send_to_alsa(struct cx18 *cx, struct cx18_stream *s, 20562306a36Sopenharmony_ci struct cx18_mdl *mdl) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct cx18_buffer *buf; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (mdl->bytesused == 0) 21062306a36Sopenharmony_ci return; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* We ignore mdl and buf readpos accounting here - it doesn't matter */ 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* The likely case */ 21562306a36Sopenharmony_ci if (list_is_singular(&mdl->buf_list)) { 21662306a36Sopenharmony_ci buf = list_first_entry(&mdl->buf_list, struct cx18_buffer, 21762306a36Sopenharmony_ci list); 21862306a36Sopenharmony_ci if (buf->bytesused) 21962306a36Sopenharmony_ci cx->pcm_announce_callback(cx->alsa, buf->buf, 22062306a36Sopenharmony_ci buf->bytesused); 22162306a36Sopenharmony_ci return; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci list_for_each_entry(buf, &mdl->buf_list, list) { 22562306a36Sopenharmony_ci if (buf->bytesused == 0) 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci cx->pcm_announce_callback(cx->alsa, buf->buf, buf->bytesused); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci u32 handle, mdl_ack_count, id; 23462306a36Sopenharmony_ci struct cx18_mailbox *mb; 23562306a36Sopenharmony_ci struct cx18_mdl_ack *mdl_ack; 23662306a36Sopenharmony_ci struct cx18_stream *s; 23762306a36Sopenharmony_ci struct cx18_mdl *mdl; 23862306a36Sopenharmony_ci int i; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci mb = &order->mb; 24162306a36Sopenharmony_ci handle = mb->args[0]; 24262306a36Sopenharmony_ci s = cx18_handle_to_stream(cx, handle); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (s == NULL) { 24562306a36Sopenharmony_ci CX18_WARN("Got DMA done notification for unknown/inactive handle %d, %s mailbox seq no %d\n", 24662306a36Sopenharmony_ci handle, 24762306a36Sopenharmony_ci (order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) ? 24862306a36Sopenharmony_ci "stale" : "good", mb->request); 24962306a36Sopenharmony_ci return; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci mdl_ack_count = mb->args[2]; 25362306a36Sopenharmony_ci mdl_ack = order->mdl_ack; 25462306a36Sopenharmony_ci for (i = 0; i < mdl_ack_count; i++, mdl_ack++) { 25562306a36Sopenharmony_ci id = mdl_ack->id; 25662306a36Sopenharmony_ci /* 25762306a36Sopenharmony_ci * Simple integrity check for processing a stale (and possibly 25862306a36Sopenharmony_ci * inconsistent mailbox): make sure the MDL id is in the 25962306a36Sopenharmony_ci * valid range for the stream. 26062306a36Sopenharmony_ci * 26162306a36Sopenharmony_ci * We go through the trouble of dealing with stale mailboxes 26262306a36Sopenharmony_ci * because most of the time, the mailbox data is still valid and 26362306a36Sopenharmony_ci * unchanged (and in practice the firmware ping-pongs the 26462306a36Sopenharmony_ci * two mdl_ack buffers so mdl_acks are not stale). 26562306a36Sopenharmony_ci * 26662306a36Sopenharmony_ci * There are occasions when we get a half changed mailbox, 26762306a36Sopenharmony_ci * which this check catches for a handle & id mismatch. If the 26862306a36Sopenharmony_ci * handle and id do correspond, the worst case is that we 26962306a36Sopenharmony_ci * completely lost the old MDL, but pick up the new MDL 27062306a36Sopenharmony_ci * early (but the new mdl_ack is guaranteed to be good in this 27162306a36Sopenharmony_ci * case as the firmware wouldn't point us to a new mdl_ack until 27262306a36Sopenharmony_ci * it's filled in). 27362306a36Sopenharmony_ci * 27462306a36Sopenharmony_ci * cx18_queue_get_mdl() will detect the lost MDLs 27562306a36Sopenharmony_ci * and send them back to q_free for fw rotation eventually. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) && 27862306a36Sopenharmony_ci !(id >= s->mdl_base_idx && 27962306a36Sopenharmony_ci id < (s->mdl_base_idx + s->buffers))) { 28062306a36Sopenharmony_ci CX18_WARN("Fell behind! Ignoring stale mailbox with inconsistent data. Lost MDL for mailbox seq no %d\n", 28162306a36Sopenharmony_ci mb->request); 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci mdl = cx18_queue_get_mdl(s, id, mdl_ack->data_used); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci CX18_DEBUG_HI_DMA("DMA DONE for %s (MDL %d)\n", s->name, id); 28762306a36Sopenharmony_ci if (mdl == NULL) { 28862306a36Sopenharmony_ci CX18_WARN("Could not find MDL %d for stream %s\n", 28962306a36Sopenharmony_ci id, s->name); 29062306a36Sopenharmony_ci continue; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n", 29462306a36Sopenharmony_ci s->name, mdl->bytesused); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (s->type == CX18_ENC_STREAM_TYPE_TS) { 29762306a36Sopenharmony_ci cx18_mdl_send_to_dvb(s, mdl); 29862306a36Sopenharmony_ci cx18_enqueue(s, mdl, &s->q_free); 29962306a36Sopenharmony_ci } else if (s->type == CX18_ENC_STREAM_TYPE_PCM) { 30062306a36Sopenharmony_ci /* Pass the data to cx18-alsa */ 30162306a36Sopenharmony_ci if (cx->pcm_announce_callback != NULL) { 30262306a36Sopenharmony_ci cx18_mdl_send_to_alsa(cx, s, mdl); 30362306a36Sopenharmony_ci cx18_enqueue(s, mdl, &s->q_free); 30462306a36Sopenharmony_ci } else { 30562306a36Sopenharmony_ci cx18_enqueue(s, mdl, &s->q_full); 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci } else if (s->type == CX18_ENC_STREAM_TYPE_YUV) { 30862306a36Sopenharmony_ci cx18_mdl_send_to_vb2(s, mdl); 30962306a36Sopenharmony_ci cx18_enqueue(s, mdl, &s->q_free); 31062306a36Sopenharmony_ci } else { 31162306a36Sopenharmony_ci cx18_enqueue(s, mdl, &s->q_full); 31262306a36Sopenharmony_ci if (s->type == CX18_ENC_STREAM_TYPE_IDX) 31362306a36Sopenharmony_ci cx18_stream_rotate_idx_mdls(cx); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci /* Put as many MDLs as possible back into fw use */ 31762306a36Sopenharmony_ci cx18_stream_load_fw_queue(s); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci wake_up(&cx->dma_waitq); 32062306a36Sopenharmony_ci if (s->id != -1) 32162306a36Sopenharmony_ci wake_up(&s->waitq); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic void epu_debug(struct cx18 *cx, struct cx18_in_work_order *order) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci char *p; 32762306a36Sopenharmony_ci char *str = order->str; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci CX18_DEBUG_INFO("%x %s\n", order->mb.args[0], str); 33062306a36Sopenharmony_ci p = strchr(str, '.'); 33162306a36Sopenharmony_ci if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str) 33262306a36Sopenharmony_ci CX18_INFO("FW version: %s\n", p - 1); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic void epu_cmd(struct cx18 *cx, struct cx18_in_work_order *order) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci switch (order->rpu) { 33862306a36Sopenharmony_ci case CPU: 33962306a36Sopenharmony_ci { 34062306a36Sopenharmony_ci switch (order->mb.cmd) { 34162306a36Sopenharmony_ci case CX18_EPU_DMA_DONE: 34262306a36Sopenharmony_ci epu_dma_done(cx, order); 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci case CX18_EPU_DEBUG: 34562306a36Sopenharmony_ci epu_debug(cx, order); 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci default: 34862306a36Sopenharmony_ci CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n", 34962306a36Sopenharmony_ci order->mb.cmd); 35062306a36Sopenharmony_ci break; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci break; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci case APU: 35562306a36Sopenharmony_ci CX18_WARN("Unknown APU to EPU mailbox command %#0x\n", 35662306a36Sopenharmony_ci order->mb.cmd); 35762306a36Sopenharmony_ci break; 35862306a36Sopenharmony_ci default: 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic 36462306a36Sopenharmony_civoid free_in_work_order(struct cx18 *cx, struct cx18_in_work_order *order) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci atomic_set(&order->pending, 0); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_civoid cx18_in_work_handler(struct work_struct *work) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct cx18_in_work_order *order = 37262306a36Sopenharmony_ci container_of(work, struct cx18_in_work_order, work); 37362306a36Sopenharmony_ci struct cx18 *cx = order->cx; 37462306a36Sopenharmony_ci epu_cmd(cx, order); 37562306a36Sopenharmony_ci free_in_work_order(cx, order); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/* 38062306a36Sopenharmony_ci * Functions that run in an interrupt handling context 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic void mb_ack_irq(struct cx18 *cx, struct cx18_in_work_order *order) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct cx18_mailbox __iomem *ack_mb; 38662306a36Sopenharmony_ci u32 ack_irq, req; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci switch (order->rpu) { 38962306a36Sopenharmony_ci case APU: 39062306a36Sopenharmony_ci ack_irq = IRQ_EPU_TO_APU_ACK; 39162306a36Sopenharmony_ci ack_mb = &cx->scb->apu2epu_mb; 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci case CPU: 39462306a36Sopenharmony_ci ack_irq = IRQ_EPU_TO_CPU_ACK; 39562306a36Sopenharmony_ci ack_mb = &cx->scb->cpu2epu_mb; 39662306a36Sopenharmony_ci break; 39762306a36Sopenharmony_ci default: 39862306a36Sopenharmony_ci CX18_WARN("Unhandled RPU (%d) for command %x ack\n", 39962306a36Sopenharmony_ci order->rpu, order->mb.cmd); 40062306a36Sopenharmony_ci return; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci req = order->mb.request; 40462306a36Sopenharmony_ci /* Don't ack if the RPU has gotten impatient and timed us out */ 40562306a36Sopenharmony_ci if (req != cx18_readl(cx, &ack_mb->request) || 40662306a36Sopenharmony_ci req == cx18_readl(cx, &ack_mb->ack)) { 40762306a36Sopenharmony_ci CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our incoming %s to EPU mailbox (sequence no. %u) while processing\n", 40862306a36Sopenharmony_ci rpu_str[order->rpu], rpu_str[order->rpu], req); 40962306a36Sopenharmony_ci order->flags |= CX18_F_EWO_MB_STALE_WHILE_PROC; 41062306a36Sopenharmony_ci return; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci cx18_writel(cx, req, &ack_mb->ack); 41362306a36Sopenharmony_ci cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq); 41462306a36Sopenharmony_ci return; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int epu_dma_done_irq(struct cx18 *cx, struct cx18_in_work_order *order) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci u32 handle, mdl_ack_offset, mdl_ack_count; 42062306a36Sopenharmony_ci struct cx18_mailbox *mb; 42162306a36Sopenharmony_ci int i; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci mb = &order->mb; 42462306a36Sopenharmony_ci handle = mb->args[0]; 42562306a36Sopenharmony_ci mdl_ack_offset = mb->args[1]; 42662306a36Sopenharmony_ci mdl_ack_count = mb->args[2]; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (handle == CX18_INVALID_TASK_HANDLE || 42962306a36Sopenharmony_ci mdl_ack_count == 0 || mdl_ack_count > CX18_MAX_MDL_ACKS) { 43062306a36Sopenharmony_ci if ((order->flags & CX18_F_EWO_MB_STALE) == 0) 43162306a36Sopenharmony_ci mb_ack_irq(cx, order); 43262306a36Sopenharmony_ci return -1; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci for (i = 0; i < sizeof(struct cx18_mdl_ack) * mdl_ack_count; i += sizeof(u32)) 43662306a36Sopenharmony_ci ((u32 *)order->mdl_ack)[i / sizeof(u32)] = 43762306a36Sopenharmony_ci cx18_readl(cx, cx->enc_mem + mdl_ack_offset + i); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if ((order->flags & CX18_F_EWO_MB_STALE) == 0) 44062306a36Sopenharmony_ci mb_ack_irq(cx, order); 44162306a36Sopenharmony_ci return 1; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic 44562306a36Sopenharmony_ciint epu_debug_irq(struct cx18 *cx, struct cx18_in_work_order *order) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci u32 str_offset; 44862306a36Sopenharmony_ci char *str = order->str; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci str[0] = '\0'; 45162306a36Sopenharmony_ci str_offset = order->mb.args[1]; 45262306a36Sopenharmony_ci if (str_offset) { 45362306a36Sopenharmony_ci cx18_setup_page(cx, str_offset); 45462306a36Sopenharmony_ci cx18_memcpy_fromio(cx, str, cx->enc_mem + str_offset, 252); 45562306a36Sopenharmony_ci str[252] = '\0'; 45662306a36Sopenharmony_ci cx18_setup_page(cx, SCB_OFFSET); 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if ((order->flags & CX18_F_EWO_MB_STALE) == 0) 46062306a36Sopenharmony_ci mb_ack_irq(cx, order); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return str_offset ? 1 : 0; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic inline 46662306a36Sopenharmony_ciint epu_cmd_irq(struct cx18 *cx, struct cx18_in_work_order *order) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci int ret = -1; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci switch (order->rpu) { 47162306a36Sopenharmony_ci case CPU: 47262306a36Sopenharmony_ci { 47362306a36Sopenharmony_ci switch (order->mb.cmd) { 47462306a36Sopenharmony_ci case CX18_EPU_DMA_DONE: 47562306a36Sopenharmony_ci ret = epu_dma_done_irq(cx, order); 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci case CX18_EPU_DEBUG: 47862306a36Sopenharmony_ci ret = epu_debug_irq(cx, order); 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci default: 48162306a36Sopenharmony_ci CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n", 48262306a36Sopenharmony_ci order->mb.cmd); 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci break; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci case APU: 48862306a36Sopenharmony_ci CX18_WARN("Unknown APU to EPU mailbox command %#0x\n", 48962306a36Sopenharmony_ci order->mb.cmd); 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci default: 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci return ret; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic inline 49862306a36Sopenharmony_cistruct cx18_in_work_order *alloc_in_work_order_irq(struct cx18 *cx) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci int i; 50162306a36Sopenharmony_ci struct cx18_in_work_order *order = NULL; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) { 50462306a36Sopenharmony_ci /* 50562306a36Sopenharmony_ci * We only need "pending" atomic to inspect its contents, 50662306a36Sopenharmony_ci * and need not do a check and set because: 50762306a36Sopenharmony_ci * 1. Any work handler thread only clears "pending" and only 50862306a36Sopenharmony_ci * on one, particular work order at a time, per handler thread. 50962306a36Sopenharmony_ci * 2. "pending" is only set here, and we're serialized because 51062306a36Sopenharmony_ci * we're called in an IRQ handler context. 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_ci if (atomic_read(&cx->in_work_order[i].pending) == 0) { 51362306a36Sopenharmony_ci order = &cx->in_work_order[i]; 51462306a36Sopenharmony_ci atomic_set(&order->pending, 1); 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci return order; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_civoid cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci struct cx18_mailbox __iomem *mb; 52462306a36Sopenharmony_ci struct cx18_mailbox *order_mb; 52562306a36Sopenharmony_ci struct cx18_in_work_order *order; 52662306a36Sopenharmony_ci int submit; 52762306a36Sopenharmony_ci int i; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci switch (rpu) { 53062306a36Sopenharmony_ci case CPU: 53162306a36Sopenharmony_ci mb = &cx->scb->cpu2epu_mb; 53262306a36Sopenharmony_ci break; 53362306a36Sopenharmony_ci case APU: 53462306a36Sopenharmony_ci mb = &cx->scb->apu2epu_mb; 53562306a36Sopenharmony_ci break; 53662306a36Sopenharmony_ci default: 53762306a36Sopenharmony_ci return; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci order = alloc_in_work_order_irq(cx); 54162306a36Sopenharmony_ci if (order == NULL) { 54262306a36Sopenharmony_ci CX18_WARN("Unable to find blank work order form to schedule incoming mailbox command processing\n"); 54362306a36Sopenharmony_ci return; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci order->flags = 0; 54762306a36Sopenharmony_ci order->rpu = rpu; 54862306a36Sopenharmony_ci order_mb = &order->mb; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* mb->cmd and mb->args[0] through mb->args[2] */ 55162306a36Sopenharmony_ci for (i = 0; i < 4; i++) 55262306a36Sopenharmony_ci (&order_mb->cmd)[i] = cx18_readl(cx, &mb->cmd + i); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* mb->request and mb->ack. N.B. we want to read mb->ack last */ 55562306a36Sopenharmony_ci for (i = 0; i < 2; i++) 55662306a36Sopenharmony_ci (&order_mb->request)[i] = cx18_readl(cx, &mb->request + i); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (order_mb->request == order_mb->ack) { 55962306a36Sopenharmony_ci CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our incoming %s to EPU mailbox (sequence no. %u)\n", 56062306a36Sopenharmony_ci rpu_str[rpu], rpu_str[rpu], order_mb->request); 56162306a36Sopenharmony_ci if (cx18_debug & CX18_DBGFLG_WARN) 56262306a36Sopenharmony_ci dump_mb(cx, order_mb, "incoming"); 56362306a36Sopenharmony_ci order->flags = CX18_F_EWO_MB_STALE_UPON_RECEIPT; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* 56762306a36Sopenharmony_ci * Individual EPU command processing is responsible for ack-ing 56862306a36Sopenharmony_ci * a non-stale mailbox as soon as possible 56962306a36Sopenharmony_ci */ 57062306a36Sopenharmony_ci submit = epu_cmd_irq(cx, order); 57162306a36Sopenharmony_ci if (submit > 0) { 57262306a36Sopenharmony_ci queue_work(cx->in_work_queue, &order->work); 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci/* 57862306a36Sopenharmony_ci * Functions called from a non-interrupt, non work_queue context 57962306a36Sopenharmony_ci */ 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci const struct cx18_api_info *info = find_api_info(cmd); 58462306a36Sopenharmony_ci u32 irq, req, ack, err; 58562306a36Sopenharmony_ci struct cx18_mailbox __iomem *mb; 58662306a36Sopenharmony_ci wait_queue_head_t *waitq; 58762306a36Sopenharmony_ci struct mutex *mb_lock; 58862306a36Sopenharmony_ci unsigned long int t0, timeout, ret; 58962306a36Sopenharmony_ci int i; 59062306a36Sopenharmony_ci char argstr[MAX_MB_ARGUMENTS*11+1]; 59162306a36Sopenharmony_ci DEFINE_WAIT(w); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (info == NULL) { 59462306a36Sopenharmony_ci CX18_WARN("unknown cmd %x\n", cmd); 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (cx18_debug & CX18_DBGFLG_API) { /* only call u32arr2hex if needed */ 59962306a36Sopenharmony_ci if (cmd == CX18_CPU_DE_SET_MDL) { 60062306a36Sopenharmony_ci if (cx18_debug & CX18_DBGFLG_HIGHVOL) 60162306a36Sopenharmony_ci CX18_DEBUG_HI_API("%s\tcmd %#010x args%s\n", 60262306a36Sopenharmony_ci info->name, cmd, 60362306a36Sopenharmony_ci u32arr2hex(data, args, argstr)); 60462306a36Sopenharmony_ci } else 60562306a36Sopenharmony_ci CX18_DEBUG_API("%s\tcmd %#010x args%s\n", 60662306a36Sopenharmony_ci info->name, cmd, 60762306a36Sopenharmony_ci u32arr2hex(data, args, argstr)); 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci switch (info->rpu) { 61162306a36Sopenharmony_ci case APU: 61262306a36Sopenharmony_ci waitq = &cx->mb_apu_waitq; 61362306a36Sopenharmony_ci mb_lock = &cx->epu2apu_mb_lock; 61462306a36Sopenharmony_ci irq = IRQ_EPU_TO_APU; 61562306a36Sopenharmony_ci mb = &cx->scb->epu2apu_mb; 61662306a36Sopenharmony_ci break; 61762306a36Sopenharmony_ci case CPU: 61862306a36Sopenharmony_ci waitq = &cx->mb_cpu_waitq; 61962306a36Sopenharmony_ci mb_lock = &cx->epu2cpu_mb_lock; 62062306a36Sopenharmony_ci irq = IRQ_EPU_TO_CPU; 62162306a36Sopenharmony_ci mb = &cx->scb->epu2cpu_mb; 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci default: 62462306a36Sopenharmony_ci CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu); 62562306a36Sopenharmony_ci return -EINVAL; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci mutex_lock(mb_lock); 62962306a36Sopenharmony_ci /* 63062306a36Sopenharmony_ci * Wait for an in-use mailbox to complete 63162306a36Sopenharmony_ci * 63262306a36Sopenharmony_ci * If the XPU is responding with Ack's, the mailbox shouldn't be in 63362306a36Sopenharmony_ci * a busy state, since we serialize access to it on our end. 63462306a36Sopenharmony_ci * 63562306a36Sopenharmony_ci * If the wait for ack after sending a previous command was interrupted 63662306a36Sopenharmony_ci * by a signal, we may get here and find a busy mailbox. After waiting, 63762306a36Sopenharmony_ci * mark it "not busy" from our end, if the XPU hasn't ack'ed it still. 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_ci req = cx18_readl(cx, &mb->request); 64062306a36Sopenharmony_ci timeout = msecs_to_jiffies(10); 64162306a36Sopenharmony_ci ret = wait_event_timeout(*waitq, 64262306a36Sopenharmony_ci (ack = cx18_readl(cx, &mb->ack)) == req, 64362306a36Sopenharmony_ci timeout); 64462306a36Sopenharmony_ci if (req != ack) { 64562306a36Sopenharmony_ci /* waited long enough, make the mbox "not busy" from our end */ 64662306a36Sopenharmony_ci cx18_writel(cx, req, &mb->ack); 64762306a36Sopenharmony_ci CX18_ERR("mbox was found stuck busy when setting up for %s; clearing busy and trying to proceed\n", 64862306a36Sopenharmony_ci info->name); 64962306a36Sopenharmony_ci } else if (ret != timeout) 65062306a36Sopenharmony_ci CX18_DEBUG_API("waited %u msecs for busy mbox to be acked\n", 65162306a36Sopenharmony_ci jiffies_to_msecs(timeout-ret)); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* Build the outgoing mailbox */ 65462306a36Sopenharmony_ci req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci cx18_writel(cx, cmd, &mb->cmd); 65762306a36Sopenharmony_ci for (i = 0; i < args; i++) 65862306a36Sopenharmony_ci cx18_writel(cx, data[i], &mb->args[i]); 65962306a36Sopenharmony_ci cx18_writel(cx, 0, &mb->error); 66062306a36Sopenharmony_ci cx18_writel(cx, req, &mb->request); 66162306a36Sopenharmony_ci cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */ 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* 66462306a36Sopenharmony_ci * Notify the XPU and wait for it to send an Ack back 66562306a36Sopenharmony_ci */ 66662306a36Sopenharmony_ci timeout = msecs_to_jiffies((info->flags & API_FAST) ? 10 : 20); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n", 66962306a36Sopenharmony_ci irq, info->name); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* So we don't miss the wakeup, prepare to wait before notifying fw */ 67262306a36Sopenharmony_ci prepare_to_wait(waitq, &w, TASK_UNINTERRUPTIBLE); 67362306a36Sopenharmony_ci cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci t0 = jiffies; 67662306a36Sopenharmony_ci ack = cx18_readl(cx, &mb->ack); 67762306a36Sopenharmony_ci if (ack != req) { 67862306a36Sopenharmony_ci schedule_timeout(timeout); 67962306a36Sopenharmony_ci ret = jiffies - t0; 68062306a36Sopenharmony_ci ack = cx18_readl(cx, &mb->ack); 68162306a36Sopenharmony_ci } else { 68262306a36Sopenharmony_ci ret = jiffies - t0; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci finish_wait(waitq, &w); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (req != ack) { 68862306a36Sopenharmony_ci mutex_unlock(mb_lock); 68962306a36Sopenharmony_ci if (ret >= timeout) { 69062306a36Sopenharmony_ci /* Timed out */ 69162306a36Sopenharmony_ci CX18_DEBUG_WARN("sending %s timed out waiting %d msecs for RPU acknowledgment\n", 69262306a36Sopenharmony_ci info->name, jiffies_to_msecs(ret)); 69362306a36Sopenharmony_ci } else { 69462306a36Sopenharmony_ci CX18_DEBUG_WARN("woken up before mailbox ack was ready after submitting %s to RPU. only waited %d msecs on req %u but awakened with unmatched ack %u\n", 69562306a36Sopenharmony_ci info->name, 69662306a36Sopenharmony_ci jiffies_to_msecs(ret), 69762306a36Sopenharmony_ci req, ack); 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci return -EINVAL; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (ret >= timeout) 70362306a36Sopenharmony_ci CX18_DEBUG_WARN("failed to be awakened upon RPU acknowledgment sending %s; timed out waiting %d msecs\n", 70462306a36Sopenharmony_ci info->name, jiffies_to_msecs(ret)); 70562306a36Sopenharmony_ci else 70662306a36Sopenharmony_ci CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n", 70762306a36Sopenharmony_ci jiffies_to_msecs(ret), info->name); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* Collect data returned by the XPU */ 71062306a36Sopenharmony_ci for (i = 0; i < MAX_MB_ARGUMENTS; i++) 71162306a36Sopenharmony_ci data[i] = cx18_readl(cx, &mb->args[i]); 71262306a36Sopenharmony_ci err = cx18_readl(cx, &mb->error); 71362306a36Sopenharmony_ci mutex_unlock(mb_lock); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* 71662306a36Sopenharmony_ci * Wait for XPU to perform extra actions for the caller in some cases. 71762306a36Sopenharmony_ci * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all MDLs 71862306a36Sopenharmony_ci * back in a burst shortly thereafter 71962306a36Sopenharmony_ci */ 72062306a36Sopenharmony_ci if (info->flags & API_SLOW) 72162306a36Sopenharmony_ci cx18_msleep_timeout(300, 0); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (err) 72462306a36Sopenharmony_ci CX18_DEBUG_API("mailbox error %08x for command %s\n", err, 72562306a36Sopenharmony_ci info->name); 72662306a36Sopenharmony_ci return err ? -EIO : 0; 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ciint cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci return cx18_api_call(cx, cmd, args, data); 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic int cx18_set_filter_param(struct cx18_stream *s) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct cx18 *cx = s->cx; 73762306a36Sopenharmony_ci u32 mode; 73862306a36Sopenharmony_ci int ret; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci mode = (cx->filter_mode & 1) ? 2 : (cx->spatial_strength ? 1 : 0); 74162306a36Sopenharmony_ci ret = cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, 74262306a36Sopenharmony_ci s->handle, 1, mode, cx->spatial_strength); 74362306a36Sopenharmony_ci mode = (cx->filter_mode & 2) ? 2 : (cx->temporal_strength ? 1 : 0); 74462306a36Sopenharmony_ci ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, 74562306a36Sopenharmony_ci s->handle, 0, mode, cx->temporal_strength); 74662306a36Sopenharmony_ci ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, 74762306a36Sopenharmony_ci s->handle, 2, cx->filter_mode >> 2, 0); 74862306a36Sopenharmony_ci return ret; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ciint cx18_api_func(void *priv, u32 cmd, int in, int out, 75262306a36Sopenharmony_ci u32 data[CX2341X_MBOX_MAX_DATA]) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci struct cx18_stream *s = priv; 75562306a36Sopenharmony_ci struct cx18 *cx = s->cx; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci switch (cmd) { 75862306a36Sopenharmony_ci case CX2341X_ENC_SET_OUTPUT_PORT: 75962306a36Sopenharmony_ci return 0; 76062306a36Sopenharmony_ci case CX2341X_ENC_SET_FRAME_RATE: 76162306a36Sopenharmony_ci return cx18_vapi(cx, CX18_CPU_SET_VIDEO_IN, 6, 76262306a36Sopenharmony_ci s->handle, 0, 0, 0, 0, data[0]); 76362306a36Sopenharmony_ci case CX2341X_ENC_SET_FRAME_SIZE: 76462306a36Sopenharmony_ci return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RESOLUTION, 3, 76562306a36Sopenharmony_ci s->handle, data[1], data[0]); 76662306a36Sopenharmony_ci case CX2341X_ENC_SET_STREAM_TYPE: 76762306a36Sopenharmony_ci return cx18_vapi(cx, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 2, 76862306a36Sopenharmony_ci s->handle, data[0]); 76962306a36Sopenharmony_ci case CX2341X_ENC_SET_ASPECT_RATIO: 77062306a36Sopenharmony_ci return cx18_vapi(cx, CX18_CPU_SET_ASPECT_RATIO, 2, 77162306a36Sopenharmony_ci s->handle, data[0]); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci case CX2341X_ENC_SET_GOP_PROPERTIES: 77462306a36Sopenharmony_ci return cx18_vapi(cx, CX18_CPU_SET_GOP_STRUCTURE, 3, 77562306a36Sopenharmony_ci s->handle, data[0], data[1]); 77662306a36Sopenharmony_ci case CX2341X_ENC_SET_GOP_CLOSURE: 77762306a36Sopenharmony_ci return 0; 77862306a36Sopenharmony_ci case CX2341X_ENC_SET_AUDIO_PROPERTIES: 77962306a36Sopenharmony_ci return cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2, 78062306a36Sopenharmony_ci s->handle, data[0]); 78162306a36Sopenharmony_ci case CX2341X_ENC_MUTE_AUDIO: 78262306a36Sopenharmony_ci return cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, 78362306a36Sopenharmony_ci s->handle, data[0]); 78462306a36Sopenharmony_ci case CX2341X_ENC_SET_BIT_RATE: 78562306a36Sopenharmony_ci return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RATE, 5, 78662306a36Sopenharmony_ci s->handle, data[0], data[1], data[2], data[3]); 78762306a36Sopenharmony_ci case CX2341X_ENC_MUTE_VIDEO: 78862306a36Sopenharmony_ci return cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, 78962306a36Sopenharmony_ci s->handle, data[0]); 79062306a36Sopenharmony_ci case CX2341X_ENC_SET_FRAME_DROP_RATE: 79162306a36Sopenharmony_ci return cx18_vapi(cx, CX18_CPU_SET_SKIP_INPUT_FRAME, 2, 79262306a36Sopenharmony_ci s->handle, data[0]); 79362306a36Sopenharmony_ci case CX2341X_ENC_MISC: 79462306a36Sopenharmony_ci return cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 4, 79562306a36Sopenharmony_ci s->handle, data[0], data[1], data[2]); 79662306a36Sopenharmony_ci case CX2341X_ENC_SET_DNR_FILTER_MODE: 79762306a36Sopenharmony_ci cx->filter_mode = (data[0] & 3) | (data[1] << 2); 79862306a36Sopenharmony_ci return cx18_set_filter_param(s); 79962306a36Sopenharmony_ci case CX2341X_ENC_SET_DNR_FILTER_PROPS: 80062306a36Sopenharmony_ci cx->spatial_strength = data[0]; 80162306a36Sopenharmony_ci cx->temporal_strength = data[1]; 80262306a36Sopenharmony_ci return cx18_set_filter_param(s); 80362306a36Sopenharmony_ci case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE: 80462306a36Sopenharmony_ci return cx18_vapi(cx, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 3, 80562306a36Sopenharmony_ci s->handle, data[0], data[1]); 80662306a36Sopenharmony_ci case CX2341X_ENC_SET_CORING_LEVELS: 80762306a36Sopenharmony_ci return cx18_vapi(cx, CX18_CPU_SET_MEDIAN_CORING, 5, 80862306a36Sopenharmony_ci s->handle, data[0], data[1], data[2], data[3]); 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci CX18_WARN("Unknown cmd %x\n", cmd); 81162306a36Sopenharmony_ci return 0; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ciint cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], 81562306a36Sopenharmony_ci u32 cmd, int args, ...) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci va_list ap; 81862306a36Sopenharmony_ci int i; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci va_start(ap, args); 82162306a36Sopenharmony_ci for (i = 0; i < args; i++) 82262306a36Sopenharmony_ci data[i] = va_arg(ap, u32); 82362306a36Sopenharmony_ci va_end(ap); 82462306a36Sopenharmony_ci return cx18_api(cx, cmd, args, data); 82562306a36Sopenharmony_ci} 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ciint cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci u32 data[MAX_MB_ARGUMENTS]; 83062306a36Sopenharmony_ci va_list ap; 83162306a36Sopenharmony_ci int i; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (cx == NULL) { 83462306a36Sopenharmony_ci CX18_ERR("cx == NULL (cmd=%x)\n", cmd); 83562306a36Sopenharmony_ci return 0; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci if (args > MAX_MB_ARGUMENTS) { 83862306a36Sopenharmony_ci CX18_ERR("args too big (cmd=%x)\n", cmd); 83962306a36Sopenharmony_ci args = MAX_MB_ARGUMENTS; 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci va_start(ap, args); 84262306a36Sopenharmony_ci for (i = 0; i < args; i++) 84362306a36Sopenharmony_ci data[i] = va_arg(ap, u32); 84462306a36Sopenharmony_ci va_end(ap); 84562306a36Sopenharmony_ci return cx18_api(cx, cmd, args, data); 84662306a36Sopenharmony_ci} 847