Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
/*
 * Copyright (c) 2020, FrankLi Limited
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT ovti_ov7725
#include <zephyr.h>
#include <device.h>

#include <sys/byteorder.h>

#include <drivers/video.h>
#include <drivers/i2c.h>
#include <drivers/gpio.h>

#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(ov7725);

#define OV7725_REVISION  0x7721U

#define OV7725_GAIN       0x00U
#define OV7725_BLUE       0x01U
#define OV7725_RED        0x02U
#define OV7725_GREEN      0x03U
#define OV7725_BAVG       0x05U
#define OV7725_GAVG       0x06U
#define OV7725_RAVG       0x07U
#define OV7725_AECH       0x08U
#define OV7725_COM2       0x09U
#define OV7725_PID        0x0AU
#define OV7725_VER        0x0BU
#define OV7725_COM3       0x0CU
#define OV7725_COM4       0x0DU
#define OV7725_COM5       0x0EU
#define OV7725_COM6       0x0FU
#define OV7725_AEC        0x10U
#define OV7725_CLKRC      0x11U
#define OV7725_COM7       0x12U
#define OV7725_COM8       0x13U
#define OV7725_COM9       0x14U
#define OV7725_COM10      0x15U
#define OV7725_REG16      0x16U
#define OV7725_HSTART     0x17U
#define OV7725_HSIZE      0x18U
#define OV7725_VSTART     0x19U
#define OV7725_VSIZE      0x1AU
#define OV7725_PSHFT      0x1BU
#define OV7725_MIDH       0x1CU
#define OV7725_MIDL       0x1DU
#define OV7725_LAEC       0x1FU
#define OV7725_COM11      0x20U
#define OV7725_BDBASE     0x22U
#define OV7725_BDMSTEP    0x23U
#define OV7725_AEW        0x24U
#define OV7725_AEB        0x25U
#define OV7725_VPT        0x26U
#define OV7725_REG28      0x28U
#define OV7725_HOUTSIZE   0x29U
#define OV7725_EXHCH      0x2AU
#define OV7725_EXHCL      0x2BU
#define OV7725_VOUTSIZE   0x2CU
#define OV7725_ADVFL      0x2DU
#define OV7725_ADVFH      0x2EU
#define OV7725_YAVE       0x2FU
#define OV7725_LUMHTH     0x30U
#define OV7725_LUMLTH     0x31U
#define OV7725_HREF       0x32U
#define OV7725_DM_LNL     0x33U
#define OV7725_DM_LNH     0x34U
#define OV7725_ADOFF_B    0x35U
#define OV7725_ADOFF_R    0x36U
#define OV7725_ADOFF_GB   0x37U
#define OV7725_ADOFF_GR   0x38U
#define OV7725_OFF_B      0x39U
#define OV7725_OFF_R      0x3AU
#define OV7725_OFF_GB     0x3BU
#define OV7725_OFF_GR     0x3CU
#define OV7725_COM12      0x3DU
#define OV7725_COM13      0x3EU
#define OV7725_COM14      0x3FU
#define OV7725_COM16      0x41U
#define OV7725_TGT_B      0x42U
#define OV7725_TGT_R      0x43U
#define OV7725_TGT_GB     0x44U
#define OV7725_TGT_GR     0x45U
#define OV7725_LC_CTR     0x46U
#define OV7725_LC_XC      0x47U
#define OV7725_LC_YC      0x48U
#define OV7725_LC_COEF    0x49U
#define OV7725_LC_RADI    0x4AU
#define OV7725_LC_COEFB   0x4BU
#define OV7725_LC_COEFR   0x4CU
#define OV7725_FIXGAIN    0x4DU
#define OV7725_AREF1      0x4FU
#define OV7725_AREF6      0x54U
#define OV7725_UFIX       0x60U
#define OV7725_VFIX       0x61U
#define OV7725_AWBB_BLK   0x62U
#define OV7725_AWB_CTRL0  0x63U
#define OV7725_DSP_CTRL1  0x64U
#define OV7725_DSP_CTRL2  0x65U
#define OV7725_DSP_CTRL3  0x66U
#define OV7725_DSP_CTRL4  0x67U
#define OV7725_AWB_BIAS   0x68U
#define OV7725_AWB_CTRL1  0x69U
#define OV7725_AWB_CTRL2  0x6AU
#define OV7725_AWB_CTRL3  0x6BU
#define OV7725_AWB_CTRL4  0x6CU
#define OV7725_AWB_CTRL5  0x6DU
#define OV7725_AWB_CTRL6  0x6EU
#define OV7725_AWB_CTRL7  0x6FU
#define OV7725_AWB_CTRL8  0x70U
#define OV7725_AWB_CTRL9  0x71U
#define OV7725_AWB_CTRL10 0x72U
#define OV7725_AWB_CTRL11 0x73U
#define OV7725_AWB_CTRL12 0x74U
#define OV7725_AWB_CTRL13 0x75U
#define OV7725_AWB_CTRL14 0x76U
#define OV7725_AWB_CTRL15 0x77U
#define OV7725_AWB_CTRL16 0x78U
#define OV7725_AWB_CTRL17 0x79U
#define OV7725_AWB_CTRL18 0x7AU
#define OV7725_AWB_CTRL19 0x7BU
#define OV7725_AWB_CTRL20 0x7CU
#define OV7725_AWB_CTRL21 0x7DU
#define OV7725_GAM1       0x7EU
#define OV7725_GAM2       0x7FU
#define OV7725_GAM3       0x80U
#define OV7725_GAM4       0x81U
#define OV7725_GAM5       0x82U
#define OV7725_GAM6       0x83U
#define OV7725_GAM7       0x84U
#define OV7725_GAM8       0x85U
#define OV7725_GAM9       0x86U
#define OV7725_GAM10      0x87U
#define OV7725_GAM11      0x88U
#define OV7725_GAM12      0x89U
#define OV7725_GAM13      0x8AU
#define OV7725_GAM14      0x8BU
#define OV7725_GAM15      0x8CU
#define OV7725_SLOP       0x8DU
#define OV7725_DNSTH      0x8EU
#define OV7725_EDGE0      0x8FU
#define OV7725_EDGE1      0x90U
#define OV7725_DNSOFF     0x91U
#define OV7725_EDGE2      0x92U
#define OV7725_EDGE3      0x93U
#define OV7725_MTX1       0x94U
#define OV7725_MTX2       0x95U
#define OV7725_MTX3       0x96U
#define OV7725_MTX4       0x97U
#define OV7725_MTX5       0x98U
#define OV7725_MTX6       0x99U
#define OV7725_MTX_CTRL   0x9AU
#define OV7725_BRIGHT     0x9BU
#define OV7725_CNST       0x9CU
#define OV7725_UVADJ0     0x9EU
#define OV7725_UVADJ1     0x9FU
#define OV7725_SCAL0      0xA0U
#define OV7725_SCAL1      0xA1U
#define OV7725_SCAL2      0xA2U
#define OV7725_SDE        0xA6U
#define OV7725_USAT       0xA7U
#define OV7725_VSAT       0xA8U
#define OV7725_HUECOS     0xA9U
#define OV7725_HUESIN     0xAAU
#define OV7725_SIGN       0xABU
#define OV7725_DSPAUTO    0xACU

#define OV7725_COM10_VSYNC_NEG_MASK    BIT(1)
#define OV7725_COM10_HREF_REVERSE_MASK BIT(3)
#define OV7725_COM10_PCLK_REVERSE_MASK BIT(4)
#define OV7725_COM10_PCLK_OUT_MASK     BIT(5)
#define OV7725_COM10_DATA_NEG_MASK     BIT(7)

struct ov7725_data {
	const struct device *i2c;
	const struct device *reset_gpio;
	uint8_t reset_pin;
	gpio_dt_flags_t reset_flags;
	struct video_format fmt;
	uint8_t i2c_addr;
};

struct ov7725_clock {
	uint32_t input_clk;
	uint32_t framerate;
	uint8_t clkrc;  /*!< Register CLKRC. */
	uint8_t com4;   /*!< Register COM4. */
	uint8_t dm_lnl; /*!< Register DM_LNL. */
};

struct ov7725_pixel_format {
	uint32_t pixel_format;
	uint8_t com7;
};

struct ov7725_reg {
	uint8_t addr;
	uint8_t value;
};

static const struct ov7725_clock ov7725_clock_configs[] = {
	{ .input_clk = 24000000, .framerate = 30,
		.clkrc = 0x01, .com4 = 0x41, .dm_lnl = 0x00 },
	{ .input_clk = 24000000, .framerate = 15,
		.clkrc = 0x03, .com4 = 0x41, .dm_lnl = 0x00 },
	{ .input_clk = 24000000, .framerate = 25,
		.clkrc = 0x01, .com4 = 0x41, .dm_lnl = 0x66 },
	{ .input_clk = 24000000, .framerate = 14,
		.clkrc = 0x03, .com4 = 0x41, .dm_lnl = 0x1a },
	{ .input_clk = 26000000, .framerate = 30,
		.clkrc = 0x01, .com4 = 0x41, .dm_lnl = 0x2b },
	{ .input_clk = 26000000, .framerate = 15,
		.clkrc = 0x03, .com4 = 0x41, .dm_lnl = 0x2b },
	{ .input_clk = 26000000, .framerate = 25,
		.clkrc = 0x01, .com4 = 0x41, .dm_lnl = 0x99 },
	{ .input_clk = 26000000, .framerate = 14,
		.clkrc = 0x03, .com4 = 0x41, .dm_lnl = 0x46 },
	{ .input_clk = 13000000, .framerate = 30,
		.clkrc = 0x00, .com4 = 0x41, .dm_lnl = 0x2b },
	{ .input_clk = 13000000, .framerate = 15,
		.clkrc = 0x01, .com4 = 0x41, .dm_lnl = 0x2b },
	{ .input_clk = 13000000, .framerate = 25,
		.clkrc = 0x00, .com4 = 0x41, .dm_lnl = 0x99 },
	{ .input_clk = 13000000, .framerate = 14,
		.clkrc = 0x01, .com4 = 0x41, .dm_lnl = 0x46 },
};


static const struct ov7725_pixel_format ov7725_pf_configs[] = {
	{ .pixel_format = VIDEO_PIX_FMT_RGB565, .com7 = (1 << 2) | (2) }
};

static const struct ov7725_reg ov7725_init_reg_tb[] = {
	/*Output config*/
	{ OV7725_CLKRC,          0x00 },
	{ OV7725_COM7,           0x06 },
	{ OV7725_HSTART,         0x3f },
	{ OV7725_HSIZE,          0x50 },
	{ OV7725_VSTART,         0x03 },
	{ OV7725_VSIZE,          0x78 },
	{ OV7725_HREF,           0x00 },
	{ OV7725_HOUTSIZE,       0x50 },
	{ OV7725_VOUTSIZE,       0x78 },

	/*DSP control*/
	{ OV7725_TGT_B,          0x7f },
	{ OV7725_FIXGAIN,        0x09 },
	{ OV7725_AWB_CTRL0,      0xe0 },
	{ OV7725_DSP_CTRL1,      0xff },
	{ OV7725_DSP_CTRL2,      0x00 },
	{ OV7725_DSP_CTRL3,      0x00 },
	{ OV7725_DSP_CTRL4,      0x00 },

	/*AGC AEC AWB*/
	{ OV7725_COM8,           0xf0 },
	{ OV7725_COM4,           0x81 },
	{ OV7725_COM6,           0xc5 },
	{ OV7725_COM9,           0x11 },
	{ OV7725_BDBASE,         0x7F },
	{ OV7725_BDMSTEP,        0x03 },
	{ OV7725_AEW,            0x40 },
	{ OV7725_AEB,            0x30 },
	{ OV7725_VPT,            0xa1 },
	{ OV7725_EXHCL,          0x9e },
	{ OV7725_AWB_CTRL3,      0xaa },
	{ OV7725_COM8,           0xff },

	/*matrix shapness brightness contrast*/
	{ OV7725_EDGE1,          0x08 },
	{ OV7725_DNSOFF,         0x01 },
	{ OV7725_EDGE2,          0x03 },
	{ OV7725_EDGE3,          0x00 },
	{ OV7725_MTX1,           0xb0 },
	{ OV7725_MTX2,           0x9d },
	{ OV7725_MTX3,           0x13 },
	{ OV7725_MTX4,           0x16 },
	{ OV7725_MTX5,           0x7b },
	{ OV7725_MTX6,           0x91 },
	{ OV7725_MTX_CTRL,       0x1e },
	{ OV7725_BRIGHT,         0x08 },
	{ OV7725_CNST,           0x20 },
	{ OV7725_UVADJ0,         0x81 },
	{ OV7725_SDE,            0X06 },
	{ OV7725_USAT,           0x65 },
	{ OV7725_VSAT,           0x65 },
	{ OV7725_HUECOS,         0X80 },
	{ OV7725_HUESIN,         0X80 },

	/*GAMMA config*/
	{ OV7725_GAM1,           0x0c },
	{ OV7725_GAM2,           0x16 },
	{ OV7725_GAM3,           0x2a },
	{ OV7725_GAM4,           0x4e },
	{ OV7725_GAM5,           0x61 },
	{ OV7725_GAM6,           0x6f },
	{ OV7725_GAM7,           0x7b },
	{ OV7725_GAM8,           0x86 },
	{ OV7725_GAM9,           0x8e },
	{ OV7725_GAM10,          0x97 },
	{ OV7725_GAM11,          0xa4 },
	{ OV7725_GAM12,          0xaf },
	{ OV7725_GAM13,          0xc5 },
	{ OV7725_GAM14,          0xd7 },
	{ OV7725_GAM15,          0xe8 },
	{ OV7725_SLOP,           0x20 },

	{ OV7725_COM3,           0x40 },
	{ OV7725_COM5,           0xf5 },
	{ OV7725_COM10,          0x02 },
	{ OV7725_COM2,           0x01 }
};

static int ov7725_write_reg(const struct device *dev, uint8_t reg_addr,
			    uint8_t value)
{
	struct ov7725_data *drv_data = dev->data;
	struct i2c_msg msgs[2];

	msgs[0].buf = (uint8_t *)&reg_addr;
	msgs[0].len = 1;
	msgs[0].flags = I2C_MSG_WRITE;

	msgs[1].buf = (uint8_t *)&value;
	msgs[1].len = 1;
	msgs[1].flags = I2C_MSG_WRITE | I2C_MSG_STOP;

	return i2c_transfer(drv_data->i2c, msgs, 2, drv_data->i2c_addr);
}

static int ov7725_read_reg(const struct device *dev, uint8_t reg_addr,
			   uint8_t *value)
{
	struct ov7725_data *drv_data = dev->data;
	struct i2c_msg msgs[2];

	msgs[0].buf = (uint8_t *)&reg_addr;
	msgs[0].len = 1;
	/*
	 * When using I2C to read the registers of the SCCB device,
	 * a stop bit is required after writing the register address
	 */
	msgs[0].flags = I2C_MSG_WRITE | I2C_MSG_STOP;

	msgs[1].buf = (uint8_t *)value;
	msgs[1].len = 1;
	msgs[1].flags = I2C_MSG_READ | I2C_MSG_STOP | I2C_MSG_RESTART;

	return i2c_transfer(drv_data->i2c, msgs, 2, drv_data->i2c_addr);
}

int ov7725_modify_reg(const struct device *dev,
		      uint8_t reg_addr,
		      uint8_t clear_mask,
		      uint8_t value)
{
	int ret;
	uint8_t set_value;

	ret = ov7725_read_reg(dev, reg_addr, &set_value);

	if (ret == 0) {
		set_value = (set_value & (~clear_mask)) |
				(set_value & clear_mask);
		ret = ov7725_write_reg(dev, reg_addr, set_value);
	}


	return ret;
}

static int ov7725_write_all(const struct device *dev,
			    const struct ov7725_reg *regs,
			    uint16_t reg_num)
{
	uint16_t i = 0;

	for (i = 0; i < reg_num; i++) {
		int err;

		err = ov7725_write_reg(dev, regs[i].addr, regs[i].value);
		if (err) {
			return err;
		}
	}

	return 0;
}

static int ov7725_set_clock(const struct device *dev,
				unsigned int framerate,
				unsigned int input_clk)
{
	for (unsigned int i = 0; i < ARRAY_SIZE(ov7725_clock_configs); i++) {
		if ((ov7725_clock_configs[i].framerate == framerate) &&
			(ov7725_clock_configs[i].input_clk == input_clk)) {
			ov7725_write_reg(dev, OV7725_CLKRC,
						ov7725_clock_configs[i].clkrc);
			ov7725_modify_reg(dev, OV7725_COM4, 0xc0,
						ov7725_clock_configs[i].com4);
			ov7725_write_reg(dev, OV7725_EXHCL, 0x00);
			ov7725_write_reg(dev, OV7725_DM_LNL,
						ov7725_clock_configs[i].dm_lnl);
			ov7725_write_reg(dev, OV7725_DM_LNH, 0x00);
			ov7725_write_reg(dev, OV7725_ADVFL, 0x00);
			ov7725_write_reg(dev, OV7725_ADVFH, 0x00);
			return ov7725_write_reg(dev, OV7725_COM5, 0x65);
		}
	}

	return -1;
}

static int ov7725_set_fmt(const struct device *dev,
			  enum video_endpoint_id ep,
			  struct video_format *fmt)
{
	struct ov7725_data *drv_data = dev->data;
	uint8_t com10 = 0;
	uint16_t width, height;
	uint16_t hstart, vstart, hsize;
	int ret;

	/* we only support one format for now (VGA RGB565) */
	if (fmt->pixelformat != VIDEO_PIX_FMT_RGB565 || fmt->height != 480 ||
	    fmt->width != 640) {
		return -ENOTSUP;
	}

	width = fmt->width;
	height = fmt->height;

	if (!memcmp(&drv_data->fmt, fmt, sizeof(drv_data->fmt))) {
		/* nothing to do */
		return 0;
	}

	drv_data->fmt = *fmt;

	/* Configure Sensor */
	ret = ov7725_write_all(dev, ov7725_init_reg_tb,
				ARRAY_SIZE(ov7725_init_reg_tb));
	if (ret) {
		LOG_ERR("Unable to write ov7725 config");
		return ret;
	}

	/* Set clock : framerate 30fps, input clock 24M*/
	ov7725_set_clock(dev, 30, 24000000);

	/* Set output format */
	for (uint8_t i = 0; i < ARRAY_SIZE(ov7725_pf_configs); i++) {
		if (ov7725_pf_configs[i].pixel_format == fmt->pixelformat) {
			ret =  ov7725_modify_reg(dev,
						OV7725_COM7,
						0x1FU,
						ov7725_pf_configs[i].com7);
			if (ret) {
				LOG_ERR("Unable to write ov7725 pixel format");
				return ret;
			}
		}
	}

	ov7725_modify_reg(dev, OV7725_COM7, (1 << 5), (0 << 5));

	com10 |= OV7725_COM10_VSYNC_NEG_MASK;
	ov7725_write_reg(dev, OV7725_COM10, com10);

	/* Don't swap output MSB/LSB. */
	ov7725_write_reg(dev, OV7725_COM3, 0x00);

	/*
	 * Output drive capability
	 * 0: 1X
	 * 1: 2X
	 * 2: 3X
	 * 3: 4X
	 */
	ov7725_modify_reg(dev, OV7725_COM2, 0x03, 0x03);

	/* Resolution and timing. */
	hstart = 0x22U << 2U;
	vstart = 0x07U << 1U;
	hsize = width + 16U;

	/* Set the window size. */
	ov7725_write_reg(dev, OV7725_HSTART, hstart >> 2U);
	ov7725_write_reg(dev, OV7725_HSIZE, hsize >> 2U);
	ov7725_write_reg(dev, OV7725_VSTART, vstart >> 1U);
	ov7725_write_reg(dev, OV7725_VSIZE, height >> 1U);
	ov7725_write_reg(dev, OV7725_HOUTSIZE, width >> 2U);
	ov7725_write_reg(dev, OV7725_VOUTSIZE, height >> 1U);
	ov7725_write_reg(dev, OV7725_HREF,
			 ((vstart & 1U) << 6U) |
			 ((hstart & 3U) << 4U) |
			 ((height & 1U) << 2U) |
			 ((hsize & 3U) << 0U));
	return ov7725_write_reg(dev, OV7725_EXHCH,
					((height & 1U) << 2U) |
					((width & 3U) << 0U));
}

static int ov7725_get_fmt(const struct device *dev,
			  enum video_endpoint_id ep,
			  struct video_format *fmt)
{
	struct ov7725_data *drv_data = dev->data;

	*fmt = drv_data->fmt;

	return 0;
}

static int ov7725_stream_start(const struct device *dev)
{
	return 0;
}

static int ov7725_stream_stop(const struct device *dev)
{
	return 0;
}

static const struct video_format_cap fmts[] = {
	{
		.pixelformat = VIDEO_PIX_FMT_RGB565,
		.width_min = 640,
		.width_max = 640,
		.height_min = 480,
		.height_max = 480,
		.width_step = 0,
		.height_step = 0,
	},
	{ 0 }
};

static int ov7725_get_caps(const struct device *dev,
			   enum video_endpoint_id ep,
			   struct video_caps *caps)
{
	caps->format_caps = fmts;
	return 0;
}

static const struct video_driver_api ov7725_driver_api = {
	.set_format = ov7725_set_fmt,
	.get_format = ov7725_get_fmt,
	.get_caps = ov7725_get_caps,
	.stream_start = ov7725_stream_start,
	.stream_stop = ov7725_stream_stop,
};

static int ov7725_init(const struct device *dev)
{
	struct ov7725_data *drv_data = dev->data;
	struct video_format fmt;
	uint8_t pid, ver;
	int ret;


	if (drv_data->reset_gpio) {
		ret = gpio_pin_configure(drv_data->reset_gpio,
					 drv_data->reset_pin,
					 GPIO_OUTPUT_ACTIVE |
					 drv_data->reset_flags);
		if (ret) {
			return ret;
		}

		gpio_pin_set(drv_data->reset_gpio,
			     drv_data->reset_pin,
			     0);
		k_sleep(K_MSEC(1));
		gpio_pin_set(drv_data->reset_gpio,
			     drv_data->reset_pin,
			     1);
		k_sleep(K_MSEC(1));
	}

	/* Identify the device. */
	ret = ov7725_read_reg(dev, OV7725_PID, &pid);
	if (ret) {
		LOG_ERR("Unable to read PID");
		return -ENODEV;
	}

	ret = ov7725_read_reg(dev, OV7725_VER, &ver);
	if (ret) {
		LOG_ERR("Unable to read VER");
		return -ENODEV;
	}

	if (OV7725_REVISION != (((uint32_t)pid << 8U) | (uint32_t)ver)) {
		LOG_ERR("OV7725 Get Vision fail\n");
		return -ENODEV;
	}

	/* Device identify OK, perform software reset. */
	ov7725_write_reg(dev, OV7725_COM7, 0x80);

	k_sleep(K_MSEC(2));

	/* set default/init format VGA RGB565 */
	fmt.pixelformat = VIDEO_PIX_FMT_RGB565;
	fmt.width = 640;
	fmt.height = 480;
	fmt.pitch = 640 * 2;
	ret = ov7725_set_fmt(dev, VIDEO_EP_OUT, &fmt);
	if (ret) {
		LOG_ERR("Unable to configure default format");
		return -EIO;
	}

	return 0;
}

/* Unique Instance */
static struct ov7725_data ov7725_data_0;

static int ov7725_init_0(const struct device *dev)
{
	struct ov7725_data *drv_data = dev->data;
	char *gpio_name = DT_INST_GPIO_LABEL(0, reset_gpios);

	drv_data->reset_pin = DT_INST_GPIO_PIN(0, reset_gpios),
	drv_data->reset_flags = DT_INST_GPIO_FLAGS(0, reset_gpios),

	drv_data->i2c = device_get_binding(DT_INST_BUS_LABEL(0));
	if (drv_data->i2c == NULL) {
		LOG_ERR("Failed to get pointer to %s device!",
			DT_INST_LABEL(0));
		return -EINVAL;
	}

	if (gpio_name) {
		drv_data->reset_gpio = device_get_binding(gpio_name);
		if (drv_data->reset_gpio == NULL) {
			LOG_ERR("Failed to get pointer to %s device!",
				gpio_name);
		}
	}

	drv_data->i2c_addr = DT_INST_REG_ADDR(0);

	return ov7725_init(dev);
}

DEVICE_DT_INST_DEFINE(0, &ov7725_init_0, device_pm_control_nop,
		    &ov7725_data_0, NULL,
		    POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
		    &ov7725_driver_api);