18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci    mailbox functions
48c2ecf20Sopenharmony_ci    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
58c2ecf20Sopenharmony_ci    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
68c2ecf20Sopenharmony_ci    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "ivtv-driver.h"
118c2ecf20Sopenharmony_ci#include "ivtv-mailbox.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/* Firmware mailbox flags*/
148c2ecf20Sopenharmony_ci#define IVTV_MBOX_FIRMWARE_DONE 0x00000004
158c2ecf20Sopenharmony_ci#define IVTV_MBOX_DRIVER_DONE   0x00000002
168c2ecf20Sopenharmony_ci#define IVTV_MBOX_DRIVER_BUSY   0x00000001
178c2ecf20Sopenharmony_ci#define IVTV_MBOX_FREE		0x00000000
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* Firmware mailbox standard timeout */
208c2ecf20Sopenharmony_ci#define IVTV_API_STD_TIMEOUT	0x02000000
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define API_CACHE	 (1 << 0)	/* Allow the command to be stored in the cache */
238c2ecf20Sopenharmony_ci#define API_RESULT	 (1 << 1)	/* Allow 1 second for this cmd to end */
248c2ecf20Sopenharmony_ci#define API_FAST_RESULT	 (3 << 1)	/* Allow 0.1 second for this cmd to end */
258c2ecf20Sopenharmony_ci#define API_DMA		 (1 << 3)	/* DMA mailbox, has special handling */
268c2ecf20Sopenharmony_ci#define API_HIGH_VOL	 (1 << 5)	/* High volume command (i.e. called during encoding or decoding) */
278c2ecf20Sopenharmony_ci#define API_NO_WAIT_MB	 (1 << 4)	/* Command may not wait for a free mailbox */
288c2ecf20Sopenharmony_ci#define API_NO_WAIT_RES	 (1 << 5)	/* Command may not wait for the result */
298c2ecf20Sopenharmony_ci#define API_NO_POLL	 (1 << 6)	/* Avoid pointless polling */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct ivtv_api_info {
328c2ecf20Sopenharmony_ci	int flags;		/* Flags, see above */
338c2ecf20Sopenharmony_ci	const char *name;	/* The name of the command */
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define API_ENTRY(x, f) [x] = { (f), #x }
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic const struct ivtv_api_info api_info[256] = {
398c2ecf20Sopenharmony_ci	/* MPEG encoder API */
408c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_PING_FW,			API_FAST_RESULT),
418c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_START_CAPTURE,		API_RESULT | API_NO_POLL),
428c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_STOP_CAPTURE,		API_RESULT),
438c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_AUDIO_ID,		API_CACHE),
448c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_VIDEO_ID,		API_CACHE),
458c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_PCR_ID,		API_CACHE),
468c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_FRAME_RATE,		API_CACHE),
478c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_FRAME_SIZE,		API_CACHE),
488c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_BIT_RATE,		API_CACHE),
498c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_GOP_PROPERTIES,	API_CACHE),
508c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_ASPECT_RATIO,		API_CACHE),
518c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_MODE,	API_CACHE),
528c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_PROPS,	API_CACHE),
538c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_CORING_LEVELS,	API_CACHE),
548c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_SPATIAL_FILTER_TYPE,	API_CACHE),
558c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_VBI_LINE,		API_RESULT),
568c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_STREAM_TYPE,		API_CACHE),
578c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_OUTPUT_PORT,		API_CACHE),
588c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_AUDIO_PROPERTIES,	API_CACHE),
598c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_HALT_FW,			API_FAST_RESULT),
608c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_GET_VERSION,		API_FAST_RESULT),
618c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_GOP_CLOSURE,		API_CACHE),
628c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_GET_SEQ_END,		API_RESULT),
638c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_PGM_INDEX_INFO,	API_FAST_RESULT),
648c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_VBI_CONFIG,		API_RESULT),
658c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_DMA_BLOCK_SIZE,	API_CACHE),
668c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_10,	API_FAST_RESULT),
678c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_9,	API_FAST_RESULT),
688c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SCHED_DMA_TO_HOST,	API_DMA | API_HIGH_VOL),
698c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_INITIALIZE_INPUT,		API_RESULT),
708c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_FRAME_DROP_RATE,	API_CACHE),
718c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_PAUSE_ENCODER,		API_RESULT),
728c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_REFRESH_INPUT,		API_NO_WAIT_MB | API_HIGH_VOL),
738c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_COPYRIGHT,		API_CACHE),
748c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_EVENT_NOTIFICATION,	API_RESULT),
758c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_NUM_VSYNC_LINES,	API_CACHE),
768c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_PLACEHOLDER,		API_CACHE),
778c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_MUTE_VIDEO,		API_RESULT),
788c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_MUTE_AUDIO,		API_RESULT),
798c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_SET_VERT_CROP_LINE,	API_FAST_RESULT),
808c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_ENC_MISC,			API_FAST_RESULT),
818c2ecf20Sopenharmony_ci	/* Obsolete PULLDOWN API command */
828c2ecf20Sopenharmony_ci	API_ENTRY(0xb1,					API_CACHE),
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/* MPEG decoder API */
858c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_PING_FW,			API_FAST_RESULT),
868c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_START_PLAYBACK,		API_RESULT | API_NO_POLL),
878c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_STOP_PLAYBACK,		API_RESULT),
888c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_SET_PLAYBACK_SPEED,	API_RESULT),
898c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_STEP_VIDEO,		API_RESULT),
908c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_SET_DMA_BLOCK_SIZE,	API_CACHE),
918c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_GET_XFER_INFO,		API_FAST_RESULT),
928c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_GET_DMA_STATUS,		API_FAST_RESULT),
938c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_SCHED_DMA_FROM_HOST,	API_DMA | API_HIGH_VOL),
948c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_PAUSE_PLAYBACK,		API_RESULT),
958c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_HALT_FW,			API_FAST_RESULT),
968c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_SET_STANDARD,		API_CACHE),
978c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_GET_VERSION,		API_FAST_RESULT),
988c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_SET_STREAM_INPUT,		API_CACHE),
998c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_GET_TIMING_INFO,		API_RESULT /*| API_NO_WAIT_RES*/),
1008c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_SET_AUDIO_MODE,		API_CACHE),
1018c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_SET_EVENT_NOTIFICATION,	API_RESULT),
1028c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_SET_DISPLAY_BUFFERS,	API_CACHE),
1038c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_EXTRACT_VBI,		API_RESULT),
1048c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_SET_DECODER_SOURCE,	API_FAST_RESULT),
1058c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_DEC_SET_PREBUFFERING,		API_CACHE),
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* OSD API */
1088c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_GET_FRAMEBUFFER,		API_FAST_RESULT),
1098c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_GET_PIXEL_FORMAT,		API_FAST_RESULT),
1108c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_SET_PIXEL_FORMAT,		API_CACHE),
1118c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_GET_STATE,		API_FAST_RESULT),
1128c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_SET_STATE,		API_CACHE),
1138c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_GET_OSD_COORDS,		API_FAST_RESULT),
1148c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_SET_OSD_COORDS,		API_CACHE),
1158c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_GET_SCREEN_COORDS,	API_FAST_RESULT),
1168c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_SET_SCREEN_COORDS,	API_CACHE),
1178c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_GET_GLOBAL_ALPHA,		API_FAST_RESULT),
1188c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_SET_GLOBAL_ALPHA,		API_CACHE),
1198c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_SET_BLEND_COORDS,		API_CACHE),
1208c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_GET_FLICKER_STATE,	API_FAST_RESULT),
1218c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_SET_FLICKER_STATE,	API_CACHE),
1228c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_BLT_COPY,			API_RESULT),
1238c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_BLT_FILL,			API_RESULT),
1248c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_BLT_TEXT,			API_RESULT),
1258c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_SET_FRAMEBUFFER_WINDOW,	API_CACHE),
1268c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_SET_CHROMA_KEY,		API_CACHE),
1278c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_GET_ALPHA_CONTENT_INDEX,	API_FAST_RESULT),
1288c2ecf20Sopenharmony_ci	API_ENTRY(CX2341X_OSD_SET_ALPHA_CONTENT_INDEX,	API_CACHE)
1298c2ecf20Sopenharmony_ci};
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int try_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int mb)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	u32 flags = readl(&mbdata->mbox[mb].flags);
1348c2ecf20Sopenharmony_ci	int is_free = flags == IVTV_MBOX_FREE || (flags & IVTV_MBOX_FIRMWARE_DONE);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/* if the mailbox is free, then try to claim it */
1378c2ecf20Sopenharmony_ci	if (is_free && !test_and_set_bit(mb, &mbdata->busy)) {
1388c2ecf20Sopenharmony_ci		write_sync(IVTV_MBOX_DRIVER_BUSY, &mbdata->mbox[mb].flags);
1398c2ecf20Sopenharmony_ci		return 1;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci	return 0;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci/* Try to find a free mailbox. Note mailbox 0 is reserved for DMA and so is not
1458c2ecf20Sopenharmony_ci   attempted here. */
1468c2ecf20Sopenharmony_cistatic int get_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int flags)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	unsigned long then = jiffies;
1498c2ecf20Sopenharmony_ci	int i, mb;
1508c2ecf20Sopenharmony_ci	int max_mbox = mbdata->max_mbox;
1518c2ecf20Sopenharmony_ci	int retries = 100;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* All slow commands use the same mailbox, serializing them and also
1548c2ecf20Sopenharmony_ci	   leaving the other mailbox free for simple fast commands. */
1558c2ecf20Sopenharmony_ci	if ((flags & API_FAST_RESULT) == API_RESULT)
1568c2ecf20Sopenharmony_ci		max_mbox = 1;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* find free non-DMA mailbox */
1598c2ecf20Sopenharmony_ci	for (i = 0; i < retries; i++) {
1608c2ecf20Sopenharmony_ci		for (mb = 1; mb <= max_mbox; mb++)
1618c2ecf20Sopenharmony_ci			if (try_mailbox(itv, mbdata, mb))
1628c2ecf20Sopenharmony_ci				return mb;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci		/* Sleep before a retry, if not atomic */
1658c2ecf20Sopenharmony_ci		if (!(flags & API_NO_WAIT_MB)) {
1668c2ecf20Sopenharmony_ci			if (time_after(jiffies,
1678c2ecf20Sopenharmony_ci				       then + msecs_to_jiffies(10*retries)))
1688c2ecf20Sopenharmony_ci			       break;
1698c2ecf20Sopenharmony_ci			ivtv_msleep_timeout(10, 0);
1708c2ecf20Sopenharmony_ci		}
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci	return -ENODEV;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic void write_mailbox(volatile struct ivtv_mailbox __iomem *mbox, int cmd, int args, u32 data[])
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	int i;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	write_sync(cmd, &mbox->cmd);
1808c2ecf20Sopenharmony_ci	write_sync(IVTV_API_STD_TIMEOUT, &mbox->timeout);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
1838c2ecf20Sopenharmony_ci		write_sync(data[i], &mbox->data[i]);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	write_sync(IVTV_MBOX_DRIVER_DONE | IVTV_MBOX_DRIVER_BUSY, &mbox->flags);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic void clear_all_mailboxes(struct ivtv *itv, struct ivtv_mailbox_data *mbdata)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	int i;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	for (i = 0; i <= mbdata->max_mbox; i++) {
1938c2ecf20Sopenharmony_ci		IVTV_DEBUG_WARN("Clearing mailbox %d: cmd 0x%08x flags 0x%08x\n",
1948c2ecf20Sopenharmony_ci			i, readl(&mbdata->mbox[i].cmd), readl(&mbdata->mbox[i].flags));
1958c2ecf20Sopenharmony_ci		write_sync(0, &mbdata->mbox[i].flags);
1968c2ecf20Sopenharmony_ci		clear_bit(i, &mbdata->busy);
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[])
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	struct ivtv_mailbox_data *mbdata = (cmd >= 128) ? &itv->enc_mbox : &itv->dec_mbox;
2038c2ecf20Sopenharmony_ci	volatile struct ivtv_mailbox __iomem *mbox;
2048c2ecf20Sopenharmony_ci	int api_timeout = msecs_to_jiffies(1000);
2058c2ecf20Sopenharmony_ci	int flags, mb, i;
2068c2ecf20Sopenharmony_ci	unsigned long then;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* sanity checks */
2098c2ecf20Sopenharmony_ci	if (NULL == mbdata) {
2108c2ecf20Sopenharmony_ci		IVTV_ERR("No mailbox allocated\n");
2118c2ecf20Sopenharmony_ci		return -ENODEV;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci	if (args < 0 || args > CX2341X_MBOX_MAX_DATA ||
2148c2ecf20Sopenharmony_ci	    cmd < 0 || cmd > 255 || api_info[cmd].name == NULL) {
2158c2ecf20Sopenharmony_ci		IVTV_ERR("Invalid MB call: cmd = 0x%02x, args = %d\n", cmd, args);
2168c2ecf20Sopenharmony_ci		return -EINVAL;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (api_info[cmd].flags & API_HIGH_VOL) {
2208c2ecf20Sopenharmony_ci	    IVTV_DEBUG_HI_MB("MB Call: %s\n", api_info[cmd].name);
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci	else {
2238c2ecf20Sopenharmony_ci	    IVTV_DEBUG_MB("MB Call: %s\n", api_info[cmd].name);
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* clear possibly uninitialized part of data array */
2278c2ecf20Sopenharmony_ci	for (i = args; i < CX2341X_MBOX_MAX_DATA; i++)
2288c2ecf20Sopenharmony_ci		data[i] = 0;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/* If this command was issued within the last 30 minutes and with identical
2318c2ecf20Sopenharmony_ci	   data, then just return 0 as there is no need to issue this command again.
2328c2ecf20Sopenharmony_ci	   Just an optimization to prevent unnecessary use of mailboxes. */
2338c2ecf20Sopenharmony_ci	if (itv->api_cache[cmd].last_jiffies &&
2348c2ecf20Sopenharmony_ci	    time_before(jiffies,
2358c2ecf20Sopenharmony_ci			itv->api_cache[cmd].last_jiffies +
2368c2ecf20Sopenharmony_ci			msecs_to_jiffies(1800000)) &&
2378c2ecf20Sopenharmony_ci	    !memcmp(data, itv->api_cache[cmd].data, sizeof(itv->api_cache[cmd].data))) {
2388c2ecf20Sopenharmony_ci		itv->api_cache[cmd].last_jiffies = jiffies;
2398c2ecf20Sopenharmony_ci		return 0;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	flags = api_info[cmd].flags;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (flags & API_DMA) {
2458c2ecf20Sopenharmony_ci		for (i = 0; i < 100; i++) {
2468c2ecf20Sopenharmony_ci			mb = i % (mbdata->max_mbox + 1);
2478c2ecf20Sopenharmony_ci			if (try_mailbox(itv, mbdata, mb)) {
2488c2ecf20Sopenharmony_ci				write_mailbox(&mbdata->mbox[mb], cmd, args, data);
2498c2ecf20Sopenharmony_ci				clear_bit(mb, &mbdata->busy);
2508c2ecf20Sopenharmony_ci				return 0;
2518c2ecf20Sopenharmony_ci			}
2528c2ecf20Sopenharmony_ci			IVTV_DEBUG_WARN("%s: mailbox %d not free %08x\n",
2538c2ecf20Sopenharmony_ci					api_info[cmd].name, mb, readl(&mbdata->mbox[mb].flags));
2548c2ecf20Sopenharmony_ci		}
2558c2ecf20Sopenharmony_ci		IVTV_WARN("Could not find free DMA mailbox for %s\n", api_info[cmd].name);
2568c2ecf20Sopenharmony_ci		clear_all_mailboxes(itv, mbdata);
2578c2ecf20Sopenharmony_ci		return -EBUSY;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if ((flags & API_FAST_RESULT) == API_FAST_RESULT)
2618c2ecf20Sopenharmony_ci		api_timeout = msecs_to_jiffies(100);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	mb = get_mailbox(itv, mbdata, flags);
2648c2ecf20Sopenharmony_ci	if (mb < 0) {
2658c2ecf20Sopenharmony_ci		IVTV_DEBUG_WARN("No free mailbox found (%s)\n", api_info[cmd].name);
2668c2ecf20Sopenharmony_ci		clear_all_mailboxes(itv, mbdata);
2678c2ecf20Sopenharmony_ci		return -EBUSY;
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci	mbox = &mbdata->mbox[mb];
2708c2ecf20Sopenharmony_ci	write_mailbox(mbox, cmd, args, data);
2718c2ecf20Sopenharmony_ci	if (flags & API_CACHE) {
2728c2ecf20Sopenharmony_ci		memcpy(itv->api_cache[cmd].data, data, sizeof(itv->api_cache[cmd].data));
2738c2ecf20Sopenharmony_ci		itv->api_cache[cmd].last_jiffies = jiffies;
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci	if ((flags & API_RESULT) == 0) {
2768c2ecf20Sopenharmony_ci		clear_bit(mb, &mbdata->busy);
2778c2ecf20Sopenharmony_ci		return 0;
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	/* Get results */
2818c2ecf20Sopenharmony_ci	then = jiffies;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (!(flags & API_NO_POLL)) {
2848c2ecf20Sopenharmony_ci		/* First try to poll, then switch to delays */
2858c2ecf20Sopenharmony_ci		for (i = 0; i < 100; i++) {
2868c2ecf20Sopenharmony_ci			if (readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)
2878c2ecf20Sopenharmony_ci				break;
2888c2ecf20Sopenharmony_ci		}
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci	while (!(readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)) {
2918c2ecf20Sopenharmony_ci		if (time_after(jiffies, then + api_timeout)) {
2928c2ecf20Sopenharmony_ci			IVTV_DEBUG_WARN("Could not get result (%s)\n", api_info[cmd].name);
2938c2ecf20Sopenharmony_ci			/* reset the mailbox, but it is likely too late already */
2948c2ecf20Sopenharmony_ci			write_sync(0, &mbox->flags);
2958c2ecf20Sopenharmony_ci			clear_bit(mb, &mbdata->busy);
2968c2ecf20Sopenharmony_ci			return -EIO;
2978c2ecf20Sopenharmony_ci		}
2988c2ecf20Sopenharmony_ci		if (flags & API_NO_WAIT_RES)
2998c2ecf20Sopenharmony_ci			mdelay(1);
3008c2ecf20Sopenharmony_ci		else
3018c2ecf20Sopenharmony_ci			ivtv_msleep_timeout(1, 0);
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci	if (time_after(jiffies, then + msecs_to_jiffies(100)))
3048c2ecf20Sopenharmony_ci		IVTV_DEBUG_WARN("%s took %u jiffies\n",
3058c2ecf20Sopenharmony_ci				api_info[cmd].name,
3068c2ecf20Sopenharmony_ci				jiffies_to_msecs(jiffies - then));
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
3098c2ecf20Sopenharmony_ci		data[i] = readl(&mbox->data[i]);
3108c2ecf20Sopenharmony_ci	write_sync(0, &mbox->flags);
3118c2ecf20Sopenharmony_ci	clear_bit(mb, &mbdata->busy);
3128c2ecf20Sopenharmony_ci	return 0;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ciint ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[])
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	int res = ivtv_api_call(itv, cmd, args, data);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	/* Allow a single retry, probably already too late though.
3208c2ecf20Sopenharmony_ci	   If there is no free mailbox then that is usually an indication
3218c2ecf20Sopenharmony_ci	   of a more serious problem. */
3228c2ecf20Sopenharmony_ci	return (res == -EBUSY) ? ivtv_api_call(itv, cmd, args, data) : res;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ciint ivtv_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	return ivtv_api(priv, cmd, in, data);
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ciint ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	va_list ap;
3338c2ecf20Sopenharmony_ci	int i;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	va_start(ap, args);
3368c2ecf20Sopenharmony_ci	for (i = 0; i < args; i++) {
3378c2ecf20Sopenharmony_ci		data[i] = va_arg(ap, u32);
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci	va_end(ap);
3408c2ecf20Sopenharmony_ci	return ivtv_api(itv, cmd, args, data);
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ciint ivtv_vapi(struct ivtv *itv, int cmd, int args, ...)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	u32 data[CX2341X_MBOX_MAX_DATA];
3468c2ecf20Sopenharmony_ci	va_list ap;
3478c2ecf20Sopenharmony_ci	int i;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	va_start(ap, args);
3508c2ecf20Sopenharmony_ci	for (i = 0; i < args; i++) {
3518c2ecf20Sopenharmony_ci		data[i] = va_arg(ap, u32);
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci	va_end(ap);
3548c2ecf20Sopenharmony_ci	return ivtv_api(itv, cmd, args, data);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci/* This one is for stuff that can't sleep.. irq handlers, etc.. */
3588c2ecf20Sopenharmony_civoid ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb,
3598c2ecf20Sopenharmony_ci		       int argc, u32 data[])
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	volatile u32 __iomem *p = mbdata->mbox[mb].data;
3628c2ecf20Sopenharmony_ci	int i;
3638c2ecf20Sopenharmony_ci	for (i = 0; i < argc; i++, p++)
3648c2ecf20Sopenharmony_ci		data[i] = readl(p);
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci/* Wipe api cache */
3688c2ecf20Sopenharmony_civoid ivtv_mailbox_cache_invalidate(struct ivtv *itv)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	int i;
3718c2ecf20Sopenharmony_ci	for (i = 0; i < 256; i++)
3728c2ecf20Sopenharmony_ci		itv->api_cache[i].last_jiffies = 0;
3738c2ecf20Sopenharmony_ci}
374