diff options
author | Andrzej Hajda <a.hajda@samsung.com> | 2012-11-22 09:39:18 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2013-02-05 14:39:04 -0500 |
commit | cac47f1822fcb97018e24b05a7fb31f11a6bc28c (patch) | |
tree | 12f68b61d933c8885354b388244cce9136f571f6 /drivers/media/i2c/s5c73m3 | |
parent | 6e83e6e25eb49dc57a69b3f8ecc1e764c9775101 (diff) |
[media] V4L: Add S5C73M3 camera driver
Add driver for S5C73M3 image sensor. The driver exposes the sensor as
two subdevs: pure sensor and output interface. Two subdev architecture
supports interleaved UYVY/JPEG image format with separate frame size
for both sub-formats, there is a spearate pad for each sub-format.
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/i2c/s5c73m3')
-rw-r--r-- | drivers/media/i2c/s5c73m3/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/i2c/s5c73m3/s5c73m3-core.c | 1706 | ||||
-rw-r--r-- | drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c | 563 | ||||
-rw-r--r-- | drivers/media/i2c/s5c73m3/s5c73m3-spi.c | 156 | ||||
-rw-r--r-- | drivers/media/i2c/s5c73m3/s5c73m3.h | 459 |
5 files changed, 2886 insertions, 0 deletions
diff --git a/drivers/media/i2c/s5c73m3/Makefile b/drivers/media/i2c/s5c73m3/Makefile new file mode 100644 index 000000000000..fa4df342d1f1 --- /dev/null +++ b/drivers/media/i2c/s5c73m3/Makefile | |||
@@ -0,0 +1,2 @@ | |||
1 | s5c73m3-objs := s5c73m3-core.o s5c73m3-spi.o s5c73m3-ctrls.o | ||
2 | obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3.o | ||
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c new file mode 100644 index 000000000000..600909ddb150 --- /dev/null +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c | |||
@@ -0,0 +1,1706 @@ | |||
1 | /* | ||
2 | * Samsung LSI S5C73M3 8M pixel camera driver | ||
3 | * | ||
4 | * Copyright (C) 2012, Samsung Electronics, Co., Ltd. | ||
5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
6 | * Andrzej Hajda <a.hajda@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/sizes.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/firmware.h> | ||
21 | #include <linux/gpio.h> | ||
22 | #include <linux/i2c.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/media.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/regulator/consumer.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/spi/spi.h> | ||
29 | #include <linux/videodev2.h> | ||
30 | #include <media/media-entity.h> | ||
31 | #include <media/v4l2-ctrls.h> | ||
32 | #include <media/v4l2-device.h> | ||
33 | #include <media/v4l2-subdev.h> | ||
34 | #include <media/v4l2-mediabus.h> | ||
35 | #include <media/s5c73m3.h> | ||
36 | |||
37 | #include "s5c73m3.h" | ||
38 | |||
39 | int s5c73m3_dbg; | ||
40 | module_param_named(debug, s5c73m3_dbg, int, 0644); | ||
41 | |||
42 | int boot_from_rom = 1; | ||
43 | module_param(boot_from_rom, int, 0644); | ||
44 | |||
45 | int update_fw; | ||
46 | module_param(update_fw, int, 0644); | ||
47 | |||
48 | #define S5C73M3_EMBEDDED_DATA_MAXLEN SZ_4K | ||
49 | |||
50 | static const char * const s5c73m3_supply_names[S5C73M3_MAX_SUPPLIES] = { | ||
51 | "vdd-int", /* Digital Core supply (1.2V), CAM_ISP_CORE_1.2V */ | ||
52 | "vdda", /* Analog Core supply (1.2V), CAM_SENSOR_CORE_1.2V */ | ||
53 | "vdd-reg", /* Regulator input supply (2.8V), CAM_SENSOR_A2.8V */ | ||
54 | "vddio-host", /* Digital Host I/O power supply (1.8V...2.8V), | ||
55 | CAM_ISP_SENSOR_1.8V */ | ||
56 | "vddio-cis", /* Digital CIS I/O power (1.2V...1.8V), | ||
57 | CAM_ISP_MIPI_1.2V */ | ||
58 | "vdd-af", /* Lens, CAM_AF_2.8V */ | ||
59 | }; | ||
60 | |||
61 | static const struct s5c73m3_frame_size s5c73m3_isp_resolutions[] = { | ||
62 | { 320, 240, COMM_CHG_MODE_YUV_320_240 }, | ||
63 | { 352, 288, COMM_CHG_MODE_YUV_352_288 }, | ||
64 | { 640, 480, COMM_CHG_MODE_YUV_640_480 }, | ||
65 | { 880, 720, COMM_CHG_MODE_YUV_880_720 }, | ||
66 | { 960, 720, COMM_CHG_MODE_YUV_960_720 }, | ||
67 | { 1008, 672, COMM_CHG_MODE_YUV_1008_672 }, | ||
68 | { 1184, 666, COMM_CHG_MODE_YUV_1184_666 }, | ||
69 | { 1280, 720, COMM_CHG_MODE_YUV_1280_720 }, | ||
70 | { 1536, 864, COMM_CHG_MODE_YUV_1536_864 }, | ||
71 | { 1600, 1200, COMM_CHG_MODE_YUV_1600_1200 }, | ||
72 | { 1632, 1224, COMM_CHG_MODE_YUV_1632_1224 }, | ||
73 | { 1920, 1080, COMM_CHG_MODE_YUV_1920_1080 }, | ||
74 | { 1920, 1440, COMM_CHG_MODE_YUV_1920_1440 }, | ||
75 | { 2304, 1296, COMM_CHG_MODE_YUV_2304_1296 }, | ||
76 | { 3264, 2448, COMM_CHG_MODE_YUV_3264_2448 }, | ||
77 | }; | ||
78 | |||
79 | static const struct s5c73m3_frame_size s5c73m3_jpeg_resolutions[] = { | ||
80 | { 640, 480, COMM_CHG_MODE_JPEG_640_480 }, | ||
81 | { 800, 450, COMM_CHG_MODE_JPEG_800_450 }, | ||
82 | { 800, 600, COMM_CHG_MODE_JPEG_800_600 }, | ||
83 | { 1024, 768, COMM_CHG_MODE_JPEG_1024_768 }, | ||
84 | { 1280, 720, COMM_CHG_MODE_JPEG_1280_720 }, | ||
85 | { 1280, 960, COMM_CHG_MODE_JPEG_1280_960 }, | ||
86 | { 1600, 900, COMM_CHG_MODE_JPEG_1600_900 }, | ||
87 | { 1600, 1200, COMM_CHG_MODE_JPEG_1600_1200 }, | ||
88 | { 2048, 1152, COMM_CHG_MODE_JPEG_2048_1152 }, | ||
89 | { 2048, 1536, COMM_CHG_MODE_JPEG_2048_1536 }, | ||
90 | { 2560, 1440, COMM_CHG_MODE_JPEG_2560_1440 }, | ||
91 | { 2560, 1920, COMM_CHG_MODE_JPEG_2560_1920 }, | ||
92 | { 3264, 1836, COMM_CHG_MODE_JPEG_3264_1836 }, | ||
93 | { 3264, 2176, COMM_CHG_MODE_JPEG_3264_2176 }, | ||
94 | { 3264, 2448, COMM_CHG_MODE_JPEG_3264_2448 }, | ||
95 | }; | ||
96 | |||
97 | static const struct s5c73m3_frame_size * const s5c73m3_resolutions[] = { | ||
98 | [RES_ISP] = s5c73m3_isp_resolutions, | ||
99 | [RES_JPEG] = s5c73m3_jpeg_resolutions | ||
100 | }; | ||
101 | |||
102 | static const int s5c73m3_resolutions_len[] = { | ||
103 | [RES_ISP] = ARRAY_SIZE(s5c73m3_isp_resolutions), | ||
104 | [RES_JPEG] = ARRAY_SIZE(s5c73m3_jpeg_resolutions) | ||
105 | }; | ||
106 | |||
107 | static const struct s5c73m3_interval s5c73m3_intervals[] = { | ||
108 | { COMM_FRAME_RATE_FIXED_7FPS, {142857, 1000000}, {3264, 2448} }, | ||
109 | { COMM_FRAME_RATE_FIXED_15FPS, {66667, 1000000}, {3264, 2448} }, | ||
110 | { COMM_FRAME_RATE_FIXED_20FPS, {50000, 1000000}, {2304, 1296} }, | ||
111 | { COMM_FRAME_RATE_FIXED_30FPS, {33333, 1000000}, {2304, 1296} }, | ||
112 | }; | ||
113 | |||
114 | #define S5C73M3_DEFAULT_FRAME_INTERVAL 3 /* 30 fps */ | ||
115 | |||
116 | static void s5c73m3_fill_mbus_fmt(struct v4l2_mbus_framefmt *mf, | ||
117 | const struct s5c73m3_frame_size *fs, | ||
118 | u32 code) | ||
119 | { | ||
120 | mf->width = fs->width; | ||
121 | mf->height = fs->height; | ||
122 | mf->code = code; | ||
123 | mf->colorspace = V4L2_COLORSPACE_JPEG; | ||
124 | mf->field = V4L2_FIELD_NONE; | ||
125 | } | ||
126 | |||
127 | static int s5c73m3_i2c_write(struct i2c_client *client, u16 addr, u16 data) | ||
128 | { | ||
129 | u8 buf[4] = { addr >> 8, addr & 0xff, data >> 8, data & 0xff }; | ||
130 | |||
131 | int ret = i2c_master_send(client, buf, sizeof(buf)); | ||
132 | |||
133 | v4l_dbg(4, s5c73m3_dbg, client, "%s: addr 0x%04x, data 0x%04x\n", | ||
134 | __func__, addr, data); | ||
135 | |||
136 | if (ret == 4) | ||
137 | return 0; | ||
138 | |||
139 | return ret < 0 ? ret : -EREMOTEIO; | ||
140 | } | ||
141 | |||
142 | static int s5c73m3_i2c_read(struct i2c_client *client, u16 addr, u16 *data) | ||
143 | { | ||
144 | int ret; | ||
145 | u8 rbuf[2], wbuf[2] = { addr >> 8, addr & 0xff }; | ||
146 | struct i2c_msg msg[2] = { | ||
147 | { | ||
148 | .addr = client->addr, | ||
149 | .flags = 0, | ||
150 | .len = sizeof(wbuf), | ||
151 | .buf = wbuf | ||
152 | }, { | ||
153 | .addr = client->addr, | ||
154 | .flags = I2C_M_RD, | ||
155 | .len = sizeof(rbuf), | ||
156 | .buf = rbuf | ||
157 | } | ||
158 | }; | ||
159 | /* | ||
160 | * Issue repeated START after writing 2 address bytes and | ||
161 | * just one STOP only after reading the data bytes. | ||
162 | */ | ||
163 | ret = i2c_transfer(client->adapter, msg, 2); | ||
164 | if (ret == 2) { | ||
165 | *data = be16_to_cpup((u16 *)rbuf); | ||
166 | v4l2_dbg(4, s5c73m3_dbg, client, | ||
167 | "%s: addr: 0x%04x, data: 0x%04x\n", | ||
168 | __func__, addr, *data); | ||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | v4l2_err(client, "I2C read failed: addr: %04x, (%d)\n", addr, ret); | ||
173 | |||
174 | return ret >= 0 ? -EREMOTEIO : ret; | ||
175 | } | ||
176 | |||
177 | int s5c73m3_write(struct s5c73m3 *state, u32 addr, u16 data) | ||
178 | { | ||
179 | struct i2c_client *client = state->i2c_client; | ||
180 | int ret; | ||
181 | |||
182 | if ((addr ^ state->i2c_write_address) & 0xffff0000) { | ||
183 | ret = s5c73m3_i2c_write(client, REG_CMDWR_ADDRH, addr >> 16); | ||
184 | if (ret < 0) { | ||
185 | state->i2c_write_address = 0; | ||
186 | return ret; | ||
187 | } | ||
188 | } | ||
189 | |||
190 | if ((addr ^ state->i2c_write_address) & 0xffff) { | ||
191 | ret = s5c73m3_i2c_write(client, REG_CMDWR_ADDRL, addr & 0xffff); | ||
192 | if (ret < 0) { | ||
193 | state->i2c_write_address = 0; | ||
194 | return ret; | ||
195 | } | ||
196 | } | ||
197 | |||
198 | state->i2c_write_address = addr; | ||
199 | |||
200 | ret = s5c73m3_i2c_write(client, REG_CMDBUF_ADDR, data); | ||
201 | if (ret < 0) | ||
202 | return ret; | ||
203 | |||
204 | state->i2c_write_address += 2; | ||
205 | |||
206 | return ret; | ||
207 | } | ||
208 | |||
209 | int s5c73m3_read(struct s5c73m3 *state, u32 addr, u16 *data) | ||
210 | { | ||
211 | struct i2c_client *client = state->i2c_client; | ||
212 | int ret; | ||
213 | |||
214 | if ((addr ^ state->i2c_read_address) & 0xffff0000) { | ||
215 | ret = s5c73m3_i2c_write(client, REG_CMDRD_ADDRH, addr >> 16); | ||
216 | if (ret < 0) { | ||
217 | state->i2c_read_address = 0; | ||
218 | return ret; | ||
219 | } | ||
220 | } | ||
221 | |||
222 | if ((addr ^ state->i2c_read_address) & 0xffff) { | ||
223 | ret = s5c73m3_i2c_write(client, REG_CMDRD_ADDRL, addr & 0xffff); | ||
224 | if (ret < 0) { | ||
225 | state->i2c_read_address = 0; | ||
226 | return ret; | ||
227 | } | ||
228 | } | ||
229 | |||
230 | state->i2c_read_address = addr; | ||
231 | |||
232 | ret = s5c73m3_i2c_read(client, REG_CMDBUF_ADDR, data); | ||
233 | if (ret < 0) | ||
234 | return ret; | ||
235 | |||
236 | state->i2c_read_address += 2; | ||
237 | |||
238 | return ret; | ||
239 | } | ||
240 | |||
241 | static int s5c73m3_check_status(struct s5c73m3 *state, unsigned int value) | ||
242 | { | ||
243 | unsigned long start = jiffies; | ||
244 | unsigned long end = start + msecs_to_jiffies(2000); | ||
245 | int ret = 0; | ||
246 | u16 status; | ||
247 | int count = 0; | ||
248 | |||
249 | while (time_is_after_jiffies(end)) { | ||
250 | ret = s5c73m3_read(state, REG_STATUS, &status); | ||
251 | if (ret < 0 || status == value) | ||
252 | break; | ||
253 | usleep_range(500, 1000); | ||
254 | ++count; | ||
255 | } | ||
256 | |||
257 | if (count > 0) | ||
258 | v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, | ||
259 | "status check took %dms\n", | ||
260 | jiffies_to_msecs(jiffies - start)); | ||
261 | |||
262 | if (ret == 0 && status != value) { | ||
263 | u16 i2c_status = 0; | ||
264 | u16 i2c_seq_status = 0; | ||
265 | |||
266 | s5c73m3_read(state, REG_I2C_STATUS, &i2c_status); | ||
267 | s5c73m3_read(state, REG_I2C_SEQ_STATUS, &i2c_seq_status); | ||
268 | |||
269 | v4l2_err(&state->sensor_sd, | ||
270 | "wrong status %#x, expected: %#x, i2c_status: %#x/%#x\n", | ||
271 | status, value, i2c_status, i2c_seq_status); | ||
272 | |||
273 | return -ETIMEDOUT; | ||
274 | } | ||
275 | |||
276 | return ret; | ||
277 | } | ||
278 | |||
279 | int s5c73m3_isp_command(struct s5c73m3 *state, u16 command, u16 data) | ||
280 | { | ||
281 | int ret; | ||
282 | |||
283 | ret = s5c73m3_check_status(state, REG_STATUS_ISP_COMMAND_COMPLETED); | ||
284 | if (ret < 0) | ||
285 | return ret; | ||
286 | |||
287 | ret = s5c73m3_write(state, 0x00095000, command); | ||
288 | if (ret < 0) | ||
289 | return ret; | ||
290 | |||
291 | ret = s5c73m3_write(state, 0x00095002, data); | ||
292 | if (ret < 0) | ||
293 | return ret; | ||
294 | |||
295 | return s5c73m3_write(state, REG_STATUS, 0x0001); | ||
296 | } | ||
297 | |||
298 | int s5c73m3_isp_comm_result(struct s5c73m3 *state, u16 command, u16 *data) | ||
299 | { | ||
300 | return s5c73m3_read(state, COMM_RESULT_OFFSET + command, data); | ||
301 | } | ||
302 | |||
303 | static int s5c73m3_set_af_softlanding(struct s5c73m3 *state) | ||
304 | { | ||
305 | unsigned long start = jiffies; | ||
306 | u16 af_softlanding; | ||
307 | int count = 0; | ||
308 | int ret; | ||
309 | const char *msg; | ||
310 | |||
311 | ret = s5c73m3_isp_command(state, COMM_AF_SOFTLANDING, | ||
312 | COMM_AF_SOFTLANDING_ON); | ||
313 | if (ret < 0) { | ||
314 | v4l2_info(&state->sensor_sd, "AF soft-landing failed\n"); | ||
315 | return ret; | ||
316 | } | ||
317 | |||
318 | for (;;) { | ||
319 | ret = s5c73m3_isp_comm_result(state, COMM_AF_SOFTLANDING, | ||
320 | &af_softlanding); | ||
321 | if (ret < 0) { | ||
322 | msg = "failed"; | ||
323 | break; | ||
324 | } | ||
325 | if (af_softlanding == COMM_AF_SOFTLANDING_RES_COMPLETE) { | ||
326 | msg = "succeeded"; | ||
327 | break; | ||
328 | } | ||
329 | if (++count > 100) { | ||
330 | ret = -ETIME; | ||
331 | msg = "timed out"; | ||
332 | break; | ||
333 | } | ||
334 | msleep(25); | ||
335 | } | ||
336 | |||
337 | v4l2_info(&state->sensor_sd, "AF soft-landing %s after %dms\n", | ||
338 | msg, jiffies_to_msecs(jiffies - start)); | ||
339 | |||
340 | return ret; | ||
341 | } | ||
342 | |||
343 | static int s5c73m3_load_fw(struct v4l2_subdev *sd) | ||
344 | { | ||
345 | struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); | ||
346 | struct i2c_client *client = state->i2c_client; | ||
347 | const struct firmware *fw; | ||
348 | int ret; | ||
349 | char fw_name[20]; | ||
350 | |||
351 | snprintf(fw_name, sizeof(fw_name), "SlimISP_%.2s.bin", | ||
352 | state->fw_file_version); | ||
353 | ret = request_firmware(&fw, fw_name, &client->dev); | ||
354 | if (ret < 0) { | ||
355 | v4l2_err(sd, "Firmware request failed (%s)\n", fw_name); | ||
356 | return -EINVAL; | ||
357 | } | ||
358 | |||
359 | v4l2_info(sd, "Loading firmware (%s, %d B)\n", fw_name, fw->size); | ||
360 | |||
361 | ret = s5c73m3_spi_write(state, fw->data, fw->size, 64); | ||
362 | |||
363 | if (ret >= 0) | ||
364 | state->isp_ready = 1; | ||
365 | else | ||
366 | v4l2_err(sd, "SPI write failed\n"); | ||
367 | |||
368 | release_firmware(fw); | ||
369 | |||
370 | return ret; | ||
371 | } | ||
372 | |||
373 | static int s5c73m3_set_frame_size(struct s5c73m3 *state) | ||
374 | { | ||
375 | const struct s5c73m3_frame_size *prev_size = | ||
376 | state->sensor_pix_size[RES_ISP]; | ||
377 | const struct s5c73m3_frame_size *cap_size = | ||
378 | state->sensor_pix_size[RES_JPEG]; | ||
379 | unsigned int chg_mode; | ||
380 | |||
381 | v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, | ||
382 | "Preview size: %dx%d, reg_val: 0x%x\n", | ||
383 | prev_size->width, prev_size->height, prev_size->reg_val); | ||
384 | |||
385 | chg_mode = prev_size->reg_val | COMM_CHG_MODE_NEW; | ||
386 | |||
387 | if (state->mbus_code == S5C73M3_JPEG_FMT) { | ||
388 | v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, | ||
389 | "Capture size: %dx%d, reg_val: 0x%x\n", | ||
390 | cap_size->width, cap_size->height, cap_size->reg_val); | ||
391 | chg_mode |= cap_size->reg_val; | ||
392 | } | ||
393 | |||
394 | return s5c73m3_isp_command(state, COMM_CHG_MODE, chg_mode); | ||
395 | } | ||
396 | |||
397 | static int s5c73m3_set_frame_rate(struct s5c73m3 *state) | ||
398 | { | ||
399 | int ret; | ||
400 | |||
401 | if (state->ctrls.stabilization->val) | ||
402 | return 0; | ||
403 | |||
404 | if (WARN_ON(state->fiv == NULL)) | ||
405 | return -EINVAL; | ||
406 | |||
407 | ret = s5c73m3_isp_command(state, COMM_FRAME_RATE, state->fiv->fps_reg); | ||
408 | if (!ret) | ||
409 | state->apply_fiv = 0; | ||
410 | |||
411 | return ret; | ||
412 | } | ||
413 | |||
414 | static int __s5c73m3_s_stream(struct s5c73m3 *state, struct v4l2_subdev *sd, | ||
415 | int on) | ||
416 | { | ||
417 | u16 mode; | ||
418 | int ret; | ||
419 | |||
420 | if (on && state->apply_fmt) { | ||
421 | if (state->mbus_code == S5C73M3_JPEG_FMT) | ||
422 | mode = COMM_IMG_OUTPUT_INTERLEAVED; | ||
423 | else | ||
424 | mode = COMM_IMG_OUTPUT_YUV; | ||
425 | |||
426 | ret = s5c73m3_isp_command(state, COMM_IMG_OUTPUT, mode); | ||
427 | if (!ret) | ||
428 | ret = s5c73m3_set_frame_size(state); | ||
429 | if (ret) | ||
430 | return ret; | ||
431 | state->apply_fmt = 0; | ||
432 | } | ||
433 | |||
434 | ret = s5c73m3_isp_command(state, COMM_SENSOR_STREAMING, !!on); | ||
435 | if (ret) | ||
436 | return ret; | ||
437 | |||
438 | state->streaming = !!on; | ||
439 | |||
440 | if (!on) | ||
441 | return ret; | ||
442 | |||
443 | if (state->apply_fiv) { | ||
444 | ret = s5c73m3_set_frame_rate(state); | ||
445 | if (ret < 0) | ||
446 | v4l2_err(sd, "Error setting frame rate(%d)\n", ret); | ||
447 | } | ||
448 | |||
449 | return s5c73m3_check_status(state, REG_STATUS_ISP_COMMAND_COMPLETED); | ||
450 | } | ||
451 | |||
452 | static int s5c73m3_oif_s_stream(struct v4l2_subdev *sd, int on) | ||
453 | { | ||
454 | struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); | ||
455 | int ret; | ||
456 | |||
457 | mutex_lock(&state->lock); | ||
458 | ret = __s5c73m3_s_stream(state, sd, on); | ||
459 | mutex_unlock(&state->lock); | ||
460 | |||
461 | return ret; | ||
462 | } | ||
463 | |||
464 | static int s5c73m3_system_status_wait(struct s5c73m3 *state, u32 value, | ||
465 | unsigned int delay, unsigned int steps) | ||
466 | { | ||
467 | u16 reg = 0; | ||
468 | |||
469 | while (steps-- > 0) { | ||
470 | int ret = s5c73m3_read(state, 0x30100010, ®); | ||
471 | if (ret < 0) | ||
472 | return ret; | ||
473 | if (reg == value) | ||
474 | return 0; | ||
475 | usleep_range(delay, delay + 25); | ||
476 | } | ||
477 | return -ETIMEDOUT; | ||
478 | } | ||
479 | |||
480 | static int s5c73m3_read_fw_version(struct s5c73m3 *state) | ||
481 | { | ||
482 | struct v4l2_subdev *sd = &state->sensor_sd; | ||
483 | int i, ret; | ||
484 | u16 data[2]; | ||
485 | int offset; | ||
486 | |||
487 | offset = state->isp_ready ? 0x60 : 0; | ||
488 | |||
489 | for (i = 0; i < S5C73M3_SENSOR_FW_LEN / 2; i++) { | ||
490 | ret = s5c73m3_read(state, offset + i * 2, data); | ||
491 | if (ret < 0) | ||
492 | return ret; | ||
493 | state->sensor_fw[i * 2] = (char)(*data & 0xff); | ||
494 | state->sensor_fw[i * 2 + 1] = (char)(*data >> 8); | ||
495 | } | ||
496 | state->sensor_fw[S5C73M3_SENSOR_FW_LEN] = '\0'; | ||
497 | |||
498 | |||
499 | for (i = 0; i < S5C73M3_SENSOR_TYPE_LEN / 2; i++) { | ||
500 | ret = s5c73m3_read(state, offset + 6 + i * 2, data); | ||
501 | if (ret < 0) | ||
502 | return ret; | ||
503 | state->sensor_type[i * 2] = (char)(*data & 0xff); | ||
504 | state->sensor_type[i * 2 + 1] = (char)(*data >> 8); | ||
505 | } | ||
506 | state->sensor_type[S5C73M3_SENSOR_TYPE_LEN] = '\0'; | ||
507 | |||
508 | ret = s5c73m3_read(state, offset + 0x14, data); | ||
509 | if (ret >= 0) { | ||
510 | ret = s5c73m3_read(state, offset + 0x16, data + 1); | ||
511 | if (ret >= 0) | ||
512 | state->fw_size = data[0] + (data[1] << 16); | ||
513 | } | ||
514 | |||
515 | v4l2_info(sd, "Sensor type: %s, FW version: %s\n", | ||
516 | state->sensor_type, state->sensor_fw); | ||
517 | return ret; | ||
518 | } | ||
519 | |||
520 | static int s5c73m3_fw_update_from(struct s5c73m3 *state) | ||
521 | { | ||
522 | struct v4l2_subdev *sd = &state->sensor_sd; | ||
523 | u16 status = COMM_FW_UPDATE_NOT_READY; | ||
524 | int ret; | ||
525 | int count = 0; | ||
526 | |||
527 | v4l2_warn(sd, "Updating F-ROM firmware.\n"); | ||
528 | do { | ||
529 | if (status == COMM_FW_UPDATE_NOT_READY) { | ||
530 | ret = s5c73m3_isp_command(state, COMM_FW_UPDATE, 0); | ||
531 | if (ret < 0) | ||
532 | return ret; | ||
533 | } | ||
534 | |||
535 | ret = s5c73m3_read(state, 0x00095906, &status); | ||
536 | if (ret < 0) | ||
537 | return ret; | ||
538 | switch (status) { | ||
539 | case COMM_FW_UPDATE_FAIL: | ||
540 | v4l2_warn(sd, "Updating F-ROM firmware failed.\n"); | ||
541 | return -EIO; | ||
542 | case COMM_FW_UPDATE_SUCCESS: | ||
543 | v4l2_warn(sd, "Updating F-ROM firmware finished.\n"); | ||
544 | return 0; | ||
545 | } | ||
546 | ++count; | ||
547 | msleep(20); | ||
548 | } while (count < 500); | ||
549 | |||
550 | v4l2_warn(sd, "Updating F-ROM firmware timed-out.\n"); | ||
551 | return -ETIMEDOUT; | ||
552 | } | ||
553 | |||
554 | static int s5c73m3_spi_boot(struct s5c73m3 *state, bool load_fw) | ||
555 | { | ||
556 | struct v4l2_subdev *sd = &state->sensor_sd; | ||
557 | int ret; | ||
558 | |||
559 | /* Run ARM MCU */ | ||
560 | ret = s5c73m3_write(state, 0x30000004, 0xffff); | ||
561 | if (ret < 0) | ||
562 | return ret; | ||
563 | |||
564 | usleep_range(400, 500); | ||
565 | |||
566 | /* Check booting status */ | ||
567 | ret = s5c73m3_system_status_wait(state, 0x0c, 100, 3); | ||
568 | if (ret < 0) { | ||
569 | v4l2_err(sd, "booting failed: %d\n", ret); | ||
570 | return ret; | ||
571 | } | ||
572 | |||
573 | /* P,M,S and Boot Mode */ | ||
574 | ret = s5c73m3_write(state, 0x30100014, 0x2146); | ||
575 | if (ret < 0) | ||
576 | return ret; | ||
577 | |||
578 | ret = s5c73m3_write(state, 0x30100010, 0x210c); | ||
579 | if (ret < 0) | ||
580 | return ret; | ||
581 | |||
582 | usleep_range(200, 250); | ||
583 | |||
584 | /* Check SPI status */ | ||
585 | ret = s5c73m3_system_status_wait(state, 0x210d, 100, 300); | ||
586 | if (ret < 0) | ||
587 | v4l2_err(sd, "SPI not ready: %d\n", ret); | ||
588 | |||
589 | /* Firmware download over SPI */ | ||
590 | if (load_fw) | ||
591 | s5c73m3_load_fw(sd); | ||
592 | |||
593 | /* MCU reset */ | ||
594 | ret = s5c73m3_write(state, 0x30000004, 0xfffd); | ||
595 | if (ret < 0) | ||
596 | return ret; | ||
597 | |||
598 | /* Remap */ | ||
599 | ret = s5c73m3_write(state, 0x301000a4, 0x0183); | ||
600 | if (ret < 0) | ||
601 | return ret; | ||
602 | |||
603 | /* MCU restart */ | ||
604 | ret = s5c73m3_write(state, 0x30000004, 0xffff); | ||
605 | if (ret < 0 || !load_fw) | ||
606 | return ret; | ||
607 | |||
608 | ret = s5c73m3_read_fw_version(state); | ||
609 | if (ret < 0) | ||
610 | return ret; | ||
611 | |||
612 | if (load_fw && update_fw) { | ||
613 | ret = s5c73m3_fw_update_from(state); | ||
614 | update_fw = 0; | ||
615 | } | ||
616 | |||
617 | return ret; | ||
618 | } | ||
619 | |||
620 | static int s5c73m3_set_timing_register_for_vdd(struct s5c73m3 *state) | ||
621 | { | ||
622 | static const u32 regs[][2] = { | ||
623 | { 0x30100018, 0x0618 }, | ||
624 | { 0x3010001c, 0x10c1 }, | ||
625 | { 0x30100020, 0x249e } | ||
626 | }; | ||
627 | int ret; | ||
628 | int i; | ||
629 | |||
630 | for (i = 0; i < ARRAY_SIZE(regs); i++) { | ||
631 | ret = s5c73m3_write(state, regs[i][0], regs[i][1]); | ||
632 | if (ret < 0) | ||
633 | return ret; | ||
634 | } | ||
635 | |||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | static void s5c73m3_set_fw_file_version(struct s5c73m3 *state) | ||
640 | { | ||
641 | switch (state->sensor_fw[0]) { | ||
642 | case 'G': | ||
643 | case 'O': | ||
644 | state->fw_file_version[0] = 'G'; | ||
645 | break; | ||
646 | case 'S': | ||
647 | case 'Z': | ||
648 | state->fw_file_version[0] = 'Z'; | ||
649 | break; | ||
650 | } | ||
651 | |||
652 | switch (state->sensor_fw[1]) { | ||
653 | case 'C'...'F': | ||
654 | state->fw_file_version[1] = state->sensor_fw[1]; | ||
655 | break; | ||
656 | } | ||
657 | } | ||
658 | |||
659 | static int s5c73m3_get_fw_version(struct s5c73m3 *state) | ||
660 | { | ||
661 | struct v4l2_subdev *sd = &state->sensor_sd; | ||
662 | int ret; | ||
663 | |||
664 | /* Run ARM MCU */ | ||
665 | ret = s5c73m3_write(state, 0x30000004, 0xffff); | ||
666 | if (ret < 0) | ||
667 | return ret; | ||
668 | usleep_range(400, 500); | ||
669 | |||
670 | /* Check booting status */ | ||
671 | ret = s5c73m3_system_status_wait(state, 0x0c, 100, 3); | ||
672 | if (ret < 0) { | ||
673 | |||
674 | v4l2_err(sd, "%s: booting failed: %d\n", __func__, ret); | ||
675 | return ret; | ||
676 | } | ||
677 | |||
678 | /* Change I/O Driver Current in order to read from F-ROM */ | ||
679 | ret = s5c73m3_write(state, 0x30100120, 0x0820); | ||
680 | ret = s5c73m3_write(state, 0x30100124, 0x0820); | ||
681 | |||
682 | /* Offset Setting */ | ||
683 | ret = s5c73m3_write(state, 0x00010418, 0x0008); | ||
684 | |||
685 | /* P,M,S and Boot Mode */ | ||
686 | ret = s5c73m3_write(state, 0x30100014, 0x2146); | ||
687 | if (ret < 0) | ||
688 | return ret; | ||
689 | ret = s5c73m3_write(state, 0x30100010, 0x230c); | ||
690 | if (ret < 0) | ||
691 | return ret; | ||
692 | |||
693 | usleep_range(200, 250); | ||
694 | |||
695 | /* Check SPI status */ | ||
696 | ret = s5c73m3_system_status_wait(state, 0x230e, 100, 300); | ||
697 | if (ret < 0) | ||
698 | v4l2_err(sd, "SPI not ready: %d\n", ret); | ||
699 | |||
700 | /* ARM reset */ | ||
701 | ret = s5c73m3_write(state, 0x30000004, 0xfffd); | ||
702 | if (ret < 0) | ||
703 | return ret; | ||
704 | |||
705 | /* Remap */ | ||
706 | ret = s5c73m3_write(state, 0x301000a4, 0x0183); | ||
707 | if (ret < 0) | ||
708 | return ret; | ||
709 | |||
710 | s5c73m3_set_timing_register_for_vdd(state); | ||
711 | |||
712 | ret = s5c73m3_read_fw_version(state); | ||
713 | |||
714 | s5c73m3_set_fw_file_version(state); | ||
715 | |||
716 | return ret; | ||
717 | } | ||
718 | |||
719 | static int s5c73m3_rom_boot(struct s5c73m3 *state, bool load_fw) | ||
720 | { | ||
721 | static const u32 boot_regs[][2] = { | ||
722 | { 0x3100010c, 0x0044 }, | ||
723 | { 0x31000108, 0x000d }, | ||
724 | { 0x31000304, 0x0001 }, | ||
725 | { 0x00010000, 0x5800 }, | ||
726 | { 0x00010002, 0x0002 }, | ||
727 | { 0x31000000, 0x0001 }, | ||
728 | { 0x30100014, 0x1b85 }, | ||
729 | { 0x30100010, 0x230c } | ||
730 | }; | ||
731 | struct v4l2_subdev *sd = &state->sensor_sd; | ||
732 | int i, ret; | ||
733 | |||
734 | /* Run ARM MCU */ | ||
735 | ret = s5c73m3_write(state, 0x30000004, 0xffff); | ||
736 | if (ret < 0) | ||
737 | return ret; | ||
738 | usleep_range(400, 450); | ||
739 | |||
740 | /* Check booting status */ | ||
741 | ret = s5c73m3_system_status_wait(state, 0x0c, 100, 4); | ||
742 | if (ret < 0) { | ||
743 | v4l2_err(sd, "Booting failed: %d\n", ret); | ||
744 | return ret; | ||
745 | } | ||
746 | |||
747 | for (i = 0; i < ARRAY_SIZE(boot_regs); i++) { | ||
748 | ret = s5c73m3_write(state, boot_regs[i][0], boot_regs[i][1]); | ||
749 | if (ret < 0) | ||
750 | return ret; | ||
751 | } | ||
752 | msleep(200); | ||
753 | |||
754 | /* Check the binary read status */ | ||
755 | ret = s5c73m3_system_status_wait(state, 0x230e, 1000, 150); | ||
756 | if (ret < 0) { | ||
757 | v4l2_err(sd, "Binary read failed: %d\n", ret); | ||
758 | return ret; | ||
759 | } | ||
760 | |||
761 | /* ARM reset */ | ||
762 | ret = s5c73m3_write(state, 0x30000004, 0xfffd); | ||
763 | if (ret < 0) | ||
764 | return ret; | ||
765 | /* Remap */ | ||
766 | ret = s5c73m3_write(state, 0x301000a4, 0x0183); | ||
767 | if (ret < 0) | ||
768 | return ret; | ||
769 | /* MCU re-start */ | ||
770 | ret = s5c73m3_write(state, 0x30000004, 0xffff); | ||
771 | if (ret < 0) | ||
772 | return ret; | ||
773 | |||
774 | state->isp_ready = 1; | ||
775 | |||
776 | return s5c73m3_read_fw_version(state); | ||
777 | } | ||
778 | |||
779 | static int s5c73m3_isp_init(struct s5c73m3 *state) | ||
780 | { | ||
781 | int ret; | ||
782 | |||
783 | state->i2c_read_address = 0; | ||
784 | state->i2c_write_address = 0; | ||
785 | |||
786 | ret = s5c73m3_i2c_write(state->i2c_client, AHB_MSB_ADDR_PTR, 0x3310); | ||
787 | if (ret < 0) | ||
788 | return ret; | ||
789 | |||
790 | if (boot_from_rom) | ||
791 | return s5c73m3_rom_boot(state, true); | ||
792 | else | ||
793 | return s5c73m3_spi_boot(state, true); | ||
794 | } | ||
795 | |||
796 | static const struct s5c73m3_frame_size *s5c73m3_find_frame_size( | ||
797 | struct v4l2_mbus_framefmt *fmt, | ||
798 | enum s5c73m3_resolution_types idx) | ||
799 | { | ||
800 | const struct s5c73m3_frame_size *fs; | ||
801 | const struct s5c73m3_frame_size *best_fs; | ||
802 | int best_dist = INT_MAX; | ||
803 | int i; | ||
804 | |||
805 | fs = s5c73m3_resolutions[idx]; | ||
806 | best_fs = NULL; | ||
807 | for (i = 0; i < s5c73m3_resolutions_len[idx]; ++i) { | ||
808 | int dist = abs(fs->width - fmt->width) + | ||
809 | abs(fs->height - fmt->height); | ||
810 | if (dist < best_dist) { | ||
811 | best_dist = dist; | ||
812 | best_fs = fs; | ||
813 | } | ||
814 | ++fs; | ||
815 | } | ||
816 | |||
817 | return best_fs; | ||
818 | } | ||
819 | |||
820 | static void s5c73m3_oif_try_format(struct s5c73m3 *state, | ||
821 | struct v4l2_subdev_fh *fh, | ||
822 | struct v4l2_subdev_format *fmt, | ||
823 | const struct s5c73m3_frame_size **fs) | ||
824 | { | ||
825 | u32 code; | ||
826 | |||
827 | switch (fmt->pad) { | ||
828 | case OIF_ISP_PAD: | ||
829 | *fs = s5c73m3_find_frame_size(&fmt->format, RES_ISP); | ||
830 | code = S5C73M3_ISP_FMT; | ||
831 | break; | ||
832 | case OIF_JPEG_PAD: | ||
833 | *fs = s5c73m3_find_frame_size(&fmt->format, RES_JPEG); | ||
834 | code = S5C73M3_JPEG_FMT; | ||
835 | break; | ||
836 | case OIF_SOURCE_PAD: | ||
837 | default: | ||
838 | if (fmt->format.code == S5C73M3_JPEG_FMT) | ||
839 | code = S5C73M3_JPEG_FMT; | ||
840 | else | ||
841 | code = S5C73M3_ISP_FMT; | ||
842 | |||
843 | if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) | ||
844 | *fs = state->oif_pix_size[RES_ISP]; | ||
845 | else | ||
846 | *fs = s5c73m3_find_frame_size( | ||
847 | v4l2_subdev_get_try_format(fh, | ||
848 | OIF_ISP_PAD), | ||
849 | RES_ISP); | ||
850 | break; | ||
851 | } | ||
852 | |||
853 | s5c73m3_fill_mbus_fmt(&fmt->format, *fs, code); | ||
854 | } | ||
855 | |||
856 | static void s5c73m3_try_format(struct s5c73m3 *state, | ||
857 | struct v4l2_subdev_fh *fh, | ||
858 | struct v4l2_subdev_format *fmt, | ||
859 | const struct s5c73m3_frame_size **fs) | ||
860 | { | ||
861 | u32 code; | ||
862 | |||
863 | if (fmt->pad == S5C73M3_ISP_PAD) { | ||
864 | *fs = s5c73m3_find_frame_size(&fmt->format, RES_ISP); | ||
865 | code = S5C73M3_ISP_FMT; | ||
866 | } else { | ||
867 | *fs = s5c73m3_find_frame_size(&fmt->format, RES_JPEG); | ||
868 | code = S5C73M3_JPEG_FMT; | ||
869 | } | ||
870 | |||
871 | s5c73m3_fill_mbus_fmt(&fmt->format, *fs, code); | ||
872 | } | ||
873 | |||
874 | static int s5c73m3_oif_g_frame_interval(struct v4l2_subdev *sd, | ||
875 | struct v4l2_subdev_frame_interval *fi) | ||
876 | { | ||
877 | struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); | ||
878 | |||
879 | if (fi->pad != OIF_SOURCE_PAD) | ||
880 | return -EINVAL; | ||
881 | |||
882 | mutex_lock(&state->lock); | ||
883 | fi->interval = state->fiv->interval; | ||
884 | mutex_unlock(&state->lock); | ||
885 | |||
886 | return 0; | ||
887 | } | ||
888 | |||
889 | static int __s5c73m3_set_frame_interval(struct s5c73m3 *state, | ||
890 | struct v4l2_subdev_frame_interval *fi) | ||
891 | { | ||
892 | const struct s5c73m3_frame_size *prev_size = | ||
893 | state->sensor_pix_size[RES_ISP]; | ||
894 | const struct s5c73m3_interval *fiv = &s5c73m3_intervals[0]; | ||
895 | unsigned int ret, min_err = UINT_MAX; | ||
896 | unsigned int i, fr_time; | ||
897 | |||
898 | if (fi->interval.denominator == 0) | ||
899 | return -EINVAL; | ||
900 | |||
901 | fr_time = fi->interval.numerator * 1000 / fi->interval.denominator; | ||
902 | |||
903 | for (i = 0; i < ARRAY_SIZE(s5c73m3_intervals); i++) { | ||
904 | const struct s5c73m3_interval *iv = &s5c73m3_intervals[i]; | ||
905 | |||
906 | if (prev_size->width > iv->size.width || | ||
907 | prev_size->height > iv->size.height) | ||
908 | continue; | ||
909 | |||
910 | ret = abs(iv->interval.numerator / 1000 - fr_time); | ||
911 | if (ret < min_err) { | ||
912 | fiv = iv; | ||
913 | min_err = ret; | ||
914 | } | ||
915 | } | ||
916 | state->fiv = fiv; | ||
917 | |||
918 | v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, | ||
919 | "Changed frame interval to %u us\n", fiv->interval.numerator); | ||
920 | return 0; | ||
921 | } | ||
922 | |||
923 | static int s5c73m3_oif_s_frame_interval(struct v4l2_subdev *sd, | ||
924 | struct v4l2_subdev_frame_interval *fi) | ||
925 | { | ||
926 | struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); | ||
927 | int ret; | ||
928 | |||
929 | if (fi->pad != OIF_SOURCE_PAD) | ||
930 | return -EINVAL; | ||
931 | |||
932 | v4l2_dbg(1, s5c73m3_dbg, sd, "Setting %d/%d frame interval\n", | ||
933 | fi->interval.numerator, fi->interval.denominator); | ||
934 | |||
935 | mutex_lock(&state->lock); | ||
936 | |||
937 | ret = __s5c73m3_set_frame_interval(state, fi); | ||
938 | if (!ret) { | ||
939 | if (state->streaming) | ||
940 | ret = s5c73m3_set_frame_rate(state); | ||
941 | else | ||
942 | state->apply_fiv = 1; | ||
943 | } | ||
944 | mutex_unlock(&state->lock); | ||
945 | return ret; | ||
946 | } | ||
947 | |||
948 | static int s5c73m3_oif_enum_frame_interval(struct v4l2_subdev *sd, | ||
949 | struct v4l2_subdev_fh *fh, | ||
950 | struct v4l2_subdev_frame_interval_enum *fie) | ||
951 | { | ||
952 | struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); | ||
953 | const struct s5c73m3_interval *fi; | ||
954 | int ret = 0; | ||
955 | |||
956 | if (fie->pad != OIF_SOURCE_PAD) | ||
957 | return -EINVAL; | ||
958 | if (fie->index > ARRAY_SIZE(s5c73m3_intervals)) | ||
959 | return -EINVAL; | ||
960 | |||
961 | mutex_lock(&state->lock); | ||
962 | fi = &s5c73m3_intervals[fie->index]; | ||
963 | if (fie->width > fi->size.width || fie->height > fi->size.height) | ||
964 | ret = -EINVAL; | ||
965 | else | ||
966 | fie->interval = fi->interval; | ||
967 | mutex_unlock(&state->lock); | ||
968 | |||
969 | return ret; | ||
970 | } | ||
971 | |||
972 | static int s5c73m3_oif_get_pad_code(int pad, int index) | ||
973 | { | ||
974 | if (pad == OIF_SOURCE_PAD) { | ||
975 | if (index > 1) | ||
976 | return -EINVAL; | ||
977 | return (index == 0) ? S5C73M3_ISP_FMT : S5C73M3_JPEG_FMT; | ||
978 | } | ||
979 | |||
980 | if (index > 0) | ||
981 | return -EINVAL; | ||
982 | |||
983 | return (pad == OIF_ISP_PAD) ? S5C73M3_ISP_FMT : S5C73M3_JPEG_FMT; | ||
984 | } | ||
985 | |||
986 | static int s5c73m3_get_fmt(struct v4l2_subdev *sd, | ||
987 | struct v4l2_subdev_fh *fh, | ||
988 | struct v4l2_subdev_format *fmt) | ||
989 | { | ||
990 | struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); | ||
991 | const struct s5c73m3_frame_size *fs; | ||
992 | u32 code; | ||
993 | |||
994 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
995 | fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); | ||
996 | return 0; | ||
997 | } | ||
998 | |||
999 | mutex_lock(&state->lock); | ||
1000 | |||
1001 | switch (fmt->pad) { | ||
1002 | case S5C73M3_ISP_PAD: | ||
1003 | code = S5C73M3_ISP_FMT; | ||
1004 | fs = state->sensor_pix_size[RES_ISP]; | ||
1005 | break; | ||
1006 | case S5C73M3_JPEG_PAD: | ||
1007 | code = S5C73M3_JPEG_FMT; | ||
1008 | fs = state->sensor_pix_size[RES_JPEG]; | ||
1009 | break; | ||
1010 | default: | ||
1011 | mutex_unlock(&state->lock); | ||
1012 | return -EINVAL; | ||
1013 | } | ||
1014 | s5c73m3_fill_mbus_fmt(&fmt->format, fs, code); | ||
1015 | |||
1016 | mutex_unlock(&state->lock); | ||
1017 | return 0; | ||
1018 | } | ||
1019 | |||
1020 | static int s5c73m3_oif_get_fmt(struct v4l2_subdev *sd, | ||
1021 | struct v4l2_subdev_fh *fh, | ||
1022 | struct v4l2_subdev_format *fmt) | ||
1023 | { | ||
1024 | struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); | ||
1025 | const struct s5c73m3_frame_size *fs; | ||
1026 | u32 code; | ||
1027 | |||
1028 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1029 | fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); | ||
1030 | return 0; | ||
1031 | } | ||
1032 | |||
1033 | mutex_lock(&state->lock); | ||
1034 | |||
1035 | switch (fmt->pad) { | ||
1036 | case OIF_ISP_PAD: | ||
1037 | code = S5C73M3_ISP_FMT; | ||
1038 | fs = state->oif_pix_size[RES_ISP]; | ||
1039 | break; | ||
1040 | case OIF_JPEG_PAD: | ||
1041 | code = S5C73M3_JPEG_FMT; | ||
1042 | fs = state->oif_pix_size[RES_JPEG]; | ||
1043 | break; | ||
1044 | case OIF_SOURCE_PAD: | ||
1045 | code = state->mbus_code; | ||
1046 | fs = state->oif_pix_size[RES_ISP]; | ||
1047 | break; | ||
1048 | default: | ||
1049 | mutex_unlock(&state->lock); | ||
1050 | return -EINVAL; | ||
1051 | } | ||
1052 | s5c73m3_fill_mbus_fmt(&fmt->format, fs, code); | ||
1053 | |||
1054 | mutex_unlock(&state->lock); | ||
1055 | return 0; | ||
1056 | } | ||
1057 | |||
1058 | static int s5c73m3_set_fmt(struct v4l2_subdev *sd, | ||
1059 | struct v4l2_subdev_fh *fh, | ||
1060 | struct v4l2_subdev_format *fmt) | ||
1061 | { | ||
1062 | const struct s5c73m3_frame_size *frame_size = NULL; | ||
1063 | struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); | ||
1064 | struct v4l2_mbus_framefmt *mf; | ||
1065 | int ret = 0; | ||
1066 | |||
1067 | mutex_lock(&state->lock); | ||
1068 | |||
1069 | s5c73m3_try_format(state, fh, fmt, &frame_size); | ||
1070 | |||
1071 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1072 | mf = v4l2_subdev_get_try_format(fh, fmt->pad); | ||
1073 | *mf = fmt->format; | ||
1074 | } else { | ||
1075 | switch (fmt->pad) { | ||
1076 | case S5C73M3_ISP_PAD: | ||
1077 | state->sensor_pix_size[RES_ISP] = frame_size; | ||
1078 | break; | ||
1079 | case S5C73M3_JPEG_PAD: | ||
1080 | state->sensor_pix_size[RES_JPEG] = frame_size; | ||
1081 | break; | ||
1082 | default: | ||
1083 | ret = -EBUSY; | ||
1084 | } | ||
1085 | |||
1086 | if (state->streaming) | ||
1087 | ret = -EBUSY; | ||
1088 | else | ||
1089 | state->apply_fmt = 1; | ||
1090 | } | ||
1091 | |||
1092 | mutex_unlock(&state->lock); | ||
1093 | |||
1094 | return ret; | ||
1095 | } | ||
1096 | |||
1097 | static int s5c73m3_oif_set_fmt(struct v4l2_subdev *sd, | ||
1098 | struct v4l2_subdev_fh *fh, | ||
1099 | struct v4l2_subdev_format *fmt) | ||
1100 | { | ||
1101 | const struct s5c73m3_frame_size *frame_size = NULL; | ||
1102 | struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); | ||
1103 | struct v4l2_mbus_framefmt *mf; | ||
1104 | int ret = 0; | ||
1105 | |||
1106 | mutex_lock(&state->lock); | ||
1107 | |||
1108 | s5c73m3_oif_try_format(state, fh, fmt, &frame_size); | ||
1109 | |||
1110 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | ||
1111 | mf = v4l2_subdev_get_try_format(fh, fmt->pad); | ||
1112 | *mf = fmt->format; | ||
1113 | } else { | ||
1114 | switch (fmt->pad) { | ||
1115 | case OIF_ISP_PAD: | ||
1116 | state->oif_pix_size[RES_ISP] = frame_size; | ||
1117 | break; | ||
1118 | case OIF_JPEG_PAD: | ||
1119 | state->oif_pix_size[RES_JPEG] = frame_size; | ||
1120 | break; | ||
1121 | case OIF_SOURCE_PAD: | ||
1122 | state->mbus_code = fmt->format.code; | ||
1123 | break; | ||
1124 | default: | ||
1125 | ret = -EBUSY; | ||
1126 | } | ||
1127 | |||
1128 | if (state->streaming) | ||
1129 | ret = -EBUSY; | ||
1130 | else | ||
1131 | state->apply_fmt = 1; | ||
1132 | } | ||
1133 | |||
1134 | mutex_unlock(&state->lock); | ||
1135 | |||
1136 | return ret; | ||
1137 | } | ||
1138 | |||
1139 | static int s5c73m3_oif_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, | ||
1140 | struct v4l2_mbus_frame_desc *fd) | ||
1141 | { | ||
1142 | struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); | ||
1143 | int i; | ||
1144 | |||
1145 | if (pad != OIF_SOURCE_PAD || fd == NULL) | ||
1146 | return -EINVAL; | ||
1147 | |||
1148 | mutex_lock(&state->lock); | ||
1149 | fd->num_entries = 2; | ||
1150 | for (i = 0; i < fd->num_entries; i++) | ||
1151 | fd->entry[i] = state->frame_desc.entry[i]; | ||
1152 | mutex_unlock(&state->lock); | ||
1153 | |||
1154 | return 0; | ||
1155 | } | ||
1156 | |||
1157 | static int s5c73m3_oif_set_frame_desc(struct v4l2_subdev *sd, unsigned int pad, | ||
1158 | struct v4l2_mbus_frame_desc *fd) | ||
1159 | { | ||
1160 | struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); | ||
1161 | struct v4l2_mbus_frame_desc *frame_desc = &state->frame_desc; | ||
1162 | int i; | ||
1163 | |||
1164 | if (pad != OIF_SOURCE_PAD || fd == NULL) | ||
1165 | return -EINVAL; | ||
1166 | |||
1167 | fd->entry[0].length = 10 * SZ_1M; | ||
1168 | fd->entry[1].length = max_t(u32, fd->entry[1].length, | ||
1169 | S5C73M3_EMBEDDED_DATA_MAXLEN); | ||
1170 | fd->num_entries = 2; | ||
1171 | |||
1172 | mutex_lock(&state->lock); | ||
1173 | for (i = 0; i < fd->num_entries; i++) | ||
1174 | frame_desc->entry[i] = fd->entry[i]; | ||
1175 | mutex_unlock(&state->lock); | ||
1176 | |||
1177 | return 0; | ||
1178 | } | ||
1179 | |||
1180 | static int s5c73m3_enum_mbus_code(struct v4l2_subdev *sd, | ||
1181 | struct v4l2_subdev_fh *fh, | ||
1182 | struct v4l2_subdev_mbus_code_enum *code) | ||
1183 | { | ||
1184 | static const int codes[] = { | ||
1185 | [S5C73M3_ISP_PAD] = S5C73M3_ISP_FMT, | ||
1186 | [S5C73M3_JPEG_PAD] = S5C73M3_JPEG_FMT}; | ||
1187 | |||
1188 | if (code->index > 0 || code->pad >= S5C73M3_NUM_PADS) | ||
1189 | return -EINVAL; | ||
1190 | |||
1191 | code->code = codes[code->pad]; | ||
1192 | |||
1193 | return 0; | ||
1194 | } | ||
1195 | |||
1196 | static int s5c73m3_oif_enum_mbus_code(struct v4l2_subdev *sd, | ||
1197 | struct v4l2_subdev_fh *fh, | ||
1198 | struct v4l2_subdev_mbus_code_enum *code) | ||
1199 | { | ||
1200 | int ret; | ||
1201 | |||
1202 | ret = s5c73m3_oif_get_pad_code(code->pad, code->index); | ||
1203 | if (ret < 0) | ||
1204 | return ret; | ||
1205 | |||
1206 | code->code = ret; | ||
1207 | |||
1208 | return 0; | ||
1209 | } | ||
1210 | |||
1211 | static int s5c73m3_enum_frame_size(struct v4l2_subdev *sd, | ||
1212 | struct v4l2_subdev_fh *fh, | ||
1213 | struct v4l2_subdev_frame_size_enum *fse) | ||
1214 | { | ||
1215 | int idx; | ||
1216 | |||
1217 | if (fse->pad == S5C73M3_ISP_PAD) { | ||
1218 | if (fse->code != S5C73M3_ISP_FMT) | ||
1219 | return -EINVAL; | ||
1220 | idx = RES_ISP; | ||
1221 | } else{ | ||
1222 | if (fse->code != S5C73M3_JPEG_FMT) | ||
1223 | return -EINVAL; | ||
1224 | idx = RES_JPEG; | ||
1225 | } | ||
1226 | |||
1227 | if (fse->index >= s5c73m3_resolutions_len[idx]) | ||
1228 | return -EINVAL; | ||
1229 | |||
1230 | fse->min_width = s5c73m3_resolutions[idx][fse->index].width; | ||
1231 | fse->max_width = fse->min_width; | ||
1232 | fse->max_height = s5c73m3_resolutions[idx][fse->index].height; | ||
1233 | fse->min_height = fse->max_height; | ||
1234 | |||
1235 | return 0; | ||
1236 | } | ||
1237 | |||
1238 | static int s5c73m3_oif_enum_frame_size(struct v4l2_subdev *sd, | ||
1239 | struct v4l2_subdev_fh *fh, | ||
1240 | struct v4l2_subdev_frame_size_enum *fse) | ||
1241 | { | ||
1242 | int idx; | ||
1243 | |||
1244 | if (fse->pad == OIF_SOURCE_PAD) { | ||
1245 | if (fse->index > 0) | ||
1246 | return -EINVAL; | ||
1247 | |||
1248 | switch (fse->code) { | ||
1249 | case S5C73M3_JPEG_FMT: | ||
1250 | case S5C73M3_ISP_FMT: { | ||
1251 | struct v4l2_mbus_framefmt *mf = | ||
1252 | v4l2_subdev_get_try_format(fh, OIF_ISP_PAD); | ||
1253 | |||
1254 | fse->max_width = fse->min_width = mf->width; | ||
1255 | fse->max_height = fse->min_height = mf->height; | ||
1256 | return 0; | ||
1257 | } | ||
1258 | default: | ||
1259 | return -EINVAL; | ||
1260 | } | ||
1261 | } | ||
1262 | |||
1263 | if (fse->code != s5c73m3_oif_get_pad_code(fse->pad, 0)) | ||
1264 | return -EINVAL; | ||
1265 | |||
1266 | if (fse->pad == OIF_JPEG_PAD) | ||
1267 | idx = RES_JPEG; | ||
1268 | else | ||
1269 | idx = RES_ISP; | ||
1270 | |||
1271 | if (fse->index >= s5c73m3_resolutions_len[idx]) | ||
1272 | return -EINVAL; | ||
1273 | |||
1274 | fse->min_width = s5c73m3_resolutions[idx][fse->index].width; | ||
1275 | fse->max_width = fse->min_width; | ||
1276 | fse->max_height = s5c73m3_resolutions[idx][fse->index].height; | ||
1277 | fse->min_height = fse->max_height; | ||
1278 | |||
1279 | return 0; | ||
1280 | } | ||
1281 | |||
1282 | static int s5c73m3_oif_log_status(struct v4l2_subdev *sd) | ||
1283 | { | ||
1284 | struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); | ||
1285 | |||
1286 | v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name); | ||
1287 | |||
1288 | v4l2_info(sd, "power: %d, apply_fmt: %d\n", state->power, | ||
1289 | state->apply_fmt); | ||
1290 | |||
1291 | return 0; | ||
1292 | } | ||
1293 | |||
1294 | static int s5c73m3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
1295 | { | ||
1296 | struct v4l2_mbus_framefmt *mf; | ||
1297 | |||
1298 | mf = v4l2_subdev_get_try_format(fh, S5C73M3_ISP_PAD); | ||
1299 | s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], | ||
1300 | S5C73M3_ISP_FMT); | ||
1301 | |||
1302 | mf = v4l2_subdev_get_try_format(fh, S5C73M3_JPEG_PAD); | ||
1303 | s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], | ||
1304 | S5C73M3_JPEG_FMT); | ||
1305 | |||
1306 | return 0; | ||
1307 | } | ||
1308 | |||
1309 | static int s5c73m3_oif_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
1310 | { | ||
1311 | struct v4l2_mbus_framefmt *mf; | ||
1312 | |||
1313 | mf = v4l2_subdev_get_try_format(fh, OIF_ISP_PAD); | ||
1314 | s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], | ||
1315 | S5C73M3_ISP_FMT); | ||
1316 | |||
1317 | mf = v4l2_subdev_get_try_format(fh, OIF_JPEG_PAD); | ||
1318 | s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], | ||
1319 | S5C73M3_JPEG_FMT); | ||
1320 | |||
1321 | mf = v4l2_subdev_get_try_format(fh, OIF_SOURCE_PAD); | ||
1322 | s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], | ||
1323 | S5C73M3_ISP_FMT); | ||
1324 | return 0; | ||
1325 | } | ||
1326 | |||
1327 | static int s5c73m3_gpio_set_value(struct s5c73m3 *priv, int id, u32 val) | ||
1328 | { | ||
1329 | if (!gpio_is_valid(priv->gpio[id].gpio)) | ||
1330 | return 0; | ||
1331 | gpio_set_value(priv->gpio[id].gpio, !!val); | ||
1332 | return 1; | ||
1333 | } | ||
1334 | |||
1335 | static int s5c73m3_gpio_assert(struct s5c73m3 *priv, int id) | ||
1336 | { | ||
1337 | return s5c73m3_gpio_set_value(priv, id, priv->gpio[id].level); | ||
1338 | } | ||
1339 | |||
1340 | static int s5c73m3_gpio_deassert(struct s5c73m3 *priv, int id) | ||
1341 | { | ||
1342 | return s5c73m3_gpio_set_value(priv, id, !priv->gpio[id].level); | ||
1343 | } | ||
1344 | |||
1345 | static int __s5c73m3_power_on(struct s5c73m3 *state) | ||
1346 | { | ||
1347 | int i, ret; | ||
1348 | |||
1349 | for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) { | ||
1350 | ret = regulator_enable(state->supplies[i].consumer); | ||
1351 | if (ret) | ||
1352 | goto err; | ||
1353 | } | ||
1354 | |||
1355 | s5c73m3_gpio_deassert(state, STBY); | ||
1356 | usleep_range(100, 200); | ||
1357 | |||
1358 | s5c73m3_gpio_deassert(state, RST); | ||
1359 | usleep_range(50, 100); | ||
1360 | |||
1361 | return 0; | ||
1362 | err: | ||
1363 | for (--i; i >= 0; i--) | ||
1364 | regulator_disable(state->supplies[i].consumer); | ||
1365 | return ret; | ||
1366 | } | ||
1367 | |||
1368 | static int __s5c73m3_power_off(struct s5c73m3 *state) | ||
1369 | { | ||
1370 | int i, ret; | ||
1371 | |||
1372 | if (s5c73m3_gpio_assert(state, RST)) | ||
1373 | usleep_range(10, 50); | ||
1374 | |||
1375 | if (s5c73m3_gpio_assert(state, STBY)) | ||
1376 | usleep_range(100, 200); | ||
1377 | state->streaming = 0; | ||
1378 | state->isp_ready = 0; | ||
1379 | |||
1380 | for (i = S5C73M3_MAX_SUPPLIES - 1; i >= 0; i--) { | ||
1381 | ret = regulator_disable(state->supplies[i].consumer); | ||
1382 | if (ret) | ||
1383 | goto err; | ||
1384 | } | ||
1385 | return 0; | ||
1386 | err: | ||
1387 | for (++i; i < S5C73M3_MAX_SUPPLIES; i++) | ||
1388 | regulator_enable(state->supplies[i].consumer); | ||
1389 | |||
1390 | return ret; | ||
1391 | } | ||
1392 | |||
1393 | static int s5c73m3_oif_set_power(struct v4l2_subdev *sd, int on) | ||
1394 | { | ||
1395 | struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); | ||
1396 | int ret = 0; | ||
1397 | |||
1398 | mutex_lock(&state->lock); | ||
1399 | |||
1400 | if (on && !state->power) { | ||
1401 | ret = __s5c73m3_power_on(state); | ||
1402 | if (!ret) | ||
1403 | ret = s5c73m3_isp_init(state); | ||
1404 | if (!ret) { | ||
1405 | state->apply_fiv = 1; | ||
1406 | state->apply_fmt = 1; | ||
1407 | } | ||
1408 | } else if (!on == state->power) { | ||
1409 | ret = s5c73m3_set_af_softlanding(state); | ||
1410 | if (!ret) | ||
1411 | ret = __s5c73m3_power_off(state); | ||
1412 | else | ||
1413 | v4l2_err(sd, "Soft landing lens failed\n"); | ||
1414 | } | ||
1415 | if (!ret) | ||
1416 | state->power += on ? 1 : -1; | ||
1417 | |||
1418 | v4l2_dbg(1, s5c73m3_dbg, sd, "%s: power: %d\n", | ||
1419 | __func__, state->power); | ||
1420 | |||
1421 | mutex_unlock(&state->lock); | ||
1422 | return ret; | ||
1423 | } | ||
1424 | |||
1425 | static int s5c73m3_oif_registered(struct v4l2_subdev *sd) | ||
1426 | { | ||
1427 | struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); | ||
1428 | int ret; | ||
1429 | |||
1430 | ret = v4l2_device_register_subdev(sd->v4l2_dev, &state->sensor_sd); | ||
1431 | if (ret) { | ||
1432 | v4l2_err(sd->v4l2_dev, "Failed to register %s\n", | ||
1433 | state->oif_sd.name); | ||
1434 | return ret; | ||
1435 | } | ||
1436 | |||
1437 | ret = media_entity_create_link(&state->sensor_sd.entity, | ||
1438 | S5C73M3_ISP_PAD, &state->oif_sd.entity, OIF_ISP_PAD, | ||
1439 | MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); | ||
1440 | |||
1441 | ret = media_entity_create_link(&state->sensor_sd.entity, | ||
1442 | S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD, | ||
1443 | MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); | ||
1444 | |||
1445 | mutex_lock(&state->lock); | ||
1446 | ret = __s5c73m3_power_on(state); | ||
1447 | if (ret == 0) | ||
1448 | s5c73m3_get_fw_version(state); | ||
1449 | |||
1450 | __s5c73m3_power_off(state); | ||
1451 | mutex_unlock(&state->lock); | ||
1452 | |||
1453 | v4l2_dbg(1, s5c73m3_dbg, sd, "%s: Booting %s (%d)\n", | ||
1454 | __func__, ret ? "failed" : "succeded", ret); | ||
1455 | |||
1456 | return ret; | ||
1457 | } | ||
1458 | |||
1459 | static const struct v4l2_subdev_internal_ops s5c73m3_internal_ops = { | ||
1460 | .open = s5c73m3_open, | ||
1461 | }; | ||
1462 | |||
1463 | static const struct v4l2_subdev_pad_ops s5c73m3_pad_ops = { | ||
1464 | .enum_mbus_code = s5c73m3_enum_mbus_code, | ||
1465 | .enum_frame_size = s5c73m3_enum_frame_size, | ||
1466 | .get_fmt = s5c73m3_get_fmt, | ||
1467 | .set_fmt = s5c73m3_set_fmt, | ||
1468 | }; | ||
1469 | |||
1470 | static const struct v4l2_subdev_ops s5c73m3_subdev_ops = { | ||
1471 | .pad = &s5c73m3_pad_ops, | ||
1472 | }; | ||
1473 | |||
1474 | static const struct v4l2_subdev_internal_ops oif_internal_ops = { | ||
1475 | .registered = s5c73m3_oif_registered, | ||
1476 | .open = s5c73m3_oif_open, | ||
1477 | }; | ||
1478 | |||
1479 | static const struct v4l2_subdev_pad_ops s5c73m3_oif_pad_ops = { | ||
1480 | .enum_mbus_code = s5c73m3_oif_enum_mbus_code, | ||
1481 | .enum_frame_size = s5c73m3_oif_enum_frame_size, | ||
1482 | .enum_frame_interval = s5c73m3_oif_enum_frame_interval, | ||
1483 | .get_fmt = s5c73m3_oif_get_fmt, | ||
1484 | .set_fmt = s5c73m3_oif_set_fmt, | ||
1485 | .get_frame_desc = s5c73m3_oif_get_frame_desc, | ||
1486 | .set_frame_desc = s5c73m3_oif_set_frame_desc, | ||
1487 | }; | ||
1488 | |||
1489 | static const struct v4l2_subdev_core_ops s5c73m3_oif_core_ops = { | ||
1490 | .s_power = s5c73m3_oif_set_power, | ||
1491 | .log_status = s5c73m3_oif_log_status, | ||
1492 | }; | ||
1493 | |||
1494 | static const struct v4l2_subdev_video_ops s5c73m3_oif_video_ops = { | ||
1495 | .s_stream = s5c73m3_oif_s_stream, | ||
1496 | .g_frame_interval = s5c73m3_oif_g_frame_interval, | ||
1497 | .s_frame_interval = s5c73m3_oif_s_frame_interval, | ||
1498 | }; | ||
1499 | |||
1500 | static const struct v4l2_subdev_ops oif_subdev_ops = { | ||
1501 | .core = &s5c73m3_oif_core_ops, | ||
1502 | .pad = &s5c73m3_oif_pad_ops, | ||
1503 | .video = &s5c73m3_oif_video_ops, | ||
1504 | }; | ||
1505 | |||
1506 | static int s5c73m3_configure_gpio(int nr, int val, const char *name) | ||
1507 | { | ||
1508 | unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; | ||
1509 | int ret; | ||
1510 | |||
1511 | if (!gpio_is_valid(nr)) | ||
1512 | return 0; | ||
1513 | ret = gpio_request_one(nr, flags, name); | ||
1514 | if (!ret) | ||
1515 | gpio_export(nr, 0); | ||
1516 | return ret; | ||
1517 | } | ||
1518 | |||
1519 | static int s5c73m3_free_gpios(struct s5c73m3 *state) | ||
1520 | { | ||
1521 | int i; | ||
1522 | |||
1523 | for (i = 0; i < ARRAY_SIZE(state->gpio); i++) { | ||
1524 | if (!gpio_is_valid(state->gpio[i].gpio)) | ||
1525 | continue; | ||
1526 | gpio_free(state->gpio[i].gpio); | ||
1527 | state->gpio[i].gpio = -EINVAL; | ||
1528 | } | ||
1529 | return 0; | ||
1530 | } | ||
1531 | |||
1532 | static int s5c73m3_configure_gpios(struct s5c73m3 *state, | ||
1533 | const struct s5c73m3_platform_data *pdata) | ||
1534 | { | ||
1535 | const struct s5c73m3_gpio *gpio = &pdata->gpio_stby; | ||
1536 | int ret; | ||
1537 | |||
1538 | state->gpio[STBY].gpio = -EINVAL; | ||
1539 | state->gpio[RST].gpio = -EINVAL; | ||
1540 | |||
1541 | ret = s5c73m3_configure_gpio(gpio->gpio, gpio->level, "S5C73M3_STBY"); | ||
1542 | if (ret) { | ||
1543 | s5c73m3_free_gpios(state); | ||
1544 | return ret; | ||
1545 | } | ||
1546 | state->gpio[STBY] = *gpio; | ||
1547 | if (gpio_is_valid(gpio->gpio)) | ||
1548 | gpio_set_value(gpio->gpio, 0); | ||
1549 | |||
1550 | gpio = &pdata->gpio_reset; | ||
1551 | ret = s5c73m3_configure_gpio(gpio->gpio, gpio->level, "S5C73M3_RST"); | ||
1552 | if (ret) { | ||
1553 | s5c73m3_free_gpios(state); | ||
1554 | return ret; | ||
1555 | } | ||
1556 | state->gpio[RST] = *gpio; | ||
1557 | if (gpio_is_valid(gpio->gpio)) | ||
1558 | gpio_set_value(gpio->gpio, 0); | ||
1559 | |||
1560 | return 0; | ||
1561 | } | ||
1562 | |||
1563 | static int __devinit s5c73m3_probe(struct i2c_client *client, | ||
1564 | const struct i2c_device_id *id) | ||
1565 | { | ||
1566 | struct device *dev = &client->dev; | ||
1567 | const struct s5c73m3_platform_data *pdata = client->dev.platform_data; | ||
1568 | struct v4l2_subdev *sd; | ||
1569 | struct v4l2_subdev *oif_sd; | ||
1570 | struct s5c73m3 *state; | ||
1571 | int ret, i; | ||
1572 | |||
1573 | if (pdata == NULL) { | ||
1574 | dev_err(&client->dev, "Platform data not specified\n"); | ||
1575 | return -EINVAL; | ||
1576 | } | ||
1577 | |||
1578 | state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); | ||
1579 | if (!state) | ||
1580 | return -ENOMEM; | ||
1581 | |||
1582 | mutex_init(&state->lock); | ||
1583 | sd = &state->sensor_sd; | ||
1584 | oif_sd = &state->oif_sd; | ||
1585 | |||
1586 | v4l2_subdev_init(sd, &s5c73m3_subdev_ops); | ||
1587 | sd->owner = client->driver->driver.owner; | ||
1588 | v4l2_set_subdevdata(sd, state); | ||
1589 | strlcpy(sd->name, "S5C73M3", sizeof(sd->name)); | ||
1590 | |||
1591 | sd->internal_ops = &s5c73m3_internal_ops; | ||
1592 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1593 | |||
1594 | state->sensor_pads[S5C73M3_JPEG_PAD].flags = MEDIA_PAD_FL_SOURCE; | ||
1595 | state->sensor_pads[S5C73M3_ISP_PAD].flags = MEDIA_PAD_FL_SOURCE; | ||
1596 | sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; | ||
1597 | |||
1598 | ret = media_entity_init(&sd->entity, S5C73M3_NUM_PADS, | ||
1599 | state->sensor_pads, 0); | ||
1600 | if (ret < 0) | ||
1601 | return ret; | ||
1602 | |||
1603 | v4l2_i2c_subdev_init(oif_sd, client, &oif_subdev_ops); | ||
1604 | strcpy(oif_sd->name, "S5C73M3-OIF"); | ||
1605 | |||
1606 | oif_sd->internal_ops = &oif_internal_ops; | ||
1607 | oif_sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1608 | |||
1609 | state->oif_pads[OIF_ISP_PAD].flags = MEDIA_PAD_FL_SINK; | ||
1610 | state->oif_pads[OIF_JPEG_PAD].flags = MEDIA_PAD_FL_SINK; | ||
1611 | state->oif_pads[OIF_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE; | ||
1612 | oif_sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; | ||
1613 | |||
1614 | ret = media_entity_init(&oif_sd->entity, OIF_NUM_PADS, | ||
1615 | state->oif_pads, 0); | ||
1616 | if (ret < 0) | ||
1617 | return ret; | ||
1618 | |||
1619 | state->mclk_frequency = pdata->mclk_frequency; | ||
1620 | state->bus_type = pdata->bus_type; | ||
1621 | |||
1622 | ret = s5c73m3_configure_gpios(state, pdata); | ||
1623 | if (ret) | ||
1624 | goto out_err1; | ||
1625 | |||
1626 | for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) | ||
1627 | state->supplies[i].supply = s5c73m3_supply_names[i]; | ||
1628 | |||
1629 | ret = regulator_bulk_get(dev, S5C73M3_MAX_SUPPLIES, | ||
1630 | state->supplies); | ||
1631 | if (ret) { | ||
1632 | dev_err(dev, "failed to get regulators\n"); | ||
1633 | goto out_err2; | ||
1634 | } | ||
1635 | |||
1636 | ret = s5c73m3_init_controls(state); | ||
1637 | if (ret) | ||
1638 | goto out_err3; | ||
1639 | |||
1640 | state->sensor_pix_size[RES_ISP] = &s5c73m3_isp_resolutions[1]; | ||
1641 | state->sensor_pix_size[RES_JPEG] = &s5c73m3_jpeg_resolutions[1]; | ||
1642 | state->oif_pix_size[RES_ISP] = state->sensor_pix_size[RES_ISP]; | ||
1643 | state->oif_pix_size[RES_JPEG] = state->sensor_pix_size[RES_JPEG]; | ||
1644 | |||
1645 | state->mbus_code = S5C73M3_ISP_FMT; | ||
1646 | |||
1647 | state->fiv = &s5c73m3_intervals[S5C73M3_DEFAULT_FRAME_INTERVAL]; | ||
1648 | |||
1649 | state->fw_file_version[0] = 'G'; | ||
1650 | state->fw_file_version[1] = 'C'; | ||
1651 | |||
1652 | ret = s5c73m3_register_spi_driver(state); | ||
1653 | if (ret < 0) | ||
1654 | goto out_err3; | ||
1655 | |||
1656 | state->i2c_client = client; | ||
1657 | |||
1658 | v4l2_info(sd, "%s: completed succesfully\n", __func__); | ||
1659 | return 0; | ||
1660 | |||
1661 | out_err3: | ||
1662 | regulator_bulk_free(S5C73M3_MAX_SUPPLIES, state->supplies); | ||
1663 | out_err2: | ||
1664 | s5c73m3_free_gpios(state); | ||
1665 | out_err1: | ||
1666 | media_entity_cleanup(&sd->entity); | ||
1667 | return ret; | ||
1668 | } | ||
1669 | |||
1670 | static int __devexit s5c73m3_remove(struct i2c_client *client) | ||
1671 | { | ||
1672 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
1673 | struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); | ||
1674 | |||
1675 | v4l2_device_unregister_subdev(sd); | ||
1676 | |||
1677 | v4l2_ctrl_handler_free(sd->ctrl_handler); | ||
1678 | media_entity_cleanup(&sd->entity); | ||
1679 | |||
1680 | s5c73m3_unregister_spi_driver(state); | ||
1681 | regulator_bulk_free(S5C73M3_MAX_SUPPLIES, state->supplies); | ||
1682 | s5c73m3_free_gpios(state); | ||
1683 | |||
1684 | return 0; | ||
1685 | } | ||
1686 | |||
1687 | static const struct i2c_device_id s5c73m3_id[] = { | ||
1688 | { DRIVER_NAME, 0 }, | ||
1689 | { } | ||
1690 | }; | ||
1691 | MODULE_DEVICE_TABLE(i2c, s5c73m3_id); | ||
1692 | |||
1693 | static struct i2c_driver s5c73m3_i2c_driver = { | ||
1694 | .driver = { | ||
1695 | .name = DRIVER_NAME, | ||
1696 | }, | ||
1697 | .probe = s5c73m3_probe, | ||
1698 | .remove = __devexit_p(s5c73m3_remove), | ||
1699 | .id_table = s5c73m3_id, | ||
1700 | }; | ||
1701 | |||
1702 | module_i2c_driver(s5c73m3_i2c_driver); | ||
1703 | |||
1704 | MODULE_DESCRIPTION("Samsung S5C73M3 camera driver"); | ||
1705 | MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); | ||
1706 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c new file mode 100644 index 000000000000..8001cde1db1e --- /dev/null +++ b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c | |||
@@ -0,0 +1,563 @@ | |||
1 | /* | ||
2 | * Samsung LSI S5C73M3 8M pixel camera driver | ||
3 | * | ||
4 | * Copyright (C) 2012, Samsung Electronics, Co., Ltd. | ||
5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
6 | * Andrzej Hajda <a.hajda@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/sizes.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/firmware.h> | ||
21 | #include <linux/gpio.h> | ||
22 | #include <linux/i2c.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/media.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/regulator/consumer.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/spi/spi.h> | ||
29 | #include <linux/videodev2.h> | ||
30 | #include <media/media-entity.h> | ||
31 | #include <media/v4l2-ctrls.h> | ||
32 | #include <media/v4l2-device.h> | ||
33 | #include <media/v4l2-subdev.h> | ||
34 | #include <media/v4l2-mediabus.h> | ||
35 | #include <media/s5c73m3.h> | ||
36 | |||
37 | #include "s5c73m3.h" | ||
38 | |||
39 | static int s5c73m3_get_af_status(struct s5c73m3 *state, struct v4l2_ctrl *ctrl) | ||
40 | { | ||
41 | u16 reg = REG_AF_STATUS_UNFOCUSED; | ||
42 | |||
43 | int ret = s5c73m3_read(state, REG_AF_STATUS, ®); | ||
44 | |||
45 | switch (reg) { | ||
46 | case REG_CAF_STATUS_FIND_SEARCH_DIR: | ||
47 | case REG_AF_STATUS_FOCUSING: | ||
48 | case REG_CAF_STATUS_FOCUSING: | ||
49 | ctrl->val = V4L2_AUTO_FOCUS_STATUS_BUSY; | ||
50 | break; | ||
51 | case REG_CAF_STATUS_FOCUSED: | ||
52 | case REG_AF_STATUS_FOCUSED: | ||
53 | ctrl->val = V4L2_AUTO_FOCUS_STATUS_REACHED; | ||
54 | break; | ||
55 | default: | ||
56 | v4l2_info(&state->sensor_sd, "Unknown AF status %#x\n", reg); | ||
57 | /* Fall through */ | ||
58 | case REG_CAF_STATUS_UNFOCUSED: | ||
59 | case REG_AF_STATUS_UNFOCUSED: | ||
60 | case REG_AF_STATUS_INVALID: | ||
61 | ctrl->val = V4L2_AUTO_FOCUS_STATUS_FAILED; | ||
62 | break; | ||
63 | } | ||
64 | |||
65 | return ret; | ||
66 | } | ||
67 | |||
68 | static int s5c73m3_g_volatile_ctrl(struct v4l2_ctrl *ctrl) | ||
69 | { | ||
70 | struct v4l2_subdev *sd = ctrl_to_sensor_sd(ctrl); | ||
71 | struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); | ||
72 | int ret; | ||
73 | |||
74 | if (state->power == 0) | ||
75 | return -EBUSY; | ||
76 | |||
77 | switch (ctrl->id) { | ||
78 | case V4L2_CID_FOCUS_AUTO: | ||
79 | ret = s5c73m3_get_af_status(state, state->ctrls.af_status); | ||
80 | if (ret) | ||
81 | return ret; | ||
82 | break; | ||
83 | } | ||
84 | |||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static int s5c73m3_set_colorfx(struct s5c73m3 *state, int val) | ||
89 | { | ||
90 | static const unsigned short colorfx[][2] = { | ||
91 | { V4L2_COLORFX_NONE, COMM_IMAGE_EFFECT_NONE }, | ||
92 | { V4L2_COLORFX_BW, COMM_IMAGE_EFFECT_MONO }, | ||
93 | { V4L2_COLORFX_SEPIA, COMM_IMAGE_EFFECT_SEPIA }, | ||
94 | { V4L2_COLORFX_NEGATIVE, COMM_IMAGE_EFFECT_NEGATIVE }, | ||
95 | { V4L2_COLORFX_AQUA, COMM_IMAGE_EFFECT_AQUA }, | ||
96 | }; | ||
97 | int i; | ||
98 | |||
99 | for (i = 0; i < ARRAY_SIZE(colorfx); i++) { | ||
100 | if (colorfx[i][0] != val) | ||
101 | continue; | ||
102 | |||
103 | v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, | ||
104 | "Setting %s color effect\n", | ||
105 | v4l2_ctrl_get_menu(state->ctrls.colorfx->id)[i]); | ||
106 | |||
107 | return s5c73m3_isp_command(state, COMM_IMAGE_EFFECT, | ||
108 | colorfx[i][1]); | ||
109 | } | ||
110 | return -EINVAL; | ||
111 | } | ||
112 | |||
113 | /* Set exposure metering/exposure bias */ | ||
114 | static int s5c73m3_set_exposure(struct s5c73m3 *state, int auto_exp) | ||
115 | { | ||
116 | struct v4l2_subdev *sd = &state->sensor_sd; | ||
117 | struct s5c73m3_ctrls *ctrls = &state->ctrls; | ||
118 | int ret = 0; | ||
119 | |||
120 | if (ctrls->exposure_metering->is_new) { | ||
121 | u16 metering; | ||
122 | |||
123 | switch (ctrls->exposure_metering->val) { | ||
124 | case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: | ||
125 | metering = COMM_METERING_CENTER; | ||
126 | break; | ||
127 | case V4L2_EXPOSURE_METERING_SPOT: | ||
128 | metering = COMM_METERING_SPOT; | ||
129 | break; | ||
130 | default: | ||
131 | metering = COMM_METERING_AVERAGE; | ||
132 | break; | ||
133 | } | ||
134 | |||
135 | ret = s5c73m3_isp_command(state, COMM_METERING, metering); | ||
136 | } | ||
137 | |||
138 | if (!ret && ctrls->exposure_bias->is_new) { | ||
139 | u16 exp_bias = ctrls->exposure_bias->val; | ||
140 | ret = s5c73m3_isp_command(state, COMM_EV, exp_bias); | ||
141 | } | ||
142 | |||
143 | v4l2_dbg(1, s5c73m3_dbg, sd, | ||
144 | "%s: exposure bias: %#x, metering: %#x (%d)\n", __func__, | ||
145 | ctrls->exposure_bias->val, ctrls->exposure_metering->val, ret); | ||
146 | |||
147 | return ret; | ||
148 | } | ||
149 | |||
150 | static int s5c73m3_set_white_balance(struct s5c73m3 *state, int val) | ||
151 | { | ||
152 | static const unsigned short wb[][2] = { | ||
153 | { V4L2_WHITE_BALANCE_INCANDESCENT, COMM_AWB_MODE_INCANDESCENT}, | ||
154 | { V4L2_WHITE_BALANCE_FLUORESCENT, COMM_AWB_MODE_FLUORESCENT1}, | ||
155 | { V4L2_WHITE_BALANCE_FLUORESCENT_H, COMM_AWB_MODE_FLUORESCENT2}, | ||
156 | { V4L2_WHITE_BALANCE_CLOUDY, COMM_AWB_MODE_CLOUDY}, | ||
157 | { V4L2_WHITE_BALANCE_DAYLIGHT, COMM_AWB_MODE_DAYLIGHT}, | ||
158 | { V4L2_WHITE_BALANCE_AUTO, COMM_AWB_MODE_AUTO}, | ||
159 | }; | ||
160 | int i; | ||
161 | |||
162 | for (i = 0; i < ARRAY_SIZE(wb); i++) { | ||
163 | if (wb[i][0] != val) | ||
164 | continue; | ||
165 | |||
166 | v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, | ||
167 | "Setting white balance to: %s\n", | ||
168 | v4l2_ctrl_get_menu(state->ctrls.auto_wb->id)[i]); | ||
169 | |||
170 | return s5c73m3_isp_command(state, COMM_AWB_MODE, wb[i][1]); | ||
171 | } | ||
172 | |||
173 | return -EINVAL; | ||
174 | } | ||
175 | |||
176 | static int s5c73m3_af_run(struct s5c73m3 *state, bool on) | ||
177 | { | ||
178 | struct s5c73m3_ctrls *c = &state->ctrls; | ||
179 | |||
180 | if (!on) | ||
181 | return s5c73m3_isp_command(state, COMM_AF_CON, | ||
182 | COMM_AF_CON_STOP); | ||
183 | |||
184 | if (c->focus_auto->val) | ||
185 | return s5c73m3_isp_command(state, COMM_AF_MODE, | ||
186 | COMM_AF_MODE_PREVIEW_CAF_START); | ||
187 | |||
188 | return s5c73m3_isp_command(state, COMM_AF_CON, COMM_AF_CON_START); | ||
189 | } | ||
190 | |||
191 | static int s5c73m3_3a_lock(struct s5c73m3 *state, struct v4l2_ctrl *ctrl) | ||
192 | { | ||
193 | bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; | ||
194 | bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; | ||
195 | bool af_lock = ctrl->val & V4L2_LOCK_FOCUS; | ||
196 | int ret = 0; | ||
197 | |||
198 | if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) { | ||
199 | ret = s5c73m3_isp_command(state, COMM_AE_CON, | ||
200 | ae_lock ? COMM_AE_STOP : COMM_AE_START); | ||
201 | if (ret) | ||
202 | return ret; | ||
203 | } | ||
204 | |||
205 | if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) | ||
206 | && state->ctrls.auto_wb->val) { | ||
207 | ret = s5c73m3_isp_command(state, COMM_AWB_CON, | ||
208 | awb_lock ? COMM_AWB_STOP : COMM_AWB_START); | ||
209 | if (ret) | ||
210 | return ret; | ||
211 | } | ||
212 | |||
213 | if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) | ||
214 | ret = s5c73m3_af_run(state, ~af_lock); | ||
215 | |||
216 | return ret; | ||
217 | } | ||
218 | |||
219 | static int s5c73m3_set_auto_focus(struct s5c73m3 *state, int caf) | ||
220 | { | ||
221 | struct s5c73m3_ctrls *c = &state->ctrls; | ||
222 | int ret = 1; | ||
223 | |||
224 | if (c->af_distance->is_new) { | ||
225 | u16 mode = (c->af_distance->val == V4L2_AUTO_FOCUS_RANGE_MACRO) | ||
226 | ? COMM_AF_MODE_MACRO : COMM_AF_MODE_NORMAL; | ||
227 | ret = s5c73m3_isp_command(state, COMM_AF_MODE, mode); | ||
228 | if (ret != 0) | ||
229 | return ret; | ||
230 | } | ||
231 | |||
232 | if (!ret || (c->focus_auto->is_new && c->focus_auto->val) || | ||
233 | c->af_start->is_new) | ||
234 | ret = s5c73m3_af_run(state, 1); | ||
235 | else if ((c->focus_auto->is_new && !c->focus_auto->val) || | ||
236 | c->af_stop->is_new) | ||
237 | ret = s5c73m3_af_run(state, 0); | ||
238 | else | ||
239 | ret = 0; | ||
240 | |||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | static int s5c73m3_set_contrast(struct s5c73m3 *state, int val) | ||
245 | { | ||
246 | u16 reg = (val < 0) ? -val + 2 : val; | ||
247 | return s5c73m3_isp_command(state, COMM_CONTRAST, reg); | ||
248 | } | ||
249 | |||
250 | static int s5c73m3_set_saturation(struct s5c73m3 *state, int val) | ||
251 | { | ||
252 | u16 reg = (val < 0) ? -val + 2 : val; | ||
253 | return s5c73m3_isp_command(state, COMM_SATURATION, reg); | ||
254 | } | ||
255 | |||
256 | static int s5c73m3_set_sharpness(struct s5c73m3 *state, int val) | ||
257 | { | ||
258 | u16 reg = (val < 0) ? -val + 2 : val; | ||
259 | return s5c73m3_isp_command(state, COMM_SHARPNESS, reg); | ||
260 | } | ||
261 | |||
262 | static int s5c73m3_set_iso(struct s5c73m3 *state, int val) | ||
263 | { | ||
264 | u32 iso; | ||
265 | |||
266 | if (val == V4L2_ISO_SENSITIVITY_MANUAL) | ||
267 | iso = state->ctrls.iso->val + 1; | ||
268 | else | ||
269 | iso = 0; | ||
270 | |||
271 | return s5c73m3_isp_command(state, COMM_ISO, iso); | ||
272 | } | ||
273 | |||
274 | static int s5c73m3_set_stabilization(struct s5c73m3 *state, int val) | ||
275 | { | ||
276 | struct v4l2_subdev *sd = &state->sensor_sd; | ||
277 | |||
278 | v4l2_dbg(1, s5c73m3_dbg, sd, "Image stabilization: %d\n", val); | ||
279 | |||
280 | return s5c73m3_isp_command(state, COMM_FRAME_RATE, val ? | ||
281 | COMM_FRAME_RATE_ANTI_SHAKE : COMM_FRAME_RATE_AUTO_SET); | ||
282 | } | ||
283 | |||
284 | static int s5c73m3_set_jpeg_quality(struct s5c73m3 *state, int quality) | ||
285 | { | ||
286 | int reg; | ||
287 | |||
288 | if (quality <= 65) | ||
289 | reg = COMM_IMAGE_QUALITY_NORMAL; | ||
290 | else if (quality <= 75) | ||
291 | reg = COMM_IMAGE_QUALITY_FINE; | ||
292 | else | ||
293 | reg = COMM_IMAGE_QUALITY_SUPERFINE; | ||
294 | |||
295 | return s5c73m3_isp_command(state, COMM_IMAGE_QUALITY, reg); | ||
296 | } | ||
297 | |||
298 | static int s5c73m3_set_scene_program(struct s5c73m3 *state, int val) | ||
299 | { | ||
300 | static const unsigned short scene_lookup[] = { | ||
301 | COMM_SCENE_MODE_NONE, /* V4L2_SCENE_MODE_NONE */ | ||
302 | COMM_SCENE_MODE_AGAINST_LIGHT,/* V4L2_SCENE_MODE_BACKLIGHT */ | ||
303 | COMM_SCENE_MODE_BEACH, /* V4L2_SCENE_MODE_BEACH_SNOW */ | ||
304 | COMM_SCENE_MODE_CANDLE, /* V4L2_SCENE_MODE_CANDLE_LIGHT */ | ||
305 | COMM_SCENE_MODE_DAWN, /* V4L2_SCENE_MODE_DAWN_DUSK */ | ||
306 | COMM_SCENE_MODE_FALL, /* V4L2_SCENE_MODE_FALL_COLORS */ | ||
307 | COMM_SCENE_MODE_FIRE, /* V4L2_SCENE_MODE_FIREWORKS */ | ||
308 | COMM_SCENE_MODE_LANDSCAPE, /* V4L2_SCENE_MODE_LANDSCAPE */ | ||
309 | COMM_SCENE_MODE_NIGHT, /* V4L2_SCENE_MODE_NIGHT */ | ||
310 | COMM_SCENE_MODE_INDOOR, /* V4L2_SCENE_MODE_PARTY_INDOOR */ | ||
311 | COMM_SCENE_MODE_PORTRAIT, /* V4L2_SCENE_MODE_PORTRAIT */ | ||
312 | COMM_SCENE_MODE_SPORTS, /* V4L2_SCENE_MODE_SPORTS */ | ||
313 | COMM_SCENE_MODE_SUNSET, /* V4L2_SCENE_MODE_SUNSET */ | ||
314 | COMM_SCENE_MODE_TEXT, /* V4L2_SCENE_MODE_TEXT */ | ||
315 | }; | ||
316 | |||
317 | v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, "Setting %s scene mode\n", | ||
318 | v4l2_ctrl_get_menu(state->ctrls.scene_mode->id)[val]); | ||
319 | |||
320 | return s5c73m3_isp_command(state, COMM_SCENE_MODE, scene_lookup[val]); | ||
321 | } | ||
322 | |||
323 | static int s5c73m3_set_power_line_freq(struct s5c73m3 *state, int val) | ||
324 | { | ||
325 | unsigned int pwr_line_freq = COMM_FLICKER_NONE; | ||
326 | |||
327 | switch (val) { | ||
328 | case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: | ||
329 | pwr_line_freq = COMM_FLICKER_NONE; | ||
330 | break; | ||
331 | case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: | ||
332 | pwr_line_freq = COMM_FLICKER_AUTO_50HZ; | ||
333 | break; | ||
334 | case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: | ||
335 | pwr_line_freq = COMM_FLICKER_AUTO_60HZ; | ||
336 | break; | ||
337 | default: | ||
338 | case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: | ||
339 | pwr_line_freq = COMM_FLICKER_NONE; | ||
340 | } | ||
341 | |||
342 | return s5c73m3_isp_command(state, COMM_FLICKER_MODE, pwr_line_freq); | ||
343 | } | ||
344 | |||
345 | static int s5c73m3_s_ctrl(struct v4l2_ctrl *ctrl) | ||
346 | { | ||
347 | struct v4l2_subdev *sd = ctrl_to_sensor_sd(ctrl); | ||
348 | struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); | ||
349 | int ret = 0; | ||
350 | |||
351 | v4l2_dbg(1, s5c73m3_dbg, sd, "set_ctrl: %s, value: %d\n", | ||
352 | ctrl->name, ctrl->val); | ||
353 | |||
354 | mutex_lock(&state->lock); | ||
355 | /* | ||
356 | * If the device is not powered up by the host driver do | ||
357 | * not apply any controls to H/W at this time. Instead | ||
358 | * the controls will be restored right after power-up. | ||
359 | */ | ||
360 | if (state->power == 0) | ||
361 | goto unlock; | ||
362 | |||
363 | if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) { | ||
364 | ret = -EINVAL; | ||
365 | goto unlock; | ||
366 | } | ||
367 | |||
368 | switch (ctrl->id) { | ||
369 | case V4L2_CID_3A_LOCK: | ||
370 | ret = s5c73m3_3a_lock(state, ctrl); | ||
371 | break; | ||
372 | |||
373 | case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: | ||
374 | ret = s5c73m3_set_white_balance(state, ctrl->val); | ||
375 | break; | ||
376 | |||
377 | case V4L2_CID_CONTRAST: | ||
378 | ret = s5c73m3_set_contrast(state, ctrl->val); | ||
379 | break; | ||
380 | |||
381 | case V4L2_CID_COLORFX: | ||
382 | ret = s5c73m3_set_colorfx(state, ctrl->val); | ||
383 | break; | ||
384 | |||
385 | case V4L2_CID_EXPOSURE_AUTO: | ||
386 | ret = s5c73m3_set_exposure(state, ctrl->val); | ||
387 | break; | ||
388 | |||
389 | case V4L2_CID_FOCUS_AUTO: | ||
390 | ret = s5c73m3_set_auto_focus(state, ctrl->val); | ||
391 | break; | ||
392 | |||
393 | case V4L2_CID_IMAGE_STABILIZATION: | ||
394 | ret = s5c73m3_set_stabilization(state, ctrl->val); | ||
395 | break; | ||
396 | |||
397 | case V4L2_CID_ISO_SENSITIVITY: | ||
398 | ret = s5c73m3_set_iso(state, ctrl->val); | ||
399 | break; | ||
400 | |||
401 | case V4L2_CID_JPEG_COMPRESSION_QUALITY: | ||
402 | ret = s5c73m3_set_jpeg_quality(state, ctrl->val); | ||
403 | break; | ||
404 | |||
405 | case V4L2_CID_POWER_LINE_FREQUENCY: | ||
406 | ret = s5c73m3_set_power_line_freq(state, ctrl->val); | ||
407 | break; | ||
408 | |||
409 | case V4L2_CID_SATURATION: | ||
410 | ret = s5c73m3_set_saturation(state, ctrl->val); | ||
411 | break; | ||
412 | |||
413 | case V4L2_CID_SCENE_MODE: | ||
414 | ret = s5c73m3_set_scene_program(state, ctrl->val); | ||
415 | break; | ||
416 | |||
417 | case V4L2_CID_SHARPNESS: | ||
418 | ret = s5c73m3_set_sharpness(state, ctrl->val); | ||
419 | break; | ||
420 | |||
421 | case V4L2_CID_WIDE_DYNAMIC_RANGE: | ||
422 | ret = s5c73m3_isp_command(state, COMM_WDR, !!ctrl->val); | ||
423 | break; | ||
424 | |||
425 | case V4L2_CID_ZOOM_ABSOLUTE: | ||
426 | ret = s5c73m3_isp_command(state, COMM_ZOOM_STEP, ctrl->val); | ||
427 | break; | ||
428 | } | ||
429 | unlock: | ||
430 | mutex_unlock(&state->lock); | ||
431 | return ret; | ||
432 | } | ||
433 | |||
434 | static const struct v4l2_ctrl_ops s5c73m3_ctrl_ops = { | ||
435 | .g_volatile_ctrl = s5c73m3_g_volatile_ctrl, | ||
436 | .s_ctrl = s5c73m3_s_ctrl, | ||
437 | }; | ||
438 | |||
439 | /* Supported manual ISO values */ | ||
440 | static const s64 iso_qmenu[] = { | ||
441 | /* COMM_ISO: 0x0001...0x0004 */ | ||
442 | 100, 200, 400, 800, | ||
443 | }; | ||
444 | |||
445 | /* Supported exposure bias values (-2.0EV...+2.0EV) */ | ||
446 | static const s64 ev_bias_qmenu[] = { | ||
447 | /* COMM_EV: 0x0000...0x0008 */ | ||
448 | -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000 | ||
449 | }; | ||
450 | |||
451 | int s5c73m3_init_controls(struct s5c73m3 *state) | ||
452 | { | ||
453 | const struct v4l2_ctrl_ops *ops = &s5c73m3_ctrl_ops; | ||
454 | struct s5c73m3_ctrls *ctrls = &state->ctrls; | ||
455 | struct v4l2_ctrl_handler *hdl = &ctrls->handler; | ||
456 | |||
457 | int ret = v4l2_ctrl_handler_init(hdl, 22); | ||
458 | if (ret) | ||
459 | return ret; | ||
460 | |||
461 | /* White balance */ | ||
462 | ctrls->auto_wb = v4l2_ctrl_new_std_menu(hdl, ops, | ||
463 | V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, | ||
464 | 9, ~0x15e, V4L2_WHITE_BALANCE_AUTO); | ||
465 | |||
466 | /* Exposure (only automatic exposure) */ | ||
467 | ctrls->auto_exposure = v4l2_ctrl_new_std_menu(hdl, ops, | ||
468 | V4L2_CID_EXPOSURE_AUTO, 0, ~0x01, V4L2_EXPOSURE_AUTO); | ||
469 | |||
470 | ctrls->exposure_bias = v4l2_ctrl_new_int_menu(hdl, ops, | ||
471 | V4L2_CID_AUTO_EXPOSURE_BIAS, | ||
472 | ARRAY_SIZE(ev_bias_qmenu) - 1, | ||
473 | ARRAY_SIZE(ev_bias_qmenu)/2 - 1, | ||
474 | ev_bias_qmenu); | ||
475 | |||
476 | ctrls->exposure_metering = v4l2_ctrl_new_std_menu(hdl, ops, | ||
477 | V4L2_CID_EXPOSURE_METERING, | ||
478 | 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE); | ||
479 | |||
480 | /* Auto focus */ | ||
481 | ctrls->focus_auto = v4l2_ctrl_new_std(hdl, ops, | ||
482 | V4L2_CID_FOCUS_AUTO, 0, 1, 1, 0); | ||
483 | |||
484 | ctrls->af_start = v4l2_ctrl_new_std(hdl, ops, | ||
485 | V4L2_CID_AUTO_FOCUS_START, 0, 1, 1, 0); | ||
486 | |||
487 | ctrls->af_stop = v4l2_ctrl_new_std(hdl, ops, | ||
488 | V4L2_CID_AUTO_FOCUS_STOP, 0, 1, 1, 0); | ||
489 | |||
490 | ctrls->af_status = v4l2_ctrl_new_std(hdl, ops, | ||
491 | V4L2_CID_AUTO_FOCUS_STATUS, 0, | ||
492 | (V4L2_AUTO_FOCUS_STATUS_BUSY | | ||
493 | V4L2_AUTO_FOCUS_STATUS_REACHED | | ||
494 | V4L2_AUTO_FOCUS_STATUS_FAILED), | ||
495 | 0, V4L2_AUTO_FOCUS_STATUS_IDLE); | ||
496 | |||
497 | ctrls->af_distance = v4l2_ctrl_new_std_menu(hdl, ops, | ||
498 | V4L2_CID_AUTO_FOCUS_RANGE, | ||
499 | V4L2_AUTO_FOCUS_RANGE_MACRO, | ||
500 | ~(1 << V4L2_AUTO_FOCUS_RANGE_NORMAL | | ||
501 | 1 << V4L2_AUTO_FOCUS_RANGE_MACRO), | ||
502 | V4L2_AUTO_FOCUS_RANGE_NORMAL); | ||
503 | /* ISO sensitivity */ | ||
504 | ctrls->auto_iso = v4l2_ctrl_new_std_menu(hdl, ops, | ||
505 | V4L2_CID_ISO_SENSITIVITY_AUTO, 1, 0, | ||
506 | V4L2_ISO_SENSITIVITY_AUTO); | ||
507 | |||
508 | ctrls->iso = v4l2_ctrl_new_int_menu(hdl, ops, | ||
509 | V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, | ||
510 | ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); | ||
511 | |||
512 | ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, | ||
513 | V4L2_CID_CONTRAST, -2, 2, 1, 0); | ||
514 | |||
515 | ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, | ||
516 | V4L2_CID_SATURATION, -2, 2, 1, 0); | ||
517 | |||
518 | ctrls->sharpness = v4l2_ctrl_new_std(hdl, ops, | ||
519 | V4L2_CID_SHARPNESS, -2, 2, 1, 0); | ||
520 | |||
521 | ctrls->zoom = v4l2_ctrl_new_std(hdl, ops, | ||
522 | V4L2_CID_ZOOM_ABSOLUTE, 0, 30, 1, 0); | ||
523 | |||
524 | ctrls->colorfx = v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, | ||
525 | V4L2_COLORFX_AQUA, ~0x40f, V4L2_COLORFX_NONE); | ||
526 | |||
527 | ctrls->wdr = v4l2_ctrl_new_std(hdl, ops, | ||
528 | V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); | ||
529 | |||
530 | ctrls->stabilization = v4l2_ctrl_new_std(hdl, ops, | ||
531 | V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); | ||
532 | |||
533 | v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, | ||
534 | V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, | ||
535 | V4L2_CID_POWER_LINE_FREQUENCY_AUTO); | ||
536 | |||
537 | ctrls->jpeg_quality = v4l2_ctrl_new_std(hdl, ops, | ||
538 | V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80); | ||
539 | |||
540 | ctrls->scene_mode = v4l2_ctrl_new_std_menu(hdl, ops, | ||
541 | V4L2_CID_SCENE_MODE, V4L2_SCENE_MODE_TEXT, ~0x3fff, | ||
542 | V4L2_SCENE_MODE_NONE); | ||
543 | |||
544 | ctrls->aaa_lock = v4l2_ctrl_new_std(hdl, ops, | ||
545 | V4L2_CID_3A_LOCK, 0, 0x7, 0, 0); | ||
546 | |||
547 | if (hdl->error) { | ||
548 | ret = hdl->error; | ||
549 | v4l2_ctrl_handler_free(hdl); | ||
550 | return ret; | ||
551 | } | ||
552 | |||
553 | v4l2_ctrl_auto_cluster(3, &ctrls->auto_exposure, 0, false); | ||
554 | ctrls->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | | ||
555 | V4L2_CTRL_FLAG_UPDATE; | ||
556 | v4l2_ctrl_auto_cluster(2, &ctrls->auto_iso, 0, false); | ||
557 | ctrls->af_status->flags |= V4L2_CTRL_FLAG_VOLATILE; | ||
558 | v4l2_ctrl_cluster(6, &ctrls->focus_auto); | ||
559 | |||
560 | state->sensor_sd.ctrl_handler = hdl; | ||
561 | |||
562 | return 0; | ||
563 | } | ||
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c new file mode 100644 index 000000000000..889139c2ac5e --- /dev/null +++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c | |||
@@ -0,0 +1,156 @@ | |||
1 | /* | ||
2 | * Samsung LSI S5C73M3 8M pixel camera driver | ||
3 | * | ||
4 | * Copyright (C) 2012, Samsung Electronics, Co., Ltd. | ||
5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
6 | * Andrzej Hajda <a.hajda@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/sizes.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/media.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/spi/spi.h> | ||
25 | |||
26 | #include "s5c73m3.h" | ||
27 | |||
28 | #define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI" | ||
29 | |||
30 | enum spi_direction { | ||
31 | SPI_DIR_RX, | ||
32 | SPI_DIR_TX | ||
33 | }; | ||
34 | |||
35 | static int spi_xmit(struct spi_device *spi_dev, void *addr, const int len, | ||
36 | enum spi_direction dir) | ||
37 | { | ||
38 | struct spi_message msg; | ||
39 | int r; | ||
40 | struct spi_transfer xfer = { | ||
41 | .len = len, | ||
42 | }; | ||
43 | |||
44 | if (dir == SPI_DIR_TX) | ||
45 | xfer.tx_buf = addr; | ||
46 | else | ||
47 | xfer.rx_buf = addr; | ||
48 | |||
49 | if (spi_dev == NULL) { | ||
50 | dev_err(&spi_dev->dev, "SPI device is uninitialized\n"); | ||
51 | return -ENODEV; | ||
52 | } | ||
53 | |||
54 | spi_message_init(&msg); | ||
55 | spi_message_add_tail(&xfer, &msg); | ||
56 | |||
57 | r = spi_sync(spi_dev, &msg); | ||
58 | if (r < 0) | ||
59 | dev_err(&spi_dev->dev, "%s spi_sync failed %d\n", __func__, r); | ||
60 | |||
61 | return r; | ||
62 | } | ||
63 | |||
64 | int s5c73m3_spi_write(struct s5c73m3 *state, const void *addr, | ||
65 | const unsigned int len, const unsigned int tx_size) | ||
66 | { | ||
67 | struct spi_device *spi_dev = state->spi_dev; | ||
68 | u32 count = len / tx_size; | ||
69 | u32 extra = len % tx_size; | ||
70 | unsigned int i, j = 0; | ||
71 | u8 padding[32]; | ||
72 | int r = 0; | ||
73 | |||
74 | memset(padding, 0, sizeof(padding)); | ||
75 | |||
76 | for (i = 0; i < count ; i++) { | ||
77 | r = spi_xmit(spi_dev, (void *)addr + j, tx_size, SPI_DIR_TX); | ||
78 | if (r < 0) | ||
79 | return r; | ||
80 | j += tx_size; | ||
81 | } | ||
82 | |||
83 | if (extra > 0) { | ||
84 | r = spi_xmit(spi_dev, (void *)addr + j, extra, SPI_DIR_TX); | ||
85 | if (r < 0) | ||
86 | return r; | ||
87 | } | ||
88 | |||
89 | return spi_xmit(spi_dev, padding, sizeof(padding), SPI_DIR_TX); | ||
90 | } | ||
91 | |||
92 | int s5c73m3_spi_read(struct s5c73m3 *state, void *addr, | ||
93 | const unsigned int len, const unsigned int tx_size) | ||
94 | { | ||
95 | struct spi_device *spi_dev = state->spi_dev; | ||
96 | u32 count = len / tx_size; | ||
97 | u32 extra = len % tx_size; | ||
98 | unsigned int i, j = 0; | ||
99 | int r = 0; | ||
100 | |||
101 | for (i = 0; i < count ; i++) { | ||
102 | r = spi_xmit(spi_dev, addr + j, tx_size, SPI_DIR_RX); | ||
103 | if (r < 0) | ||
104 | return r; | ||
105 | j += tx_size; | ||
106 | } | ||
107 | |||
108 | if (extra > 0) | ||
109 | return spi_xmit(spi_dev, addr + j, extra, SPI_DIR_RX); | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static int __devinit s5c73m3_spi_probe(struct spi_device *spi) | ||
115 | { | ||
116 | int r; | ||
117 | struct s5c73m3 *state = container_of(spi->dev.driver, struct s5c73m3, | ||
118 | spidrv.driver); | ||
119 | spi->bits_per_word = 32; | ||
120 | |||
121 | r = spi_setup(spi); | ||
122 | if (r < 0) { | ||
123 | dev_err(&spi->dev, "spi_setup() failed\n"); | ||
124 | return r; | ||
125 | } | ||
126 | |||
127 | mutex_lock(&state->lock); | ||
128 | state->spi_dev = spi; | ||
129 | mutex_unlock(&state->lock); | ||
130 | |||
131 | v4l2_info(&state->sensor_sd, "S5C73M3 SPI probed successfully\n"); | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | static int __devexit s5c73m3_spi_remove(struct spi_device *spi) | ||
136 | { | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | int s5c73m3_register_spi_driver(struct s5c73m3 *state) | ||
141 | { | ||
142 | struct spi_driver *spidrv = &state->spidrv; | ||
143 | |||
144 | spidrv->remove = __devexit_p(s5c73m3_spi_remove); | ||
145 | spidrv->probe = s5c73m3_spi_probe; | ||
146 | spidrv->driver.name = S5C73M3_SPI_DRV_NAME; | ||
147 | spidrv->driver.bus = &spi_bus_type; | ||
148 | spidrv->driver.owner = THIS_MODULE; | ||
149 | |||
150 | return spi_register_driver(spidrv); | ||
151 | } | ||
152 | |||
153 | void s5c73m3_unregister_spi_driver(struct s5c73m3 *state) | ||
154 | { | ||
155 | spi_unregister_driver(&state->spidrv); | ||
156 | } | ||
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h new file mode 100644 index 000000000000..9d2c08652246 --- /dev/null +++ b/drivers/media/i2c/s5c73m3/s5c73m3.h | |||
@@ -0,0 +1,459 @@ | |||
1 | /* | ||
2 | * Samsung LSI S5C73M3 8M pixel camera driver | ||
3 | * | ||
4 | * Copyright (C) 2012, Samsung Electronics, Co., Ltd. | ||
5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
6 | * Andrzej Hajda <a.hajda@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | #ifndef S5C73M3_H_ | ||
18 | #define S5C73M3_H_ | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/regulator/consumer.h> | ||
22 | #include <media/v4l2-common.h> | ||
23 | #include <media/v4l2-ctrls.h> | ||
24 | #include <media/v4l2-subdev.h> | ||
25 | #include <media/s5c73m3.h> | ||
26 | |||
27 | #define DRIVER_NAME "S5C73M3" | ||
28 | |||
29 | #define S5C73M3_ISP_FMT V4L2_MBUS_FMT_VYUY8_2X8 | ||
30 | #define S5C73M3_JPEG_FMT V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8 | ||
31 | |||
32 | /* Subdevs pad index definitions */ | ||
33 | enum s5c73m3_pads { | ||
34 | S5C73M3_ISP_PAD, | ||
35 | S5C73M3_JPEG_PAD, | ||
36 | S5C73M3_NUM_PADS | ||
37 | }; | ||
38 | |||
39 | enum s5c73m3_oif_pads { | ||
40 | OIF_ISP_PAD, | ||
41 | OIF_JPEG_PAD, | ||
42 | OIF_SOURCE_PAD, | ||
43 | OIF_NUM_PADS | ||
44 | }; | ||
45 | |||
46 | #define S5C73M3_SENSOR_FW_LEN 6 | ||
47 | #define S5C73M3_SENSOR_TYPE_LEN 12 | ||
48 | |||
49 | #define S5C73M3_REG(_addrh, _addrl) (((_addrh) << 16) | _addrl) | ||
50 | |||
51 | #define AHB_MSB_ADDR_PTR 0xfcfc | ||
52 | #define REG_CMDWR_ADDRH 0x0050 | ||
53 | #define REG_CMDWR_ADDRL 0x0054 | ||
54 | #define REG_CMDRD_ADDRH 0x0058 | ||
55 | #define REG_CMDRD_ADDRL 0x005c | ||
56 | #define REG_CMDBUF_ADDR 0x0f14 | ||
57 | |||
58 | #define REG_I2C_SEQ_STATUS S5C73M3_REG(0x0009, 0x59A6) | ||
59 | #define SEQ_END_PLL (1<<0x0) | ||
60 | #define SEQ_END_SENSOR (1<<0x1) | ||
61 | #define SEQ_END_GPIO (1<<0x2) | ||
62 | #define SEQ_END_FROM (1<<0x3) | ||
63 | #define SEQ_END_STABLE_AE_AWB (1<<0x4) | ||
64 | #define SEQ_END_READY_I2C_CMD (1<<0x5) | ||
65 | |||
66 | #define REG_I2C_STATUS S5C73M3_REG(0x0009, 0x599E) | ||
67 | #define I2C_STATUS_CIS_I2C (1<<0x0) | ||
68 | #define I2C_STATUS_AF_INIT (1<<0x1) | ||
69 | #define I2C_STATUS_CAL_DATA (1<<0x2) | ||
70 | #define I2C_STATUS_FRAME_COUNT (1<<0x3) | ||
71 | #define I2C_STATUS_FROM_INIT (1<<0x4) | ||
72 | #define I2C_STATUS_I2C_CIS_STREAM_OFF (1<<0x5) | ||
73 | #define I2C_STATUS_I2C_N_CMD_OVER (1<<0x6) | ||
74 | #define I2C_STATUS_I2C_N_CMD_MISMATCH (1<<0x7) | ||
75 | #define I2C_STATUS_CHECK_BIN_CRC (1<<0x8) | ||
76 | #define I2C_STATUS_EXCEPTION (1<<0x9) | ||
77 | #define I2C_STATUS_INIF_INIT_STATE (0x8) | ||
78 | |||
79 | #define REG_STATUS S5C73M3_REG(0x0009, 0x5080) | ||
80 | #define REG_STATUS_BOOT_SUB_MAIN_ENTER 0xff01 | ||
81 | #define REG_STATUS_BOOT_SRAM_TIMING_OK 0xff02 | ||
82 | #define REG_STATUS_BOOT_INTERRUPTS_EN 0xff03 | ||
83 | #define REG_STATUS_BOOT_R_PLL_DONE 0xff04 | ||
84 | #define REG_STATUS_BOOT_R_PLL_LOCKTIME_DONE 0xff05 | ||
85 | #define REG_STATUS_BOOT_DELAY_COUNT_DONE 0xff06 | ||
86 | #define REG_STATUS_BOOT_I_PLL_DONE 0xff07 | ||
87 | #define REG_STATUS_BOOT_I_PLL_LOCKTIME_DONE 0xff08 | ||
88 | #define REG_STATUS_BOOT_PLL_INIT_OK 0xff09 | ||
89 | #define REG_STATUS_BOOT_SENSOR_INIT_OK 0xff0a | ||
90 | #define REG_STATUS_BOOT_GPIO_SETTING_OK 0xff0b | ||
91 | #define REG_STATUS_BOOT_READ_CAL_DATA_OK 0xff0c | ||
92 | #define REG_STATUS_BOOT_STABLE_AE_AWB_OK 0xff0d | ||
93 | #define REG_STATUS_ISP_COMMAND_COMPLETED 0xffff | ||
94 | #define REG_STATUS_EXCEPTION_OCCURED 0xdead | ||
95 | |||
96 | #define COMM_RESULT_OFFSET S5C73M3_REG(0x0009, 0x5000) | ||
97 | |||
98 | #define COMM_IMG_OUTPUT 0x0902 | ||
99 | #define COMM_IMG_OUTPUT_HDR 0x0008 | ||
100 | #define COMM_IMG_OUTPUT_YUV 0x0009 | ||
101 | #define COMM_IMG_OUTPUT_INTERLEAVED 0x000d | ||
102 | |||
103 | #define COMM_STILL_PRE_FLASH 0x0a00 | ||
104 | #define COMM_STILL_PRE_FLASH_FIRE 0x0000 | ||
105 | #define COMM_STILL_PRE_FLASH_NON_FIRED 0x0000 | ||
106 | #define COMM_STILL_PRE_FLASH_FIRED 0x0001 | ||
107 | |||
108 | #define COMM_STILL_MAIN_FLASH 0x0a02 | ||
109 | #define COMM_STILL_MAIN_FLASH_CANCEL 0x0001 | ||
110 | #define COMM_STILL_MAIN_FLASH_FIRE 0x0002 | ||
111 | |||
112 | #define COMM_ZOOM_STEP 0x0b00 | ||
113 | |||
114 | #define COMM_IMAGE_EFFECT 0x0b0a | ||
115 | #define COMM_IMAGE_EFFECT_NONE 0x0001 | ||
116 | #define COMM_IMAGE_EFFECT_NEGATIVE 0x0002 | ||
117 | #define COMM_IMAGE_EFFECT_AQUA 0x0003 | ||
118 | #define COMM_IMAGE_EFFECT_SEPIA 0x0004 | ||
119 | #define COMM_IMAGE_EFFECT_MONO 0x0005 | ||
120 | |||
121 | #define COMM_IMAGE_QUALITY 0x0b0c | ||
122 | #define COMM_IMAGE_QUALITY_SUPERFINE 0x0000 | ||
123 | #define COMM_IMAGE_QUALITY_FINE 0x0001 | ||
124 | #define COMM_IMAGE_QUALITY_NORMAL 0x0002 | ||
125 | |||
126 | #define COMM_FLASH_MODE 0x0b0e | ||
127 | #define COMM_FLASH_MODE_OFF 0x0000 | ||
128 | #define COMM_FLASH_MODE_ON 0x0001 | ||
129 | #define COMM_FLASH_MODE_AUTO 0x0002 | ||
130 | |||
131 | #define COMM_FLASH_STATUS 0x0b80 | ||
132 | #define COMM_FLASH_STATUS_OFF 0x0001 | ||
133 | #define COMM_FLASH_STATUS_ON 0x0002 | ||
134 | #define COMM_FLASH_STATUS_AUTO 0x0003 | ||
135 | |||
136 | #define COMM_FLASH_TORCH 0x0b12 | ||
137 | #define COMM_FLASH_TORCH_OFF 0x0000 | ||
138 | #define COMM_FLASH_TORCH_ON 0x0001 | ||
139 | |||
140 | #define COMM_AE_NEEDS_FLASH 0x0cba | ||
141 | #define COMM_AE_NEEDS_FLASH_OFF 0x0000 | ||
142 | #define COMM_AE_NEEDS_FLASH_ON 0x0001 | ||
143 | |||
144 | #define COMM_CHG_MODE 0x0b10 | ||
145 | #define COMM_CHG_MODE_NEW 0x8000 | ||
146 | #define COMM_CHG_MODE_SUBSAMPLING_HALF 0x2000 | ||
147 | #define COMM_CHG_MODE_SUBSAMPLING_QUARTER 0x4000 | ||
148 | |||
149 | #define COMM_CHG_MODE_YUV_320_240 0x0001 | ||
150 | #define COMM_CHG_MODE_YUV_640_480 0x0002 | ||
151 | #define COMM_CHG_MODE_YUV_880_720 0x0003 | ||
152 | #define COMM_CHG_MODE_YUV_960_720 0x0004 | ||
153 | #define COMM_CHG_MODE_YUV_1184_666 0x0005 | ||
154 | #define COMM_CHG_MODE_YUV_1280_720 0x0006 | ||
155 | #define COMM_CHG_MODE_YUV_1536_864 0x0007 | ||
156 | #define COMM_CHG_MODE_YUV_1600_1200 0x0008 | ||
157 | #define COMM_CHG_MODE_YUV_1632_1224 0x0009 | ||
158 | #define COMM_CHG_MODE_YUV_1920_1080 0x000a | ||
159 | #define COMM_CHG_MODE_YUV_1920_1440 0x000b | ||
160 | #define COMM_CHG_MODE_YUV_2304_1296 0x000c | ||
161 | #define COMM_CHG_MODE_YUV_3264_2448 0x000d | ||
162 | #define COMM_CHG_MODE_YUV_352_288 0x000e | ||
163 | #define COMM_CHG_MODE_YUV_1008_672 0x000f | ||
164 | |||
165 | #define COMM_CHG_MODE_JPEG_640_480 0x0010 | ||
166 | #define COMM_CHG_MODE_JPEG_800_450 0x0020 | ||
167 | #define COMM_CHG_MODE_JPEG_800_600 0x0030 | ||
168 | #define COMM_CHG_MODE_JPEG_1280_720 0x0040 | ||
169 | #define COMM_CHG_MODE_JPEG_1280_960 0x0050 | ||
170 | #define COMM_CHG_MODE_JPEG_1600_900 0x0060 | ||
171 | #define COMM_CHG_MODE_JPEG_1600_1200 0x0070 | ||
172 | #define COMM_CHG_MODE_JPEG_2048_1152 0x0080 | ||
173 | #define COMM_CHG_MODE_JPEG_2048_1536 0x0090 | ||
174 | #define COMM_CHG_MODE_JPEG_2560_1440 0x00a0 | ||
175 | #define COMM_CHG_MODE_JPEG_2560_1920 0x00b0 | ||
176 | #define COMM_CHG_MODE_JPEG_3264_2176 0x00c0 | ||
177 | #define COMM_CHG_MODE_JPEG_1024_768 0x00d0 | ||
178 | #define COMM_CHG_MODE_JPEG_3264_1836 0x00e0 | ||
179 | #define COMM_CHG_MODE_JPEG_3264_2448 0x00f0 | ||
180 | |||
181 | #define COMM_AF_CON 0x0e00 | ||
182 | #define COMM_AF_CON_STOP 0x0000 | ||
183 | #define COMM_AF_CON_SCAN 0x0001 /* Full Search */ | ||
184 | #define COMM_AF_CON_START 0x0002 /* Fast Search */ | ||
185 | |||
186 | #define COMM_AF_CAL 0x0e06 | ||
187 | #define COMM_AF_TOUCH_AF 0x0e0a | ||
188 | |||
189 | #define REG_AF_STATUS S5C73M3_REG(0x0009, 0x5e80) | ||
190 | #define REG_CAF_STATUS_FIND_SEARCH_DIR 0x0001 | ||
191 | #define REG_CAF_STATUS_FOCUSING 0x0002 | ||
192 | #define REG_CAF_STATUS_FOCUSED 0x0003 | ||
193 | #define REG_CAF_STATUS_UNFOCUSED 0x0004 | ||
194 | #define REG_AF_STATUS_INVALID 0x0010 | ||
195 | #define REG_AF_STATUS_FOCUSING 0x0020 | ||
196 | #define REG_AF_STATUS_FOCUSED 0x0030 | ||
197 | #define REG_AF_STATUS_UNFOCUSED 0x0040 | ||
198 | |||
199 | #define REG_AF_TOUCH_POSITION S5C73M3_REG(0x0009, 0x5e8e) | ||
200 | #define COMM_AF_FACE_ZOOM 0x0e10 | ||
201 | |||
202 | #define COMM_AF_MODE 0x0e02 | ||
203 | #define COMM_AF_MODE_NORMAL 0x0000 | ||
204 | #define COMM_AF_MODE_MACRO 0x0001 | ||
205 | #define COMM_AF_MODE_MOVIE_CAF_START 0x0002 | ||
206 | #define COMM_AF_MODE_MOVIE_CAF_STOP 0x0003 | ||
207 | #define COMM_AF_MODE_PREVIEW_CAF_START 0x0004 | ||
208 | #define COMM_AF_MODE_PREVIEW_CAF_STOP 0x0005 | ||
209 | |||
210 | #define COMM_AF_SOFTLANDING 0x0e16 | ||
211 | #define COMM_AF_SOFTLANDING_ON 0x0000 | ||
212 | #define COMM_AF_SOFTLANDING_RES_COMPLETE 0x0001 | ||
213 | |||
214 | #define COMM_FACE_DET 0x0e0c | ||
215 | #define COMM_FACE_DET_OFF 0x0000 | ||
216 | #define COMM_FACE_DET_ON 0x0001 | ||
217 | |||
218 | #define COMM_FACE_DET_OSD 0x0e0e | ||
219 | #define COMM_FACE_DET_OSD_OFF 0x0000 | ||
220 | #define COMM_FACE_DET_OSD_ON 0x0001 | ||
221 | |||
222 | #define COMM_AE_CON 0x0c00 | ||
223 | #define COMM_AE_STOP 0x0000 /* lock */ | ||
224 | #define COMM_AE_START 0x0001 /* unlock */ | ||
225 | |||
226 | #define COMM_ISO 0x0c02 | ||
227 | #define COMM_ISO_AUTO 0x0000 | ||
228 | #define COMM_ISO_100 0x0001 | ||
229 | #define COMM_ISO_200 0x0002 | ||
230 | #define COMM_ISO_400 0x0003 | ||
231 | #define COMM_ISO_800 0x0004 | ||
232 | #define COMM_ISO_SPORTS 0x0005 | ||
233 | #define COMM_ISO_NIGHT 0x0006 | ||
234 | #define COMM_ISO_INDOOR 0x0007 | ||
235 | |||
236 | /* 0x00000 (-2.0 EV)...0x0008 (2.0 EV), 0.5EV step */ | ||
237 | #define COMM_EV 0x0c04 | ||
238 | |||
239 | #define COMM_METERING 0x0c06 | ||
240 | #define COMM_METERING_CENTER 0x0000 | ||
241 | #define COMM_METERING_SPOT 0x0001 | ||
242 | #define COMM_METERING_AVERAGE 0x0002 | ||
243 | #define COMM_METERING_SMART 0x0003 | ||
244 | |||
245 | #define COMM_WDR 0x0c08 | ||
246 | #define COMM_WDR_OFF 0x0000 | ||
247 | #define COMM_WDR_ON 0x0001 | ||
248 | |||
249 | #define COMM_FLICKER_MODE 0x0c12 | ||
250 | #define COMM_FLICKER_NONE 0x0000 | ||
251 | #define COMM_FLICKER_MANUAL_50HZ 0x0001 | ||
252 | #define COMM_FLICKER_MANUAL_60HZ 0x0002 | ||
253 | #define COMM_FLICKER_AUTO 0x0003 | ||
254 | #define COMM_FLICKER_AUTO_50HZ 0x0004 | ||
255 | #define COMM_FLICKER_AUTO_60HZ 0x0005 | ||
256 | |||
257 | #define COMM_FRAME_RATE 0x0c1e | ||
258 | #define COMM_FRAME_RATE_AUTO_SET 0x0000 | ||
259 | #define COMM_FRAME_RATE_FIXED_30FPS 0x0002 | ||
260 | #define COMM_FRAME_RATE_FIXED_20FPS 0x0003 | ||
261 | #define COMM_FRAME_RATE_FIXED_15FPS 0x0004 | ||
262 | #define COMM_FRAME_RATE_FIXED_60FPS 0x0007 | ||
263 | #define COMM_FRAME_RATE_FIXED_120FPS 0x0008 | ||
264 | #define COMM_FRAME_RATE_FIXED_7FPS 0x0009 | ||
265 | #define COMM_FRAME_RATE_FIXED_10FPS 0x000a | ||
266 | #define COMM_FRAME_RATE_FIXED_90FPS 0x000b | ||
267 | #define COMM_FRAME_RATE_ANTI_SHAKE 0x0013 | ||
268 | |||
269 | /* 0x0000...0x0004 -> sharpness: 0, 1, 2, -1, -2 */ | ||
270 | #define COMM_SHARPNESS 0x0c14 | ||
271 | |||
272 | /* 0x0000...0x0004 -> saturation: 0, 1, 2, -1, -2 */ | ||
273 | #define COMM_SATURATION 0x0c16 | ||
274 | |||
275 | /* 0x0000...0x0004 -> contrast: 0, 1, 2, -1, -2 */ | ||
276 | #define COMM_CONTRAST 0x0c18 | ||
277 | |||
278 | #define COMM_SCENE_MODE 0x0c1a | ||
279 | #define COMM_SCENE_MODE_NONE 0x0000 | ||
280 | #define COMM_SCENE_MODE_PORTRAIT 0x0001 | ||
281 | #define COMM_SCENE_MODE_LANDSCAPE 0x0002 | ||
282 | #define COMM_SCENE_MODE_SPORTS 0x0003 | ||
283 | #define COMM_SCENE_MODE_INDOOR 0x0004 | ||
284 | #define COMM_SCENE_MODE_BEACH 0x0005 | ||
285 | #define COMM_SCENE_MODE_SUNSET 0x0006 | ||
286 | #define COMM_SCENE_MODE_DAWN 0x0007 | ||
287 | #define COMM_SCENE_MODE_FALL 0x0008 | ||
288 | #define COMM_SCENE_MODE_NIGHT 0x0009 | ||
289 | #define COMM_SCENE_MODE_AGAINST_LIGHT 0x000a | ||
290 | #define COMM_SCENE_MODE_FIRE 0x000b | ||
291 | #define COMM_SCENE_MODE_TEXT 0x000c | ||
292 | #define COMM_SCENE_MODE_CANDLE 0x000d | ||
293 | |||
294 | #define COMM_AE_AUTO_BRACKET 0x0b14 | ||
295 | #define COMM_AE_AUTO_BRAKET_EV05 0x0080 | ||
296 | #define COMM_AE_AUTO_BRAKET_EV10 0x0100 | ||
297 | #define COMM_AE_AUTO_BRAKET_EV15 0x0180 | ||
298 | #define COMM_AE_AUTO_BRAKET_EV20 0x0200 | ||
299 | |||
300 | #define COMM_SENSOR_STREAMING 0x090a | ||
301 | #define COMM_SENSOR_STREAMING_OFF 0x0000 | ||
302 | #define COMM_SENSOR_STREAMING_ON 0x0001 | ||
303 | |||
304 | #define COMM_AWB_MODE 0x0d02 | ||
305 | #define COMM_AWB_MODE_INCANDESCENT 0x0000 | ||
306 | #define COMM_AWB_MODE_FLUORESCENT1 0x0001 | ||
307 | #define COMM_AWB_MODE_FLUORESCENT2 0x0002 | ||
308 | #define COMM_AWB_MODE_DAYLIGHT 0x0003 | ||
309 | #define COMM_AWB_MODE_CLOUDY 0x0004 | ||
310 | #define COMM_AWB_MODE_AUTO 0x0005 | ||
311 | |||
312 | #define COMM_AWB_CON 0x0d00 | ||
313 | #define COMM_AWB_STOP 0x0000 /* lock */ | ||
314 | #define COMM_AWB_START 0x0001 /* unlock */ | ||
315 | |||
316 | #define COMM_FW_UPDATE 0x0906 | ||
317 | #define COMM_FW_UPDATE_NOT_READY 0x0000 | ||
318 | #define COMM_FW_UPDATE_SUCCESS 0x0005 | ||
319 | #define COMM_FW_UPDATE_FAIL 0x0007 | ||
320 | #define COMM_FW_UPDATE_BUSY 0xffff | ||
321 | |||
322 | |||
323 | #define S5C73M3_MAX_SUPPLIES 6 | ||
324 | |||
325 | struct s5c73m3_ctrls { | ||
326 | struct v4l2_ctrl_handler handler; | ||
327 | struct { | ||
328 | /* exposure/exposure bias cluster */ | ||
329 | struct v4l2_ctrl *auto_exposure; | ||
330 | struct v4l2_ctrl *exposure_bias; | ||
331 | struct v4l2_ctrl *exposure_metering; | ||
332 | }; | ||
333 | struct { | ||
334 | /* iso/auto iso cluster */ | ||
335 | struct v4l2_ctrl *auto_iso; | ||
336 | struct v4l2_ctrl *iso; | ||
337 | }; | ||
338 | struct v4l2_ctrl *auto_wb; | ||
339 | struct { | ||
340 | /* continuous auto focus/auto focus cluster */ | ||
341 | struct v4l2_ctrl *focus_auto; | ||
342 | struct v4l2_ctrl *af_start; | ||
343 | struct v4l2_ctrl *af_stop; | ||
344 | struct v4l2_ctrl *af_status; | ||
345 | struct v4l2_ctrl *af_distance; | ||
346 | }; | ||
347 | |||
348 | struct v4l2_ctrl *aaa_lock; | ||
349 | struct v4l2_ctrl *colorfx; | ||
350 | struct v4l2_ctrl *contrast; | ||
351 | struct v4l2_ctrl *saturation; | ||
352 | struct v4l2_ctrl *sharpness; | ||
353 | struct v4l2_ctrl *zoom; | ||
354 | struct v4l2_ctrl *wdr; | ||
355 | struct v4l2_ctrl *stabilization; | ||
356 | struct v4l2_ctrl *jpeg_quality; | ||
357 | struct v4l2_ctrl *scene_mode; | ||
358 | }; | ||
359 | |||
360 | enum s5c73m3_gpio_id { | ||
361 | STBY, | ||
362 | RST, | ||
363 | GPIO_NUM, | ||
364 | }; | ||
365 | |||
366 | enum s5c73m3_resolution_types { | ||
367 | RES_ISP, | ||
368 | RES_JPEG, | ||
369 | }; | ||
370 | |||
371 | struct s5c73m3_interval { | ||
372 | u16 fps_reg; | ||
373 | struct v4l2_fract interval; | ||
374 | /* Maximum rectangle for the interval */ | ||
375 | struct v4l2_frmsize_discrete size; | ||
376 | }; | ||
377 | |||
378 | struct s5c73m3 { | ||
379 | struct v4l2_subdev sensor_sd; | ||
380 | struct media_pad sensor_pads[S5C73M3_NUM_PADS]; | ||
381 | |||
382 | struct v4l2_subdev oif_sd; | ||
383 | struct media_pad oif_pads[OIF_NUM_PADS]; | ||
384 | |||
385 | struct spi_driver spidrv; | ||
386 | struct spi_device *spi_dev; | ||
387 | struct i2c_client *i2c_client; | ||
388 | u32 i2c_write_address; | ||
389 | u32 i2c_read_address; | ||
390 | |||
391 | struct regulator_bulk_data supplies[S5C73M3_MAX_SUPPLIES]; | ||
392 | struct s5c73m3_gpio gpio[GPIO_NUM]; | ||
393 | |||
394 | /* External master clock frequency */ | ||
395 | u32 mclk_frequency; | ||
396 | /* Video bus type - MIPI-CSI2/paralell */ | ||
397 | enum v4l2_mbus_type bus_type; | ||
398 | |||
399 | const struct s5c73m3_frame_size *sensor_pix_size[2]; | ||
400 | const struct s5c73m3_frame_size *oif_pix_size[2]; | ||
401 | enum v4l2_mbus_pixelcode mbus_code; | ||
402 | |||
403 | const struct s5c73m3_interval *fiv; | ||
404 | |||
405 | struct v4l2_mbus_frame_desc frame_desc; | ||
406 | /* protects the struct members below */ | ||
407 | struct mutex lock; | ||
408 | |||
409 | struct s5c73m3_ctrls ctrls; | ||
410 | |||
411 | u8 streaming:1; | ||
412 | u8 apply_fmt:1; | ||
413 | u8 apply_fiv:1; | ||
414 | u8 isp_ready:1; | ||
415 | |||
416 | short power; | ||
417 | |||
418 | char sensor_fw[S5C73M3_SENSOR_FW_LEN + 2]; | ||
419 | char sensor_type[S5C73M3_SENSOR_TYPE_LEN + 2]; | ||
420 | char fw_file_version[2]; | ||
421 | unsigned int fw_size; | ||
422 | }; | ||
423 | |||
424 | struct s5c73m3_frame_size { | ||
425 | u32 width; | ||
426 | u32 height; | ||
427 | u8 reg_val; | ||
428 | }; | ||
429 | |||
430 | extern int s5c73m3_dbg; | ||
431 | |||
432 | int s5c73m3_register_spi_driver(struct s5c73m3 *state); | ||
433 | void s5c73m3_unregister_spi_driver(struct s5c73m3 *state); | ||
434 | int s5c73m3_spi_write(struct s5c73m3 *state, const void *addr, | ||
435 | const unsigned int len, const unsigned int tx_size); | ||
436 | int s5c73m3_spi_read(struct s5c73m3 *state, void *addr, | ||
437 | const unsigned int len, const unsigned int tx_size); | ||
438 | |||
439 | int s5c73m3_read(struct s5c73m3 *state, u32 addr, u16 *data); | ||
440 | int s5c73m3_write(struct s5c73m3 *state, u32 addr, u16 data); | ||
441 | int s5c73m3_isp_command(struct s5c73m3 *state, u16 command, u16 data); | ||
442 | int s5c73m3_init_controls(struct s5c73m3 *state); | ||
443 | |||
444 | static inline struct v4l2_subdev *ctrl_to_sensor_sd(struct v4l2_ctrl *ctrl) | ||
445 | { | ||
446 | return &container_of(ctrl->handler, struct s5c73m3, | ||
447 | ctrls.handler)->sensor_sd; | ||
448 | } | ||
449 | |||
450 | static inline struct s5c73m3 *sensor_sd_to_s5c73m3(struct v4l2_subdev *sd) | ||
451 | { | ||
452 | return container_of(sd, struct s5c73m3, sensor_sd); | ||
453 | } | ||
454 | |||
455 | static inline struct s5c73m3 *oif_sd_to_s5c73m3(struct v4l2_subdev *sd) | ||
456 | { | ||
457 | return container_of(sd, struct s5c73m3, oif_sd); | ||
458 | } | ||
459 | #endif /* S5C73M3_H_ */ | ||