1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2013 Samsung Electronics Co., Ltd.
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com>
8*4882a593Smuzhiyun * Younghwan Joo <yhwan.joo@samsung.com>
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun #define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/device.h>
13*4882a593Smuzhiyun #include <linux/debugfs.h>
14*4882a593Smuzhiyun #include <linux/delay.h>
15*4882a593Smuzhiyun #include <linux/errno.h>
16*4882a593Smuzhiyun #include <linux/firmware.h>
17*4882a593Smuzhiyun #include <linux/interrupt.h>
18*4882a593Smuzhiyun #include <linux/kernel.h>
19*4882a593Smuzhiyun #include <linux/module.h>
20*4882a593Smuzhiyun #include <linux/i2c.h>
21*4882a593Smuzhiyun #include <linux/of_irq.h>
22*4882a593Smuzhiyun #include <linux/of_address.h>
23*4882a593Smuzhiyun #include <linux/of_graph.h>
24*4882a593Smuzhiyun #include <linux/of_platform.h>
25*4882a593Smuzhiyun #include <linux/platform_device.h>
26*4882a593Smuzhiyun #include <linux/pm_runtime.h>
27*4882a593Smuzhiyun #include <linux/slab.h>
28*4882a593Smuzhiyun #include <linux/types.h>
29*4882a593Smuzhiyun #include <linux/videodev2.h>
30*4882a593Smuzhiyun #include <media/videobuf2-dma-contig.h>
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun #include "media-dev.h"
33*4882a593Smuzhiyun #include "fimc-is.h"
34*4882a593Smuzhiyun #include "fimc-is-command.h"
35*4882a593Smuzhiyun #include "fimc-is-errno.h"
36*4882a593Smuzhiyun #include "fimc-is-i2c.h"
37*4882a593Smuzhiyun #include "fimc-is-param.h"
38*4882a593Smuzhiyun #include "fimc-is-regs.h"
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun static char *fimc_is_clocks[ISS_CLKS_MAX] = {
42*4882a593Smuzhiyun [ISS_CLK_PPMUISPX] = "ppmuispx",
43*4882a593Smuzhiyun [ISS_CLK_PPMUISPMX] = "ppmuispmx",
44*4882a593Smuzhiyun [ISS_CLK_LITE0] = "lite0",
45*4882a593Smuzhiyun [ISS_CLK_LITE1] = "lite1",
46*4882a593Smuzhiyun [ISS_CLK_MPLL] = "mpll",
47*4882a593Smuzhiyun [ISS_CLK_ISP] = "isp",
48*4882a593Smuzhiyun [ISS_CLK_DRC] = "drc",
49*4882a593Smuzhiyun [ISS_CLK_FD] = "fd",
50*4882a593Smuzhiyun [ISS_CLK_MCUISP] = "mcuisp",
51*4882a593Smuzhiyun [ISS_CLK_GICISP] = "gicisp",
52*4882a593Smuzhiyun [ISS_CLK_PWM_ISP] = "pwm_isp",
53*4882a593Smuzhiyun [ISS_CLK_MCUCTL_ISP] = "mcuctl_isp",
54*4882a593Smuzhiyun [ISS_CLK_UART] = "uart",
55*4882a593Smuzhiyun [ISS_CLK_ISP_DIV0] = "ispdiv0",
56*4882a593Smuzhiyun [ISS_CLK_ISP_DIV1] = "ispdiv1",
57*4882a593Smuzhiyun [ISS_CLK_MCUISP_DIV0] = "mcuispdiv0",
58*4882a593Smuzhiyun [ISS_CLK_MCUISP_DIV1] = "mcuispdiv1",
59*4882a593Smuzhiyun [ISS_CLK_ACLK200] = "aclk200",
60*4882a593Smuzhiyun [ISS_CLK_ACLK200_DIV] = "div_aclk200",
61*4882a593Smuzhiyun [ISS_CLK_ACLK400MCUISP] = "aclk400mcuisp",
62*4882a593Smuzhiyun [ISS_CLK_ACLK400MCUISP_DIV] = "div_aclk400mcuisp",
63*4882a593Smuzhiyun };
64*4882a593Smuzhiyun
fimc_is_put_clocks(struct fimc_is * is)65*4882a593Smuzhiyun static void fimc_is_put_clocks(struct fimc_is *is)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun int i;
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun for (i = 0; i < ISS_CLKS_MAX; i++) {
70*4882a593Smuzhiyun if (IS_ERR(is->clocks[i]))
71*4882a593Smuzhiyun continue;
72*4882a593Smuzhiyun clk_put(is->clocks[i]);
73*4882a593Smuzhiyun is->clocks[i] = ERR_PTR(-EINVAL);
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun
fimc_is_get_clocks(struct fimc_is * is)77*4882a593Smuzhiyun static int fimc_is_get_clocks(struct fimc_is *is)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun int i, ret;
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun for (i = 0; i < ISS_CLKS_MAX; i++)
82*4882a593Smuzhiyun is->clocks[i] = ERR_PTR(-EINVAL);
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun for (i = 0; i < ISS_CLKS_MAX; i++) {
85*4882a593Smuzhiyun is->clocks[i] = clk_get(&is->pdev->dev, fimc_is_clocks[i]);
86*4882a593Smuzhiyun if (IS_ERR(is->clocks[i])) {
87*4882a593Smuzhiyun ret = PTR_ERR(is->clocks[i]);
88*4882a593Smuzhiyun goto err;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun return 0;
93*4882a593Smuzhiyun err:
94*4882a593Smuzhiyun fimc_is_put_clocks(is);
95*4882a593Smuzhiyun dev_err(&is->pdev->dev, "failed to get clock: %s\n",
96*4882a593Smuzhiyun fimc_is_clocks[i]);
97*4882a593Smuzhiyun return ret;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun
fimc_is_setup_clocks(struct fimc_is * is)100*4882a593Smuzhiyun static int fimc_is_setup_clocks(struct fimc_is *is)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun int ret;
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun ret = clk_set_parent(is->clocks[ISS_CLK_ACLK200],
105*4882a593Smuzhiyun is->clocks[ISS_CLK_ACLK200_DIV]);
106*4882a593Smuzhiyun if (ret < 0)
107*4882a593Smuzhiyun return ret;
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun ret = clk_set_parent(is->clocks[ISS_CLK_ACLK400MCUISP],
110*4882a593Smuzhiyun is->clocks[ISS_CLK_ACLK400MCUISP_DIV]);
111*4882a593Smuzhiyun if (ret < 0)
112*4882a593Smuzhiyun return ret;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV0], ACLK_AXI_FREQUENCY);
115*4882a593Smuzhiyun if (ret < 0)
116*4882a593Smuzhiyun return ret;
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV1], ACLK_AXI_FREQUENCY);
119*4882a593Smuzhiyun if (ret < 0)
120*4882a593Smuzhiyun return ret;
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun ret = clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV0],
123*4882a593Smuzhiyun ATCLK_MCUISP_FREQUENCY);
124*4882a593Smuzhiyun if (ret < 0)
125*4882a593Smuzhiyun return ret;
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun return clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV1],
128*4882a593Smuzhiyun ATCLK_MCUISP_FREQUENCY);
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
fimc_is_enable_clocks(struct fimc_is * is)131*4882a593Smuzhiyun static int fimc_is_enable_clocks(struct fimc_is *is)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun int i, ret;
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun for (i = 0; i < ISS_GATE_CLKS_MAX; i++) {
136*4882a593Smuzhiyun if (IS_ERR(is->clocks[i]))
137*4882a593Smuzhiyun continue;
138*4882a593Smuzhiyun ret = clk_prepare_enable(is->clocks[i]);
139*4882a593Smuzhiyun if (ret < 0) {
140*4882a593Smuzhiyun dev_err(&is->pdev->dev, "clock %s enable failed\n",
141*4882a593Smuzhiyun fimc_is_clocks[i]);
142*4882a593Smuzhiyun for (--i; i >= 0; i--)
143*4882a593Smuzhiyun clk_disable_unprepare(is->clocks[i]);
144*4882a593Smuzhiyun return ret;
145*4882a593Smuzhiyun }
146*4882a593Smuzhiyun pr_debug("enabled clock: %s\n", fimc_is_clocks[i]);
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun return 0;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
fimc_is_disable_clocks(struct fimc_is * is)151*4882a593Smuzhiyun static void fimc_is_disable_clocks(struct fimc_is *is)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun int i;
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun for (i = 0; i < ISS_GATE_CLKS_MAX; i++) {
156*4882a593Smuzhiyun if (!IS_ERR(is->clocks[i])) {
157*4882a593Smuzhiyun clk_disable_unprepare(is->clocks[i]);
158*4882a593Smuzhiyun pr_debug("disabled clock: %s\n", fimc_is_clocks[i]);
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
fimc_is_parse_sensor_config(struct fimc_is * is,unsigned int index,struct device_node * node)163*4882a593Smuzhiyun static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index,
164*4882a593Smuzhiyun struct device_node *node)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun struct fimc_is_sensor *sensor = &is->sensor[index];
167*4882a593Smuzhiyun struct device_node *ep, *port;
168*4882a593Smuzhiyun u32 tmp = 0;
169*4882a593Smuzhiyun int ret;
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun sensor->drvdata = fimc_is_sensor_get_drvdata(node);
172*4882a593Smuzhiyun if (!sensor->drvdata) {
173*4882a593Smuzhiyun dev_err(&is->pdev->dev, "no driver data found for: %pOF\n",
174*4882a593Smuzhiyun node);
175*4882a593Smuzhiyun return -EINVAL;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun ep = of_graph_get_next_endpoint(node, NULL);
179*4882a593Smuzhiyun if (!ep)
180*4882a593Smuzhiyun return -ENXIO;
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun port = of_graph_get_remote_port(ep);
183*4882a593Smuzhiyun of_node_put(ep);
184*4882a593Smuzhiyun if (!port)
185*4882a593Smuzhiyun return -ENXIO;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun /* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */
188*4882a593Smuzhiyun ret = of_property_read_u32(port, "reg", &tmp);
189*4882a593Smuzhiyun if (ret < 0) {
190*4882a593Smuzhiyun dev_err(&is->pdev->dev, "reg property not found at: %pOF\n",
191*4882a593Smuzhiyun port);
192*4882a593Smuzhiyun of_node_put(port);
193*4882a593Smuzhiyun return ret;
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun of_node_put(port);
197*4882a593Smuzhiyun sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0;
198*4882a593Smuzhiyun return 0;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun
fimc_is_register_subdevs(struct fimc_is * is)201*4882a593Smuzhiyun static int fimc_is_register_subdevs(struct fimc_is *is)
202*4882a593Smuzhiyun {
203*4882a593Smuzhiyun struct device_node *i2c_bus, *child;
204*4882a593Smuzhiyun int ret, index = 0;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun ret = fimc_isp_subdev_create(&is->isp);
207*4882a593Smuzhiyun if (ret < 0)
208*4882a593Smuzhiyun return ret;
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) {
211*4882a593Smuzhiyun for_each_available_child_of_node(i2c_bus, child) {
212*4882a593Smuzhiyun ret = fimc_is_parse_sensor_config(is, index, child);
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun if (ret < 0 || index >= FIMC_IS_SENSORS_NUM) {
215*4882a593Smuzhiyun of_node_put(child);
216*4882a593Smuzhiyun of_node_put(i2c_bus);
217*4882a593Smuzhiyun return ret;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun index++;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun return 0;
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun
fimc_is_unregister_subdevs(struct fimc_is * is)225*4882a593Smuzhiyun static int fimc_is_unregister_subdevs(struct fimc_is *is)
226*4882a593Smuzhiyun {
227*4882a593Smuzhiyun fimc_isp_subdev_destroy(&is->isp);
228*4882a593Smuzhiyun return 0;
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun
fimc_is_load_setfile(struct fimc_is * is,char * file_name)231*4882a593Smuzhiyun static int fimc_is_load_setfile(struct fimc_is *is, char *file_name)
232*4882a593Smuzhiyun {
233*4882a593Smuzhiyun const struct firmware *fw;
234*4882a593Smuzhiyun void *buf;
235*4882a593Smuzhiyun int ret;
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun ret = request_firmware(&fw, file_name, &is->pdev->dev);
238*4882a593Smuzhiyun if (ret < 0) {
239*4882a593Smuzhiyun dev_err(&is->pdev->dev, "firmware request failed (%d)\n", ret);
240*4882a593Smuzhiyun return ret;
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun buf = is->memory.vaddr + is->setfile.base;
243*4882a593Smuzhiyun memcpy(buf, fw->data, fw->size);
244*4882a593Smuzhiyun fimc_is_mem_barrier();
245*4882a593Smuzhiyun is->setfile.size = fw->size;
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun pr_debug("mem vaddr: %p, setfile buf: %p\n", is->memory.vaddr, buf);
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun memcpy(is->fw.setfile_info,
250*4882a593Smuzhiyun fw->data + fw->size - FIMC_IS_SETFILE_INFO_LEN,
251*4882a593Smuzhiyun FIMC_IS_SETFILE_INFO_LEN - 1);
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun is->fw.setfile_info[FIMC_IS_SETFILE_INFO_LEN - 1] = '\0';
254*4882a593Smuzhiyun is->setfile.state = 1;
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun pr_debug("FIMC-IS setfile loaded: base: %#x, size: %zu B\n",
257*4882a593Smuzhiyun is->setfile.base, fw->size);
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun release_firmware(fw);
260*4882a593Smuzhiyun return ret;
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun
fimc_is_cpu_set_power(struct fimc_is * is,int on)263*4882a593Smuzhiyun int fimc_is_cpu_set_power(struct fimc_is *is, int on)
264*4882a593Smuzhiyun {
265*4882a593Smuzhiyun unsigned int timeout = FIMC_IS_POWER_ON_TIMEOUT;
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun if (on) {
268*4882a593Smuzhiyun /* Disable watchdog */
269*4882a593Smuzhiyun mcuctl_write(0, is, REG_WDT_ISP);
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun /* Cortex-A5 start address setting */
272*4882a593Smuzhiyun mcuctl_write(is->memory.paddr, is, MCUCTL_REG_BBOAR);
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun /* Enable and start Cortex-A5 */
275*4882a593Smuzhiyun pmuisp_write(0x18000, is, REG_PMU_ISP_ARM_OPTION);
276*4882a593Smuzhiyun pmuisp_write(0x1, is, REG_PMU_ISP_ARM_CONFIGURATION);
277*4882a593Smuzhiyun } else {
278*4882a593Smuzhiyun /* A5 power off */
279*4882a593Smuzhiyun pmuisp_write(0x10000, is, REG_PMU_ISP_ARM_OPTION);
280*4882a593Smuzhiyun pmuisp_write(0x0, is, REG_PMU_ISP_ARM_CONFIGURATION);
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun while (pmuisp_read(is, REG_PMU_ISP_ARM_STATUS) & 1) {
283*4882a593Smuzhiyun if (timeout == 0)
284*4882a593Smuzhiyun return -ETIME;
285*4882a593Smuzhiyun timeout--;
286*4882a593Smuzhiyun udelay(1);
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun return 0;
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun /* Wait until @bit of @is->state is set to @state in the interrupt handler. */
fimc_is_wait_event(struct fimc_is * is,unsigned long bit,unsigned int state,unsigned int timeout)294*4882a593Smuzhiyun int fimc_is_wait_event(struct fimc_is *is, unsigned long bit,
295*4882a593Smuzhiyun unsigned int state, unsigned int timeout)
296*4882a593Smuzhiyun {
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun int ret = wait_event_timeout(is->irq_queue,
299*4882a593Smuzhiyun !state ^ test_bit(bit, &is->state),
300*4882a593Smuzhiyun timeout);
301*4882a593Smuzhiyun if (ret == 0) {
302*4882a593Smuzhiyun dev_WARN(&is->pdev->dev, "%s() timed out\n", __func__);
303*4882a593Smuzhiyun return -ETIME;
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun return 0;
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun
fimc_is_start_firmware(struct fimc_is * is)308*4882a593Smuzhiyun int fimc_is_start_firmware(struct fimc_is *is)
309*4882a593Smuzhiyun {
310*4882a593Smuzhiyun struct device *dev = &is->pdev->dev;
311*4882a593Smuzhiyun int ret;
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun if (is->fw.f_w == NULL) {
314*4882a593Smuzhiyun dev_err(dev, "firmware is not loaded\n");
315*4882a593Smuzhiyun return -EINVAL;
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun memcpy(is->memory.vaddr, is->fw.f_w->data, is->fw.f_w->size);
319*4882a593Smuzhiyun wmb();
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun ret = fimc_is_cpu_set_power(is, 1);
322*4882a593Smuzhiyun if (ret < 0)
323*4882a593Smuzhiyun return ret;
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun ret = fimc_is_wait_event(is, IS_ST_A5_PWR_ON, 1,
326*4882a593Smuzhiyun msecs_to_jiffies(FIMC_IS_FW_LOAD_TIMEOUT));
327*4882a593Smuzhiyun if (ret < 0)
328*4882a593Smuzhiyun dev_err(dev, "FIMC-IS CPU power on failed\n");
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun return ret;
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun /* Allocate working memory for the FIMC-IS CPU. */
fimc_is_alloc_cpu_memory(struct fimc_is * is)334*4882a593Smuzhiyun static int fimc_is_alloc_cpu_memory(struct fimc_is *is)
335*4882a593Smuzhiyun {
336*4882a593Smuzhiyun struct device *dev = &is->pdev->dev;
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun is->memory.vaddr = dma_alloc_coherent(dev, FIMC_IS_CPU_MEM_SIZE,
339*4882a593Smuzhiyun &is->memory.paddr, GFP_KERNEL);
340*4882a593Smuzhiyun if (is->memory.vaddr == NULL)
341*4882a593Smuzhiyun return -ENOMEM;
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun is->memory.size = FIMC_IS_CPU_MEM_SIZE;
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun dev_info(dev, "FIMC-IS CPU memory base: %#x\n", (u32)is->memory.paddr);
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun if (((u32)is->memory.paddr) & FIMC_IS_FW_ADDR_MASK) {
348*4882a593Smuzhiyun dev_err(dev, "invalid firmware memory alignment: %#x\n",
349*4882a593Smuzhiyun (u32)is->memory.paddr);
350*4882a593Smuzhiyun dma_free_coherent(dev, is->memory.size, is->memory.vaddr,
351*4882a593Smuzhiyun is->memory.paddr);
352*4882a593Smuzhiyun return -EIO;
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun is->is_p_region = (struct is_region *)(is->memory.vaddr +
356*4882a593Smuzhiyun FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE);
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun is->is_dma_p_region = is->memory.paddr +
359*4882a593Smuzhiyun FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE;
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun is->is_shared_region = (struct is_share_region *)(is->memory.vaddr +
362*4882a593Smuzhiyun FIMC_IS_SHARED_REGION_OFFSET);
363*4882a593Smuzhiyun return 0;
364*4882a593Smuzhiyun }
365*4882a593Smuzhiyun
fimc_is_free_cpu_memory(struct fimc_is * is)366*4882a593Smuzhiyun static void fimc_is_free_cpu_memory(struct fimc_is *is)
367*4882a593Smuzhiyun {
368*4882a593Smuzhiyun struct device *dev = &is->pdev->dev;
369*4882a593Smuzhiyun
370*4882a593Smuzhiyun if (is->memory.vaddr == NULL)
371*4882a593Smuzhiyun return;
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun dma_free_coherent(dev, is->memory.size, is->memory.vaddr,
374*4882a593Smuzhiyun is->memory.paddr);
375*4882a593Smuzhiyun }
376*4882a593Smuzhiyun
fimc_is_load_firmware(const struct firmware * fw,void * context)377*4882a593Smuzhiyun static void fimc_is_load_firmware(const struct firmware *fw, void *context)
378*4882a593Smuzhiyun {
379*4882a593Smuzhiyun struct fimc_is *is = context;
380*4882a593Smuzhiyun struct device *dev = &is->pdev->dev;
381*4882a593Smuzhiyun void *buf;
382*4882a593Smuzhiyun int ret;
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun if (fw == NULL) {
385*4882a593Smuzhiyun dev_err(dev, "firmware request failed\n");
386*4882a593Smuzhiyun return;
387*4882a593Smuzhiyun }
388*4882a593Smuzhiyun mutex_lock(&is->lock);
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun if (fw->size < FIMC_IS_FW_SIZE_MIN || fw->size > FIMC_IS_FW_SIZE_MAX) {
391*4882a593Smuzhiyun dev_err(dev, "wrong firmware size: %zu\n", fw->size);
392*4882a593Smuzhiyun goto done;
393*4882a593Smuzhiyun }
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun is->fw.size = fw->size;
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun ret = fimc_is_alloc_cpu_memory(is);
398*4882a593Smuzhiyun if (ret < 0) {
399*4882a593Smuzhiyun dev_err(dev, "failed to allocate FIMC-IS CPU memory\n");
400*4882a593Smuzhiyun goto done;
401*4882a593Smuzhiyun }
402*4882a593Smuzhiyun
403*4882a593Smuzhiyun memcpy(is->memory.vaddr, fw->data, fw->size);
404*4882a593Smuzhiyun wmb();
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun /* Read firmware description. */
407*4882a593Smuzhiyun buf = (void *)(is->memory.vaddr + fw->size - FIMC_IS_FW_DESC_LEN);
408*4882a593Smuzhiyun memcpy(&is->fw.info, buf, FIMC_IS_FW_INFO_LEN);
409*4882a593Smuzhiyun is->fw.info[FIMC_IS_FW_INFO_LEN] = 0;
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun buf = (void *)(is->memory.vaddr + fw->size - FIMC_IS_FW_VER_LEN);
412*4882a593Smuzhiyun memcpy(&is->fw.version, buf, FIMC_IS_FW_VER_LEN);
413*4882a593Smuzhiyun is->fw.version[FIMC_IS_FW_VER_LEN - 1] = 0;
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun is->fw.state = 1;
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun dev_info(dev, "loaded firmware: %s, rev. %s\n",
418*4882a593Smuzhiyun is->fw.info, is->fw.version);
419*4882a593Smuzhiyun dev_dbg(dev, "FW size: %zu, paddr: %pad\n", fw->size, &is->memory.paddr);
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun is->is_shared_region->chip_id = 0xe4412;
422*4882a593Smuzhiyun is->is_shared_region->chip_rev_no = 1;
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun fimc_is_mem_barrier();
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun /*
427*4882a593Smuzhiyun * FIXME: The firmware is not being released for now, as it is
428*4882a593Smuzhiyun * needed around for copying to the IS working memory every
429*4882a593Smuzhiyun * time before the Cortex-A5 is restarted.
430*4882a593Smuzhiyun */
431*4882a593Smuzhiyun release_firmware(is->fw.f_w);
432*4882a593Smuzhiyun is->fw.f_w = fw;
433*4882a593Smuzhiyun done:
434*4882a593Smuzhiyun mutex_unlock(&is->lock);
435*4882a593Smuzhiyun }
436*4882a593Smuzhiyun
fimc_is_request_firmware(struct fimc_is * is,const char * fw_name)437*4882a593Smuzhiyun static int fimc_is_request_firmware(struct fimc_is *is, const char *fw_name)
438*4882a593Smuzhiyun {
439*4882a593Smuzhiyun return request_firmware_nowait(THIS_MODULE,
440*4882a593Smuzhiyun FW_ACTION_HOTPLUG, fw_name, &is->pdev->dev,
441*4882a593Smuzhiyun GFP_KERNEL, is, fimc_is_load_firmware);
442*4882a593Smuzhiyun }
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun /* General IS interrupt handler */
fimc_is_general_irq_handler(struct fimc_is * is)445*4882a593Smuzhiyun static void fimc_is_general_irq_handler(struct fimc_is *is)
446*4882a593Smuzhiyun {
447*4882a593Smuzhiyun is->i2h_cmd.cmd = mcuctl_read(is, MCUCTL_REG_ISSR(10));
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun switch (is->i2h_cmd.cmd) {
450*4882a593Smuzhiyun case IHC_GET_SENSOR_NUM:
451*4882a593Smuzhiyun fimc_is_hw_get_params(is, 1);
452*4882a593Smuzhiyun fimc_is_hw_wait_intmsr0_intmsd0(is);
453*4882a593Smuzhiyun fimc_is_hw_set_sensor_num(is);
454*4882a593Smuzhiyun pr_debug("ISP FW version: %#x\n", is->i2h_cmd.args[0]);
455*4882a593Smuzhiyun break;
456*4882a593Smuzhiyun case IHC_SET_FACE_MARK:
457*4882a593Smuzhiyun case IHC_FRAME_DONE:
458*4882a593Smuzhiyun fimc_is_hw_get_params(is, 2);
459*4882a593Smuzhiyun break;
460*4882a593Smuzhiyun case IHC_SET_SHOT_MARK:
461*4882a593Smuzhiyun case IHC_AA_DONE:
462*4882a593Smuzhiyun case IH_REPLY_DONE:
463*4882a593Smuzhiyun fimc_is_hw_get_params(is, 3);
464*4882a593Smuzhiyun break;
465*4882a593Smuzhiyun case IH_REPLY_NOT_DONE:
466*4882a593Smuzhiyun fimc_is_hw_get_params(is, 4);
467*4882a593Smuzhiyun break;
468*4882a593Smuzhiyun case IHC_NOT_READY:
469*4882a593Smuzhiyun break;
470*4882a593Smuzhiyun default:
471*4882a593Smuzhiyun pr_info("unknown command: %#x\n", is->i2h_cmd.cmd);
472*4882a593Smuzhiyun }
473*4882a593Smuzhiyun
474*4882a593Smuzhiyun fimc_is_fw_clear_irq1(is, FIMC_IS_INT_GENERAL);
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun switch (is->i2h_cmd.cmd) {
477*4882a593Smuzhiyun case IHC_GET_SENSOR_NUM:
478*4882a593Smuzhiyun fimc_is_hw_set_intgr0_gd0(is);
479*4882a593Smuzhiyun set_bit(IS_ST_A5_PWR_ON, &is->state);
480*4882a593Smuzhiyun break;
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun case IHC_SET_SHOT_MARK:
483*4882a593Smuzhiyun break;
484*4882a593Smuzhiyun
485*4882a593Smuzhiyun case IHC_SET_FACE_MARK:
486*4882a593Smuzhiyun is->fd_header.count = is->i2h_cmd.args[0];
487*4882a593Smuzhiyun is->fd_header.index = is->i2h_cmd.args[1];
488*4882a593Smuzhiyun is->fd_header.offset = 0;
489*4882a593Smuzhiyun break;
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun case IHC_FRAME_DONE:
492*4882a593Smuzhiyun break;
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun case IHC_AA_DONE:
495*4882a593Smuzhiyun pr_debug("AA_DONE - %d, %d, %d\n", is->i2h_cmd.args[0],
496*4882a593Smuzhiyun is->i2h_cmd.args[1], is->i2h_cmd.args[2]);
497*4882a593Smuzhiyun break;
498*4882a593Smuzhiyun
499*4882a593Smuzhiyun case IH_REPLY_DONE:
500*4882a593Smuzhiyun pr_debug("ISR_DONE: args[0]: %#x\n", is->i2h_cmd.args[0]);
501*4882a593Smuzhiyun
502*4882a593Smuzhiyun switch (is->i2h_cmd.args[0]) {
503*4882a593Smuzhiyun case HIC_PREVIEW_STILL...HIC_CAPTURE_VIDEO:
504*4882a593Smuzhiyun /* Get CAC margin */
505*4882a593Smuzhiyun set_bit(IS_ST_CHANGE_MODE, &is->state);
506*4882a593Smuzhiyun is->isp.cac_margin_x = is->i2h_cmd.args[1];
507*4882a593Smuzhiyun is->isp.cac_margin_y = is->i2h_cmd.args[2];
508*4882a593Smuzhiyun pr_debug("CAC margin (x,y): (%d,%d)\n",
509*4882a593Smuzhiyun is->isp.cac_margin_x, is->isp.cac_margin_y);
510*4882a593Smuzhiyun break;
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun case HIC_STREAM_ON:
513*4882a593Smuzhiyun clear_bit(IS_ST_STREAM_OFF, &is->state);
514*4882a593Smuzhiyun set_bit(IS_ST_STREAM_ON, &is->state);
515*4882a593Smuzhiyun break;
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun case HIC_STREAM_OFF:
518*4882a593Smuzhiyun clear_bit(IS_ST_STREAM_ON, &is->state);
519*4882a593Smuzhiyun set_bit(IS_ST_STREAM_OFF, &is->state);
520*4882a593Smuzhiyun break;
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun case HIC_SET_PARAMETER:
523*4882a593Smuzhiyun is->config[is->config_index].p_region_index[0] = 0;
524*4882a593Smuzhiyun is->config[is->config_index].p_region_index[1] = 0;
525*4882a593Smuzhiyun set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state);
526*4882a593Smuzhiyun pr_debug("HIC_SET_PARAMETER\n");
527*4882a593Smuzhiyun break;
528*4882a593Smuzhiyun
529*4882a593Smuzhiyun case HIC_GET_PARAMETER:
530*4882a593Smuzhiyun break;
531*4882a593Smuzhiyun
532*4882a593Smuzhiyun case HIC_SET_TUNE:
533*4882a593Smuzhiyun break;
534*4882a593Smuzhiyun
535*4882a593Smuzhiyun case HIC_GET_STATUS:
536*4882a593Smuzhiyun break;
537*4882a593Smuzhiyun
538*4882a593Smuzhiyun case HIC_OPEN_SENSOR:
539*4882a593Smuzhiyun set_bit(IS_ST_OPEN_SENSOR, &is->state);
540*4882a593Smuzhiyun pr_debug("data lanes: %d, settle line: %d\n",
541*4882a593Smuzhiyun is->i2h_cmd.args[2], is->i2h_cmd.args[1]);
542*4882a593Smuzhiyun break;
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun case HIC_CLOSE_SENSOR:
545*4882a593Smuzhiyun clear_bit(IS_ST_OPEN_SENSOR, &is->state);
546*4882a593Smuzhiyun is->sensor_index = 0;
547*4882a593Smuzhiyun break;
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun case HIC_MSG_TEST:
550*4882a593Smuzhiyun pr_debug("config MSG level completed\n");
551*4882a593Smuzhiyun break;
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun case HIC_POWER_DOWN:
554*4882a593Smuzhiyun clear_bit(IS_ST_PWR_SUBIP_ON, &is->state);
555*4882a593Smuzhiyun break;
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun case HIC_GET_SET_FILE_ADDR:
558*4882a593Smuzhiyun is->setfile.base = is->i2h_cmd.args[1];
559*4882a593Smuzhiyun set_bit(IS_ST_SETFILE_LOADED, &is->state);
560*4882a593Smuzhiyun break;
561*4882a593Smuzhiyun
562*4882a593Smuzhiyun case HIC_LOAD_SET_FILE:
563*4882a593Smuzhiyun set_bit(IS_ST_SETFILE_LOADED, &is->state);
564*4882a593Smuzhiyun break;
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun break;
567*4882a593Smuzhiyun
568*4882a593Smuzhiyun case IH_REPLY_NOT_DONE:
569*4882a593Smuzhiyun pr_err("ISR_NDONE: %d: %#x, %s\n", is->i2h_cmd.args[0],
570*4882a593Smuzhiyun is->i2h_cmd.args[1],
571*4882a593Smuzhiyun fimc_is_strerr(is->i2h_cmd.args[1]));
572*4882a593Smuzhiyun
573*4882a593Smuzhiyun if (is->i2h_cmd.args[1] & IS_ERROR_TIME_OUT_FLAG)
574*4882a593Smuzhiyun pr_err("IS_ERROR_TIME_OUT\n");
575*4882a593Smuzhiyun
576*4882a593Smuzhiyun switch (is->i2h_cmd.args[1]) {
577*4882a593Smuzhiyun case IS_ERROR_SET_PARAMETER:
578*4882a593Smuzhiyun fimc_is_mem_barrier();
579*4882a593Smuzhiyun }
580*4882a593Smuzhiyun
581*4882a593Smuzhiyun switch (is->i2h_cmd.args[0]) {
582*4882a593Smuzhiyun case HIC_SET_PARAMETER:
583*4882a593Smuzhiyun is->config[is->config_index].p_region_index[0] = 0;
584*4882a593Smuzhiyun is->config[is->config_index].p_region_index[1] = 0;
585*4882a593Smuzhiyun set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state);
586*4882a593Smuzhiyun break;
587*4882a593Smuzhiyun }
588*4882a593Smuzhiyun break;
589*4882a593Smuzhiyun
590*4882a593Smuzhiyun case IHC_NOT_READY:
591*4882a593Smuzhiyun pr_err("IS control sequence error: Not Ready\n");
592*4882a593Smuzhiyun break;
593*4882a593Smuzhiyun }
594*4882a593Smuzhiyun
595*4882a593Smuzhiyun wake_up(&is->irq_queue);
596*4882a593Smuzhiyun }
597*4882a593Smuzhiyun
fimc_is_irq_handler(int irq,void * priv)598*4882a593Smuzhiyun static irqreturn_t fimc_is_irq_handler(int irq, void *priv)
599*4882a593Smuzhiyun {
600*4882a593Smuzhiyun struct fimc_is *is = priv;
601*4882a593Smuzhiyun unsigned long flags;
602*4882a593Smuzhiyun u32 status;
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun spin_lock_irqsave(&is->slock, flags);
605*4882a593Smuzhiyun status = mcuctl_read(is, MCUCTL_REG_INTSR1);
606*4882a593Smuzhiyun
607*4882a593Smuzhiyun if (status & (1UL << FIMC_IS_INT_GENERAL))
608*4882a593Smuzhiyun fimc_is_general_irq_handler(is);
609*4882a593Smuzhiyun
610*4882a593Smuzhiyun if (status & (1UL << FIMC_IS_INT_FRAME_DONE_ISP))
611*4882a593Smuzhiyun fimc_isp_irq_handler(is);
612*4882a593Smuzhiyun
613*4882a593Smuzhiyun spin_unlock_irqrestore(&is->slock, flags);
614*4882a593Smuzhiyun return IRQ_HANDLED;
615*4882a593Smuzhiyun }
616*4882a593Smuzhiyun
fimc_is_hw_open_sensor(struct fimc_is * is,struct fimc_is_sensor * sensor)617*4882a593Smuzhiyun static int fimc_is_hw_open_sensor(struct fimc_is *is,
618*4882a593Smuzhiyun struct fimc_is_sensor *sensor)
619*4882a593Smuzhiyun {
620*4882a593Smuzhiyun struct sensor_open_extended *soe = (void *)&is->is_p_region->shared;
621*4882a593Smuzhiyun
622*4882a593Smuzhiyun fimc_is_hw_wait_intmsr0_intmsd0(is);
623*4882a593Smuzhiyun
624*4882a593Smuzhiyun soe->self_calibration_mode = 1;
625*4882a593Smuzhiyun soe->actuator_type = 0;
626*4882a593Smuzhiyun soe->mipi_lane_num = 0;
627*4882a593Smuzhiyun soe->mclk = 0;
628*4882a593Smuzhiyun soe->mipi_speed = 0;
629*4882a593Smuzhiyun soe->fast_open_sensor = 0;
630*4882a593Smuzhiyun soe->i2c_sclk = 88000000;
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun fimc_is_mem_barrier();
633*4882a593Smuzhiyun
634*4882a593Smuzhiyun /*
635*4882a593Smuzhiyun * Some user space use cases hang up here without this
636*4882a593Smuzhiyun * empirically chosen delay.
637*4882a593Smuzhiyun */
638*4882a593Smuzhiyun udelay(100);
639*4882a593Smuzhiyun
640*4882a593Smuzhiyun mcuctl_write(HIC_OPEN_SENSOR, is, MCUCTL_REG_ISSR(0));
641*4882a593Smuzhiyun mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
642*4882a593Smuzhiyun mcuctl_write(sensor->drvdata->id, is, MCUCTL_REG_ISSR(2));
643*4882a593Smuzhiyun mcuctl_write(sensor->i2c_bus, is, MCUCTL_REG_ISSR(3));
644*4882a593Smuzhiyun mcuctl_write(is->is_dma_p_region, is, MCUCTL_REG_ISSR(4));
645*4882a593Smuzhiyun
646*4882a593Smuzhiyun fimc_is_hw_set_intgr0_gd0(is);
647*4882a593Smuzhiyun
648*4882a593Smuzhiyun return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1,
649*4882a593Smuzhiyun sensor->drvdata->open_timeout);
650*4882a593Smuzhiyun }
651*4882a593Smuzhiyun
652*4882a593Smuzhiyun
fimc_is_hw_initialize(struct fimc_is * is)653*4882a593Smuzhiyun int fimc_is_hw_initialize(struct fimc_is *is)
654*4882a593Smuzhiyun {
655*4882a593Smuzhiyun static const int config_ids[] = {
656*4882a593Smuzhiyun IS_SC_PREVIEW_STILL, IS_SC_PREVIEW_VIDEO,
657*4882a593Smuzhiyun IS_SC_CAPTURE_STILL, IS_SC_CAPTURE_VIDEO
658*4882a593Smuzhiyun };
659*4882a593Smuzhiyun struct device *dev = &is->pdev->dev;
660*4882a593Smuzhiyun u32 prev_id;
661*4882a593Smuzhiyun int i, ret;
662*4882a593Smuzhiyun
663*4882a593Smuzhiyun /* Sensor initialization. Only one sensor is currently supported. */
664*4882a593Smuzhiyun ret = fimc_is_hw_open_sensor(is, &is->sensor[0]);
665*4882a593Smuzhiyun if (ret < 0)
666*4882a593Smuzhiyun return ret;
667*4882a593Smuzhiyun
668*4882a593Smuzhiyun /* Get the setfile address. */
669*4882a593Smuzhiyun fimc_is_hw_get_setfile_addr(is);
670*4882a593Smuzhiyun
671*4882a593Smuzhiyun ret = fimc_is_wait_event(is, IS_ST_SETFILE_LOADED, 1,
672*4882a593Smuzhiyun FIMC_IS_CONFIG_TIMEOUT);
673*4882a593Smuzhiyun if (ret < 0) {
674*4882a593Smuzhiyun dev_err(dev, "get setfile address timed out\n");
675*4882a593Smuzhiyun return ret;
676*4882a593Smuzhiyun }
677*4882a593Smuzhiyun pr_debug("setfile.base: %#x\n", is->setfile.base);
678*4882a593Smuzhiyun
679*4882a593Smuzhiyun /* Load the setfile. */
680*4882a593Smuzhiyun fimc_is_load_setfile(is, FIMC_IS_SETFILE_6A3);
681*4882a593Smuzhiyun clear_bit(IS_ST_SETFILE_LOADED, &is->state);
682*4882a593Smuzhiyun fimc_is_hw_load_setfile(is);
683*4882a593Smuzhiyun ret = fimc_is_wait_event(is, IS_ST_SETFILE_LOADED, 1,
684*4882a593Smuzhiyun FIMC_IS_CONFIG_TIMEOUT);
685*4882a593Smuzhiyun if (ret < 0) {
686*4882a593Smuzhiyun dev_err(dev, "loading setfile timed out\n");
687*4882a593Smuzhiyun return ret;
688*4882a593Smuzhiyun }
689*4882a593Smuzhiyun
690*4882a593Smuzhiyun pr_debug("setfile: base: %#x, size: %d\n",
691*4882a593Smuzhiyun is->setfile.base, is->setfile.size);
692*4882a593Smuzhiyun pr_info("FIMC-IS Setfile info: %s\n", is->fw.setfile_info);
693*4882a593Smuzhiyun
694*4882a593Smuzhiyun /* Check magic number. */
695*4882a593Smuzhiyun if (is->is_p_region->shared[MAX_SHARED_COUNT - 1] !=
696*4882a593Smuzhiyun FIMC_IS_MAGIC_NUMBER) {
697*4882a593Smuzhiyun dev_err(dev, "magic number error!\n");
698*4882a593Smuzhiyun return -EIO;
699*4882a593Smuzhiyun }
700*4882a593Smuzhiyun
701*4882a593Smuzhiyun pr_debug("shared region: %pad, parameter region: %pad\n",
702*4882a593Smuzhiyun &is->memory.paddr + FIMC_IS_SHARED_REGION_OFFSET,
703*4882a593Smuzhiyun &is->is_dma_p_region);
704*4882a593Smuzhiyun
705*4882a593Smuzhiyun is->setfile.sub_index = 0;
706*4882a593Smuzhiyun
707*4882a593Smuzhiyun /* Stream off. */
708*4882a593Smuzhiyun fimc_is_hw_stream_off(is);
709*4882a593Smuzhiyun ret = fimc_is_wait_event(is, IS_ST_STREAM_OFF, 1,
710*4882a593Smuzhiyun FIMC_IS_CONFIG_TIMEOUT);
711*4882a593Smuzhiyun if (ret < 0) {
712*4882a593Smuzhiyun dev_err(dev, "stream off timeout\n");
713*4882a593Smuzhiyun return ret;
714*4882a593Smuzhiyun }
715*4882a593Smuzhiyun
716*4882a593Smuzhiyun /* Preserve previous mode. */
717*4882a593Smuzhiyun prev_id = is->config_index;
718*4882a593Smuzhiyun
719*4882a593Smuzhiyun /* Set initial parameter values. */
720*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(config_ids); i++) {
721*4882a593Smuzhiyun is->config_index = config_ids[i];
722*4882a593Smuzhiyun fimc_is_set_initial_params(is);
723*4882a593Smuzhiyun ret = fimc_is_itf_s_param(is, true);
724*4882a593Smuzhiyun if (ret < 0) {
725*4882a593Smuzhiyun is->config_index = prev_id;
726*4882a593Smuzhiyun return ret;
727*4882a593Smuzhiyun }
728*4882a593Smuzhiyun }
729*4882a593Smuzhiyun is->config_index = prev_id;
730*4882a593Smuzhiyun
731*4882a593Smuzhiyun set_bit(IS_ST_INIT_DONE, &is->state);
732*4882a593Smuzhiyun dev_info(dev, "initialization sequence completed (%d)\n",
733*4882a593Smuzhiyun is->config_index);
734*4882a593Smuzhiyun return 0;
735*4882a593Smuzhiyun }
736*4882a593Smuzhiyun
fimc_is_show(struct seq_file * s,void * data)737*4882a593Smuzhiyun static int fimc_is_show(struct seq_file *s, void *data)
738*4882a593Smuzhiyun {
739*4882a593Smuzhiyun struct fimc_is *is = s->private;
740*4882a593Smuzhiyun const u8 *buf = is->memory.vaddr + FIMC_IS_DEBUG_REGION_OFFSET;
741*4882a593Smuzhiyun
742*4882a593Smuzhiyun if (is->memory.vaddr == NULL) {
743*4882a593Smuzhiyun dev_err(&is->pdev->dev, "firmware memory is not initialized\n");
744*4882a593Smuzhiyun return -EIO;
745*4882a593Smuzhiyun }
746*4882a593Smuzhiyun
747*4882a593Smuzhiyun seq_printf(s, "%s\n", buf);
748*4882a593Smuzhiyun return 0;
749*4882a593Smuzhiyun }
750*4882a593Smuzhiyun
751*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(fimc_is);
752*4882a593Smuzhiyun
fimc_is_debugfs_remove(struct fimc_is * is)753*4882a593Smuzhiyun static void fimc_is_debugfs_remove(struct fimc_is *is)
754*4882a593Smuzhiyun {
755*4882a593Smuzhiyun debugfs_remove_recursive(is->debugfs_entry);
756*4882a593Smuzhiyun is->debugfs_entry = NULL;
757*4882a593Smuzhiyun }
758*4882a593Smuzhiyun
fimc_is_debugfs_create(struct fimc_is * is)759*4882a593Smuzhiyun static void fimc_is_debugfs_create(struct fimc_is *is)
760*4882a593Smuzhiyun {
761*4882a593Smuzhiyun is->debugfs_entry = debugfs_create_dir("fimc_is", NULL);
762*4882a593Smuzhiyun
763*4882a593Smuzhiyun debugfs_create_file("fw_log", S_IRUGO, is->debugfs_entry, is,
764*4882a593Smuzhiyun &fimc_is_fops);
765*4882a593Smuzhiyun }
766*4882a593Smuzhiyun
767*4882a593Smuzhiyun static int fimc_is_runtime_resume(struct device *dev);
768*4882a593Smuzhiyun static int fimc_is_runtime_suspend(struct device *dev);
769*4882a593Smuzhiyun
fimc_is_probe(struct platform_device * pdev)770*4882a593Smuzhiyun static int fimc_is_probe(struct platform_device *pdev)
771*4882a593Smuzhiyun {
772*4882a593Smuzhiyun struct device *dev = &pdev->dev;
773*4882a593Smuzhiyun struct fimc_is *is;
774*4882a593Smuzhiyun struct resource res;
775*4882a593Smuzhiyun struct device_node *node;
776*4882a593Smuzhiyun int ret;
777*4882a593Smuzhiyun
778*4882a593Smuzhiyun is = devm_kzalloc(&pdev->dev, sizeof(*is), GFP_KERNEL);
779*4882a593Smuzhiyun if (!is)
780*4882a593Smuzhiyun return -ENOMEM;
781*4882a593Smuzhiyun
782*4882a593Smuzhiyun is->pdev = pdev;
783*4882a593Smuzhiyun is->isp.pdev = pdev;
784*4882a593Smuzhiyun
785*4882a593Smuzhiyun init_waitqueue_head(&is->irq_queue);
786*4882a593Smuzhiyun spin_lock_init(&is->slock);
787*4882a593Smuzhiyun mutex_init(&is->lock);
788*4882a593Smuzhiyun
789*4882a593Smuzhiyun ret = of_address_to_resource(dev->of_node, 0, &res);
790*4882a593Smuzhiyun if (ret < 0)
791*4882a593Smuzhiyun return ret;
792*4882a593Smuzhiyun
793*4882a593Smuzhiyun is->regs = devm_ioremap_resource(dev, &res);
794*4882a593Smuzhiyun if (IS_ERR(is->regs))
795*4882a593Smuzhiyun return PTR_ERR(is->regs);
796*4882a593Smuzhiyun
797*4882a593Smuzhiyun node = of_get_child_by_name(dev->of_node, "pmu");
798*4882a593Smuzhiyun if (!node)
799*4882a593Smuzhiyun return -ENODEV;
800*4882a593Smuzhiyun
801*4882a593Smuzhiyun is->pmu_regs = of_iomap(node, 0);
802*4882a593Smuzhiyun of_node_put(node);
803*4882a593Smuzhiyun if (!is->pmu_regs)
804*4882a593Smuzhiyun return -ENOMEM;
805*4882a593Smuzhiyun
806*4882a593Smuzhiyun is->irq = irq_of_parse_and_map(dev->of_node, 0);
807*4882a593Smuzhiyun if (!is->irq) {
808*4882a593Smuzhiyun dev_err(dev, "no irq found\n");
809*4882a593Smuzhiyun ret = -EINVAL;
810*4882a593Smuzhiyun goto err_iounmap;
811*4882a593Smuzhiyun }
812*4882a593Smuzhiyun
813*4882a593Smuzhiyun ret = fimc_is_get_clocks(is);
814*4882a593Smuzhiyun if (ret < 0)
815*4882a593Smuzhiyun goto err_iounmap;
816*4882a593Smuzhiyun
817*4882a593Smuzhiyun platform_set_drvdata(pdev, is);
818*4882a593Smuzhiyun
819*4882a593Smuzhiyun ret = request_irq(is->irq, fimc_is_irq_handler, 0, dev_name(dev), is);
820*4882a593Smuzhiyun if (ret < 0) {
821*4882a593Smuzhiyun dev_err(dev, "irq request failed\n");
822*4882a593Smuzhiyun goto err_clk;
823*4882a593Smuzhiyun }
824*4882a593Smuzhiyun pm_runtime_enable(dev);
825*4882a593Smuzhiyun
826*4882a593Smuzhiyun if (!pm_runtime_enabled(dev)) {
827*4882a593Smuzhiyun ret = fimc_is_runtime_resume(dev);
828*4882a593Smuzhiyun if (ret < 0)
829*4882a593Smuzhiyun goto err_irq;
830*4882a593Smuzhiyun }
831*4882a593Smuzhiyun
832*4882a593Smuzhiyun ret = pm_runtime_resume_and_get(dev);
833*4882a593Smuzhiyun if (ret < 0)
834*4882a593Smuzhiyun goto err_pm_disable;
835*4882a593Smuzhiyun
836*4882a593Smuzhiyun vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
837*4882a593Smuzhiyun
838*4882a593Smuzhiyun ret = devm_of_platform_populate(dev);
839*4882a593Smuzhiyun if (ret < 0)
840*4882a593Smuzhiyun goto err_pm;
841*4882a593Smuzhiyun
842*4882a593Smuzhiyun /*
843*4882a593Smuzhiyun * Register FIMC-IS V4L2 subdevs to this driver. The video nodes
844*4882a593Smuzhiyun * will be created within the subdev's registered() callback.
845*4882a593Smuzhiyun */
846*4882a593Smuzhiyun ret = fimc_is_register_subdevs(is);
847*4882a593Smuzhiyun if (ret < 0)
848*4882a593Smuzhiyun goto err_pm;
849*4882a593Smuzhiyun
850*4882a593Smuzhiyun fimc_is_debugfs_create(is);
851*4882a593Smuzhiyun
852*4882a593Smuzhiyun ret = fimc_is_request_firmware(is, FIMC_IS_FW_FILENAME);
853*4882a593Smuzhiyun if (ret < 0)
854*4882a593Smuzhiyun goto err_dfs;
855*4882a593Smuzhiyun
856*4882a593Smuzhiyun pm_runtime_put_sync(dev);
857*4882a593Smuzhiyun
858*4882a593Smuzhiyun dev_dbg(dev, "FIMC-IS registered successfully\n");
859*4882a593Smuzhiyun return 0;
860*4882a593Smuzhiyun
861*4882a593Smuzhiyun err_dfs:
862*4882a593Smuzhiyun fimc_is_debugfs_remove(is);
863*4882a593Smuzhiyun fimc_is_unregister_subdevs(is);
864*4882a593Smuzhiyun err_pm:
865*4882a593Smuzhiyun pm_runtime_put_noidle(dev);
866*4882a593Smuzhiyun if (!pm_runtime_enabled(dev))
867*4882a593Smuzhiyun fimc_is_runtime_suspend(dev);
868*4882a593Smuzhiyun err_pm_disable:
869*4882a593Smuzhiyun pm_runtime_disable(dev);
870*4882a593Smuzhiyun err_irq:
871*4882a593Smuzhiyun free_irq(is->irq, is);
872*4882a593Smuzhiyun err_clk:
873*4882a593Smuzhiyun fimc_is_put_clocks(is);
874*4882a593Smuzhiyun err_iounmap:
875*4882a593Smuzhiyun iounmap(is->pmu_regs);
876*4882a593Smuzhiyun return ret;
877*4882a593Smuzhiyun }
878*4882a593Smuzhiyun
fimc_is_runtime_resume(struct device * dev)879*4882a593Smuzhiyun static int fimc_is_runtime_resume(struct device *dev)
880*4882a593Smuzhiyun {
881*4882a593Smuzhiyun struct fimc_is *is = dev_get_drvdata(dev);
882*4882a593Smuzhiyun int ret;
883*4882a593Smuzhiyun
884*4882a593Smuzhiyun ret = fimc_is_setup_clocks(is);
885*4882a593Smuzhiyun if (ret)
886*4882a593Smuzhiyun return ret;
887*4882a593Smuzhiyun
888*4882a593Smuzhiyun return fimc_is_enable_clocks(is);
889*4882a593Smuzhiyun }
890*4882a593Smuzhiyun
fimc_is_runtime_suspend(struct device * dev)891*4882a593Smuzhiyun static int fimc_is_runtime_suspend(struct device *dev)
892*4882a593Smuzhiyun {
893*4882a593Smuzhiyun struct fimc_is *is = dev_get_drvdata(dev);
894*4882a593Smuzhiyun
895*4882a593Smuzhiyun fimc_is_disable_clocks(is);
896*4882a593Smuzhiyun return 0;
897*4882a593Smuzhiyun }
898*4882a593Smuzhiyun
899*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
fimc_is_resume(struct device * dev)900*4882a593Smuzhiyun static int fimc_is_resume(struct device *dev)
901*4882a593Smuzhiyun {
902*4882a593Smuzhiyun /* TODO: */
903*4882a593Smuzhiyun return 0;
904*4882a593Smuzhiyun }
905*4882a593Smuzhiyun
fimc_is_suspend(struct device * dev)906*4882a593Smuzhiyun static int fimc_is_suspend(struct device *dev)
907*4882a593Smuzhiyun {
908*4882a593Smuzhiyun struct fimc_is *is = dev_get_drvdata(dev);
909*4882a593Smuzhiyun
910*4882a593Smuzhiyun /* TODO: */
911*4882a593Smuzhiyun if (test_bit(IS_ST_A5_PWR_ON, &is->state))
912*4882a593Smuzhiyun return -EBUSY;
913*4882a593Smuzhiyun
914*4882a593Smuzhiyun return 0;
915*4882a593Smuzhiyun }
916*4882a593Smuzhiyun #endif /* CONFIG_PM_SLEEP */
917*4882a593Smuzhiyun
fimc_is_remove(struct platform_device * pdev)918*4882a593Smuzhiyun static int fimc_is_remove(struct platform_device *pdev)
919*4882a593Smuzhiyun {
920*4882a593Smuzhiyun struct device *dev = &pdev->dev;
921*4882a593Smuzhiyun struct fimc_is *is = dev_get_drvdata(dev);
922*4882a593Smuzhiyun
923*4882a593Smuzhiyun pm_runtime_disable(dev);
924*4882a593Smuzhiyun pm_runtime_set_suspended(dev);
925*4882a593Smuzhiyun if (!pm_runtime_status_suspended(dev))
926*4882a593Smuzhiyun fimc_is_runtime_suspend(dev);
927*4882a593Smuzhiyun free_irq(is->irq, is);
928*4882a593Smuzhiyun fimc_is_unregister_subdevs(is);
929*4882a593Smuzhiyun vb2_dma_contig_clear_max_seg_size(dev);
930*4882a593Smuzhiyun fimc_is_put_clocks(is);
931*4882a593Smuzhiyun iounmap(is->pmu_regs);
932*4882a593Smuzhiyun fimc_is_debugfs_remove(is);
933*4882a593Smuzhiyun release_firmware(is->fw.f_w);
934*4882a593Smuzhiyun fimc_is_free_cpu_memory(is);
935*4882a593Smuzhiyun
936*4882a593Smuzhiyun return 0;
937*4882a593Smuzhiyun }
938*4882a593Smuzhiyun
939*4882a593Smuzhiyun static const struct of_device_id fimc_is_of_match[] = {
940*4882a593Smuzhiyun { .compatible = "samsung,exynos4212-fimc-is" },
941*4882a593Smuzhiyun { /* sentinel */ },
942*4882a593Smuzhiyun };
943*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, fimc_is_of_match);
944*4882a593Smuzhiyun
945*4882a593Smuzhiyun static const struct dev_pm_ops fimc_is_pm_ops = {
946*4882a593Smuzhiyun SET_SYSTEM_SLEEP_PM_OPS(fimc_is_suspend, fimc_is_resume)
947*4882a593Smuzhiyun SET_RUNTIME_PM_OPS(fimc_is_runtime_suspend, fimc_is_runtime_resume,
948*4882a593Smuzhiyun NULL)
949*4882a593Smuzhiyun };
950*4882a593Smuzhiyun
951*4882a593Smuzhiyun static struct platform_driver fimc_is_driver = {
952*4882a593Smuzhiyun .probe = fimc_is_probe,
953*4882a593Smuzhiyun .remove = fimc_is_remove,
954*4882a593Smuzhiyun .driver = {
955*4882a593Smuzhiyun .of_match_table = fimc_is_of_match,
956*4882a593Smuzhiyun .name = FIMC_IS_DRV_NAME,
957*4882a593Smuzhiyun .pm = &fimc_is_pm_ops,
958*4882a593Smuzhiyun }
959*4882a593Smuzhiyun };
960*4882a593Smuzhiyun
fimc_is_module_init(void)961*4882a593Smuzhiyun static int fimc_is_module_init(void)
962*4882a593Smuzhiyun {
963*4882a593Smuzhiyun int ret;
964*4882a593Smuzhiyun
965*4882a593Smuzhiyun ret = fimc_is_register_i2c_driver();
966*4882a593Smuzhiyun if (ret < 0)
967*4882a593Smuzhiyun return ret;
968*4882a593Smuzhiyun
969*4882a593Smuzhiyun ret = platform_driver_register(&fimc_is_driver);
970*4882a593Smuzhiyun
971*4882a593Smuzhiyun if (ret < 0)
972*4882a593Smuzhiyun fimc_is_unregister_i2c_driver();
973*4882a593Smuzhiyun
974*4882a593Smuzhiyun return ret;
975*4882a593Smuzhiyun }
976*4882a593Smuzhiyun
fimc_is_module_exit(void)977*4882a593Smuzhiyun static void fimc_is_module_exit(void)
978*4882a593Smuzhiyun {
979*4882a593Smuzhiyun fimc_is_unregister_i2c_driver();
980*4882a593Smuzhiyun platform_driver_unregister(&fimc_is_driver);
981*4882a593Smuzhiyun }
982*4882a593Smuzhiyun
983*4882a593Smuzhiyun module_init(fimc_is_module_init);
984*4882a593Smuzhiyun module_exit(fimc_is_module_exit);
985*4882a593Smuzhiyun
986*4882a593Smuzhiyun MODULE_ALIAS("platform:" FIMC_IS_DRV_NAME);
987*4882a593Smuzhiyun MODULE_AUTHOR("Younghwan Joo <yhwan.joo@samsung.com>");
988*4882a593Smuzhiyun MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
989*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
990