18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci ivtv firmware 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#include "ivtv-firmware.h" 138c2ecf20Sopenharmony_ci#include "ivtv-yuv.h" 148c2ecf20Sopenharmony_ci#include "ivtv-ioctl.h" 158c2ecf20Sopenharmony_ci#include "ivtv-cards.h" 168c2ecf20Sopenharmony_ci#include <linux/firmware.h> 178c2ecf20Sopenharmony_ci#include <media/i2c/saa7127.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE 208c2ecf20Sopenharmony_ci#define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6 218c2ecf20Sopenharmony_ci#define IVTV_MASK_VPU_ENABLE16 0xFFFFFFFB 228c2ecf20Sopenharmony_ci#define IVTV_CMD_VDM_STOP 0x00000000 238c2ecf20Sopenharmony_ci#define IVTV_CMD_AO_STOP 0x00000005 248c2ecf20Sopenharmony_ci#define IVTV_CMD_APU_PING 0x00000000 258c2ecf20Sopenharmony_ci#define IVTV_CMD_VPU_STOP15 0xFFFFFFFE 268c2ecf20Sopenharmony_ci#define IVTV_CMD_VPU_STOP16 0xFFFFFFEE 278c2ecf20Sopenharmony_ci#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF 288c2ecf20Sopenharmony_ci#define IVTV_CMD_SPU_STOP 0x00000001 298c2ecf20Sopenharmony_ci#define IVTV_CMD_SDRAM_PRECHARGE_INIT 0x0000001A 308c2ecf20Sopenharmony_ci#define IVTV_CMD_SDRAM_REFRESH_INIT 0x80000640 318c2ecf20Sopenharmony_ci#define IVTV_SDRAM_SLEEPTIME 600 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define IVTV_DECODE_INIT_MPEG_FILENAME "v4l-cx2341x-init.mpg" 348c2ecf20Sopenharmony_ci#define IVTV_DECODE_INIT_MPEG_SIZE (152*1024) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* Encoder/decoder firmware sizes */ 378c2ecf20Sopenharmony_ci#define IVTV_FW_ENC_SIZE (376836) 388c2ecf20Sopenharmony_ci#define IVTV_FW_DEC_SIZE (256*1024) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv *itv, long size) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci const struct firmware *fw = NULL; 438c2ecf20Sopenharmony_ci int retries = 3; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ciretry: 468c2ecf20Sopenharmony_ci if (retries && request_firmware(&fw, fn, &itv->pdev->dev) == 0) { 478c2ecf20Sopenharmony_ci int i; 488c2ecf20Sopenharmony_ci volatile u32 __iomem *dst = (volatile u32 __iomem *)mem; 498c2ecf20Sopenharmony_ci const u32 *src = (const u32 *)fw->data; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (fw->size != size) { 528c2ecf20Sopenharmony_ci /* Due to race conditions in firmware loading (esp. with udev <0.95) 538c2ecf20Sopenharmony_ci the wrong file was sometimes loaded. So we check filesizes to 548c2ecf20Sopenharmony_ci see if at least the right-sized file was loaded. If not, then we 558c2ecf20Sopenharmony_ci retry. */ 568c2ecf20Sopenharmony_ci IVTV_INFO("Retry: file loaded was not %s (expected size %ld, got %zu)\n", fn, size, fw->size); 578c2ecf20Sopenharmony_ci release_firmware(fw); 588c2ecf20Sopenharmony_ci retries--; 598c2ecf20Sopenharmony_ci goto retry; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci for (i = 0; i < fw->size; i += 4) { 628c2ecf20Sopenharmony_ci /* no need for endianness conversion on the ppc */ 638c2ecf20Sopenharmony_ci __raw_writel(*src, dst); 648c2ecf20Sopenharmony_ci dst++; 658c2ecf20Sopenharmony_ci src++; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci IVTV_INFO("Loaded %s firmware (%zu bytes)\n", fn, fw->size); 688c2ecf20Sopenharmony_ci release_firmware(fw); 698c2ecf20Sopenharmony_ci return size; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci IVTV_ERR("Unable to open firmware %s (must be %ld bytes)\n", fn, size); 728c2ecf20Sopenharmony_ci IVTV_ERR("Did you put the firmware in the hotplug firmware directory?\n"); 738c2ecf20Sopenharmony_ci return -ENOMEM; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_civoid ivtv_halt_firmware(struct ivtv *itv) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci IVTV_DEBUG_INFO("Preparing for firmware halt.\n"); 798c2ecf20Sopenharmony_ci if (itv->has_cx23415 && itv->dec_mbox.mbox) 808c2ecf20Sopenharmony_ci ivtv_vapi(itv, CX2341X_DEC_HALT_FW, 0); 818c2ecf20Sopenharmony_ci if (itv->enc_mbox.mbox) 828c2ecf20Sopenharmony_ci ivtv_vapi(itv, CX2341X_ENC_HALT_FW, 0); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci ivtv_msleep_timeout(10, 0); 858c2ecf20Sopenharmony_ci itv->enc_mbox.mbox = itv->dec_mbox.mbox = NULL; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci IVTV_DEBUG_INFO("Stopping VDM\n"); 888c2ecf20Sopenharmony_ci write_reg(IVTV_CMD_VDM_STOP, IVTV_REG_VDM); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci IVTV_DEBUG_INFO("Stopping AO\n"); 918c2ecf20Sopenharmony_ci write_reg(IVTV_CMD_AO_STOP, IVTV_REG_AO); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci IVTV_DEBUG_INFO("pinging (?) APU\n"); 948c2ecf20Sopenharmony_ci write_reg(IVTV_CMD_APU_PING, IVTV_REG_APU); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci IVTV_DEBUG_INFO("Stopping VPU\n"); 978c2ecf20Sopenharmony_ci if (!itv->has_cx23415) 988c2ecf20Sopenharmony_ci write_reg(IVTV_CMD_VPU_STOP16, IVTV_REG_VPU); 998c2ecf20Sopenharmony_ci else 1008c2ecf20Sopenharmony_ci write_reg(IVTV_CMD_VPU_STOP15, IVTV_REG_VPU); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci IVTV_DEBUG_INFO("Resetting Hw Blocks\n"); 1038c2ecf20Sopenharmony_ci write_reg(IVTV_CMD_HW_BLOCKS_RST, IVTV_REG_HW_BLOCKS); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci IVTV_DEBUG_INFO("Stopping SPU\n"); 1068c2ecf20Sopenharmony_ci write_reg(IVTV_CMD_SPU_STOP, IVTV_REG_SPU); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci ivtv_msleep_timeout(10, 0); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci IVTV_DEBUG_INFO("init Encoder SDRAM pre-charge\n"); 1118c2ecf20Sopenharmony_ci write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_ENC_SDRAM_PRECHARGE); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci IVTV_DEBUG_INFO("init Encoder SDRAM refresh to 1us\n"); 1148c2ecf20Sopenharmony_ci write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_ENC_SDRAM_REFRESH); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (itv->has_cx23415) { 1178c2ecf20Sopenharmony_ci IVTV_DEBUG_INFO("init Decoder SDRAM pre-charge\n"); 1188c2ecf20Sopenharmony_ci write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_DEC_SDRAM_PRECHARGE); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci IVTV_DEBUG_INFO("init Decoder SDRAM refresh to 1us\n"); 1218c2ecf20Sopenharmony_ci write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_DEC_SDRAM_REFRESH); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci IVTV_DEBUG_INFO("Sleeping for %dms\n", IVTV_SDRAM_SLEEPTIME); 1258c2ecf20Sopenharmony_ci ivtv_msleep_timeout(IVTV_SDRAM_SLEEPTIME, 0); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_civoid ivtv_firmware_versions(struct ivtv *itv) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci u32 data[CX2341X_MBOX_MAX_DATA]; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Encoder */ 1338c2ecf20Sopenharmony_ci ivtv_vapi_result(itv, data, CX2341X_ENC_GET_VERSION, 0); 1348c2ecf20Sopenharmony_ci IVTV_INFO("Encoder revision: 0x%08x\n", data[0]); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (data[0] != 0x02060039) 1378c2ecf20Sopenharmony_ci IVTV_WARN("Recommended firmware version is 0x02060039.\n"); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (itv->has_cx23415) { 1408c2ecf20Sopenharmony_ci /* Decoder */ 1418c2ecf20Sopenharmony_ci ivtv_vapi_result(itv, data, CX2341X_DEC_GET_VERSION, 0); 1428c2ecf20Sopenharmony_ci IVTV_INFO("Decoder revision: 0x%08x\n", data[0]); 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int ivtv_firmware_copy(struct ivtv *itv) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci IVTV_DEBUG_INFO("Loading encoder image\n"); 1498c2ecf20Sopenharmony_ci if (load_fw_direct(CX2341X_FIRM_ENC_FILENAME, 1508c2ecf20Sopenharmony_ci itv->enc_mem, itv, IVTV_FW_ENC_SIZE) != IVTV_FW_ENC_SIZE) { 1518c2ecf20Sopenharmony_ci IVTV_DEBUG_WARN("failed loading encoder firmware\n"); 1528c2ecf20Sopenharmony_ci return -3; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci if (!itv->has_cx23415) 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci IVTV_DEBUG_INFO("Loading decoder image\n"); 1588c2ecf20Sopenharmony_ci if (load_fw_direct(CX2341X_FIRM_DEC_FILENAME, 1598c2ecf20Sopenharmony_ci itv->dec_mem, itv, IVTV_FW_DEC_SIZE) != IVTV_FW_DEC_SIZE) { 1608c2ecf20Sopenharmony_ci IVTV_DEBUG_WARN("failed loading decoder firmware\n"); 1618c2ecf20Sopenharmony_ci return -1; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic volatile struct ivtv_mailbox __iomem *ivtv_search_mailbox(const volatile u8 __iomem *mem, u32 size) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci int i; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* mailbox is preceded by a 16 byte 'magic cookie' starting at a 256-byte 1718c2ecf20Sopenharmony_ci address boundary */ 1728c2ecf20Sopenharmony_ci for (i = 0; i < size; i += 0x100) { 1738c2ecf20Sopenharmony_ci if (readl(mem + i) == 0x12345678 && 1748c2ecf20Sopenharmony_ci readl(mem + i + 4) == 0x34567812 && 1758c2ecf20Sopenharmony_ci readl(mem + i + 8) == 0x56781234 && 1768c2ecf20Sopenharmony_ci readl(mem + i + 12) == 0x78123456) { 1778c2ecf20Sopenharmony_ci return (volatile struct ivtv_mailbox __iomem *)(mem + i + 16); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci return NULL; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ciint ivtv_firmware_init(struct ivtv *itv) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci int err; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci ivtv_halt_firmware(itv); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* load firmware */ 1908c2ecf20Sopenharmony_ci err = ivtv_firmware_copy(itv); 1918c2ecf20Sopenharmony_ci if (err) { 1928c2ecf20Sopenharmony_ci IVTV_DEBUG_WARN("Error %d loading firmware\n", err); 1938c2ecf20Sopenharmony_ci return err; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* start firmware */ 1978c2ecf20Sopenharmony_ci write_reg(read_reg(IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE, IVTV_REG_SPU); 1988c2ecf20Sopenharmony_ci ivtv_msleep_timeout(100, 0); 1998c2ecf20Sopenharmony_ci if (itv->has_cx23415) 2008c2ecf20Sopenharmony_ci write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE15, IVTV_REG_VPU); 2018c2ecf20Sopenharmony_ci else 2028c2ecf20Sopenharmony_ci write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE16, IVTV_REG_VPU); 2038c2ecf20Sopenharmony_ci ivtv_msleep_timeout(100, 0); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* find mailboxes and ping firmware */ 2068c2ecf20Sopenharmony_ci itv->enc_mbox.mbox = ivtv_search_mailbox(itv->enc_mem, IVTV_ENCODER_SIZE); 2078c2ecf20Sopenharmony_ci if (itv->enc_mbox.mbox == NULL) 2088c2ecf20Sopenharmony_ci IVTV_ERR("Encoder mailbox not found\n"); 2098c2ecf20Sopenharmony_ci else if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0)) { 2108c2ecf20Sopenharmony_ci IVTV_ERR("Encoder firmware dead!\n"); 2118c2ecf20Sopenharmony_ci itv->enc_mbox.mbox = NULL; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci if (itv->enc_mbox.mbox == NULL) 2148c2ecf20Sopenharmony_ci return -ENODEV; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (!itv->has_cx23415) 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci itv->dec_mbox.mbox = ivtv_search_mailbox(itv->dec_mem, IVTV_DECODER_SIZE); 2208c2ecf20Sopenharmony_ci if (itv->dec_mbox.mbox == NULL) { 2218c2ecf20Sopenharmony_ci IVTV_ERR("Decoder mailbox not found\n"); 2228c2ecf20Sopenharmony_ci } else if (itv->has_cx23415 && ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0)) { 2238c2ecf20Sopenharmony_ci IVTV_ERR("Decoder firmware dead!\n"); 2248c2ecf20Sopenharmony_ci itv->dec_mbox.mbox = NULL; 2258c2ecf20Sopenharmony_ci } else { 2268c2ecf20Sopenharmony_ci /* Firmware okay, so check yuv output filter table */ 2278c2ecf20Sopenharmony_ci ivtv_yuv_filter_check(itv); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci return itv->dec_mbox.mbox ? 0 : -ENODEV; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_civoid ivtv_init_mpeg_decoder(struct ivtv *itv) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci u32 data[CX2341X_MBOX_MAX_DATA]; 2358c2ecf20Sopenharmony_ci long readbytes; 2368c2ecf20Sopenharmony_ci volatile u8 __iomem *mem_offset; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci data[0] = 0; 2398c2ecf20Sopenharmony_ci data[1] = itv->cxhdl.width; /* YUV source width */ 2408c2ecf20Sopenharmony_ci data[2] = itv->cxhdl.height; 2418c2ecf20Sopenharmony_ci data[3] = itv->cxhdl.audio_properties; /* Audio settings to use, 2428c2ecf20Sopenharmony_ci bitmap. see docs. */ 2438c2ecf20Sopenharmony_ci if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) { 2448c2ecf20Sopenharmony_ci IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n"); 2458c2ecf20Sopenharmony_ci return; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1) != 0) { 2498c2ecf20Sopenharmony_ci IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n"); 2508c2ecf20Sopenharmony_ci return; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data); 2538c2ecf20Sopenharmony_ci mem_offset = itv->dec_mem + data[1]; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME, 2568c2ecf20Sopenharmony_ci mem_offset, itv, IVTV_DECODE_INIT_MPEG_SIZE)) <= 0) { 2578c2ecf20Sopenharmony_ci IVTV_DEBUG_WARN("failed to read mpeg decoder initialisation file %s\n", 2588c2ecf20Sopenharmony_ci IVTV_DECODE_INIT_MPEG_FILENAME); 2598c2ecf20Sopenharmony_ci } else { 2608c2ecf20Sopenharmony_ci ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, readbytes, 0); 2618c2ecf20Sopenharmony_ci ivtv_msleep_timeout(100, 0); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* Try to restart the card & restore previous settings */ 2678c2ecf20Sopenharmony_cistatic int ivtv_firmware_restart(struct ivtv *itv) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci int rc = 0; 2708c2ecf20Sopenharmony_ci v4l2_std_id std; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) 2738c2ecf20Sopenharmony_ci /* Display test image during restart */ 2748c2ecf20Sopenharmony_ci ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, 2758c2ecf20Sopenharmony_ci SAA7127_INPUT_TYPE_TEST_IMAGE, 2768c2ecf20Sopenharmony_ci itv->card->video_outputs[itv->active_output].video_output, 2778c2ecf20Sopenharmony_ci 0); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci mutex_lock(&itv->udma.lock); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci rc = ivtv_firmware_init(itv); 2828c2ecf20Sopenharmony_ci if (rc) { 2838c2ecf20Sopenharmony_ci mutex_unlock(&itv->udma.lock); 2848c2ecf20Sopenharmony_ci return rc; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* Allow settings to reload */ 2888c2ecf20Sopenharmony_ci ivtv_mailbox_cache_invalidate(itv); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* Restore encoder video standard */ 2918c2ecf20Sopenharmony_ci std = itv->std; 2928c2ecf20Sopenharmony_ci itv->std = 0; 2938c2ecf20Sopenharmony_ci ivtv_s_std_enc(itv, std); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { 2968c2ecf20Sopenharmony_ci ivtv_init_mpeg_decoder(itv); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* Restore decoder video standard */ 2998c2ecf20Sopenharmony_ci std = itv->std_out; 3008c2ecf20Sopenharmony_ci itv->std_out = 0; 3018c2ecf20Sopenharmony_ci ivtv_s_std_dec(itv, std); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* Restore framebuffer if active */ 3048c2ecf20Sopenharmony_ci if (itv->ivtvfb_restore) 3058c2ecf20Sopenharmony_ci itv->ivtvfb_restore(itv); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* Restore alpha settings */ 3088c2ecf20Sopenharmony_ci ivtv_set_osd_alpha(itv); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* Restore normal output */ 3118c2ecf20Sopenharmony_ci ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, 3128c2ecf20Sopenharmony_ci SAA7127_INPUT_TYPE_NORMAL, 3138c2ecf20Sopenharmony_ci itv->card->video_outputs[itv->active_output].video_output, 3148c2ecf20Sopenharmony_ci 0); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci mutex_unlock(&itv->udma.lock); 3188c2ecf20Sopenharmony_ci return rc; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci/* Check firmware running state. The checks fall through 3228c2ecf20Sopenharmony_ci allowing multiple failures to be logged. */ 3238c2ecf20Sopenharmony_ciint ivtv_firmware_check(struct ivtv *itv, char *where) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci int res = 0; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* Check encoder is still running */ 3288c2ecf20Sopenharmony_ci if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0) < 0) { 3298c2ecf20Sopenharmony_ci IVTV_WARN("Encoder has died : %s\n", where); 3308c2ecf20Sopenharmony_ci res = -1; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* Also check audio. Only check if not in use & encoder is okay */ 3348c2ecf20Sopenharmony_ci if (!res && !atomic_read(&itv->capturing) && 3358c2ecf20Sopenharmony_ci (!atomic_read(&itv->decoding) || 3368c2ecf20Sopenharmony_ci (atomic_read(&itv->decoding) < 2 && test_bit(IVTV_F_I_DEC_YUV, 3378c2ecf20Sopenharmony_ci &itv->i_flags)))) { 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12) < 0) { 3408c2ecf20Sopenharmony_ci IVTV_WARN("Audio has died (Encoder OK) : %s\n", where); 3418c2ecf20Sopenharmony_ci res = -2; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { 3468c2ecf20Sopenharmony_ci /* Second audio check. Skip if audio already failed */ 3478c2ecf20Sopenharmony_ci if (res != -2 && read_dec(0x100) != read_dec(0x104)) { 3488c2ecf20Sopenharmony_ci /* Wait & try again to be certain. */ 3498c2ecf20Sopenharmony_ci ivtv_msleep_timeout(14, 0); 3508c2ecf20Sopenharmony_ci if (read_dec(0x100) != read_dec(0x104)) { 3518c2ecf20Sopenharmony_ci IVTV_WARN("Audio has died (Decoder) : %s\n", 3528c2ecf20Sopenharmony_ci where); 3538c2ecf20Sopenharmony_ci res = -1; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* Check decoder is still running */ 3588c2ecf20Sopenharmony_ci if (ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0) < 0) { 3598c2ecf20Sopenharmony_ci IVTV_WARN("Decoder has died : %s\n", where); 3608c2ecf20Sopenharmony_ci res = -1; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* If something failed & currently idle, try to reload */ 3658c2ecf20Sopenharmony_ci if (res && !atomic_read(&itv->capturing) && 3668c2ecf20Sopenharmony_ci !atomic_read(&itv->decoding)) { 3678c2ecf20Sopenharmony_ci IVTV_INFO("Detected in %s that firmware had failed - Reloading\n", 3688c2ecf20Sopenharmony_ci where); 3698c2ecf20Sopenharmony_ci res = ivtv_firmware_restart(itv); 3708c2ecf20Sopenharmony_ci /* 3718c2ecf20Sopenharmony_ci * Even if restarted ok, still signal a problem had occurred. 3728c2ecf20Sopenharmony_ci * The caller can come through this function again to check 3738c2ecf20Sopenharmony_ci * if things are really ok after the restart. 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_ci if (!res) { 3768c2ecf20Sopenharmony_ci IVTV_INFO("Firmware restart okay\n"); 3778c2ecf20Sopenharmony_ci res = -EAGAIN; 3788c2ecf20Sopenharmony_ci } else { 3798c2ecf20Sopenharmony_ci IVTV_INFO("Firmware restart failed\n"); 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci } else if (res) { 3828c2ecf20Sopenharmony_ci res = -EIO; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return res; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CX2341X_FIRM_ENC_FILENAME); 3898c2ecf20Sopenharmony_ciMODULE_FIRMWARE(CX2341X_FIRM_DEC_FILENAME); 3908c2ecf20Sopenharmony_ciMODULE_FIRMWARE(IVTV_DECODE_INIT_MPEG_FILENAME); 391