1 /*
2 * Copyright (c) 2021 HPMicro
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include "hpm_ov7725.h"
9
10 static const uint8_t ov7725_default_regs[][2] = {
11 {COM3, COM3_SWAP_YUV},
12 {COM7, COM7_RES_VGA | COM7_FMT_RGB565},
13
14 {COM4, 0x01}, /* bypass PLL */
15 {CLKRC, 0xC0}, /* Res/Bypass pre-scalar */
16
17 /*
18 * VGA Window Size
19 */
20 {HSTART, 0x23},
21 {HSIZE, 0xA0},
22 {VSTART, 0x07},
23 {VSIZE, 0xF0},
24 {HREF, 0x00},
25
26 /*
27 * Scale down to VGA Resolution
28 */
29 {HOUTSIZE, 0xA0},
30 {VOUTSIZE, 0xF0},
31
32 {COM12, 0x03},
33 {EXHCH, 0x00},
34 {TGT_B, 0x7F},
35 {FIXGAIN, 0x09},
36 {AWB_CTRL0, 0xE0},
37 {DSP_CTRL1, 0xFF},
38
39 {DSP_CTRL2, DSP_CTRL2_VDCW_EN | DSP_CTRL2_HDCW_EN | DSP_CTRL2_HZOOM_EN | DSP_CTRL2_VZOOM_EN},
40
41 {DSP_CTRL3, 0x00},
42 {DSP_CTRL4, 0x00},
43 {DSPAUTO, 0xFF},
44
45 {COM8, 0xF0},
46 {COM6, 0xC5},
47 {COM9, 0x11},
48 {COM10, COM10_VSYNC_NEG | COM10_PCLK_MASK}, /* Invert VSYNC and MASK PCLK */
49 {BDBASE, 0x7F},
50 {DBSTEP, 0x03},
51 {AEW, 0x70},
52 {AEB, 0x43},
53 {VPT, 0xA1},
54 {EXHCL, 0x00},
55 {AWB_CTRL3, 0xAA},
56 {COM8, 0xFF},
57
58 /*
59 * Gamma
60 */
61 {GAM1, 0x0C},
62 {GAM2, 0x16},
63 {GAM3, 0x2A},
64 {GAM4, 0x4E},
65 {GAM5, 0x61},
66 {GAM6, 0x6F},
67 {GAM7, 0x7B},
68 {GAM8, 0x86},
69 {GAM9, 0x8E},
70 {GAM10, 0x97},
71 {GAM11, 0xA4},
72 {GAM12, 0xAF},
73 {GAM13, 0xC5},
74 {GAM14, 0xD7},
75 {GAM15, 0xE8},
76
77 {SLOP, 0x20},
78 {EDGE1, 0x05},
79 {EDGE2, 0x03},
80 {EDGE3, 0x00},
81 {DNSOFF, 0x01},
82
83 {MTX1, 0xB0},
84 {MTX2, 0x9D},
85 {MTX3, 0x13},
86 {MTX4, 0x16},
87 {MTX5, 0x7B},
88 {MTX6, 0x91},
89 {MTX_CTRL, 0x1E},
90
91 {BRIGHTNESS, 0x08},
92 {CONTRAST, 0x30},
93 {UVADJ0, 0x81},
94 {SDE, (SDE_CONT_BRIGHT_EN | SDE_SATURATION_EN)},
95
96 /*
97 * For 30 fps/60Hz
98 */
99 {DM_LNL, 0x00},
100 {DM_LNH, 0x00},
101 {BDBASE, 0x7F},
102 {DBSTEP, 0x03},
103
104 /*
105 * Lens Correction, should be tuned with real camera module
106 */
107 {LC_RADI, 0x10},
108 {LC_COEF, 0x10},
109 {LC_COEFB, 0x14},
110 {LC_COEFR, 0x17},
111 {LC_CTR, 0x05},
112 {COM5, 0xF5},
113 };
114
ov7725_read_register(camera_context_t * context,uint8_t reg,uint8_t * buf)115 hpm_stat_t ov7725_read_register(camera_context_t *context, uint8_t reg, uint8_t *buf)
116 {
117 return i2c_master_address_read(context->ptr, OV7725_I2C_ADDR, ®, 1, buf, 1);
118 }
119
ov7725_write_register(camera_context_t * context,uint8_t reg,uint8_t val)120 hpm_stat_t ov7725_write_register(camera_context_t *context, uint8_t reg, uint8_t val)
121 {
122 return i2c_master_address_write(context->ptr, OV7725_I2C_ADDR, ®, 1, &val, 1);
123 }
124
ov7725_restore_default_values(camera_context_t * context)125 void ov7725_restore_default_values(camera_context_t *context)
126 {
127 uint32_t i;
128 for (i = 0; i < (ARRAY_SIZE(ov7725_default_regs)); i++) {
129 ov7725_write_register(context, ov7725_default_regs[i][0], ov7725_default_regs[i][1]);
130 }
131 }
132
ov7725_software_reset(camera_context_t * context)133 hpm_stat_t ov7725_software_reset(camera_context_t *context)
134 {
135 hpm_stat_t stat = status_success;
136
137 stat = ov7725_write_register(context, COM7, COM7_RESET);
138 if (stat != status_success) {
139 return stat;
140 }
141
142 return stat;
143 }
144
ov7725_check_chip_id(camera_context_t * context)145 hpm_stat_t ov7725_check_chip_id(camera_context_t *context)
146 {
147 hpm_stat_t stat = status_success;
148 uint8_t val_h = 0;
149 uint8_t val_l = 0;
150
151 HPM_CHECK_RET(ov7725_read_register(context, OV7725_CHIP_ID_HIGH_BYTE_ADDR, &val_h));
152 HPM_CHECK_RET(ov7725_read_register(context, OV7725_CHIP_ID_LOW_BYTE_ADDR, &val_l));
153
154 if (val_h != OV7725_CHIP_ID_HIGH_BYTE_VALUE) {
155 return status_fail;
156 }
157
158 if (val_l != OV7725_CHIP_ID_LOW_BYTE_VALUE) {
159 return status_fail;
160 }
161
162 return stat;
163 }
164
ov7725_set_framesize(camera_context_t * context,uint16_t width,uint16_t height)165 static hpm_stat_t ov7725_set_framesize(camera_context_t *context, uint16_t width, uint16_t height)
166 {
167 hpm_stat_t stat = status_success;
168 uint32_t hstart = 0x22U << 2;
169 uint32_t vstart = 0x7U << 1;
170 uint32_t hsize = width + 16;
171
172 stat |= ov7725_write_register(context, HSTART, hstart >> 2);
173 stat |= ov7725_write_register(context, HSIZE, hsize >> 2);
174 stat |= ov7725_write_register(context, VSTART, vstart >> 1);
175 stat |= ov7725_write_register(context, VSIZE, height >> 1);
176
177 stat |= ov7725_write_register(context, HOUTSIZE, width >> 2);
178 stat |= ov7725_write_register(context, VOUTSIZE, height >> 1);
179
180 stat |= ov7725_write_register(context, HREF,
181 ((vstart & 1) << 6) | ((hstart & 3) << 4) | ((height & 1) << 2) | ((hsize & 3) << 0));
182
183 stat = ov7725_write_register(context, EXHCH, ((height & 0x1) << 2) | (width & 0x3));
184 if (stat != status_success) {
185 return stat;
186 }
187 return stat;
188 }
189
ov7725_set_pixel_format(camera_context_t * context,display_pixel_format_t pixel_format)190 hpm_stat_t ov7725_set_pixel_format(camera_context_t *context, display_pixel_format_t pixel_format)
191 {
192 hpm_stat_t stat = status_success;
193 uint8_t val = 0;
194
195 stat |= ov7725_read_register(context, COM7, &val);
196 val &= ~0x1F;
197 switch (pixel_format) {
198 case display_pixel_format_rgb565:
199 val |= COM7_FMT_RGB565;
200 break;
201 case display_pixel_format_rgb444:
202 val |= COM7_FMT_RGB444;
203 break;
204 default:
205 stat = status_invalid_argument;
206 break;
207 }
208 if (stat != status_success) {
209 return stat;
210 }
211
212 stat |= ov7725_write_register(context, COM7, val);
213 if (stat != status_success) {
214 return stat;
215 }
216 return stat;
217 }
218
ov7725_init(camera_context_t * context,camera_config_t * ov_config)219 hpm_stat_t ov7725_init(camera_context_t *context, camera_config_t *ov_config)
220 {
221 hpm_stat_t stat = status_success;
222
223 ov7725_restore_default_values(context);
224
225 stat |= ov7725_write_register(context, CLKRC, 0x2);
226 stat |= ov7725_write_register(context, COM4, 0x41);
227 stat |= ov7725_write_register(context, EXHCL, 0);
228 stat |= ov7725_write_register(context, DM_LNL, 0);
229 stat |= ov7725_write_register(context, DM_LNH, 0);
230 stat |= ov7725_write_register(context, ADVFL, 0);
231 stat |= ov7725_write_register(context, ADVFH, 0);
232 stat |= ov7725_write_register(context, COM5, 0x65);
233 stat |= ov7725_write_register(context, COM10, 0);
234 stat |= ov7725_write_register(context, COM3, 0);
235 stat |= ov7725_write_register(context, COM2, 0x3);
236 stat |= ov7725_set_framesize(context, ov_config->width, ov_config->height);
237 stat |= ov7725_set_pixel_format(context, ov_config->pixel_format);
238 if (stat != status_success) {
239 return stat;
240 }
241
242 return stat;
243 }
244
ov7725_power_up(camera_context_t * context)245 void ov7725_power_up(camera_context_t *context)
246 {
247 assert((context->delay_ms != NULL) && (context->write_rst != NULL) && (context->write_pwdn != NULL));
248
249 context->write_rst(OV7725_RST_ACTIVE);
250 context->write_pwdn(OV7725_PWDN_ACTIVE);
251 context->delay_ms(5);
252 context->write_pwdn(OV7725_PWDN_INACTIVE);
253 context->delay_ms(2);
254 context->write_rst(OV7725_RST_INACTIVE);
255 context->delay_ms(20);
256 }
257