1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * ov772x Camera Driver |
4 | * |
5 | * Copyright (C) 2017 Jacopo Mondi <jacopo+renesas@jmondi.org> |
6 | * |
7 | * Copyright (C) 2008 Renesas Solutions Corp. |
8 | * Kuninori Morimoto <morimoto.kuninori@renesas.com> |
9 | * |
10 | * Based on ov7670 and soc_camera_platform driver, |
11 | * |
12 | * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> |
13 | * Copyright (C) 2008 Magnus Damm |
14 | * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> |
15 | */ |
16 | |
17 | #include <linux/clk.h> |
18 | #include <linux/delay.h> |
19 | #include <linux/gpio/consumer.h> |
20 | #include <linux/i2c.h> |
21 | #include <linux/init.h> |
22 | #include <linux/kernel.h> |
23 | #include <linux/module.h> |
24 | #include <linux/regmap.h> |
25 | #include <linux/slab.h> |
26 | #include <linux/v4l2-mediabus.h> |
27 | #include <linux/videodev2.h> |
28 | |
29 | #include <media/i2c/ov772x.h> |
30 | |
31 | #include <media/v4l2-ctrls.h> |
32 | #include <media/v4l2-device.h> |
33 | #include <media/v4l2-event.h> |
34 | #include <media/v4l2-fwnode.h> |
35 | #include <media/v4l2-image-sizes.h> |
36 | #include <media/v4l2-subdev.h> |
37 | |
38 | /* |
39 | * register offset |
40 | */ |
41 | #define GAIN 0x00 /* AGC - Gain control gain setting */ |
42 | #define BLUE 0x01 /* AWB - Blue channel gain setting */ |
43 | #define RED 0x02 /* AWB - Red channel gain setting */ |
44 | #define GREEN 0x03 /* AWB - Green channel gain setting */ |
45 | #define COM1 0x04 /* Common control 1 */ |
46 | #define BAVG 0x05 /* U/B Average Level */ |
47 | #define GAVG 0x06 /* Y/Gb Average Level */ |
48 | #define RAVG 0x07 /* V/R Average Level */ |
49 | #define AECH 0x08 /* Exposure Value - AEC MSBs */ |
50 | #define COM2 0x09 /* Common control 2 */ |
51 | #define PID 0x0A /* Product ID Number MSB */ |
52 | #define VER 0x0B /* Product ID Number LSB */ |
53 | #define COM3 0x0C /* Common control 3 */ |
54 | #define COM4 0x0D /* Common control 4 */ |
55 | #define COM5 0x0E /* Common control 5 */ |
56 | #define COM6 0x0F /* Common control 6 */ |
57 | #define AEC 0x10 /* Exposure Value */ |
58 | #define CLKRC 0x11 /* Internal clock */ |
59 | #define COM7 0x12 /* Common control 7 */ |
60 | #define COM8 0x13 /* Common control 8 */ |
61 | #define COM9 0x14 /* Common control 9 */ |
62 | #define COM10 0x15 /* Common control 10 */ |
63 | #define REG16 0x16 /* Register 16 */ |
64 | #define HSTART 0x17 /* Horizontal sensor size */ |
65 | #define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */ |
66 | #define VSTART 0x19 /* Vertical frame (row) start high 8-bit */ |
67 | #define VSIZE 0x1A /* Vertical sensor size */ |
68 | #define PSHFT 0x1B /* Data format - pixel delay select */ |
69 | #define MIDH 0x1C /* Manufacturer ID byte - high */ |
70 | #define MIDL 0x1D /* Manufacturer ID byte - low */ |
71 | #define LAEC 0x1F /* Fine AEC value */ |
72 | #define COM11 0x20 /* Common control 11 */ |
73 | #define BDBASE 0x22 /* Banding filter Minimum AEC value */ |
74 | #define DBSTEP 0x23 /* Banding filter Maximum Setp */ |
75 | #define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ |
76 | #define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ |
77 | #define VPT 0x26 /* AGC/AEC Fast mode operating region */ |
78 | #define REG28 0x28 /* Register 28 */ |
79 | #define HOUTSIZE 0x29 /* Horizontal data output size MSBs */ |
80 | #define EXHCH 0x2A /* Dummy pixel insert MSB */ |
81 | #define EXHCL 0x2B /* Dummy pixel insert LSB */ |
82 | #define VOUTSIZE 0x2C /* Vertical data output size MSBs */ |
83 | #define ADVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ |
84 | #define ADVFH 0x2E /* MSG of insert dummy lines in Vertical direction */ |
85 | #define YAVE 0x2F /* Y/G Channel Average value */ |
86 | #define LUMHTH 0x30 /* Histogram AEC/AGC Luminance high level threshold */ |
87 | #define LUMLTH 0x31 /* Histogram AEC/AGC Luminance low level threshold */ |
88 | #define HREF 0x32 /* Image start and size control */ |
89 | #define DM_LNL 0x33 /* Dummy line low 8 bits */ |
90 | #define DM_LNH 0x34 /* Dummy line high 8 bits */ |
91 | #define ADOFF_B 0x35 /* AD offset compensation value for B channel */ |
92 | #define ADOFF_R 0x36 /* AD offset compensation value for R channel */ |
93 | #define ADOFF_GB 0x37 /* AD offset compensation value for Gb channel */ |
94 | #define ADOFF_GR 0x38 /* AD offset compensation value for Gr channel */ |
95 | #define OFF_B 0x39 /* Analog process B channel offset value */ |
96 | #define OFF_R 0x3A /* Analog process R channel offset value */ |
97 | #define OFF_GB 0x3B /* Analog process Gb channel offset value */ |
98 | #define OFF_GR 0x3C /* Analog process Gr channel offset value */ |
99 | #define COM12 0x3D /* Common control 12 */ |
100 | #define COM13 0x3E /* Common control 13 */ |
101 | #define COM14 0x3F /* Common control 14 */ |
102 | #define COM15 0x40 /* Common control 15*/ |
103 | #define COM16 0x41 /* Common control 16 */ |
104 | #define TGT_B 0x42 /* BLC blue channel target value */ |
105 | #define TGT_R 0x43 /* BLC red channel target value */ |
106 | #define TGT_GB 0x44 /* BLC Gb channel target value */ |
107 | #define TGT_GR 0x45 /* BLC Gr channel target value */ |
108 | /* for ov7720 */ |
109 | #define LCC0 0x46 /* Lens correction control 0 */ |
110 | #define LCC1 0x47 /* Lens correction option 1 - X coordinate */ |
111 | #define LCC2 0x48 /* Lens correction option 2 - Y coordinate */ |
112 | #define LCC3 0x49 /* Lens correction option 3 */ |
113 | #define LCC4 0x4A /* Lens correction option 4 - radius of the circular */ |
114 | #define LCC5 0x4B /* Lens correction option 5 */ |
115 | #define LCC6 0x4C /* Lens correction option 6 */ |
116 | /* for ov7725 */ |
117 | #define LC_CTR 0x46 /* Lens correction control */ |
118 | #define LC_XC 0x47 /* X coordinate of lens correction center relative */ |
119 | #define LC_YC 0x48 /* Y coordinate of lens correction center relative */ |
120 | #define LC_COEF 0x49 /* Lens correction coefficient */ |
121 | #define LC_RADI 0x4A /* Lens correction radius */ |
122 | #define LC_COEFB 0x4B /* Lens B channel compensation coefficient */ |
123 | #define LC_COEFR 0x4C /* Lens R channel compensation coefficient */ |
124 | |
125 | #define FIXGAIN 0x4D /* Analog fix gain amplifer */ |
126 | #define AREF0 0x4E /* Sensor reference control */ |
127 | #define AREF1 0x4F /* Sensor reference current control */ |
128 | #define AREF2 0x50 /* Analog reference control */ |
129 | #define AREF3 0x51 /* ADC reference control */ |
130 | #define AREF4 0x52 /* ADC reference control */ |
131 | #define AREF5 0x53 /* ADC reference control */ |
132 | #define AREF6 0x54 /* Analog reference control */ |
133 | #define AREF7 0x55 /* Analog reference control */ |
134 | #define UFIX 0x60 /* U channel fixed value output */ |
135 | #define VFIX 0x61 /* V channel fixed value output */ |
136 | #define AWBB_BLK 0x62 /* AWB option for advanced AWB */ |
137 | #define AWB_CTRL0 0x63 /* AWB control byte 0 */ |
138 | #define DSP_CTRL1 0x64 /* DSP control byte 1 */ |
139 | #define DSP_CTRL2 0x65 /* DSP control byte 2 */ |
140 | #define DSP_CTRL3 0x66 /* DSP control byte 3 */ |
141 | #define DSP_CTRL4 0x67 /* DSP control byte 4 */ |
142 | #define AWB_BIAS 0x68 /* AWB BLC level clip */ |
143 | #define AWB_CTRL1 0x69 /* AWB control 1 */ |
144 | #define AWB_CTRL2 0x6A /* AWB control 2 */ |
145 | #define AWB_CTRL3 0x6B /* AWB control 3 */ |
146 | #define AWB_CTRL4 0x6C /* AWB control 4 */ |
147 | #define AWB_CTRL5 0x6D /* AWB control 5 */ |
148 | #define AWB_CTRL6 0x6E /* AWB control 6 */ |
149 | #define AWB_CTRL7 0x6F /* AWB control 7 */ |
150 | #define AWB_CTRL8 0x70 /* AWB control 8 */ |
151 | #define AWB_CTRL9 0x71 /* AWB control 9 */ |
152 | #define AWB_CTRL10 0x72 /* AWB control 10 */ |
153 | #define AWB_CTRL11 0x73 /* AWB control 11 */ |
154 | #define AWB_CTRL12 0x74 /* AWB control 12 */ |
155 | #define AWB_CTRL13 0x75 /* AWB control 13 */ |
156 | #define AWB_CTRL14 0x76 /* AWB control 14 */ |
157 | #define AWB_CTRL15 0x77 /* AWB control 15 */ |
158 | #define AWB_CTRL16 0x78 /* AWB control 16 */ |
159 | #define AWB_CTRL17 0x79 /* AWB control 17 */ |
160 | #define AWB_CTRL18 0x7A /* AWB control 18 */ |
161 | #define AWB_CTRL19 0x7B /* AWB control 19 */ |
162 | #define AWB_CTRL20 0x7C /* AWB control 20 */ |
163 | #define AWB_CTRL21 0x7D /* AWB control 21 */ |
164 | #define GAM1 0x7E /* Gamma Curve 1st segment input end point */ |
165 | #define GAM2 0x7F /* Gamma Curve 2nd segment input end point */ |
166 | #define GAM3 0x80 /* Gamma Curve 3rd segment input end point */ |
167 | #define GAM4 0x81 /* Gamma Curve 4th segment input end point */ |
168 | #define GAM5 0x82 /* Gamma Curve 5th segment input end point */ |
169 | #define GAM6 0x83 /* Gamma Curve 6th segment input end point */ |
170 | #define GAM7 0x84 /* Gamma Curve 7th segment input end point */ |
171 | #define GAM8 0x85 /* Gamma Curve 8th segment input end point */ |
172 | #define GAM9 0x86 /* Gamma Curve 9th segment input end point */ |
173 | #define GAM10 0x87 /* Gamma Curve 10th segment input end point */ |
174 | #define GAM11 0x88 /* Gamma Curve 11th segment input end point */ |
175 | #define GAM12 0x89 /* Gamma Curve 12th segment input end point */ |
176 | #define GAM13 0x8A /* Gamma Curve 13th segment input end point */ |
177 | #define GAM14 0x8B /* Gamma Curve 14th segment input end point */ |
178 | #define GAM15 0x8C /* Gamma Curve 15th segment input end point */ |
179 | #define SLOP 0x8D /* Gamma curve highest segment slope */ |
180 | #define DNSTH 0x8E /* De-noise threshold */ |
181 | #define EDGE_STRNGT 0x8F /* Edge strength control when manual mode */ |
182 | #define EDGE_TRSHLD 0x90 /* Edge threshold control when manual mode */ |
183 | #define DNSOFF 0x91 /* Auto De-noise threshold control */ |
184 | #define EDGE_UPPER 0x92 /* Edge strength upper limit when Auto mode */ |
185 | #define EDGE_LOWER 0x93 /* Edge strength lower limit when Auto mode */ |
186 | #define MTX1 0x94 /* Matrix coefficient 1 */ |
187 | #define MTX2 0x95 /* Matrix coefficient 2 */ |
188 | #define MTX3 0x96 /* Matrix coefficient 3 */ |
189 | #define MTX4 0x97 /* Matrix coefficient 4 */ |
190 | #define MTX5 0x98 /* Matrix coefficient 5 */ |
191 | #define MTX6 0x99 /* Matrix coefficient 6 */ |
192 | #define MTX_CTRL 0x9A /* Matrix control */ |
193 | #define BRIGHT 0x9B /* Brightness control */ |
194 | #define CNTRST 0x9C /* Contrast contrast */ |
195 | #define CNTRST_CTRL 0x9D /* Contrast contrast center */ |
196 | #define UVAD_J0 0x9E /* Auto UV adjust contrast 0 */ |
197 | #define UVAD_J1 0x9F /* Auto UV adjust contrast 1 */ |
198 | #define SCAL0 0xA0 /* Scaling control 0 */ |
199 | #define SCAL1 0xA1 /* Scaling control 1 */ |
200 | #define SCAL2 0xA2 /* Scaling control 2 */ |
201 | #define FIFODLYM 0xA3 /* FIFO manual mode delay control */ |
202 | #define FIFODLYA 0xA4 /* FIFO auto mode delay control */ |
203 | #define SDE 0xA6 /* Special digital effect control */ |
204 | #define USAT 0xA7 /* U component saturation control */ |
205 | #define VSAT 0xA8 /* V component saturation control */ |
206 | /* for ov7720 */ |
207 | #define HUE0 0xA9 /* Hue control 0 */ |
208 | #define HUE1 0xAA /* Hue control 1 */ |
209 | /* for ov7725 */ |
210 | #define HUECOS 0xA9 /* Cosine value */ |
211 | #define HUESIN 0xAA /* Sine value */ |
212 | |
213 | #define SIGN 0xAB /* Sign bit for Hue and contrast */ |
214 | #define DSPAUTO 0xAC /* DSP auto function ON/OFF control */ |
215 | |
216 | /* |
217 | * register detail |
218 | */ |
219 | |
220 | /* COM2 */ |
221 | #define SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ |
222 | /* Output drive capability */ |
223 | #define OCAP_1x 0x00 /* 1x */ |
224 | #define OCAP_2x 0x01 /* 2x */ |
225 | #define OCAP_3x 0x02 /* 3x */ |
226 | #define OCAP_4x 0x03 /* 4x */ |
227 | |
228 | /* COM3 */ |
229 | #define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML) |
230 | #define IMG_MASK (VFLIP_IMG | HFLIP_IMG | SCOLOR_TEST) |
231 | |
232 | #define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */ |
233 | #define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */ |
234 | #define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */ |
235 | #define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */ |
236 | #define SWAP_ML 0x08 /* Swap output MSB/LSB */ |
237 | /* Tri-state option for output clock */ |
238 | #define NOTRI_CLOCK 0x04 /* 0: Tri-state at this period */ |
239 | /* 1: No tri-state at this period */ |
240 | /* Tri-state option for output data */ |
241 | #define NOTRI_DATA 0x02 /* 0: Tri-state at this period */ |
242 | /* 1: No tri-state at this period */ |
243 | #define SCOLOR_TEST 0x01 /* Sensor color bar test pattern */ |
244 | |
245 | /* COM4 */ |
246 | /* PLL frequency control */ |
247 | #define PLL_BYPASS 0x00 /* 00: Bypass PLL */ |
248 | #define PLL_4x 0x40 /* 01: PLL 4x */ |
249 | #define PLL_6x 0x80 /* 10: PLL 6x */ |
250 | #define PLL_8x 0xc0 /* 11: PLL 8x */ |
251 | /* AEC evaluate window */ |
252 | #define AEC_FULL 0x00 /* 00: Full window */ |
253 | #define AEC_1p2 0x10 /* 01: 1/2 window */ |
254 | #define AEC_1p4 0x20 /* 10: 1/4 window */ |
255 | #define AEC_2p3 0x30 /* 11: Low 2/3 window */ |
256 | #define COM4_RESERVED 0x01 /* Reserved bit */ |
257 | |
258 | /* COM5 */ |
259 | #define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */ |
260 | #define AFR_SPPED 0x40 /* Auto frame rate control speed selection */ |
261 | /* Auto frame rate max rate control */ |
262 | #define AFR_NO_RATE 0x00 /* No reduction of frame rate */ |
263 | #define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */ |
264 | #define AFR_1p4 0x20 /* Max reduction to 1/4 frame rate */ |
265 | #define AFR_1p8 0x30 /* Max reduction to 1/8 frame rate */ |
266 | /* Auto frame rate active point control */ |
267 | #define AF_2x 0x00 /* Add frame when AGC reaches 2x gain */ |
268 | #define AF_4x 0x04 /* Add frame when AGC reaches 4x gain */ |
269 | #define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */ |
270 | #define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */ |
271 | /* AEC max step control */ |
272 | #define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */ |
273 | /* 1 : No limit to AEC increase step */ |
274 | /* CLKRC */ |
275 | /* Input clock divider register */ |
276 | #define CLKRC_RESERVED 0x80 /* Reserved bit */ |
277 | #define CLKRC_DIV(n) ((n) - 1) |
278 | |
279 | /* COM7 */ |
280 | /* SCCB Register Reset */ |
281 | #define SCCB_RESET 0x80 /* 0 : No change */ |
282 | /* 1 : Resets all registers to default */ |
283 | /* Resolution selection */ |
284 | #define SLCT_MASK 0x40 /* Mask of VGA or QVGA */ |
285 | #define SLCT_VGA 0x00 /* 0 : VGA */ |
286 | #define SLCT_QVGA 0x40 /* 1 : QVGA */ |
287 | #define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */ |
288 | #define SENSOR_RAW 0x10 /* Sensor RAW */ |
289 | /* RGB output format control */ |
290 | #define FMT_MASK 0x0c /* Mask of color format */ |
291 | #define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */ |
292 | #define FMT_RGB565 0x04 /* 01 : RGB 565 */ |
293 | #define FMT_RGB555 0x08 /* 10 : RGB 555 */ |
294 | #define FMT_RGB444 0x0c /* 11 : RGB 444 */ |
295 | /* Output format control */ |
296 | #define OFMT_MASK 0x03 /* Mask of output format */ |
297 | #define OFMT_YUV 0x00 /* 00 : YUV */ |
298 | #define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */ |
299 | #define OFMT_RGB 0x02 /* 10 : RGB */ |
300 | #define OFMT_BRAW 0x03 /* 11 : Bayer RAW */ |
301 | |
302 | /* COM8 */ |
303 | #define FAST_ALGO 0x80 /* Enable fast AGC/AEC algorithm */ |
304 | /* AEC Setp size limit */ |
305 | #define UNLMT_STEP 0x40 /* 0 : Step size is limited */ |
306 | /* 1 : Unlimited step size */ |
307 | #define BNDF_ON_OFF 0x20 /* Banding filter ON/OFF */ |
308 | #define AEC_BND 0x10 /* Enable AEC below banding value */ |
309 | #define AEC_ON_OFF 0x08 /* Fine AEC ON/OFF control */ |
310 | #define AGC_ON 0x04 /* AGC Enable */ |
311 | #define AWB_ON 0x02 /* AWB Enable */ |
312 | #define AEC_ON 0x01 /* AEC Enable */ |
313 | |
314 | /* COM9 */ |
315 | #define BASE_AECAGC 0x80 /* Histogram or average based AEC/AGC */ |
316 | /* Automatic gain ceiling - maximum AGC value */ |
317 | #define GAIN_2x 0x00 /* 000 : 2x */ |
318 | #define GAIN_4x 0x10 /* 001 : 4x */ |
319 | #define GAIN_8x 0x20 /* 010 : 8x */ |
320 | #define GAIN_16x 0x30 /* 011 : 16x */ |
321 | #define GAIN_32x 0x40 /* 100 : 32x */ |
322 | #define GAIN_64x 0x50 /* 101 : 64x */ |
323 | #define GAIN_128x 0x60 /* 110 : 128x */ |
324 | #define DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */ |
325 | #define DROP_HREF 0x02 /* Drop HREF output of corrupt frame */ |
326 | |
327 | /* COM11 */ |
328 | #define SGLF_ON_OFF 0x02 /* Single frame ON/OFF selection */ |
329 | #define SGLF_TRIG 0x01 /* Single frame transfer trigger */ |
330 | |
331 | /* HREF */ |
332 | #define HREF_VSTART_SHIFT 6 /* VSTART LSB */ |
333 | #define HREF_HSTART_SHIFT 4 /* HSTART 2 LSBs */ |
334 | #define HREF_VSIZE_SHIFT 2 /* VSIZE LSB */ |
335 | #define HREF_HSIZE_SHIFT 0 /* HSIZE 2 LSBs */ |
336 | |
337 | /* EXHCH */ |
338 | #define EXHCH_VSIZE_SHIFT 2 /* VOUTSIZE LSB */ |
339 | #define EXHCH_HSIZE_SHIFT 0 /* HOUTSIZE 2 LSBs */ |
340 | |
341 | /* DSP_CTRL1 */ |
342 | #define FIFO_ON 0x80 /* FIFO enable/disable selection */ |
343 | #define UV_ON_OFF 0x40 /* UV adjust function ON/OFF selection */ |
344 | #define YUV444_2_422 0x20 /* YUV444 to 422 UV channel option selection */ |
345 | #define CLR_MTRX_ON_OFF 0x10 /* Color matrix ON/OFF selection */ |
346 | #define INTPLT_ON_OFF 0x08 /* Interpolation ON/OFF selection */ |
347 | #define GMM_ON_OFF 0x04 /* Gamma function ON/OFF selection */ |
348 | #define AUTO_BLK_ON_OFF 0x02 /* Black defect auto correction ON/OFF */ |
349 | #define AUTO_WHT_ON_OFF 0x01 /* White define auto correction ON/OFF */ |
350 | |
351 | /* DSP_CTRL3 */ |
352 | #define UV_MASK 0x80 /* UV output sequence option */ |
353 | #define UV_ON 0x80 /* ON */ |
354 | #define UV_OFF 0x00 /* OFF */ |
355 | #define CBAR_MASK 0x20 /* DSP Color bar mask */ |
356 | #define CBAR_ON 0x20 /* ON */ |
357 | #define CBAR_OFF 0x00 /* OFF */ |
358 | |
359 | /* DSP_CTRL4 */ |
360 | #define DSP_OFMT_YUV 0x00 |
361 | #define DSP_OFMT_RGB 0x00 |
362 | #define DSP_OFMT_RAW8 0x02 |
363 | #define DSP_OFMT_RAW10 0x03 |
364 | |
365 | /* DSPAUTO (DSP Auto Function ON/OFF Control) */ |
366 | #define AWB_ACTRL 0x80 /* AWB auto threshold control */ |
367 | #define DENOISE_ACTRL 0x40 /* De-noise auto threshold control */ |
368 | #define EDGE_ACTRL 0x20 /* Edge enhancement auto strength control */ |
369 | #define UV_ACTRL 0x10 /* UV adjust auto slope control */ |
370 | #define SCAL0_ACTRL 0x08 /* Auto scaling factor control */ |
371 | #define SCAL1_2_ACTRL 0x04 /* Auto scaling factor control */ |
372 | |
373 | #define OV772X_MAX_WIDTH VGA_WIDTH |
374 | #define OV772X_MAX_HEIGHT VGA_HEIGHT |
375 | |
376 | /* |
377 | * ID |
378 | */ |
379 | #define OV7720 0x7720 |
380 | #define OV7725 0x7721 |
381 | #define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF)) |
382 | |
383 | /* |
384 | * PLL multipliers |
385 | */ |
386 | static struct { |
387 | unsigned int mult; |
388 | u8 com4; |
389 | } ov772x_pll[] = { |
390 | { 1, PLL_BYPASS, }, |
391 | { 4, PLL_4x, }, |
392 | { 6, PLL_6x, }, |
393 | { 8, PLL_8x, }, |
394 | }; |
395 | |
396 | /* |
397 | * struct |
398 | */ |
399 | |
400 | struct ov772x_color_format { |
401 | u32 code; |
402 | enum v4l2_colorspace colorspace; |
403 | u8 dsp3; |
404 | u8 dsp4; |
405 | u8 com3; |
406 | u8 com7; |
407 | }; |
408 | |
409 | struct ov772x_win_size { |
410 | char *name; |
411 | unsigned char com7_bit; |
412 | unsigned int sizeimage; |
413 | struct v4l2_rect rect; |
414 | }; |
415 | |
416 | struct ov772x_priv { |
417 | struct v4l2_subdev subdev; |
418 | struct v4l2_ctrl_handler hdl; |
419 | struct clk *clk; |
420 | struct regmap *regmap; |
421 | struct ov772x_camera_info *info; |
422 | struct gpio_desc *pwdn_gpio; |
423 | struct gpio_desc *rstb_gpio; |
424 | const struct ov772x_color_format *cfmt; |
425 | const struct ov772x_win_size *win; |
426 | struct v4l2_ctrl *vflip_ctrl; |
427 | struct v4l2_ctrl *hflip_ctrl; |
428 | unsigned int test_pattern; |
429 | /* band_filter = COM8[5] ? 256 - BDBASE : 0 */ |
430 | struct v4l2_ctrl *band_filter_ctrl; |
431 | unsigned int fps; |
432 | /* lock to protect power_count and streaming */ |
433 | struct mutex lock; |
434 | int power_count; |
435 | int streaming; |
436 | struct media_pad pad; |
437 | enum v4l2_mbus_type bus_type; |
438 | }; |
439 | |
440 | /* |
441 | * supported color format list |
442 | */ |
443 | static const struct ov772x_color_format ov772x_cfmts[] = { |
444 | { |
445 | .code = MEDIA_BUS_FMT_YUYV8_2X8, |
446 | .colorspace = V4L2_COLORSPACE_SRGB, |
447 | .dsp3 = 0x0, |
448 | .dsp4 = DSP_OFMT_YUV, |
449 | .com3 = SWAP_YUV, |
450 | .com7 = OFMT_YUV, |
451 | }, |
452 | { |
453 | .code = MEDIA_BUS_FMT_YVYU8_2X8, |
454 | .colorspace = V4L2_COLORSPACE_SRGB, |
455 | .dsp3 = UV_ON, |
456 | .dsp4 = DSP_OFMT_YUV, |
457 | .com3 = SWAP_YUV, |
458 | .com7 = OFMT_YUV, |
459 | }, |
460 | { |
461 | .code = MEDIA_BUS_FMT_UYVY8_2X8, |
462 | .colorspace = V4L2_COLORSPACE_SRGB, |
463 | .dsp3 = 0x0, |
464 | .dsp4 = DSP_OFMT_YUV, |
465 | .com3 = 0x0, |
466 | .com7 = OFMT_YUV, |
467 | }, |
468 | { |
469 | .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, |
470 | .colorspace = V4L2_COLORSPACE_SRGB, |
471 | .dsp3 = 0x0, |
472 | .dsp4 = DSP_OFMT_YUV, |
473 | .com3 = SWAP_RGB, |
474 | .com7 = FMT_RGB555 | OFMT_RGB, |
475 | }, |
476 | { |
477 | .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, |
478 | .colorspace = V4L2_COLORSPACE_SRGB, |
479 | .dsp3 = 0x0, |
480 | .dsp4 = DSP_OFMT_YUV, |
481 | .com3 = 0x0, |
482 | .com7 = FMT_RGB555 | OFMT_RGB, |
483 | }, |
484 | { |
485 | .code = MEDIA_BUS_FMT_RGB565_2X8_LE, |
486 | .colorspace = V4L2_COLORSPACE_SRGB, |
487 | .dsp3 = 0x0, |
488 | .dsp4 = DSP_OFMT_YUV, |
489 | .com3 = SWAP_RGB, |
490 | .com7 = FMT_RGB565 | OFMT_RGB, |
491 | }, |
492 | { |
493 | .code = MEDIA_BUS_FMT_RGB565_2X8_BE, |
494 | .colorspace = V4L2_COLORSPACE_SRGB, |
495 | .dsp3 = 0x0, |
496 | .dsp4 = DSP_OFMT_YUV, |
497 | .com3 = 0x0, |
498 | .com7 = FMT_RGB565 | OFMT_RGB, |
499 | }, |
500 | { |
501 | /* Setting DSP4 to DSP_OFMT_RAW8 still gives 10-bit output, |
502 | * regardless of the COM7 value. We can thus only support 10-bit |
503 | * Bayer until someone figures it out. |
504 | */ |
505 | .code = MEDIA_BUS_FMT_SBGGR10_1X10, |
506 | .colorspace = V4L2_COLORSPACE_SRGB, |
507 | .dsp3 = 0x0, |
508 | .dsp4 = DSP_OFMT_RAW10, |
509 | .com3 = 0x0, |
510 | .com7 = SENSOR_RAW | OFMT_BRAW, |
511 | }, |
512 | }; |
513 | |
514 | /* |
515 | * window size list |
516 | */ |
517 | |
518 | static const struct ov772x_win_size ov772x_win_sizes[] = { |
519 | { |
520 | .name = "VGA" , |
521 | .com7_bit = SLCT_VGA, |
522 | .sizeimage = 510 * 748, |
523 | .rect = { |
524 | .left = 140, |
525 | .top = 14, |
526 | .width = VGA_WIDTH, |
527 | .height = VGA_HEIGHT, |
528 | }, |
529 | }, { |
530 | .name = "QVGA" , |
531 | .com7_bit = SLCT_QVGA, |
532 | .sizeimage = 278 * 576, |
533 | .rect = { |
534 | .left = 252, |
535 | .top = 6, |
536 | .width = QVGA_WIDTH, |
537 | .height = QVGA_HEIGHT, |
538 | }, |
539 | }, |
540 | }; |
541 | |
542 | static const char * const [] = { |
543 | "Disabled" , |
544 | "Vertical Color Bar Type 1" , |
545 | }; |
546 | |
547 | /* |
548 | * frame rate settings lists |
549 | */ |
550 | static const unsigned int ov772x_frame_intervals[] = { 5, 10, 15, 20, 30, 60 }; |
551 | |
552 | /* |
553 | * general function |
554 | */ |
555 | |
556 | static struct ov772x_priv *to_ov772x(struct v4l2_subdev *sd) |
557 | { |
558 | return container_of(sd, struct ov772x_priv, subdev); |
559 | } |
560 | |
561 | static int ov772x_reset(struct ov772x_priv *priv) |
562 | { |
563 | int ret; |
564 | |
565 | ret = regmap_write(map: priv->regmap, COM7, SCCB_RESET); |
566 | if (ret < 0) |
567 | return ret; |
568 | |
569 | usleep_range(min: 1000, max: 5000); |
570 | |
571 | return regmap_update_bits(map: priv->regmap, COM2, SOFT_SLEEP_MODE, |
572 | SOFT_SLEEP_MODE); |
573 | } |
574 | |
575 | /* |
576 | * subdev ops |
577 | */ |
578 | |
579 | static int ov772x_s_stream(struct v4l2_subdev *sd, int enable) |
580 | { |
581 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
582 | struct ov772x_priv *priv = to_ov772x(sd); |
583 | int ret = 0; |
584 | |
585 | mutex_lock(&priv->lock); |
586 | |
587 | if (priv->streaming == enable) |
588 | goto done; |
589 | |
590 | if (priv->bus_type == V4L2_MBUS_BT656) { |
591 | ret = regmap_update_bits(map: priv->regmap, COM7, ITU656_ON_OFF, |
592 | val: enable ? |
593 | ITU656_ON_OFF : ~ITU656_ON_OFF); |
594 | if (ret) |
595 | goto done; |
596 | } |
597 | |
598 | ret = regmap_update_bits(map: priv->regmap, COM2, SOFT_SLEEP_MODE, |
599 | val: enable ? 0 : SOFT_SLEEP_MODE); |
600 | if (ret) |
601 | goto done; |
602 | |
603 | if (enable) { |
604 | dev_dbg(&client->dev, "format %d, win %s\n" , |
605 | priv->cfmt->code, priv->win->name); |
606 | } |
607 | priv->streaming = enable; |
608 | |
609 | done: |
610 | mutex_unlock(lock: &priv->lock); |
611 | |
612 | return ret; |
613 | } |
614 | |
615 | static unsigned int ov772x_select_fps(struct ov772x_priv *priv, |
616 | struct v4l2_fract *tpf) |
617 | { |
618 | unsigned int fps = tpf->numerator ? |
619 | tpf->denominator / tpf->numerator : |
620 | tpf->denominator; |
621 | unsigned int best_diff; |
622 | unsigned int diff; |
623 | unsigned int idx; |
624 | unsigned int i; |
625 | |
626 | /* Approximate to the closest supported frame interval. */ |
627 | best_diff = ~0L; |
628 | for (i = 0, idx = 0; i < ARRAY_SIZE(ov772x_frame_intervals); i++) { |
629 | diff = abs(fps - ov772x_frame_intervals[i]); |
630 | if (diff < best_diff) { |
631 | idx = i; |
632 | best_diff = diff; |
633 | } |
634 | } |
635 | |
636 | return ov772x_frame_intervals[idx]; |
637 | } |
638 | |
639 | static int ov772x_set_frame_rate(struct ov772x_priv *priv, |
640 | unsigned int fps, |
641 | const struct ov772x_color_format *cfmt, |
642 | const struct ov772x_win_size *win) |
643 | { |
644 | unsigned long fin = clk_get_rate(clk: priv->clk); |
645 | unsigned int best_diff; |
646 | unsigned int fsize; |
647 | unsigned int pclk; |
648 | unsigned int diff; |
649 | unsigned int i; |
650 | u8 clkrc = 0; |
651 | u8 com4 = 0; |
652 | int ret; |
653 | |
654 | /* Use image size (with blankings) to calculate desired pixel clock. */ |
655 | switch (cfmt->com7 & OFMT_MASK) { |
656 | case OFMT_BRAW: |
657 | fsize = win->sizeimage; |
658 | break; |
659 | case OFMT_RGB: |
660 | case OFMT_YUV: |
661 | default: |
662 | fsize = win->sizeimage * 2; |
663 | break; |
664 | } |
665 | |
666 | pclk = fps * fsize; |
667 | |
668 | /* |
669 | * Pixel clock generation circuit is pretty simple: |
670 | * |
671 | * Fin -> [ / CLKRC_div] -> [ * PLL_mult] -> pclk |
672 | * |
673 | * Try to approximate the desired pixel clock testing all available |
674 | * PLL multipliers (1x, 4x, 6x, 8x) and calculate corresponding |
675 | * divisor with: |
676 | * |
677 | * div = PLL_mult * Fin / pclk |
678 | * |
679 | * and re-calculate the pixel clock using it: |
680 | * |
681 | * pclk = Fin * PLL_mult / CLKRC_div |
682 | * |
683 | * Choose the PLL_mult and CLKRC_div pair that gives a pixel clock |
684 | * closer to the desired one. |
685 | * |
686 | * The desired pixel clock is calculated using a known frame size |
687 | * (blanking included) and FPS. |
688 | */ |
689 | best_diff = ~0L; |
690 | for (i = 0; i < ARRAY_SIZE(ov772x_pll); i++) { |
691 | unsigned int pll_mult = ov772x_pll[i].mult; |
692 | unsigned int pll_out = pll_mult * fin; |
693 | unsigned int t_pclk; |
694 | unsigned int div; |
695 | |
696 | if (pll_out < pclk) |
697 | continue; |
698 | |
699 | div = DIV_ROUND_CLOSEST(pll_out, pclk); |
700 | t_pclk = DIV_ROUND_CLOSEST(fin * pll_mult, div); |
701 | diff = abs(pclk - t_pclk); |
702 | if (diff < best_diff) { |
703 | best_diff = diff; |
704 | clkrc = CLKRC_DIV(div); |
705 | com4 = ov772x_pll[i].com4; |
706 | } |
707 | } |
708 | |
709 | ret = regmap_write(map: priv->regmap, COM4, val: com4 | COM4_RESERVED); |
710 | if (ret < 0) |
711 | return ret; |
712 | |
713 | ret = regmap_write(map: priv->regmap, CLKRC, val: clkrc | CLKRC_RESERVED); |
714 | if (ret < 0) |
715 | return ret; |
716 | |
717 | return 0; |
718 | } |
719 | |
720 | static int ov772x_get_frame_interval(struct v4l2_subdev *sd, |
721 | struct v4l2_subdev_state *sd_state, |
722 | struct v4l2_subdev_frame_interval *ival) |
723 | { |
724 | struct ov772x_priv *priv = to_ov772x(sd); |
725 | struct v4l2_fract *tpf = &ival->interval; |
726 | |
727 | /* |
728 | * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 |
729 | * subdev active state API. |
730 | */ |
731 | if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) |
732 | return -EINVAL; |
733 | |
734 | tpf->numerator = 1; |
735 | tpf->denominator = priv->fps; |
736 | |
737 | return 0; |
738 | } |
739 | |
740 | static int ov772x_set_frame_interval(struct v4l2_subdev *sd, |
741 | struct v4l2_subdev_state *sd_state, |
742 | struct v4l2_subdev_frame_interval *ival) |
743 | { |
744 | struct ov772x_priv *priv = to_ov772x(sd); |
745 | struct v4l2_fract *tpf = &ival->interval; |
746 | unsigned int fps; |
747 | int ret = 0; |
748 | |
749 | /* |
750 | * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 |
751 | * subdev active state API. |
752 | */ |
753 | if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE) |
754 | return -EINVAL; |
755 | |
756 | mutex_lock(&priv->lock); |
757 | |
758 | if (priv->streaming) { |
759 | ret = -EBUSY; |
760 | goto error; |
761 | } |
762 | |
763 | fps = ov772x_select_fps(priv, tpf); |
764 | |
765 | /* |
766 | * If the device is not powered up by the host driver do |
767 | * not apply any changes to H/W at this time. Instead |
768 | * the frame rate will be restored right after power-up. |
769 | */ |
770 | if (priv->power_count > 0) { |
771 | ret = ov772x_set_frame_rate(priv, fps, cfmt: priv->cfmt, win: priv->win); |
772 | if (ret) |
773 | goto error; |
774 | } |
775 | |
776 | tpf->numerator = 1; |
777 | tpf->denominator = fps; |
778 | priv->fps = fps; |
779 | |
780 | error: |
781 | mutex_unlock(lock: &priv->lock); |
782 | |
783 | return ret; |
784 | } |
785 | |
786 | static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) |
787 | { |
788 | struct ov772x_priv *priv = container_of(ctrl->handler, |
789 | struct ov772x_priv, hdl); |
790 | struct regmap *regmap = priv->regmap; |
791 | int ret = 0; |
792 | u8 val; |
793 | |
794 | /* v4l2_ctrl_lock() locks our own mutex */ |
795 | |
796 | /* |
797 | * If the device is not powered up by the host driver do |
798 | * not apply any controls to H/W at this time. Instead |
799 | * the controls will be restored right after power-up. |
800 | */ |
801 | if (priv->power_count == 0) |
802 | return 0; |
803 | |
804 | switch (ctrl->id) { |
805 | case V4L2_CID_VFLIP: |
806 | val = ctrl->val ? VFLIP_IMG : 0x00; |
807 | if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP)) |
808 | val ^= VFLIP_IMG; |
809 | return regmap_update_bits(map: regmap, COM3, VFLIP_IMG, val); |
810 | case V4L2_CID_HFLIP: |
811 | val = ctrl->val ? HFLIP_IMG : 0x00; |
812 | if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP)) |
813 | val ^= HFLIP_IMG; |
814 | return regmap_update_bits(map: regmap, COM3, HFLIP_IMG, val); |
815 | case V4L2_CID_BAND_STOP_FILTER: |
816 | if (!ctrl->val) { |
817 | /* Switch the filter off, it is on now */ |
818 | ret = regmap_update_bits(map: regmap, BDBASE, mask: 0xff, val: 0xff); |
819 | if (!ret) |
820 | ret = regmap_update_bits(map: regmap, COM8, |
821 | BNDF_ON_OFF, val: 0); |
822 | } else { |
823 | /* Switch the filter on, set AEC low limit */ |
824 | val = 256 - ctrl->val; |
825 | ret = regmap_update_bits(map: regmap, COM8, |
826 | BNDF_ON_OFF, BNDF_ON_OFF); |
827 | if (!ret) |
828 | ret = regmap_update_bits(map: regmap, BDBASE, |
829 | mask: 0xff, val); |
830 | } |
831 | |
832 | return ret; |
833 | case V4L2_CID_TEST_PATTERN: |
834 | priv->test_pattern = ctrl->val; |
835 | return 0; |
836 | } |
837 | |
838 | return -EINVAL; |
839 | } |
840 | |
841 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
842 | static int ov772x_g_register(struct v4l2_subdev *sd, |
843 | struct v4l2_dbg_register *reg) |
844 | { |
845 | struct ov772x_priv *priv = to_ov772x(sd); |
846 | int ret; |
847 | unsigned int val; |
848 | |
849 | reg->size = 1; |
850 | if (reg->reg > 0xff) |
851 | return -EINVAL; |
852 | |
853 | ret = regmap_read(map: priv->regmap, reg: reg->reg, val: &val); |
854 | if (ret < 0) |
855 | return ret; |
856 | |
857 | reg->val = (__u64)val; |
858 | |
859 | return 0; |
860 | } |
861 | |
862 | static int ov772x_s_register(struct v4l2_subdev *sd, |
863 | const struct v4l2_dbg_register *reg) |
864 | { |
865 | struct ov772x_priv *priv = to_ov772x(sd); |
866 | |
867 | if (reg->reg > 0xff || |
868 | reg->val > 0xff) |
869 | return -EINVAL; |
870 | |
871 | return regmap_write(map: priv->regmap, reg: reg->reg, val: reg->val); |
872 | } |
873 | #endif |
874 | |
875 | static int ov772x_power_on(struct ov772x_priv *priv) |
876 | { |
877 | struct i2c_client *client = v4l2_get_subdevdata(sd: &priv->subdev); |
878 | int ret; |
879 | |
880 | if (priv->clk) { |
881 | ret = clk_prepare_enable(clk: priv->clk); |
882 | if (ret) |
883 | return ret; |
884 | } |
885 | |
886 | if (priv->pwdn_gpio) { |
887 | gpiod_set_value(desc: priv->pwdn_gpio, value: 1); |
888 | usleep_range(min: 500, max: 1000); |
889 | } |
890 | |
891 | /* |
892 | * FIXME: The reset signal is connected to a shared GPIO on some |
893 | * platforms (namely the SuperH Migo-R). Until a framework becomes |
894 | * available to handle this cleanly, request the GPIO temporarily |
895 | * to avoid conflicts. |
896 | */ |
897 | priv->rstb_gpio = gpiod_get_optional(dev: &client->dev, con_id: "reset" , |
898 | flags: GPIOD_OUT_LOW); |
899 | if (IS_ERR(ptr: priv->rstb_gpio)) { |
900 | dev_info(&client->dev, "Unable to get GPIO \"reset\"" ); |
901 | clk_disable_unprepare(clk: priv->clk); |
902 | return PTR_ERR(ptr: priv->rstb_gpio); |
903 | } |
904 | |
905 | if (priv->rstb_gpio) { |
906 | gpiod_set_value(desc: priv->rstb_gpio, value: 1); |
907 | usleep_range(min: 500, max: 1000); |
908 | gpiod_set_value(desc: priv->rstb_gpio, value: 0); |
909 | usleep_range(min: 500, max: 1000); |
910 | |
911 | gpiod_put(desc: priv->rstb_gpio); |
912 | } |
913 | |
914 | return 0; |
915 | } |
916 | |
917 | static int ov772x_power_off(struct ov772x_priv *priv) |
918 | { |
919 | clk_disable_unprepare(clk: priv->clk); |
920 | |
921 | if (priv->pwdn_gpio) { |
922 | gpiod_set_value(desc: priv->pwdn_gpio, value: 0); |
923 | usleep_range(min: 500, max: 1000); |
924 | } |
925 | |
926 | return 0; |
927 | } |
928 | |
929 | static int ov772x_set_params(struct ov772x_priv *priv, |
930 | const struct ov772x_color_format *cfmt, |
931 | const struct ov772x_win_size *win); |
932 | |
933 | static int ov772x_s_power(struct v4l2_subdev *sd, int on) |
934 | { |
935 | struct ov772x_priv *priv = to_ov772x(sd); |
936 | int ret = 0; |
937 | |
938 | mutex_lock(&priv->lock); |
939 | |
940 | /* If the power count is modified from 0 to != 0 or from != 0 to 0, |
941 | * update the power state. |
942 | */ |
943 | if (priv->power_count == !on) { |
944 | if (on) { |
945 | ret = ov772x_power_on(priv); |
946 | /* |
947 | * Restore the format, the frame rate, and |
948 | * the controls |
949 | */ |
950 | if (!ret) |
951 | ret = ov772x_set_params(priv, cfmt: priv->cfmt, |
952 | win: priv->win); |
953 | } else { |
954 | ret = ov772x_power_off(priv); |
955 | } |
956 | } |
957 | |
958 | if (!ret) { |
959 | /* Update the power count. */ |
960 | priv->power_count += on ? 1 : -1; |
961 | WARN(priv->power_count < 0, "Unbalanced power count\n" ); |
962 | WARN(priv->power_count > 1, "Duplicated s_power call\n" ); |
963 | } |
964 | |
965 | mutex_unlock(lock: &priv->lock); |
966 | |
967 | return ret; |
968 | } |
969 | |
970 | static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height) |
971 | { |
972 | const struct ov772x_win_size *win = &ov772x_win_sizes[0]; |
973 | u32 best_diff = UINT_MAX; |
974 | unsigned int i; |
975 | |
976 | for (i = 0; i < ARRAY_SIZE(ov772x_win_sizes); ++i) { |
977 | u32 diff = abs(width - ov772x_win_sizes[i].rect.width) |
978 | + abs(height - ov772x_win_sizes[i].rect.height); |
979 | if (diff < best_diff) { |
980 | best_diff = diff; |
981 | win = &ov772x_win_sizes[i]; |
982 | } |
983 | } |
984 | |
985 | return win; |
986 | } |
987 | |
988 | static void ov772x_select_params(const struct v4l2_mbus_framefmt *mf, |
989 | const struct ov772x_color_format **cfmt, |
990 | const struct ov772x_win_size **win) |
991 | { |
992 | unsigned int i; |
993 | |
994 | /* Select a format. */ |
995 | *cfmt = &ov772x_cfmts[0]; |
996 | |
997 | for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) { |
998 | if (mf->code == ov772x_cfmts[i].code) { |
999 | *cfmt = &ov772x_cfmts[i]; |
1000 | break; |
1001 | } |
1002 | } |
1003 | |
1004 | /* Select a window size. */ |
1005 | *win = ov772x_select_win(width: mf->width, height: mf->height); |
1006 | } |
1007 | |
1008 | static int ov772x_edgectrl(struct ov772x_priv *priv) |
1009 | { |
1010 | struct regmap *regmap = priv->regmap; |
1011 | int ret; |
1012 | |
1013 | if (!priv->info) |
1014 | return 0; |
1015 | |
1016 | if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) { |
1017 | /* |
1018 | * Manual Edge Control Mode. |
1019 | * |
1020 | * Edge auto strength bit is set by default. |
1021 | * Remove it when manual mode. |
1022 | */ |
1023 | |
1024 | ret = regmap_update_bits(map: regmap, DSPAUTO, EDGE_ACTRL, val: 0x00); |
1025 | if (ret < 0) |
1026 | return ret; |
1027 | |
1028 | ret = regmap_update_bits(map: regmap, EDGE_TRSHLD, |
1029 | OV772X_EDGE_THRESHOLD_MASK, |
1030 | val: priv->info->edgectrl.threshold); |
1031 | if (ret < 0) |
1032 | return ret; |
1033 | |
1034 | ret = regmap_update_bits(map: regmap, EDGE_STRNGT, |
1035 | OV772X_EDGE_STRENGTH_MASK, |
1036 | val: priv->info->edgectrl.strength); |
1037 | if (ret < 0) |
1038 | return ret; |
1039 | |
1040 | } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) { |
1041 | /* |
1042 | * Auto Edge Control Mode. |
1043 | * |
1044 | * Set upper and lower limit. |
1045 | */ |
1046 | ret = regmap_update_bits(map: regmap, EDGE_UPPER, |
1047 | OV772X_EDGE_UPPER_MASK, |
1048 | val: priv->info->edgectrl.upper); |
1049 | if (ret < 0) |
1050 | return ret; |
1051 | |
1052 | ret = regmap_update_bits(map: regmap, EDGE_LOWER, |
1053 | OV772X_EDGE_LOWER_MASK, |
1054 | val: priv->info->edgectrl.lower); |
1055 | if (ret < 0) |
1056 | return ret; |
1057 | } |
1058 | |
1059 | return 0; |
1060 | } |
1061 | |
1062 | static int ov772x_set_params(struct ov772x_priv *priv, |
1063 | const struct ov772x_color_format *cfmt, |
1064 | const struct ov772x_win_size *win) |
1065 | { |
1066 | int ret; |
1067 | u8 val; |
1068 | |
1069 | /* Reset hardware. */ |
1070 | ov772x_reset(priv); |
1071 | |
1072 | /* Edge Ctrl. */ |
1073 | ret = ov772x_edgectrl(priv); |
1074 | if (ret < 0) |
1075 | return ret; |
1076 | |
1077 | /* Format and window size. */ |
1078 | ret = regmap_write(map: priv->regmap, HSTART, val: win->rect.left >> 2); |
1079 | if (ret < 0) |
1080 | goto ov772x_set_fmt_error; |
1081 | ret = regmap_write(map: priv->regmap, HSIZE, val: win->rect.width >> 2); |
1082 | if (ret < 0) |
1083 | goto ov772x_set_fmt_error; |
1084 | ret = regmap_write(map: priv->regmap, VSTART, val: win->rect.top >> 1); |
1085 | if (ret < 0) |
1086 | goto ov772x_set_fmt_error; |
1087 | ret = regmap_write(map: priv->regmap, VSIZE, val: win->rect.height >> 1); |
1088 | if (ret < 0) |
1089 | goto ov772x_set_fmt_error; |
1090 | ret = regmap_write(map: priv->regmap, HOUTSIZE, val: win->rect.width >> 2); |
1091 | if (ret < 0) |
1092 | goto ov772x_set_fmt_error; |
1093 | ret = regmap_write(map: priv->regmap, VOUTSIZE, val: win->rect.height >> 1); |
1094 | if (ret < 0) |
1095 | goto ov772x_set_fmt_error; |
1096 | ret = regmap_write(map: priv->regmap, HREF, |
1097 | val: ((win->rect.top & 1) << HREF_VSTART_SHIFT) | |
1098 | ((win->rect.left & 3) << HREF_HSTART_SHIFT) | |
1099 | ((win->rect.height & 1) << HREF_VSIZE_SHIFT) | |
1100 | ((win->rect.width & 3) << HREF_HSIZE_SHIFT)); |
1101 | if (ret < 0) |
1102 | goto ov772x_set_fmt_error; |
1103 | ret = regmap_write(map: priv->regmap, EXHCH, |
1104 | val: ((win->rect.height & 1) << EXHCH_VSIZE_SHIFT) | |
1105 | ((win->rect.width & 3) << EXHCH_HSIZE_SHIFT)); |
1106 | if (ret < 0) |
1107 | goto ov772x_set_fmt_error; |
1108 | |
1109 | /* Set DSP_CTRL3. */ |
1110 | val = cfmt->dsp3; |
1111 | if (val) { |
1112 | ret = regmap_update_bits(map: priv->regmap, DSP_CTRL3, UV_MASK, val); |
1113 | if (ret < 0) |
1114 | goto ov772x_set_fmt_error; |
1115 | } |
1116 | |
1117 | /* DSP_CTRL4: AEC reference point and DSP output format. */ |
1118 | if (cfmt->dsp4) { |
1119 | ret = regmap_write(map: priv->regmap, DSP_CTRL4, val: cfmt->dsp4); |
1120 | if (ret < 0) |
1121 | goto ov772x_set_fmt_error; |
1122 | } |
1123 | |
1124 | /* Set COM3. */ |
1125 | val = cfmt->com3; |
1126 | if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP)) |
1127 | val |= VFLIP_IMG; |
1128 | if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP)) |
1129 | val |= HFLIP_IMG; |
1130 | if (priv->vflip_ctrl->val) |
1131 | val ^= VFLIP_IMG; |
1132 | if (priv->hflip_ctrl->val) |
1133 | val ^= HFLIP_IMG; |
1134 | if (priv->test_pattern) |
1135 | val |= SCOLOR_TEST; |
1136 | |
1137 | ret = regmap_update_bits(map: priv->regmap, COM3, SWAP_MASK | IMG_MASK, val); |
1138 | if (ret < 0) |
1139 | goto ov772x_set_fmt_error; |
1140 | |
1141 | /* COM7: Sensor resolution and output format control. */ |
1142 | ret = regmap_write(map: priv->regmap, COM7, val: win->com7_bit | cfmt->com7); |
1143 | if (ret < 0) |
1144 | goto ov772x_set_fmt_error; |
1145 | |
1146 | /* COM4, CLKRC: Set pixel clock and framerate. */ |
1147 | ret = ov772x_set_frame_rate(priv, fps: priv->fps, cfmt, win); |
1148 | if (ret < 0) |
1149 | goto ov772x_set_fmt_error; |
1150 | |
1151 | /* Set COM8. */ |
1152 | if (priv->band_filter_ctrl->val) { |
1153 | unsigned short band_filter = priv->band_filter_ctrl->val; |
1154 | |
1155 | ret = regmap_update_bits(map: priv->regmap, COM8, |
1156 | BNDF_ON_OFF, BNDF_ON_OFF); |
1157 | if (!ret) |
1158 | ret = regmap_update_bits(map: priv->regmap, BDBASE, |
1159 | mask: 0xff, val: 256 - band_filter); |
1160 | if (ret < 0) |
1161 | goto ov772x_set_fmt_error; |
1162 | } |
1163 | |
1164 | return ret; |
1165 | |
1166 | ov772x_set_fmt_error: |
1167 | |
1168 | ov772x_reset(priv); |
1169 | |
1170 | return ret; |
1171 | } |
1172 | |
1173 | static int ov772x_get_selection(struct v4l2_subdev *sd, |
1174 | struct v4l2_subdev_state *sd_state, |
1175 | struct v4l2_subdev_selection *sel) |
1176 | { |
1177 | struct ov772x_priv *priv = to_ov772x(sd); |
1178 | |
1179 | if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) |
1180 | return -EINVAL; |
1181 | |
1182 | sel->r.left = 0; |
1183 | sel->r.top = 0; |
1184 | switch (sel->target) { |
1185 | case V4L2_SEL_TGT_CROP_BOUNDS: |
1186 | case V4L2_SEL_TGT_CROP: |
1187 | sel->r.width = priv->win->rect.width; |
1188 | sel->r.height = priv->win->rect.height; |
1189 | return 0; |
1190 | default: |
1191 | return -EINVAL; |
1192 | } |
1193 | } |
1194 | |
1195 | static int ov772x_get_fmt(struct v4l2_subdev *sd, |
1196 | struct v4l2_subdev_state *sd_state, |
1197 | struct v4l2_subdev_format *format) |
1198 | { |
1199 | struct v4l2_mbus_framefmt *mf = &format->format; |
1200 | struct ov772x_priv *priv = to_ov772x(sd); |
1201 | |
1202 | if (format->pad) |
1203 | return -EINVAL; |
1204 | |
1205 | mf->width = priv->win->rect.width; |
1206 | mf->height = priv->win->rect.height; |
1207 | mf->code = priv->cfmt->code; |
1208 | mf->colorspace = priv->cfmt->colorspace; |
1209 | mf->field = V4L2_FIELD_NONE; |
1210 | |
1211 | return 0; |
1212 | } |
1213 | |
1214 | static int ov772x_set_fmt(struct v4l2_subdev *sd, |
1215 | struct v4l2_subdev_state *sd_state, |
1216 | struct v4l2_subdev_format *format) |
1217 | { |
1218 | struct ov772x_priv *priv = to_ov772x(sd); |
1219 | struct v4l2_mbus_framefmt *mf = &format->format; |
1220 | const struct ov772x_color_format *cfmt; |
1221 | const struct ov772x_win_size *win; |
1222 | int ret = 0; |
1223 | |
1224 | if (format->pad) |
1225 | return -EINVAL; |
1226 | |
1227 | ov772x_select_params(mf, cfmt: &cfmt, win: &win); |
1228 | |
1229 | mf->code = cfmt->code; |
1230 | mf->width = win->rect.width; |
1231 | mf->height = win->rect.height; |
1232 | mf->field = V4L2_FIELD_NONE; |
1233 | mf->colorspace = cfmt->colorspace; |
1234 | mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; |
1235 | mf->quantization = V4L2_QUANTIZATION_DEFAULT; |
1236 | mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; |
1237 | |
1238 | if (format->which == V4L2_SUBDEV_FORMAT_TRY) { |
1239 | *v4l2_subdev_state_get_format(sd_state, 0) = *mf; |
1240 | return 0; |
1241 | } |
1242 | |
1243 | mutex_lock(&priv->lock); |
1244 | |
1245 | if (priv->streaming) { |
1246 | ret = -EBUSY; |
1247 | goto error; |
1248 | } |
1249 | |
1250 | /* |
1251 | * If the device is not powered up by the host driver do |
1252 | * not apply any changes to H/W at this time. Instead |
1253 | * the format will be restored right after power-up. |
1254 | */ |
1255 | if (priv->power_count > 0) { |
1256 | ret = ov772x_set_params(priv, cfmt, win); |
1257 | if (ret < 0) |
1258 | goto error; |
1259 | } |
1260 | priv->win = win; |
1261 | priv->cfmt = cfmt; |
1262 | |
1263 | error: |
1264 | mutex_unlock(lock: &priv->lock); |
1265 | |
1266 | return ret; |
1267 | } |
1268 | |
1269 | static int ov772x_video_probe(struct ov772x_priv *priv) |
1270 | { |
1271 | struct i2c_client *client = v4l2_get_subdevdata(sd: &priv->subdev); |
1272 | int pid, ver, midh, midl; |
1273 | const char *devname; |
1274 | int ret; |
1275 | |
1276 | ret = ov772x_power_on(priv); |
1277 | if (ret < 0) |
1278 | return ret; |
1279 | |
1280 | /* Check and show product ID and manufacturer ID. */ |
1281 | ret = regmap_read(map: priv->regmap, PID, val: &pid); |
1282 | if (ret < 0) |
1283 | return ret; |
1284 | ret = regmap_read(map: priv->regmap, VER, val: &ver); |
1285 | if (ret < 0) |
1286 | return ret; |
1287 | |
1288 | switch (VERSION(pid, ver)) { |
1289 | case OV7720: |
1290 | devname = "ov7720" ; |
1291 | break; |
1292 | case OV7725: |
1293 | devname = "ov7725" ; |
1294 | break; |
1295 | default: |
1296 | dev_err(&client->dev, |
1297 | "Product ID error %x:%x\n" , pid, ver); |
1298 | ret = -ENODEV; |
1299 | goto done; |
1300 | } |
1301 | |
1302 | ret = regmap_read(map: priv->regmap, MIDH, val: &midh); |
1303 | if (ret < 0) |
1304 | return ret; |
1305 | ret = regmap_read(map: priv->regmap, MIDL, val: &midl); |
1306 | if (ret < 0) |
1307 | return ret; |
1308 | |
1309 | dev_info(&client->dev, |
1310 | "%s Product ID %0x:%0x Manufacturer ID %x:%x\n" , |
1311 | devname, pid, ver, midh, midl); |
1312 | |
1313 | ret = v4l2_ctrl_handler_setup(hdl: &priv->hdl); |
1314 | |
1315 | done: |
1316 | ov772x_power_off(priv); |
1317 | |
1318 | return ret; |
1319 | } |
1320 | |
1321 | static const struct v4l2_ctrl_ops ov772x_ctrl_ops = { |
1322 | .s_ctrl = ov772x_s_ctrl, |
1323 | }; |
1324 | |
1325 | static const struct v4l2_subdev_core_ops ov772x_subdev_core_ops = { |
1326 | .log_status = v4l2_ctrl_subdev_log_status, |
1327 | .subscribe_event = v4l2_ctrl_subdev_subscribe_event, |
1328 | .unsubscribe_event = v4l2_event_subdev_unsubscribe, |
1329 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
1330 | .g_register = ov772x_g_register, |
1331 | .s_register = ov772x_s_register, |
1332 | #endif |
1333 | .s_power = ov772x_s_power, |
1334 | }; |
1335 | |
1336 | static int ov772x_enum_frame_interval(struct v4l2_subdev *sd, |
1337 | struct v4l2_subdev_state *sd_state, |
1338 | struct v4l2_subdev_frame_interval_enum *fie) |
1339 | { |
1340 | if (fie->pad || fie->index >= ARRAY_SIZE(ov772x_frame_intervals)) |
1341 | return -EINVAL; |
1342 | |
1343 | if (fie->width != VGA_WIDTH && fie->width != QVGA_WIDTH) |
1344 | return -EINVAL; |
1345 | if (fie->height != VGA_HEIGHT && fie->height != QVGA_HEIGHT) |
1346 | return -EINVAL; |
1347 | |
1348 | fie->interval.numerator = 1; |
1349 | fie->interval.denominator = ov772x_frame_intervals[fie->index]; |
1350 | |
1351 | return 0; |
1352 | } |
1353 | |
1354 | static int ov772x_enum_mbus_code(struct v4l2_subdev *sd, |
1355 | struct v4l2_subdev_state *sd_state, |
1356 | struct v4l2_subdev_mbus_code_enum *code) |
1357 | { |
1358 | if (code->pad || code->index >= ARRAY_SIZE(ov772x_cfmts)) |
1359 | return -EINVAL; |
1360 | |
1361 | code->code = ov772x_cfmts[code->index].code; |
1362 | |
1363 | return 0; |
1364 | } |
1365 | |
1366 | static const struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { |
1367 | .s_stream = ov772x_s_stream, |
1368 | }; |
1369 | |
1370 | static const struct v4l2_subdev_pad_ops ov772x_subdev_pad_ops = { |
1371 | .enum_frame_interval = ov772x_enum_frame_interval, |
1372 | .enum_mbus_code = ov772x_enum_mbus_code, |
1373 | .get_selection = ov772x_get_selection, |
1374 | .get_fmt = ov772x_get_fmt, |
1375 | .set_fmt = ov772x_set_fmt, |
1376 | .get_frame_interval = ov772x_get_frame_interval, |
1377 | .set_frame_interval = ov772x_set_frame_interval, |
1378 | }; |
1379 | |
1380 | static const struct v4l2_subdev_ops ov772x_subdev_ops = { |
1381 | .core = &ov772x_subdev_core_ops, |
1382 | .video = &ov772x_subdev_video_ops, |
1383 | .pad = &ov772x_subdev_pad_ops, |
1384 | }; |
1385 | |
1386 | static int ov772x_parse_dt(struct i2c_client *client, |
1387 | struct ov772x_priv *priv) |
1388 | { |
1389 | struct v4l2_fwnode_endpoint bus_cfg = { |
1390 | .bus_type = V4L2_MBUS_PARALLEL |
1391 | }; |
1392 | struct fwnode_handle *ep; |
1393 | int ret; |
1394 | |
1395 | ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); |
1396 | if (!ep) { |
1397 | dev_err(&client->dev, "Endpoint node not found\n" ); |
1398 | return -EINVAL; |
1399 | } |
1400 | |
1401 | /* |
1402 | * For backward compatibility with older DTS where the |
1403 | * bus-type property was not mandatory, assume |
1404 | * V4L2_MBUS_PARALLEL as it was the only supported bus at the |
1405 | * time. v4l2_fwnode_endpoint_alloc_parse() will not fail if |
1406 | * 'bus-type' is not specified. |
1407 | */ |
1408 | ret = v4l2_fwnode_endpoint_alloc_parse(fwnode: ep, vep: &bus_cfg); |
1409 | if (ret) { |
1410 | bus_cfg = (struct v4l2_fwnode_endpoint) |
1411 | { .bus_type = V4L2_MBUS_BT656 }; |
1412 | ret = v4l2_fwnode_endpoint_alloc_parse(fwnode: ep, vep: &bus_cfg); |
1413 | if (ret) |
1414 | goto error_fwnode_put; |
1415 | } |
1416 | |
1417 | priv->bus_type = bus_cfg.bus_type; |
1418 | v4l2_fwnode_endpoint_free(vep: &bus_cfg); |
1419 | |
1420 | error_fwnode_put: |
1421 | fwnode_handle_put(fwnode: ep); |
1422 | |
1423 | return ret; |
1424 | } |
1425 | |
1426 | /* |
1427 | * i2c_driver function |
1428 | */ |
1429 | |
1430 | static int ov772x_probe(struct i2c_client *client) |
1431 | { |
1432 | struct ov772x_priv *priv; |
1433 | int ret; |
1434 | static const struct regmap_config ov772x_regmap_config = { |
1435 | .reg_bits = 8, |
1436 | .val_bits = 8, |
1437 | .max_register = DSPAUTO, |
1438 | }; |
1439 | |
1440 | if (!client->dev.of_node && !client->dev.platform_data) { |
1441 | dev_err(&client->dev, |
1442 | "Missing ov772x platform data for non-DT device\n" ); |
1443 | return -EINVAL; |
1444 | } |
1445 | |
1446 | priv = devm_kzalloc(dev: &client->dev, size: sizeof(*priv), GFP_KERNEL); |
1447 | if (!priv) |
1448 | return -ENOMEM; |
1449 | |
1450 | priv->regmap = devm_regmap_init_sccb(client, &ov772x_regmap_config); |
1451 | if (IS_ERR(ptr: priv->regmap)) { |
1452 | dev_err(&client->dev, "Failed to allocate register map\n" ); |
1453 | return PTR_ERR(ptr: priv->regmap); |
1454 | } |
1455 | |
1456 | priv->info = client->dev.platform_data; |
1457 | mutex_init(&priv->lock); |
1458 | |
1459 | v4l2_i2c_subdev_init(sd: &priv->subdev, client, ops: &ov772x_subdev_ops); |
1460 | priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | |
1461 | V4L2_SUBDEV_FL_HAS_EVENTS; |
1462 | v4l2_ctrl_handler_init(&priv->hdl, 3); |
1463 | /* Use our mutex for the controls */ |
1464 | priv->hdl.lock = &priv->lock; |
1465 | priv->vflip_ctrl = v4l2_ctrl_new_std(hdl: &priv->hdl, ops: &ov772x_ctrl_ops, |
1466 | V4L2_CID_VFLIP, min: 0, max: 1, step: 1, def: 0); |
1467 | priv->hflip_ctrl = v4l2_ctrl_new_std(hdl: &priv->hdl, ops: &ov772x_ctrl_ops, |
1468 | V4L2_CID_HFLIP, min: 0, max: 1, step: 1, def: 0); |
1469 | priv->band_filter_ctrl = v4l2_ctrl_new_std(hdl: &priv->hdl, ops: &ov772x_ctrl_ops, |
1470 | V4L2_CID_BAND_STOP_FILTER, |
1471 | min: 0, max: 256, step: 1, def: 0); |
1472 | v4l2_ctrl_new_std_menu_items(hdl: &priv->hdl, ops: &ov772x_ctrl_ops, |
1473 | V4L2_CID_TEST_PATTERN, |
1474 | ARRAY_SIZE(ov772x_test_pattern_menu) - 1, |
1475 | mask: 0, def: 0, qmenu: ov772x_test_pattern_menu); |
1476 | priv->subdev.ctrl_handler = &priv->hdl; |
1477 | if (priv->hdl.error) { |
1478 | ret = priv->hdl.error; |
1479 | goto error_ctrl_free; |
1480 | } |
1481 | |
1482 | priv->clk = clk_get(dev: &client->dev, NULL); |
1483 | if (IS_ERR(ptr: priv->clk)) { |
1484 | dev_err(&client->dev, "Unable to get xclk clock\n" ); |
1485 | ret = PTR_ERR(ptr: priv->clk); |
1486 | goto error_ctrl_free; |
1487 | } |
1488 | |
1489 | priv->pwdn_gpio = gpiod_get_optional(dev: &client->dev, con_id: "powerdown" , |
1490 | flags: GPIOD_OUT_LOW); |
1491 | if (IS_ERR(ptr: priv->pwdn_gpio)) { |
1492 | dev_info(&client->dev, "Unable to get GPIO \"powerdown\"" ); |
1493 | ret = PTR_ERR(ptr: priv->pwdn_gpio); |
1494 | goto error_clk_put; |
1495 | } |
1496 | |
1497 | ret = ov772x_parse_dt(client, priv); |
1498 | if (ret) |
1499 | goto error_clk_put; |
1500 | |
1501 | ret = ov772x_video_probe(priv); |
1502 | if (ret < 0) |
1503 | goto error_gpio_put; |
1504 | |
1505 | priv->pad.flags = MEDIA_PAD_FL_SOURCE; |
1506 | priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; |
1507 | ret = media_entity_pads_init(entity: &priv->subdev.entity, num_pads: 1, pads: &priv->pad); |
1508 | if (ret < 0) |
1509 | goto error_gpio_put; |
1510 | |
1511 | priv->cfmt = &ov772x_cfmts[0]; |
1512 | priv->win = &ov772x_win_sizes[0]; |
1513 | priv->fps = 15; |
1514 | |
1515 | ret = v4l2_async_register_subdev(sd: &priv->subdev); |
1516 | if (ret) |
1517 | goto error_entity_cleanup; |
1518 | |
1519 | return 0; |
1520 | |
1521 | error_entity_cleanup: |
1522 | media_entity_cleanup(entity: &priv->subdev.entity); |
1523 | error_gpio_put: |
1524 | if (priv->pwdn_gpio) |
1525 | gpiod_put(desc: priv->pwdn_gpio); |
1526 | error_clk_put: |
1527 | clk_put(clk: priv->clk); |
1528 | error_ctrl_free: |
1529 | v4l2_ctrl_handler_free(hdl: &priv->hdl); |
1530 | mutex_destroy(lock: &priv->lock); |
1531 | |
1532 | return ret; |
1533 | } |
1534 | |
1535 | static void ov772x_remove(struct i2c_client *client) |
1536 | { |
1537 | struct ov772x_priv *priv = to_ov772x(sd: i2c_get_clientdata(client)); |
1538 | |
1539 | media_entity_cleanup(entity: &priv->subdev.entity); |
1540 | clk_put(clk: priv->clk); |
1541 | if (priv->pwdn_gpio) |
1542 | gpiod_put(desc: priv->pwdn_gpio); |
1543 | v4l2_async_unregister_subdev(sd: &priv->subdev); |
1544 | v4l2_ctrl_handler_free(hdl: &priv->hdl); |
1545 | mutex_destroy(lock: &priv->lock); |
1546 | } |
1547 | |
1548 | static const struct i2c_device_id ov772x_id[] = { |
1549 | { "ov772x" , 0 }, |
1550 | { } |
1551 | }; |
1552 | MODULE_DEVICE_TABLE(i2c, ov772x_id); |
1553 | |
1554 | static const struct of_device_id ov772x_of_match[] = { |
1555 | { .compatible = "ovti,ov7725" , }, |
1556 | { .compatible = "ovti,ov7720" , }, |
1557 | { /* sentinel */ }, |
1558 | }; |
1559 | MODULE_DEVICE_TABLE(of, ov772x_of_match); |
1560 | |
1561 | static struct i2c_driver ov772x_i2c_driver = { |
1562 | .driver = { |
1563 | .name = "ov772x" , |
1564 | .of_match_table = ov772x_of_match, |
1565 | }, |
1566 | .probe = ov772x_probe, |
1567 | .remove = ov772x_remove, |
1568 | .id_table = ov772x_id, |
1569 | }; |
1570 | |
1571 | module_i2c_driver(ov772x_i2c_driver); |
1572 | |
1573 | MODULE_DESCRIPTION("V4L2 driver for OV772x image sensor" ); |
1574 | MODULE_AUTHOR("Kuninori Morimoto" ); |
1575 | MODULE_LICENSE("GPL v2" ); |
1576 | |