File Index
Files and directories
Sign in
FILE

og0ve1b.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/i2c/og0ve1b.c C
  1// SPDX-License-Identifier: GPL-2.0
  2// Copyright (c) 2024-2025 Linaro Ltd
  3
  4#include <linux/clk.h>
  5#include <linux/delay.h>
  6#include <linux/gpio/consumer.h>
  7#include <linux/i2c.h>
  8#include <linux/module.h>
  9#include <linux/pm_runtime.h>
 10#include <linux/regulator/consumer.h>
 11#include <linux/units.h>
 12#include <media/v4l2-cci.h>
 13#include <media/v4l2-ctrls.h>
 14#include <media/v4l2-device.h>
 15#include <media/v4l2-fwnode.h>
 16
 17#define OG0VE1B_LINK_FREQ_500MHZ	(500 * HZ_PER_MHZ)
 18#define OG0VE1B_MCLK_FREQ_24MHZ		(24 * HZ_PER_MHZ)
 19
 20#define OG0VE1B_REG_CHIP_ID		CCI_REG24(0x300a)
 21#define OG0VE1B_CHIP_ID			0xc75645
 22
 23#define OG0VE1B_REG_MODE_SELECT		CCI_REG8(0x0100)
 24#define OG0VE1B_MODE_STANDBY		0x00
 25#define OG0VE1B_MODE_STREAMING		BIT(0)
 26
 27#define OG0VE1B_REG_SOFTWARE_RST	CCI_REG8(0x0103)
 28#define OG0VE1B_SOFTWARE_RST		BIT(0)
 29
 30/* Exposure controls from sensor */
 31#define OG0VE1B_REG_EXPOSURE		CCI_REG24(0x3500)
 32#define OG0VE1B_EXPOSURE_MIN		1
 33#define OG0VE1B_EXPOSURE_MAX_MARGIN	14
 34#define OG0VE1B_EXPOSURE_STEP		1
 35#define OG0VE1B_EXPOSURE_DEFAULT	554
 36
 37/* Analogue gain controls from sensor */
 38#define OG0VE1B_REG_ANALOGUE_GAIN	CCI_REG16(0x350a)
 39#define OG0VE1B_ANALOGUE_GAIN_MIN	1
 40#define OG0VE1B_ANALOGUE_GAIN_MAX	0x1ff
 41#define OG0VE1B_ANALOGUE_GAIN_STEP	1
 42#define OG0VE1B_ANALOGUE_GAIN_DEFAULT	16
 43
 44/* Test pattern */
 45#define OG0VE1B_REG_PRE_ISP		CCI_REG8(0x5e00)
 46#define OG0VE1B_TEST_PATTERN_ENABLE	BIT(7)
 47
 48#define to_og0ve1b(_sd)			container_of(_sd, struct og0ve1b, sd)
 49
 50static const s64 og0ve1b_link_freq_menu[] = {
 51	OG0VE1B_LINK_FREQ_500MHZ,
 52};
 53
 54struct og0ve1b_reg_list {
 55	const struct cci_reg_sequence *regs;
 56	unsigned int num_regs;
 57};
 58
 59struct og0ve1b_mode {
 60	u32 width;	/* Frame width in pixels */
 61	u32 height;	/* Frame height in pixels */
 62	u32 hts;	/* Horizontal timing size */
 63	u32 vts;	/* Default vertical timing size */
 64	u32 bpp;	/* Bits per pixel */
 65
 66	const struct og0ve1b_reg_list reg_list;	/* Sensor register setting */
 67};
 68
 69static const char * const og0ve1b_test_pattern_menu[] = {
 70	"Disabled",
 71	"Vertical Colour Bars",
 72};
 73
 74static const char * const og0ve1b_supply_names[] = {
 75	"avdd",		/* Analog power */
 76	"dovdd",	/* Digital I/O power */
 77	"dvdd",		/* Digital core power */
 78};
 79
 80#define OG0VE1B_NUM_SUPPLIES	ARRAY_SIZE(og0ve1b_supply_names)
 81
 82struct og0ve1b {
 83	struct device *dev;
 84	struct regmap *regmap;
 85	struct clk *xvclk;
 86	struct gpio_desc *reset_gpio;
 87	struct regulator_bulk_data supplies[OG0VE1B_NUM_SUPPLIES];
 88
 89	struct v4l2_subdev sd;
 90	struct media_pad pad;
 91
 92	struct v4l2_ctrl_handler ctrl_handler;
 93
 94	/* Saved register value */
 95	u64 pre_isp;
 96};
 97
 98static const struct cci_reg_sequence og0ve1b_640x480_120fps_mode[] = {
 99	{ CCI_REG8(0x30a0), 0x02 },
100	{ CCI_REG8(0x30a1), 0x00 },
101	{ CCI_REG8(0x30a2), 0x48 },
102	{ CCI_REG8(0x30a3), 0x34 },
103	{ CCI_REG8(0x30a4), 0xf7 },
104	{ CCI_REG8(0x30a5), 0x00 },
105	{ CCI_REG8(0x3082), 0x32 },
106	{ CCI_REG8(0x3083), 0x01 },
107	{ CCI_REG8(0x301c), 0xf0 },
108	{ CCI_REG8(0x301e), 0x0b },
109	{ CCI_REG8(0x3106), 0x10 },
110	{ CCI_REG8(0x3708), 0x77 },
111	{ CCI_REG8(0x3709), 0xf8 },
112	{ CCI_REG8(0x3717), 0x00 },
113	{ CCI_REG8(0x3782), 0x00 },
114	{ CCI_REG8(0x3783), 0x47 },
115	{ CCI_REG8(0x37a2), 0x00 },
116	{ CCI_REG8(0x3503), 0x07 },
117	{ CCI_REG8(0x3509), 0x10 },
118	{ CCI_REG8(0x3600), 0x83 },
119	{ CCI_REG8(0x3601), 0x21 },
120	{ CCI_REG8(0x3602), 0xf1 },
121	{ CCI_REG8(0x360a), 0x18 },
122	{ CCI_REG8(0x360e), 0xb3 },
123	{ CCI_REG8(0x3613), 0x20 },
124	{ CCI_REG8(0x366a), 0x78 },
125	{ CCI_REG8(0x3706), 0x63 },
126	{ CCI_REG8(0x3713), 0x00 },
127	{ CCI_REG8(0x3716), 0xb0 },
128	{ CCI_REG8(0x37a1), 0x38 },
129	{ CCI_REG8(0x3800), 0x00 },
130	{ CCI_REG8(0x3801), 0x04 },
131	{ CCI_REG8(0x3802), 0x00 },
132	{ CCI_REG8(0x3803), 0x04 },
133	{ CCI_REG8(0x3804), 0x02 },
134	{ CCI_REG8(0x3805), 0x8b },
135	{ CCI_REG8(0x3806), 0x01 },
136	{ CCI_REG8(0x3807), 0xeb },
137	{ CCI_REG8(0x3808), 0x02 },	/* output width */
138	{ CCI_REG8(0x3809), 0x80 },
139	{ CCI_REG8(0x380a), 0x01 },	/* output height */
140	{ CCI_REG8(0x380b), 0xe0 },
141	{ CCI_REG8(0x380c), 0x03 },	/* horizontal timing size */
142	{ CCI_REG8(0x380d), 0x18 },
143	{ CCI_REG8(0x380e), 0x02 },	/* vertical timing size */
144	{ CCI_REG8(0x380f), 0x38 },
145	{ CCI_REG8(0x3811), 0x04 },
146	{ CCI_REG8(0x3813), 0x04 },
147	{ CCI_REG8(0x3814), 0x11 },
148	{ CCI_REG8(0x3815), 0x11 },
149	{ CCI_REG8(0x3820), 0x00 },
150	{ CCI_REG8(0x3821), 0x00 },
151	{ CCI_REG8(0x3823), 0x04 },
152	{ CCI_REG8(0x382a), 0x00 },
153	{ CCI_REG8(0x382b), 0x03 },
154	{ CCI_REG8(0x3840), 0x00 },
155	{ CCI_REG8(0x389e), 0x00 },
156	{ CCI_REG8(0x3c05), 0x08 },
157	{ CCI_REG8(0x3c26), 0x02 },
158	{ CCI_REG8(0x3c27), 0xc0 },
159	{ CCI_REG8(0x3c28), 0x00 },
160	{ CCI_REG8(0x3c29), 0x40 },
161	{ CCI_REG8(0x3c2c), 0x00 },
162	{ CCI_REG8(0x3c2d), 0x50 },
163	{ CCI_REG8(0x3c2e), 0x02 },
164	{ CCI_REG8(0x3c2f), 0x66 },
165	{ CCI_REG8(0x3c33), 0x08 },
166	{ CCI_REG8(0x3c35), 0x00 },
167	{ CCI_REG8(0x3c36), 0x00 },
168	{ CCI_REG8(0x3c37), 0x00 },
169	{ CCI_REG8(0x3f52), 0x9b },
170	{ CCI_REG8(0x4001), 0x42 },
171	{ CCI_REG8(0x4004), 0x08 },
172	{ CCI_REG8(0x4005), 0x00 },
173	{ CCI_REG8(0x4007), 0x28 },
174	{ CCI_REG8(0x4009), 0x40 },
175	{ CCI_REG8(0x4307), 0x30 },
176	{ CCI_REG8(0x4500), 0x80 },
177	{ CCI_REG8(0x4501), 0x02 },
178	{ CCI_REG8(0x4502), 0x47 },
179	{ CCI_REG8(0x4504), 0x7f },
180	{ CCI_REG8(0x4601), 0x48 },
181	{ CCI_REG8(0x4800), 0x64 },
182	{ CCI_REG8(0x4801), 0x0f },
183	{ CCI_REG8(0x4806), 0x2f },
184	{ CCI_REG8(0x4819), 0xaa },
185	{ CCI_REG8(0x4823), 0x3e },
186	{ CCI_REG8(0x5000), 0x85 },
187	{ CCI_REG8(0x5e00), 0x0c },
188	{ CCI_REG8(0x3899), 0x09 },
189	{ CCI_REG8(0x4f00), 0x64 },
190	{ CCI_REG8(0x4f02), 0x0a },
191	{ CCI_REG8(0x4f05), 0x0e },
192	{ CCI_REG8(0x4f06), 0x11 },
193	{ CCI_REG8(0x4f08), 0x0b },
194	{ CCI_REG8(0x4f0a), 0xc4 },
195	{ CCI_REG8(0x4f20), 0x1f },
196	{ CCI_REG8(0x4f25), 0x10 },
197	{ CCI_REG8(0x3016), 0x10 },
198	{ CCI_REG8(0x3017), 0x00 },
199	{ CCI_REG8(0x3018), 0x00 },
200	{ CCI_REG8(0x3019), 0x00 },
201	{ CCI_REG8(0x301a), 0x00 },
202	{ CCI_REG8(0x301b), 0x00 },
203	{ CCI_REG8(0x301c), 0x72 },
204	{ CCI_REG8(0x3037), 0x40 },
205	{ CCI_REG8(0x4f2c), 0x00 },
206	{ CCI_REG8(0x4f21), 0x00 },
207	{ CCI_REG8(0x4f23), 0x00 },
208	{ CCI_REG8(0x4f2a), 0x00 },
209	{ CCI_REG8(0x3665), 0xe7 },
210	{ CCI_REG8(0x3668), 0x48 },
211	{ CCI_REG8(0x3671), 0x3c },
212	{ CCI_REG8(0x389a), 0x02 },
213	{ CCI_REG8(0x389b), 0x00 },
214	{ CCI_REG8(0x303c), 0xa0 },
215	{ CCI_REG8(0x300f), 0xf0 },
216	{ CCI_REG8(0x304b), 0x0f },
217	{ CCI_REG8(0x3662), 0x24 },
218	{ CCI_REG8(0x3006), 0x40 },
219	{ CCI_REG8(0x4f26), 0x45 },
220	{ CCI_REG8(0x3607), 0x34 },
221	{ CCI_REG8(0x3608), 0x01 },
222	{ CCI_REG8(0x360a), 0x0c },
223	{ CCI_REG8(0x360b), 0x86 },
224	{ CCI_REG8(0x360c), 0xcc },
225	{ CCI_REG8(0x3013), 0x00 },
226	{ CCI_REG8(0x3083), 0x02 },
227	{ CCI_REG8(0x3084), 0x12 },
228	{ CCI_REG8(0x4601), 0x38 },
229	{ CCI_REG8(0x366f), 0x3a },
230	{ CCI_REG8(0x3713), 0x19 },
231	{ CCI_REG8(0x37a2), 0x00 },
232	{ CCI_REG8(0x3f43), 0x27 },
233	{ CCI_REG8(0x3f45), 0x27 },
234	{ CCI_REG8(0x3f47), 0x32 },
235	{ CCI_REG8(0x3f49), 0x3e },
236	{ CCI_REG8(0x3f4b), 0x20 },
237	{ CCI_REG8(0x3f4d), 0x30 },
238	{ CCI_REG8(0x4300), 0x3f },
239	{ CCI_REG8(0x4009), 0x10 },
240	{ CCI_REG8(0x3f02), 0x68 },
241	{ CCI_REG8(0x3700), 0x8c },
242	{ CCI_REG8(0x370b), 0x7e },
243	{ CCI_REG8(0x3f47), 0x35 },
244};
245
246static const struct og0ve1b_mode supported_modes[] = {
247	{
248		.width = 640,
249		.height = 480,
250		.hts = 792,
251		.vts = 568,
252		.bpp = 8,
253		.reg_list = {
254			.regs = og0ve1b_640x480_120fps_mode,
255			.num_regs = ARRAY_SIZE(og0ve1b_640x480_120fps_mode),
256		},
257	},
258};
259
260static int og0ve1b_enable_test_pattern(struct og0ve1b *og0ve1b, u32 pattern)
261{
262	u64 val = og0ve1b->pre_isp;
263
264	if (pattern)
265		val |= OG0VE1B_TEST_PATTERN_ENABLE;
266	else
267		val &= ~OG0VE1B_TEST_PATTERN_ENABLE;
268
269	return cci_write(og0ve1b->regmap, OG0VE1B_REG_PRE_ISP, val, NULL);
270}
271
272static int og0ve1b_set_ctrl(struct v4l2_ctrl *ctrl)
273{
274	struct og0ve1b *og0ve1b = container_of(ctrl->handler, struct og0ve1b,
275					       ctrl_handler);
276	int ret;
277
278	/* V4L2 controls are applied, when sensor is powered up for streaming */
279	if (!pm_runtime_get_if_active(og0ve1b->dev))
280		return 0;
281
282	switch (ctrl->id) {
283	case V4L2_CID_ANALOGUE_GAIN:
284		ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_ANALOGUE_GAIN,
285				ctrl->val, NULL);
286		break;
287	case V4L2_CID_EXPOSURE:
288		ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_EXPOSURE,
289				ctrl->val << 4, NULL);
290		break;
291	case V4L2_CID_TEST_PATTERN:
292		ret = og0ve1b_enable_test_pattern(og0ve1b, ctrl->val);
293		break;
294	default:
295		ret = -EINVAL;
296		break;
297	}
298
299	pm_runtime_put(og0ve1b->dev);
300
301	return ret;
302}
303
304static const struct v4l2_ctrl_ops og0ve1b_ctrl_ops = {
305	.s_ctrl = og0ve1b_set_ctrl,
306};
307
308static int og0ve1b_init_controls(struct og0ve1b *og0ve1b)
309{
310	struct v4l2_ctrl_handler *ctrl_hdlr = &og0ve1b->ctrl_handler;
311	const struct og0ve1b_mode *mode = &supported_modes[0];
312	struct v4l2_fwnode_device_properties props;
313	s64 exposure_max, pixel_rate, h_blank;
314	struct v4l2_ctrl *ctrl;
315	int ret;
316
317	v4l2_ctrl_handler_init(ctrl_hdlr, 9);
318
319	ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, &og0ve1b_ctrl_ops,
320				      V4L2_CID_LINK_FREQ,
321				      ARRAY_SIZE(og0ve1b_link_freq_menu) - 1,
322				      0, og0ve1b_link_freq_menu);
323	if (ctrl)
324		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
325
326	pixel_rate = og0ve1b_link_freq_menu[0] / mode->bpp;
327	v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, V4L2_CID_PIXEL_RATE,
328			  0, pixel_rate, 1, pixel_rate);
329
330	h_blank = mode->hts - mode->width;
331	ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, V4L2_CID_HBLANK,
332				 h_blank, h_blank, 1, h_blank);
333	if (ctrl)
334		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
335
336	ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, V4L2_CID_VBLANK,
337				 mode->vts - mode->height,
338				 mode->vts - mode->height, 1,
339				 mode->vts - mode->height);
340	if (ctrl)
341		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
342
343	v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
344			  OG0VE1B_ANALOGUE_GAIN_MIN, OG0VE1B_ANALOGUE_GAIN_MAX,
345			  OG0VE1B_ANALOGUE_GAIN_STEP,
346			  OG0VE1B_ANALOGUE_GAIN_DEFAULT);
347
348	exposure_max = (mode->vts - OG0VE1B_EXPOSURE_MAX_MARGIN);
349	v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops,
350			  V4L2_CID_EXPOSURE,
351			  OG0VE1B_EXPOSURE_MIN, exposure_max,
352			  OG0VE1B_EXPOSURE_STEP,
353			  OG0VE1B_EXPOSURE_DEFAULT);
354
355	v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &og0ve1b_ctrl_ops,
356				     V4L2_CID_TEST_PATTERN,
357				     ARRAY_SIZE(og0ve1b_test_pattern_menu) - 1,
358				     0, 0, og0ve1b_test_pattern_menu);
359
360	if (ctrl_hdlr->error)
361		return ctrl_hdlr->error;
362
363	ret = v4l2_fwnode_device_parse(og0ve1b->dev, &props);
364	if (ret)
365		goto error_free_hdlr;
366
367	ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &og0ve1b_ctrl_ops,
368					      &props);
369	if (ret)
370		goto error_free_hdlr;
371
372	og0ve1b->sd.ctrl_handler = ctrl_hdlr;
373
374	return 0;
375
376error_free_hdlr:
377	v4l2_ctrl_handler_free(ctrl_hdlr);
378
379	return ret;
380}
381
382static void og0ve1b_update_pad_format(const struct og0ve1b_mode *mode,
383				      struct v4l2_mbus_framefmt *fmt)
384{
385	fmt->code = MEDIA_BUS_FMT_Y8_1X8;
386	fmt->width = mode->width;
387	fmt->height = mode->height;
388	fmt->field = V4L2_FIELD_NONE;
389	fmt->colorspace = V4L2_COLORSPACE_RAW;
390	fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
391	fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
392	fmt->xfer_func = V4L2_XFER_FUNC_NONE;
393}
394
395static int og0ve1b_enable_streams(struct v4l2_subdev *sd,
396				  struct v4l2_subdev_state *state, u32 pad,
397				  u64 streams_mask)
398{
399	const struct og0ve1b_reg_list *reg_list = &supported_modes[0].reg_list;
400	struct og0ve1b *og0ve1b = to_og0ve1b(sd);
401	int ret;
402
403	ret = pm_runtime_resume_and_get(og0ve1b->dev);
404	if (ret)
405		return ret;
406
407	/* Skip a step of explicit entering into the standby mode */
408	ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_SOFTWARE_RST,
409			OG0VE1B_SOFTWARE_RST, NULL);
410	if (ret) {
411		dev_err(og0ve1b->dev, "failed to software reset: %d\n", ret);
412		goto error;
413	}
414
415	ret = cci_multi_reg_write(og0ve1b->regmap, reg_list->regs,
416				  reg_list->num_regs, NULL);
417	if (ret) {
418		dev_err(og0ve1b->dev, "failed to set mode: %d\n", ret);
419		goto error;
420	}
421
422	ret = __v4l2_ctrl_handler_setup(og0ve1b->sd.ctrl_handler);
423	if (ret)
424		goto error;
425
426	ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_MODE_SELECT,
427			OG0VE1B_MODE_STREAMING, NULL);
428	if (ret) {
429		dev_err(og0ve1b->dev, "failed to start streaming: %d\n", ret);
430		goto error;
431	}
432
433	return 0;
434
435error:
436	pm_runtime_put_autosuspend(og0ve1b->dev);
437
438	return ret;
439}
440
441static int og0ve1b_disable_streams(struct v4l2_subdev *sd,
442				   struct v4l2_subdev_state *state, u32 pad,
443				   u64 streams_mask)
444{
445	struct og0ve1b *og0ve1b = to_og0ve1b(sd);
446	int ret;
447
448	ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_MODE_SELECT,
449			OG0VE1B_MODE_STANDBY, NULL);
450	if (ret)
451		dev_err(og0ve1b->dev, "failed to stop streaming: %d\n", ret);
452
453	pm_runtime_put_autosuspend(og0ve1b->dev);
454
455	return ret;
456}
457
458static int og0ve1b_set_pad_format(struct v4l2_subdev *sd,
459				  struct v4l2_subdev_state *state,
460				  struct v4l2_subdev_format *fmt)
461{
462	struct v4l2_mbus_framefmt *format;
463	const struct og0ve1b_mode *mode;
464
465	format = v4l2_subdev_state_get_format(state, 0);
466
467	mode = v4l2_find_nearest_size(supported_modes,
468				      ARRAY_SIZE(supported_modes),
469				      width, height,
470				      fmt->format.width,
471				      fmt->format.height);
472
473	og0ve1b_update_pad_format(mode, &fmt->format);
474	*format = fmt->format;
475
476	return 0;
477}
478
479static int og0ve1b_enum_mbus_code(struct v4l2_subdev *sd,
480				  struct v4l2_subdev_state *sd_state,
481				  struct v4l2_subdev_mbus_code_enum *code)
482{
483	if (code->index > 0)
484		return -EINVAL;
485
486	code->code = MEDIA_BUS_FMT_Y8_1X8;
487
488	return 0;
489}
490
491static int og0ve1b_enum_frame_size(struct v4l2_subdev *sd,
492				   struct v4l2_subdev_state *sd_state,
493				   struct v4l2_subdev_frame_size_enum *fse)
494{
495	if (fse->index >= ARRAY_SIZE(supported_modes))
496		return -EINVAL;
497
498	if (fse->code != MEDIA_BUS_FMT_Y8_1X8)
499		return -EINVAL;
500
501	fse->min_width = supported_modes[fse->index].width;
502	fse->max_width = fse->min_width;
503	fse->min_height = supported_modes[fse->index].height;
504	fse->max_height = fse->min_height;
505
506	return 0;
507}
508
509static int og0ve1b_init_state(struct v4l2_subdev *sd,
510			      struct v4l2_subdev_state *state)
511{
512	struct v4l2_subdev_format fmt = {
513		.which = V4L2_SUBDEV_FORMAT_TRY,
514		.pad = 0,
515		.format = {
516			.code = MEDIA_BUS_FMT_Y8_1X8,
517			.width = supported_modes[0].width,
518			.height = supported_modes[0].height,
519		},
520	};
521
522	og0ve1b_set_pad_format(sd, state, &fmt);
523
524	return 0;
525}
526
527static const struct v4l2_subdev_video_ops og0ve1b_video_ops = {
528	.s_stream = v4l2_subdev_s_stream_helper,
529};
530
531static const struct v4l2_subdev_pad_ops og0ve1b_pad_ops = {
532	.set_fmt = og0ve1b_set_pad_format,
533	.get_fmt = v4l2_subdev_get_fmt,
534	.enum_mbus_code = og0ve1b_enum_mbus_code,
535	.enum_frame_size = og0ve1b_enum_frame_size,
536	.enable_streams = og0ve1b_enable_streams,
537	.disable_streams = og0ve1b_disable_streams,
538};
539
540static const struct v4l2_subdev_ops og0ve1b_subdev_ops = {
541	.video = &og0ve1b_video_ops,
542	.pad = &og0ve1b_pad_ops,
543};
544
545static const struct v4l2_subdev_internal_ops og0ve1b_internal_ops = {
546	.init_state = og0ve1b_init_state,
547};
548
549static const struct media_entity_operations og0ve1b_subdev_entity_ops = {
550	.link_validate = v4l2_subdev_link_validate,
551};
552
553static int og0ve1b_identify_sensor(struct og0ve1b *og0ve1b)
554{
555	u64 val;
556	int ret;
557
558	ret = cci_read(og0ve1b->regmap, OG0VE1B_REG_CHIP_ID, &val, NULL);
559	if (ret) {
560		dev_err(og0ve1b->dev, "failed to read chip id: %d\n", ret);
561		return ret;
562	}
563
564	if (val != OG0VE1B_CHIP_ID) {
565		dev_err(og0ve1b->dev, "chip id mismatch: %x!=%llx\n",
566			OG0VE1B_CHIP_ID, val);
567		return -ENODEV;
568	}
569
570	ret = cci_read(og0ve1b->regmap, OG0VE1B_REG_PRE_ISP,
571		       &og0ve1b->pre_isp, NULL);
572	if (ret)
573		dev_err(og0ve1b->dev, "failed to read pre_isp: %d\n", ret);
574
575	return ret;
576}
577
578static int og0ve1b_check_hwcfg(struct og0ve1b *og0ve1b)
579{
580	struct fwnode_handle *fwnode = dev_fwnode(og0ve1b->dev), *ep;
581	struct v4l2_fwnode_endpoint bus_cfg = {
582		.bus_type = V4L2_MBUS_CSI2_DPHY,
583	};
584	unsigned long freq_bitmap;
585	int ret;
586
587	if (!fwnode)
588		return -ENODEV;
589
590	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
591	if (!ep)
592		return -EINVAL;
593
594	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
595	fwnode_handle_put(ep);
596	if (ret)
597		return ret;
598
599	ret = v4l2_link_freq_to_bitmap(og0ve1b->dev,
600				       bus_cfg.link_frequencies,
601				       bus_cfg.nr_of_link_frequencies,
602				       og0ve1b_link_freq_menu,
603				       ARRAY_SIZE(og0ve1b_link_freq_menu),
604				       &freq_bitmap);
605
606	v4l2_fwnode_endpoint_free(&bus_cfg);
607
608	return ret;
609}
610
611static int og0ve1b_power_on(struct device *dev)
612{
613	struct v4l2_subdev *sd = dev_get_drvdata(dev);
614	struct og0ve1b *og0ve1b = to_og0ve1b(sd);
615	int ret;
616
617	ret = regulator_bulk_enable(OG0VE1B_NUM_SUPPLIES, og0ve1b->supplies);
618	if (ret)
619		return ret;
620
621	gpiod_set_value_cansleep(og0ve1b->reset_gpio, 0);
622	usleep_range(10 * USEC_PER_MSEC, 15 * USEC_PER_MSEC);
623
624	ret = clk_prepare_enable(og0ve1b->xvclk);
625	if (ret)
626		goto reset_gpio;
627
628	return 0;
629
630reset_gpio:
631	gpiod_set_value_cansleep(og0ve1b->reset_gpio, 1);
632
633	regulator_bulk_disable(OG0VE1B_NUM_SUPPLIES, og0ve1b->supplies);
634
635	return ret;
636}
637
638static int og0ve1b_power_off(struct device *dev)
639{
640	struct v4l2_subdev *sd = dev_get_drvdata(dev);
641	struct og0ve1b *og0ve1b = to_og0ve1b(sd);
642
643	clk_disable_unprepare(og0ve1b->xvclk);
644
645	gpiod_set_value_cansleep(og0ve1b->reset_gpio, 1);
646
647	regulator_bulk_disable(OG0VE1B_NUM_SUPPLIES, og0ve1b->supplies);
648
649	return 0;
650}
651
652static int og0ve1b_probe(struct i2c_client *client)
653{
654	struct og0ve1b *og0ve1b;
655	unsigned long freq;
656	unsigned int i;
657	int ret;
658
659	og0ve1b = devm_kzalloc(&client->dev, sizeof(*og0ve1b), GFP_KERNEL);
660	if (!og0ve1b)
661		return -ENOMEM;
662
663	og0ve1b->dev = &client->dev;
664
665	v4l2_i2c_subdev_init(&og0ve1b->sd, client, &og0ve1b_subdev_ops);
666
667	og0ve1b->regmap = devm_cci_regmap_init_i2c(client, 16);
668	if (IS_ERR(og0ve1b->regmap))
669		return dev_err_probe(og0ve1b->dev, PTR_ERR(og0ve1b->regmap),
670				     "failed to init CCI\n");
671
672	og0ve1b->xvclk = devm_v4l2_sensor_clk_get(og0ve1b->dev, NULL);
673	if (IS_ERR(og0ve1b->xvclk))
674		return dev_err_probe(og0ve1b->dev, PTR_ERR(og0ve1b->xvclk),
675				     "failed to get XVCLK clock\n");
676
677	freq = clk_get_rate(og0ve1b->xvclk);
678	if (freq && freq != OG0VE1B_MCLK_FREQ_24MHZ)
679		return dev_err_probe(og0ve1b->dev, -EINVAL,
680				     "XVCLK clock frequency %lu is not supported\n",
681				     freq);
682
683	ret = og0ve1b_check_hwcfg(og0ve1b);
684	if (ret)
685		return dev_err_probe(og0ve1b->dev, ret,
686				     "failed to check HW configuration\n");
687
688	og0ve1b->reset_gpio = devm_gpiod_get_optional(og0ve1b->dev, "reset",
689						      GPIOD_OUT_HIGH);
690	if (IS_ERR(og0ve1b->reset_gpio))
691		return dev_err_probe(og0ve1b->dev, PTR_ERR(og0ve1b->reset_gpio),
692				     "cannot get reset GPIO\n");
693
694	for (i = 0; i < OG0VE1B_NUM_SUPPLIES; i++)
695		og0ve1b->supplies[i].supply = og0ve1b_supply_names[i];
696
697	ret = devm_regulator_bulk_get(og0ve1b->dev, OG0VE1B_NUM_SUPPLIES,
698				      og0ve1b->supplies);
699	if (ret)
700		return dev_err_probe(og0ve1b->dev, ret,
701				     "failed to get supply regulators\n");
702
703	/* The sensor must be powered on to read the CHIP_ID register */
704	ret = og0ve1b_power_on(og0ve1b->dev);
705	if (ret)
706		return ret;
707
708	ret = og0ve1b_identify_sensor(og0ve1b);
709	if (ret) {
710		dev_err_probe(og0ve1b->dev, ret, "failed to find sensor\n");
711		goto power_off;
712	}
713
714	ret = og0ve1b_init_controls(og0ve1b);
715	if (ret) {
716		dev_err_probe(og0ve1b->dev, ret, "failed to init controls\n");
717		goto power_off;
718	}
719
720	og0ve1b->sd.state_lock = og0ve1b->ctrl_handler.lock;
721	og0ve1b->sd.internal_ops = &og0ve1b_internal_ops;
722	og0ve1b->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
723	og0ve1b->sd.entity.ops = &og0ve1b_subdev_entity_ops;
724	og0ve1b->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
725	og0ve1b->pad.flags = MEDIA_PAD_FL_SOURCE;
726
727	ret = media_entity_pads_init(&og0ve1b->sd.entity, 1, &og0ve1b->pad);
728	if (ret) {
729		dev_err_probe(og0ve1b->dev, ret,
730			      "failed to init media entity pads\n");
731		goto v4l2_ctrl_handler_free;
732	}
733
734	ret = v4l2_subdev_init_finalize(&og0ve1b->sd);
735	if (ret < 0) {
736		dev_err_probe(og0ve1b->dev, ret,
737			      "failed to init media entity pads\n");
738		goto media_entity_cleanup;
739	}
740
741	pm_runtime_set_active(og0ve1b->dev);
742	pm_runtime_enable(og0ve1b->dev);
743
744	ret = v4l2_async_register_subdev_sensor(&og0ve1b->sd);
745	if (ret < 0) {
746		dev_err_probe(og0ve1b->dev, ret,
747			      "failed to register V4L2 subdev\n");
748		goto subdev_cleanup;
749	}
750
751	/* Enable runtime PM and turn off the device */
752	pm_runtime_idle(og0ve1b->dev);
753	pm_runtime_set_autosuspend_delay(og0ve1b->dev, 1000);
754	pm_runtime_use_autosuspend(og0ve1b->dev);
755
756	return 0;
757
758subdev_cleanup:
759	v4l2_subdev_cleanup(&og0ve1b->sd);
760	pm_runtime_disable(og0ve1b->dev);
761	pm_runtime_set_suspended(og0ve1b->dev);
762
763media_entity_cleanup:
764	media_entity_cleanup(&og0ve1b->sd.entity);
765
766v4l2_ctrl_handler_free:
767	v4l2_ctrl_handler_free(og0ve1b->sd.ctrl_handler);
768
769power_off:
770	og0ve1b_power_off(og0ve1b->dev);
771
772	return ret;
773}
774
775static void og0ve1b_remove(struct i2c_client *client)
776{
777	struct v4l2_subdev *sd = i2c_get_clientdata(client);
778	struct og0ve1b *og0ve1b = to_og0ve1b(sd);
779
780	v4l2_async_unregister_subdev(sd);
781	v4l2_subdev_cleanup(sd);
782	media_entity_cleanup(&sd->entity);
783	v4l2_ctrl_handler_free(sd->ctrl_handler);
784	pm_runtime_disable(og0ve1b->dev);
785
786	if (!pm_runtime_status_suspended(og0ve1b->dev)) {
787		og0ve1b_power_off(og0ve1b->dev);
788		pm_runtime_set_suspended(og0ve1b->dev);
789	}
790}
791
792static const struct dev_pm_ops og0ve1b_pm_ops = {
793	SET_RUNTIME_PM_OPS(og0ve1b_power_off, og0ve1b_power_on, NULL)
794};
795
796static const struct of_device_id og0ve1b_of_match[] = {
797	{ .compatible = "ovti,og0ve1b" },
798	{ /* sentinel */ }
799};
800MODULE_DEVICE_TABLE(of, og0ve1b_of_match);
801
802static struct i2c_driver og0ve1b_i2c_driver = {
803	.driver = {
804		.name = "og0ve1b",
805		.pm = &og0ve1b_pm_ops,
806		.of_match_table = og0ve1b_of_match,
807	},
808	.probe = og0ve1b_probe,
809	.remove = og0ve1b_remove,
810};
811
812module_i2c_driver(og0ve1b_i2c_driver);
813
814MODULE_AUTHOR("Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>");
815MODULE_DESCRIPTION("OmniVision OG0VE1B sensor driver");
816MODULE_LICENSE("GPL");
~/ Read-only file preview.