Commit | Line | Data |
---|---|---|
3c2472a3 RO |
1 | /* |
2 | * A V4L2 driver for OmniVision OV5647 cameras. | |
3 | * | |
4 | * Based on Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor driver | |
5 | * Copyright (C) 2011 Sylwester Nawrocki <s.nawrocki@samsung.com> | |
6 | * | |
7 | * Based on Omnivision OV7670 Camera Driver | |
8 | * Copyright (C) 2006-7 Jonathan Corbet <corbet@lwn.net> | |
9 | * | |
10 | * Copyright (C) 2016, Synopsys, Inc. | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or | |
13 | * modify it under the terms of the GNU General Public License as | |
14 | * published by the Free Software Foundation version 2. | |
15 | * | |
16 | * This program is distributed .as is. WITHOUT ANY WARRANTY of any | |
17 | * kind, whether express or implied; without even the implied warranty | |
18 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | * GNU General Public License for more details. | |
20 | */ | |
21 | ||
22 | #include <linux/clk.h> | |
23 | #include <linux/delay.h> | |
24 | #include <linux/i2c.h> | |
25 | #include <linux/init.h> | |
26 | #include <linux/io.h> | |
27 | #include <linux/module.h> | |
859969b3 | 28 | #include <linux/of_graph.h> |
3c2472a3 RO |
29 | #include <linux/slab.h> |
30 | #include <linux/videodev2.h> | |
31 | #include <media/v4l2-device.h> | |
859969b3 | 32 | #include <media/v4l2-fwnode.h> |
3c2472a3 RO |
33 | #include <media/v4l2-image-sizes.h> |
34 | #include <media/v4l2-mediabus.h> | |
3c2472a3 RO |
35 | |
36 | #define SENSOR_NAME "ov5647" | |
37 | ||
cef66734 JC |
38 | #define MIPI_CTRL00_CLOCK_LANE_GATE BIT(5) |
39 | #define MIPI_CTRL00_BUS_IDLE BIT(2) | |
40 | #define MIPI_CTRL00_CLOCK_LANE_DISABLE BIT(0) | |
41 | ||
42 | #define OV5647_SW_STANDBY 0x0100 | |
43 | #define OV5647_SW_RESET 0x0103 | |
44 | #define OV5647_REG_CHIPID_H 0x300A | |
45 | #define OV5647_REG_CHIPID_L 0x300B | |
46 | #define OV5640_REG_PAD_OUT 0x300D | |
47 | #define OV5647_REG_FRAME_OFF_NUMBER 0x4202 | |
48 | #define OV5647_REG_MIPI_CTRL00 0x4800 | |
49 | #define OV5647_REG_MIPI_CTRL14 0x4814 | |
3c2472a3 RO |
50 | |
51 | #define REG_TERM 0xfffe | |
52 | #define VAL_TERM 0xfe | |
53 | #define REG_DLY 0xffff | |
54 | ||
55 | #define OV5647_ROW_START 0x01 | |
56 | #define OV5647_ROW_START_MIN 0 | |
57 | #define OV5647_ROW_START_MAX 2004 | |
58 | #define OV5647_ROW_START_DEF 54 | |
59 | ||
60 | #define OV5647_COLUMN_START 0x02 | |
61 | #define OV5647_COLUMN_START_MIN 0 | |
62 | #define OV5647_COLUMN_START_MAX 2750 | |
63 | #define OV5647_COLUMN_START_DEF 16 | |
64 | ||
65 | #define OV5647_WINDOW_HEIGHT 0x03 | |
66 | #define OV5647_WINDOW_HEIGHT_MIN 2 | |
67 | #define OV5647_WINDOW_HEIGHT_MAX 2006 | |
68 | #define OV5647_WINDOW_HEIGHT_DEF 1944 | |
69 | ||
70 | #define OV5647_WINDOW_WIDTH 0x04 | |
71 | #define OV5647_WINDOW_WIDTH_MIN 2 | |
72 | #define OV5647_WINDOW_WIDTH_MAX 2752 | |
73 | #define OV5647_WINDOW_WIDTH_DEF 2592 | |
74 | ||
75 | struct regval_list { | |
76 | u16 addr; | |
77 | u8 data; | |
78 | }; | |
79 | ||
80 | struct ov5647 { | |
81 | struct v4l2_subdev sd; | |
82 | struct media_pad pad; | |
83 | struct mutex lock; | |
84 | struct v4l2_mbus_framefmt format; | |
85 | unsigned int width; | |
86 | unsigned int height; | |
87 | int power_count; | |
88 | struct clk *xclk; | |
89 | }; | |
90 | ||
91 | static inline struct ov5647 *to_state(struct v4l2_subdev *sd) | |
92 | { | |
93 | return container_of(sd, struct ov5647, sd); | |
94 | } | |
95 | ||
96 | static struct regval_list sensor_oe_disable_regs[] = { | |
97 | {0x3000, 0x00}, | |
98 | {0x3001, 0x00}, | |
99 | {0x3002, 0x00}, | |
100 | }; | |
101 | ||
102 | static struct regval_list sensor_oe_enable_regs[] = { | |
103 | {0x3000, 0x0f}, | |
104 | {0x3001, 0xff}, | |
105 | {0x3002, 0xe4}, | |
106 | }; | |
107 | ||
108 | static struct regval_list ov5647_640x480[] = { | |
109 | {0x0100, 0x00}, | |
110 | {0x0103, 0x01}, | |
111 | {0x3034, 0x08}, | |
112 | {0x3035, 0x21}, | |
113 | {0x3036, 0x46}, | |
114 | {0x303c, 0x11}, | |
115 | {0x3106, 0xf5}, | |
116 | {0x3821, 0x07}, | |
117 | {0x3820, 0x41}, | |
118 | {0x3827, 0xec}, | |
119 | {0x370c, 0x0f}, | |
120 | {0x3612, 0x59}, | |
121 | {0x3618, 0x00}, | |
122 | {0x5000, 0x06}, | |
123 | {0x5001, 0x01}, | |
124 | {0x5002, 0x41}, | |
125 | {0x5003, 0x08}, | |
126 | {0x5a00, 0x08}, | |
127 | {0x3000, 0x00}, | |
128 | {0x3001, 0x00}, | |
129 | {0x3002, 0x00}, | |
130 | {0x3016, 0x08}, | |
131 | {0x3017, 0xe0}, | |
132 | {0x3018, 0x44}, | |
133 | {0x301c, 0xf8}, | |
134 | {0x301d, 0xf0}, | |
135 | {0x3a18, 0x00}, | |
136 | {0x3a19, 0xf8}, | |
137 | {0x3c01, 0x80}, | |
138 | {0x3b07, 0x0c}, | |
139 | {0x380c, 0x07}, | |
140 | {0x380d, 0x68}, | |
141 | {0x380e, 0x03}, | |
142 | {0x380f, 0xd8}, | |
143 | {0x3814, 0x31}, | |
144 | {0x3815, 0x31}, | |
145 | {0x3708, 0x64}, | |
146 | {0x3709, 0x52}, | |
147 | {0x3808, 0x02}, | |
148 | {0x3809, 0x80}, | |
149 | {0x380a, 0x01}, | |
150 | {0x380b, 0xE0}, | |
151 | {0x3801, 0x00}, | |
152 | {0x3802, 0x00}, | |
153 | {0x3803, 0x00}, | |
154 | {0x3804, 0x0a}, | |
155 | {0x3805, 0x3f}, | |
156 | {0x3806, 0x07}, | |
157 | {0x3807, 0xa1}, | |
158 | {0x3811, 0x08}, | |
159 | {0x3813, 0x02}, | |
160 | {0x3630, 0x2e}, | |
161 | {0x3632, 0xe2}, | |
162 | {0x3633, 0x23}, | |
163 | {0x3634, 0x44}, | |
164 | {0x3636, 0x06}, | |
165 | {0x3620, 0x64}, | |
166 | {0x3621, 0xe0}, | |
167 | {0x3600, 0x37}, | |
168 | {0x3704, 0xa0}, | |
169 | {0x3703, 0x5a}, | |
170 | {0x3715, 0x78}, | |
171 | {0x3717, 0x01}, | |
172 | {0x3731, 0x02}, | |
173 | {0x370b, 0x60}, | |
174 | {0x3705, 0x1a}, | |
175 | {0x3f05, 0x02}, | |
176 | {0x3f06, 0x10}, | |
177 | {0x3f01, 0x0a}, | |
178 | {0x3a08, 0x01}, | |
179 | {0x3a09, 0x27}, | |
180 | {0x3a0a, 0x00}, | |
181 | {0x3a0b, 0xf6}, | |
182 | {0x3a0d, 0x04}, | |
183 | {0x3a0e, 0x03}, | |
184 | {0x3a0f, 0x58}, | |
185 | {0x3a10, 0x50}, | |
186 | {0x3a1b, 0x58}, | |
187 | {0x3a1e, 0x50}, | |
188 | {0x3a11, 0x60}, | |
189 | {0x3a1f, 0x28}, | |
190 | {0x4001, 0x02}, | |
191 | {0x4004, 0x02}, | |
192 | {0x4000, 0x09}, | |
193 | {0x4837, 0x24}, | |
194 | {0x4050, 0x6e}, | |
195 | {0x4051, 0x8f}, | |
196 | {0x0100, 0x01}, | |
197 | }; | |
198 | ||
199 | static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) | |
200 | { | |
201 | int ret; | |
202 | unsigned char data[3] = { reg >> 8, reg & 0xff, val}; | |
203 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
204 | ||
205 | ret = i2c_master_send(client, data, 3); | |
206 | if (ret < 0) | |
207 | dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", | |
208 | __func__, reg); | |
209 | ||
210 | return ret; | |
211 | } | |
212 | ||
213 | static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val) | |
214 | { | |
215 | int ret; | |
216 | unsigned char data_w[2] = { reg >> 8, reg & 0xff }; | |
217 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
218 | ||
219 | ret = i2c_master_send(client, data_w, 2); | |
220 | if (ret < 0) { | |
221 | dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", | |
222 | __func__, reg); | |
223 | return ret; | |
224 | } | |
225 | ||
226 | ret = i2c_master_recv(client, val, 1); | |
227 | if (ret < 0) | |
228 | dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n", | |
229 | __func__, reg); | |
230 | ||
231 | return ret; | |
232 | } | |
233 | ||
234 | static int ov5647_write_array(struct v4l2_subdev *sd, | |
235 | struct regval_list *regs, int array_size) | |
236 | { | |
237 | int i, ret; | |
238 | ||
239 | for (i = 0; i < array_size; i++) { | |
240 | ret = ov5647_write(sd, regs[i].addr, regs[i].data); | |
241 | if (ret < 0) | |
242 | return ret; | |
243 | } | |
244 | ||
245 | return 0; | |
246 | } | |
247 | ||
248 | static int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel) | |
249 | { | |
250 | u8 channel_id; | |
251 | int ret; | |
252 | ||
cef66734 | 253 | ret = ov5647_read(sd, OV5647_REG_MIPI_CTRL14, &channel_id); |
3c2472a3 RO |
254 | if (ret < 0) |
255 | return ret; | |
256 | ||
257 | channel_id &= ~(3 << 6); | |
cef66734 | 258 | return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, channel_id | (channel << 6)); |
3c2472a3 RO |
259 | } |
260 | ||
261 | static int ov5647_stream_on(struct v4l2_subdev *sd) | |
262 | { | |
263 | int ret; | |
264 | ||
cef66734 | 265 | ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, MIPI_CTRL00_BUS_IDLE); |
7bc9f038 JC |
266 | if (ret < 0) |
267 | return ret; | |
268 | ||
cef66734 | 269 | ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x00); |
3c2472a3 RO |
270 | if (ret < 0) |
271 | return ret; | |
272 | ||
cef66734 | 273 | return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x00); |
3c2472a3 RO |
274 | } |
275 | ||
276 | static int ov5647_stream_off(struct v4l2_subdev *sd) | |
277 | { | |
278 | int ret; | |
279 | ||
cef66734 JC |
280 | ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, MIPI_CTRL00_CLOCK_LANE_GATE |
281 | | MIPI_CTRL00_BUS_IDLE | MIPI_CTRL00_CLOCK_LANE_DISABLE); | |
7bc9f038 JC |
282 | if (ret < 0) |
283 | return ret; | |
284 | ||
cef66734 | 285 | ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x0f); |
3c2472a3 RO |
286 | if (ret < 0) |
287 | return ret; | |
288 | ||
cef66734 | 289 | return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x01); |
3c2472a3 RO |
290 | } |
291 | ||
292 | static int set_sw_standby(struct v4l2_subdev *sd, bool standby) | |
293 | { | |
294 | int ret; | |
295 | u8 rdval; | |
296 | ||
cef66734 | 297 | ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); |
3c2472a3 RO |
298 | if (ret < 0) |
299 | return ret; | |
300 | ||
301 | if (standby) | |
302 | rdval &= ~0x01; | |
303 | else | |
304 | rdval |= 0x01; | |
305 | ||
cef66734 | 306 | return ov5647_write(sd, OV5647_SW_STANDBY, rdval); |
3c2472a3 RO |
307 | } |
308 | ||
309 | static int __sensor_init(struct v4l2_subdev *sd) | |
310 | { | |
311 | int ret; | |
312 | u8 resetval, rdval; | |
313 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
314 | ||
cef66734 | 315 | ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); |
3c2472a3 RO |
316 | if (ret < 0) |
317 | return ret; | |
318 | ||
319 | ret = ov5647_write_array(sd, ov5647_640x480, | |
320 | ARRAY_SIZE(ov5647_640x480)); | |
321 | if (ret < 0) { | |
322 | dev_err(&client->dev, "write sensor default regs error\n"); | |
323 | return ret; | |
324 | } | |
325 | ||
326 | ret = ov5647_set_virtual_channel(sd, 0); | |
327 | if (ret < 0) | |
328 | return ret; | |
329 | ||
cef66734 | 330 | ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval); |
3c2472a3 RO |
331 | if (ret < 0) |
332 | return ret; | |
333 | ||
334 | if (!(resetval & 0x01)) { | |
335 | dev_err(&client->dev, "Device was in SW standby"); | |
cef66734 | 336 | ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01); |
3c2472a3 RO |
337 | if (ret < 0) |
338 | return ret; | |
339 | } | |
340 | ||
7bc9f038 JC |
341 | /* |
342 | * stream off to make the clock lane into LP-11 state. | |
343 | */ | |
344 | return ov5647_stream_off(sd); | |
3c2472a3 RO |
345 | } |
346 | ||
347 | static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) | |
348 | { | |
349 | int ret = 0; | |
350 | struct ov5647 *ov5647 = to_state(sd); | |
351 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
352 | ||
353 | mutex_lock(&ov5647->lock); | |
354 | ||
355 | if (on && !ov5647->power_count) { | |
356 | dev_dbg(&client->dev, "OV5647 power on\n"); | |
357 | ||
358 | ret = clk_prepare_enable(ov5647->xclk); | |
359 | if (ret < 0) { | |
360 | dev_err(&client->dev, "clk prepare enable failed\n"); | |
361 | goto out; | |
362 | } | |
363 | ||
364 | ret = ov5647_write_array(sd, sensor_oe_enable_regs, | |
365 | ARRAY_SIZE(sensor_oe_enable_regs)); | |
366 | if (ret < 0) { | |
367 | clk_disable_unprepare(ov5647->xclk); | |
368 | dev_err(&client->dev, | |
369 | "write sensor_oe_enable_regs error\n"); | |
370 | goto out; | |
371 | } | |
372 | ||
373 | ret = __sensor_init(sd); | |
374 | if (ret < 0) { | |
375 | clk_disable_unprepare(ov5647->xclk); | |
376 | dev_err(&client->dev, | |
377 | "Camera not available, check Power\n"); | |
378 | goto out; | |
379 | } | |
380 | } else if (!on && ov5647->power_count == 1) { | |
381 | dev_dbg(&client->dev, "OV5647 power off\n"); | |
382 | ||
383 | ret = ov5647_write_array(sd, sensor_oe_disable_regs, | |
384 | ARRAY_SIZE(sensor_oe_disable_regs)); | |
385 | ||
386 | if (ret < 0) | |
387 | dev_dbg(&client->dev, "disable oe failed\n"); | |
388 | ||
389 | ret = set_sw_standby(sd, true); | |
390 | ||
391 | if (ret < 0) | |
392 | dev_dbg(&client->dev, "soft stby failed\n"); | |
393 | ||
394 | clk_disable_unprepare(ov5647->xclk); | |
395 | } | |
396 | ||
397 | /* Update the power count. */ | |
398 | ov5647->power_count += on ? 1 : -1; | |
399 | WARN_ON(ov5647->power_count < 0); | |
400 | ||
401 | out: | |
402 | mutex_unlock(&ov5647->lock); | |
403 | ||
404 | return ret; | |
405 | } | |
406 | ||
407 | #ifdef CONFIG_VIDEO_ADV_DEBUG | |
408 | static int ov5647_sensor_get_register(struct v4l2_subdev *sd, | |
409 | struct v4l2_dbg_register *reg) | |
410 | { | |
411 | u8 val; | |
412 | int ret; | |
413 | ||
414 | ret = ov5647_read(sd, reg->reg & 0xff, &val); | |
415 | if (ret < 0) | |
416 | return ret; | |
417 | ||
418 | reg->val = val; | |
419 | reg->size = 1; | |
420 | ||
421 | return 0; | |
422 | } | |
423 | ||
424 | static int ov5647_sensor_set_register(struct v4l2_subdev *sd, | |
425 | const struct v4l2_dbg_register *reg) | |
426 | { | |
427 | return ov5647_write(sd, reg->reg & 0xff, reg->val & 0xff); | |
428 | } | |
429 | #endif | |
430 | ||
cba862dc MCC |
431 | /* |
432 | * Subdev core operations registration | |
3c2472a3 RO |
433 | */ |
434 | static const struct v4l2_subdev_core_ops ov5647_subdev_core_ops = { | |
435 | .s_power = ov5647_sensor_power, | |
436 | #ifdef CONFIG_VIDEO_ADV_DEBUG | |
437 | .g_register = ov5647_sensor_get_register, | |
438 | .s_register = ov5647_sensor_set_register, | |
439 | #endif | |
440 | }; | |
441 | ||
442 | static int ov5647_s_stream(struct v4l2_subdev *sd, int enable) | |
443 | { | |
444 | if (enable) | |
445 | return ov5647_stream_on(sd); | |
446 | else | |
447 | return ov5647_stream_off(sd); | |
448 | } | |
449 | ||
450 | static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = { | |
451 | .s_stream = ov5647_s_stream, | |
452 | }; | |
453 | ||
454 | static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, | |
455 | struct v4l2_subdev_pad_config *cfg, | |
456 | struct v4l2_subdev_mbus_code_enum *code) | |
457 | { | |
458 | if (code->index > 0) | |
459 | return -EINVAL; | |
460 | ||
461 | code->code = MEDIA_BUS_FMT_SBGGR8_1X8; | |
462 | ||
463 | return 0; | |
464 | } | |
465 | ||
466 | static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = { | |
467 | .enum_mbus_code = ov5647_enum_mbus_code, | |
468 | }; | |
469 | ||
470 | static const struct v4l2_subdev_ops ov5647_subdev_ops = { | |
471 | .core = &ov5647_subdev_core_ops, | |
472 | .video = &ov5647_subdev_video_ops, | |
473 | .pad = &ov5647_subdev_pad_ops, | |
474 | }; | |
475 | ||
476 | static int ov5647_detect(struct v4l2_subdev *sd) | |
477 | { | |
478 | u8 read; | |
479 | int ret; | |
480 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
481 | ||
482 | ret = ov5647_write(sd, OV5647_SW_RESET, 0x01); | |
483 | if (ret < 0) | |
484 | return ret; | |
485 | ||
486 | ret = ov5647_read(sd, OV5647_REG_CHIPID_H, &read); | |
487 | if (ret < 0) | |
488 | return ret; | |
489 | ||
490 | if (read != 0x56) { | |
491 | dev_err(&client->dev, "ID High expected 0x56 got %x", read); | |
492 | return -ENODEV; | |
493 | } | |
494 | ||
495 | ret = ov5647_read(sd, OV5647_REG_CHIPID_L, &read); | |
496 | if (ret < 0) | |
497 | return ret; | |
498 | ||
499 | if (read != 0x47) { | |
500 | dev_err(&client->dev, "ID Low expected 0x47 got %x", read); | |
501 | return -ENODEV; | |
502 | } | |
503 | ||
504 | return ov5647_write(sd, OV5647_SW_RESET, 0x00); | |
505 | } | |
506 | ||
507 | static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | |
508 | { | |
509 | struct v4l2_mbus_framefmt *format = | |
510 | v4l2_subdev_get_try_format(sd, fh->pad, 0); | |
511 | struct v4l2_rect *crop = | |
512 | v4l2_subdev_get_try_crop(sd, fh->pad, 0); | |
513 | ||
514 | crop->left = OV5647_COLUMN_START_DEF; | |
515 | crop->top = OV5647_ROW_START_DEF; | |
516 | crop->width = OV5647_WINDOW_WIDTH_DEF; | |
517 | crop->height = OV5647_WINDOW_HEIGHT_DEF; | |
518 | ||
519 | format->code = MEDIA_BUS_FMT_SBGGR8_1X8; | |
520 | ||
521 | format->width = OV5647_WINDOW_WIDTH_DEF; | |
522 | format->height = OV5647_WINDOW_HEIGHT_DEF; | |
523 | format->field = V4L2_FIELD_NONE; | |
524 | format->colorspace = V4L2_COLORSPACE_SRGB; | |
525 | ||
526 | return 0; | |
527 | } | |
528 | ||
529 | static const struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = { | |
530 | .open = ov5647_open, | |
531 | }; | |
532 | ||
533 | static int ov5647_parse_dt(struct device_node *np) | |
534 | { | |
60359a28 | 535 | struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; |
3c2472a3 RO |
536 | struct device_node *ep; |
537 | ||
538 | int ret; | |
539 | ||
540 | ep = of_graph_get_next_endpoint(np, NULL); | |
541 | if (!ep) | |
542 | return -EINVAL; | |
543 | ||
859969b3 | 544 | ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); |
3c2472a3 RO |
545 | |
546 | of_node_put(ep); | |
547 | return ret; | |
548 | } | |
549 | ||
550 | static int ov5647_probe(struct i2c_client *client, | |
551 | const struct i2c_device_id *id) | |
552 | { | |
553 | struct device *dev = &client->dev; | |
554 | struct ov5647 *sensor; | |
555 | int ret; | |
556 | struct v4l2_subdev *sd; | |
557 | struct device_node *np = client->dev.of_node; | |
558 | u32 xclk_freq; | |
559 | ||
560 | sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); | |
561 | if (!sensor) | |
562 | return -ENOMEM; | |
563 | ||
564 | if (IS_ENABLED(CONFIG_OF) && np) { | |
565 | ret = ov5647_parse_dt(np); | |
566 | if (ret) { | |
567 | dev_err(dev, "DT parsing error: %d\n", ret); | |
568 | return ret; | |
569 | } | |
570 | } | |
571 | ||
572 | /* get system clock (xclk) */ | |
573 | sensor->xclk = devm_clk_get(dev, NULL); | |
574 | if (IS_ERR(sensor->xclk)) { | |
575 | dev_err(dev, "could not get xclk"); | |
576 | return PTR_ERR(sensor->xclk); | |
577 | } | |
578 | ||
579 | xclk_freq = clk_get_rate(sensor->xclk); | |
580 | if (xclk_freq != 25000000) { | |
581 | dev_err(dev, "Unsupported clock frequency: %u\n", xclk_freq); | |
582 | return -EINVAL; | |
583 | } | |
584 | ||
585 | mutex_init(&sensor->lock); | |
586 | ||
587 | sd = &sensor->sd; | |
588 | v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops); | |
589 | sensor->sd.internal_ops = &ov5647_subdev_internal_ops; | |
590 | sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | |
591 | ||
592 | sensor->pad.flags = MEDIA_PAD_FL_SOURCE; | |
593 | sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; | |
594 | ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad); | |
595 | if (ret < 0) | |
596 | goto mutex_remove; | |
597 | ||
598 | ret = ov5647_detect(sd); | |
599 | if (ret < 0) | |
600 | goto error; | |
601 | ||
602 | ret = v4l2_async_register_subdev(sd); | |
603 | if (ret < 0) | |
604 | goto error; | |
605 | ||
606 | dev_dbg(dev, "OmniVision OV5647 camera driver probed\n"); | |
607 | return 0; | |
608 | error: | |
609 | media_entity_cleanup(&sd->entity); | |
610 | mutex_remove: | |
611 | mutex_destroy(&sensor->lock); | |
612 | return ret; | |
613 | } | |
614 | ||
615 | static int ov5647_remove(struct i2c_client *client) | |
616 | { | |
617 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | |
618 | struct ov5647 *ov5647 = to_state(sd); | |
619 | ||
620 | v4l2_async_unregister_subdev(&ov5647->sd); | |
621 | media_entity_cleanup(&ov5647->sd.entity); | |
622 | v4l2_device_unregister_subdev(sd); | |
623 | mutex_destroy(&ov5647->lock); | |
624 | ||
625 | return 0; | |
626 | } | |
627 | ||
628 | static const struct i2c_device_id ov5647_id[] = { | |
629 | { "ov5647", 0 }, | |
630 | { } | |
631 | }; | |
632 | MODULE_DEVICE_TABLE(i2c, ov5647_id); | |
633 | ||
634 | #if IS_ENABLED(CONFIG_OF) | |
635 | static const struct of_device_id ov5647_of_match[] = { | |
636 | { .compatible = "ovti,ov5647" }, | |
637 | { /* sentinel */ }, | |
638 | }; | |
639 | MODULE_DEVICE_TABLE(of, ov5647_of_match); | |
640 | #endif | |
641 | ||
642 | static struct i2c_driver ov5647_driver = { | |
643 | .driver = { | |
644 | .of_match_table = of_match_ptr(ov5647_of_match), | |
645 | .name = SENSOR_NAME, | |
646 | }, | |
647 | .probe = ov5647_probe, | |
648 | .remove = ov5647_remove, | |
649 | .id_table = ov5647_id, | |
650 | }; | |
651 | ||
652 | module_i2c_driver(ov5647_driver); | |
653 | ||
654 | MODULE_AUTHOR("Ramiro Oliveira <roliveir@synopsys.com>"); | |
655 | MODULE_DESCRIPTION("A low-level driver for OmniVision ov5647 sensors"); | |
656 | MODULE_LICENSE("GPL v2"); |