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