162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci ivtv firmware 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#include "ivtv-firmware.h" 1362306a36Sopenharmony_ci#include "ivtv-yuv.h" 1462306a36Sopenharmony_ci#include "ivtv-ioctl.h" 1562306a36Sopenharmony_ci#include "ivtv-cards.h" 1662306a36Sopenharmony_ci#include <linux/firmware.h> 1762306a36Sopenharmony_ci#include <media/i2c/saa7127.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE 2062306a36Sopenharmony_ci#define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6 2162306a36Sopenharmony_ci#define IVTV_MASK_VPU_ENABLE16 0xFFFFFFFB 2262306a36Sopenharmony_ci#define IVTV_CMD_VDM_STOP 0x00000000 2362306a36Sopenharmony_ci#define IVTV_CMD_AO_STOP 0x00000005 2462306a36Sopenharmony_ci#define IVTV_CMD_APU_PING 0x00000000 2562306a36Sopenharmony_ci#define IVTV_CMD_VPU_STOP15 0xFFFFFFFE 2662306a36Sopenharmony_ci#define IVTV_CMD_VPU_STOP16 0xFFFFFFEE 2762306a36Sopenharmony_ci#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF 2862306a36Sopenharmony_ci#define IVTV_CMD_SPU_STOP 0x00000001 2962306a36Sopenharmony_ci#define IVTV_CMD_SDRAM_PRECHARGE_INIT 0x0000001A 3062306a36Sopenharmony_ci#define IVTV_CMD_SDRAM_REFRESH_INIT 0x80000640 3162306a36Sopenharmony_ci#define IVTV_SDRAM_SLEEPTIME 600 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define IVTV_DECODE_INIT_MPEG_FILENAME "v4l-cx2341x-init.mpg" 3462306a36Sopenharmony_ci#define IVTV_DECODE_INIT_MPEG_SIZE (152*1024) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* Encoder/decoder firmware sizes */ 3762306a36Sopenharmony_ci#define IVTV_FW_ENC_SIZE (376836) 3862306a36Sopenharmony_ci#define IVTV_FW_DEC_SIZE (256*1024) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv *itv, long size) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci const struct firmware *fw = NULL; 4362306a36Sopenharmony_ci int retries = 3; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciretry: 4662306a36Sopenharmony_ci if (retries && request_firmware(&fw, fn, &itv->pdev->dev) == 0) { 4762306a36Sopenharmony_ci int i; 4862306a36Sopenharmony_ci volatile u32 __iomem *dst = (volatile u32 __iomem *)mem; 4962306a36Sopenharmony_ci const u32 *src = (const u32 *)fw->data; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (fw->size != size) { 5262306a36Sopenharmony_ci /* Due to race conditions in firmware loading (esp. with udev <0.95) 5362306a36Sopenharmony_ci the wrong file was sometimes loaded. So we check filesizes to 5462306a36Sopenharmony_ci see if at least the right-sized file was loaded. If not, then we 5562306a36Sopenharmony_ci retry. */ 5662306a36Sopenharmony_ci IVTV_INFO("Retry: file loaded was not %s (expected size %ld, got %zu)\n", fn, size, fw->size); 5762306a36Sopenharmony_ci release_firmware(fw); 5862306a36Sopenharmony_ci retries--; 5962306a36Sopenharmony_ci goto retry; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci for (i = 0; i < fw->size; i += 4) { 6262306a36Sopenharmony_ci /* no need for endianness conversion on the ppc */ 6362306a36Sopenharmony_ci __raw_writel(*src, dst); 6462306a36Sopenharmony_ci dst++; 6562306a36Sopenharmony_ci src++; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci IVTV_INFO("Loaded %s firmware (%zu bytes)\n", fn, fw->size); 6862306a36Sopenharmony_ci release_firmware(fw); 6962306a36Sopenharmony_ci return size; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci IVTV_ERR("Unable to open firmware %s (must be %ld bytes)\n", fn, size); 7262306a36Sopenharmony_ci IVTV_ERR("Did you put the firmware in the hotplug firmware directory?\n"); 7362306a36Sopenharmony_ci return -ENOMEM; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_civoid ivtv_halt_firmware(struct ivtv *itv) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci IVTV_DEBUG_INFO("Preparing for firmware halt.\n"); 7962306a36Sopenharmony_ci if (itv->has_cx23415 && itv->dec_mbox.mbox) 8062306a36Sopenharmony_ci ivtv_vapi(itv, CX2341X_DEC_HALT_FW, 0); 8162306a36Sopenharmony_ci if (itv->enc_mbox.mbox) 8262306a36Sopenharmony_ci ivtv_vapi(itv, CX2341X_ENC_HALT_FW, 0); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci ivtv_msleep_timeout(10, 0); 8562306a36Sopenharmony_ci itv->enc_mbox.mbox = itv->dec_mbox.mbox = NULL; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci IVTV_DEBUG_INFO("Stopping VDM\n"); 8862306a36Sopenharmony_ci write_reg(IVTV_CMD_VDM_STOP, IVTV_REG_VDM); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci IVTV_DEBUG_INFO("Stopping AO\n"); 9162306a36Sopenharmony_ci write_reg(IVTV_CMD_AO_STOP, IVTV_REG_AO); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci IVTV_DEBUG_INFO("pinging (?) APU\n"); 9462306a36Sopenharmony_ci write_reg(IVTV_CMD_APU_PING, IVTV_REG_APU); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci IVTV_DEBUG_INFO("Stopping VPU\n"); 9762306a36Sopenharmony_ci if (!itv->has_cx23415) 9862306a36Sopenharmony_ci write_reg(IVTV_CMD_VPU_STOP16, IVTV_REG_VPU); 9962306a36Sopenharmony_ci else 10062306a36Sopenharmony_ci write_reg(IVTV_CMD_VPU_STOP15, IVTV_REG_VPU); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci IVTV_DEBUG_INFO("Resetting Hw Blocks\n"); 10362306a36Sopenharmony_ci write_reg(IVTV_CMD_HW_BLOCKS_RST, IVTV_REG_HW_BLOCKS); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci IVTV_DEBUG_INFO("Stopping SPU\n"); 10662306a36Sopenharmony_ci write_reg(IVTV_CMD_SPU_STOP, IVTV_REG_SPU); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci ivtv_msleep_timeout(10, 0); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci IVTV_DEBUG_INFO("init Encoder SDRAM pre-charge\n"); 11162306a36Sopenharmony_ci write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_ENC_SDRAM_PRECHARGE); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci IVTV_DEBUG_INFO("init Encoder SDRAM refresh to 1us\n"); 11462306a36Sopenharmony_ci write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_ENC_SDRAM_REFRESH); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (itv->has_cx23415) { 11762306a36Sopenharmony_ci IVTV_DEBUG_INFO("init Decoder SDRAM pre-charge\n"); 11862306a36Sopenharmony_ci write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_DEC_SDRAM_PRECHARGE); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci IVTV_DEBUG_INFO("init Decoder SDRAM refresh to 1us\n"); 12162306a36Sopenharmony_ci write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_DEC_SDRAM_REFRESH); 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci IVTV_DEBUG_INFO("Sleeping for %dms\n", IVTV_SDRAM_SLEEPTIME); 12562306a36Sopenharmony_ci ivtv_msleep_timeout(IVTV_SDRAM_SLEEPTIME, 0); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_civoid ivtv_firmware_versions(struct ivtv *itv) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci u32 data[CX2341X_MBOX_MAX_DATA]; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* Encoder */ 13362306a36Sopenharmony_ci ivtv_vapi_result(itv, data, CX2341X_ENC_GET_VERSION, 0); 13462306a36Sopenharmony_ci IVTV_INFO("Encoder revision: 0x%08x\n", data[0]); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (data[0] != 0x02060039) 13762306a36Sopenharmony_ci IVTV_WARN("Recommended firmware version is 0x02060039.\n"); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (itv->has_cx23415) { 14062306a36Sopenharmony_ci /* Decoder */ 14162306a36Sopenharmony_ci ivtv_vapi_result(itv, data, CX2341X_DEC_GET_VERSION, 0); 14262306a36Sopenharmony_ci IVTV_INFO("Decoder revision: 0x%08x\n", data[0]); 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int ivtv_firmware_copy(struct ivtv *itv) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci IVTV_DEBUG_INFO("Loading encoder image\n"); 14962306a36Sopenharmony_ci if (load_fw_direct(CX2341X_FIRM_ENC_FILENAME, 15062306a36Sopenharmony_ci itv->enc_mem, itv, IVTV_FW_ENC_SIZE) != IVTV_FW_ENC_SIZE) { 15162306a36Sopenharmony_ci IVTV_DEBUG_WARN("failed loading encoder firmware\n"); 15262306a36Sopenharmony_ci return -3; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci if (!itv->has_cx23415) 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci IVTV_DEBUG_INFO("Loading decoder image\n"); 15862306a36Sopenharmony_ci if (load_fw_direct(CX2341X_FIRM_DEC_FILENAME, 15962306a36Sopenharmony_ci itv->dec_mem, itv, IVTV_FW_DEC_SIZE) != IVTV_FW_DEC_SIZE) { 16062306a36Sopenharmony_ci IVTV_DEBUG_WARN("failed loading decoder firmware\n"); 16162306a36Sopenharmony_ci return -1; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic volatile struct ivtv_mailbox __iomem *ivtv_search_mailbox(const volatile u8 __iomem *mem, u32 size) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci int i; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* mailbox is preceded by a 16 byte 'magic cookie' starting at a 256-byte 17162306a36Sopenharmony_ci address boundary */ 17262306a36Sopenharmony_ci for (i = 0; i < size; i += 0x100) { 17362306a36Sopenharmony_ci if (readl(mem + i) == 0x12345678 && 17462306a36Sopenharmony_ci readl(mem + i + 4) == 0x34567812 && 17562306a36Sopenharmony_ci readl(mem + i + 8) == 0x56781234 && 17662306a36Sopenharmony_ci readl(mem + i + 12) == 0x78123456) { 17762306a36Sopenharmony_ci return (volatile struct ivtv_mailbox __iomem *)(mem + i + 16); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci return NULL; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ciint ivtv_firmware_init(struct ivtv *itv) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci int err; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci ivtv_halt_firmware(itv); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* load firmware */ 19062306a36Sopenharmony_ci err = ivtv_firmware_copy(itv); 19162306a36Sopenharmony_ci if (err) { 19262306a36Sopenharmony_ci IVTV_DEBUG_WARN("Error %d loading firmware\n", err); 19362306a36Sopenharmony_ci return err; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* start firmware */ 19762306a36Sopenharmony_ci write_reg(read_reg(IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE, IVTV_REG_SPU); 19862306a36Sopenharmony_ci ivtv_msleep_timeout(100, 0); 19962306a36Sopenharmony_ci if (itv->has_cx23415) 20062306a36Sopenharmony_ci write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE15, IVTV_REG_VPU); 20162306a36Sopenharmony_ci else 20262306a36Sopenharmony_ci write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE16, IVTV_REG_VPU); 20362306a36Sopenharmony_ci ivtv_msleep_timeout(100, 0); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* find mailboxes and ping firmware */ 20662306a36Sopenharmony_ci itv->enc_mbox.mbox = ivtv_search_mailbox(itv->enc_mem, IVTV_ENCODER_SIZE); 20762306a36Sopenharmony_ci if (itv->enc_mbox.mbox == NULL) 20862306a36Sopenharmony_ci IVTV_ERR("Encoder mailbox not found\n"); 20962306a36Sopenharmony_ci else if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0)) { 21062306a36Sopenharmony_ci IVTV_ERR("Encoder firmware dead!\n"); 21162306a36Sopenharmony_ci itv->enc_mbox.mbox = NULL; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci if (itv->enc_mbox.mbox == NULL) 21462306a36Sopenharmony_ci return -ENODEV; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (!itv->has_cx23415) 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci itv->dec_mbox.mbox = ivtv_search_mailbox(itv->dec_mem, IVTV_DECODER_SIZE); 22062306a36Sopenharmony_ci if (itv->dec_mbox.mbox == NULL) { 22162306a36Sopenharmony_ci IVTV_ERR("Decoder mailbox not found\n"); 22262306a36Sopenharmony_ci } else if (itv->has_cx23415 && ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0)) { 22362306a36Sopenharmony_ci IVTV_ERR("Decoder firmware dead!\n"); 22462306a36Sopenharmony_ci itv->dec_mbox.mbox = NULL; 22562306a36Sopenharmony_ci } else { 22662306a36Sopenharmony_ci /* Firmware okay, so check yuv output filter table */ 22762306a36Sopenharmony_ci ivtv_yuv_filter_check(itv); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci return itv->dec_mbox.mbox ? 0 : -ENODEV; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_civoid ivtv_init_mpeg_decoder(struct ivtv *itv) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci u32 data[CX2341X_MBOX_MAX_DATA]; 23562306a36Sopenharmony_ci long readbytes; 23662306a36Sopenharmony_ci volatile u8 __iomem *mem_offset; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci data[0] = 0; 23962306a36Sopenharmony_ci data[1] = itv->cxhdl.width; /* YUV source width */ 24062306a36Sopenharmony_ci data[2] = itv->cxhdl.height; 24162306a36Sopenharmony_ci data[3] = itv->cxhdl.audio_properties; /* Audio settings to use, 24262306a36Sopenharmony_ci bitmap. see docs. */ 24362306a36Sopenharmony_ci if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) { 24462306a36Sopenharmony_ci IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n"); 24562306a36Sopenharmony_ci return; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1) != 0) { 24962306a36Sopenharmony_ci IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n"); 25062306a36Sopenharmony_ci return; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data); 25362306a36Sopenharmony_ci mem_offset = itv->dec_mem + data[1]; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME, 25662306a36Sopenharmony_ci mem_offset, itv, IVTV_DECODE_INIT_MPEG_SIZE)) <= 0) { 25762306a36Sopenharmony_ci IVTV_DEBUG_WARN("failed to read mpeg decoder initialisation file %s\n", 25862306a36Sopenharmony_ci IVTV_DECODE_INIT_MPEG_FILENAME); 25962306a36Sopenharmony_ci } else { 26062306a36Sopenharmony_ci ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, readbytes, 0); 26162306a36Sopenharmony_ci ivtv_msleep_timeout(100, 0); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci/* Try to restart the card & restore previous settings */ 26762306a36Sopenharmony_cistatic int ivtv_firmware_restart(struct ivtv *itv) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci int rc = 0; 27062306a36Sopenharmony_ci v4l2_std_id std; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) 27362306a36Sopenharmony_ci /* Display test image during restart */ 27462306a36Sopenharmony_ci ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, 27562306a36Sopenharmony_ci SAA7127_INPUT_TYPE_TEST_IMAGE, 27662306a36Sopenharmony_ci itv->card->video_outputs[itv->active_output].video_output, 27762306a36Sopenharmony_ci 0); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci mutex_lock(&itv->udma.lock); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci rc = ivtv_firmware_init(itv); 28262306a36Sopenharmony_ci if (rc) { 28362306a36Sopenharmony_ci mutex_unlock(&itv->udma.lock); 28462306a36Sopenharmony_ci return rc; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* Allow settings to reload */ 28862306a36Sopenharmony_ci ivtv_mailbox_cache_invalidate(itv); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* Restore encoder video standard */ 29162306a36Sopenharmony_ci std = itv->std; 29262306a36Sopenharmony_ci itv->std = 0; 29362306a36Sopenharmony_ci ivtv_s_std_enc(itv, std); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { 29662306a36Sopenharmony_ci ivtv_init_mpeg_decoder(itv); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* Restore decoder video standard */ 29962306a36Sopenharmony_ci std = itv->std_out; 30062306a36Sopenharmony_ci itv->std_out = 0; 30162306a36Sopenharmony_ci ivtv_s_std_dec(itv, std); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* Restore framebuffer if active */ 30462306a36Sopenharmony_ci if (itv->ivtvfb_restore) 30562306a36Sopenharmony_ci itv->ivtvfb_restore(itv); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* Restore alpha settings */ 30862306a36Sopenharmony_ci ivtv_set_osd_alpha(itv); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Restore normal output */ 31162306a36Sopenharmony_ci ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, 31262306a36Sopenharmony_ci SAA7127_INPUT_TYPE_NORMAL, 31362306a36Sopenharmony_ci itv->card->video_outputs[itv->active_output].video_output, 31462306a36Sopenharmony_ci 0); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci mutex_unlock(&itv->udma.lock); 31862306a36Sopenharmony_ci return rc; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci/* Check firmware running state. The checks fall through 32262306a36Sopenharmony_ci allowing multiple failures to be logged. */ 32362306a36Sopenharmony_ciint ivtv_firmware_check(struct ivtv *itv, char *where) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci int res = 0; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* Check encoder is still running */ 32862306a36Sopenharmony_ci if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0) < 0) { 32962306a36Sopenharmony_ci IVTV_WARN("Encoder has died : %s\n", where); 33062306a36Sopenharmony_ci res = -1; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* Also check audio. Only check if not in use & encoder is okay */ 33462306a36Sopenharmony_ci if (!res && !atomic_read(&itv->capturing) && 33562306a36Sopenharmony_ci (!atomic_read(&itv->decoding) || 33662306a36Sopenharmony_ci (atomic_read(&itv->decoding) < 2 && test_bit(IVTV_F_I_DEC_YUV, 33762306a36Sopenharmony_ci &itv->i_flags)))) { 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12) < 0) { 34062306a36Sopenharmony_ci IVTV_WARN("Audio has died (Encoder OK) : %s\n", where); 34162306a36Sopenharmony_ci res = -2; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { 34662306a36Sopenharmony_ci /* Second audio check. Skip if audio already failed */ 34762306a36Sopenharmony_ci if (res != -2 && read_dec(0x100) != read_dec(0x104)) { 34862306a36Sopenharmony_ci /* Wait & try again to be certain. */ 34962306a36Sopenharmony_ci ivtv_msleep_timeout(14, 0); 35062306a36Sopenharmony_ci if (read_dec(0x100) != read_dec(0x104)) { 35162306a36Sopenharmony_ci IVTV_WARN("Audio has died (Decoder) : %s\n", 35262306a36Sopenharmony_ci where); 35362306a36Sopenharmony_ci res = -1; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* Check decoder is still running */ 35862306a36Sopenharmony_ci if (ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0) < 0) { 35962306a36Sopenharmony_ci IVTV_WARN("Decoder has died : %s\n", where); 36062306a36Sopenharmony_ci res = -1; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* If something failed & currently idle, try to reload */ 36562306a36Sopenharmony_ci if (res && !atomic_read(&itv->capturing) && 36662306a36Sopenharmony_ci !atomic_read(&itv->decoding)) { 36762306a36Sopenharmony_ci IVTV_INFO("Detected in %s that firmware had failed - Reloading\n", 36862306a36Sopenharmony_ci where); 36962306a36Sopenharmony_ci res = ivtv_firmware_restart(itv); 37062306a36Sopenharmony_ci /* 37162306a36Sopenharmony_ci * Even if restarted ok, still signal a problem had occurred. 37262306a36Sopenharmony_ci * The caller can come through this function again to check 37362306a36Sopenharmony_ci * if things are really ok after the restart. 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_ci if (!res) { 37662306a36Sopenharmony_ci IVTV_INFO("Firmware restart okay\n"); 37762306a36Sopenharmony_ci res = -EAGAIN; 37862306a36Sopenharmony_ci } else { 37962306a36Sopenharmony_ci IVTV_INFO("Firmware restart failed\n"); 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci } else if (res) { 38262306a36Sopenharmony_ci res = -EIO; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return res; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ciMODULE_FIRMWARE(CX2341X_FIRM_ENC_FILENAME); 38962306a36Sopenharmony_ciMODULE_FIRMWARE(CX2341X_FIRM_DEC_FILENAME); 39062306a36Sopenharmony_ciMODULE_FIRMWARE(IVTV_DECODE_INIT_MPEG_FILENAME); 391