162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Authors: Younghwan Joo <yhwan.joo@samsung.com>
862306a36Sopenharmony_ci *          Sylwester Nawrocki <s.nawrocki@samsung.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "fimc-is.h"
1362306a36Sopenharmony_ci#include "fimc-is-command.h"
1462306a36Sopenharmony_ci#include "fimc-is-regs.h"
1562306a36Sopenharmony_ci#include "fimc-is-sensor.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_civoid fimc_is_fw_clear_irq1(struct fimc_is *is, unsigned int nr)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	mcuctl_write(1UL << nr, is, MCUCTL_REG_INTCR1);
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_civoid fimc_is_fw_clear_irq2(struct fimc_is *is)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	u32 cfg = mcuctl_read(is, MCUCTL_REG_INTSR2);
2562306a36Sopenharmony_ci	mcuctl_write(cfg, is, MCUCTL_REG_INTCR2);
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_civoid fimc_is_hw_set_intgr0_gd0(struct fimc_is *is)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	mcuctl_write(INTGR0_INTGD(0), is, MCUCTL_REG_INTGR0);
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciint fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	unsigned int timeout = 2000;
3662306a36Sopenharmony_ci	u32 cfg, status;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	do {
3962306a36Sopenharmony_ci		cfg = mcuctl_read(is, MCUCTL_REG_INTMSR0);
4062306a36Sopenharmony_ci		status = INTMSR0_GET_INTMSD(0, cfg);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci		if (--timeout == 0) {
4362306a36Sopenharmony_ci			dev_warn(&is->pdev->dev, "%s timeout\n",
4462306a36Sopenharmony_ci				 __func__);
4562306a36Sopenharmony_ci			return -ETIMEDOUT;
4662306a36Sopenharmony_ci		}
4762306a36Sopenharmony_ci		udelay(1);
4862306a36Sopenharmony_ci	} while (status != 0);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	return 0;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ciint fimc_is_hw_set_param(struct fimc_is *is)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	struct chain_config *config = &is->config[is->config_index];
5662306a36Sopenharmony_ci	unsigned int param_count = __get_pending_param_count(is);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	fimc_is_hw_wait_intmsr0_intmsd0(is);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	mcuctl_write(HIC_SET_PARAMETER, is, MCUCTL_REG_ISSR(0));
6162306a36Sopenharmony_ci	mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
6262306a36Sopenharmony_ci	mcuctl_write(is->config_index, is, MCUCTL_REG_ISSR(2));
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	mcuctl_write(param_count, is, MCUCTL_REG_ISSR(3));
6562306a36Sopenharmony_ci	mcuctl_write(config->p_region_index[0], is, MCUCTL_REG_ISSR(4));
6662306a36Sopenharmony_ci	mcuctl_write(config->p_region_index[1], is, MCUCTL_REG_ISSR(5));
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	fimc_is_hw_set_intgr0_gd0(is);
6962306a36Sopenharmony_ci	return 0;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int __maybe_unused fimc_is_hw_set_tune(struct fimc_is *is)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	fimc_is_hw_wait_intmsr0_intmsd0(is);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	mcuctl_write(HIC_SET_TUNE, is, MCUCTL_REG_ISSR(0));
7762306a36Sopenharmony_ci	mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
7862306a36Sopenharmony_ci	mcuctl_write(is->h2i_cmd.entry_id, is, MCUCTL_REG_ISSR(2));
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	fimc_is_hw_set_intgr0_gd0(is);
8162306a36Sopenharmony_ci	return 0;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define FIMC_IS_MAX_PARAMS	4
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciint fimc_is_hw_get_params(struct fimc_is *is, unsigned int num_args)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	int i;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (num_args > FIMC_IS_MAX_PARAMS)
9162306a36Sopenharmony_ci		return -EINVAL;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	is->i2h_cmd.num_args = num_args;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	for (i = 0; i < FIMC_IS_MAX_PARAMS; i++) {
9662306a36Sopenharmony_ci		if (i < num_args)
9762306a36Sopenharmony_ci			is->i2h_cmd.args[i] = mcuctl_read(is,
9862306a36Sopenharmony_ci					MCUCTL_REG_ISSR(12 + i));
9962306a36Sopenharmony_ci		else
10062306a36Sopenharmony_ci			is->i2h_cmd.args[i] = 0;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci	return 0;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_civoid fimc_is_hw_set_isp_buf_mask(struct fimc_is *is, unsigned int mask)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	if (hweight32(mask) == 1) {
10862306a36Sopenharmony_ci		dev_err(&is->pdev->dev, "%s(): not enough buffers (mask %#x)\n",
10962306a36Sopenharmony_ci							__func__, mask);
11062306a36Sopenharmony_ci		return;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (mcuctl_read(is, MCUCTL_REG_ISSR(23)) != 0)
11462306a36Sopenharmony_ci		dev_dbg(&is->pdev->dev, "non-zero DMA buffer mask\n");
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	mcuctl_write(mask, is, MCUCTL_REG_ISSR(23));
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_civoid fimc_is_hw_set_sensor_num(struct fimc_is *is)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	pr_debug("setting sensor index to: %d\n", is->sensor_index);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	mcuctl_write(IH_REPLY_DONE, is, MCUCTL_REG_ISSR(0));
12462306a36Sopenharmony_ci	mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
12562306a36Sopenharmony_ci	mcuctl_write(IHC_GET_SENSOR_NUM, is, MCUCTL_REG_ISSR(2));
12662306a36Sopenharmony_ci	mcuctl_write(FIMC_IS_SENSORS_NUM, is, MCUCTL_REG_ISSR(3));
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_civoid fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	if (is->sensor_index != index)
13262306a36Sopenharmony_ci		return;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	fimc_is_hw_wait_intmsr0_intmsd0(is);
13562306a36Sopenharmony_ci	mcuctl_write(HIC_CLOSE_SENSOR, is, MCUCTL_REG_ISSR(0));
13662306a36Sopenharmony_ci	mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
13762306a36Sopenharmony_ci	mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(2));
13862306a36Sopenharmony_ci	fimc_is_hw_set_intgr0_gd0(is);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_civoid fimc_is_hw_get_setfile_addr(struct fimc_is *is)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	fimc_is_hw_wait_intmsr0_intmsd0(is);
14462306a36Sopenharmony_ci	mcuctl_write(HIC_GET_SET_FILE_ADDR, is, MCUCTL_REG_ISSR(0));
14562306a36Sopenharmony_ci	mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
14662306a36Sopenharmony_ci	fimc_is_hw_set_intgr0_gd0(is);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_civoid fimc_is_hw_load_setfile(struct fimc_is *is)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	fimc_is_hw_wait_intmsr0_intmsd0(is);
15262306a36Sopenharmony_ci	mcuctl_write(HIC_LOAD_SET_FILE, is, MCUCTL_REG_ISSR(0));
15362306a36Sopenharmony_ci	mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
15462306a36Sopenharmony_ci	fimc_is_hw_set_intgr0_gd0(is);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ciint fimc_is_hw_change_mode(struct fimc_is *is)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	static const u8 cmd[] = {
16062306a36Sopenharmony_ci		HIC_PREVIEW_STILL, HIC_PREVIEW_VIDEO,
16162306a36Sopenharmony_ci		HIC_CAPTURE_STILL, HIC_CAPTURE_VIDEO,
16262306a36Sopenharmony_ci	};
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (WARN_ON(is->config_index >= ARRAY_SIZE(cmd)))
16562306a36Sopenharmony_ci		return -EINVAL;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	mcuctl_write(cmd[is->config_index], is, MCUCTL_REG_ISSR(0));
16862306a36Sopenharmony_ci	mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
16962306a36Sopenharmony_ci	mcuctl_write(is->setfile.sub_index, is, MCUCTL_REG_ISSR(2));
17062306a36Sopenharmony_ci	fimc_is_hw_set_intgr0_gd0(is);
17162306a36Sopenharmony_ci	return 0;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_civoid fimc_is_hw_stream_on(struct fimc_is *is)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	fimc_is_hw_wait_intmsr0_intmsd0(is);
17762306a36Sopenharmony_ci	mcuctl_write(HIC_STREAM_ON, is, MCUCTL_REG_ISSR(0));
17862306a36Sopenharmony_ci	mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
17962306a36Sopenharmony_ci	mcuctl_write(0, is, MCUCTL_REG_ISSR(2));
18062306a36Sopenharmony_ci	fimc_is_hw_set_intgr0_gd0(is);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_civoid fimc_is_hw_stream_off(struct fimc_is *is)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	fimc_is_hw_wait_intmsr0_intmsd0(is);
18662306a36Sopenharmony_ci	mcuctl_write(HIC_STREAM_OFF, is, MCUCTL_REG_ISSR(0));
18762306a36Sopenharmony_ci	mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
18862306a36Sopenharmony_ci	fimc_is_hw_set_intgr0_gd0(is);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_civoid fimc_is_hw_subip_power_off(struct fimc_is *is)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	fimc_is_hw_wait_intmsr0_intmsd0(is);
19462306a36Sopenharmony_ci	mcuctl_write(HIC_POWER_DOWN, is, MCUCTL_REG_ISSR(0));
19562306a36Sopenharmony_ci	mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
19662306a36Sopenharmony_ci	fimc_is_hw_set_intgr0_gd0(is);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ciint fimc_is_itf_s_param(struct fimc_is *is, bool update)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	int ret;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (update)
20462306a36Sopenharmony_ci		__is_hw_update_params(is);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	fimc_is_mem_barrier();
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	clear_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state);
20962306a36Sopenharmony_ci	fimc_is_hw_set_param(is);
21062306a36Sopenharmony_ci	ret = fimc_is_wait_event(is, IS_ST_BLOCK_CMD_CLEARED, 1,
21162306a36Sopenharmony_ci				FIMC_IS_CONFIG_TIMEOUT);
21262306a36Sopenharmony_ci	if (ret < 0)
21362306a36Sopenharmony_ci		dev_err(&is->pdev->dev, "%s() timeout\n", __func__);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return ret;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ciint fimc_is_itf_mode_change(struct fimc_is *is)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	int ret;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	clear_bit(IS_ST_CHANGE_MODE, &is->state);
22362306a36Sopenharmony_ci	fimc_is_hw_change_mode(is);
22462306a36Sopenharmony_ci	ret = fimc_is_wait_event(is, IS_ST_CHANGE_MODE, 1,
22562306a36Sopenharmony_ci				FIMC_IS_CONFIG_TIMEOUT);
22662306a36Sopenharmony_ci	if (ret < 0)
22762306a36Sopenharmony_ci		dev_err(&is->pdev->dev, "%s(): mode change (%d) timeout\n",
22862306a36Sopenharmony_ci			__func__, is->config_index);
22962306a36Sopenharmony_ci	return ret;
23062306a36Sopenharmony_ci}
231