143ecec16SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0-or-later
243ecec16SMauro Carvalho Chehab /*
343ecec16SMauro Carvalho Chehab  * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c
443ecec16SMauro Carvalho Chehab  *
543ecec16SMauro Carvalho Chehab  * Copyright (c) 2010 Samsung Electronics Co., Ltd.
643ecec16SMauro Carvalho Chehab  *		http://www.samsung.com/
743ecec16SMauro Carvalho Chehab  */
843ecec16SMauro Carvalho Chehab 
943ecec16SMauro Carvalho Chehab #include <linux/delay.h>
1043ecec16SMauro Carvalho Chehab #include <linux/err.h>
1143ecec16SMauro Carvalho Chehab #include <linux/firmware.h>
1243ecec16SMauro Carvalho Chehab #include <linux/jiffies.h>
1343ecec16SMauro Carvalho Chehab #include <linux/sched.h>
1443ecec16SMauro Carvalho Chehab #include "s5p_mfc_cmd.h"
1543ecec16SMauro Carvalho Chehab #include "s5p_mfc_common.h"
1643ecec16SMauro Carvalho Chehab #include "s5p_mfc_debug.h"
1743ecec16SMauro Carvalho Chehab #include "s5p_mfc_intr.h"
1843ecec16SMauro Carvalho Chehab #include "s5p_mfc_opr.h"
1943ecec16SMauro Carvalho Chehab #include "s5p_mfc_pm.h"
2043ecec16SMauro Carvalho Chehab #include "s5p_mfc_ctrl.h"
2143ecec16SMauro Carvalho Chehab 
2243ecec16SMauro Carvalho Chehab /* Allocate memory for firmware */
s5p_mfc_alloc_firmware(struct s5p_mfc_dev * dev)2343ecec16SMauro Carvalho Chehab int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev)
2443ecec16SMauro Carvalho Chehab {
2543ecec16SMauro Carvalho Chehab 	struct s5p_mfc_priv_buf *fw_buf = &dev->fw_buf;
2643ecec16SMauro Carvalho Chehab 	int err;
2743ecec16SMauro Carvalho Chehab 
2843ecec16SMauro Carvalho Chehab 	fw_buf->size = dev->variant->buf_size->fw;
2943ecec16SMauro Carvalho Chehab 
3043ecec16SMauro Carvalho Chehab 	if (fw_buf->virt) {
3143ecec16SMauro Carvalho Chehab 		mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n");
3243ecec16SMauro Carvalho Chehab 		return -ENOMEM;
3343ecec16SMauro Carvalho Chehab 	}
3443ecec16SMauro Carvalho Chehab 
3543ecec16SMauro Carvalho Chehab 	err = s5p_mfc_alloc_priv_buf(dev, BANK_L_CTX, &dev->fw_buf);
3643ecec16SMauro Carvalho Chehab 	if (err) {
3743ecec16SMauro Carvalho Chehab 		mfc_err("Allocating bitprocessor buffer failed\n");
3843ecec16SMauro Carvalho Chehab 		return err;
3943ecec16SMauro Carvalho Chehab 	}
4043ecec16SMauro Carvalho Chehab 
4143ecec16SMauro Carvalho Chehab 	return 0;
4243ecec16SMauro Carvalho Chehab }
4343ecec16SMauro Carvalho Chehab 
4443ecec16SMauro Carvalho Chehab /* Load firmware */
s5p_mfc_load_firmware(struct s5p_mfc_dev * dev)4543ecec16SMauro Carvalho Chehab int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev)
4643ecec16SMauro Carvalho Chehab {
4743ecec16SMauro Carvalho Chehab 	struct firmware *fw_blob;
4843ecec16SMauro Carvalho Chehab 	int i, err = -EINVAL;
4943ecec16SMauro Carvalho Chehab 
5043ecec16SMauro Carvalho Chehab 	/* Firmware has to be present as a separate file or compiled
5143ecec16SMauro Carvalho Chehab 	 * into kernel. */
5243ecec16SMauro Carvalho Chehab 	mfc_debug_enter();
5343ecec16SMauro Carvalho Chehab 
5443ecec16SMauro Carvalho Chehab 	if (dev->fw_get_done)
5543ecec16SMauro Carvalho Chehab 		return 0;
5643ecec16SMauro Carvalho Chehab 
5743ecec16SMauro Carvalho Chehab 	for (i = MFC_FW_MAX_VERSIONS - 1; i >= 0; i--) {
5843ecec16SMauro Carvalho Chehab 		if (!dev->variant->fw_name[i])
5943ecec16SMauro Carvalho Chehab 			continue;
6043ecec16SMauro Carvalho Chehab 		err = request_firmware((const struct firmware **)&fw_blob,
6143ecec16SMauro Carvalho Chehab 				dev->variant->fw_name[i], &dev->plat_dev->dev);
6243ecec16SMauro Carvalho Chehab 		if (!err) {
6343ecec16SMauro Carvalho Chehab 			dev->fw_ver = (enum s5p_mfc_fw_ver) i;
6443ecec16SMauro Carvalho Chehab 			break;
6543ecec16SMauro Carvalho Chehab 		}
6643ecec16SMauro Carvalho Chehab 	}
6743ecec16SMauro Carvalho Chehab 
6843ecec16SMauro Carvalho Chehab 	if (err != 0) {
6943ecec16SMauro Carvalho Chehab 		mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
7043ecec16SMauro Carvalho Chehab 		return -EINVAL;
7143ecec16SMauro Carvalho Chehab 	}
7243ecec16SMauro Carvalho Chehab 	if (fw_blob->size > dev->fw_buf.size) {
7343ecec16SMauro Carvalho Chehab 		mfc_err("MFC firmware is too big to be loaded\n");
7443ecec16SMauro Carvalho Chehab 		release_firmware(fw_blob);
7543ecec16SMauro Carvalho Chehab 		return -ENOMEM;
7643ecec16SMauro Carvalho Chehab 	}
7743ecec16SMauro Carvalho Chehab 	memcpy(dev->fw_buf.virt, fw_blob->data, fw_blob->size);
7843ecec16SMauro Carvalho Chehab 	wmb();
7943ecec16SMauro Carvalho Chehab 	dev->fw_get_done = true;
8043ecec16SMauro Carvalho Chehab 	release_firmware(fw_blob);
8143ecec16SMauro Carvalho Chehab 	mfc_debug_leave();
8243ecec16SMauro Carvalho Chehab 	return 0;
8343ecec16SMauro Carvalho Chehab }
8443ecec16SMauro Carvalho Chehab 
8543ecec16SMauro Carvalho Chehab /* Release firmware memory */
s5p_mfc_release_firmware(struct s5p_mfc_dev * dev)8643ecec16SMauro Carvalho Chehab int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev)
8743ecec16SMauro Carvalho Chehab {
8843ecec16SMauro Carvalho Chehab 	/* Before calling this function one has to make sure
8943ecec16SMauro Carvalho Chehab 	 * that MFC is no longer processing */
9043ecec16SMauro Carvalho Chehab 	s5p_mfc_release_priv_buf(dev, &dev->fw_buf);
9143ecec16SMauro Carvalho Chehab 	dev->fw_get_done = false;
9243ecec16SMauro Carvalho Chehab 	return 0;
9343ecec16SMauro Carvalho Chehab }
9443ecec16SMauro Carvalho Chehab 
s5p_mfc_bus_reset(struct s5p_mfc_dev * dev)9543ecec16SMauro Carvalho Chehab static int s5p_mfc_bus_reset(struct s5p_mfc_dev *dev)
9643ecec16SMauro Carvalho Chehab {
9743ecec16SMauro Carvalho Chehab 	unsigned int status;
9843ecec16SMauro Carvalho Chehab 	unsigned long timeout;
9943ecec16SMauro Carvalho Chehab 
10043ecec16SMauro Carvalho Chehab 	/* Reset */
10143ecec16SMauro Carvalho Chehab 	mfc_write(dev, 0x1, S5P_FIMV_MFC_BUS_RESET_CTRL);
10243ecec16SMauro Carvalho Chehab 	timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
10343ecec16SMauro Carvalho Chehab 	/* Check bus status */
10443ecec16SMauro Carvalho Chehab 	do {
10543ecec16SMauro Carvalho Chehab 		if (time_after(jiffies, timeout)) {
10643ecec16SMauro Carvalho Chehab 			mfc_err("Timeout while resetting MFC.\n");
10743ecec16SMauro Carvalho Chehab 			return -EIO;
10843ecec16SMauro Carvalho Chehab 		}
10943ecec16SMauro Carvalho Chehab 		status = mfc_read(dev, S5P_FIMV_MFC_BUS_RESET_CTRL);
11043ecec16SMauro Carvalho Chehab 	} while ((status & 0x2) == 0);
11143ecec16SMauro Carvalho Chehab 	return 0;
11243ecec16SMauro Carvalho Chehab }
11343ecec16SMauro Carvalho Chehab 
11443ecec16SMauro Carvalho Chehab /* Reset the device */
s5p_mfc_reset(struct s5p_mfc_dev * dev)11543ecec16SMauro Carvalho Chehab int s5p_mfc_reset(struct s5p_mfc_dev *dev)
11643ecec16SMauro Carvalho Chehab {
11743ecec16SMauro Carvalho Chehab 	unsigned int mc_status;
11843ecec16SMauro Carvalho Chehab 	unsigned long timeout;
11943ecec16SMauro Carvalho Chehab 	int i;
12043ecec16SMauro Carvalho Chehab 
12143ecec16SMauro Carvalho Chehab 	mfc_debug_enter();
12243ecec16SMauro Carvalho Chehab 
12343ecec16SMauro Carvalho Chehab 	if (IS_MFCV6_PLUS(dev)) {
12443ecec16SMauro Carvalho Chehab 		/* Zero Initialization of MFC registers */
12543ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD_V6);
12643ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0, S5P_FIMV_HOST2RISC_CMD_V6);
12743ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0, S5P_FIMV_FW_VERSION_V6);
12843ecec16SMauro Carvalho Chehab 
12943ecec16SMauro Carvalho Chehab 		for (i = 0; i < S5P_FIMV_REG_CLEAR_COUNT_V6; i++)
13043ecec16SMauro Carvalho Chehab 			mfc_write(dev, 0, S5P_FIMV_REG_CLEAR_BEGIN_V6 + (i*4));
13143ecec16SMauro Carvalho Chehab 
13243ecec16SMauro Carvalho Chehab 		/* check bus reset control before reset */
13343ecec16SMauro Carvalho Chehab 		if (dev->risc_on)
13443ecec16SMauro Carvalho Chehab 			if (s5p_mfc_bus_reset(dev))
13543ecec16SMauro Carvalho Chehab 				return -EIO;
13643ecec16SMauro Carvalho Chehab 		/* Reset
13743ecec16SMauro Carvalho Chehab 		 * set RISC_ON to 0 during power_on & wake_up.
13843ecec16SMauro Carvalho Chehab 		 * V6 needs RISC_ON set to 0 during reset also.
13943ecec16SMauro Carvalho Chehab 		 */
14043ecec16SMauro Carvalho Chehab 		if ((!dev->risc_on) || (!IS_MFCV7_PLUS(dev)))
14143ecec16SMauro Carvalho Chehab 			mfc_write(dev, 0, S5P_FIMV_RISC_ON_V6);
14243ecec16SMauro Carvalho Chehab 
14343ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0x1FFF, S5P_FIMV_MFC_RESET_V6);
14443ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0, S5P_FIMV_MFC_RESET_V6);
14543ecec16SMauro Carvalho Chehab 	} else {
14643ecec16SMauro Carvalho Chehab 		/* Stop procedure */
14743ecec16SMauro Carvalho Chehab 		/*  reset RISC */
14843ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0x3f6, S5P_FIMV_SW_RESET);
14943ecec16SMauro Carvalho Chehab 		/*  All reset except for MC */
15043ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0x3e2, S5P_FIMV_SW_RESET);
15143ecec16SMauro Carvalho Chehab 		mdelay(10);
15243ecec16SMauro Carvalho Chehab 
15343ecec16SMauro Carvalho Chehab 		timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
15443ecec16SMauro Carvalho Chehab 		/* Check MC status */
15543ecec16SMauro Carvalho Chehab 		do {
15643ecec16SMauro Carvalho Chehab 			if (time_after(jiffies, timeout)) {
15743ecec16SMauro Carvalho Chehab 				mfc_err("Timeout while resetting MFC\n");
15843ecec16SMauro Carvalho Chehab 				return -EIO;
15943ecec16SMauro Carvalho Chehab 			}
16043ecec16SMauro Carvalho Chehab 
16143ecec16SMauro Carvalho Chehab 			mc_status = mfc_read(dev, S5P_FIMV_MC_STATUS);
16243ecec16SMauro Carvalho Chehab 
16343ecec16SMauro Carvalho Chehab 		} while (mc_status & 0x3);
16443ecec16SMauro Carvalho Chehab 
16543ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0x0, S5P_FIMV_SW_RESET);
16643ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0x3fe, S5P_FIMV_SW_RESET);
16743ecec16SMauro Carvalho Chehab 	}
16843ecec16SMauro Carvalho Chehab 
16943ecec16SMauro Carvalho Chehab 	mfc_debug_leave();
17043ecec16SMauro Carvalho Chehab 	return 0;
17143ecec16SMauro Carvalho Chehab }
17243ecec16SMauro Carvalho Chehab 
s5p_mfc_init_memctrl(struct s5p_mfc_dev * dev)17343ecec16SMauro Carvalho Chehab static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)
17443ecec16SMauro Carvalho Chehab {
17543ecec16SMauro Carvalho Chehab 	if (IS_MFCV6_PLUS(dev)) {
17643ecec16SMauro Carvalho Chehab 		mfc_write(dev, dev->dma_base[BANK_L_CTX],
17743ecec16SMauro Carvalho Chehab 			  S5P_FIMV_RISC_BASE_ADDRESS_V6);
17843ecec16SMauro Carvalho Chehab 		mfc_debug(2, "Base Address : %pad\n",
17943ecec16SMauro Carvalho Chehab 			  &dev->dma_base[BANK_L_CTX]);
18043ecec16SMauro Carvalho Chehab 	} else {
18143ecec16SMauro Carvalho Chehab 		mfc_write(dev, dev->dma_base[BANK_L_CTX],
18243ecec16SMauro Carvalho Chehab 			  S5P_FIMV_MC_DRAMBASE_ADR_A);
18343ecec16SMauro Carvalho Chehab 		mfc_write(dev, dev->dma_base[BANK_R_CTX],
18443ecec16SMauro Carvalho Chehab 			  S5P_FIMV_MC_DRAMBASE_ADR_B);
18543ecec16SMauro Carvalho Chehab 		mfc_debug(2, "Bank1: %pad, Bank2: %pad\n",
18643ecec16SMauro Carvalho Chehab 			  &dev->dma_base[BANK_L_CTX],
18743ecec16SMauro Carvalho Chehab 			  &dev->dma_base[BANK_R_CTX]);
18843ecec16SMauro Carvalho Chehab 	}
18943ecec16SMauro Carvalho Chehab }
19043ecec16SMauro Carvalho Chehab 
s5p_mfc_clear_cmds(struct s5p_mfc_dev * dev)19143ecec16SMauro Carvalho Chehab static inline void s5p_mfc_clear_cmds(struct s5p_mfc_dev *dev)
19243ecec16SMauro Carvalho Chehab {
19343ecec16SMauro Carvalho Chehab 	if (IS_MFCV6_PLUS(dev)) {
19443ecec16SMauro Carvalho Chehab 		/* Zero initialization should be done before RESET.
19543ecec16SMauro Carvalho Chehab 		 * Nothing to do here. */
19643ecec16SMauro Carvalho Chehab 	} else {
19743ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH0_INST_ID);
19843ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH1_INST_ID);
19943ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD);
20043ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0, S5P_FIMV_HOST2RISC_CMD);
20143ecec16SMauro Carvalho Chehab 	}
20243ecec16SMauro Carvalho Chehab }
20343ecec16SMauro Carvalho Chehab 
20443ecec16SMauro Carvalho Chehab /* Initialize hardware */
s5p_mfc_init_hw(struct s5p_mfc_dev * dev)20543ecec16SMauro Carvalho Chehab int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
20643ecec16SMauro Carvalho Chehab {
20743ecec16SMauro Carvalho Chehab 	unsigned int ver;
20843ecec16SMauro Carvalho Chehab 	int ret;
20943ecec16SMauro Carvalho Chehab 
21043ecec16SMauro Carvalho Chehab 	mfc_debug_enter();
21143ecec16SMauro Carvalho Chehab 	if (!dev->fw_buf.virt) {
21243ecec16SMauro Carvalho Chehab 		mfc_err("Firmware memory is not allocated.\n");
21343ecec16SMauro Carvalho Chehab 		return -EINVAL;
21443ecec16SMauro Carvalho Chehab 	}
21543ecec16SMauro Carvalho Chehab 
21643ecec16SMauro Carvalho Chehab 	/* 0. MFC reset */
21743ecec16SMauro Carvalho Chehab 	mfc_debug(2, "MFC reset..\n");
21843ecec16SMauro Carvalho Chehab 	s5p_mfc_clock_on();
21943ecec16SMauro Carvalho Chehab 	dev->risc_on = 0;
22043ecec16SMauro Carvalho Chehab 	ret = s5p_mfc_reset(dev);
22143ecec16SMauro Carvalho Chehab 	if (ret) {
22243ecec16SMauro Carvalho Chehab 		mfc_err("Failed to reset MFC - timeout\n");
22343ecec16SMauro Carvalho Chehab 		return ret;
22443ecec16SMauro Carvalho Chehab 	}
22543ecec16SMauro Carvalho Chehab 	mfc_debug(2, "Done MFC reset..\n");
22643ecec16SMauro Carvalho Chehab 	/* 1. Set DRAM base Addr */
22743ecec16SMauro Carvalho Chehab 	s5p_mfc_init_memctrl(dev);
22843ecec16SMauro Carvalho Chehab 	/* 2. Initialize registers of channel I/F */
22943ecec16SMauro Carvalho Chehab 	s5p_mfc_clear_cmds(dev);
23043ecec16SMauro Carvalho Chehab 	/* 3. Release reset signal to the RISC */
23143ecec16SMauro Carvalho Chehab 	s5p_mfc_clean_dev_int_flags(dev);
23243ecec16SMauro Carvalho Chehab 	if (IS_MFCV6_PLUS(dev)) {
23343ecec16SMauro Carvalho Chehab 		dev->risc_on = 1;
23443ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);
23543ecec16SMauro Carvalho Chehab 	}
23643ecec16SMauro Carvalho Chehab 	else
23743ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
23843ecec16SMauro Carvalho Chehab 
23943ecec16SMauro Carvalho Chehab 	if (IS_MFCV10(dev))
24043ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0x0, S5P_FIMV_MFC_CLOCK_OFF_V10);
24143ecec16SMauro Carvalho Chehab 
24243ecec16SMauro Carvalho Chehab 	mfc_debug(2, "Will now wait for completion of firmware transfer\n");
24343ecec16SMauro Carvalho Chehab 	if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_FW_STATUS_RET)) {
24443ecec16SMauro Carvalho Chehab 		mfc_err("Failed to load firmware\n");
24543ecec16SMauro Carvalho Chehab 		s5p_mfc_reset(dev);
24643ecec16SMauro Carvalho Chehab 		s5p_mfc_clock_off();
24743ecec16SMauro Carvalho Chehab 		return -EIO;
24843ecec16SMauro Carvalho Chehab 	}
24943ecec16SMauro Carvalho Chehab 	s5p_mfc_clean_dev_int_flags(dev);
25043ecec16SMauro Carvalho Chehab 	/* 4. Initialize firmware */
25143ecec16SMauro Carvalho Chehab 	ret = s5p_mfc_hw_call(dev->mfc_cmds, sys_init_cmd, dev);
25243ecec16SMauro Carvalho Chehab 	if (ret) {
25343ecec16SMauro Carvalho Chehab 		mfc_err("Failed to send command to MFC - timeout\n");
25443ecec16SMauro Carvalho Chehab 		s5p_mfc_reset(dev);
25543ecec16SMauro Carvalho Chehab 		s5p_mfc_clock_off();
25643ecec16SMauro Carvalho Chehab 		return ret;
25743ecec16SMauro Carvalho Chehab 	}
25843ecec16SMauro Carvalho Chehab 	mfc_debug(2, "Ok, now will wait for completion of hardware init\n");
25943ecec16SMauro Carvalho Chehab 	if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_SYS_INIT_RET)) {
26043ecec16SMauro Carvalho Chehab 		mfc_err("Failed to init hardware\n");
26143ecec16SMauro Carvalho Chehab 		s5p_mfc_reset(dev);
26243ecec16SMauro Carvalho Chehab 		s5p_mfc_clock_off();
26343ecec16SMauro Carvalho Chehab 		return -EIO;
26443ecec16SMauro Carvalho Chehab 	}
26543ecec16SMauro Carvalho Chehab 	dev->int_cond = 0;
26643ecec16SMauro Carvalho Chehab 	if (dev->int_err != 0 || dev->int_type !=
26743ecec16SMauro Carvalho Chehab 					S5P_MFC_R2H_CMD_SYS_INIT_RET) {
26843ecec16SMauro Carvalho Chehab 		/* Failure. */
26943ecec16SMauro Carvalho Chehab 		mfc_err("Failed to init firmware - error: %d int: %d\n",
27043ecec16SMauro Carvalho Chehab 						dev->int_err, dev->int_type);
27143ecec16SMauro Carvalho Chehab 		s5p_mfc_reset(dev);
27243ecec16SMauro Carvalho Chehab 		s5p_mfc_clock_off();
27343ecec16SMauro Carvalho Chehab 		return -EIO;
27443ecec16SMauro Carvalho Chehab 	}
27543ecec16SMauro Carvalho Chehab 	if (IS_MFCV6_PLUS(dev))
27643ecec16SMauro Carvalho Chehab 		ver = mfc_read(dev, S5P_FIMV_FW_VERSION_V6);
27743ecec16SMauro Carvalho Chehab 	else
27843ecec16SMauro Carvalho Chehab 		ver = mfc_read(dev, S5P_FIMV_FW_VERSION);
27943ecec16SMauro Carvalho Chehab 
28043ecec16SMauro Carvalho Chehab 	mfc_debug(2, "MFC F/W version : %02xyy, %02xmm, %02xdd\n",
28143ecec16SMauro Carvalho Chehab 		(ver >> 16) & 0xFF, (ver >> 8) & 0xFF, ver & 0xFF);
28243ecec16SMauro Carvalho Chehab 	s5p_mfc_clock_off();
28343ecec16SMauro Carvalho Chehab 	mfc_debug_leave();
28443ecec16SMauro Carvalho Chehab 	return 0;
28543ecec16SMauro Carvalho Chehab }
28643ecec16SMauro Carvalho Chehab 
28743ecec16SMauro Carvalho Chehab 
28843ecec16SMauro Carvalho Chehab /* Deinitialize hardware */
s5p_mfc_deinit_hw(struct s5p_mfc_dev * dev)28943ecec16SMauro Carvalho Chehab void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev)
29043ecec16SMauro Carvalho Chehab {
29143ecec16SMauro Carvalho Chehab 	s5p_mfc_clock_on();
29243ecec16SMauro Carvalho Chehab 
29343ecec16SMauro Carvalho Chehab 	s5p_mfc_reset(dev);
29443ecec16SMauro Carvalho Chehab 	s5p_mfc_hw_call(dev->mfc_ops, release_dev_context_buffer, dev);
29543ecec16SMauro Carvalho Chehab 
29643ecec16SMauro Carvalho Chehab 	s5p_mfc_clock_off();
29743ecec16SMauro Carvalho Chehab }
29843ecec16SMauro Carvalho Chehab 
s5p_mfc_sleep(struct s5p_mfc_dev * dev)29943ecec16SMauro Carvalho Chehab int s5p_mfc_sleep(struct s5p_mfc_dev *dev)
30043ecec16SMauro Carvalho Chehab {
30143ecec16SMauro Carvalho Chehab 	int ret;
30243ecec16SMauro Carvalho Chehab 
30343ecec16SMauro Carvalho Chehab 	mfc_debug_enter();
30443ecec16SMauro Carvalho Chehab 	s5p_mfc_clock_on();
30543ecec16SMauro Carvalho Chehab 	s5p_mfc_clean_dev_int_flags(dev);
30643ecec16SMauro Carvalho Chehab 	ret = s5p_mfc_hw_call(dev->mfc_cmds, sleep_cmd, dev);
30743ecec16SMauro Carvalho Chehab 	if (ret) {
30843ecec16SMauro Carvalho Chehab 		mfc_err("Failed to send command to MFC - timeout\n");
30943ecec16SMauro Carvalho Chehab 		return ret;
31043ecec16SMauro Carvalho Chehab 	}
31143ecec16SMauro Carvalho Chehab 	if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_SLEEP_RET)) {
31243ecec16SMauro Carvalho Chehab 		mfc_err("Failed to sleep\n");
31343ecec16SMauro Carvalho Chehab 		return -EIO;
31443ecec16SMauro Carvalho Chehab 	}
31543ecec16SMauro Carvalho Chehab 	s5p_mfc_clock_off();
31643ecec16SMauro Carvalho Chehab 	dev->int_cond = 0;
31743ecec16SMauro Carvalho Chehab 	if (dev->int_err != 0 || dev->int_type !=
31843ecec16SMauro Carvalho Chehab 						S5P_MFC_R2H_CMD_SLEEP_RET) {
31943ecec16SMauro Carvalho Chehab 		/* Failure. */
32043ecec16SMauro Carvalho Chehab 		mfc_err("Failed to sleep - error: %d int: %d\n", dev->int_err,
32143ecec16SMauro Carvalho Chehab 								dev->int_type);
32243ecec16SMauro Carvalho Chehab 		return -EIO;
32343ecec16SMauro Carvalho Chehab 	}
32443ecec16SMauro Carvalho Chehab 	mfc_debug_leave();
32543ecec16SMauro Carvalho Chehab 	return ret;
32643ecec16SMauro Carvalho Chehab }
32743ecec16SMauro Carvalho Chehab 
s5p_mfc_v8_wait_wakeup(struct s5p_mfc_dev * dev)32843ecec16SMauro Carvalho Chehab static int s5p_mfc_v8_wait_wakeup(struct s5p_mfc_dev *dev)
32943ecec16SMauro Carvalho Chehab {
33043ecec16SMauro Carvalho Chehab 	int ret;
33143ecec16SMauro Carvalho Chehab 
33243ecec16SMauro Carvalho Chehab 	/* Release reset signal to the RISC */
33343ecec16SMauro Carvalho Chehab 	dev->risc_on = 1;
33443ecec16SMauro Carvalho Chehab 	mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);
33543ecec16SMauro Carvalho Chehab 
33643ecec16SMauro Carvalho Chehab 	if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_FW_STATUS_RET)) {
33743ecec16SMauro Carvalho Chehab 		mfc_err("Failed to reset MFCV8\n");
33843ecec16SMauro Carvalho Chehab 		return -EIO;
33943ecec16SMauro Carvalho Chehab 	}
34043ecec16SMauro Carvalho Chehab 	mfc_debug(2, "Write command to wakeup MFCV8\n");
34143ecec16SMauro Carvalho Chehab 	ret = s5p_mfc_hw_call(dev->mfc_cmds, wakeup_cmd, dev);
34243ecec16SMauro Carvalho Chehab 	if (ret) {
34343ecec16SMauro Carvalho Chehab 		mfc_err("Failed to send command to MFCV8 - timeout\n");
34443ecec16SMauro Carvalho Chehab 		return ret;
34543ecec16SMauro Carvalho Chehab 	}
34643ecec16SMauro Carvalho Chehab 
34743ecec16SMauro Carvalho Chehab 	if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_WAKEUP_RET)) {
34843ecec16SMauro Carvalho Chehab 		mfc_err("Failed to wakeup MFC\n");
34943ecec16SMauro Carvalho Chehab 		return -EIO;
35043ecec16SMauro Carvalho Chehab 	}
35143ecec16SMauro Carvalho Chehab 	return ret;
35243ecec16SMauro Carvalho Chehab }
35343ecec16SMauro Carvalho Chehab 
s5p_mfc_wait_wakeup(struct s5p_mfc_dev * dev)35443ecec16SMauro Carvalho Chehab static int s5p_mfc_wait_wakeup(struct s5p_mfc_dev *dev)
35543ecec16SMauro Carvalho Chehab {
35643ecec16SMauro Carvalho Chehab 	int ret;
35743ecec16SMauro Carvalho Chehab 
35843ecec16SMauro Carvalho Chehab 	/* Send MFC wakeup command */
35943ecec16SMauro Carvalho Chehab 	ret = s5p_mfc_hw_call(dev->mfc_cmds, wakeup_cmd, dev);
36043ecec16SMauro Carvalho Chehab 	if (ret) {
36143ecec16SMauro Carvalho Chehab 		mfc_err("Failed to send command to MFC - timeout\n");
36243ecec16SMauro Carvalho Chehab 		return ret;
36343ecec16SMauro Carvalho Chehab 	}
36443ecec16SMauro Carvalho Chehab 
36543ecec16SMauro Carvalho Chehab 	/* Release reset signal to the RISC */
36643ecec16SMauro Carvalho Chehab 	if (IS_MFCV6_PLUS(dev)) {
36743ecec16SMauro Carvalho Chehab 		dev->risc_on = 1;
36843ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);
36943ecec16SMauro Carvalho Chehab 	} else {
37043ecec16SMauro Carvalho Chehab 		mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
37143ecec16SMauro Carvalho Chehab 	}
37243ecec16SMauro Carvalho Chehab 
37343ecec16SMauro Carvalho Chehab 	if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_WAKEUP_RET)) {
37443ecec16SMauro Carvalho Chehab 		mfc_err("Failed to wakeup MFC\n");
37543ecec16SMauro Carvalho Chehab 		return -EIO;
37643ecec16SMauro Carvalho Chehab 	}
37743ecec16SMauro Carvalho Chehab 	return ret;
37843ecec16SMauro Carvalho Chehab }
37943ecec16SMauro Carvalho Chehab 
s5p_mfc_wakeup(struct s5p_mfc_dev * dev)38043ecec16SMauro Carvalho Chehab int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)
38143ecec16SMauro Carvalho Chehab {
38243ecec16SMauro Carvalho Chehab 	int ret;
38343ecec16SMauro Carvalho Chehab 
38443ecec16SMauro Carvalho Chehab 	mfc_debug_enter();
38543ecec16SMauro Carvalho Chehab 	/* 0. MFC reset */
38643ecec16SMauro Carvalho Chehab 	mfc_debug(2, "MFC reset..\n");
38743ecec16SMauro Carvalho Chehab 	s5p_mfc_clock_on();
38843ecec16SMauro Carvalho Chehab 	dev->risc_on = 0;
38943ecec16SMauro Carvalho Chehab 	ret = s5p_mfc_reset(dev);
39043ecec16SMauro Carvalho Chehab 	if (ret) {
39143ecec16SMauro Carvalho Chehab 		mfc_err("Failed to reset MFC - timeout\n");
39243ecec16SMauro Carvalho Chehab 		s5p_mfc_clock_off();
39343ecec16SMauro Carvalho Chehab 		return ret;
39443ecec16SMauro Carvalho Chehab 	}
39543ecec16SMauro Carvalho Chehab 	mfc_debug(2, "Done MFC reset..\n");
39643ecec16SMauro Carvalho Chehab 	/* 1. Set DRAM base Addr */
39743ecec16SMauro Carvalho Chehab 	s5p_mfc_init_memctrl(dev);
39843ecec16SMauro Carvalho Chehab 	/* 2. Initialize registers of channel I/F */
39943ecec16SMauro Carvalho Chehab 	s5p_mfc_clear_cmds(dev);
40043ecec16SMauro Carvalho Chehab 	s5p_mfc_clean_dev_int_flags(dev);
40143ecec16SMauro Carvalho Chehab 	/* 3. Send MFC wakeup command and wait for completion*/
40243ecec16SMauro Carvalho Chehab 	if (IS_MFCV8_PLUS(dev))
40343ecec16SMauro Carvalho Chehab 		ret = s5p_mfc_v8_wait_wakeup(dev);
40443ecec16SMauro Carvalho Chehab 	else
40543ecec16SMauro Carvalho Chehab 		ret = s5p_mfc_wait_wakeup(dev);
40643ecec16SMauro Carvalho Chehab 
40743ecec16SMauro Carvalho Chehab 	s5p_mfc_clock_off();
40843ecec16SMauro Carvalho Chehab 	if (ret)
40943ecec16SMauro Carvalho Chehab 		return ret;
41043ecec16SMauro Carvalho Chehab 
41143ecec16SMauro Carvalho Chehab 	dev->int_cond = 0;
41243ecec16SMauro Carvalho Chehab 	if (dev->int_err != 0 || dev->int_type !=
41343ecec16SMauro Carvalho Chehab 						S5P_MFC_R2H_CMD_WAKEUP_RET) {
41443ecec16SMauro Carvalho Chehab 		/* Failure. */
41543ecec16SMauro Carvalho Chehab 		mfc_err("Failed to wakeup - error: %d int: %d\n", dev->int_err,
41643ecec16SMauro Carvalho Chehab 								dev->int_type);
41743ecec16SMauro Carvalho Chehab 		return -EIO;
41843ecec16SMauro Carvalho Chehab 	}
41943ecec16SMauro Carvalho Chehab 	mfc_debug_leave();
42043ecec16SMauro Carvalho Chehab 	return 0;
42143ecec16SMauro Carvalho Chehab }
42243ecec16SMauro Carvalho Chehab 
s5p_mfc_open_mfc_inst(struct s5p_mfc_dev * dev,struct s5p_mfc_ctx * ctx)42343ecec16SMauro Carvalho Chehab int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
42443ecec16SMauro Carvalho Chehab {
42543ecec16SMauro Carvalho Chehab 	int ret = 0;
42643ecec16SMauro Carvalho Chehab 
42743ecec16SMauro Carvalho Chehab 	ret = s5p_mfc_hw_call(dev->mfc_ops, alloc_instance_buffer, ctx);
42843ecec16SMauro Carvalho Chehab 	if (ret) {
42943ecec16SMauro Carvalho Chehab 		mfc_err("Failed allocating instance buffer\n");
43043ecec16SMauro Carvalho Chehab 		goto err;
43143ecec16SMauro Carvalho Chehab 	}
43243ecec16SMauro Carvalho Chehab 
43343ecec16SMauro Carvalho Chehab 	if (ctx->type == MFCINST_DECODER) {
43443ecec16SMauro Carvalho Chehab 		ret = s5p_mfc_hw_call(dev->mfc_ops,
43543ecec16SMauro Carvalho Chehab 					alloc_dec_temp_buffers, ctx);
43643ecec16SMauro Carvalho Chehab 		if (ret) {
43743ecec16SMauro Carvalho Chehab 			mfc_err("Failed allocating temporary buffers\n");
43843ecec16SMauro Carvalho Chehab 			goto err_free_inst_buf;
43943ecec16SMauro Carvalho Chehab 		}
44043ecec16SMauro Carvalho Chehab 	}
44143ecec16SMauro Carvalho Chehab 
44243ecec16SMauro Carvalho Chehab 	set_work_bit_irqsave(ctx);
44343ecec16SMauro Carvalho Chehab 	s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
44443ecec16SMauro Carvalho Chehab 	if (s5p_mfc_wait_for_done_ctx(ctx,
44543ecec16SMauro Carvalho Chehab 		S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 0)) {
44643ecec16SMauro Carvalho Chehab 		/* Error or timeout */
44743ecec16SMauro Carvalho Chehab 		mfc_err("Error getting instance from hardware\n");
44843ecec16SMauro Carvalho Chehab 		ret = -EIO;
44943ecec16SMauro Carvalho Chehab 		goto err_free_desc_buf;
45043ecec16SMauro Carvalho Chehab 	}
45143ecec16SMauro Carvalho Chehab 
45243ecec16SMauro Carvalho Chehab 	mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
45343ecec16SMauro Carvalho Chehab 	return ret;
45443ecec16SMauro Carvalho Chehab 
45543ecec16SMauro Carvalho Chehab err_free_desc_buf:
45643ecec16SMauro Carvalho Chehab 	if (ctx->type == MFCINST_DECODER)
45743ecec16SMauro Carvalho Chehab 		s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx);
45843ecec16SMauro Carvalho Chehab err_free_inst_buf:
45943ecec16SMauro Carvalho Chehab 	s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);
46043ecec16SMauro Carvalho Chehab err:
46143ecec16SMauro Carvalho Chehab 	return ret;
46243ecec16SMauro Carvalho Chehab }
46343ecec16SMauro Carvalho Chehab 
s5p_mfc_close_mfc_inst(struct s5p_mfc_dev * dev,struct s5p_mfc_ctx * ctx)46443ecec16SMauro Carvalho Chehab void s5p_mfc_close_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
46543ecec16SMauro Carvalho Chehab {
46643ecec16SMauro Carvalho Chehab 	ctx->state = MFCINST_RETURN_INST;
46743ecec16SMauro Carvalho Chehab 	set_work_bit_irqsave(ctx);
46843ecec16SMauro Carvalho Chehab 	s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
46943ecec16SMauro Carvalho Chehab 	/* Wait until instance is returned or timeout occurred */
47043ecec16SMauro Carvalho Chehab 	if (s5p_mfc_wait_for_done_ctx(ctx,
471*d3f3c2feSSmitha T Murthy 				S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET, 0)){
472*d3f3c2feSSmitha T Murthy 		clear_work_bit_irqsave(ctx);
47343ecec16SMauro Carvalho Chehab 		mfc_err("Err returning instance\n");
474*d3f3c2feSSmitha T Murthy 	}
47543ecec16SMauro Carvalho Chehab 
47643ecec16SMauro Carvalho Chehab 	/* Free resources */
47743ecec16SMauro Carvalho Chehab 	s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);
47843ecec16SMauro Carvalho Chehab 	s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);
47943ecec16SMauro Carvalho Chehab 	if (ctx->type == MFCINST_DECODER)
48043ecec16SMauro Carvalho Chehab 		s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx);
48143ecec16SMauro Carvalho Chehab 
48243ecec16SMauro Carvalho Chehab 	ctx->inst_no = MFC_NO_INSTANCE_SET;
48343ecec16SMauro Carvalho Chehab 	ctx->state = MFCINST_FREE;
48443ecec16SMauro Carvalho Chehab }
485