xref: /openbmc/linux/drivers/media/i2c/rdacm21.c (revision 12d21fc2)
1a59f853bSJacopo Mondi // SPDX-License-Identifier: GPL-2.0+
2a59f853bSJacopo Mondi /*
3a59f853bSJacopo Mondi  * IMI RDACM21 GMSL Camera Driver
4a59f853bSJacopo Mondi  *
5a59f853bSJacopo Mondi  * Copyright (C) 2017-2020 Jacopo Mondi
6a59f853bSJacopo Mondi  * Copyright (C) 2017-2019 Kieran Bingham
7a59f853bSJacopo Mondi  * Copyright (C) 2017-2019 Laurent Pinchart
8a59f853bSJacopo Mondi  * Copyright (C) 2017-2019 Niklas Söderlund
9a59f853bSJacopo Mondi  * Copyright (C) 2016 Renesas Electronics Corporation
10a59f853bSJacopo Mondi  * Copyright (C) 2015 Cogent Embedded, Inc.
11a59f853bSJacopo Mondi  */
12a59f853bSJacopo Mondi 
13a59f853bSJacopo Mondi #include <linux/delay.h>
14a59f853bSJacopo Mondi #include <linux/fwnode.h>
15a59f853bSJacopo Mondi #include <linux/init.h>
16a59f853bSJacopo Mondi #include <linux/i2c.h>
17a59f853bSJacopo Mondi #include <linux/module.h>
18a59f853bSJacopo Mondi #include <linux/slab.h>
19a59f853bSJacopo Mondi #include <linux/videodev2.h>
20a59f853bSJacopo Mondi 
21a59f853bSJacopo Mondi #include <media/v4l2-async.h>
22a59f853bSJacopo Mondi #include <media/v4l2-ctrls.h>
23a59f853bSJacopo Mondi #include <media/v4l2-subdev.h>
24a59f853bSJacopo Mondi #include "max9271.h"
25a59f853bSJacopo Mondi 
26a59f853bSJacopo Mondi #define MAX9271_RESET_CYCLES		10
27a59f853bSJacopo Mondi 
28a59f853bSJacopo Mondi #define OV490_I2C_ADDRESS		0x24
29a59f853bSJacopo Mondi 
30a59f853bSJacopo Mondi #define OV490_PAGE_HIGH_REG		0xfffd
31a59f853bSJacopo Mondi #define OV490_PAGE_LOW_REG		0xfffe
32a59f853bSJacopo Mondi 
33a59f853bSJacopo Mondi /*
34a59f853bSJacopo Mondi  * The SCCB slave handling is undocumented; the registers naming scheme is
35a59f853bSJacopo Mondi  * totally arbitrary.
36a59f853bSJacopo Mondi  */
37a59f853bSJacopo Mondi #define OV490_SCCB_SLAVE_WRITE		0x00
38a59f853bSJacopo Mondi #define OV490_SCCB_SLAVE_READ		0x01
39a59f853bSJacopo Mondi #define OV490_SCCB_SLAVE0_DIR		0x80195000
40a59f853bSJacopo Mondi #define OV490_SCCB_SLAVE0_ADDR_HIGH	0x80195001
41a59f853bSJacopo Mondi #define OV490_SCCB_SLAVE0_ADDR_LOW	0x80195002
42a59f853bSJacopo Mondi 
43a59f853bSJacopo Mondi #define OV490_DVP_CTRL3			0x80286009
44a59f853bSJacopo Mondi 
45a59f853bSJacopo Mondi #define OV490_ODS_CTRL_FRAME_OUTPUT_EN	0x0c
46a59f853bSJacopo Mondi #define OV490_ODS_CTRL			0x8029d000
47a59f853bSJacopo Mondi 
48a59f853bSJacopo Mondi #define OV490_HOST_CMD			0x808000c0
49a59f853bSJacopo Mondi #define OV490_HOST_CMD_TRIGGER		0xc1
50a59f853bSJacopo Mondi 
51a59f853bSJacopo Mondi #define OV490_ID_VAL			0x0490
52a59f853bSJacopo Mondi #define OV490_ID(_p, _v)		((((_p) & 0xff) << 8) | ((_v) & 0xff))
53a59f853bSJacopo Mondi #define OV490_PID			0x8080300a
54a59f853bSJacopo Mondi #define OV490_VER			0x8080300b
55a59f853bSJacopo Mondi #define OV490_PID_TIMEOUT		20
56a59f853bSJacopo Mondi #define OV490_OUTPUT_EN_TIMEOUT		300
57a59f853bSJacopo Mondi 
58a59f853bSJacopo Mondi #define OV490_GPIO0			BIT(0)
59a59f853bSJacopo Mondi #define OV490_SPWDN0			BIT(0)
60a59f853bSJacopo Mondi #define OV490_GPIO_SEL0			0x80800050
61a59f853bSJacopo Mondi #define OV490_GPIO_SEL1			0x80800051
62a59f853bSJacopo Mondi #define OV490_GPIO_DIRECTION0		0x80800054
63a59f853bSJacopo Mondi #define OV490_GPIO_DIRECTION1		0x80800055
64a59f853bSJacopo Mondi #define OV490_GPIO_OUTPUT_VALUE0	0x80800058
65a59f853bSJacopo Mondi #define OV490_GPIO_OUTPUT_VALUE1	0x80800059
66a59f853bSJacopo Mondi 
67a59f853bSJacopo Mondi #define OV490_ISP_HSIZE_LOW		0x80820060
68a59f853bSJacopo Mondi #define OV490_ISP_HSIZE_HIGH		0x80820061
69a59f853bSJacopo Mondi #define OV490_ISP_VSIZE_LOW		0x80820062
70a59f853bSJacopo Mondi #define OV490_ISP_VSIZE_HIGH		0x80820063
71a59f853bSJacopo Mondi 
722b821698SJacopo Mondi #define OV10640_PID_TIMEOUT		20
73a59f853bSJacopo Mondi #define OV10640_ID_HIGH			0xa6
74a59f853bSJacopo Mondi #define OV10640_CHIP_ID			0x300a
75a59f853bSJacopo Mondi #define OV10640_PIXEL_RATE		55000000
76a59f853bSJacopo Mondi 
77a59f853bSJacopo Mondi struct rdacm21_device {
78a59f853bSJacopo Mondi 	struct device			*dev;
79a59f853bSJacopo Mondi 	struct max9271_device		serializer;
80a59f853bSJacopo Mondi 	struct i2c_client		*isp;
81a59f853bSJacopo Mondi 	struct v4l2_subdev		sd;
82a59f853bSJacopo Mondi 	struct media_pad		pad;
83a59f853bSJacopo Mondi 	struct v4l2_mbus_framefmt	fmt;
84a59f853bSJacopo Mondi 	struct v4l2_ctrl_handler	ctrls;
85a59f853bSJacopo Mondi 	u32				addrs[2];
86a59f853bSJacopo Mondi 	u16				last_page;
87a59f853bSJacopo Mondi };
88a59f853bSJacopo Mondi 
sd_to_rdacm21(struct v4l2_subdev * sd)89a59f853bSJacopo Mondi static inline struct rdacm21_device *sd_to_rdacm21(struct v4l2_subdev *sd)
90a59f853bSJacopo Mondi {
91a59f853bSJacopo Mondi 	return container_of(sd, struct rdacm21_device, sd);
92a59f853bSJacopo Mondi }
93a59f853bSJacopo Mondi 
94a59f853bSJacopo Mondi static const struct ov490_reg {
95a59f853bSJacopo Mondi 	u16 reg;
96a59f853bSJacopo Mondi 	u8 val;
97a59f853bSJacopo Mondi } ov490_regs_wizard[] = {
98a59f853bSJacopo Mondi 	{0xfffd, 0x80},
99a59f853bSJacopo Mondi 	{0xfffe, 0x82},
100a59f853bSJacopo Mondi 	{0x0071, 0x11},
101a59f853bSJacopo Mondi 	{0x0075, 0x11},
102a59f853bSJacopo Mondi 	{0xfffe, 0x29},
103a59f853bSJacopo Mondi 	{0x6010, 0x01},
104a59f853bSJacopo Mondi 	/*
105a59f853bSJacopo Mondi 	 * OV490 EMB line disable in YUV and RAW data,
106a59f853bSJacopo Mondi 	 * NOTE: EMB line is still used in ISP and sensor
107a59f853bSJacopo Mondi 	 */
108a59f853bSJacopo Mondi 	{0xe000, 0x14},
109a59f853bSJacopo Mondi 	{0xfffe, 0x28},
110a59f853bSJacopo Mondi 	{0x6000, 0x04},
111a59f853bSJacopo Mondi 	{0x6004, 0x00},
112a59f853bSJacopo Mondi 	/*
113a59f853bSJacopo Mondi 	 * PCLK polarity - useless due to silicon bug.
114a59f853bSJacopo Mondi 	 * Use 0x808000bb register instead.
115a59f853bSJacopo Mondi 	 */
116a59f853bSJacopo Mondi 	{0x6008, 0x00},
117a59f853bSJacopo Mondi 	{0xfffe, 0x80},
118a59f853bSJacopo Mondi 	{0x0091, 0x00},
119a59f853bSJacopo Mondi 	/* bit[3]=0 - PCLK polarity workaround. */
120a59f853bSJacopo Mondi 	{0x00bb, 0x1d},
121a59f853bSJacopo Mondi 	/* Ov490 FSIN: app_fsin_from_fsync */
122a59f853bSJacopo Mondi 	{0xfffe, 0x85},
123a59f853bSJacopo Mondi 	{0x0008, 0x00},
124a59f853bSJacopo Mondi 	{0x0009, 0x01},
125a59f853bSJacopo Mondi 	/* FSIN0 source. */
126a59f853bSJacopo Mondi 	{0x000A, 0x05},
127a59f853bSJacopo Mondi 	{0x000B, 0x00},
128a59f853bSJacopo Mondi 	/* FSIN0 delay. */
129a59f853bSJacopo Mondi 	{0x0030, 0x02},
130a59f853bSJacopo Mondi 	{0x0031, 0x00},
131a59f853bSJacopo Mondi 	{0x0032, 0x00},
132a59f853bSJacopo Mondi 	{0x0033, 0x00},
133a59f853bSJacopo Mondi 	/* FSIN1 delay. */
134a59f853bSJacopo Mondi 	{0x0038, 0x02},
135a59f853bSJacopo Mondi 	{0x0039, 0x00},
136a59f853bSJacopo Mondi 	{0x003A, 0x00},
137a59f853bSJacopo Mondi 	{0x003B, 0x00},
138a59f853bSJacopo Mondi 	/* FSIN0 length. */
139a59f853bSJacopo Mondi 	{0x0070, 0x2C},
140a59f853bSJacopo Mondi 	{0x0071, 0x01},
141a59f853bSJacopo Mondi 	{0x0072, 0x00},
142a59f853bSJacopo Mondi 	{0x0073, 0x00},
143a59f853bSJacopo Mondi 	/* FSIN1 length. */
144a59f853bSJacopo Mondi 	{0x0074, 0x64},
145a59f853bSJacopo Mondi 	{0x0075, 0x00},
146a59f853bSJacopo Mondi 	{0x0076, 0x00},
147a59f853bSJacopo Mondi 	{0x0077, 0x00},
148a59f853bSJacopo Mondi 	{0x0000, 0x14},
149a59f853bSJacopo Mondi 	{0x0001, 0x00},
150a59f853bSJacopo Mondi 	{0x0002, 0x00},
151a59f853bSJacopo Mondi 	{0x0003, 0x00},
152a59f853bSJacopo Mondi 	/*
153a59f853bSJacopo Mondi 	 * Load fsin0,load fsin1,load other,
154a59f853bSJacopo Mondi 	 * It will be cleared automatically.
155a59f853bSJacopo Mondi 	 */
156a59f853bSJacopo Mondi 	{0x0004, 0x32},
157a59f853bSJacopo Mondi 	{0x0005, 0x00},
158a59f853bSJacopo Mondi 	{0x0006, 0x00},
159a59f853bSJacopo Mondi 	{0x0007, 0x00},
160a59f853bSJacopo Mondi 	{0xfffe, 0x80},
161a59f853bSJacopo Mondi 	/* Sensor FSIN. */
162a59f853bSJacopo Mondi 	{0x0081, 0x00},
163a59f853bSJacopo Mondi 	/* ov10640 FSIN enable */
164a59f853bSJacopo Mondi 	{0xfffe, 0x19},
165a59f853bSJacopo Mondi 	{0x5000, 0x00},
166a59f853bSJacopo Mondi 	{0x5001, 0x30},
167a59f853bSJacopo Mondi 	{0x5002, 0x8c},
168a59f853bSJacopo Mondi 	{0x5003, 0xb2},
169a59f853bSJacopo Mondi 	{0xfffe, 0x80},
170a59f853bSJacopo Mondi 	{0x00c0, 0xc1},
171a59f853bSJacopo Mondi 	/* ov10640 HFLIP=1 by default */
172a59f853bSJacopo Mondi 	{0xfffe, 0x19},
173a59f853bSJacopo Mondi 	{0x5000, 0x01},
174a59f853bSJacopo Mondi 	{0x5001, 0x00},
175a59f853bSJacopo Mondi 	{0xfffe, 0x80},
176a59f853bSJacopo Mondi 	{0x00c0, 0xdc},
177a59f853bSJacopo Mondi };
178a59f853bSJacopo Mondi 
ov490_read(struct rdacm21_device * dev,u16 reg,u8 * val)179a59f853bSJacopo Mondi static int ov490_read(struct rdacm21_device *dev, u16 reg, u8 *val)
180a59f853bSJacopo Mondi {
181a59f853bSJacopo Mondi 	u8 buf[2] = { reg >> 8, reg };
182a59f853bSJacopo Mondi 	int ret;
183a59f853bSJacopo Mondi 
184a59f853bSJacopo Mondi 	ret = i2c_master_send(dev->isp, buf, 2);
185a59f853bSJacopo Mondi 	if (ret == 2)
186a59f853bSJacopo Mondi 		ret = i2c_master_recv(dev->isp, val, 1);
187a59f853bSJacopo Mondi 
188a59f853bSJacopo Mondi 	if (ret < 0) {
189a59f853bSJacopo Mondi 		dev_dbg(dev->dev, "%s: register 0x%04x read failed (%d)\n",
190a59f853bSJacopo Mondi 			__func__, reg, ret);
191a59f853bSJacopo Mondi 		return ret;
192a59f853bSJacopo Mondi 	}
193a59f853bSJacopo Mondi 
194a59f853bSJacopo Mondi 	return 0;
195a59f853bSJacopo Mondi }
196a59f853bSJacopo Mondi 
ov490_write(struct rdacm21_device * dev,u16 reg,u8 val)197a59f853bSJacopo Mondi static int ov490_write(struct rdacm21_device *dev, u16 reg, u8 val)
198a59f853bSJacopo Mondi {
199a59f853bSJacopo Mondi 	u8 buf[3] = { reg >> 8, reg, val };
200a59f853bSJacopo Mondi 	int ret;
201a59f853bSJacopo Mondi 
202a59f853bSJacopo Mondi 	ret = i2c_master_send(dev->isp, buf, 3);
203a59f853bSJacopo Mondi 	if (ret < 0) {
204a59f853bSJacopo Mondi 		dev_err(dev->dev, "%s: register 0x%04x write failed (%d)\n",
205a59f853bSJacopo Mondi 			__func__, reg, ret);
206a59f853bSJacopo Mondi 		return ret;
207a59f853bSJacopo Mondi 	}
208a59f853bSJacopo Mondi 
209a59f853bSJacopo Mondi 	return 0;
210a59f853bSJacopo Mondi }
211a59f853bSJacopo Mondi 
ov490_set_page(struct rdacm21_device * dev,u16 page)212a59f853bSJacopo Mondi static int ov490_set_page(struct rdacm21_device *dev, u16 page)
213a59f853bSJacopo Mondi {
214a59f853bSJacopo Mondi 	u8 page_high = page >> 8;
215a59f853bSJacopo Mondi 	u8 page_low = page;
216a59f853bSJacopo Mondi 	int ret;
217a59f853bSJacopo Mondi 
218a59f853bSJacopo Mondi 	if (page == dev->last_page)
219a59f853bSJacopo Mondi 		return 0;
220a59f853bSJacopo Mondi 
221a59f853bSJacopo Mondi 	if (page_high != (dev->last_page >> 8)) {
222a59f853bSJacopo Mondi 		ret = ov490_write(dev, OV490_PAGE_HIGH_REG, page_high);
223a59f853bSJacopo Mondi 		if (ret)
224a59f853bSJacopo Mondi 			return ret;
225a59f853bSJacopo Mondi 	}
226a59f853bSJacopo Mondi 
227a59f853bSJacopo Mondi 	if (page_low != (u8)dev->last_page) {
228a59f853bSJacopo Mondi 		ret = ov490_write(dev, OV490_PAGE_LOW_REG, page_low);
229a59f853bSJacopo Mondi 		if (ret)
230a59f853bSJacopo Mondi 			return ret;
231a59f853bSJacopo Mondi 	}
232a59f853bSJacopo Mondi 
233a59f853bSJacopo Mondi 	dev->last_page = page;
234a59f853bSJacopo Mondi 	usleep_range(100, 150);
235a59f853bSJacopo Mondi 
236a59f853bSJacopo Mondi 	return 0;
237a59f853bSJacopo Mondi }
238a59f853bSJacopo Mondi 
ov490_read_reg(struct rdacm21_device * dev,u32 reg,u8 * val)239a59f853bSJacopo Mondi static int ov490_read_reg(struct rdacm21_device *dev, u32 reg, u8 *val)
240a59f853bSJacopo Mondi {
241a59f853bSJacopo Mondi 	int ret;
242a59f853bSJacopo Mondi 
243a59f853bSJacopo Mondi 	ret = ov490_set_page(dev, reg >> 16);
244a59f853bSJacopo Mondi 	if (ret)
245a59f853bSJacopo Mondi 		return ret;
246a59f853bSJacopo Mondi 
247a59f853bSJacopo Mondi 	ret = ov490_read(dev, (u16)reg, val);
248a59f853bSJacopo Mondi 	if (ret)
249a59f853bSJacopo Mondi 		return ret;
250a59f853bSJacopo Mondi 
251a59f853bSJacopo Mondi 	dev_dbg(dev->dev, "%s: 0x%08x = 0x%02x\n", __func__, reg, *val);
252a59f853bSJacopo Mondi 
253a59f853bSJacopo Mondi 	return 0;
254a59f853bSJacopo Mondi }
255a59f853bSJacopo Mondi 
ov490_write_reg(struct rdacm21_device * dev,u32 reg,u8 val)256a59f853bSJacopo Mondi static int ov490_write_reg(struct rdacm21_device *dev, u32 reg, u8 val)
257a59f853bSJacopo Mondi {
258a59f853bSJacopo Mondi 	int ret;
259a59f853bSJacopo Mondi 
260a59f853bSJacopo Mondi 	ret = ov490_set_page(dev, reg >> 16);
261a59f853bSJacopo Mondi 	if (ret)
262a59f853bSJacopo Mondi 		return ret;
263a59f853bSJacopo Mondi 
264a59f853bSJacopo Mondi 	ret = ov490_write(dev, (u16)reg, val);
265a59f853bSJacopo Mondi 	if (ret)
266a59f853bSJacopo Mondi 		return ret;
267a59f853bSJacopo Mondi 
268a59f853bSJacopo Mondi 	dev_dbg(dev->dev, "%s: 0x%08x = 0x%02x\n", __func__, reg, val);
269a59f853bSJacopo Mondi 
270a59f853bSJacopo Mondi 	return 0;
271a59f853bSJacopo Mondi }
272a59f853bSJacopo Mondi 
rdacm21_s_stream(struct v4l2_subdev * sd,int enable)273a59f853bSJacopo Mondi static int rdacm21_s_stream(struct v4l2_subdev *sd, int enable)
274a59f853bSJacopo Mondi {
275a59f853bSJacopo Mondi 	struct rdacm21_device *dev = sd_to_rdacm21(sd);
276a59f853bSJacopo Mondi 
277a59f853bSJacopo Mondi 	/*
278a59f853bSJacopo Mondi 	 * Enable serial link now that the ISP provides a valid pixel clock
279a59f853bSJacopo Mondi 	 * to start serializing video data on the GMSL link.
280a59f853bSJacopo Mondi 	 */
281a59f853bSJacopo Mondi 	return max9271_set_serial_link(&dev->serializer, enable);
282a59f853bSJacopo Mondi }
283a59f853bSJacopo Mondi 
rdacm21_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)284a59f853bSJacopo Mondi static int rdacm21_enum_mbus_code(struct v4l2_subdev *sd,
2850d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
286a59f853bSJacopo Mondi 				  struct v4l2_subdev_mbus_code_enum *code)
287a59f853bSJacopo Mondi {
288a59f853bSJacopo Mondi 	if (code->pad || code->index > 0)
289a59f853bSJacopo Mondi 		return -EINVAL;
290a59f853bSJacopo Mondi 
291a59f853bSJacopo Mondi 	code->code = MEDIA_BUS_FMT_YUYV8_1X16;
292a59f853bSJacopo Mondi 
293a59f853bSJacopo Mondi 	return 0;
294a59f853bSJacopo Mondi }
295a59f853bSJacopo Mondi 
rdacm21_get_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)296a59f853bSJacopo Mondi static int rdacm21_get_fmt(struct v4l2_subdev *sd,
2970d346d2aSTomi Valkeinen 			   struct v4l2_subdev_state *sd_state,
298a59f853bSJacopo Mondi 			   struct v4l2_subdev_format *format)
299a59f853bSJacopo Mondi {
300a59f853bSJacopo Mondi 	struct v4l2_mbus_framefmt *mf = &format->format;
301a59f853bSJacopo Mondi 	struct rdacm21_device *dev = sd_to_rdacm21(sd);
302a59f853bSJacopo Mondi 
303a59f853bSJacopo Mondi 	if (format->pad)
304a59f853bSJacopo Mondi 		return -EINVAL;
305a59f853bSJacopo Mondi 
306a59f853bSJacopo Mondi 	mf->width		= dev->fmt.width;
307a59f853bSJacopo Mondi 	mf->height		= dev->fmt.height;
308a59f853bSJacopo Mondi 	mf->code		= MEDIA_BUS_FMT_YUYV8_1X16;
309a59f853bSJacopo Mondi 	mf->colorspace		= V4L2_COLORSPACE_SRGB;
310a59f853bSJacopo Mondi 	mf->field		= V4L2_FIELD_NONE;
311a59f853bSJacopo Mondi 	mf->ycbcr_enc		= V4L2_YCBCR_ENC_601;
312a59f853bSJacopo Mondi 	mf->quantization	= V4L2_QUANTIZATION_FULL_RANGE;
313a59f853bSJacopo Mondi 	mf->xfer_func		= V4L2_XFER_FUNC_NONE;
314a59f853bSJacopo Mondi 
315a59f853bSJacopo Mondi 	return 0;
316a59f853bSJacopo Mondi }
317a59f853bSJacopo Mondi 
318a59f853bSJacopo Mondi static const struct v4l2_subdev_video_ops rdacm21_video_ops = {
319a59f853bSJacopo Mondi 	.s_stream	= rdacm21_s_stream,
320a59f853bSJacopo Mondi };
321a59f853bSJacopo Mondi 
322a59f853bSJacopo Mondi static const struct v4l2_subdev_pad_ops rdacm21_subdev_pad_ops = {
323a59f853bSJacopo Mondi 	.enum_mbus_code = rdacm21_enum_mbus_code,
324a59f853bSJacopo Mondi 	.get_fmt	= rdacm21_get_fmt,
325a59f853bSJacopo Mondi 	.set_fmt	= rdacm21_get_fmt,
326a59f853bSJacopo Mondi };
327a59f853bSJacopo Mondi 
328a59f853bSJacopo Mondi static const struct v4l2_subdev_ops rdacm21_subdev_ops = {
329a59f853bSJacopo Mondi 	.video		= &rdacm21_video_ops,
330a59f853bSJacopo Mondi 	.pad		= &rdacm21_subdev_pad_ops,
331a59f853bSJacopo Mondi };
332a59f853bSJacopo Mondi 
ov10640_power_up(struct rdacm21_device * dev)3332b821698SJacopo Mondi static void ov10640_power_up(struct rdacm21_device *dev)
334a59f853bSJacopo Mondi {
335ff75332bSJacopo Mondi 	/* Enable GPIO0#0 (reset) and GPIO1#0 (pwdn) as output lines. */
336a59f853bSJacopo Mondi 	ov490_write_reg(dev, OV490_GPIO_SEL0, OV490_GPIO0);
337a59f853bSJacopo Mondi 	ov490_write_reg(dev, OV490_GPIO_SEL1, OV490_SPWDN0);
338a59f853bSJacopo Mondi 	ov490_write_reg(dev, OV490_GPIO_DIRECTION0, OV490_GPIO0);
339a59f853bSJacopo Mondi 	ov490_write_reg(dev, OV490_GPIO_DIRECTION1, OV490_SPWDN0);
340ff75332bSJacopo Mondi 
341ff75332bSJacopo Mondi 	/* Power up OV10640 and then reset it. */
342ff75332bSJacopo Mondi 	ov490_write_reg(dev, OV490_GPIO_OUTPUT_VALUE1, OV490_SPWDN0);
343ff75332bSJacopo Mondi 	usleep_range(1500, 3000);
344ff75332bSJacopo Mondi 
345ff75332bSJacopo Mondi 	ov490_write_reg(dev, OV490_GPIO_OUTPUT_VALUE0, 0x00);
346ff75332bSJacopo Mondi 	usleep_range(1500, 3000);
347a59f853bSJacopo Mondi 	ov490_write_reg(dev, OV490_GPIO_OUTPUT_VALUE0, OV490_GPIO0);
348a59f853bSJacopo Mondi 	usleep_range(3000, 5000);
3492b821698SJacopo Mondi }
3502b821698SJacopo Mondi 
ov10640_check_id(struct rdacm21_device * dev)3512b821698SJacopo Mondi static int ov10640_check_id(struct rdacm21_device *dev)
3522b821698SJacopo Mondi {
3532b821698SJacopo Mondi 	unsigned int i;
354*33c7ae8fSJacopo Mondi 	u8 val = 0;
355a59f853bSJacopo Mondi 
356a59f853bSJacopo Mondi 	/* Read OV10640 ID to test communications. */
3572b821698SJacopo Mondi 	for (i = 0; i < OV10640_PID_TIMEOUT; ++i) {
3582b821698SJacopo Mondi 		ov490_write_reg(dev, OV490_SCCB_SLAVE0_DIR,
3592b821698SJacopo Mondi 				OV490_SCCB_SLAVE_READ);
3602b821698SJacopo Mondi 		ov490_write_reg(dev, OV490_SCCB_SLAVE0_ADDR_HIGH,
3612b821698SJacopo Mondi 				OV10640_CHIP_ID >> 8);
3622b821698SJacopo Mondi 		ov490_write_reg(dev, OV490_SCCB_SLAVE0_ADDR_LOW,
3632b821698SJacopo Mondi 				OV10640_CHIP_ID & 0xff);
364a59f853bSJacopo Mondi 
3652b821698SJacopo Mondi 		/*
3662b821698SJacopo Mondi 		 * Trigger SCCB slave transaction and give it some time
3672b821698SJacopo Mondi 		 * to complete.
3682b821698SJacopo Mondi 		 */
369a59f853bSJacopo Mondi 		ov490_write_reg(dev, OV490_HOST_CMD, OV490_HOST_CMD_TRIGGER);
370a59f853bSJacopo Mondi 		usleep_range(1000, 1500);
371a59f853bSJacopo Mondi 
372a59f853bSJacopo Mondi 		ov490_read_reg(dev, OV490_SCCB_SLAVE0_DIR, &val);
3732b821698SJacopo Mondi 		if (val == OV10640_ID_HIGH)
3742b821698SJacopo Mondi 			break;
3752b821698SJacopo Mondi 		usleep_range(1000, 1500);
3762b821698SJacopo Mondi 	}
3772b821698SJacopo Mondi 	if (i == OV10640_PID_TIMEOUT) {
378a59f853bSJacopo Mondi 		dev_err(dev->dev, "OV10640 ID mismatch: (0x%02x)\n", val);
379a59f853bSJacopo Mondi 		return -ENODEV;
380a59f853bSJacopo Mondi 	}
381a59f853bSJacopo Mondi 
382a59f853bSJacopo Mondi 	dev_dbg(dev->dev, "OV10640 ID = 0x%2x\n", val);
383a59f853bSJacopo Mondi 
384a59f853bSJacopo Mondi 	return 0;
385a59f853bSJacopo Mondi }
386a59f853bSJacopo Mondi 
ov490_initialize(struct rdacm21_device * dev)387a59f853bSJacopo Mondi static int ov490_initialize(struct rdacm21_device *dev)
388a59f853bSJacopo Mondi {
389a59f853bSJacopo Mondi 	u8 pid, ver, val;
390a59f853bSJacopo Mondi 	unsigned int i;
391a59f853bSJacopo Mondi 	int ret;
392a59f853bSJacopo Mondi 
3932b821698SJacopo Mondi 	ov10640_power_up(dev);
3942b821698SJacopo Mondi 
395a59f853bSJacopo Mondi 	/*
396a59f853bSJacopo Mondi 	 * Read OV490 Id to test communications. Give it up to 40msec to
397a59f853bSJacopo Mondi 	 * exit from reset.
398a59f853bSJacopo Mondi 	 */
399a59f853bSJacopo Mondi 	for (i = 0; i < OV490_PID_TIMEOUT; ++i) {
400a59f853bSJacopo Mondi 		ret = ov490_read_reg(dev, OV490_PID, &pid);
401a59f853bSJacopo Mondi 		if (ret == 0)
402a59f853bSJacopo Mondi 			break;
403a59f853bSJacopo Mondi 		usleep_range(1000, 2000);
404a59f853bSJacopo Mondi 	}
405a59f853bSJacopo Mondi 	if (i == OV490_PID_TIMEOUT) {
406a59f853bSJacopo Mondi 		dev_err(dev->dev, "OV490 PID read failed (%d)\n", ret);
407a59f853bSJacopo Mondi 		return ret;
408a59f853bSJacopo Mondi 	}
409a59f853bSJacopo Mondi 
410a59f853bSJacopo Mondi 	ret = ov490_read_reg(dev, OV490_VER, &ver);
411a59f853bSJacopo Mondi 	if (ret < 0)
412a59f853bSJacopo Mondi 		return ret;
413a59f853bSJacopo Mondi 
414a59f853bSJacopo Mondi 	if (OV490_ID(pid, ver) != OV490_ID_VAL) {
415a59f853bSJacopo Mondi 		dev_err(dev->dev, "OV490 ID mismatch (0x%04x)\n",
416a59f853bSJacopo Mondi 			OV490_ID(pid, ver));
417a59f853bSJacopo Mondi 		return -ENODEV;
418a59f853bSJacopo Mondi 	}
419a59f853bSJacopo Mondi 
420a59f853bSJacopo Mondi 	/* Wait for firmware boot by reading streamon status. */
421a59f853bSJacopo Mondi 	for (i = 0; i < OV490_OUTPUT_EN_TIMEOUT; ++i) {
422a59f853bSJacopo Mondi 		ov490_read_reg(dev, OV490_ODS_CTRL, &val);
423a59f853bSJacopo Mondi 		if (val == OV490_ODS_CTRL_FRAME_OUTPUT_EN)
424a59f853bSJacopo Mondi 			break;
425a59f853bSJacopo Mondi 		usleep_range(1000, 2000);
426a59f853bSJacopo Mondi 	}
427a59f853bSJacopo Mondi 	if (i == OV490_OUTPUT_EN_TIMEOUT) {
428a59f853bSJacopo Mondi 		dev_err(dev->dev, "Timeout waiting for firmware boot\n");
429a59f853bSJacopo Mondi 		return -ENODEV;
430a59f853bSJacopo Mondi 	}
431a59f853bSJacopo Mondi 
4322b821698SJacopo Mondi 	ret = ov10640_check_id(dev);
433a59f853bSJacopo Mondi 	if (ret)
434a59f853bSJacopo Mondi 		return ret;
435a59f853bSJacopo Mondi 
436a59f853bSJacopo Mondi 	/* Program OV490 with register-value table. */
437a59f853bSJacopo Mondi 	for (i = 0; i < ARRAY_SIZE(ov490_regs_wizard); ++i) {
438a59f853bSJacopo Mondi 		ret = ov490_write(dev, ov490_regs_wizard[i].reg,
439a59f853bSJacopo Mondi 				  ov490_regs_wizard[i].val);
440a59f853bSJacopo Mondi 		if (ret < 0) {
441a59f853bSJacopo Mondi 			dev_err(dev->dev,
442a59f853bSJacopo Mondi 				"%s: register %u (0x%04x) write failed (%d)\n",
443a59f853bSJacopo Mondi 				__func__, i, ov490_regs_wizard[i].reg, ret);
444a59f853bSJacopo Mondi 
445a59f853bSJacopo Mondi 			return -EIO;
446a59f853bSJacopo Mondi 		}
447a59f853bSJacopo Mondi 
448a59f853bSJacopo Mondi 		usleep_range(100, 150);
449a59f853bSJacopo Mondi 	}
450a59f853bSJacopo Mondi 
451a59f853bSJacopo Mondi 	/*
452a59f853bSJacopo Mondi 	 * The ISP is programmed with the content of a serial flash memory.
453a59f853bSJacopo Mondi 	 * Read the firmware configuration to reflect it through the V4L2 APIs.
454a59f853bSJacopo Mondi 	 */
455a59f853bSJacopo Mondi 	ov490_read_reg(dev, OV490_ISP_HSIZE_HIGH, &val);
456a59f853bSJacopo Mondi 	dev->fmt.width = (val & 0xf) << 8;
457a59f853bSJacopo Mondi 	ov490_read_reg(dev, OV490_ISP_HSIZE_LOW, &val);
458a59f853bSJacopo Mondi 	dev->fmt.width |= (val & 0xff);
459a59f853bSJacopo Mondi 
460a59f853bSJacopo Mondi 	ov490_read_reg(dev, OV490_ISP_VSIZE_HIGH, &val);
461a59f853bSJacopo Mondi 	dev->fmt.height = (val & 0xf) << 8;
462a59f853bSJacopo Mondi 	ov490_read_reg(dev, OV490_ISP_VSIZE_LOW, &val);
463a59f853bSJacopo Mondi 	dev->fmt.height |= val & 0xff;
464a59f853bSJacopo Mondi 
465a59f853bSJacopo Mondi 	/* Set bus width to 12 bits with [0:11] ordering. */
466a59f853bSJacopo Mondi 	ov490_write_reg(dev, OV490_DVP_CTRL3, 0x10);
467a59f853bSJacopo Mondi 
468a59f853bSJacopo Mondi 	dev_info(dev->dev, "Identified RDACM21 camera module\n");
469a59f853bSJacopo Mondi 
470a59f853bSJacopo Mondi 	return 0;
471a59f853bSJacopo Mondi }
472a59f853bSJacopo Mondi 
rdacm21_initialize(struct rdacm21_device * dev)473a59f853bSJacopo Mondi static int rdacm21_initialize(struct rdacm21_device *dev)
474a59f853bSJacopo Mondi {
475a59f853bSJacopo Mondi 	int ret;
476a59f853bSJacopo Mondi 
4779e0bf839SJacopo Mondi 	max9271_wake_up(&dev->serializer);
478a59f853bSJacopo Mondi 
479a59f853bSJacopo Mondi 	/* Enable reverse channel and disable the serial link. */
480a59f853bSJacopo Mondi 	ret = max9271_set_serial_link(&dev->serializer, false);
481a59f853bSJacopo Mondi 	if (ret)
482a59f853bSJacopo Mondi 		return ret;
483a59f853bSJacopo Mondi 
484a59f853bSJacopo Mondi 	/* Configure I2C bus at 105Kbps speed and configure GMSL. */
485a59f853bSJacopo Mondi 	ret = max9271_configure_i2c(&dev->serializer,
486a59f853bSJacopo Mondi 				    MAX9271_I2CSLVSH_469NS_234NS |
487a59f853bSJacopo Mondi 				    MAX9271_I2CSLVTO_1024US |
488a59f853bSJacopo Mondi 				    MAX9271_I2CMSTBT_105KBPS);
489a59f853bSJacopo Mondi 	if (ret)
490a59f853bSJacopo Mondi 		return ret;
491a59f853bSJacopo Mondi 
492a59f853bSJacopo Mondi 	ret = max9271_verify_id(&dev->serializer);
493a59f853bSJacopo Mondi 	if (ret)
494a59f853bSJacopo Mondi 		return ret;
495a59f853bSJacopo Mondi 
49670287720SJacopo Mondi 	/*
49770287720SJacopo Mondi 	 * Enable GPIO1 and hold OV490 in reset during max9271 configuration.
49870287720SJacopo Mondi 	 * The reset signal has to be asserted for at least 250 useconds.
49970287720SJacopo Mondi 	 */
500a59f853bSJacopo Mondi 	ret = max9271_enable_gpios(&dev->serializer, MAX9271_GPIO1OUT);
501a59f853bSJacopo Mondi 	if (ret)
502a59f853bSJacopo Mondi 		return ret;
503a59f853bSJacopo Mondi 
504a59f853bSJacopo Mondi 	ret = max9271_clear_gpios(&dev->serializer, MAX9271_GPIO1OUT);
505a59f853bSJacopo Mondi 	if (ret)
506a59f853bSJacopo Mondi 		return ret;
50770287720SJacopo Mondi 	usleep_range(250, 500);
508a59f853bSJacopo Mondi 
509a59f853bSJacopo Mondi 	ret = max9271_configure_gmsl_link(&dev->serializer);
510a59f853bSJacopo Mondi 	if (ret)
511a59f853bSJacopo Mondi 		return ret;
512a59f853bSJacopo Mondi 
513a59f853bSJacopo Mondi 	ret = max9271_set_address(&dev->serializer, dev->addrs[0]);
514a59f853bSJacopo Mondi 	if (ret)
515a59f853bSJacopo Mondi 		return ret;
516a59f853bSJacopo Mondi 	dev->serializer.client->addr = dev->addrs[0];
517a59f853bSJacopo Mondi 
518a59f853bSJacopo Mondi 	ret = max9271_set_translation(&dev->serializer, dev->addrs[1],
519a59f853bSJacopo Mondi 				      OV490_I2C_ADDRESS);
520a59f853bSJacopo Mondi 	if (ret)
521a59f853bSJacopo Mondi 		return ret;
522a59f853bSJacopo Mondi 	dev->isp->addr = dev->addrs[1];
523a59f853bSJacopo Mondi 
524a59f853bSJacopo Mondi 	/* Release OV490 from reset and initialize it. */
525a59f853bSJacopo Mondi 	ret = max9271_set_gpios(&dev->serializer, MAX9271_GPIO1OUT);
526a59f853bSJacopo Mondi 	if (ret)
527a59f853bSJacopo Mondi 		return ret;
528a59f853bSJacopo Mondi 	usleep_range(3000, 5000);
529a59f853bSJacopo Mondi 
530a59f853bSJacopo Mondi 	ret = ov490_initialize(dev);
531a59f853bSJacopo Mondi 	if (ret)
532a59f853bSJacopo Mondi 		return ret;
533a59f853bSJacopo Mondi 
534a59f853bSJacopo Mondi 	/*
535a59f853bSJacopo Mondi 	 * Set reverse channel high threshold to increase noise immunity.
536a59f853bSJacopo Mondi 	 *
537a59f853bSJacopo Mondi 	 * This should be compensated by increasing the reverse channel
538a59f853bSJacopo Mondi 	 * amplitude on the remote deserializer side.
539a59f853bSJacopo Mondi 	 */
540a59f853bSJacopo Mondi 	return max9271_set_high_threshold(&dev->serializer, true);
541a59f853bSJacopo Mondi }
542a59f853bSJacopo Mondi 
rdacm21_probe(struct i2c_client * client)543a59f853bSJacopo Mondi static int rdacm21_probe(struct i2c_client *client)
544a59f853bSJacopo Mondi {
545a59f853bSJacopo Mondi 	struct rdacm21_device *dev;
546a59f853bSJacopo Mondi 	int ret;
547a59f853bSJacopo Mondi 
548a59f853bSJacopo Mondi 	dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
549a59f853bSJacopo Mondi 	if (!dev)
550a59f853bSJacopo Mondi 		return -ENOMEM;
551a59f853bSJacopo Mondi 	dev->dev = &client->dev;
552a59f853bSJacopo Mondi 	dev->serializer.client = client;
553a59f853bSJacopo Mondi 
554a59f853bSJacopo Mondi 	ret = of_property_read_u32_array(client->dev.of_node, "reg",
555a59f853bSJacopo Mondi 					 dev->addrs, 2);
556a59f853bSJacopo Mondi 	if (ret < 0) {
557a59f853bSJacopo Mondi 		dev_err(dev->dev, "Invalid DT reg property: %d\n", ret);
558a59f853bSJacopo Mondi 		return -EINVAL;
559a59f853bSJacopo Mondi 	}
560a59f853bSJacopo Mondi 
561a59f853bSJacopo Mondi 	/* Create the dummy I2C client for the sensor. */
562a59f853bSJacopo Mondi 	dev->isp = i2c_new_dummy_device(client->adapter, OV490_I2C_ADDRESS);
563a59f853bSJacopo Mondi 	if (IS_ERR(dev->isp))
564a59f853bSJacopo Mondi 		return PTR_ERR(dev->isp);
565a59f853bSJacopo Mondi 
566a59f853bSJacopo Mondi 	ret = rdacm21_initialize(dev);
567a59f853bSJacopo Mondi 	if (ret < 0)
568a59f853bSJacopo Mondi 		goto error;
569a59f853bSJacopo Mondi 
570a59f853bSJacopo Mondi 	/* Initialize and register the subdevice. */
571a59f853bSJacopo Mondi 	v4l2_i2c_subdev_init(&dev->sd, client, &rdacm21_subdev_ops);
572a59f853bSJacopo Mondi 	dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
573a59f853bSJacopo Mondi 
574a59f853bSJacopo Mondi 	v4l2_ctrl_handler_init(&dev->ctrls, 1);
575a59f853bSJacopo Mondi 	v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE,
576a59f853bSJacopo Mondi 			  OV10640_PIXEL_RATE, OV10640_PIXEL_RATE, 1,
577a59f853bSJacopo Mondi 			  OV10640_PIXEL_RATE);
578a59f853bSJacopo Mondi 	dev->sd.ctrl_handler = &dev->ctrls;
579a59f853bSJacopo Mondi 
580a59f853bSJacopo Mondi 	ret = dev->ctrls.error;
581a59f853bSJacopo Mondi 	if (ret)
582a59f853bSJacopo Mondi 		goto error_free_ctrls;
583a59f853bSJacopo Mondi 
584a59f853bSJacopo Mondi 	dev->pad.flags = MEDIA_PAD_FL_SOURCE;
585d2facee6SLaurentiu Palcu 	dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
586a59f853bSJacopo Mondi 	ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
587a59f853bSJacopo Mondi 	if (ret < 0)
588a59f853bSJacopo Mondi 		goto error_free_ctrls;
589a59f853bSJacopo Mondi 
590a59f853bSJacopo Mondi 	ret = v4l2_async_register_subdev(&dev->sd);
591a59f853bSJacopo Mondi 	if (ret)
5921029939bSSakari Ailus 		goto error_free_ctrls;
593a59f853bSJacopo Mondi 
594a59f853bSJacopo Mondi 	return 0;
595a59f853bSJacopo Mondi 
596a59f853bSJacopo Mondi error_free_ctrls:
597a59f853bSJacopo Mondi 	v4l2_ctrl_handler_free(&dev->ctrls);
598a59f853bSJacopo Mondi error:
599a59f853bSJacopo Mondi 	i2c_unregister_device(dev->isp);
600a59f853bSJacopo Mondi 
601a59f853bSJacopo Mondi 	return ret;
602a59f853bSJacopo Mondi }
603a59f853bSJacopo Mondi 
rdacm21_remove(struct i2c_client * client)604ed5c2f5fSUwe Kleine-König static void rdacm21_remove(struct i2c_client *client)
605a59f853bSJacopo Mondi {
606a59f853bSJacopo Mondi 	struct rdacm21_device *dev = sd_to_rdacm21(i2c_get_clientdata(client));
607a59f853bSJacopo Mondi 
608a59f853bSJacopo Mondi 	v4l2_async_unregister_subdev(&dev->sd);
609a59f853bSJacopo Mondi 	v4l2_ctrl_handler_free(&dev->ctrls);
610a59f853bSJacopo Mondi 	i2c_unregister_device(dev->isp);
611a59f853bSJacopo Mondi }
612a59f853bSJacopo Mondi 
613a59f853bSJacopo Mondi static const struct of_device_id rdacm21_of_ids[] = {
614a59f853bSJacopo Mondi 	{ .compatible = "imi,rdacm21" },
615a59f853bSJacopo Mondi 	{ }
616a59f853bSJacopo Mondi };
617a59f853bSJacopo Mondi MODULE_DEVICE_TABLE(of, rdacm21_of_ids);
618a59f853bSJacopo Mondi 
619a59f853bSJacopo Mondi static struct i2c_driver rdacm21_i2c_driver = {
620a59f853bSJacopo Mondi 	.driver	= {
621a59f853bSJacopo Mondi 		.name	= "rdacm21",
622a59f853bSJacopo Mondi 		.of_match_table = rdacm21_of_ids,
623a59f853bSJacopo Mondi 	},
624aaeb31c0SUwe Kleine-König 	.probe		= rdacm21_probe,
625a59f853bSJacopo Mondi 	.remove		= rdacm21_remove,
626a59f853bSJacopo Mondi };
627a59f853bSJacopo Mondi 
628a59f853bSJacopo Mondi module_i2c_driver(rdacm21_i2c_driver);
629a59f853bSJacopo Mondi 
630a59f853bSJacopo Mondi MODULE_DESCRIPTION("GMSL Camera driver for RDACM21");
631a59f853bSJacopo Mondi MODULE_AUTHOR("Jacopo Mondi");
632a59f853bSJacopo Mondi MODULE_LICENSE("GPL v2");
633