2 * Driver for Samsung S5K4ECGX 1/4" 5Mp CMOS Image Sensor SoC
3 * with an Embedded Image Signal Processor.
5 * Copyright (C) 2012, Linaro, Sangwook Lee <sangwook.lee@linaro.org>
6 * Copyright (C) 2012, Insignal Co,. Ltd, Homin Lee <suapapa@insignal.co.kr>
8 * Based on s5k6aa and noon010pc30 driver
9 * Copyright (C) 2011, Samsung Electronics Co., Ltd.
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.
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>
29 #include <media/media-entity.h>
30 #include <media/i2c/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>
37 module_param(debug
, int, 0644);
39 #define S5K4ECGX_DRIVER_NAME "s5k4ecgx"
40 #define S5K4ECGX_FIRMWARE "s5k4ecgx.bin"
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
48 /* General purpose parameters */
49 #define REG_USER_BRIGHTNESS 0x7000022c
50 #define REG_USER_CONTRAST 0x7000022e
51 #define REG_USER_SATURATION 0x70000230
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
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
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)
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
101 #define REG_USER_SHARPNESS(n) (0x70000a28 + (n) * 0xb6)
103 /* Reduce sharpness range for user space API */
104 #define SHARPNESS_DIV 8208
105 #define TOK_TERM 0xffffffff
108 * FIXME: This is copied from s5k6aa, because of no information
109 * in the S5K4ECGX datasheet.
110 * H/W register Interface (0xd0000000 - 0xd0000fff)
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
120 struct s5k4ecgx_frmsize
{
121 struct v4l2_frmsize_discrete size
;
122 /* Fixed sensor matrix crop rectangle */
123 struct v4l2_rect input_window
;
132 * TODO: currently only preview is supported and snapshot (capture)
133 * is not implemented yet
135 static const struct s5k4ecgx_frmsize s5k4ecgx_prev_sizes
[] = {
137 .size
= { 176, 144 },
138 .input_window
= { 0x00, 0x00, 0x928, 0x780 },
140 .size
= { 352, 288 },
141 .input_window
= { 0x00, 0x00, 0x928, 0x780 },
143 .size
= { 640, 480 },
144 .input_window
= { 0x00, 0x00, 0xa00, 0x780 },
146 .size
= { 720, 480 },
147 .input_window
= { 0x00, 0x00, 0xa00, 0x6a8 },
151 #define S5K4ECGX_NUM_PREV ARRAY_SIZE(s5k4ecgx_prev_sizes)
153 struct s5k4ecgx_pixfmt
{
156 /* REG_TC_PCFG_Format register value */
160 /* By default value, output from sensor will be YUV422 0-255 */
161 static const struct s5k4ecgx_pixfmt s5k4ecgx_formats
[] = {
162 { MEDIA_BUS_FMT_YUYV8_2X8
, V4L2_COLORSPACE_JPEG
, 5 },
165 static const char * const s5k4ecgx_supply_names
[] = {
167 * Usually 2.8V is used for analog power (vdda)
168 * and digital IO (vddio, vdddcore)
173 "vddreg", /* The internal s5k4ecgx regulator's supply (1.8V) */
176 #define S5K4ECGX_NUM_SUPPLIES ARRAY_SIZE(s5k4ecgx_supply_names)
178 enum s5k4ecgx_gpio_id
{
185 struct v4l2_subdev sd
;
186 struct media_pad pad
;
187 struct v4l2_ctrl_handler handler
;
189 struct s5k4ecgx_platform_data
*pdata
;
190 const struct s5k4ecgx_pixfmt
*curr_pixfmt
;
191 const struct s5k4ecgx_frmsize
*curr_frmsize
;
196 struct regulator_bulk_data supplies
[S5K4ECGX_NUM_SUPPLIES
];
197 struct s5k4ecgx_gpio gpio
[GPIO_NUM
];
200 static inline struct s5k4ecgx
*to_s5k4ecgx(struct v4l2_subdev
*sd
)
202 return container_of(sd
, struct s5k4ecgx
, sd
);
205 static int s5k4ecgx_i2c_read(struct i2c_client
*client
, u16 addr
, u16
*val
)
207 u8 wbuf
[2] = { addr
>> 8, addr
& 0xff };
208 struct i2c_msg msg
[2];
212 msg
[0].addr
= client
->addr
;
217 msg
[1].addr
= client
->addr
;
218 msg
[1].flags
= I2C_M_RD
;
222 ret
= i2c_transfer(client
->adapter
, msg
, 2);
223 *val
= be16_to_cpu(*((__be16
*)rbuf
));
225 v4l2_dbg(4, debug
, client
, "i2c_read: 0x%04X : 0x%04x\n", addr
, *val
);
227 return ret
== 2 ? 0 : ret
;
230 static int s5k4ecgx_i2c_write(struct i2c_client
*client
, u16 addr
, u16 val
)
232 u8 buf
[4] = { addr
>> 8, addr
& 0xff, val
>> 8, val
& 0xff };
234 int ret
= i2c_master_send(client
, buf
, 4);
235 v4l2_dbg(4, debug
, client
, "i2c_write: 0x%04x : 0x%04x\n", addr
, val
);
237 return ret
== 4 ? 0 : ret
;
240 static int s5k4ecgx_write(struct i2c_client
*client
, u32 addr
, u16 val
)
242 u16 high
= addr
>> 16, low
= addr
& 0xffff;
245 v4l2_dbg(3, debug
, client
, "write: 0x%08x : 0x%04x\n", addr
, val
);
247 ret
= s5k4ecgx_i2c_write(client
, REG_CMDWR_ADDRH
, high
);
249 ret
= s5k4ecgx_i2c_write(client
, REG_CMDWR_ADDRL
, low
);
251 ret
= s5k4ecgx_i2c_write(client
, REG_CMDBUF0_ADDR
, val
);
256 static int s5k4ecgx_read(struct i2c_client
*client
, u32 addr
, u16
*val
)
258 u16 high
= addr
>> 16, low
= addr
& 0xffff;
261 ret
= s5k4ecgx_i2c_write(client
, REG_CMDRD_ADDRH
, high
);
263 ret
= s5k4ecgx_i2c_write(client
, REG_CMDRD_ADDRL
, low
);
265 ret
= s5k4ecgx_i2c_read(client
, REG_CMDBUF0_ADDR
, val
);
267 dev_err(&client
->dev
, "Failed to execute read command\n");
272 static int s5k4ecgx_read_fw_ver(struct v4l2_subdev
*sd
)
274 struct i2c_client
*client
= v4l2_get_subdevdata(sd
);
275 u16 hw_rev
, fw_ver
= 0;
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");
284 ret
= s5k4ecgx_read(client
, REG_FW_REVISION
, &hw_rev
);
288 v4l2_info(sd
, "chip found FW ver: 0x%x, HW rev: 0x%x\n",
293 static int s5k4ecgx_set_ahb_address(struct v4l2_subdev
*sd
)
295 struct i2c_client
*client
= v4l2_get_subdevdata(sd
);
298 /* Set APB peripherals start address */
299 ret
= s5k4ecgx_i2c_write(client
, AHB_MSB_ADDR_PTR
, GEN_REG_OFFSH
);
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
307 ret
= s5k4ecgx_i2c_write(client
, 0x0010, 0x0001);
311 ret
= s5k4ecgx_i2c_write(client
, 0x1030, 0x0000);
315 return s5k4ecgx_i2c_write(client
, 0x0014, 0x0001);
318 #define FW_CRC_SIZE 4
319 /* Register address, value are 4, 2 bytes */
320 #define FW_RECORD_SIZE 6
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
330 static int s5k4ecgx_load_firmware(struct v4l2_subdev
*sd
)
332 struct i2c_client
*client
= v4l2_get_subdevdata(sd
);
333 const struct firmware
*fw
;
335 int err
, i
, regs_num
;
336 u32 addr
, crc
, crc_file
, addr_inc
= 0;
339 err
= request_firmware(&fw
, S5K4ECGX_FIRMWARE
, sd
->v4l2_dev
->dev
);
341 v4l2_err(sd
, "Failed to read firmware %s\n", S5K4ECGX_FIRMWARE
);
344 regs_num
= get_unaligned_le32(fw
->data
);
346 v4l2_dbg(3, debug
, sd
, "FW: %s size %zu register sets %d\n",
347 S5K4ECGX_FIRMWARE
, fw
->size
, regs_num
);
349 regs_num
++; /* Add header */
350 if (fw
->size
!= regs_num
* FW_RECORD_SIZE
+ FW_CRC_SIZE
) {
354 crc_file
= get_unaligned_le32(fw
->data
+ regs_num
* FW_RECORD_SIZE
);
355 crc
= crc32_le(~0, fw
->data
, regs_num
* FW_RECORD_SIZE
);
356 if (crc
!= crc_file
) {
357 v4l2_err(sd
, "FW: invalid crc (%#x:%#x)\n", crc
, crc_file
);
361 ptr
= fw
->data
+ FW_RECORD_SIZE
;
362 for (i
= 1; i
< regs_num
; i
++) {
363 addr
= get_unaligned_le32(ptr
);
365 val
= get_unaligned_le16(ptr
);
367 if (addr
- addr_inc
!= 2)
368 err
= s5k4ecgx_write(client
, addr
, val
);
370 err
= s5k4ecgx_i2c_write(client
, REG_CMDBUF0_ADDR
, val
);
376 release_firmware(fw
);
380 /* Set preview and capture input window */
381 static int s5k4ecgx_set_input_window(struct i2c_client
*c
,
382 const struct v4l2_rect
*r
)
386 ret
= s5k4ecgx_write(c
, REG_G_PREV_IN_WIDTH
, r
->width
);
388 ret
= s5k4ecgx_write(c
, REG_G_PREV_IN_HEIGHT
, r
->height
);
390 ret
= s5k4ecgx_write(c
, REG_G_PREV_IN_XOFFS
, r
->left
);
392 ret
= s5k4ecgx_write(c
, REG_G_PREV_IN_YOFFS
, r
->top
);
394 ret
= s5k4ecgx_write(c
, REG_G_CAP_IN_WIDTH
, r
->width
);
396 ret
= s5k4ecgx_write(c
, REG_G_CAP_IN_HEIGHT
, r
->height
);
398 ret
= s5k4ecgx_write(c
, REG_G_CAP_IN_XOFFS
, r
->left
);
400 ret
= s5k4ecgx_write(c
, REG_G_CAP_IN_YOFFS
, r
->top
);
405 /* Set preview and capture zoom input window */
406 static int s5k4ecgx_set_zoom_window(struct i2c_client
*c
,
407 const struct v4l2_rect
*r
)
411 ret
= s5k4ecgx_write(c
, REG_G_PREVZOOM_IN_WIDTH
, r
->width
);
413 ret
= s5k4ecgx_write(c
, REG_G_PREVZOOM_IN_HEIGHT
, r
->height
);
415 ret
= s5k4ecgx_write(c
, REG_G_PREVZOOM_IN_XOFFS
, r
->left
);
417 ret
= s5k4ecgx_write(c
, REG_G_PREVZOOM_IN_YOFFS
, r
->top
);
419 ret
= s5k4ecgx_write(c
, REG_G_CAPZOOM_IN_WIDTH
, r
->width
);
421 ret
= s5k4ecgx_write(c
, REG_G_CAPZOOM_IN_HEIGHT
, r
->height
);
423 ret
= s5k4ecgx_write(c
, REG_G_CAPZOOM_IN_XOFFS
, r
->left
);
425 ret
= s5k4ecgx_write(c
, REG_G_CAPZOOM_IN_YOFFS
, r
->top
);
430 static int s5k4ecgx_set_output_framefmt(struct s5k4ecgx
*priv
)
432 struct i2c_client
*client
= v4l2_get_subdevdata(&priv
->sd
);
435 ret
= s5k4ecgx_write(client
, REG_P_OUT_WIDTH(0),
436 priv
->curr_frmsize
->size
.width
);
438 ret
= s5k4ecgx_write(client
, REG_P_OUT_HEIGHT(0),
439 priv
->curr_frmsize
->size
.height
);
441 ret
= s5k4ecgx_write(client
, REG_P_FMT(0),
442 priv
->curr_pixfmt
->reg_p_format
);
446 static int s5k4ecgx_init_sensor(struct v4l2_subdev
*sd
)
450 ret
= s5k4ecgx_set_ahb_address(sd
);
452 /* The delay is from manufacturer's settings */
456 ret
= s5k4ecgx_load_firmware(sd
);
458 v4l2_err(sd
, "Failed to write initial settings\n");
463 static int s5k4ecgx_gpio_set_value(struct s5k4ecgx
*priv
, int id
, u32 val
)
465 if (!gpio_is_valid(priv
->gpio
[id
].gpio
))
467 gpio_set_value(priv
->gpio
[id
].gpio
, val
);
472 static int __s5k4ecgx_power_on(struct s5k4ecgx
*priv
)
476 ret
= regulator_bulk_enable(S5K4ECGX_NUM_SUPPLIES
, priv
->supplies
);
479 usleep_range(30, 50);
481 /* The polarity of STBY is controlled by TSP */
482 if (s5k4ecgx_gpio_set_value(priv
, STBY
, priv
->gpio
[STBY
].level
))
483 usleep_range(30, 50);
485 if (s5k4ecgx_gpio_set_value(priv
, RST
, priv
->gpio
[RST
].level
))
486 usleep_range(30, 50);
491 static int __s5k4ecgx_power_off(struct s5k4ecgx
*priv
)
493 if (s5k4ecgx_gpio_set_value(priv
, RST
, !priv
->gpio
[RST
].level
))
494 usleep_range(30, 50);
496 if (s5k4ecgx_gpio_set_value(priv
, STBY
, !priv
->gpio
[STBY
].level
))
497 usleep_range(30, 50);
501 return regulator_bulk_disable(S5K4ECGX_NUM_SUPPLIES
, priv
->supplies
);
504 /* Find nearest matching image pixel size. */
505 static int s5k4ecgx_try_frame_size(struct v4l2_mbus_framefmt
*mf
,
506 const struct s5k4ecgx_frmsize
**size
)
508 unsigned int min_err
= ~0;
509 int i
= ARRAY_SIZE(s5k4ecgx_prev_sizes
);
510 const struct s5k4ecgx_frmsize
*fsize
= &s5k4ecgx_prev_sizes
[0],
514 int err
= abs(fsize
->size
.width
- mf
->width
)
515 + abs(fsize
->size
.height
- mf
->height
);
523 mf
->width
= match
->size
.width
;
524 mf
->height
= match
->size
.height
;
533 static int s5k4ecgx_enum_mbus_code(struct v4l2_subdev
*sd
,
534 struct v4l2_subdev_pad_config
*cfg
,
535 struct v4l2_subdev_mbus_code_enum
*code
)
537 if (code
->index
>= ARRAY_SIZE(s5k4ecgx_formats
))
539 code
->code
= s5k4ecgx_formats
[code
->index
].code
;
544 static int s5k4ecgx_get_fmt(struct v4l2_subdev
*sd
, struct v4l2_subdev_pad_config
*cfg
,
545 struct v4l2_subdev_format
*fmt
)
547 struct s5k4ecgx
*priv
= to_s5k4ecgx(sd
);
548 struct v4l2_mbus_framefmt
*mf
;
550 if (fmt
->which
== V4L2_SUBDEV_FORMAT_TRY
) {
552 mf
= v4l2_subdev_get_try_format(sd
, cfg
, 0);
560 mutex_lock(&priv
->lock
);
561 mf
->width
= priv
->curr_frmsize
->size
.width
;
562 mf
->height
= priv
->curr_frmsize
->size
.height
;
563 mf
->code
= priv
->curr_pixfmt
->code
;
564 mf
->colorspace
= priv
->curr_pixfmt
->colorspace
;
565 mf
->field
= V4L2_FIELD_NONE
;
566 mutex_unlock(&priv
->lock
);
571 static const struct s5k4ecgx_pixfmt
*s5k4ecgx_try_fmt(struct v4l2_subdev
*sd
,
572 struct v4l2_mbus_framefmt
*mf
)
574 int i
= ARRAY_SIZE(s5k4ecgx_formats
);
577 if (mf
->code
== s5k4ecgx_formats
[i
].code
)
579 mf
->code
= s5k4ecgx_formats
[i
].code
;
581 return &s5k4ecgx_formats
[i
];
584 static int s5k4ecgx_set_fmt(struct v4l2_subdev
*sd
, struct v4l2_subdev_pad_config
*cfg
,
585 struct v4l2_subdev_format
*fmt
)
587 struct s5k4ecgx
*priv
= to_s5k4ecgx(sd
);
588 const struct s5k4ecgx_frmsize
*fsize
= NULL
;
589 const struct s5k4ecgx_pixfmt
*pf
;
590 struct v4l2_mbus_framefmt
*mf
;
593 pf
= s5k4ecgx_try_fmt(sd
, &fmt
->format
);
594 s5k4ecgx_try_frame_size(&fmt
->format
, &fsize
);
595 fmt
->format
.colorspace
= V4L2_COLORSPACE_JPEG
;
596 fmt
->format
.field
= V4L2_FIELD_NONE
;
598 if (fmt
->which
== V4L2_SUBDEV_FORMAT_TRY
) {
600 mf
= v4l2_subdev_get_try_format(sd
, cfg
, 0);
606 mutex_lock(&priv
->lock
);
607 if (!priv
->streaming
) {
608 priv
->curr_frmsize
= fsize
;
609 priv
->curr_pixfmt
= pf
;
610 priv
->set_params
= 1;
614 mutex_unlock(&priv
->lock
);
619 static 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
,
626 * V4L2 subdev controls
628 static int s5k4ecgx_s_ctrl(struct v4l2_ctrl
*ctrl
)
630 struct v4l2_subdev
*sd
= &container_of(ctrl
->handler
, struct s5k4ecgx
,
632 struct i2c_client
*client
= v4l2_get_subdevdata(sd
);
633 struct s5k4ecgx
*priv
= to_s5k4ecgx(sd
);
637 v4l2_dbg(1, debug
, sd
, "ctrl: 0x%x, value: %d\n", ctrl
->id
, ctrl
->val
);
639 mutex_lock(&priv
->lock
);
641 case V4L2_CID_CONTRAST
:
642 err
= s5k4ecgx_write(client
, REG_USER_CONTRAST
, ctrl
->val
);
645 case V4L2_CID_SATURATION
:
646 err
= s5k4ecgx_write(client
, REG_USER_SATURATION
, ctrl
->val
);
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
);
656 case V4L2_CID_BRIGHTNESS
:
657 err
= s5k4ecgx_write(client
, REG_USER_BRIGHTNESS
, ctrl
->val
);
660 mutex_unlock(&priv
->lock
);
662 v4l2_err(sd
, "Failed to write s_ctrl err %d\n", err
);
667 static const struct v4l2_ctrl_ops s5k4ecgx_ctrl_ops
= {
668 .s_ctrl
= s5k4ecgx_s_ctrl
,
672 * Reading s5k4ecgx version information
674 static int s5k4ecgx_registered(struct v4l2_subdev
*sd
)
677 struct s5k4ecgx
*priv
= to_s5k4ecgx(sd
);
679 mutex_lock(&priv
->lock
);
680 ret
= __s5k4ecgx_power_on(priv
);
682 ret
= s5k4ecgx_read_fw_ver(sd
);
683 __s5k4ecgx_power_off(priv
);
685 mutex_unlock(&priv
->lock
);
691 * V4L2 subdev internal operations
693 static int s5k4ecgx_open(struct v4l2_subdev
*sd
, struct v4l2_subdev_fh
*fh
)
695 struct v4l2_mbus_framefmt
*mf
= v4l2_subdev_get_try_format(sd
, fh
->pad
, 0);
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
;
706 static const struct v4l2_subdev_internal_ops s5k4ecgx_subdev_internal_ops
= {
707 .registered
= s5k4ecgx_registered
,
708 .open
= s5k4ecgx_open
,
711 static int s5k4ecgx_s_power(struct v4l2_subdev
*sd
, int on
)
713 struct s5k4ecgx
*priv
= to_s5k4ecgx(sd
);
716 v4l2_dbg(1, debug
, sd
, "Switching %s\n", on
? "on" : "off");
719 ret
= __s5k4ecgx_power_on(priv
);
722 /* Time to stabilize sensor */
724 ret
= s5k4ecgx_init_sensor(sd
);
726 __s5k4ecgx_power_off(priv
);
728 priv
->set_params
= 1;
730 ret
= __s5k4ecgx_power_off(priv
);
736 static int s5k4ecgx_log_status(struct v4l2_subdev
*sd
)
738 v4l2_ctrl_handler_log_status(sd
->ctrl_handler
, sd
->name
);
743 static const struct v4l2_subdev_core_ops s5k4ecgx_core_ops
= {
744 .s_power
= s5k4ecgx_s_power
,
745 .log_status
= s5k4ecgx_log_status
,
748 static int __s5k4ecgx_s_params(struct s5k4ecgx
*priv
)
750 struct i2c_client
*client
= v4l2_get_subdevdata(&priv
->sd
);
751 const struct v4l2_rect
*crop_rect
= &priv
->curr_frmsize
->input_window
;
754 ret
= s5k4ecgx_set_input_window(client
, crop_rect
);
756 ret
= s5k4ecgx_set_zoom_window(client
, crop_rect
);
758 ret
= s5k4ecgx_write(client
, REG_G_INPUTS_CHANGE_REQ
, 1);
760 ret
= s5k4ecgx_write(client
, 0x70000a1e, 0x28);
762 ret
= s5k4ecgx_write(client
, 0x70000ad4, 0x3c);
764 ret
= s5k4ecgx_set_output_framefmt(priv
);
766 ret
= s5k4ecgx_write(client
, REG_P_PVI_MASK(0), 0x52);
768 ret
= s5k4ecgx_write(client
, REG_P_FR_TIME_TYPE(0),
771 ret
= s5k4ecgx_write(client
, REG_P_FR_TIME_Q_TYPE(0),
772 FR_TIME_Q_BEST_FRRATE
);
774 ret
= s5k4ecgx_write(client
, REG_P_MIN_FR_TIME(0),
775 US_TO_FR_TIME(33300));
777 ret
= s5k4ecgx_write(client
, REG_P_MAX_FR_TIME(0),
778 US_TO_FR_TIME(66600));
780 ret
= s5k4ecgx_write(client
, REG_P_PREV_MIRROR(0), 0);
782 ret
= s5k4ecgx_write(client
, REG_P_CAP_MIRROR(0), 0);
784 ret
= s5k4ecgx_write(client
, REG_G_ACTIVE_PREV_CFG
, 0);
786 ret
= s5k4ecgx_write(client
, REG_G_PREV_OPEN_AFTER_CH
, 1);
788 ret
= s5k4ecgx_write(client
, REG_G_NEW_CFG_SYNC
, 1);
790 ret
= s5k4ecgx_write(client
, REG_G_PREV_CFG_CHG
, 1);
795 static int __s5k4ecgx_s_stream(struct s5k4ecgx
*priv
, int on
)
797 struct i2c_client
*client
= v4l2_get_subdevdata(&priv
->sd
);
800 if (on
&& priv
->set_params
) {
801 ret
= __s5k4ecgx_s_params(priv
);
804 priv
->set_params
= 0;
807 * This enables/disables preview stream only. Capture requests
808 * are not supported yet.
810 ret
= s5k4ecgx_write(client
, REG_G_ENABLE_PREV
, on
);
813 return s5k4ecgx_write(client
, REG_G_ENABLE_PREV_CHG
, 1);
816 static int s5k4ecgx_s_stream(struct v4l2_subdev
*sd
, int on
)
818 struct s5k4ecgx
*priv
= to_s5k4ecgx(sd
);
821 v4l2_dbg(1, debug
, sd
, "Turn streaming %s\n", on
? "on" : "off");
823 mutex_lock(&priv
->lock
);
825 if (priv
->streaming
== !on
) {
826 ret
= __s5k4ecgx_s_stream(priv
, on
);
828 priv
->streaming
= on
& 1;
831 mutex_unlock(&priv
->lock
);
835 static const struct v4l2_subdev_video_ops s5k4ecgx_video_ops
= {
836 .s_stream
= s5k4ecgx_s_stream
,
839 static const struct v4l2_subdev_ops s5k4ecgx_ops
= {
840 .core
= &s5k4ecgx_core_ops
,
841 .pad
= &s5k4ecgx_pad_ops
,
842 .video
= &s5k4ecgx_video_ops
,
848 static int s5k4ecgx_config_gpio(int nr
, int val
, const char *name
)
850 unsigned long flags
= val
? GPIOF_OUT_INIT_HIGH
: GPIOF_OUT_INIT_LOW
;
853 if (!gpio_is_valid(nr
))
855 ret
= gpio_request_one(nr
, flags
, name
);
862 static void s5k4ecgx_free_gpios(struct s5k4ecgx
*priv
)
866 for (i
= 0; i
< ARRAY_SIZE(priv
->gpio
); i
++) {
867 if (!gpio_is_valid(priv
->gpio
[i
].gpio
))
869 gpio_free(priv
->gpio
[i
].gpio
);
870 priv
->gpio
[i
].gpio
= -EINVAL
;
874 static int s5k4ecgx_config_gpios(struct s5k4ecgx
*priv
,
875 const struct s5k4ecgx_platform_data
*pdata
)
877 const struct s5k4ecgx_gpio
*gpio
= &pdata
->gpio_stby
;
880 priv
->gpio
[STBY
].gpio
= -EINVAL
;
881 priv
->gpio
[RST
].gpio
= -EINVAL
;
883 ret
= s5k4ecgx_config_gpio(gpio
->gpio
, gpio
->level
, "S5K4ECGX_STBY");
886 s5k4ecgx_free_gpios(priv
);
889 priv
->gpio
[STBY
] = *gpio
;
890 if (gpio_is_valid(gpio
->gpio
))
891 gpio_set_value(gpio
->gpio
, 0);
893 gpio
= &pdata
->gpio_reset
;
895 ret
= s5k4ecgx_config_gpio(gpio
->gpio
, gpio
->level
, "S5K4ECGX_RST");
897 s5k4ecgx_free_gpios(priv
);
900 priv
->gpio
[RST
] = *gpio
;
901 if (gpio_is_valid(gpio
->gpio
))
902 gpio_set_value(gpio
->gpio
, 0);
907 static int s5k4ecgx_init_v4l2_ctrls(struct s5k4ecgx
*priv
)
909 const struct v4l2_ctrl_ops
*ops
= &s5k4ecgx_ctrl_ops
;
910 struct v4l2_ctrl_handler
*hdl
= &priv
->handler
;
913 ret
= v4l2_ctrl_handler_init(hdl
, 4);
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);
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);
926 v4l2_ctrl_handler_free(hdl
);
929 priv
->sd
.ctrl_handler
= hdl
;
934 static int s5k4ecgx_probe(struct i2c_client
*client
,
935 const struct i2c_device_id
*id
)
937 struct s5k4ecgx_platform_data
*pdata
= client
->dev
.platform_data
;
938 struct v4l2_subdev
*sd
;
939 struct s5k4ecgx
*priv
;
943 dev_err(&client
->dev
, "platform data is missing!\n");
947 priv
= devm_kzalloc(&client
->dev
, sizeof(struct s5k4ecgx
), GFP_KERNEL
);
951 mutex_init(&priv
->lock
);
955 /* Registering subdev */
956 v4l2_i2c_subdev_init(sd
, client
, &s5k4ecgx_ops
);
957 strlcpy(sd
->name
, S5K4ECGX_DRIVER_NAME
, sizeof(sd
->name
));
959 sd
->internal_ops
= &s5k4ecgx_subdev_internal_ops
;
960 /* Support v4l2 sub-device user space API */
961 sd
->flags
|= V4L2_SUBDEV_FL_HAS_DEVNODE
;
963 priv
->pad
.flags
= MEDIA_PAD_FL_SOURCE
;
964 sd
->entity
.function
= MEDIA_ENT_F_CAM_SENSOR
;
965 ret
= media_entity_pads_init(&sd
->entity
, 1, &priv
->pad
);
969 ret
= s5k4ecgx_config_gpios(priv
, pdata
);
971 dev_err(&client
->dev
, "Failed to set gpios\n");
974 for (i
= 0; i
< S5K4ECGX_NUM_SUPPLIES
; i
++)
975 priv
->supplies
[i
].supply
= s5k4ecgx_supply_names
[i
];
977 ret
= devm_regulator_bulk_get(&client
->dev
, S5K4ECGX_NUM_SUPPLIES
,
980 dev_err(&client
->dev
, "Failed to get regulators\n");
983 ret
= s5k4ecgx_init_v4l2_ctrls(priv
);
987 priv
->curr_pixfmt
= &s5k4ecgx_formats
[0];
988 priv
->curr_frmsize
= &s5k4ecgx_prev_sizes
[0];
993 s5k4ecgx_free_gpios(priv
);
995 media_entity_cleanup(&priv
->sd
.entity
);
1000 static int s5k4ecgx_remove(struct i2c_client
*client
)
1002 struct v4l2_subdev
*sd
= i2c_get_clientdata(client
);
1003 struct s5k4ecgx
*priv
= to_s5k4ecgx(sd
);
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
);
1014 static const struct i2c_device_id s5k4ecgx_id
[] = {
1015 { S5K4ECGX_DRIVER_NAME
, 0 },
1018 MODULE_DEVICE_TABLE(i2c
, s5k4ecgx_id
);
1020 static struct i2c_driver v4l2_i2c_driver
= {
1022 .name
= S5K4ECGX_DRIVER_NAME
,
1024 .probe
= s5k4ecgx_probe
,
1025 .remove
= s5k4ecgx_remove
,
1026 .id_table
= s5k4ecgx_id
,
1029 module_i2c_driver(v4l2_i2c_driver
);
1031 MODULE_DESCRIPTION("Samsung S5K4ECGX 5MP SOC camera");
1032 MODULE_AUTHOR("Sangwook Lee <sangwook.lee@linaro.org>");
1033 MODULE_AUTHOR("Seok-Young Jang <quartz.jang@samsung.com>");
1034 MODULE_LICENSE("GPL");
1035 MODULE_FIRMWARE(S5K4ECGX_FIRMWARE
);