162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci mailbox functions 462306a36Sopenharmony_ci Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> 562306a36Sopenharmony_ci Copyright (C) 2004 Chris Kennedy <c@groovy.org> 662306a36Sopenharmony_ci Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "ivtv-driver.h" 1162306a36Sopenharmony_ci#include "ivtv-mailbox.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* Firmware mailbox flags*/ 1462306a36Sopenharmony_ci#define IVTV_MBOX_FIRMWARE_DONE 0x00000004 1562306a36Sopenharmony_ci#define IVTV_MBOX_DRIVER_DONE 0x00000002 1662306a36Sopenharmony_ci#define IVTV_MBOX_DRIVER_BUSY 0x00000001 1762306a36Sopenharmony_ci#define IVTV_MBOX_FREE 0x00000000 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* Firmware mailbox standard timeout */ 2062306a36Sopenharmony_ci#define IVTV_API_STD_TIMEOUT 0x02000000 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define API_CACHE (1 << 0) /* Allow the command to be stored in the cache */ 2362306a36Sopenharmony_ci#define API_RESULT (1 << 1) /* Allow 1 second for this cmd to end */ 2462306a36Sopenharmony_ci#define API_FAST_RESULT (3 << 1) /* Allow 0.1 second for this cmd to end */ 2562306a36Sopenharmony_ci#define API_DMA (1 << 3) /* DMA mailbox, has special handling */ 2662306a36Sopenharmony_ci#define API_HIGH_VOL (1 << 5) /* High volume command (i.e. called during encoding or decoding) */ 2762306a36Sopenharmony_ci#define API_NO_WAIT_MB (1 << 4) /* Command may not wait for a free mailbox */ 2862306a36Sopenharmony_ci#define API_NO_WAIT_RES (1 << 5) /* Command may not wait for the result */ 2962306a36Sopenharmony_ci#define API_NO_POLL (1 << 6) /* Avoid pointless polling */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct ivtv_api_info { 3262306a36Sopenharmony_ci int flags; /* Flags, see above */ 3362306a36Sopenharmony_ci const char *name; /* The name of the command */ 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define API_ENTRY(x, f) [x] = { (f), #x } 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic const struct ivtv_api_info api_info[256] = { 3962306a36Sopenharmony_ci /* MPEG encoder API */ 4062306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_PING_FW, API_FAST_RESULT), 4162306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_START_CAPTURE, API_RESULT | API_NO_POLL), 4262306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_STOP_CAPTURE, API_RESULT), 4362306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_AUDIO_ID, API_CACHE), 4462306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_VIDEO_ID, API_CACHE), 4562306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_PCR_ID, API_CACHE), 4662306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_FRAME_RATE, API_CACHE), 4762306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_FRAME_SIZE, API_CACHE), 4862306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_BIT_RATE, API_CACHE), 4962306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_GOP_PROPERTIES, API_CACHE), 5062306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_ASPECT_RATIO, API_CACHE), 5162306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_MODE, API_CACHE), 5262306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_PROPS, API_CACHE), 5362306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_CORING_LEVELS, API_CACHE), 5462306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, API_CACHE), 5562306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_VBI_LINE, API_RESULT), 5662306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_STREAM_TYPE, API_CACHE), 5762306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_OUTPUT_PORT, API_CACHE), 5862306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_AUDIO_PROPERTIES, API_CACHE), 5962306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_HALT_FW, API_FAST_RESULT), 6062306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_GET_VERSION, API_FAST_RESULT), 6162306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_GOP_CLOSURE, API_CACHE), 6262306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_GET_SEQ_END, API_RESULT), 6362306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_PGM_INDEX_INFO, API_FAST_RESULT), 6462306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_VBI_CONFIG, API_RESULT), 6562306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_DMA_BLOCK_SIZE, API_CACHE), 6662306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_10, API_FAST_RESULT), 6762306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_9, API_FAST_RESULT), 6862306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SCHED_DMA_TO_HOST, API_DMA | API_HIGH_VOL), 6962306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_INITIALIZE_INPUT, API_RESULT), 7062306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_FRAME_DROP_RATE, API_CACHE), 7162306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_PAUSE_ENCODER, API_RESULT), 7262306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_REFRESH_INPUT, API_NO_WAIT_MB | API_HIGH_VOL), 7362306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_COPYRIGHT, API_CACHE), 7462306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_EVENT_NOTIFICATION, API_RESULT), 7562306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_NUM_VSYNC_LINES, API_CACHE), 7662306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_PLACEHOLDER, API_CACHE), 7762306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_MUTE_VIDEO, API_RESULT), 7862306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_MUTE_AUDIO, API_RESULT), 7962306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_SET_VERT_CROP_LINE, API_FAST_RESULT), 8062306a36Sopenharmony_ci API_ENTRY(CX2341X_ENC_MISC, API_FAST_RESULT), 8162306a36Sopenharmony_ci /* Obsolete PULLDOWN API command */ 8262306a36Sopenharmony_ci API_ENTRY(0xb1, API_CACHE), 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* MPEG decoder API */ 8562306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_PING_FW, API_FAST_RESULT), 8662306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_START_PLAYBACK, API_RESULT | API_NO_POLL), 8762306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_STOP_PLAYBACK, API_RESULT), 8862306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_SET_PLAYBACK_SPEED, API_RESULT), 8962306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_STEP_VIDEO, API_RESULT), 9062306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_SET_DMA_BLOCK_SIZE, API_CACHE), 9162306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_GET_XFER_INFO, API_FAST_RESULT), 9262306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_GET_DMA_STATUS, API_FAST_RESULT), 9362306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_SCHED_DMA_FROM_HOST, API_DMA | API_HIGH_VOL), 9462306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_PAUSE_PLAYBACK, API_RESULT), 9562306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_HALT_FW, API_FAST_RESULT), 9662306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_SET_STANDARD, API_CACHE), 9762306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_GET_VERSION, API_FAST_RESULT), 9862306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_SET_STREAM_INPUT, API_CACHE), 9962306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_GET_TIMING_INFO, API_RESULT /*| API_NO_WAIT_RES*/), 10062306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_SET_AUDIO_MODE, API_CACHE), 10162306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_SET_EVENT_NOTIFICATION, API_RESULT), 10262306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_SET_DISPLAY_BUFFERS, API_CACHE), 10362306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_EXTRACT_VBI, API_RESULT), 10462306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_SET_DECODER_SOURCE, API_FAST_RESULT), 10562306a36Sopenharmony_ci API_ENTRY(CX2341X_DEC_SET_PREBUFFERING, API_CACHE), 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* OSD API */ 10862306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_GET_FRAMEBUFFER, API_FAST_RESULT), 10962306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_GET_PIXEL_FORMAT, API_FAST_RESULT), 11062306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_SET_PIXEL_FORMAT, API_CACHE), 11162306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_GET_STATE, API_FAST_RESULT), 11262306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_SET_STATE, API_CACHE), 11362306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_GET_OSD_COORDS, API_FAST_RESULT), 11462306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_SET_OSD_COORDS, API_CACHE), 11562306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_GET_SCREEN_COORDS, API_FAST_RESULT), 11662306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_SET_SCREEN_COORDS, API_CACHE), 11762306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_GET_GLOBAL_ALPHA, API_FAST_RESULT), 11862306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_SET_GLOBAL_ALPHA, API_CACHE), 11962306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_SET_BLEND_COORDS, API_CACHE), 12062306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_GET_FLICKER_STATE, API_FAST_RESULT), 12162306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_SET_FLICKER_STATE, API_CACHE), 12262306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_BLT_COPY, API_RESULT), 12362306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_BLT_FILL, API_RESULT), 12462306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_BLT_TEXT, API_RESULT), 12562306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, API_CACHE), 12662306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_SET_CHROMA_KEY, API_CACHE), 12762306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_GET_ALPHA_CONTENT_INDEX, API_FAST_RESULT), 12862306a36Sopenharmony_ci API_ENTRY(CX2341X_OSD_SET_ALPHA_CONTENT_INDEX, API_CACHE) 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int try_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int mb) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci u32 flags = readl(&mbdata->mbox[mb].flags); 13462306a36Sopenharmony_ci int is_free = flags == IVTV_MBOX_FREE || (flags & IVTV_MBOX_FIRMWARE_DONE); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* if the mailbox is free, then try to claim it */ 13762306a36Sopenharmony_ci if (is_free && !test_and_set_bit(mb, &mbdata->busy)) { 13862306a36Sopenharmony_ci write_sync(IVTV_MBOX_DRIVER_BUSY, &mbdata->mbox[mb].flags); 13962306a36Sopenharmony_ci return 1; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* Try to find a free mailbox. Note mailbox 0 is reserved for DMA and so is not 14562306a36Sopenharmony_ci attempted here. */ 14662306a36Sopenharmony_cistatic int get_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int flags) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci unsigned long then = jiffies; 14962306a36Sopenharmony_ci int i, mb; 15062306a36Sopenharmony_ci int max_mbox = mbdata->max_mbox; 15162306a36Sopenharmony_ci int retries = 100; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* All slow commands use the same mailbox, serializing them and also 15462306a36Sopenharmony_ci leaving the other mailbox free for simple fast commands. */ 15562306a36Sopenharmony_ci if ((flags & API_FAST_RESULT) == API_RESULT) 15662306a36Sopenharmony_ci max_mbox = 1; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* find free non-DMA mailbox */ 15962306a36Sopenharmony_ci for (i = 0; i < retries; i++) { 16062306a36Sopenharmony_ci for (mb = 1; mb <= max_mbox; mb++) 16162306a36Sopenharmony_ci if (try_mailbox(itv, mbdata, mb)) 16262306a36Sopenharmony_ci return mb; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* Sleep before a retry, if not atomic */ 16562306a36Sopenharmony_ci if (!(flags & API_NO_WAIT_MB)) { 16662306a36Sopenharmony_ci if (time_after(jiffies, 16762306a36Sopenharmony_ci then + msecs_to_jiffies(10*retries))) 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci ivtv_msleep_timeout(10, 0); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci return -ENODEV; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic void write_mailbox(volatile struct ivtv_mailbox __iomem *mbox, int cmd, int args, u32 data[]) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci int i; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci write_sync(cmd, &mbox->cmd); 18062306a36Sopenharmony_ci write_sync(IVTV_API_STD_TIMEOUT, &mbox->timeout); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++) 18362306a36Sopenharmony_ci write_sync(data[i], &mbox->data[i]); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci write_sync(IVTV_MBOX_DRIVER_DONE | IVTV_MBOX_DRIVER_BUSY, &mbox->flags); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic void clear_all_mailboxes(struct ivtv *itv, struct ivtv_mailbox_data *mbdata) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci int i; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci for (i = 0; i <= mbdata->max_mbox; i++) { 19362306a36Sopenharmony_ci IVTV_DEBUG_WARN("Clearing mailbox %d: cmd 0x%08x flags 0x%08x\n", 19462306a36Sopenharmony_ci i, readl(&mbdata->mbox[i].cmd), readl(&mbdata->mbox[i].flags)); 19562306a36Sopenharmony_ci write_sync(0, &mbdata->mbox[i].flags); 19662306a36Sopenharmony_ci clear_bit(i, &mbdata->busy); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[]) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct ivtv_mailbox_data *mbdata = (cmd >= 128) ? &itv->enc_mbox : &itv->dec_mbox; 20362306a36Sopenharmony_ci volatile struct ivtv_mailbox __iomem *mbox; 20462306a36Sopenharmony_ci int api_timeout = msecs_to_jiffies(1000); 20562306a36Sopenharmony_ci int flags, mb, i; 20662306a36Sopenharmony_ci unsigned long then; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* sanity checks */ 20962306a36Sopenharmony_ci if (NULL == mbdata) { 21062306a36Sopenharmony_ci IVTV_ERR("No mailbox allocated\n"); 21162306a36Sopenharmony_ci return -ENODEV; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci if (args < 0 || args > CX2341X_MBOX_MAX_DATA || 21462306a36Sopenharmony_ci cmd < 0 || cmd > 255 || api_info[cmd].name == NULL) { 21562306a36Sopenharmony_ci IVTV_ERR("Invalid MB call: cmd = 0x%02x, args = %d\n", cmd, args); 21662306a36Sopenharmony_ci return -EINVAL; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (api_info[cmd].flags & API_HIGH_VOL) { 22062306a36Sopenharmony_ci IVTV_DEBUG_HI_MB("MB Call: %s\n", api_info[cmd].name); 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci else { 22362306a36Sopenharmony_ci IVTV_DEBUG_MB("MB Call: %s\n", api_info[cmd].name); 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* clear possibly uninitialized part of data array */ 22762306a36Sopenharmony_ci for (i = args; i < CX2341X_MBOX_MAX_DATA; i++) 22862306a36Sopenharmony_ci data[i] = 0; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* If this command was issued within the last 30 minutes and with identical 23162306a36Sopenharmony_ci data, then just return 0 as there is no need to issue this command again. 23262306a36Sopenharmony_ci Just an optimization to prevent unnecessary use of mailboxes. */ 23362306a36Sopenharmony_ci if (itv->api_cache[cmd].last_jiffies && 23462306a36Sopenharmony_ci time_before(jiffies, 23562306a36Sopenharmony_ci itv->api_cache[cmd].last_jiffies + 23662306a36Sopenharmony_ci msecs_to_jiffies(1800000)) && 23762306a36Sopenharmony_ci !memcmp(data, itv->api_cache[cmd].data, sizeof(itv->api_cache[cmd].data))) { 23862306a36Sopenharmony_ci itv->api_cache[cmd].last_jiffies = jiffies; 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci flags = api_info[cmd].flags; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (flags & API_DMA) { 24562306a36Sopenharmony_ci for (i = 0; i < 100; i++) { 24662306a36Sopenharmony_ci mb = i % (mbdata->max_mbox + 1); 24762306a36Sopenharmony_ci if (try_mailbox(itv, mbdata, mb)) { 24862306a36Sopenharmony_ci write_mailbox(&mbdata->mbox[mb], cmd, args, data); 24962306a36Sopenharmony_ci clear_bit(mb, &mbdata->busy); 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci IVTV_DEBUG_WARN("%s: mailbox %d not free %08x\n", 25362306a36Sopenharmony_ci api_info[cmd].name, mb, readl(&mbdata->mbox[mb].flags)); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci IVTV_WARN("Could not find free DMA mailbox for %s\n", api_info[cmd].name); 25662306a36Sopenharmony_ci clear_all_mailboxes(itv, mbdata); 25762306a36Sopenharmony_ci return -EBUSY; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if ((flags & API_FAST_RESULT) == API_FAST_RESULT) 26162306a36Sopenharmony_ci api_timeout = msecs_to_jiffies(100); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci mb = get_mailbox(itv, mbdata, flags); 26462306a36Sopenharmony_ci if (mb < 0) { 26562306a36Sopenharmony_ci IVTV_DEBUG_WARN("No free mailbox found (%s)\n", api_info[cmd].name); 26662306a36Sopenharmony_ci clear_all_mailboxes(itv, mbdata); 26762306a36Sopenharmony_ci return -EBUSY; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci mbox = &mbdata->mbox[mb]; 27062306a36Sopenharmony_ci write_mailbox(mbox, cmd, args, data); 27162306a36Sopenharmony_ci if (flags & API_CACHE) { 27262306a36Sopenharmony_ci memcpy(itv->api_cache[cmd].data, data, sizeof(itv->api_cache[cmd].data)); 27362306a36Sopenharmony_ci itv->api_cache[cmd].last_jiffies = jiffies; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci if ((flags & API_RESULT) == 0) { 27662306a36Sopenharmony_ci clear_bit(mb, &mbdata->busy); 27762306a36Sopenharmony_ci return 0; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Get results */ 28162306a36Sopenharmony_ci then = jiffies; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (!(flags & API_NO_POLL)) { 28462306a36Sopenharmony_ci /* First try to poll, then switch to delays */ 28562306a36Sopenharmony_ci for (i = 0; i < 100; i++) { 28662306a36Sopenharmony_ci if (readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE) 28762306a36Sopenharmony_ci break; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci while (!(readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)) { 29162306a36Sopenharmony_ci if (time_after(jiffies, then + api_timeout)) { 29262306a36Sopenharmony_ci IVTV_DEBUG_WARN("Could not get result (%s)\n", api_info[cmd].name); 29362306a36Sopenharmony_ci /* reset the mailbox, but it is likely too late already */ 29462306a36Sopenharmony_ci write_sync(0, &mbox->flags); 29562306a36Sopenharmony_ci clear_bit(mb, &mbdata->busy); 29662306a36Sopenharmony_ci return -EIO; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci if (flags & API_NO_WAIT_RES) 29962306a36Sopenharmony_ci mdelay(1); 30062306a36Sopenharmony_ci else 30162306a36Sopenharmony_ci ivtv_msleep_timeout(1, 0); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci if (time_after(jiffies, then + msecs_to_jiffies(100))) 30462306a36Sopenharmony_ci IVTV_DEBUG_WARN("%s took %u jiffies\n", 30562306a36Sopenharmony_ci api_info[cmd].name, 30662306a36Sopenharmony_ci jiffies_to_msecs(jiffies - then)); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++) 30962306a36Sopenharmony_ci data[i] = readl(&mbox->data[i]); 31062306a36Sopenharmony_ci write_sync(0, &mbox->flags); 31162306a36Sopenharmony_ci clear_bit(mb, &mbdata->busy); 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ciint ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci int res = ivtv_api_call(itv, cmd, args, data); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* Allow a single retry, probably already too late though. 32062306a36Sopenharmony_ci If there is no free mailbox then that is usually an indication 32162306a36Sopenharmony_ci of a more serious problem. */ 32262306a36Sopenharmony_ci return (res == -EBUSY) ? ivtv_api_call(itv, cmd, args, data) : res; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ciint ivtv_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci return ivtv_api(priv, cmd, in, data); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ciint ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci va_list ap; 33362306a36Sopenharmony_ci int i; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci va_start(ap, args); 33662306a36Sopenharmony_ci for (i = 0; i < args; i++) { 33762306a36Sopenharmony_ci data[i] = va_arg(ap, u32); 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci va_end(ap); 34062306a36Sopenharmony_ci return ivtv_api(itv, cmd, args, data); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ciint ivtv_vapi(struct ivtv *itv, int cmd, int args, ...) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci u32 data[CX2341X_MBOX_MAX_DATA]; 34662306a36Sopenharmony_ci va_list ap; 34762306a36Sopenharmony_ci int i; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci va_start(ap, args); 35062306a36Sopenharmony_ci for (i = 0; i < args; i++) { 35162306a36Sopenharmony_ci data[i] = va_arg(ap, u32); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci va_end(ap); 35462306a36Sopenharmony_ci return ivtv_api(itv, cmd, args, data); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci/* This one is for stuff that can't sleep.. irq handlers, etc.. */ 35862306a36Sopenharmony_civoid ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, 35962306a36Sopenharmony_ci int argc, u32 data[]) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci volatile u32 __iomem *p = mbdata->mbox[mb].data; 36262306a36Sopenharmony_ci int i; 36362306a36Sopenharmony_ci for (i = 0; i < argc; i++, p++) 36462306a36Sopenharmony_ci data[i] = readl(p); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/* Wipe api cache */ 36862306a36Sopenharmony_civoid ivtv_mailbox_cache_invalidate(struct ivtv *itv) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci int i; 37162306a36Sopenharmony_ci for (i = 0; i < 256; i++) 37262306a36Sopenharmony_ci itv->api_cache[i].last_jiffies = 0; 37362306a36Sopenharmony_ci} 374