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