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