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) 2013 Samsung Electronics Co., Ltd.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com>
862306a36Sopenharmony_ci *          Younghwan Joo <yhwan.joo@samsung.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/debugfs.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/errno.h>
1662306a36Sopenharmony_ci#include <linux/firmware.h>
1762306a36Sopenharmony_ci#include <linux/interrupt.h>
1862306a36Sopenharmony_ci#include <linux/kernel.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/i2c.h>
2162306a36Sopenharmony_ci#include <linux/of_irq.h>
2262306a36Sopenharmony_ci#include <linux/of_address.h>
2362306a36Sopenharmony_ci#include <linux/of_graph.h>
2462306a36Sopenharmony_ci#include <linux/of_platform.h>
2562306a36Sopenharmony_ci#include <linux/platform_device.h>
2662306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2762306a36Sopenharmony_ci#include <linux/slab.h>
2862306a36Sopenharmony_ci#include <linux/types.h>
2962306a36Sopenharmony_ci#include <linux/videodev2.h>
3062306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include "media-dev.h"
3362306a36Sopenharmony_ci#include "fimc-is.h"
3462306a36Sopenharmony_ci#include "fimc-is-command.h"
3562306a36Sopenharmony_ci#include "fimc-is-errno.h"
3662306a36Sopenharmony_ci#include "fimc-is-i2c.h"
3762306a36Sopenharmony_ci#include "fimc-is-param.h"
3862306a36Sopenharmony_ci#include "fimc-is-regs.h"
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic char *fimc_is_clocks[ISS_CLKS_MAX] = {
4262306a36Sopenharmony_ci	[ISS_CLK_PPMUISPX]		= "ppmuispx",
4362306a36Sopenharmony_ci	[ISS_CLK_PPMUISPMX]		= "ppmuispmx",
4462306a36Sopenharmony_ci	[ISS_CLK_LITE0]			= "lite0",
4562306a36Sopenharmony_ci	[ISS_CLK_LITE1]			= "lite1",
4662306a36Sopenharmony_ci	[ISS_CLK_MPLL]			= "mpll",
4762306a36Sopenharmony_ci	[ISS_CLK_ISP]			= "isp",
4862306a36Sopenharmony_ci	[ISS_CLK_DRC]			= "drc",
4962306a36Sopenharmony_ci	[ISS_CLK_FD]			= "fd",
5062306a36Sopenharmony_ci	[ISS_CLK_MCUISP]		= "mcuisp",
5162306a36Sopenharmony_ci	[ISS_CLK_GICISP]		= "gicisp",
5262306a36Sopenharmony_ci	[ISS_CLK_PWM_ISP]		= "pwm_isp",
5362306a36Sopenharmony_ci	[ISS_CLK_MCUCTL_ISP]		= "mcuctl_isp",
5462306a36Sopenharmony_ci	[ISS_CLK_UART]			= "uart",
5562306a36Sopenharmony_ci	[ISS_CLK_ISP_DIV0]		= "ispdiv0",
5662306a36Sopenharmony_ci	[ISS_CLK_ISP_DIV1]		= "ispdiv1",
5762306a36Sopenharmony_ci	[ISS_CLK_MCUISP_DIV0]		= "mcuispdiv0",
5862306a36Sopenharmony_ci	[ISS_CLK_MCUISP_DIV1]		= "mcuispdiv1",
5962306a36Sopenharmony_ci	[ISS_CLK_ACLK200]		= "aclk200",
6062306a36Sopenharmony_ci	[ISS_CLK_ACLK200_DIV]		= "div_aclk200",
6162306a36Sopenharmony_ci	[ISS_CLK_ACLK400MCUISP]		= "aclk400mcuisp",
6262306a36Sopenharmony_ci	[ISS_CLK_ACLK400MCUISP_DIV]	= "div_aclk400mcuisp",
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic void fimc_is_put_clocks(struct fimc_is *is)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	int i;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	for (i = 0; i < ISS_CLKS_MAX; i++) {
7062306a36Sopenharmony_ci		if (IS_ERR(is->clocks[i]))
7162306a36Sopenharmony_ci			continue;
7262306a36Sopenharmony_ci		clk_put(is->clocks[i]);
7362306a36Sopenharmony_ci		is->clocks[i] = ERR_PTR(-EINVAL);
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int fimc_is_get_clocks(struct fimc_is *is)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	int i, ret;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	for (i = 0; i < ISS_CLKS_MAX; i++)
8262306a36Sopenharmony_ci		is->clocks[i] = ERR_PTR(-EINVAL);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	for (i = 0; i < ISS_CLKS_MAX; i++) {
8562306a36Sopenharmony_ci		is->clocks[i] = clk_get(&is->pdev->dev, fimc_is_clocks[i]);
8662306a36Sopenharmony_ci		if (IS_ERR(is->clocks[i])) {
8762306a36Sopenharmony_ci			ret = PTR_ERR(is->clocks[i]);
8862306a36Sopenharmony_ci			goto err;
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return 0;
9362306a36Sopenharmony_cierr:
9462306a36Sopenharmony_ci	fimc_is_put_clocks(is);
9562306a36Sopenharmony_ci	dev_err(&is->pdev->dev, "failed to get clock: %s\n",
9662306a36Sopenharmony_ci		fimc_is_clocks[i]);
9762306a36Sopenharmony_ci	return ret;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int fimc_is_setup_clocks(struct fimc_is *is)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	int ret;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	ret = clk_set_parent(is->clocks[ISS_CLK_ACLK200],
10562306a36Sopenharmony_ci					is->clocks[ISS_CLK_ACLK200_DIV]);
10662306a36Sopenharmony_ci	if (ret < 0)
10762306a36Sopenharmony_ci		return ret;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	ret = clk_set_parent(is->clocks[ISS_CLK_ACLK400MCUISP],
11062306a36Sopenharmony_ci					is->clocks[ISS_CLK_ACLK400MCUISP_DIV]);
11162306a36Sopenharmony_ci	if (ret < 0)
11262306a36Sopenharmony_ci		return ret;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV0], ACLK_AXI_FREQUENCY);
11562306a36Sopenharmony_ci	if (ret < 0)
11662306a36Sopenharmony_ci		return ret;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV1], ACLK_AXI_FREQUENCY);
11962306a36Sopenharmony_ci	if (ret < 0)
12062306a36Sopenharmony_ci		return ret;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	ret = clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV0],
12362306a36Sopenharmony_ci					ATCLK_MCUISP_FREQUENCY);
12462306a36Sopenharmony_ci	if (ret < 0)
12562306a36Sopenharmony_ci		return ret;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV1],
12862306a36Sopenharmony_ci					ATCLK_MCUISP_FREQUENCY);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int fimc_is_enable_clocks(struct fimc_is *is)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	int i, ret;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	for (i = 0; i < ISS_GATE_CLKS_MAX; i++) {
13662306a36Sopenharmony_ci		if (IS_ERR(is->clocks[i]))
13762306a36Sopenharmony_ci			continue;
13862306a36Sopenharmony_ci		ret = clk_prepare_enable(is->clocks[i]);
13962306a36Sopenharmony_ci		if (ret < 0) {
14062306a36Sopenharmony_ci			dev_err(&is->pdev->dev, "clock %s enable failed\n",
14162306a36Sopenharmony_ci				fimc_is_clocks[i]);
14262306a36Sopenharmony_ci			for (--i; i >= 0; i--)
14362306a36Sopenharmony_ci				clk_disable_unprepare(is->clocks[i]);
14462306a36Sopenharmony_ci			return ret;
14562306a36Sopenharmony_ci		}
14662306a36Sopenharmony_ci		pr_debug("enabled clock: %s\n", fimc_is_clocks[i]);
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci	return 0;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic void fimc_is_disable_clocks(struct fimc_is *is)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	int i;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	for (i = 0; i < ISS_GATE_CLKS_MAX; i++) {
15662306a36Sopenharmony_ci		if (!IS_ERR(is->clocks[i])) {
15762306a36Sopenharmony_ci			clk_disable_unprepare(is->clocks[i]);
15862306a36Sopenharmony_ci			pr_debug("disabled clock: %s\n", fimc_is_clocks[i]);
15962306a36Sopenharmony_ci		}
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index,
16462306a36Sopenharmony_ci						struct device_node *node)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct fimc_is_sensor *sensor = &is->sensor[index];
16762306a36Sopenharmony_ci	struct device_node *ep, *port;
16862306a36Sopenharmony_ci	u32 tmp = 0;
16962306a36Sopenharmony_ci	int ret;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	sensor->drvdata = fimc_is_sensor_get_drvdata(node);
17262306a36Sopenharmony_ci	if (!sensor->drvdata) {
17362306a36Sopenharmony_ci		dev_err(&is->pdev->dev, "no driver data found for: %pOF\n",
17462306a36Sopenharmony_ci							 node);
17562306a36Sopenharmony_ci		return -EINVAL;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	ep = of_graph_get_next_endpoint(node, NULL);
17962306a36Sopenharmony_ci	if (!ep)
18062306a36Sopenharmony_ci		return -ENXIO;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	port = of_graph_get_remote_port(ep);
18362306a36Sopenharmony_ci	of_node_put(ep);
18462306a36Sopenharmony_ci	if (!port)
18562306a36Sopenharmony_ci		return -ENXIO;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */
18862306a36Sopenharmony_ci	ret = of_property_read_u32(port, "reg", &tmp);
18962306a36Sopenharmony_ci	if (ret < 0) {
19062306a36Sopenharmony_ci		dev_err(&is->pdev->dev, "reg property not found at: %pOF\n",
19162306a36Sopenharmony_ci							 port);
19262306a36Sopenharmony_ci		of_node_put(port);
19362306a36Sopenharmony_ci		return ret;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	of_node_put(port);
19762306a36Sopenharmony_ci	sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0;
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int fimc_is_register_subdevs(struct fimc_is *is)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct device_node *i2c_bus, *child;
20462306a36Sopenharmony_ci	int ret, index = 0;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	ret = fimc_isp_subdev_create(&is->isp);
20762306a36Sopenharmony_ci	if (ret < 0)
20862306a36Sopenharmony_ci		return ret;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) {
21162306a36Sopenharmony_ci		for_each_available_child_of_node(i2c_bus, child) {
21262306a36Sopenharmony_ci			ret = fimc_is_parse_sensor_config(is, index, child);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci			if (ret < 0 || index >= FIMC_IS_SENSORS_NUM) {
21562306a36Sopenharmony_ci				of_node_put(child);
21662306a36Sopenharmony_ci				of_node_put(i2c_bus);
21762306a36Sopenharmony_ci				return ret;
21862306a36Sopenharmony_ci			}
21962306a36Sopenharmony_ci			index++;
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci	return 0;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic int fimc_is_unregister_subdevs(struct fimc_is *is)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	fimc_isp_subdev_destroy(&is->isp);
22862306a36Sopenharmony_ci	return 0;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic int fimc_is_load_setfile(struct fimc_is *is, char *file_name)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	const struct firmware *fw;
23462306a36Sopenharmony_ci	void *buf;
23562306a36Sopenharmony_ci	int ret;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	ret = request_firmware(&fw, file_name, &is->pdev->dev);
23862306a36Sopenharmony_ci	if (ret < 0) {
23962306a36Sopenharmony_ci		dev_err(&is->pdev->dev, "firmware request failed (%d)\n", ret);
24062306a36Sopenharmony_ci		return ret;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci	buf = is->memory.vaddr + is->setfile.base;
24362306a36Sopenharmony_ci	memcpy(buf, fw->data, fw->size);
24462306a36Sopenharmony_ci	fimc_is_mem_barrier();
24562306a36Sopenharmony_ci	is->setfile.size = fw->size;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	pr_debug("mem vaddr: %p, setfile buf: %p\n", is->memory.vaddr, buf);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	memcpy(is->fw.setfile_info,
25062306a36Sopenharmony_ci		fw->data + fw->size - FIMC_IS_SETFILE_INFO_LEN,
25162306a36Sopenharmony_ci		FIMC_IS_SETFILE_INFO_LEN - 1);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	is->fw.setfile_info[FIMC_IS_SETFILE_INFO_LEN - 1] = '\0';
25462306a36Sopenharmony_ci	is->setfile.state = 1;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	pr_debug("FIMC-IS setfile loaded: base: %#x, size: %zu B\n",
25762306a36Sopenharmony_ci		 is->setfile.base, fw->size);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	release_firmware(fw);
26062306a36Sopenharmony_ci	return ret;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ciint fimc_is_cpu_set_power(struct fimc_is *is, int on)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	unsigned int timeout = FIMC_IS_POWER_ON_TIMEOUT;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (on) {
26862306a36Sopenharmony_ci		/* Disable watchdog */
26962306a36Sopenharmony_ci		mcuctl_write(0, is, REG_WDT_ISP);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		/* Cortex-A5 start address setting */
27262306a36Sopenharmony_ci		mcuctl_write(is->memory.addr, is, MCUCTL_REG_BBOAR);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci		/* Enable and start Cortex-A5 */
27562306a36Sopenharmony_ci		pmuisp_write(0x18000, is, REG_PMU_ISP_ARM_OPTION);
27662306a36Sopenharmony_ci		pmuisp_write(0x1, is, REG_PMU_ISP_ARM_CONFIGURATION);
27762306a36Sopenharmony_ci	} else {
27862306a36Sopenharmony_ci		/* A5 power off */
27962306a36Sopenharmony_ci		pmuisp_write(0x10000, is, REG_PMU_ISP_ARM_OPTION);
28062306a36Sopenharmony_ci		pmuisp_write(0x0, is, REG_PMU_ISP_ARM_CONFIGURATION);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		while (pmuisp_read(is, REG_PMU_ISP_ARM_STATUS) & 1) {
28362306a36Sopenharmony_ci			if (timeout == 0)
28462306a36Sopenharmony_ci				return -ETIME;
28562306a36Sopenharmony_ci			timeout--;
28662306a36Sopenharmony_ci			udelay(1);
28762306a36Sopenharmony_ci		}
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return 0;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci/* Wait until @bit of @is->state is set to @state in the interrupt handler. */
29462306a36Sopenharmony_ciint fimc_is_wait_event(struct fimc_is *is, unsigned long bit,
29562306a36Sopenharmony_ci		       unsigned int state, unsigned int timeout)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	int ret = wait_event_timeout(is->irq_queue,
29962306a36Sopenharmony_ci				     !state ^ test_bit(bit, &is->state),
30062306a36Sopenharmony_ci				     timeout);
30162306a36Sopenharmony_ci	if (ret == 0) {
30262306a36Sopenharmony_ci		dev_WARN(&is->pdev->dev, "%s() timed out\n", __func__);
30362306a36Sopenharmony_ci		return -ETIME;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci	return 0;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ciint fimc_is_start_firmware(struct fimc_is *is)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct device *dev = &is->pdev->dev;
31162306a36Sopenharmony_ci	int ret;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (is->fw.f_w == NULL) {
31462306a36Sopenharmony_ci		dev_err(dev, "firmware is not loaded\n");
31562306a36Sopenharmony_ci		return -EINVAL;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	memcpy(is->memory.vaddr, is->fw.f_w->data, is->fw.f_w->size);
31962306a36Sopenharmony_ci	wmb();
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	ret = fimc_is_cpu_set_power(is, 1);
32262306a36Sopenharmony_ci	if (ret < 0)
32362306a36Sopenharmony_ci		return ret;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	ret = fimc_is_wait_event(is, IS_ST_A5_PWR_ON, 1,
32662306a36Sopenharmony_ci				 msecs_to_jiffies(FIMC_IS_FW_LOAD_TIMEOUT));
32762306a36Sopenharmony_ci	if (ret < 0)
32862306a36Sopenharmony_ci		dev_err(dev, "FIMC-IS CPU power on failed\n");
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return ret;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci/* Allocate working memory for the FIMC-IS CPU. */
33462306a36Sopenharmony_cistatic int fimc_is_alloc_cpu_memory(struct fimc_is *is)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct device *dev = &is->pdev->dev;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	is->memory.vaddr = dma_alloc_coherent(dev, FIMC_IS_CPU_MEM_SIZE,
33962306a36Sopenharmony_ci					      &is->memory.addr, GFP_KERNEL);
34062306a36Sopenharmony_ci	if (is->memory.vaddr == NULL)
34162306a36Sopenharmony_ci		return -ENOMEM;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	is->memory.size = FIMC_IS_CPU_MEM_SIZE;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	dev_info(dev, "FIMC-IS CPU memory base: %pad\n", &is->memory.addr);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (((u32)is->memory.addr) & FIMC_IS_FW_ADDR_MASK) {
34862306a36Sopenharmony_ci		dev_err(dev, "invalid firmware memory alignment: %#x\n",
34962306a36Sopenharmony_ci			(u32)is->memory.addr);
35062306a36Sopenharmony_ci		dma_free_coherent(dev, is->memory.size, is->memory.vaddr,
35162306a36Sopenharmony_ci				  is->memory.addr);
35262306a36Sopenharmony_ci		return -EIO;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	is->is_p_region = (struct is_region *)(is->memory.vaddr +
35662306a36Sopenharmony_ci				FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	is->is_dma_p_region = is->memory.addr +
35962306a36Sopenharmony_ci				FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	is->is_shared_region = (struct is_share_region *)(is->memory.vaddr +
36262306a36Sopenharmony_ci				FIMC_IS_SHARED_REGION_OFFSET);
36362306a36Sopenharmony_ci	return 0;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic void fimc_is_free_cpu_memory(struct fimc_is *is)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct device *dev = &is->pdev->dev;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (is->memory.vaddr == NULL)
37162306a36Sopenharmony_ci		return;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	dma_free_coherent(dev, is->memory.size, is->memory.vaddr,
37462306a36Sopenharmony_ci			  is->memory.addr);
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic void fimc_is_load_firmware(const struct firmware *fw, void *context)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct fimc_is *is = context;
38062306a36Sopenharmony_ci	struct device *dev = &is->pdev->dev;
38162306a36Sopenharmony_ci	void *buf;
38262306a36Sopenharmony_ci	int ret;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (fw == NULL) {
38562306a36Sopenharmony_ci		dev_err(dev, "firmware request failed\n");
38662306a36Sopenharmony_ci		return;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci	mutex_lock(&is->lock);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (fw->size < FIMC_IS_FW_SIZE_MIN || fw->size > FIMC_IS_FW_SIZE_MAX) {
39162306a36Sopenharmony_ci		dev_err(dev, "wrong firmware size: %zu\n", fw->size);
39262306a36Sopenharmony_ci		goto done;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	is->fw.size = fw->size;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	ret = fimc_is_alloc_cpu_memory(is);
39862306a36Sopenharmony_ci	if (ret < 0) {
39962306a36Sopenharmony_ci		dev_err(dev, "failed to allocate FIMC-IS CPU memory\n");
40062306a36Sopenharmony_ci		goto done;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	memcpy(is->memory.vaddr, fw->data, fw->size);
40462306a36Sopenharmony_ci	wmb();
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	/* Read firmware description. */
40762306a36Sopenharmony_ci	buf = (void *)(is->memory.vaddr + fw->size - FIMC_IS_FW_DESC_LEN);
40862306a36Sopenharmony_ci	memcpy(&is->fw.info, buf, FIMC_IS_FW_INFO_LEN);
40962306a36Sopenharmony_ci	is->fw.info[FIMC_IS_FW_INFO_LEN] = 0;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	buf = (void *)(is->memory.vaddr + fw->size - FIMC_IS_FW_VER_LEN);
41262306a36Sopenharmony_ci	memcpy(&is->fw.version, buf, FIMC_IS_FW_VER_LEN);
41362306a36Sopenharmony_ci	is->fw.version[FIMC_IS_FW_VER_LEN - 1] = 0;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	is->fw.state = 1;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	dev_info(dev, "loaded firmware: %s, rev. %s\n",
41862306a36Sopenharmony_ci		 is->fw.info, is->fw.version);
41962306a36Sopenharmony_ci	dev_dbg(dev, "FW size: %zu, DMA addr: %pad\n", fw->size, &is->memory.addr);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	is->is_shared_region->chip_id = 0xe4412;
42262306a36Sopenharmony_ci	is->is_shared_region->chip_rev_no = 1;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	fimc_is_mem_barrier();
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/*
42762306a36Sopenharmony_ci	 * FIXME: The firmware is not being released for now, as it is
42862306a36Sopenharmony_ci	 * needed around for copying to the IS working memory every
42962306a36Sopenharmony_ci	 * time before the Cortex-A5 is restarted.
43062306a36Sopenharmony_ci	 */
43162306a36Sopenharmony_ci	release_firmware(is->fw.f_w);
43262306a36Sopenharmony_ci	is->fw.f_w = fw;
43362306a36Sopenharmony_cidone:
43462306a36Sopenharmony_ci	mutex_unlock(&is->lock);
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic int fimc_is_request_firmware(struct fimc_is *is, const char *fw_name)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	return request_firmware_nowait(THIS_MODULE,
44062306a36Sopenharmony_ci				FW_ACTION_UEVENT, fw_name, &is->pdev->dev,
44162306a36Sopenharmony_ci				GFP_KERNEL, is, fimc_is_load_firmware);
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci/* General IS interrupt handler */
44562306a36Sopenharmony_cistatic void fimc_is_general_irq_handler(struct fimc_is *is)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	is->i2h_cmd.cmd = mcuctl_read(is, MCUCTL_REG_ISSR(10));
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	switch (is->i2h_cmd.cmd) {
45062306a36Sopenharmony_ci	case IHC_GET_SENSOR_NUM:
45162306a36Sopenharmony_ci		fimc_is_hw_get_params(is, 1);
45262306a36Sopenharmony_ci		fimc_is_hw_wait_intmsr0_intmsd0(is);
45362306a36Sopenharmony_ci		fimc_is_hw_set_sensor_num(is);
45462306a36Sopenharmony_ci		pr_debug("ISP FW version: %#x\n", is->i2h_cmd.args[0]);
45562306a36Sopenharmony_ci		break;
45662306a36Sopenharmony_ci	case IHC_SET_FACE_MARK:
45762306a36Sopenharmony_ci	case IHC_FRAME_DONE:
45862306a36Sopenharmony_ci		fimc_is_hw_get_params(is, 2);
45962306a36Sopenharmony_ci		break;
46062306a36Sopenharmony_ci	case IHC_SET_SHOT_MARK:
46162306a36Sopenharmony_ci	case IHC_AA_DONE:
46262306a36Sopenharmony_ci	case IH_REPLY_DONE:
46362306a36Sopenharmony_ci		fimc_is_hw_get_params(is, 3);
46462306a36Sopenharmony_ci		break;
46562306a36Sopenharmony_ci	case IH_REPLY_NOT_DONE:
46662306a36Sopenharmony_ci		fimc_is_hw_get_params(is, 4);
46762306a36Sopenharmony_ci		break;
46862306a36Sopenharmony_ci	case IHC_NOT_READY:
46962306a36Sopenharmony_ci		break;
47062306a36Sopenharmony_ci	default:
47162306a36Sopenharmony_ci		pr_info("unknown command: %#x\n", is->i2h_cmd.cmd);
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	fimc_is_fw_clear_irq1(is, FIMC_IS_INT_GENERAL);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	switch (is->i2h_cmd.cmd) {
47762306a36Sopenharmony_ci	case IHC_GET_SENSOR_NUM:
47862306a36Sopenharmony_ci		fimc_is_hw_set_intgr0_gd0(is);
47962306a36Sopenharmony_ci		set_bit(IS_ST_A5_PWR_ON, &is->state);
48062306a36Sopenharmony_ci		break;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	case IHC_SET_SHOT_MARK:
48362306a36Sopenharmony_ci		break;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	case IHC_SET_FACE_MARK:
48662306a36Sopenharmony_ci		is->fd_header.count = is->i2h_cmd.args[0];
48762306a36Sopenharmony_ci		is->fd_header.index = is->i2h_cmd.args[1];
48862306a36Sopenharmony_ci		is->fd_header.offset = 0;
48962306a36Sopenharmony_ci		break;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	case IHC_FRAME_DONE:
49262306a36Sopenharmony_ci		break;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	case IHC_AA_DONE:
49562306a36Sopenharmony_ci		pr_debug("AA_DONE - %d, %d, %d\n", is->i2h_cmd.args[0],
49662306a36Sopenharmony_ci			 is->i2h_cmd.args[1], is->i2h_cmd.args[2]);
49762306a36Sopenharmony_ci		break;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	case IH_REPLY_DONE:
50062306a36Sopenharmony_ci		pr_debug("ISR_DONE: args[0]: %#x\n", is->i2h_cmd.args[0]);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci		switch (is->i2h_cmd.args[0]) {
50362306a36Sopenharmony_ci		case HIC_PREVIEW_STILL...HIC_CAPTURE_VIDEO:
50462306a36Sopenharmony_ci			/* Get CAC margin */
50562306a36Sopenharmony_ci			set_bit(IS_ST_CHANGE_MODE, &is->state);
50662306a36Sopenharmony_ci			is->isp.cac_margin_x = is->i2h_cmd.args[1];
50762306a36Sopenharmony_ci			is->isp.cac_margin_y = is->i2h_cmd.args[2];
50862306a36Sopenharmony_ci			pr_debug("CAC margin (x,y): (%d,%d)\n",
50962306a36Sopenharmony_ci				 is->isp.cac_margin_x, is->isp.cac_margin_y);
51062306a36Sopenharmony_ci			break;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		case HIC_STREAM_ON:
51362306a36Sopenharmony_ci			clear_bit(IS_ST_STREAM_OFF, &is->state);
51462306a36Sopenharmony_ci			set_bit(IS_ST_STREAM_ON, &is->state);
51562306a36Sopenharmony_ci			break;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		case HIC_STREAM_OFF:
51862306a36Sopenharmony_ci			clear_bit(IS_ST_STREAM_ON, &is->state);
51962306a36Sopenharmony_ci			set_bit(IS_ST_STREAM_OFF, &is->state);
52062306a36Sopenharmony_ci			break;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci		case HIC_SET_PARAMETER:
52362306a36Sopenharmony_ci			is->config[is->config_index].p_region_index[0] = 0;
52462306a36Sopenharmony_ci			is->config[is->config_index].p_region_index[1] = 0;
52562306a36Sopenharmony_ci			set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state);
52662306a36Sopenharmony_ci			pr_debug("HIC_SET_PARAMETER\n");
52762306a36Sopenharmony_ci			break;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci		case HIC_GET_PARAMETER:
53062306a36Sopenharmony_ci			break;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		case HIC_SET_TUNE:
53362306a36Sopenharmony_ci			break;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci		case HIC_GET_STATUS:
53662306a36Sopenharmony_ci			break;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci		case HIC_OPEN_SENSOR:
53962306a36Sopenharmony_ci			set_bit(IS_ST_OPEN_SENSOR, &is->state);
54062306a36Sopenharmony_ci			pr_debug("data lanes: %d, settle line: %d\n",
54162306a36Sopenharmony_ci				 is->i2h_cmd.args[2], is->i2h_cmd.args[1]);
54262306a36Sopenharmony_ci			break;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci		case HIC_CLOSE_SENSOR:
54562306a36Sopenharmony_ci			clear_bit(IS_ST_OPEN_SENSOR, &is->state);
54662306a36Sopenharmony_ci			is->sensor_index = 0;
54762306a36Sopenharmony_ci			break;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		case HIC_MSG_TEST:
55062306a36Sopenharmony_ci			pr_debug("config MSG level completed\n");
55162306a36Sopenharmony_ci			break;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci		case HIC_POWER_DOWN:
55462306a36Sopenharmony_ci			clear_bit(IS_ST_PWR_SUBIP_ON, &is->state);
55562306a36Sopenharmony_ci			break;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		case HIC_GET_SET_FILE_ADDR:
55862306a36Sopenharmony_ci			is->setfile.base = is->i2h_cmd.args[1];
55962306a36Sopenharmony_ci			set_bit(IS_ST_SETFILE_LOADED, &is->state);
56062306a36Sopenharmony_ci			break;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		case HIC_LOAD_SET_FILE:
56362306a36Sopenharmony_ci			set_bit(IS_ST_SETFILE_LOADED, &is->state);
56462306a36Sopenharmony_ci			break;
56562306a36Sopenharmony_ci		}
56662306a36Sopenharmony_ci		break;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	case IH_REPLY_NOT_DONE:
56962306a36Sopenharmony_ci		pr_err("ISR_NDONE: %d: %#x, %s\n", is->i2h_cmd.args[0],
57062306a36Sopenharmony_ci		       is->i2h_cmd.args[1],
57162306a36Sopenharmony_ci		       fimc_is_strerr(is->i2h_cmd.args[1]));
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		if (is->i2h_cmd.args[1] & IS_ERROR_TIME_OUT_FLAG)
57462306a36Sopenharmony_ci			pr_err("IS_ERROR_TIME_OUT\n");
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci		switch (is->i2h_cmd.args[1]) {
57762306a36Sopenharmony_ci		case IS_ERROR_SET_PARAMETER:
57862306a36Sopenharmony_ci			fimc_is_mem_barrier();
57962306a36Sopenharmony_ci		}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		switch (is->i2h_cmd.args[0]) {
58262306a36Sopenharmony_ci		case HIC_SET_PARAMETER:
58362306a36Sopenharmony_ci			is->config[is->config_index].p_region_index[0] = 0;
58462306a36Sopenharmony_ci			is->config[is->config_index].p_region_index[1] = 0;
58562306a36Sopenharmony_ci			set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state);
58662306a36Sopenharmony_ci			break;
58762306a36Sopenharmony_ci		}
58862306a36Sopenharmony_ci		break;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	case IHC_NOT_READY:
59162306a36Sopenharmony_ci		pr_err("IS control sequence error: Not Ready\n");
59262306a36Sopenharmony_ci		break;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	wake_up(&is->irq_queue);
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic irqreturn_t fimc_is_irq_handler(int irq, void *priv)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	struct fimc_is *is = priv;
60162306a36Sopenharmony_ci	unsigned long flags;
60262306a36Sopenharmony_ci	u32 status;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	spin_lock_irqsave(&is->slock, flags);
60562306a36Sopenharmony_ci	status = mcuctl_read(is, MCUCTL_REG_INTSR1);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	if (status & (1UL << FIMC_IS_INT_GENERAL))
60862306a36Sopenharmony_ci		fimc_is_general_irq_handler(is);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if (status & (1UL << FIMC_IS_INT_FRAME_DONE_ISP))
61162306a36Sopenharmony_ci		fimc_isp_irq_handler(is);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	spin_unlock_irqrestore(&is->slock, flags);
61462306a36Sopenharmony_ci	return IRQ_HANDLED;
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cistatic int fimc_is_hw_open_sensor(struct fimc_is *is,
61862306a36Sopenharmony_ci				  struct fimc_is_sensor *sensor)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	struct sensor_open_extended *soe = (void *)&is->is_p_region->shared;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	fimc_is_hw_wait_intmsr0_intmsd0(is);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	soe->self_calibration_mode = 1;
62562306a36Sopenharmony_ci	soe->actuator_type = 0;
62662306a36Sopenharmony_ci	soe->mipi_lane_num = 0;
62762306a36Sopenharmony_ci	soe->mclk = 0;
62862306a36Sopenharmony_ci	soe->mipi_speed	= 0;
62962306a36Sopenharmony_ci	soe->fast_open_sensor = 0;
63062306a36Sopenharmony_ci	soe->i2c_sclk = 88000000;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	fimc_is_mem_barrier();
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	/*
63562306a36Sopenharmony_ci	 * Some user space use cases hang up here without this
63662306a36Sopenharmony_ci	 * empirically chosen delay.
63762306a36Sopenharmony_ci	 */
63862306a36Sopenharmony_ci	udelay(100);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	mcuctl_write(HIC_OPEN_SENSOR, is, MCUCTL_REG_ISSR(0));
64162306a36Sopenharmony_ci	mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
64262306a36Sopenharmony_ci	mcuctl_write(sensor->drvdata->id, is, MCUCTL_REG_ISSR(2));
64362306a36Sopenharmony_ci	mcuctl_write(sensor->i2c_bus, is, MCUCTL_REG_ISSR(3));
64462306a36Sopenharmony_ci	mcuctl_write(is->is_dma_p_region, is, MCUCTL_REG_ISSR(4));
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	fimc_is_hw_set_intgr0_gd0(is);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1,
64962306a36Sopenharmony_ci				  sensor->drvdata->open_timeout);
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ciint fimc_is_hw_initialize(struct fimc_is *is)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	static const int config_ids[] = {
65662306a36Sopenharmony_ci		IS_SC_PREVIEW_STILL, IS_SC_PREVIEW_VIDEO,
65762306a36Sopenharmony_ci		IS_SC_CAPTURE_STILL, IS_SC_CAPTURE_VIDEO
65862306a36Sopenharmony_ci	};
65962306a36Sopenharmony_ci	struct device *dev = &is->pdev->dev;
66062306a36Sopenharmony_ci	u32 prev_id;
66162306a36Sopenharmony_ci	int i, ret;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	/* Sensor initialization. Only one sensor is currently supported. */
66462306a36Sopenharmony_ci	ret = fimc_is_hw_open_sensor(is, &is->sensor[0]);
66562306a36Sopenharmony_ci	if (ret < 0)
66662306a36Sopenharmony_ci		return ret;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/* Get the setfile address. */
66962306a36Sopenharmony_ci	fimc_is_hw_get_setfile_addr(is);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	ret = fimc_is_wait_event(is, IS_ST_SETFILE_LOADED, 1,
67262306a36Sopenharmony_ci				 FIMC_IS_CONFIG_TIMEOUT);
67362306a36Sopenharmony_ci	if (ret < 0) {
67462306a36Sopenharmony_ci		dev_err(dev, "get setfile address timed out\n");
67562306a36Sopenharmony_ci		return ret;
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci	pr_debug("setfile.base: %#x\n", is->setfile.base);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	/* Load the setfile. */
68062306a36Sopenharmony_ci	fimc_is_load_setfile(is, FIMC_IS_SETFILE_6A3);
68162306a36Sopenharmony_ci	clear_bit(IS_ST_SETFILE_LOADED, &is->state);
68262306a36Sopenharmony_ci	fimc_is_hw_load_setfile(is);
68362306a36Sopenharmony_ci	ret = fimc_is_wait_event(is, IS_ST_SETFILE_LOADED, 1,
68462306a36Sopenharmony_ci				 FIMC_IS_CONFIG_TIMEOUT);
68562306a36Sopenharmony_ci	if (ret < 0) {
68662306a36Sopenharmony_ci		dev_err(dev, "loading setfile timed out\n");
68762306a36Sopenharmony_ci		return ret;
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	pr_debug("setfile: base: %#x, size: %d\n",
69162306a36Sopenharmony_ci		 is->setfile.base, is->setfile.size);
69262306a36Sopenharmony_ci	pr_info("FIMC-IS Setfile info: %s\n", is->fw.setfile_info);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	/* Check magic number. */
69562306a36Sopenharmony_ci	if (is->is_p_region->shared[MAX_SHARED_COUNT - 1] !=
69662306a36Sopenharmony_ci	    FIMC_IS_MAGIC_NUMBER) {
69762306a36Sopenharmony_ci		dev_err(dev, "magic number error!\n");
69862306a36Sopenharmony_ci		return -EIO;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	pr_debug("shared region: %pad, parameter region: %pad\n",
70262306a36Sopenharmony_ci		 &is->memory.addr + FIMC_IS_SHARED_REGION_OFFSET,
70362306a36Sopenharmony_ci		 &is->is_dma_p_region);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	is->setfile.sub_index = 0;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	/* Stream off. */
70862306a36Sopenharmony_ci	fimc_is_hw_stream_off(is);
70962306a36Sopenharmony_ci	ret = fimc_is_wait_event(is, IS_ST_STREAM_OFF, 1,
71062306a36Sopenharmony_ci				 FIMC_IS_CONFIG_TIMEOUT);
71162306a36Sopenharmony_ci	if (ret < 0) {
71262306a36Sopenharmony_ci		dev_err(dev, "stream off timeout\n");
71362306a36Sopenharmony_ci		return ret;
71462306a36Sopenharmony_ci	}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	/* Preserve previous mode. */
71762306a36Sopenharmony_ci	prev_id = is->config_index;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	/* Set initial parameter values. */
72062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(config_ids); i++) {
72162306a36Sopenharmony_ci		is->config_index = config_ids[i];
72262306a36Sopenharmony_ci		fimc_is_set_initial_params(is);
72362306a36Sopenharmony_ci		ret = fimc_is_itf_s_param(is, true);
72462306a36Sopenharmony_ci		if (ret < 0) {
72562306a36Sopenharmony_ci			is->config_index = prev_id;
72662306a36Sopenharmony_ci			return ret;
72762306a36Sopenharmony_ci		}
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci	is->config_index = prev_id;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	set_bit(IS_ST_INIT_DONE, &is->state);
73262306a36Sopenharmony_ci	dev_info(dev, "initialization sequence completed (%d)\n",
73362306a36Sopenharmony_ci						is->config_index);
73462306a36Sopenharmony_ci	return 0;
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cistatic int fimc_is_show(struct seq_file *s, void *data)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	struct fimc_is *is = s->private;
74062306a36Sopenharmony_ci	const u8 *buf = is->memory.vaddr + FIMC_IS_DEBUG_REGION_OFFSET;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	if (is->memory.vaddr == NULL) {
74362306a36Sopenharmony_ci		dev_err(&is->pdev->dev, "firmware memory is not initialized\n");
74462306a36Sopenharmony_ci		return -EIO;
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	seq_printf(s, "%s\n", buf);
74862306a36Sopenharmony_ci	return 0;
74962306a36Sopenharmony_ci}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(fimc_is);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cistatic void fimc_is_debugfs_remove(struct fimc_is *is)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	debugfs_remove_recursive(is->debugfs_entry);
75662306a36Sopenharmony_ci	is->debugfs_entry = NULL;
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cistatic void fimc_is_debugfs_create(struct fimc_is *is)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	is->debugfs_entry = debugfs_create_dir("fimc_is", NULL);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	debugfs_create_file("fw_log", S_IRUGO, is->debugfs_entry, is,
76462306a36Sopenharmony_ci			    &fimc_is_fops);
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic int fimc_is_runtime_resume(struct device *dev);
76862306a36Sopenharmony_cistatic int fimc_is_runtime_suspend(struct device *dev);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic int fimc_is_probe(struct platform_device *pdev)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
77362306a36Sopenharmony_ci	struct fimc_is *is;
77462306a36Sopenharmony_ci	struct resource res;
77562306a36Sopenharmony_ci	struct device_node *node;
77662306a36Sopenharmony_ci	int ret;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	is = devm_kzalloc(&pdev->dev, sizeof(*is), GFP_KERNEL);
77962306a36Sopenharmony_ci	if (!is)
78062306a36Sopenharmony_ci		return -ENOMEM;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	is->pdev = pdev;
78362306a36Sopenharmony_ci	is->isp.pdev = pdev;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	init_waitqueue_head(&is->irq_queue);
78662306a36Sopenharmony_ci	spin_lock_init(&is->slock);
78762306a36Sopenharmony_ci	mutex_init(&is->lock);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	ret = of_address_to_resource(dev->of_node, 0, &res);
79062306a36Sopenharmony_ci	if (ret < 0)
79162306a36Sopenharmony_ci		return ret;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	is->regs = devm_ioremap_resource(dev, &res);
79462306a36Sopenharmony_ci	if (IS_ERR(is->regs))
79562306a36Sopenharmony_ci		return PTR_ERR(is->regs);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	node = of_get_child_by_name(dev->of_node, "pmu");
79862306a36Sopenharmony_ci	if (!node)
79962306a36Sopenharmony_ci		return -ENODEV;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	is->pmu_regs = of_iomap(node, 0);
80262306a36Sopenharmony_ci	of_node_put(node);
80362306a36Sopenharmony_ci	if (!is->pmu_regs)
80462306a36Sopenharmony_ci		return -ENOMEM;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	is->irq = irq_of_parse_and_map(dev->of_node, 0);
80762306a36Sopenharmony_ci	if (!is->irq) {
80862306a36Sopenharmony_ci		dev_err(dev, "no irq found\n");
80962306a36Sopenharmony_ci		ret = -EINVAL;
81062306a36Sopenharmony_ci		goto err_iounmap;
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	ret = fimc_is_get_clocks(is);
81462306a36Sopenharmony_ci	if (ret < 0)
81562306a36Sopenharmony_ci		goto err_iounmap;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	platform_set_drvdata(pdev, is);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	ret = request_irq(is->irq, fimc_is_irq_handler, 0, dev_name(dev), is);
82062306a36Sopenharmony_ci	if (ret < 0) {
82162306a36Sopenharmony_ci		dev_err(dev, "irq request failed\n");
82262306a36Sopenharmony_ci		goto err_clk;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci	pm_runtime_enable(dev);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	if (!pm_runtime_enabled(dev)) {
82762306a36Sopenharmony_ci		ret = fimc_is_runtime_resume(dev);
82862306a36Sopenharmony_ci		if (ret < 0)
82962306a36Sopenharmony_ci			goto err_irq;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(dev);
83362306a36Sopenharmony_ci	if (ret < 0)
83462306a36Sopenharmony_ci		goto err_pm_disable;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	ret = devm_of_platform_populate(dev);
83962306a36Sopenharmony_ci	if (ret < 0)
84062306a36Sopenharmony_ci		goto err_pm;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	/*
84362306a36Sopenharmony_ci	 * Register FIMC-IS V4L2 subdevs to this driver. The video nodes
84462306a36Sopenharmony_ci	 * will be created within the subdev's registered() callback.
84562306a36Sopenharmony_ci	 */
84662306a36Sopenharmony_ci	ret = fimc_is_register_subdevs(is);
84762306a36Sopenharmony_ci	if (ret < 0)
84862306a36Sopenharmony_ci		goto err_pm;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	fimc_is_debugfs_create(is);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	ret = fimc_is_request_firmware(is, FIMC_IS_FW_FILENAME);
85362306a36Sopenharmony_ci	if (ret < 0)
85462306a36Sopenharmony_ci		goto err_dfs;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	pm_runtime_put_sync(dev);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	dev_dbg(dev, "FIMC-IS registered successfully\n");
85962306a36Sopenharmony_ci	return 0;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cierr_dfs:
86262306a36Sopenharmony_ci	fimc_is_debugfs_remove(is);
86362306a36Sopenharmony_ci	fimc_is_unregister_subdevs(is);
86462306a36Sopenharmony_cierr_pm:
86562306a36Sopenharmony_ci	pm_runtime_put_noidle(dev);
86662306a36Sopenharmony_ci	if (!pm_runtime_enabled(dev))
86762306a36Sopenharmony_ci		fimc_is_runtime_suspend(dev);
86862306a36Sopenharmony_cierr_pm_disable:
86962306a36Sopenharmony_ci	pm_runtime_disable(dev);
87062306a36Sopenharmony_cierr_irq:
87162306a36Sopenharmony_ci	free_irq(is->irq, is);
87262306a36Sopenharmony_cierr_clk:
87362306a36Sopenharmony_ci	fimc_is_put_clocks(is);
87462306a36Sopenharmony_cierr_iounmap:
87562306a36Sopenharmony_ci	iounmap(is->pmu_regs);
87662306a36Sopenharmony_ci	return ret;
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic int fimc_is_runtime_resume(struct device *dev)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	struct fimc_is *is = dev_get_drvdata(dev);
88262306a36Sopenharmony_ci	int ret;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	ret = fimc_is_setup_clocks(is);
88562306a36Sopenharmony_ci	if (ret)
88662306a36Sopenharmony_ci		return ret;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	return fimc_is_enable_clocks(is);
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_cistatic int fimc_is_runtime_suspend(struct device *dev)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	struct fimc_is *is = dev_get_drvdata(dev);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	fimc_is_disable_clocks(is);
89662306a36Sopenharmony_ci	return 0;
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
90062306a36Sopenharmony_cistatic int fimc_is_resume(struct device *dev)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	/* TODO: */
90362306a36Sopenharmony_ci	return 0;
90462306a36Sopenharmony_ci}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_cistatic int fimc_is_suspend(struct device *dev)
90762306a36Sopenharmony_ci{
90862306a36Sopenharmony_ci	struct fimc_is *is = dev_get_drvdata(dev);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	/* TODO: */
91162306a36Sopenharmony_ci	if (test_bit(IS_ST_A5_PWR_ON, &is->state))
91262306a36Sopenharmony_ci		return -EBUSY;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	return 0;
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_cistatic void fimc_is_remove(struct platform_device *pdev)
91962306a36Sopenharmony_ci{
92062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
92162306a36Sopenharmony_ci	struct fimc_is *is = dev_get_drvdata(dev);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	pm_runtime_disable(dev);
92462306a36Sopenharmony_ci	pm_runtime_set_suspended(dev);
92562306a36Sopenharmony_ci	if (!pm_runtime_status_suspended(dev))
92662306a36Sopenharmony_ci		fimc_is_runtime_suspend(dev);
92762306a36Sopenharmony_ci	free_irq(is->irq, is);
92862306a36Sopenharmony_ci	fimc_is_unregister_subdevs(is);
92962306a36Sopenharmony_ci	vb2_dma_contig_clear_max_seg_size(dev);
93062306a36Sopenharmony_ci	fimc_is_put_clocks(is);
93162306a36Sopenharmony_ci	iounmap(is->pmu_regs);
93262306a36Sopenharmony_ci	fimc_is_debugfs_remove(is);
93362306a36Sopenharmony_ci	release_firmware(is->fw.f_w);
93462306a36Sopenharmony_ci	fimc_is_free_cpu_memory(is);
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_cistatic const struct of_device_id fimc_is_of_match[] = {
93862306a36Sopenharmony_ci	{ .compatible = "samsung,exynos4212-fimc-is" },
93962306a36Sopenharmony_ci	{ /* sentinel */ },
94062306a36Sopenharmony_ci};
94162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fimc_is_of_match);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistatic const struct dev_pm_ops fimc_is_pm_ops = {
94462306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(fimc_is_suspend, fimc_is_resume)
94562306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(fimc_is_runtime_suspend, fimc_is_runtime_resume,
94662306a36Sopenharmony_ci			   NULL)
94762306a36Sopenharmony_ci};
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_cistatic struct platform_driver fimc_is_driver = {
95062306a36Sopenharmony_ci	.probe		= fimc_is_probe,
95162306a36Sopenharmony_ci	.remove_new	= fimc_is_remove,
95262306a36Sopenharmony_ci	.driver = {
95362306a36Sopenharmony_ci		.of_match_table	= fimc_is_of_match,
95462306a36Sopenharmony_ci		.name		= FIMC_IS_DRV_NAME,
95562306a36Sopenharmony_ci		.pm		= &fimc_is_pm_ops,
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci};
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic int fimc_is_module_init(void)
96062306a36Sopenharmony_ci{
96162306a36Sopenharmony_ci	int ret;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	ret = fimc_is_register_i2c_driver();
96462306a36Sopenharmony_ci	if (ret < 0)
96562306a36Sopenharmony_ci		return ret;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	ret = platform_driver_register(&fimc_is_driver);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	if (ret < 0)
97062306a36Sopenharmony_ci		fimc_is_unregister_i2c_driver();
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	return ret;
97362306a36Sopenharmony_ci}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_cistatic void fimc_is_module_exit(void)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	fimc_is_unregister_i2c_driver();
97862306a36Sopenharmony_ci	platform_driver_unregister(&fimc_is_driver);
97962306a36Sopenharmony_ci}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_cimodule_init(fimc_is_module_init);
98262306a36Sopenharmony_cimodule_exit(fimc_is_module_exit);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ciMODULE_ALIAS("platform:" FIMC_IS_DRV_NAME);
98562306a36Sopenharmony_ciMODULE_AUTHOR("Younghwan Joo <yhwan.joo@samsung.com>");
98662306a36Sopenharmony_ciMODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
98762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
988