aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/i2c/s5k4ecgx.c
diff options
context:
space:
mode:
authorSangwook Lee <sangwook.lee@linaro.org>2012-09-13 06:02:14 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-09-26 16:32:54 -0400
commit8b99312b7214f1976ebb54071b01968536af53c9 (patch)
tree8e909259c63d82d63f21189b0b076e74240ad951 /drivers/media/i2c/s5k4ecgx.c
parent4703d356e7890764c05328cc543d6507f132dfe6 (diff)
[media] Add v4l2 subdev driver for S5K4ECGX sensor
This patch adds driver for Samsung S5K4ECGX image sensor with an embedded SoC ISP. The driver only implements preview operation mode and still capture (snapshot) and face detection features are missing now. Following controls are supported: contrast, saturation, brightness, sharpness. Signed-off-by: Sangwook Lee <sangwook.lee@linaro.org> Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/i2c/s5k4ecgx.c')
-rw-r--r--drivers/media/i2c/s5k4ecgx.c1036
1 files changed, 1036 insertions, 0 deletions
diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c
new file mode 100644
index 000000000000..49c1b3abb425
--- /dev/null
+++ b/drivers/media/i2c/s5k4ecgx.c
@@ -0,0 +1,1036 @@
1/*
2 * Driver for Samsung S5K4ECGX 1/4" 5Mp CMOS Image Sensor SoC
3 * with an Embedded Image Signal Processor.
4 *
5 * Copyright (C) 2012, Linaro, Sangwook Lee <sangwook.lee@linaro.org>
6 * Copyright (C) 2012, Insignal Co,. Ltd, Homin Lee <suapapa@insignal.co.kr>
7 *
8 * Based on s5k6aa and noon010pc30 driver
9 * Copyright (C) 2011, Samsung Electronics Co., Ltd.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 */
16
17#include <linux/clk.h>
18#include <linux/crc32.h>
19#include <linux/ctype.h>
20#include <linux/delay.h>
21#include <linux/firmware.h>
22#include <linux/gpio.h>
23#include <linux/i2c.h>
24#include <linux/module.h>
25#include <linux/regulator/consumer.h>
26#include <linux/slab.h>
27#include <asm/unaligned.h>
28
29#include <media/media-entity.h>
30#include <media/s5k4ecgx.h>
31#include <media/v4l2-ctrls.h>
32#include <media/v4l2-device.h>
33#include <media/v4l2-mediabus.h>
34#include <media/v4l2-subdev.h>
35
36static int debug;
37module_param(debug, int, 0644);
38
39#define S5K4ECGX_DRIVER_NAME "s5k4ecgx"
40#define S5K4ECGX_FIRMWARE "s5k4ecgx.bin"
41
42/* Firmware revision information */
43#define REG_FW_REVISION 0x700001a6
44#define REG_FW_VERSION 0x700001a4
45#define S5K4ECGX_REVISION_1_1 0x11
46#define S5K4ECGX_FW_VERSION 0x4ec0
47
48/* General purpose parameters */
49#define REG_USER_BRIGHTNESS 0x7000022c
50#define REG_USER_CONTRAST 0x7000022e
51#define REG_USER_SATURATION 0x70000230
52
53#define REG_G_ENABLE_PREV 0x7000023e
54#define REG_G_ENABLE_PREV_CHG 0x70000240
55#define REG_G_NEW_CFG_SYNC 0x7000024a
56#define REG_G_PREV_IN_WIDTH 0x70000250
57#define REG_G_PREV_IN_HEIGHT 0x70000252
58#define REG_G_PREV_IN_XOFFS 0x70000254
59#define REG_G_PREV_IN_YOFFS 0x70000256
60#define REG_G_CAP_IN_WIDTH 0x70000258
61#define REG_G_CAP_IN_HEIGHT 0x7000025a
62#define REG_G_CAP_IN_XOFFS 0x7000025c
63#define REG_G_CAP_IN_YOFFS 0x7000025e
64#define REG_G_INPUTS_CHANGE_REQ 0x70000262
65#define REG_G_ACTIVE_PREV_CFG 0x70000266
66#define REG_G_PREV_CFG_CHG 0x70000268
67#define REG_G_PREV_OPEN_AFTER_CH 0x7000026a
68
69/* Preview context register sets. n = 0...4. */
70#define PREG(n, x) ((n) * 0x30 + (x))
71#define REG_P_OUT_WIDTH(n) PREG(n, 0x700002a6)
72#define REG_P_OUT_HEIGHT(n) PREG(n, 0x700002a8)
73#define REG_P_FMT(n) PREG(n, 0x700002aa)
74#define REG_P_PVI_MASK(n) PREG(n, 0x700002b4)
75#define REG_P_FR_TIME_TYPE(n) PREG(n, 0x700002be)
76#define FR_TIME_DYNAMIC 0
77#define FR_TIME_FIXED 1
78#define FR_TIME_FIXED_ACCURATE 2
79#define REG_P_FR_TIME_Q_TYPE(n) PREG(n, 0x700002c0)
80#define FR_TIME_Q_DYNAMIC 0
81#define FR_TIME_Q_BEST_FRRATE 1
82#define FR_TIME_Q_BEST_QUALITY 2
83
84/* Frame period in 0.1 ms units */
85#define REG_P_MAX_FR_TIME(n) PREG(n, 0x700002c2)
86#define REG_P_MIN_FR_TIME(n) PREG(n, 0x700002c4)
87#define US_TO_FR_TIME(__t) ((__t) / 100)
88#define REG_P_PREV_MIRROR(n) PREG(n, 0x700002d0)
89#define REG_P_CAP_MIRROR(n) PREG(n, 0x700002d2)
90
91#define REG_G_PREVZOOM_IN_WIDTH 0x70000494
92#define REG_G_PREVZOOM_IN_HEIGHT 0x70000496
93#define REG_G_PREVZOOM_IN_XOFFS 0x70000498
94#define REG_G_PREVZOOM_IN_YOFFS 0x7000049a
95#define REG_G_CAPZOOM_IN_WIDTH 0x7000049c
96#define REG_G_CAPZOOM_IN_HEIGHT 0x7000049e
97#define REG_G_CAPZOOM_IN_XOFFS 0x700004a0
98#define REG_G_CAPZOOM_IN_YOFFS 0x700004a2
99
100/* n = 0...4 */
101#define REG_USER_SHARPNESS(n) (0x70000a28 + (n) * 0xb6)
102
103/* Reduce sharpness range for user space API */
104#define SHARPNESS_DIV 8208
105#define TOK_TERM 0xffffffff
106
107/*
108 * FIXME: This is copied from s5k6aa, because of no information
109 * in the S5K4ECGX datasheet.
110 * H/W register Interface (0xd0000000 - 0xd0000fff)
111 */
112#define AHB_MSB_ADDR_PTR 0xfcfc
113#define GEN_REG_OFFSH 0xd000
114#define REG_CMDWR_ADDRH 0x0028
115#define REG_CMDWR_ADDRL 0x002a
116#define REG_CMDRD_ADDRH 0x002c
117#define REG_CMDRD_ADDRL 0x002e
118#define REG_CMDBUF0_ADDR 0x0f12
119
120struct s5k4ecgx_frmsize {
121 struct v4l2_frmsize_discrete size;
122 /* Fixed sensor matrix crop rectangle */
123 struct v4l2_rect input_window;
124};
125
126struct regval_list {
127 u32 addr;
128 u16 val;
129};
130
131/*
132 * TODO: currently only preview is supported and snapshot (capture)
133 * is not implemented yet
134 */
135static const struct s5k4ecgx_frmsize s5k4ecgx_prev_sizes[] = {
136 {
137 .size = { 176, 144 },
138 .input_window = { 0x00, 0x00, 0x928, 0x780 },
139 }, {
140 .size = { 352, 288 },
141 .input_window = { 0x00, 0x00, 0x928, 0x780 },
142 }, {
143 .size = { 640, 480 },
144 .input_window = { 0x00, 0x00, 0xa00, 0x780 },
145 }, {
146 .size = { 720, 480 },
147 .input_window = { 0x00, 0x00, 0xa00, 0x6a8 },
148 }
149};
150
151#define S5K4ECGX_NUM_PREV ARRAY_SIZE(s5k4ecgx_prev_sizes)
152
153struct s5k4ecgx_pixfmt {
154 enum v4l2_mbus_pixelcode code;
155 u32 colorspace;
156 /* REG_TC_PCFG_Format register value */
157 u16 reg_p_format;
158};
159
160/* By default value, output from sensor will be YUV422 0-255 */
161static const struct s5k4ecgx_pixfmt s5k4ecgx_formats[] = {
162 { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 },
163};
164
165static const char * const s5k4ecgx_supply_names[] = {
166 /*
167 * Usually 2.8V is used for analog power (vdda)
168 * and digital IO (vddio, vdddcore)
169 */
170 "vdda",
171 "vddio",
172 "vddcore",
173 "vddreg", /* The internal s5k4ecgx regulator's supply (1.8V) */
174};
175
176#define S5K4ECGX_NUM_SUPPLIES ARRAY_SIZE(s5k4ecgx_supply_names)
177
178enum s5k4ecgx_gpio_id {
179 STBY,
180 RST,
181 GPIO_NUM,
182};
183
184struct s5k4ecgx {
185 struct v4l2_subdev sd;
186 struct media_pad pad;
187 struct v4l2_ctrl_handler handler;
188
189 struct s5k4ecgx_platform_data *pdata;
190 const struct s5k4ecgx_pixfmt *curr_pixfmt;
191 const struct s5k4ecgx_frmsize *curr_frmsize;
192 struct mutex lock;
193 u8 streaming;
194 u8 set_params;
195
196 struct regulator_bulk_data supplies[S5K4ECGX_NUM_SUPPLIES];
197 struct s5k4ecgx_gpio gpio[GPIO_NUM];
198};
199
200static inline struct s5k4ecgx *to_s5k4ecgx(struct v4l2_subdev *sd)
201{
202 return container_of(sd, struct s5k4ecgx, sd);
203}
204
205static int s5k4ecgx_i2c_read(struct i2c_client *client, u16 addr, u16 *val)
206{
207 u8 wbuf[2] = { addr >> 8, addr & 0xff };
208 struct i2c_msg msg[2];
209 u8 rbuf[2];
210 int ret;
211
212 msg[0].addr = client->addr;
213 msg[0].flags = 0;
214 msg[0].len = 2;
215 msg[0].buf = wbuf;
216
217 msg[1].addr = client->addr;
218 msg[1].flags = I2C_M_RD;
219 msg[1].len = 2;
220 msg[1].buf = rbuf;
221
222 ret = i2c_transfer(client->adapter, msg, 2);
223 *val = be16_to_cpu(*((u16 *)rbuf));
224
225 v4l2_dbg(4, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val);
226
227 return ret == 2 ? 0 : ret;
228}
229
230static int s5k4ecgx_i2c_write(struct i2c_client *client, u16 addr, u16 val)
231{
232 u8 buf[4] = { addr >> 8, addr & 0xff, val >> 8, val & 0xff };
233
234 int ret = i2c_master_send(client, buf, 4);
235 v4l2_dbg(4, debug, client, "i2c_write: 0x%04x : 0x%04x\n", addr, val);
236
237 return ret == 4 ? 0 : ret;
238}
239
240static int s5k4ecgx_write(struct i2c_client *client, u32 addr, u16 val)
241{
242 u16 high = addr >> 16, low = addr & 0xffff;
243 int ret;
244
245 v4l2_dbg(3, debug, client, "write: 0x%08x : 0x%04x\n", addr, val);
246
247 ret = s5k4ecgx_i2c_write(client, REG_CMDWR_ADDRH, high);
248 if (!ret)
249 ret = s5k4ecgx_i2c_write(client, REG_CMDWR_ADDRL, low);
250 if (!ret)
251 ret = s5k4ecgx_i2c_write(client, REG_CMDBUF0_ADDR, val);
252
253 return ret;
254}
255
256static int s5k4ecgx_read(struct i2c_client *client, u32 addr, u16 *val)
257{
258 u16 high = addr >> 16, low = addr & 0xffff;
259 int ret;
260
261 ret = s5k4ecgx_i2c_write(client, REG_CMDRD_ADDRH, high);
262 if (!ret)
263 ret = s5k4ecgx_i2c_write(client, REG_CMDRD_ADDRL, low);
264 if (!ret)
265 ret = s5k4ecgx_i2c_read(client, REG_CMDBUF0_ADDR, val);
266 if (!ret)
267 dev_err(&client->dev, "Failed to execute read command\n");
268
269 return ret;
270}
271
272static int s5k4ecgx_read_fw_ver(struct v4l2_subdev *sd)
273{
274 struct i2c_client *client = v4l2_get_subdevdata(sd);
275 u16 hw_rev, fw_ver = 0;
276 int ret;
277
278 ret = s5k4ecgx_read(client, REG_FW_VERSION, &fw_ver);
279 if (ret < 0 || fw_ver != S5K4ECGX_FW_VERSION) {
280 v4l2_err(sd, "FW version check failed!\n");
281 return -ENODEV;
282 }
283
284 ret = s5k4ecgx_read(client, REG_FW_REVISION, &hw_rev);
285 if (ret < 0)
286 return ret;
287
288 v4l2_info(sd, "chip found FW ver: 0x%x, HW rev: 0x%x\n",
289 fw_ver, hw_rev);
290 return 0;
291}
292
293static int s5k4ecgx_set_ahb_address(struct v4l2_subdev *sd)
294{
295 struct i2c_client *client = v4l2_get_subdevdata(sd);
296 int ret;
297
298 /* Set APB peripherals start address */
299 ret = s5k4ecgx_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH);
300 if (ret < 0)
301 return ret;
302 /*
303 * FIXME: This is copied from s5k6aa, because of no information
304 * in s5k4ecgx's datasheet.
305 * sw_reset is activated to put device into idle status
306 */
307 ret = s5k4ecgx_i2c_write(client, 0x0010, 0x0001);
308 if (ret < 0)
309 return ret;
310
311 ret = s5k4ecgx_i2c_write(client, 0x1030, 0x0000);
312 if (ret < 0)
313 return ret;
314 /* Halt ARM CPU */
315 return s5k4ecgx_i2c_write(client, 0x0014, 0x0001);
316}
317
318#define FW_CRC_SIZE 4
319/* Register address, value are 4, 2 bytes */
320#define FW_RECORD_SIZE 6
321/*
322 * The firmware has following format:
323 * < total number of records (4 bytes + 2 bytes padding) N >,
324 * < record 0 >, ..., < record N - 1 >, < CRC32-CCITT (4-bytes) >,
325 * where "record" is a 4-byte register address followed by 2-byte
326 * register value (little endian).
327 * The firmware generator can be found in following git repository:
328 * git://git.linaro.org/people/sangwook/fimc-v4l2-app.git
329 */
330static int s5k4ecgx_load_firmware(struct v4l2_subdev *sd)
331{
332 struct i2c_client *client = v4l2_get_subdevdata(sd);
333 const struct firmware *fw;
334 const u8 *ptr;
335 int err, i, regs_num;
336 u32 addr, crc, crc_file, addr_inc = 0;
337 u16 val;
338
339 err = request_firmware(&fw, S5K4ECGX_FIRMWARE, sd->v4l2_dev->dev);
340 if (err) {
341 v4l2_err(sd, "Failed to read firmware %s\n", S5K4ECGX_FIRMWARE);
342 return err;
343 }
344 regs_num = le32_to_cpu(get_unaligned_le32(fw->data));
345
346 v4l2_dbg(3, debug, sd, "FW: %s size %d register sets %d\n",
347 S5K4ECGX_FIRMWARE, fw->size, regs_num);
348
349 regs_num++; /* Add header */
350 if (fw->size != regs_num * FW_RECORD_SIZE + FW_CRC_SIZE) {
351 err = -EINVAL;
352 goto fw_out;
353 }
354 crc_file = le32_to_cpu(get_unaligned_le32(fw->data +
355 regs_num * FW_RECORD_SIZE));
356 crc = crc32_le(~0, fw->data, regs_num * FW_RECORD_SIZE);
357 if (crc != crc_file) {
358 v4l2_err(sd, "FW: invalid crc (%#x:%#x)\n", crc, crc_file);
359 err = -EINVAL;
360 goto fw_out;
361 }
362 ptr = fw->data + FW_RECORD_SIZE;
363 for (i = 1; i < regs_num; i++) {
364 addr = le32_to_cpu(get_unaligned_le32(ptr));
365 ptr += sizeof(u32);
366 val = le16_to_cpu(get_unaligned_le16(ptr));
367 ptr += sizeof(u16);
368 if (addr - addr_inc != 2)
369 err = s5k4ecgx_write(client, addr, val);
370 else
371 err = s5k4ecgx_i2c_write(client, REG_CMDBUF0_ADDR, val);
372 if (err)
373 break;
374 addr_inc = addr;
375 }
376fw_out:
377 release_firmware(fw);
378 return err;
379}
380
381/* Set preview and capture input window */
382static int s5k4ecgx_set_input_window(struct i2c_client *c,
383 const struct v4l2_rect *r)
384{
385 int ret;
386
387 ret = s5k4ecgx_write(c, REG_G_PREV_IN_WIDTH, r->width);
388 if (!ret)
389 ret = s5k4ecgx_write(c, REG_G_PREV_IN_HEIGHT, r->height);
390 if (!ret)
391 ret = s5k4ecgx_write(c, REG_G_PREV_IN_XOFFS, r->left);
392 if (!ret)
393 ret = s5k4ecgx_write(c, REG_G_PREV_IN_YOFFS, r->top);
394 if (!ret)
395 ret = s5k4ecgx_write(c, REG_G_CAP_IN_WIDTH, r->width);
396 if (!ret)
397 ret = s5k4ecgx_write(c, REG_G_CAP_IN_HEIGHT, r->height);
398 if (!ret)
399 ret = s5k4ecgx_write(c, REG_G_CAP_IN_XOFFS, r->left);
400 if (!ret)
401 ret = s5k4ecgx_write(c, REG_G_CAP_IN_YOFFS, r->top);
402
403 return ret;
404}
405
406/* Set preview and capture zoom input window */
407static int s5k4ecgx_set_zoom_window(struct i2c_client *c,
408 const struct v4l2_rect *r)
409{
410 int ret;
411
412 ret = s5k4ecgx_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width);
413 if (!ret)
414 ret = s5k4ecgx_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height);
415 if (!ret)
416 ret = s5k4ecgx_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left);
417 if (!ret)
418 ret = s5k4ecgx_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top);
419 if (!ret)
420 ret = s5k4ecgx_write(c, REG_G_CAPZOOM_IN_WIDTH, r->width);
421 if (!ret)
422 ret = s5k4ecgx_write(c, REG_G_CAPZOOM_IN_HEIGHT, r->height);
423 if (!ret)
424 ret = s5k4ecgx_write(c, REG_G_CAPZOOM_IN_XOFFS, r->left);
425 if (!ret)
426 ret = s5k4ecgx_write(c, REG_G_CAPZOOM_IN_YOFFS, r->top);
427
428 return ret;
429}
430
431static int s5k4ecgx_set_output_framefmt(struct s5k4ecgx *priv)
432{
433 struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
434 int ret;
435
436 ret = s5k4ecgx_write(client, REG_P_OUT_WIDTH(0),
437 priv->curr_frmsize->size.width);
438 if (!ret)
439 ret = s5k4ecgx_write(client, REG_P_OUT_HEIGHT(0),
440 priv->curr_frmsize->size.height);
441 if (!ret)
442 ret = s5k4ecgx_write(client, REG_P_FMT(0),
443 priv->curr_pixfmt->reg_p_format);
444 return ret;
445}
446
447static int s5k4ecgx_init_sensor(struct v4l2_subdev *sd)
448{
449 int ret;
450
451 ret = s5k4ecgx_set_ahb_address(sd);
452
453 /* The delay is from manufacturer's settings */
454 msleep(100);
455
456 if (!ret)
457 ret = s5k4ecgx_load_firmware(sd);
458 if (ret)
459 v4l2_err(sd, "Failed to write initial settings\n");
460
461 return ret;
462}
463
464static int s5k4ecgx_gpio_set_value(struct s5k4ecgx *priv, int id, u32 val)
465{
466 if (!gpio_is_valid(priv->gpio[id].gpio))
467 return 0;
468 gpio_set_value(priv->gpio[id].gpio, val);
469
470 return 1;
471}
472
473static int __s5k4ecgx_power_on(struct s5k4ecgx *priv)
474{
475 int ret;
476
477 ret = regulator_bulk_enable(S5K4ECGX_NUM_SUPPLIES, priv->supplies);
478 if (ret)
479 return ret;
480 usleep_range(30, 50);
481
482 /* The polarity of STBY is controlled by TSP */
483 if (s5k4ecgx_gpio_set_value(priv, STBY, priv->gpio[STBY].level))
484 usleep_range(30, 50);
485
486 if (s5k4ecgx_gpio_set_value(priv, RST, priv->gpio[RST].level))
487 usleep_range(30, 50);
488
489 return 0;
490}
491
492static int __s5k4ecgx_power_off(struct s5k4ecgx *priv)
493{
494 if (s5k4ecgx_gpio_set_value(priv, RST, !priv->gpio[RST].level))
495 usleep_range(30, 50);
496
497 if (s5k4ecgx_gpio_set_value(priv, STBY, !priv->gpio[STBY].level))
498 usleep_range(30, 50);
499
500 priv->streaming = 0;
501
502 return regulator_bulk_disable(S5K4ECGX_NUM_SUPPLIES, priv->supplies);
503}
504
505/* Find nearest matching image pixel size. */
506static int s5k4ecgx_try_frame_size(struct v4l2_mbus_framefmt *mf,
507 const struct s5k4ecgx_frmsize **size)
508{
509 unsigned int min_err = ~0;
510 int i = ARRAY_SIZE(s5k4ecgx_prev_sizes);
511 const struct s5k4ecgx_frmsize *fsize = &s5k4ecgx_prev_sizes[0],
512 *match = NULL;
513
514 while (i--) {
515 int err = abs(fsize->size.width - mf->width)
516 + abs(fsize->size.height - mf->height);
517 if (err < min_err) {
518 min_err = err;
519 match = fsize;
520 }
521 fsize++;
522 }
523 if (match) {
524 mf->width = match->size.width;
525 mf->height = match->size.height;
526 if (size)
527 *size = match;
528 return 0;
529 }
530
531 return -EINVAL;
532}
533
534static int s5k4ecgx_enum_mbus_code(struct v4l2_subdev *sd,
535 struct v4l2_subdev_fh *fh,
536 struct v4l2_subdev_mbus_code_enum *code)
537{
538 if (code->index >= ARRAY_SIZE(s5k4ecgx_formats))
539 return -EINVAL;
540 code->code = s5k4ecgx_formats[code->index].code;
541
542 return 0;
543}
544
545static int s5k4ecgx_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
546 struct v4l2_subdev_format *fmt)
547{
548 struct s5k4ecgx *priv = to_s5k4ecgx(sd);
549 struct v4l2_mbus_framefmt *mf;
550
551 if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
552 if (fh) {
553 mf = v4l2_subdev_get_try_format(fh, 0);
554 fmt->format = *mf;
555 }
556 return 0;
557 }
558
559 mf = &fmt->format;
560
561 mutex_lock(&priv->lock);
562 mf->width = priv->curr_frmsize->size.width;
563 mf->height = priv->curr_frmsize->size.height;
564 mf->code = priv->curr_pixfmt->code;
565 mf->colorspace = priv->curr_pixfmt->colorspace;
566 mf->field = V4L2_FIELD_NONE;
567 mutex_unlock(&priv->lock);
568
569 return 0;
570}
571
572static const struct s5k4ecgx_pixfmt *s5k4ecgx_try_fmt(struct v4l2_subdev *sd,
573 struct v4l2_mbus_framefmt *mf)
574{
575 int i = ARRAY_SIZE(s5k4ecgx_formats);
576
577 while (--i)
578 if (mf->code == s5k4ecgx_formats[i].code)
579 break;
580 mf->code = s5k4ecgx_formats[i].code;
581
582 return &s5k4ecgx_formats[i];
583}
584
585static int s5k4ecgx_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
586 struct v4l2_subdev_format *fmt)
587{
588 struct s5k4ecgx *priv = to_s5k4ecgx(sd);
589 const struct s5k4ecgx_frmsize *fsize = NULL;
590 const struct s5k4ecgx_pixfmt *pf;
591 struct v4l2_mbus_framefmt *mf;
592 int ret = 0;
593
594 pf = s5k4ecgx_try_fmt(sd, &fmt->format);
595 s5k4ecgx_try_frame_size(&fmt->format, &fsize);
596 fmt->format.colorspace = V4L2_COLORSPACE_JPEG;
597
598 if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
599 if (fh) {
600 mf = v4l2_subdev_get_try_format(fh, 0);
601 *mf = fmt->format;
602 }
603 return 0;
604 }
605
606 mutex_lock(&priv->lock);
607 if (!priv->streaming) {
608 priv->curr_frmsize = fsize;
609 priv->curr_pixfmt = pf;
610 priv->set_params = 1;
611 } else {
612 ret = -EBUSY;
613 }
614 mutex_unlock(&priv->lock);
615
616 return ret;
617}
618
619static const struct v4l2_subdev_pad_ops s5k4ecgx_pad_ops = {
620 .enum_mbus_code = s5k4ecgx_enum_mbus_code,
621 .get_fmt = s5k4ecgx_get_fmt,
622 .set_fmt = s5k4ecgx_set_fmt,
623};
624
625/*
626 * V4L2 subdev controls
627 */
628static int s5k4ecgx_s_ctrl(struct v4l2_ctrl *ctrl)
629{
630 struct v4l2_subdev *sd = &container_of(ctrl->handler, struct s5k4ecgx,
631 handler)->sd;
632 struct i2c_client *client = v4l2_get_subdevdata(sd);
633 struct s5k4ecgx *priv = to_s5k4ecgx(sd);
634 unsigned int i;
635 int err = 0;
636
637 v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val);
638
639 mutex_lock(&priv->lock);
640 switch (ctrl->id) {
641 case V4L2_CID_CONTRAST:
642 err = s5k4ecgx_write(client, REG_USER_CONTRAST, ctrl->val);
643 break;
644
645 case V4L2_CID_SATURATION:
646 err = s5k4ecgx_write(client, REG_USER_SATURATION, ctrl->val);
647 break;
648
649 case V4L2_CID_SHARPNESS:
650 /* TODO: Revisit, is this setting for all presets ? */
651 for (i = 0; i < 4 && !err; i++)
652 err = s5k4ecgx_write(client, REG_USER_SHARPNESS(i),
653 ctrl->val * SHARPNESS_DIV);
654 break;
655
656 case V4L2_CID_BRIGHTNESS:
657 err = s5k4ecgx_write(client, REG_USER_BRIGHTNESS, ctrl->val);
658 break;
659 }
660 mutex_unlock(&priv->lock);
661 if (err < 0)
662 v4l2_err(sd, "Failed to write s_ctrl err %d\n", err);
663
664 return err;
665}
666
667static const struct v4l2_ctrl_ops s5k4ecgx_ctrl_ops = {
668 .s_ctrl = s5k4ecgx_s_ctrl,
669};
670
671/*
672 * Reading s5k4ecgx version information
673 */
674static int s5k4ecgx_registered(struct v4l2_subdev *sd)
675{
676 int ret;
677 struct s5k4ecgx *priv = to_s5k4ecgx(sd);
678
679 mutex_lock(&priv->lock);
680 ret = __s5k4ecgx_power_on(priv);
681 if (!ret) {
682 ret = s5k4ecgx_read_fw_ver(sd);
683 __s5k4ecgx_power_off(priv);
684 }
685 mutex_unlock(&priv->lock);
686
687 return ret;
688}
689
690/*
691 * V4L2 subdev internal operations
692 */
693static int s5k4ecgx_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
694{
695 struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0);
696
697 mf->width = s5k4ecgx_prev_sizes[0].size.width;
698 mf->height = s5k4ecgx_prev_sizes[0].size.height;
699 mf->code = s5k4ecgx_formats[0].code;
700 mf->colorspace = V4L2_COLORSPACE_JPEG;
701 mf->field = V4L2_FIELD_NONE;
702
703 return 0;
704}
705
706static const struct v4l2_subdev_internal_ops s5k4ecgx_subdev_internal_ops = {
707 .registered = s5k4ecgx_registered,
708 .open = s5k4ecgx_open,
709};
710
711static int s5k4ecgx_s_power(struct v4l2_subdev *sd, int on)
712{
713 struct s5k4ecgx *priv = to_s5k4ecgx(sd);
714 int ret;
715
716 v4l2_dbg(1, debug, sd, "Switching %s\n", on ? "on" : "off");
717
718 if (on) {
719 ret = __s5k4ecgx_power_on(priv);
720 if (ret < 0)
721 return ret;
722 /* Time to stabilize sensor */
723 msleep(100);
724 ret = s5k4ecgx_init_sensor(sd);
725 if (ret < 0)
726 __s5k4ecgx_power_off(priv);
727 else
728 priv->set_params = 1;
729 } else {
730 ret = __s5k4ecgx_power_off(priv);
731 }
732
733 return ret;
734}
735
736static int s5k4ecgx_log_status(struct v4l2_subdev *sd)
737{
738 v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name);
739
740 return 0;
741}
742
743static const struct v4l2_subdev_core_ops s5k4ecgx_core_ops = {
744 .s_power = s5k4ecgx_s_power,
745 .log_status = s5k4ecgx_log_status,
746};
747
748static int __s5k4ecgx_s_params(struct s5k4ecgx *priv)
749{
750 struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
751 const struct v4l2_rect *crop_rect = &priv->curr_frmsize->input_window;
752 int ret;
753
754 ret = s5k4ecgx_set_input_window(client, crop_rect);
755 if (!ret)
756 ret = s5k4ecgx_set_zoom_window(client, crop_rect);
757 if (!ret)
758 ret = s5k4ecgx_write(client, REG_G_INPUTS_CHANGE_REQ, 1);
759 if (!ret)
760 ret = s5k4ecgx_write(client, 0x70000a1e, 0x28);
761 if (!ret)
762 ret = s5k4ecgx_write(client, 0x70000ad4, 0x3c);
763 if (!ret)
764 ret = s5k4ecgx_set_output_framefmt(priv);
765 if (!ret)
766 ret = s5k4ecgx_write(client, REG_P_PVI_MASK(0), 0x52);
767 if (!ret)
768 ret = s5k4ecgx_write(client, REG_P_FR_TIME_TYPE(0),
769 FR_TIME_DYNAMIC);
770 if (!ret)
771 ret = s5k4ecgx_write(client, REG_P_FR_TIME_Q_TYPE(0),
772 FR_TIME_Q_BEST_FRRATE);
773 if (!ret)
774 ret = s5k4ecgx_write(client, REG_P_MIN_FR_TIME(0),
775 US_TO_FR_TIME(33300));
776 if (!ret)
777 ret = s5k4ecgx_write(client, REG_P_MAX_FR_TIME(0),
778 US_TO_FR_TIME(66600));
779 if (!ret)
780 ret = s5k4ecgx_write(client, REG_P_PREV_MIRROR(0), 0);
781 if (!ret)
782 ret = s5k4ecgx_write(client, REG_P_CAP_MIRROR(0), 0);
783 if (!ret)
784 ret = s5k4ecgx_write(client, REG_G_ACTIVE_PREV_CFG, 0);
785 if (!ret)
786 ret = s5k4ecgx_write(client, REG_G_PREV_OPEN_AFTER_CH, 1);
787 if (!ret)
788 ret = s5k4ecgx_write(client, REG_G_NEW_CFG_SYNC, 1);
789 if (!ret)
790 ret = s5k4ecgx_write(client, REG_G_PREV_CFG_CHG, 1);
791
792 return ret;
793}
794
795static int __s5k4ecgx_s_stream(struct s5k4ecgx *priv, int on)
796{
797 struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
798 int ret;
799
800 if (on && priv->set_params) {
801 ret = __s5k4ecgx_s_params(priv);
802 if (ret < 0)
803 return ret;
804 priv->set_params = 0;
805 }
806 /*
807 * This enables/disables preview stream only. Capture requests
808 * are not supported yet.
809 */
810 ret = s5k4ecgx_write(client, REG_G_ENABLE_PREV, on);
811 if (ret < 0)
812 return ret;
813 return s5k4ecgx_write(client, REG_G_ENABLE_PREV_CHG, 1);
814}
815
816static int s5k4ecgx_s_stream(struct v4l2_subdev *sd, int on)
817{
818 struct s5k4ecgx *priv = to_s5k4ecgx(sd);
819 int ret = 0;
820
821 v4l2_dbg(1, debug, sd, "Turn streaming %s\n", on ? "on" : "off");
822
823 mutex_lock(&priv->lock);
824
825 if (priv->streaming == !on) {
826 ret = __s5k4ecgx_s_stream(priv, on);
827 if (!ret)
828 priv->streaming = on & 1;
829 }
830
831 mutex_unlock(&priv->lock);
832 return ret;
833}
834
835static const struct v4l2_subdev_video_ops s5k4ecgx_video_ops = {
836 .s_stream = s5k4ecgx_s_stream,
837};
838
839static const struct v4l2_subdev_ops s5k4ecgx_ops = {
840 .core = &s5k4ecgx_core_ops,
841 .pad = &s5k4ecgx_pad_ops,
842 .video = &s5k4ecgx_video_ops,
843};
844
845/*
846 * GPIO setup
847 */
848static int s5k4ecgx_config_gpio(int nr, int val, const char *name)
849{
850 unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
851 int ret;
852
853 if (!gpio_is_valid(nr))
854 return 0;
855 ret = gpio_request_one(nr, flags, name);
856 if (!ret)
857 gpio_export(nr, 0);
858
859 return ret;
860}
861
862static void s5k4ecgx_free_gpios(struct s5k4ecgx *priv)
863{
864 int i;
865
866 for (i = 0; i < ARRAY_SIZE(priv->gpio); i++) {
867 if (!gpio_is_valid(priv->gpio[i].gpio))
868 continue;
869 gpio_free(priv->gpio[i].gpio);
870 priv->gpio[i].gpio = -EINVAL;
871 }
872}
873
874static int s5k4ecgx_config_gpios(struct s5k4ecgx *priv,
875 const struct s5k4ecgx_platform_data *pdata)
876{
877 const struct s5k4ecgx_gpio *gpio = &pdata->gpio_stby;
878 int ret;
879
880 priv->gpio[STBY].gpio = -EINVAL;
881 priv->gpio[RST].gpio = -EINVAL;
882
883 ret = s5k4ecgx_config_gpio(gpio->gpio, gpio->level, "S5K4ECGX_STBY");
884
885 if (ret) {
886 s5k4ecgx_free_gpios(priv);
887 return ret;
888 }
889 priv->gpio[STBY] = *gpio;
890 if (gpio_is_valid(gpio->gpio))
891 gpio_set_value(gpio->gpio, 0);
892
893 gpio = &pdata->gpio_reset;
894
895 ret = s5k4ecgx_config_gpio(gpio->gpio, gpio->level, "S5K4ECGX_RST");
896 if (ret) {
897 s5k4ecgx_free_gpios(priv);
898 return ret;
899 }
900 priv->gpio[RST] = *gpio;
901 if (gpio_is_valid(gpio->gpio))
902 gpio_set_value(gpio->gpio, 0);
903
904 return 0;
905}
906
907static int s5k4ecgx_init_v4l2_ctrls(struct s5k4ecgx *priv)
908{
909 const struct v4l2_ctrl_ops *ops = &s5k4ecgx_ctrl_ops;
910 struct v4l2_ctrl_handler *hdl = &priv->handler;
911 int ret;
912
913 ret = v4l2_ctrl_handler_init(hdl, 4);
914 if (ret)
915 return ret;
916
917 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -208, 127, 1, 0);
918 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0);
919 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0);
920
921 /* Sharpness default is 24612, and then (24612/SHARPNESS_DIV) = 2 */
922 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -32704/SHARPNESS_DIV,
923 24612/SHARPNESS_DIV, 1, 2);
924 if (hdl->error) {
925 ret = hdl->error;
926 v4l2_ctrl_handler_free(hdl);
927 return ret;
928 }
929 priv->sd.ctrl_handler = hdl;
930
931 return 0;
932};
933
934static int s5k4ecgx_probe(struct i2c_client *client,
935 const struct i2c_device_id *id)
936{
937 struct s5k4ecgx_platform_data *pdata = client->dev.platform_data;
938 struct v4l2_subdev *sd;
939 struct s5k4ecgx *priv;
940 int ret, i;
941
942 if (pdata == NULL) {
943 dev_err(&client->dev, "platform data is missing!\n");
944 return -EINVAL;
945 }
946
947 priv = devm_kzalloc(&client->dev, sizeof(struct s5k4ecgx), GFP_KERNEL);
948 if (!priv)
949 return -ENOMEM;
950
951 mutex_init(&priv->lock);
952 priv->streaming = 0;
953
954 sd = &priv->sd;
955 /* Registering subdev */
956 v4l2_i2c_subdev_init(sd, client, &s5k4ecgx_ops);
957 strlcpy(sd->name, S5K4ECGX_DRIVER_NAME, sizeof(sd->name));
958
959 sd->internal_ops = &s5k4ecgx_subdev_internal_ops;
960 /* Support v4l2 sub-device user space API */
961 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
962
963 priv->pad.flags = MEDIA_PAD_FL_SOURCE;
964 sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
965 ret = media_entity_init(&sd->entity, 1, &priv->pad, 0);
966 if (ret)
967 return ret;
968
969 ret = s5k4ecgx_config_gpios(priv, pdata);
970 if (ret) {
971 dev_err(&client->dev, "Failed to set gpios\n");
972 goto out_err1;
973 }
974 for (i = 0; i < S5K4ECGX_NUM_SUPPLIES; i++)
975 priv->supplies[i].supply = s5k4ecgx_supply_names[i];
976
977 ret = devm_regulator_bulk_get(&client->dev, S5K4ECGX_NUM_SUPPLIES,
978 priv->supplies);
979 if (ret) {
980 dev_err(&client->dev, "Failed to get regulators\n");
981 goto out_err2;
982 }
983 ret = s5k4ecgx_init_v4l2_ctrls(priv);
984 if (ret)
985 goto out_err2;
986
987 priv->curr_pixfmt = &s5k4ecgx_formats[0];
988 priv->curr_frmsize = &s5k4ecgx_prev_sizes[0];
989
990 return 0;
991
992out_err2:
993 s5k4ecgx_free_gpios(priv);
994out_err1:
995 media_entity_cleanup(&priv->sd.entity);
996
997 return ret;
998}
999
1000static int s5k4ecgx_remove(struct i2c_client *client)
1001{
1002 struct v4l2_subdev *sd = i2c_get_clientdata(client);
1003 struct s5k4ecgx *priv = to_s5k4ecgx(sd);
1004
1005 mutex_destroy(&priv->lock);
1006 s5k4ecgx_free_gpios(priv);
1007 v4l2_device_unregister_subdev(sd);
1008 v4l2_ctrl_handler_free(&priv->handler);
1009 media_entity_cleanup(&sd->entity);
1010
1011 return 0;
1012}
1013
1014static const struct i2c_device_id s5k4ecgx_id[] = {
1015 { S5K4ECGX_DRIVER_NAME, 0 },
1016 {}
1017};
1018MODULE_DEVICE_TABLE(i2c, s5k4ecgx_id);
1019
1020static struct i2c_driver v4l2_i2c_driver = {
1021 .driver = {
1022 .owner = THIS_MODULE,
1023 .name = S5K4ECGX_DRIVER_NAME,
1024 },
1025 .probe = s5k4ecgx_probe,
1026 .remove = s5k4ecgx_remove,
1027 .id_table = s5k4ecgx_id,
1028};
1029
1030module_i2c_driver(v4l2_i2c_driver);
1031
1032MODULE_DESCRIPTION("Samsung S5K4ECGX 5MP SOC camera");
1033MODULE_AUTHOR("Sangwook Lee <sangwook.lee@linaro.org>");
1034MODULE_AUTHOR("Seok-Young Jang <quartz.jang@samsung.com>");
1035MODULE_LICENSE("GPL");
1036MODULE_FIRMWARE(S5K4ECGX_FIRMWARE);