Commit | Line | Data |
---|---|---|
bc125106 HK |
1 | /* |
2 | * Driver for M-5MOLS 8M Pixel camera sensor with ISP | |
3 | * | |
4 | * Copyright (C) 2011 Samsung Electronics Co., Ltd. | |
c3070113 | 5 | * Author: HeungJun Kim <riverful.kim@samsung.com> |
bc125106 HK |
6 | * |
7 | * Copyright (C) 2009 Samsung Electronics Co., Ltd. | |
c3070113 | 8 | * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com> |
bc125106 HK |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or | |
13 | * (at your option) any later version. | |
14 | */ | |
15 | ||
16 | #include <linux/i2c.h> | |
17 | #include <linux/slab.h> | |
18 | #include <linux/irq.h> | |
19 | #include <linux/interrupt.h> | |
20 | #include <linux/delay.h> | |
bc125106 HK |
21 | #include <linux/gpio.h> |
22 | #include <linux/regulator/consumer.h> | |
23 | #include <linux/videodev2.h> | |
24 | #include <media/v4l2-ctrls.h> | |
25 | #include <media/v4l2-device.h> | |
26 | #include <media/v4l2-subdev.h> | |
27 | #include <media/m5mols.h> | |
28 | ||
29 | #include "m5mols.h" | |
30 | #include "m5mols_reg.h" | |
31 | ||
32 | int m5mols_debug; | |
33 | module_param(m5mols_debug, int, 0644); | |
34 | ||
35 | #define MODULE_NAME "M5MOLS" | |
36 | #define M5MOLS_I2C_CHECK_RETRY 500 | |
37 | ||
38 | /* The regulator consumer names for external voltage regulators */ | |
39 | static struct regulator_bulk_data supplies[] = { | |
40 | { | |
41 | .supply = "core", /* ARM core power, 1.2V */ | |
42 | }, { | |
43 | .supply = "dig_18", /* digital power 1, 1.8V */ | |
44 | }, { | |
45 | .supply = "d_sensor", /* sensor power 1, 1.8V */ | |
46 | }, { | |
47 | .supply = "dig_28", /* digital power 2, 2.8V */ | |
48 | }, { | |
49 | .supply = "a_sensor", /* analog power */ | |
50 | }, { | |
51 | .supply = "dig_12", /* digital power 3, 1.2V */ | |
52 | }, | |
53 | }; | |
54 | ||
55 | static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = { | |
56 | [M5MOLS_RESTYPE_MONITOR] = { | |
57 | .width = 1920, | |
58 | .height = 1080, | |
59 | .code = V4L2_MBUS_FMT_VYUY8_2X8, | |
60 | .field = V4L2_FIELD_NONE, | |
61 | .colorspace = V4L2_COLORSPACE_JPEG, | |
62 | }, | |
63 | [M5MOLS_RESTYPE_CAPTURE] = { | |
64 | .width = 1920, | |
65 | .height = 1080, | |
66 | .code = V4L2_MBUS_FMT_JPEG_1X8, | |
67 | .field = V4L2_FIELD_NONE, | |
68 | .colorspace = V4L2_COLORSPACE_JPEG, | |
69 | }, | |
70 | }; | |
71 | #define SIZE_DEFAULT_FFMT ARRAY_SIZE(m5mols_default_ffmt) | |
72 | ||
73 | static const struct m5mols_resolution m5mols_reg_res[] = { | |
74 | { 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 }, /* SUB-QCIF */ | |
75 | { 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 }, /* QQVGA */ | |
76 | { 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 }, /* QCIF */ | |
77 | { 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 }, | |
78 | { 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 }, /* QVGA */ | |
79 | { 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 }, /* QVGA */ | |
80 | { 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 }, /* WQVGA */ | |
81 | { 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 }, /* WQVGA */ | |
82 | { 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 }, /* CIF */ | |
83 | { 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 }, | |
84 | { 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 }, /* qHD */ | |
85 | { 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 }, /* VGA */ | |
86 | { 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 }, | |
87 | { 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 }, /* WVGA */ | |
88 | { 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 }, /* SVGA */ | |
89 | { 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 }, /* HD */ | |
90 | { 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 }, /* 1080p */ | |
91 | { 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 }, /* 2.63fps 8M */ | |
92 | { 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 }, /* AHS_MON debug */ | |
93 | ||
94 | { 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 }, /* QVGA */ | |
95 | { 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 }, /* WQVGA */ | |
96 | { 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 }, | |
97 | { 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 }, /* qHD */ | |
98 | { 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 }, /* VGA */ | |
99 | { 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 }, /* WVGA */ | |
100 | { 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 }, /* HD */ | |
101 | { 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 }, /* 1M */ | |
102 | { 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 }, /* 2M */ | |
103 | { 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 }, /* Full-HD */ | |
104 | { 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 }, /* 3Mega */ | |
105 | { 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 }, | |
106 | { 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 }, /* 4Mega */ | |
107 | { 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 }, | |
108 | { 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 }, /* 5Mega */ | |
109 | { 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 }, /* 6Mega */ | |
110 | { 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 }, | |
111 | { 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 }, /* 8Mega */ | |
112 | }; | |
113 | ||
114 | /** | |
115 | * m5mols_swap_byte - an byte array to integer conversion function | |
116 | * @size: size in bytes of I2C packet defined in the M-5MOLS datasheet | |
117 | * | |
118 | * Convert I2C data byte array with performing any required byte | |
119 | * reordering to assure proper values for each data type, regardless | |
120 | * of the architecture endianness. | |
121 | */ | |
122 | static u32 m5mols_swap_byte(u8 *data, u8 length) | |
123 | { | |
124 | if (length == 1) | |
125 | return *data; | |
126 | else if (length == 2) | |
127 | return be16_to_cpu(*((u16 *)data)); | |
128 | else | |
129 | return be32_to_cpu(*((u32 *)data)); | |
130 | } | |
131 | ||
132 | /** | |
133 | * m5mols_read - I2C read function | |
134 | * @reg: combination of size, category and command for the I2C packet | |
57644f56 | 135 | * @size: desired size of I2C packet |
bc125106 HK |
136 | * @val: read value |
137 | */ | |
57644f56 | 138 | static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val) |
bc125106 HK |
139 | { |
140 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
141 | u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1]; | |
bc125106 HK |
142 | u8 category = I2C_CATEGORY(reg); |
143 | u8 cmd = I2C_COMMAND(reg); | |
144 | struct i2c_msg msg[2]; | |
145 | u8 wbuf[5]; | |
146 | int ret; | |
147 | ||
148 | if (!client->adapter) | |
149 | return -ENODEV; | |
150 | ||
bc125106 HK |
151 | msg[0].addr = client->addr; |
152 | msg[0].flags = 0; | |
153 | msg[0].len = 5; | |
154 | msg[0].buf = wbuf; | |
155 | wbuf[0] = 5; | |
156 | wbuf[1] = M5MOLS_BYTE_READ; | |
157 | wbuf[2] = category; | |
158 | wbuf[3] = cmd; | |
159 | wbuf[4] = size; | |
160 | ||
161 | msg[1].addr = client->addr; | |
162 | msg[1].flags = I2C_M_RD; | |
163 | msg[1].len = size + 1; | |
164 | msg[1].buf = rbuf; | |
165 | ||
166 | /* minimum stabilization time */ | |
167 | usleep_range(200, 200); | |
168 | ||
169 | ret = i2c_transfer(client->adapter, msg, 2); | |
170 | if (ret < 0) { | |
171 | v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n", | |
172 | size, category, cmd, ret); | |
173 | return ret; | |
174 | } | |
175 | ||
176 | *val = m5mols_swap_byte(&rbuf[1], size); | |
177 | ||
178 | return 0; | |
179 | } | |
180 | ||
57644f56 HK |
181 | int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg, u8 *val) |
182 | { | |
183 | u32 val_32; | |
184 | int ret; | |
185 | ||
186 | if (I2C_SIZE(reg) != 1) { | |
187 | v4l2_err(sd, "Wrong data size\n"); | |
188 | return -EINVAL; | |
189 | } | |
190 | ||
191 | ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); | |
192 | if (ret) | |
193 | return ret; | |
194 | ||
195 | *val = (u8)val_32; | |
196 | return ret; | |
197 | } | |
198 | ||
199 | int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg, u16 *val) | |
200 | { | |
201 | u32 val_32; | |
202 | int ret; | |
203 | ||
204 | if (I2C_SIZE(reg) != 2) { | |
205 | v4l2_err(sd, "Wrong data size\n"); | |
206 | return -EINVAL; | |
207 | } | |
208 | ||
209 | ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); | |
210 | if (ret) | |
211 | return ret; | |
212 | ||
213 | *val = (u16)val_32; | |
214 | return ret; | |
215 | } | |
216 | ||
217 | int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg, u32 *val) | |
218 | { | |
219 | if (I2C_SIZE(reg) != 4) { | |
220 | v4l2_err(sd, "Wrong data size\n"); | |
221 | return -EINVAL; | |
222 | } | |
223 | ||
224 | return m5mols_read(sd, I2C_SIZE(reg), reg, val); | |
225 | } | |
226 | ||
bc125106 HK |
227 | /** |
228 | * m5mols_write - I2C command write function | |
229 | * @reg: combination of size, category and command for the I2C packet | |
230 | * @val: value to write | |
231 | */ | |
232 | int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val) | |
233 | { | |
234 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
235 | u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4]; | |
236 | u8 category = I2C_CATEGORY(reg); | |
237 | u8 cmd = I2C_COMMAND(reg); | |
238 | u8 size = I2C_SIZE(reg); | |
239 | u32 *buf = (u32 *)&wbuf[4]; | |
240 | struct i2c_msg msg[1]; | |
241 | int ret; | |
242 | ||
243 | if (!client->adapter) | |
244 | return -ENODEV; | |
245 | ||
246 | if (size != 1 && size != 2 && size != 4) { | |
247 | v4l2_err(sd, "Wrong data size\n"); | |
248 | return -EINVAL; | |
249 | } | |
250 | ||
251 | msg->addr = client->addr; | |
252 | msg->flags = 0; | |
253 | msg->len = (u16)size + 4; | |
254 | msg->buf = wbuf; | |
255 | wbuf[0] = size + 4; | |
256 | wbuf[1] = M5MOLS_BYTE_WRITE; | |
257 | wbuf[2] = category; | |
258 | wbuf[3] = cmd; | |
259 | ||
260 | *buf = m5mols_swap_byte((u8 *)&val, size); | |
261 | ||
262 | usleep_range(200, 200); | |
263 | ||
264 | ret = i2c_transfer(client->adapter, msg, 1); | |
265 | if (ret < 0) { | |
266 | v4l2_err(sd, "write failed: size:%d cat:%02x cmd:%02x. %d\n", | |
267 | size, category, cmd, ret); | |
268 | return ret; | |
269 | } | |
270 | ||
271 | return 0; | |
272 | } | |
273 | ||
57644f56 | 274 | int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u8 mask) |
bc125106 | 275 | { |
57644f56 HK |
276 | u8 busy; |
277 | int i; | |
bc125106 HK |
278 | int ret; |
279 | ||
280 | for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) { | |
57644f56 | 281 | ret = m5mols_read_u8(sd, I2C_REG(category, cmd, 1), &busy); |
bc125106 HK |
282 | if (ret < 0) |
283 | return ret; | |
284 | if ((busy & mask) == mask) | |
285 | return 0; | |
286 | } | |
287 | return -EBUSY; | |
288 | } | |
289 | ||
290 | /** | |
291 | * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts | |
292 | * | |
293 | * Before writing desired interrupt value the INT_FACTOR register should | |
294 | * be read to clear pending interrupts. | |
295 | */ | |
57644f56 | 296 | int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg) |
bc125106 HK |
297 | { |
298 | struct m5mols_info *info = to_m5mols(sd); | |
57644f56 HK |
299 | u8 mask = is_available_af(info) ? REG_INT_AF : 0; |
300 | u8 dummy; | |
bc125106 HK |
301 | int ret; |
302 | ||
57644f56 | 303 | ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy); |
bc125106 HK |
304 | if (!ret) |
305 | ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask); | |
306 | return ret; | |
307 | } | |
308 | ||
309 | /** | |
310 | * m5mols_reg_mode - Write the mode and check busy status | |
311 | * | |
312 | * It always accompanies a little delay changing the M-5MOLS mode, so it is | |
313 | * needed checking current busy status to guarantee right mode. | |
314 | */ | |
57644f56 | 315 | static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode) |
bc125106 HK |
316 | { |
317 | int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode); | |
318 | ||
319 | return ret ? ret : m5mols_busy(sd, CAT_SYSTEM, CAT0_SYSMODE, mode); | |
320 | } | |
321 | ||
322 | /** | |
323 | * m5mols_mode - manage the M-5MOLS's mode | |
324 | * @mode: the required operation mode | |
325 | * | |
326 | * The commands of M-5MOLS are grouped into specific modes. Each functionality | |
327 | * can be guaranteed only when the sensor is operating in mode which which | |
328 | * a command belongs to. | |
329 | */ | |
57644f56 | 330 | int m5mols_mode(struct m5mols_info *info, u8 mode) |
bc125106 HK |
331 | { |
332 | struct v4l2_subdev *sd = &info->sd; | |
333 | int ret = -EINVAL; | |
57644f56 | 334 | u8 reg; |
bc125106 HK |
335 | |
336 | if (mode < REG_PARAMETER && mode > REG_CAPTURE) | |
337 | return ret; | |
338 | ||
57644f56 | 339 | ret = m5mols_read_u8(sd, SYSTEM_SYSMODE, ®); |
bc125106 HK |
340 | if ((!ret && reg == mode) || ret) |
341 | return ret; | |
342 | ||
343 | switch (reg) { | |
344 | case REG_PARAMETER: | |
345 | ret = m5mols_reg_mode(sd, REG_MONITOR); | |
346 | if (!ret && mode == REG_MONITOR) | |
347 | break; | |
348 | if (!ret) | |
349 | ret = m5mols_reg_mode(sd, REG_CAPTURE); | |
350 | break; | |
351 | ||
352 | case REG_MONITOR: | |
353 | if (mode == REG_PARAMETER) { | |
354 | ret = m5mols_reg_mode(sd, REG_PARAMETER); | |
355 | break; | |
356 | } | |
357 | ||
358 | ret = m5mols_reg_mode(sd, REG_CAPTURE); | |
359 | break; | |
360 | ||
361 | case REG_CAPTURE: | |
362 | ret = m5mols_reg_mode(sd, REG_MONITOR); | |
363 | if (!ret && mode == REG_MONITOR) | |
364 | break; | |
365 | if (!ret) | |
366 | ret = m5mols_reg_mode(sd, REG_PARAMETER); | |
367 | break; | |
368 | ||
369 | default: | |
370 | v4l2_warn(sd, "Wrong mode: %d\n", mode); | |
371 | } | |
372 | ||
373 | if (!ret) | |
374 | info->mode = mode; | |
375 | ||
376 | return ret; | |
377 | } | |
378 | ||
379 | /** | |
380 | * m5mols_get_version - retrieve full revisions information of M-5MOLS | |
381 | * | |
382 | * The version information includes revisions of hardware and firmware, | |
383 | * AutoFocus alghorithm version and the version string. | |
384 | */ | |
385 | static int m5mols_get_version(struct v4l2_subdev *sd) | |
386 | { | |
387 | struct m5mols_info *info = to_m5mols(sd); | |
a6354d2e HK |
388 | struct m5mols_version *ver = &info->ver; |
389 | u8 *str = ver->str; | |
390 | int i; | |
bc125106 HK |
391 | int ret; |
392 | ||
a6354d2e HK |
393 | ret = m5mols_read_u8(sd, SYSTEM_VER_CUSTOMER, &ver->customer); |
394 | if (!ret) | |
395 | ret = m5mols_read_u8(sd, SYSTEM_VER_PROJECT, &ver->project); | |
396 | if (!ret) | |
397 | ret = m5mols_read_u16(sd, SYSTEM_VER_FIRMWARE, &ver->fw); | |
398 | if (!ret) | |
399 | ret = m5mols_read_u16(sd, SYSTEM_VER_HARDWARE, &ver->hw); | |
400 | if (!ret) | |
401 | ret = m5mols_read_u16(sd, SYSTEM_VER_PARAMETER, &ver->param); | |
402 | if (!ret) | |
403 | ret = m5mols_read_u16(sd, SYSTEM_VER_AWB, &ver->awb); | |
404 | if (!ret) | |
405 | ret = m5mols_read_u8(sd, AF_VERSION, &ver->af); | |
406 | if (ret) | |
407 | return ret; | |
bc125106 | 408 | |
a6354d2e HK |
409 | for (i = 0; i < VERSION_STRING_SIZE; i++) { |
410 | ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]); | |
bc125106 HK |
411 | if (ret) |
412 | return ret; | |
a6354d2e | 413 | } |
bc125106 | 414 | |
a6354d2e HK |
415 | ver->fw = be16_to_cpu(ver->fw); |
416 | ver->hw = be16_to_cpu(ver->hw); | |
417 | ver->param = be16_to_cpu(ver->param); | |
418 | ver->awb = be16_to_cpu(ver->awb); | |
bc125106 HK |
419 | |
420 | v4l2_info(sd, "Manufacturer\t[%s]\n", | |
421 | is_manufacturer(info, REG_SAMSUNG_ELECTRO) ? | |
422 | "Samsung Electro-Machanics" : | |
423 | is_manufacturer(info, REG_SAMSUNG_OPTICS) ? | |
424 | "Samsung Fiber-Optics" : | |
425 | is_manufacturer(info, REG_SAMSUNG_TECHWIN) ? | |
426 | "Samsung Techwin" : "None"); | |
427 | v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n", | |
428 | info->ver.customer, info->ver.project); | |
429 | ||
430 | if (!is_available_af(info)) | |
431 | v4l2_info(sd, "No support Auto Focus on this firmware\n"); | |
432 | ||
433 | return ret; | |
434 | } | |
435 | ||
436 | /** | |
437 | * __find_restype - Lookup M-5MOLS resolution type according to pixel code | |
438 | * @code: pixel code | |
439 | */ | |
440 | static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code) | |
441 | { | |
442 | enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR; | |
443 | ||
444 | do { | |
445 | if (code == m5mols_default_ffmt[type].code) | |
446 | return type; | |
447 | } while (type++ != SIZE_DEFAULT_FFMT); | |
448 | ||
449 | return 0; | |
450 | } | |
451 | ||
452 | /** | |
453 | * __find_resolution - Lookup preset and type of M-5MOLS's resolution | |
454 | * @mf: pixel format to find/negotiate the resolution preset for | |
455 | * @type: M-5MOLS resolution type | |
456 | * @resolution: M-5MOLS resolution preset register value | |
457 | * | |
458 | * Find nearest resolution matching resolution preset and adjust mf | |
459 | * to supported values. | |
460 | */ | |
461 | static int __find_resolution(struct v4l2_subdev *sd, | |
462 | struct v4l2_mbus_framefmt *mf, | |
463 | enum m5mols_restype *type, | |
464 | u32 *resolution) | |
465 | { | |
466 | const struct m5mols_resolution *fsize = &m5mols_reg_res[0]; | |
467 | const struct m5mols_resolution *match = NULL; | |
468 | enum m5mols_restype stype = __find_restype(mf->code); | |
469 | int i = ARRAY_SIZE(m5mols_reg_res); | |
470 | unsigned int min_err = ~0; | |
471 | ||
472 | while (i--) { | |
473 | int err; | |
474 | if (stype == fsize->type) { | |
475 | err = abs(fsize->width - mf->width) | |
476 | + abs(fsize->height - mf->height); | |
477 | ||
478 | if (err < min_err) { | |
479 | min_err = err; | |
480 | match = fsize; | |
481 | } | |
482 | } | |
483 | fsize++; | |
484 | } | |
485 | if (match) { | |
486 | mf->width = match->width; | |
487 | mf->height = match->height; | |
488 | *resolution = match->reg; | |
489 | *type = stype; | |
490 | return 0; | |
491 | } | |
492 | ||
493 | return -EINVAL; | |
494 | } | |
495 | ||
496 | static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info, | |
497 | struct v4l2_subdev_fh *fh, | |
498 | enum v4l2_subdev_format_whence which, | |
499 | enum m5mols_restype type) | |
500 | { | |
501 | if (which == V4L2_SUBDEV_FORMAT_TRY) | |
502 | return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL; | |
503 | ||
504 | return &info->ffmt[type]; | |
505 | } | |
506 | ||
507 | static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | |
508 | struct v4l2_subdev_format *fmt) | |
509 | { | |
510 | struct m5mols_info *info = to_m5mols(sd); | |
511 | struct v4l2_mbus_framefmt *format; | |
512 | ||
513 | if (fmt->pad != 0) | |
514 | return -EINVAL; | |
515 | ||
516 | format = __find_format(info, fh, fmt->which, info->res_type); | |
517 | if (!format) | |
518 | return -EINVAL; | |
519 | ||
520 | fmt->format = *format; | |
521 | return 0; | |
522 | } | |
523 | ||
524 | static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | |
525 | struct v4l2_subdev_format *fmt) | |
526 | { | |
527 | struct m5mols_info *info = to_m5mols(sd); | |
528 | struct v4l2_mbus_framefmt *format = &fmt->format; | |
529 | struct v4l2_mbus_framefmt *sfmt; | |
530 | enum m5mols_restype type; | |
531 | u32 resolution = 0; | |
532 | int ret; | |
533 | ||
534 | if (fmt->pad != 0) | |
535 | return -EINVAL; | |
536 | ||
537 | ret = __find_resolution(sd, format, &type, &resolution); | |
538 | if (ret < 0) | |
539 | return ret; | |
540 | ||
541 | sfmt = __find_format(info, fh, fmt->which, type); | |
542 | if (!sfmt) | |
543 | return 0; | |
544 | ||
545 | *sfmt = m5mols_default_ffmt[type]; | |
546 | sfmt->width = format->width; | |
547 | sfmt->height = format->height; | |
548 | ||
549 | if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { | |
550 | info->resolution = resolution; | |
551 | info->code = format->code; | |
552 | info->res_type = type; | |
553 | } | |
554 | ||
555 | return 0; | |
556 | } | |
557 | ||
558 | static int m5mols_enum_mbus_code(struct v4l2_subdev *sd, | |
559 | struct v4l2_subdev_fh *fh, | |
560 | struct v4l2_subdev_mbus_code_enum *code) | |
561 | { | |
562 | if (!code || code->index >= SIZE_DEFAULT_FFMT) | |
563 | return -EINVAL; | |
564 | ||
565 | code->code = m5mols_default_ffmt[code->index].code; | |
566 | ||
567 | return 0; | |
568 | } | |
569 | ||
570 | static struct v4l2_subdev_pad_ops m5mols_pad_ops = { | |
571 | .enum_mbus_code = m5mols_enum_mbus_code, | |
572 | .get_fmt = m5mols_get_fmt, | |
573 | .set_fmt = m5mols_set_fmt, | |
574 | }; | |
575 | ||
576 | /** | |
577 | * m5mols_sync_controls - Apply default scene mode and the current controls | |
578 | * | |
579 | * This is used only streaming for syncing between v4l2_ctrl framework and | |
580 | * m5mols's controls. First, do the scenemode to the sensor, then call | |
581 | * v4l2_ctrl_handler_setup. It can be same between some commands and | |
582 | * the scenemode's in the default v4l2_ctrls. But, such commands of control | |
583 | * should be prior to the scenemode's one. | |
584 | */ | |
585 | int m5mols_sync_controls(struct m5mols_info *info) | |
586 | { | |
587 | int ret = -EINVAL; | |
588 | ||
589 | if (!is_ctrl_synced(info)) { | |
590 | ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL); | |
591 | if (ret) | |
592 | return ret; | |
593 | ||
594 | v4l2_ctrl_handler_setup(&info->handle); | |
595 | info->ctrl_sync = true; | |
596 | } | |
597 | ||
598 | return ret; | |
599 | } | |
600 | ||
601 | /** | |
602 | * m5mols_start_monitor - Start the monitor mode | |
603 | * | |
604 | * Before applying the controls setup the resolution and frame rate | |
605 | * in PARAMETER mode, and then switch over to MONITOR mode. | |
606 | */ | |
607 | static int m5mols_start_monitor(struct m5mols_info *info) | |
608 | { | |
609 | struct v4l2_subdev *sd = &info->sd; | |
610 | int ret; | |
611 | ||
612 | ret = m5mols_mode(info, REG_PARAMETER); | |
613 | if (!ret) | |
614 | ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution); | |
615 | if (!ret) | |
616 | ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30); | |
617 | if (!ret) | |
618 | ret = m5mols_mode(info, REG_MONITOR); | |
619 | if (!ret) | |
620 | ret = m5mols_sync_controls(info); | |
621 | ||
622 | return ret; | |
623 | } | |
624 | ||
625 | static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) | |
626 | { | |
627 | struct m5mols_info *info = to_m5mols(sd); | |
628 | ||
629 | if (enable) { | |
630 | int ret = -EINVAL; | |
631 | ||
632 | if (is_code(info->code, M5MOLS_RESTYPE_MONITOR)) | |
633 | ret = m5mols_start_monitor(info); | |
634 | if (is_code(info->code, M5MOLS_RESTYPE_CAPTURE)) | |
635 | ret = m5mols_start_capture(info); | |
636 | ||
637 | return ret; | |
638 | } | |
639 | ||
640 | return m5mols_mode(info, REG_PARAMETER); | |
641 | } | |
642 | ||
643 | static const struct v4l2_subdev_video_ops m5mols_video_ops = { | |
644 | .s_stream = m5mols_s_stream, | |
645 | }; | |
646 | ||
647 | static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) | |
648 | { | |
649 | struct v4l2_subdev *sd = to_sd(ctrl); | |
650 | struct m5mols_info *info = to_m5mols(sd); | |
651 | int ret; | |
652 | ||
653 | info->mode_save = info->mode; | |
654 | ||
655 | ret = m5mols_mode(info, REG_PARAMETER); | |
656 | if (!ret) | |
657 | ret = m5mols_set_ctrl(ctrl); | |
658 | if (!ret) | |
659 | ret = m5mols_mode(info, info->mode_save); | |
660 | ||
661 | return ret; | |
662 | } | |
663 | ||
664 | static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { | |
665 | .s_ctrl = m5mols_s_ctrl, | |
666 | }; | |
667 | ||
668 | static int m5mols_sensor_power(struct m5mols_info *info, bool enable) | |
669 | { | |
670 | struct v4l2_subdev *sd = &info->sd; | |
671 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
672 | const struct m5mols_platform_data *pdata = info->pdata; | |
673 | int ret; | |
674 | ||
675 | if (enable) { | |
676 | if (is_powered(info)) | |
677 | return 0; | |
678 | ||
679 | if (info->set_power) { | |
680 | ret = info->set_power(&client->dev, 1); | |
681 | if (ret) | |
682 | return ret; | |
683 | } | |
684 | ||
685 | ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); | |
686 | if (ret) { | |
687 | info->set_power(&client->dev, 0); | |
688 | return ret; | |
689 | } | |
690 | ||
691 | gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity); | |
692 | usleep_range(1000, 1000); | |
693 | info->power = true; | |
694 | ||
695 | return ret; | |
696 | } | |
697 | ||
698 | if (!is_powered(info)) | |
699 | return 0; | |
700 | ||
701 | ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); | |
702 | if (ret) | |
703 | return ret; | |
704 | ||
705 | if (info->set_power) | |
706 | info->set_power(&client->dev, 0); | |
707 | ||
708 | gpio_set_value(pdata->gpio_reset, pdata->reset_polarity); | |
709 | usleep_range(1000, 1000); | |
710 | info->power = false; | |
711 | ||
712 | return ret; | |
713 | } | |
714 | ||
715 | /* m5mols_update_fw - optional firmware update routine */ | |
716 | int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd, | |
717 | int (*set_power)(struct m5mols_info *, bool)) | |
718 | { | |
719 | return 0; | |
720 | } | |
721 | ||
722 | /** | |
723 | * m5mols_sensor_armboot - Booting M-5MOLS internal ARM core. | |
724 | * | |
725 | * Booting internal ARM core makes the M-5MOLS is ready for getting commands | |
726 | * with I2C. It's the first thing to be done after it powered up. It must wait | |
727 | * at least 520ms recommended by M-5MOLS datasheet, after executing arm booting. | |
728 | */ | |
729 | static int m5mols_sensor_armboot(struct v4l2_subdev *sd) | |
730 | { | |
731 | int ret; | |
732 | ||
733 | ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT); | |
734 | if (ret < 0) | |
735 | return ret; | |
736 | ||
737 | msleep(520); | |
738 | ||
739 | ret = m5mols_get_version(sd); | |
740 | if (!ret) | |
741 | ret = m5mols_update_fw(sd, m5mols_sensor_power); | |
742 | if (ret) | |
743 | return ret; | |
744 | ||
745 | v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n"); | |
746 | ||
747 | ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI); | |
748 | if (!ret) | |
749 | ret = m5mols_enable_interrupt(sd, REG_INT_AF); | |
750 | ||
751 | return ret; | |
752 | } | |
753 | ||
754 | static int m5mols_init_controls(struct m5mols_info *info) | |
755 | { | |
756 | struct v4l2_subdev *sd = &info->sd; | |
757 | u16 max_exposure; | |
758 | u16 step_zoom; | |
759 | int ret; | |
760 | ||
761 | /* Determine value's range & step of controls for various FW version */ | |
57644f56 | 762 | ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &max_exposure); |
bc125106 HK |
763 | if (!ret) |
764 | step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; | |
765 | if (ret) | |
766 | return ret; | |
767 | ||
768 | v4l2_ctrl_handler_init(&info->handle, 6); | |
769 | info->autowb = v4l2_ctrl_new_std(&info->handle, | |
770 | &m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, | |
771 | 0, 1, 1, 0); | |
772 | info->saturation = v4l2_ctrl_new_std(&info->handle, | |
773 | &m5mols_ctrl_ops, V4L2_CID_SATURATION, | |
774 | 1, 5, 1, 3); | |
775 | info->zoom = v4l2_ctrl_new_std(&info->handle, | |
776 | &m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE, | |
777 | 1, 70, step_zoom, 1); | |
778 | info->exposure = v4l2_ctrl_new_std(&info->handle, | |
779 | &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, | |
780 | 0, max_exposure, 1, (int)max_exposure/2); | |
781 | info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, | |
782 | &m5mols_ctrl_ops, V4L2_CID_COLORFX, | |
783 | 4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE); | |
784 | info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle, | |
785 | &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, | |
786 | 1, 0, V4L2_EXPOSURE_MANUAL); | |
787 | ||
788 | sd->ctrl_handler = &info->handle; | |
789 | if (info->handle.error) { | |
790 | v4l2_err(sd, "Failed to initialize controls: %d\n", ret); | |
791 | v4l2_ctrl_handler_free(&info->handle); | |
792 | return info->handle.error; | |
793 | } | |
794 | ||
795 | v4l2_ctrl_cluster(2, &info->autoexposure); | |
796 | ||
797 | return 0; | |
798 | } | |
799 | ||
800 | /** | |
801 | * m5mols_s_power - Main sensor power control function | |
802 | * | |
803 | * To prevent breaking the lens when the sensor is powered off the Soft-Landing | |
804 | * algorithm is called where available. The Soft-Landing algorithm availability | |
805 | * dependends on the firmware provider. | |
806 | */ | |
807 | static int m5mols_s_power(struct v4l2_subdev *sd, int on) | |
808 | { | |
809 | struct m5mols_info *info = to_m5mols(sd); | |
810 | int ret; | |
811 | ||
812 | if (on) { | |
813 | ret = m5mols_sensor_power(info, true); | |
814 | if (!ret) | |
815 | ret = m5mols_sensor_armboot(sd); | |
816 | if (!ret) | |
817 | ret = m5mols_init_controls(info); | |
818 | if (ret) | |
819 | return ret; | |
820 | ||
821 | info->ffmt[M5MOLS_RESTYPE_MONITOR] = | |
822 | m5mols_default_ffmt[M5MOLS_RESTYPE_MONITOR]; | |
823 | info->ffmt[M5MOLS_RESTYPE_CAPTURE] = | |
824 | m5mols_default_ffmt[M5MOLS_RESTYPE_CAPTURE]; | |
825 | return ret; | |
826 | } | |
827 | ||
828 | if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) { | |
829 | ret = m5mols_mode(info, REG_MONITOR); | |
830 | if (!ret) | |
831 | ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP); | |
832 | if (!ret) | |
833 | ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF); | |
834 | if (!ret) | |
835 | ret = m5mols_busy(sd, CAT_SYSTEM, CAT0_STATUS, | |
836 | REG_AF_IDLE); | |
837 | if (!ret) | |
838 | v4l2_info(sd, "Success soft-landing lens\n"); | |
839 | } | |
840 | ||
841 | ret = m5mols_sensor_power(info, false); | |
842 | if (!ret) { | |
843 | v4l2_ctrl_handler_free(&info->handle); | |
844 | info->ctrl_sync = false; | |
845 | } | |
846 | ||
847 | return ret; | |
848 | } | |
849 | ||
850 | static int m5mols_log_status(struct v4l2_subdev *sd) | |
851 | { | |
852 | struct m5mols_info *info = to_m5mols(sd); | |
853 | ||
854 | v4l2_ctrl_handler_log_status(&info->handle, sd->name); | |
855 | ||
856 | return 0; | |
857 | } | |
858 | ||
859 | static const struct v4l2_subdev_core_ops m5mols_core_ops = { | |
860 | .s_power = m5mols_s_power, | |
861 | .g_ctrl = v4l2_subdev_g_ctrl, | |
862 | .s_ctrl = v4l2_subdev_s_ctrl, | |
863 | .queryctrl = v4l2_subdev_queryctrl, | |
864 | .querymenu = v4l2_subdev_querymenu, | |
865 | .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, | |
866 | .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, | |
867 | .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, | |
868 | .log_status = m5mols_log_status, | |
869 | }; | |
870 | ||
871 | static const struct v4l2_subdev_ops m5mols_ops = { | |
872 | .core = &m5mols_core_ops, | |
873 | .pad = &m5mols_pad_ops, | |
874 | .video = &m5mols_video_ops, | |
875 | }; | |
876 | ||
877 | static void m5mols_irq_work(struct work_struct *work) | |
878 | { | |
879 | struct m5mols_info *info = | |
880 | container_of(work, struct m5mols_info, work_irq); | |
881 | struct v4l2_subdev *sd = &info->sd; | |
57644f56 | 882 | u8 reg; |
bc125106 HK |
883 | int ret; |
884 | ||
885 | if (!is_powered(info) || | |
57644f56 | 886 | m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &info->interrupt)) |
bc125106 HK |
887 | return; |
888 | ||
889 | switch (info->interrupt & REG_INT_MASK) { | |
890 | case REG_INT_AF: | |
891 | if (!is_available_af(info)) | |
892 | break; | |
57644f56 | 893 | ret = m5mols_read_u8(sd, AF_STATUS, ®); |
bc125106 HK |
894 | v4l2_dbg(2, m5mols_debug, sd, "AF %s\n", |
895 | reg == REG_AF_FAIL ? "Failed" : | |
896 | reg == REG_AF_SUCCESS ? "Success" : | |
897 | reg == REG_AF_IDLE ? "Idle" : "Busy"); | |
898 | break; | |
899 | case REG_INT_CAPTURE: | |
900 | if (!test_and_set_bit(ST_CAPT_IRQ, &info->flags)) | |
901 | wake_up_interruptible(&info->irq_waitq); | |
902 | ||
903 | v4l2_dbg(2, m5mols_debug, sd, "CAPTURE\n"); | |
904 | break; | |
905 | default: | |
906 | v4l2_dbg(2, m5mols_debug, sd, "Undefined: %02x\n", reg); | |
907 | break; | |
908 | }; | |
909 | } | |
910 | ||
911 | static irqreturn_t m5mols_irq_handler(int irq, void *data) | |
912 | { | |
913 | struct v4l2_subdev *sd = data; | |
914 | struct m5mols_info *info = to_m5mols(sd); | |
915 | ||
916 | schedule_work(&info->work_irq); | |
917 | ||
918 | return IRQ_HANDLED; | |
919 | } | |
920 | ||
921 | static int __devinit m5mols_probe(struct i2c_client *client, | |
922 | const struct i2c_device_id *id) | |
923 | { | |
924 | const struct m5mols_platform_data *pdata = client->dev.platform_data; | |
925 | struct m5mols_info *info; | |
926 | struct v4l2_subdev *sd; | |
927 | int ret; | |
928 | ||
929 | if (pdata == NULL) { | |
930 | dev_err(&client->dev, "No platform data\n"); | |
931 | return -EINVAL; | |
932 | } | |
933 | ||
934 | if (!gpio_is_valid(pdata->gpio_reset)) { | |
935 | dev_err(&client->dev, "No valid RESET GPIO specified\n"); | |
936 | return -EINVAL; | |
937 | } | |
938 | ||
939 | if (!pdata->irq) { | |
940 | dev_err(&client->dev, "Interrupt not assigned\n"); | |
941 | return -EINVAL; | |
942 | } | |
943 | ||
944 | info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL); | |
945 | if (!info) | |
946 | return -ENOMEM; | |
947 | ||
948 | info->pdata = pdata; | |
949 | info->set_power = pdata->set_power; | |
950 | ||
951 | ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST"); | |
952 | if (ret) { | |
953 | dev_err(&client->dev, "Failed to request gpio: %d\n", ret); | |
954 | goto out_free; | |
955 | } | |
956 | gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity); | |
957 | ||
958 | ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies); | |
959 | if (ret) { | |
960 | dev_err(&client->dev, "Failed to get regulators: %d\n", ret); | |
961 | goto out_gpio; | |
962 | } | |
963 | ||
964 | sd = &info->sd; | |
965 | strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); | |
966 | v4l2_i2c_subdev_init(sd, client, &m5mols_ops); | |
967 | ||
968 | info->pad.flags = MEDIA_PAD_FL_SOURCE; | |
969 | ret = media_entity_init(&sd->entity, 1, &info->pad, 0); | |
970 | if (ret < 0) | |
971 | goto out_reg; | |
972 | sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; | |
973 | ||
974 | init_waitqueue_head(&info->irq_waitq); | |
975 | INIT_WORK(&info->work_irq, m5mols_irq_work); | |
976 | ret = request_irq(pdata->irq, m5mols_irq_handler, | |
977 | IRQF_TRIGGER_RISING, MODULE_NAME, sd); | |
978 | if (ret) { | |
979 | dev_err(&client->dev, "Interrupt request failed: %d\n", ret); | |
980 | goto out_me; | |
981 | } | |
982 | info->res_type = M5MOLS_RESTYPE_MONITOR; | |
983 | return 0; | |
984 | out_me: | |
985 | media_entity_cleanup(&sd->entity); | |
986 | out_reg: | |
987 | regulator_bulk_free(ARRAY_SIZE(supplies), supplies); | |
988 | out_gpio: | |
989 | gpio_free(pdata->gpio_reset); | |
990 | out_free: | |
991 | kfree(info); | |
992 | return ret; | |
993 | } | |
994 | ||
995 | static int __devexit m5mols_remove(struct i2c_client *client) | |
996 | { | |
997 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | |
998 | struct m5mols_info *info = to_m5mols(sd); | |
999 | ||
1000 | v4l2_device_unregister_subdev(sd); | |
1001 | free_irq(info->pdata->irq, sd); | |
1002 | ||
1003 | regulator_bulk_free(ARRAY_SIZE(supplies), supplies); | |
1004 | gpio_free(info->pdata->gpio_reset); | |
1005 | media_entity_cleanup(&sd->entity); | |
1006 | kfree(info); | |
1007 | return 0; | |
1008 | } | |
1009 | ||
1010 | static const struct i2c_device_id m5mols_id[] = { | |
1011 | { MODULE_NAME, 0 }, | |
1012 | { }, | |
1013 | }; | |
1014 | MODULE_DEVICE_TABLE(i2c, m5mols_id); | |
1015 | ||
1016 | static struct i2c_driver m5mols_i2c_driver = { | |
1017 | .driver = { | |
1018 | .name = MODULE_NAME, | |
1019 | }, | |
1020 | .probe = m5mols_probe, | |
1021 | .remove = __devexit_p(m5mols_remove), | |
1022 | .id_table = m5mols_id, | |
1023 | }; | |
1024 | ||
1025 | static int __init m5mols_mod_init(void) | |
1026 | { | |
1027 | return i2c_add_driver(&m5mols_i2c_driver); | |
1028 | } | |
1029 | ||
1030 | static void __exit m5mols_mod_exit(void) | |
1031 | { | |
1032 | i2c_del_driver(&m5mols_i2c_driver); | |
1033 | } | |
1034 | ||
1035 | module_init(m5mols_mod_init); | |
1036 | module_exit(m5mols_mod_exit); | |
1037 | ||
1038 | MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>"); | |
1039 | MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>"); | |
1040 | MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver"); | |
1041 | MODULE_LICENSE("GPL"); |