1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Driver for Samsung S5K4ECGX 1/4" 5Mp CMOS Image Sensor SoC
4 * with an Embedded Image Signal Processor.
6 * Copyright (C) 2012, Linaro, Sangwook Lee <sangwook.lee@linaro.org>
7 * Copyright (C) 2012, Insignal Co,. Ltd, Homin Lee <suapapa@insignal.co.kr>
9 * Based on s5k6aa and noon010pc30 driver
10 * Copyright (C) 2011, Samsung Electronics Co., Ltd.
13 #include <linux/clk.h>
14 #include <linux/crc32.h>
15 #include <linux/ctype.h>
16 #include <linux/delay.h>
17 #include <linux/firmware.h>
18 #include <linux/gpio.h>
19 #include <linux/i2c.h>
20 #include <linux/module.h>
21 #include <linux/regulator/consumer.h>
22 #include <linux/slab.h>
23 #include <asm/unaligned.h>
25 #include <media/media-entity.h>
26 #include <media/i2c/s5k4ecgx.h>
27 #include <media/v4l2-ctrls.h>
28 #include <media/v4l2-device.h>
29 #include <media/v4l2-mediabus.h>
30 #include <media/v4l2-subdev.h>
33 module_param(debug
, int, 0644);
35 #define S5K4ECGX_DRIVER_NAME "s5k4ecgx"
36 #define S5K4ECGX_FIRMWARE "s5k4ecgx.bin"
38 /* Firmware revision information */
39 #define REG_FW_REVISION 0x700001a6
40 #define REG_FW_VERSION 0x700001a4
41 #define S5K4ECGX_REVISION_1_1 0x11
42 #define S5K4ECGX_FW_VERSION 0x4ec0
44 /* General purpose parameters */
45 #define REG_USER_BRIGHTNESS 0x7000022c
46 #define REG_USER_CONTRAST 0x7000022e
47 #define REG_USER_SATURATION 0x70000230
49 #define REG_G_ENABLE_PREV 0x7000023e
50 #define REG_G_ENABLE_PREV_CHG 0x70000240
51 #define REG_G_NEW_CFG_SYNC 0x7000024a
52 #define REG_G_PREV_IN_WIDTH 0x70000250
53 #define REG_G_PREV_IN_HEIGHT 0x70000252
54 #define REG_G_PREV_IN_XOFFS 0x70000254
55 #define REG_G_PREV_IN_YOFFS 0x70000256
56 #define REG_G_CAP_IN_WIDTH 0x70000258
57 #define REG_G_CAP_IN_HEIGHT 0x7000025a
58 #define REG_G_CAP_IN_XOFFS 0x7000025c
59 #define REG_G_CAP_IN_YOFFS 0x7000025e
60 #define REG_G_INPUTS_CHANGE_REQ 0x70000262
61 #define REG_G_ACTIVE_PREV_CFG 0x70000266
62 #define REG_G_PREV_CFG_CHG 0x70000268
63 #define REG_G_PREV_OPEN_AFTER_CH 0x7000026a
65 /* Preview context register sets. n = 0...4. */
66 #define PREG(n, x) ((n) * 0x30 + (x))
67 #define REG_P_OUT_WIDTH(n) PREG(n, 0x700002a6)
68 #define REG_P_OUT_HEIGHT(n) PREG(n, 0x700002a8)
69 #define REG_P_FMT(n) PREG(n, 0x700002aa)
70 #define REG_P_PVI_MASK(n) PREG(n, 0x700002b4)
71 #define REG_P_FR_TIME_TYPE(n) PREG(n, 0x700002be)
72 #define FR_TIME_DYNAMIC 0
73 #define FR_TIME_FIXED 1
74 #define FR_TIME_FIXED_ACCURATE 2
75 #define REG_P_FR_TIME_Q_TYPE(n) PREG(n, 0x700002c0)
76 #define FR_TIME_Q_DYNAMIC 0
77 #define FR_TIME_Q_BEST_FRRATE 1
78 #define FR_TIME_Q_BEST_QUALITY 2
80 /* Frame period in 0.1 ms units */
81 #define REG_P_MAX_FR_TIME(n) PREG(n, 0x700002c2)
82 #define REG_P_MIN_FR_TIME(n) PREG(n, 0x700002c4)
83 #define US_TO_FR_TIME(__t) ((__t) / 100)
84 #define REG_P_PREV_MIRROR(n) PREG(n, 0x700002d0)
85 #define REG_P_CAP_MIRROR(n) PREG(n, 0x700002d2)
87 #define REG_G_PREVZOOM_IN_WIDTH 0x70000494
88 #define REG_G_PREVZOOM_IN_HEIGHT 0x70000496
89 #define REG_G_PREVZOOM_IN_XOFFS 0x70000498
90 #define REG_G_PREVZOOM_IN_YOFFS 0x7000049a
91 #define REG_G_CAPZOOM_IN_WIDTH 0x7000049c
92 #define REG_G_CAPZOOM_IN_HEIGHT 0x7000049e
93 #define REG_G_CAPZOOM_IN_XOFFS 0x700004a0
94 #define REG_G_CAPZOOM_IN_YOFFS 0x700004a2
97 #define REG_USER_SHARPNESS(n) (0x70000a28 + (n) * 0xb6)
99 /* Reduce sharpness range for user space API */
100 #define SHARPNESS_DIV 8208
101 #define TOK_TERM 0xffffffff
104 * FIXME: This is copied from s5k6aa, because of no information
105 * in the S5K4ECGX datasheet.
106 * H/W register Interface (0xd0000000 - 0xd0000fff)
108 #define AHB_MSB_ADDR_PTR 0xfcfc
109 #define GEN_REG_OFFSH 0xd000
110 #define REG_CMDWR_ADDRH 0x0028
111 #define REG_CMDWR_ADDRL 0x002a
112 #define REG_CMDRD_ADDRH 0x002c
113 #define REG_CMDRD_ADDRL 0x002e
114 #define REG_CMDBUF0_ADDR 0x0f12
116 struct s5k4ecgx_frmsize
{
117 struct v4l2_frmsize_discrete size
;
118 /* Fixed sensor matrix crop rectangle */
119 struct v4l2_rect input_window
;
128 * TODO: currently only preview is supported and snapshot (capture)
129 * is not implemented yet
131 static const struct s5k4ecgx_frmsize s5k4ecgx_prev_sizes
[] = {
133 .size
= { 176, 144 },
134 .input_window
= { 0x00, 0x00, 0x928, 0x780 },
136 .size
= { 352, 288 },
137 .input_window
= { 0x00, 0x00, 0x928, 0x780 },
139 .size
= { 640, 480 },
140 .input_window
= { 0x00, 0x00, 0xa00, 0x780 },
142 .size
= { 720, 480 },
143 .input_window
= { 0x00, 0x00, 0xa00, 0x6a8 },
147 #define S5K4ECGX_NUM_PREV ARRAY_SIZE(s5k4ecgx_prev_sizes)
149 struct s5k4ecgx_pixfmt
{
152 /* REG_TC_PCFG_Format register value */
156 /* By default value, output from sensor will be YUV422 0-255 */
157 static const struct s5k4ecgx_pixfmt s5k4ecgx_formats
[] = {
158 { MEDIA_BUS_FMT_YUYV8_2X8
, V4L2_COLORSPACE_JPEG
, 5 },
161 static const char * const s5k4ecgx_supply_names
[] = {
163 * Usually 2.8V is used for analog power (vdda)
164 * and digital IO (vddio, vdddcore)
169 "vddreg", /* The internal s5k4ecgx regulator's supply (1.8V) */
172 #define S5K4ECGX_NUM_SUPPLIES ARRAY_SIZE(s5k4ecgx_supply_names)
174 enum s5k4ecgx_gpio_id
{
181 struct v4l2_subdev sd
;
182 struct media_pad pad
;
183 struct v4l2_ctrl_handler handler
;
185 struct s5k4ecgx_platform_data
*pdata
;
186 const struct s5k4ecgx_pixfmt
*curr_pixfmt
;
187 const struct s5k4ecgx_frmsize
*curr_frmsize
;
192 struct regulator_bulk_data supplies
[S5K4ECGX_NUM_SUPPLIES
];
193 struct s5k4ecgx_gpio gpio
[GPIO_NUM
];
196 static inline struct s5k4ecgx
*to_s5k4ecgx(struct v4l2_subdev
*sd
)
198 return container_of(sd
, struct s5k4ecgx
, sd
);
201 static int s5k4ecgx_i2c_read(struct i2c_client
*client
, u16 addr
, u16
*val
)
203 u8 wbuf
[2] = { addr
>> 8, addr
& 0xff };
204 struct i2c_msg msg
[2];
208 msg
[0].addr
= client
->addr
;
213 msg
[1].addr
= client
->addr
;
214 msg
[1].flags
= I2C_M_RD
;
218 ret
= i2c_transfer(client
->adapter
, msg
, 2);
219 *val
= be16_to_cpu(*((__be16
*)rbuf
));
221 v4l2_dbg(4, debug
, client
, "i2c_read: 0x%04X : 0x%04x\n", addr
, *val
);
223 return ret
== 2 ? 0 : ret
;
226 static int s5k4ecgx_i2c_write(struct i2c_client
*client
, u16 addr
, u16 val
)
228 u8 buf
[4] = { addr
>> 8, addr
& 0xff, val
>> 8, val
& 0xff };
230 int ret
= i2c_master_send(client
, buf
, 4);
231 v4l2_dbg(4, debug
, client
, "i2c_write: 0x%04x : 0x%04x\n", addr
, val
);
233 return ret
== 4 ? 0 : ret
;
236 static int s5k4ecgx_write(struct i2c_client
*client
, u32 addr
, u16 val
)
238 u16 high
= addr
>> 16, low
= addr
& 0xffff;
241 v4l2_dbg(3, debug
, client
, "write: 0x%08x : 0x%04x\n", addr
, val
);
243 ret
= s5k4ecgx_i2c_write(client
, REG_CMDWR_ADDRH
, high
);
245 ret
= s5k4ecgx_i2c_write(client
, REG_CMDWR_ADDRL
, low
);
247 ret
= s5k4ecgx_i2c_write(client
, REG_CMDBUF0_ADDR
, val
);
252 static int s5k4ecgx_read(struct i2c_client
*client
, u32 addr
, u16
*val
)
254 u16 high
= addr
>> 16, low
= addr
& 0xffff;
257 ret
= s5k4ecgx_i2c_write(client
, REG_CMDRD_ADDRH
, high
);
259 ret
= s5k4ecgx_i2c_write(client
, REG_CMDRD_ADDRL
, low
);
261 ret
= s5k4ecgx_i2c_read(client
, REG_CMDBUF0_ADDR
, val
);
266 static int s5k4ecgx_read_fw_ver(struct v4l2_subdev
*sd
)
268 struct i2c_client
*client
= v4l2_get_subdevdata(sd
);
269 u16 hw_rev
, fw_ver
= 0;
272 ret
= s5k4ecgx_read(client
, REG_FW_VERSION
, &fw_ver
);
273 if (ret
< 0 || fw_ver
!= S5K4ECGX_FW_VERSION
) {
274 v4l2_err(sd
, "FW version check failed!\n");
278 ret
= s5k4ecgx_read(client
, REG_FW_REVISION
, &hw_rev
);
282 v4l2_info(sd
, "chip found FW ver: 0x%x, HW rev: 0x%x\n",
287 static int s5k4ecgx_set_ahb_address(struct v4l2_subdev
*sd
)
289 struct i2c_client
*client
= v4l2_get_subdevdata(sd
);
292 /* Set APB peripherals start address */
293 ret
= s5k4ecgx_i2c_write(client
, AHB_MSB_ADDR_PTR
, GEN_REG_OFFSH
);
297 * FIXME: This is copied from s5k6aa, because of no information
298 * in s5k4ecgx's datasheet.
299 * sw_reset is activated to put device into idle status
301 ret
= s5k4ecgx_i2c_write(client
, 0x0010, 0x0001);
305 ret
= s5k4ecgx_i2c_write(client
, 0x1030, 0x0000);
309 return s5k4ecgx_i2c_write(client
, 0x0014, 0x0001);
312 #define FW_CRC_SIZE 4
313 /* Register address, value are 4, 2 bytes */
314 #define FW_RECORD_SIZE 6
316 * The firmware has following format:
317 * < total number of records (4 bytes + 2 bytes padding) N >,
318 * < record 0 >, ..., < record N - 1 >, < CRC32-CCITT (4-bytes) >,
319 * where "record" is a 4-byte register address followed by 2-byte
320 * register value (little endian).
321 * The firmware generator can be found in following git repository:
322 * git://git.linaro.org/people/sangwook/fimc-v4l2-app.git
324 static int s5k4ecgx_load_firmware(struct v4l2_subdev
*sd
)
326 struct i2c_client
*client
= v4l2_get_subdevdata(sd
);
327 const struct firmware
*fw
;
329 int err
, i
, regs_num
;
330 u32 addr
, crc
, crc_file
, addr_inc
= 0;
333 err
= request_firmware(&fw
, S5K4ECGX_FIRMWARE
, sd
->v4l2_dev
->dev
);
335 v4l2_err(sd
, "Failed to read firmware %s\n", S5K4ECGX_FIRMWARE
);
338 regs_num
= get_unaligned_le32(fw
->data
);
340 v4l2_dbg(3, debug
, sd
, "FW: %s size %zu register sets %d\n",
341 S5K4ECGX_FIRMWARE
, fw
->size
, regs_num
);
343 regs_num
++; /* Add header */
344 if (fw
->size
!= regs_num
* FW_RECORD_SIZE
+ FW_CRC_SIZE
) {
348 crc_file
= get_unaligned_le32(fw
->data
+ regs_num
* FW_RECORD_SIZE
);
349 crc
= crc32_le(~0, fw
->data
, regs_num
* FW_RECORD_SIZE
);
350 if (crc
!= crc_file
) {
351 v4l2_err(sd
, "FW: invalid crc (%#x:%#x)\n", crc
, crc_file
);
355 ptr
= fw
->data
+ FW_RECORD_SIZE
;
356 for (i
= 1; i
< regs_num
; i
++) {
357 addr
= get_unaligned_le32(ptr
);
359 val
= get_unaligned_le16(ptr
);
361 if (addr
- addr_inc
!= 2)
362 err
= s5k4ecgx_write(client
, addr
, val
);
364 err
= s5k4ecgx_i2c_write(client
, REG_CMDBUF0_ADDR
, val
);
370 release_firmware(fw
);
374 /* Set preview and capture input window */
375 static int s5k4ecgx_set_input_window(struct i2c_client
*c
,
376 const struct v4l2_rect
*r
)
380 ret
= s5k4ecgx_write(c
, REG_G_PREV_IN_WIDTH
, r
->width
);
382 ret
= s5k4ecgx_write(c
, REG_G_PREV_IN_HEIGHT
, r
->height
);
384 ret
= s5k4ecgx_write(c
, REG_G_PREV_IN_XOFFS
, r
->left
);
386 ret
= s5k4ecgx_write(c
, REG_G_PREV_IN_YOFFS
, r
->top
);
388 ret
= s5k4ecgx_write(c
, REG_G_CAP_IN_WIDTH
, r
->width
);
390 ret
= s5k4ecgx_write(c
, REG_G_CAP_IN_HEIGHT
, r
->height
);
392 ret
= s5k4ecgx_write(c
, REG_G_CAP_IN_XOFFS
, r
->left
);
394 ret
= s5k4ecgx_write(c
, REG_G_CAP_IN_YOFFS
, r
->top
);
399 /* Set preview and capture zoom input window */
400 static int s5k4ecgx_set_zoom_window(struct i2c_client
*c
,
401 const struct v4l2_rect
*r
)
405 ret
= s5k4ecgx_write(c
, REG_G_PREVZOOM_IN_WIDTH
, r
->width
);
407 ret
= s5k4ecgx_write(c
, REG_G_PREVZOOM_IN_HEIGHT
, r
->height
);
409 ret
= s5k4ecgx_write(c
, REG_G_PREVZOOM_IN_XOFFS
, r
->left
);
411 ret
= s5k4ecgx_write(c
, REG_G_PREVZOOM_IN_YOFFS
, r
->top
);
413 ret
= s5k4ecgx_write(c
, REG_G_CAPZOOM_IN_WIDTH
, r
->width
);
415 ret
= s5k4ecgx_write(c
, REG_G_CAPZOOM_IN_HEIGHT
, r
->height
);
417 ret
= s5k4ecgx_write(c
, REG_G_CAPZOOM_IN_XOFFS
, r
->left
);
419 ret
= s5k4ecgx_write(c
, REG_G_CAPZOOM_IN_YOFFS
, r
->top
);
424 static int s5k4ecgx_set_output_framefmt(struct s5k4ecgx
*priv
)
426 struct i2c_client
*client
= v4l2_get_subdevdata(&priv
->sd
);
429 ret
= s5k4ecgx_write(client
, REG_P_OUT_WIDTH(0),
430 priv
->curr_frmsize
->size
.width
);
432 ret
= s5k4ecgx_write(client
, REG_P_OUT_HEIGHT(0),
433 priv
->curr_frmsize
->size
.height
);
435 ret
= s5k4ecgx_write(client
, REG_P_FMT(0),
436 priv
->curr_pixfmt
->reg_p_format
);
440 static int s5k4ecgx_init_sensor(struct v4l2_subdev
*sd
)
444 ret
= s5k4ecgx_set_ahb_address(sd
);
446 /* The delay is from manufacturer's settings */
450 ret
= s5k4ecgx_load_firmware(sd
);
452 v4l2_err(sd
, "Failed to write initial settings\n");
457 static int s5k4ecgx_gpio_set_value(struct s5k4ecgx
*priv
, int id
, u32 val
)
459 if (!gpio_is_valid(priv
->gpio
[id
].gpio
))
461 gpio_set_value(priv
->gpio
[id
].gpio
, val
);
466 static int __s5k4ecgx_power_on(struct s5k4ecgx
*priv
)
470 ret
= regulator_bulk_enable(S5K4ECGX_NUM_SUPPLIES
, priv
->supplies
);
473 usleep_range(30, 50);
475 /* The polarity of STBY is controlled by TSP */
476 if (s5k4ecgx_gpio_set_value(priv
, STBY
, priv
->gpio
[STBY
].level
))
477 usleep_range(30, 50);
479 if (s5k4ecgx_gpio_set_value(priv
, RST
, priv
->gpio
[RST
].level
))
480 usleep_range(30, 50);
485 static int __s5k4ecgx_power_off(struct s5k4ecgx
*priv
)
487 if (s5k4ecgx_gpio_set_value(priv
, RST
, !priv
->gpio
[RST
].level
))
488 usleep_range(30, 50);
490 if (s5k4ecgx_gpio_set_value(priv
, STBY
, !priv
->gpio
[STBY
].level
))
491 usleep_range(30, 50);
495 return regulator_bulk_disable(S5K4ECGX_NUM_SUPPLIES
, priv
->supplies
);
498 /* Find nearest matching image pixel size. */
499 static int s5k4ecgx_try_frame_size(struct v4l2_mbus_framefmt
*mf
,
500 const struct s5k4ecgx_frmsize
**size
)
502 unsigned int min_err
= ~0;
503 int i
= ARRAY_SIZE(s5k4ecgx_prev_sizes
);
504 const struct s5k4ecgx_frmsize
*fsize
= &s5k4ecgx_prev_sizes
[0],
508 int err
= abs(fsize
->size
.width
- mf
->width
)
509 + abs(fsize
->size
.height
- mf
->height
);
517 mf
->width
= match
->size
.width
;
518 mf
->height
= match
->size
.height
;
527 static int s5k4ecgx_enum_mbus_code(struct v4l2_subdev
*sd
,
528 struct v4l2_subdev_pad_config
*cfg
,
529 struct v4l2_subdev_mbus_code_enum
*code
)
531 if (code
->index
>= ARRAY_SIZE(s5k4ecgx_formats
))
533 code
->code
= s5k4ecgx_formats
[code
->index
].code
;
538 static int s5k4ecgx_get_fmt(struct v4l2_subdev
*sd
, struct v4l2_subdev_pad_config
*cfg
,
539 struct v4l2_subdev_format
*fmt
)
541 struct s5k4ecgx
*priv
= to_s5k4ecgx(sd
);
542 struct v4l2_mbus_framefmt
*mf
;
544 if (fmt
->which
== V4L2_SUBDEV_FORMAT_TRY
) {
546 mf
= v4l2_subdev_get_try_format(sd
, cfg
, 0);
554 mutex_lock(&priv
->lock
);
555 mf
->width
= priv
->curr_frmsize
->size
.width
;
556 mf
->height
= priv
->curr_frmsize
->size
.height
;
557 mf
->code
= priv
->curr_pixfmt
->code
;
558 mf
->colorspace
= priv
->curr_pixfmt
->colorspace
;
559 mf
->field
= V4L2_FIELD_NONE
;
560 mutex_unlock(&priv
->lock
);
565 static const struct s5k4ecgx_pixfmt
*s5k4ecgx_try_fmt(struct v4l2_subdev
*sd
,
566 struct v4l2_mbus_framefmt
*mf
)
568 int i
= ARRAY_SIZE(s5k4ecgx_formats
);
571 if (mf
->code
== s5k4ecgx_formats
[i
].code
)
573 mf
->code
= s5k4ecgx_formats
[i
].code
;
575 return &s5k4ecgx_formats
[i
];
578 static int s5k4ecgx_set_fmt(struct v4l2_subdev
*sd
, struct v4l2_subdev_pad_config
*cfg
,
579 struct v4l2_subdev_format
*fmt
)
581 struct s5k4ecgx
*priv
= to_s5k4ecgx(sd
);
582 const struct s5k4ecgx_frmsize
*fsize
= NULL
;
583 const struct s5k4ecgx_pixfmt
*pf
;
584 struct v4l2_mbus_framefmt
*mf
;
587 pf
= s5k4ecgx_try_fmt(sd
, &fmt
->format
);
588 s5k4ecgx_try_frame_size(&fmt
->format
, &fsize
);
589 fmt
->format
.colorspace
= V4L2_COLORSPACE_JPEG
;
590 fmt
->format
.field
= V4L2_FIELD_NONE
;
592 if (fmt
->which
== V4L2_SUBDEV_FORMAT_TRY
) {
594 mf
= v4l2_subdev_get_try_format(sd
, cfg
, 0);
600 mutex_lock(&priv
->lock
);
601 if (!priv
->streaming
) {
602 priv
->curr_frmsize
= fsize
;
603 priv
->curr_pixfmt
= pf
;
604 priv
->set_params
= 1;
608 mutex_unlock(&priv
->lock
);
613 static const struct v4l2_subdev_pad_ops s5k4ecgx_pad_ops
= {
614 .enum_mbus_code
= s5k4ecgx_enum_mbus_code
,
615 .get_fmt
= s5k4ecgx_get_fmt
,
616 .set_fmt
= s5k4ecgx_set_fmt
,
620 * V4L2 subdev controls
622 static int s5k4ecgx_s_ctrl(struct v4l2_ctrl
*ctrl
)
624 struct v4l2_subdev
*sd
= &container_of(ctrl
->handler
, struct s5k4ecgx
,
626 struct i2c_client
*client
= v4l2_get_subdevdata(sd
);
627 struct s5k4ecgx
*priv
= to_s5k4ecgx(sd
);
631 v4l2_dbg(1, debug
, sd
, "ctrl: 0x%x, value: %d\n", ctrl
->id
, ctrl
->val
);
633 mutex_lock(&priv
->lock
);
635 case V4L2_CID_CONTRAST
:
636 err
= s5k4ecgx_write(client
, REG_USER_CONTRAST
, ctrl
->val
);
639 case V4L2_CID_SATURATION
:
640 err
= s5k4ecgx_write(client
, REG_USER_SATURATION
, ctrl
->val
);
643 case V4L2_CID_SHARPNESS
:
644 /* TODO: Revisit, is this setting for all presets ? */
645 for (i
= 0; i
< 4 && !err
; i
++)
646 err
= s5k4ecgx_write(client
, REG_USER_SHARPNESS(i
),
647 ctrl
->val
* SHARPNESS_DIV
);
650 case V4L2_CID_BRIGHTNESS
:
651 err
= s5k4ecgx_write(client
, REG_USER_BRIGHTNESS
, ctrl
->val
);
654 mutex_unlock(&priv
->lock
);
656 v4l2_err(sd
, "Failed to write s_ctrl err %d\n", err
);
661 static const struct v4l2_ctrl_ops s5k4ecgx_ctrl_ops
= {
662 .s_ctrl
= s5k4ecgx_s_ctrl
,
666 * Reading s5k4ecgx version information
668 static int s5k4ecgx_registered(struct v4l2_subdev
*sd
)
671 struct s5k4ecgx
*priv
= to_s5k4ecgx(sd
);
673 mutex_lock(&priv
->lock
);
674 ret
= __s5k4ecgx_power_on(priv
);
676 ret
= s5k4ecgx_read_fw_ver(sd
);
677 __s5k4ecgx_power_off(priv
);
679 mutex_unlock(&priv
->lock
);
685 * V4L2 subdev internal operations
687 static int s5k4ecgx_open(struct v4l2_subdev
*sd
, struct v4l2_subdev_fh
*fh
)
689 struct v4l2_mbus_framefmt
*mf
= v4l2_subdev_get_try_format(sd
, fh
->pad
, 0);
691 mf
->width
= s5k4ecgx_prev_sizes
[0].size
.width
;
692 mf
->height
= s5k4ecgx_prev_sizes
[0].size
.height
;
693 mf
->code
= s5k4ecgx_formats
[0].code
;
694 mf
->colorspace
= V4L2_COLORSPACE_JPEG
;
695 mf
->field
= V4L2_FIELD_NONE
;
700 static const struct v4l2_subdev_internal_ops s5k4ecgx_subdev_internal_ops
= {
701 .registered
= s5k4ecgx_registered
,
702 .open
= s5k4ecgx_open
,
705 static int s5k4ecgx_s_power(struct v4l2_subdev
*sd
, int on
)
707 struct s5k4ecgx
*priv
= to_s5k4ecgx(sd
);
710 v4l2_dbg(1, debug
, sd
, "Switching %s\n", on
? "on" : "off");
713 ret
= __s5k4ecgx_power_on(priv
);
716 /* Time to stabilize sensor */
718 ret
= s5k4ecgx_init_sensor(sd
);
720 __s5k4ecgx_power_off(priv
);
722 priv
->set_params
= 1;
724 ret
= __s5k4ecgx_power_off(priv
);
730 static int s5k4ecgx_log_status(struct v4l2_subdev
*sd
)
732 v4l2_ctrl_handler_log_status(sd
->ctrl_handler
, sd
->name
);
737 static const struct v4l2_subdev_core_ops s5k4ecgx_core_ops
= {
738 .s_power
= s5k4ecgx_s_power
,
739 .log_status
= s5k4ecgx_log_status
,
742 static int __s5k4ecgx_s_params(struct s5k4ecgx
*priv
)
744 struct i2c_client
*client
= v4l2_get_subdevdata(&priv
->sd
);
745 const struct v4l2_rect
*crop_rect
= &priv
->curr_frmsize
->input_window
;
748 ret
= s5k4ecgx_set_input_window(client
, crop_rect
);
750 ret
= s5k4ecgx_set_zoom_window(client
, crop_rect
);
752 ret
= s5k4ecgx_write(client
, REG_G_INPUTS_CHANGE_REQ
, 1);
754 ret
= s5k4ecgx_write(client
, 0x70000a1e, 0x28);
756 ret
= s5k4ecgx_write(client
, 0x70000ad4, 0x3c);
758 ret
= s5k4ecgx_set_output_framefmt(priv
);
760 ret
= s5k4ecgx_write(client
, REG_P_PVI_MASK(0), 0x52);
762 ret
= s5k4ecgx_write(client
, REG_P_FR_TIME_TYPE(0),
765 ret
= s5k4ecgx_write(client
, REG_P_FR_TIME_Q_TYPE(0),
766 FR_TIME_Q_BEST_FRRATE
);
768 ret
= s5k4ecgx_write(client
, REG_P_MIN_FR_TIME(0),
769 US_TO_FR_TIME(33300));
771 ret
= s5k4ecgx_write(client
, REG_P_MAX_FR_TIME(0),
772 US_TO_FR_TIME(66600));
774 ret
= s5k4ecgx_write(client
, REG_P_PREV_MIRROR(0), 0);
776 ret
= s5k4ecgx_write(client
, REG_P_CAP_MIRROR(0), 0);
778 ret
= s5k4ecgx_write(client
, REG_G_ACTIVE_PREV_CFG
, 0);
780 ret
= s5k4ecgx_write(client
, REG_G_PREV_OPEN_AFTER_CH
, 1);
782 ret
= s5k4ecgx_write(client
, REG_G_NEW_CFG_SYNC
, 1);
784 ret
= s5k4ecgx_write(client
, REG_G_PREV_CFG_CHG
, 1);
789 static int __s5k4ecgx_s_stream(struct s5k4ecgx
*priv
, int on
)
791 struct i2c_client
*client
= v4l2_get_subdevdata(&priv
->sd
);
794 if (on
&& priv
->set_params
) {
795 ret
= __s5k4ecgx_s_params(priv
);
798 priv
->set_params
= 0;
801 * This enables/disables preview stream only. Capture requests
802 * are not supported yet.
804 ret
= s5k4ecgx_write(client
, REG_G_ENABLE_PREV
, on
);
807 return s5k4ecgx_write(client
, REG_G_ENABLE_PREV_CHG
, 1);
810 static int s5k4ecgx_s_stream(struct v4l2_subdev
*sd
, int on
)
812 struct s5k4ecgx
*priv
= to_s5k4ecgx(sd
);
815 v4l2_dbg(1, debug
, sd
, "Turn streaming %s\n", on
? "on" : "off");
817 mutex_lock(&priv
->lock
);
819 if (priv
->streaming
== !on
) {
820 ret
= __s5k4ecgx_s_stream(priv
, on
);
822 priv
->streaming
= on
& 1;
825 mutex_unlock(&priv
->lock
);
829 static const struct v4l2_subdev_video_ops s5k4ecgx_video_ops
= {
830 .s_stream
= s5k4ecgx_s_stream
,
833 static const struct v4l2_subdev_ops s5k4ecgx_ops
= {
834 .core
= &s5k4ecgx_core_ops
,
835 .pad
= &s5k4ecgx_pad_ops
,
836 .video
= &s5k4ecgx_video_ops
,
842 static int s5k4ecgx_config_gpio(int nr
, int val
, const char *name
)
844 unsigned long flags
= val
? GPIOF_OUT_INIT_HIGH
: GPIOF_OUT_INIT_LOW
;
847 if (!gpio_is_valid(nr
))
849 ret
= gpio_request_one(nr
, flags
, name
);
856 static void s5k4ecgx_free_gpios(struct s5k4ecgx
*priv
)
860 for (i
= 0; i
< ARRAY_SIZE(priv
->gpio
); i
++) {
861 if (!gpio_is_valid(priv
->gpio
[i
].gpio
))
863 gpio_free(priv
->gpio
[i
].gpio
);
864 priv
->gpio
[i
].gpio
= -EINVAL
;
868 static int s5k4ecgx_config_gpios(struct s5k4ecgx
*priv
,
869 const struct s5k4ecgx_platform_data
*pdata
)
871 const struct s5k4ecgx_gpio
*gpio
= &pdata
->gpio_stby
;
874 priv
->gpio
[STBY
].gpio
= -EINVAL
;
875 priv
->gpio
[RST
].gpio
= -EINVAL
;
877 ret
= s5k4ecgx_config_gpio(gpio
->gpio
, gpio
->level
, "S5K4ECGX_STBY");
880 s5k4ecgx_free_gpios(priv
);
883 priv
->gpio
[STBY
] = *gpio
;
884 if (gpio_is_valid(gpio
->gpio
))
885 gpio_set_value(gpio
->gpio
, 0);
887 gpio
= &pdata
->gpio_reset
;
889 ret
= s5k4ecgx_config_gpio(gpio
->gpio
, gpio
->level
, "S5K4ECGX_RST");
891 s5k4ecgx_free_gpios(priv
);
894 priv
->gpio
[RST
] = *gpio
;
895 if (gpio_is_valid(gpio
->gpio
))
896 gpio_set_value(gpio
->gpio
, 0);
901 static int s5k4ecgx_init_v4l2_ctrls(struct s5k4ecgx
*priv
)
903 const struct v4l2_ctrl_ops
*ops
= &s5k4ecgx_ctrl_ops
;
904 struct v4l2_ctrl_handler
*hdl
= &priv
->handler
;
907 ret
= v4l2_ctrl_handler_init(hdl
, 4);
911 v4l2_ctrl_new_std(hdl
, ops
, V4L2_CID_BRIGHTNESS
, -208, 127, 1, 0);
912 v4l2_ctrl_new_std(hdl
, ops
, V4L2_CID_CONTRAST
, -127, 127, 1, 0);
913 v4l2_ctrl_new_std(hdl
, ops
, V4L2_CID_SATURATION
, -127, 127, 1, 0);
915 /* Sharpness default is 24612, and then (24612/SHARPNESS_DIV) = 2 */
916 v4l2_ctrl_new_std(hdl
, ops
, V4L2_CID_SHARPNESS
, -32704/SHARPNESS_DIV
,
917 24612/SHARPNESS_DIV
, 1, 2);
920 v4l2_ctrl_handler_free(hdl
);
923 priv
->sd
.ctrl_handler
= hdl
;
928 static int s5k4ecgx_probe(struct i2c_client
*client
,
929 const struct i2c_device_id
*id
)
931 struct s5k4ecgx_platform_data
*pdata
= client
->dev
.platform_data
;
932 struct v4l2_subdev
*sd
;
933 struct s5k4ecgx
*priv
;
937 dev_err(&client
->dev
, "platform data is missing!\n");
941 priv
= devm_kzalloc(&client
->dev
, sizeof(struct s5k4ecgx
), GFP_KERNEL
);
945 mutex_init(&priv
->lock
);
949 /* Registering subdev */
950 v4l2_i2c_subdev_init(sd
, client
, &s5k4ecgx_ops
);
951 /* Static name; NEVER use in new drivers! */
952 strscpy(sd
->name
, S5K4ECGX_DRIVER_NAME
, sizeof(sd
->name
));
954 sd
->internal_ops
= &s5k4ecgx_subdev_internal_ops
;
955 /* Support v4l2 sub-device user space API */
956 sd
->flags
|= V4L2_SUBDEV_FL_HAS_DEVNODE
;
958 priv
->pad
.flags
= MEDIA_PAD_FL_SOURCE
;
959 sd
->entity
.function
= MEDIA_ENT_F_CAM_SENSOR
;
960 ret
= media_entity_pads_init(&sd
->entity
, 1, &priv
->pad
);
964 ret
= s5k4ecgx_config_gpios(priv
, pdata
);
966 dev_err(&client
->dev
, "Failed to set gpios\n");
969 for (i
= 0; i
< S5K4ECGX_NUM_SUPPLIES
; i
++)
970 priv
->supplies
[i
].supply
= s5k4ecgx_supply_names
[i
];
972 ret
= devm_regulator_bulk_get(&client
->dev
, S5K4ECGX_NUM_SUPPLIES
,
975 dev_err(&client
->dev
, "Failed to get regulators\n");
978 ret
= s5k4ecgx_init_v4l2_ctrls(priv
);
982 priv
->curr_pixfmt
= &s5k4ecgx_formats
[0];
983 priv
->curr_frmsize
= &s5k4ecgx_prev_sizes
[0];
988 s5k4ecgx_free_gpios(priv
);
990 media_entity_cleanup(&priv
->sd
.entity
);
995 static int s5k4ecgx_remove(struct i2c_client
*client
)
997 struct v4l2_subdev
*sd
= i2c_get_clientdata(client
);
998 struct s5k4ecgx
*priv
= to_s5k4ecgx(sd
);
1000 mutex_destroy(&priv
->lock
);
1001 s5k4ecgx_free_gpios(priv
);
1002 v4l2_device_unregister_subdev(sd
);
1003 v4l2_ctrl_handler_free(&priv
->handler
);
1004 media_entity_cleanup(&sd
->entity
);
1009 static const struct i2c_device_id s5k4ecgx_id
[] = {
1010 { S5K4ECGX_DRIVER_NAME
, 0 },
1013 MODULE_DEVICE_TABLE(i2c
, s5k4ecgx_id
);
1015 static struct i2c_driver v4l2_i2c_driver
= {
1017 .name
= S5K4ECGX_DRIVER_NAME
,
1019 .probe
= s5k4ecgx_probe
,
1020 .remove
= s5k4ecgx_remove
,
1021 .id_table
= s5k4ecgx_id
,
1024 module_i2c_driver(v4l2_i2c_driver
);
1026 MODULE_DESCRIPTION("Samsung S5K4ECGX 5MP SOC camera");
1027 MODULE_AUTHOR("Sangwook Lee <sangwook.lee@linaro.org>");
1028 MODULE_AUTHOR("Seok-Young Jang <quartz.jang@samsung.com>");
1029 MODULE_LICENSE("GPL");
1030 MODULE_FIRMWARE(S5K4ECGX_FIRMWARE
);