aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorSylwester Nawrocki <s.nawrocki@samsung.com>2013-08-09 14:56:00 -0400
committerMauro Carvalho Chehab <m.chehab@samsung.com>2014-03-14 09:32:17 -0400
commit814b4dd9aa4734f33ccf0e13d872391eaaa72762 (patch)
treeef0688e0b3a6a6217e9990c2295f8ccaa697ee88 /drivers/media
parent53f8a899a38d8e8a02ce390d400327f6abd66074 (diff)
[media] V4L: Add driver for s5k6a3 image sensor
This patch adds subdev driver for Samsung S5K6A3 raw image sensor. As it is intended at the moment to be used only with the Exynos FIMC-IS (camera ISP) subsystem it is pretty minimal subdev driver. It doesn't do any I2C communication since the sensor is controlled by the ISP and its own firmware. This driver, if needed, can be updated in future into a regular subdev driver where the main CPU communicates with the sensor directly. Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Acked-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/i2c/Kconfig8
-rw-r--r--drivers/media/i2c/Makefile1
-rw-r--r--drivers/media/i2c/s5k6a3.c389
3 files changed, 398 insertions, 0 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 194caba3ea83..4cb85ce0a66a 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -579,6 +579,14 @@ config VIDEO_S5K6AA
579 This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M 579 This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
580 camera sensor with an embedded SoC image signal processor. 580 camera sensor with an embedded SoC image signal processor.
581 581
582config VIDEO_S5K6A3
583 tristate "Samsung S5K6A3 sensor support"
584 depends on MEDIA_CAMERA_SUPPORT
585 depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
586 ---help---
587 This is a V4L2 sensor-level driver for Samsung S5K6A3 raw
588 camera sensor.
589
582config VIDEO_S5K4ECGX 590config VIDEO_S5K4ECGX
583 tristate "Samsung S5K4ECGX sensor support" 591 tristate "Samsung S5K4ECGX sensor support"
584 depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API 592 depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 01b6bfc0db5b..01ae9328e582 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
66obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o 66obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
67obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o 67obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
68obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o 68obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o
69obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o
69obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o 70obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o
70obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o 71obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o
71obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ 72obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/
diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c
new file mode 100644
index 000000000000..7bc2271ca009
--- /dev/null
+++ b/drivers/media/i2c/s5k6a3.c
@@ -0,0 +1,389 @@
1/*
2 * Samsung S5K6A3 image sensor driver
3 *
4 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
5 * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/clk.h>
13#include <linux/delay.h>
14#include <linux/device.h>
15#include <linux/errno.h>
16#include <linux/gpio.h>
17#include <linux/i2c.h>
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/of_gpio.h>
21#include <linux/pm_runtime.h>
22#include <linux/regulator/consumer.h>
23#include <linux/slab.h>
24#include <linux/videodev2.h>
25#include <media/v4l2-async.h>
26#include <media/v4l2-subdev.h>
27
28#define S5K6A3_SENSOR_MAX_WIDTH 1412
29#define S5K6A3_SENSOR_MAX_HEIGHT 1412
30#define S5K6A3_SENSOR_MIN_WIDTH 32
31#define S5K6A3_SENSOR_MIN_HEIGHT 32
32
33#define S5K6A3_DEFAULT_WIDTH 1296
34#define S5K6A3_DEFAULT_HEIGHT 732
35
36#define S5K6A3_DRV_NAME "S5K6A3"
37#define S5K6A3_CLK_NAME "extclk"
38#define S5K6A3_DEFAULT_CLK_FREQ 24000000U
39
40enum {
41 S5K6A3_SUPP_VDDA,
42 S5K6A3_SUPP_VDDIO,
43 S5K6A3_SUPP_AFVDD,
44 S5K6A3_NUM_SUPPLIES,
45};
46
47/**
48 * struct s5k6a3 - fimc-is sensor data structure
49 * @dev: pointer to this I2C client device structure
50 * @subdev: the image sensor's v4l2 subdev
51 * @pad: subdev media source pad
52 * @supplies: image sensor's voltage regulator supplies
53 * @gpio_reset: GPIO connected to the sensor's reset pin
54 * @lock: mutex protecting the structure's members below
55 * @format: media bus format at the sensor's source pad
56 */
57struct s5k6a3 {
58 struct device *dev;
59 struct v4l2_subdev subdev;
60 struct media_pad pad;
61 struct regulator_bulk_data supplies[S5K6A3_NUM_SUPPLIES];
62 int gpio_reset;
63 struct mutex lock;
64 struct v4l2_mbus_framefmt format;
65 struct clk *clock;
66 u32 clock_frequency;
67 int power_count;
68};
69
70static const char * const s5k6a3_supply_names[] = {
71 [S5K6A3_SUPP_VDDA] = "svdda",
72 [S5K6A3_SUPP_VDDIO] = "svddio",
73 [S5K6A3_SUPP_AFVDD] = "afvdd",
74};
75
76static inline struct s5k6a3 *sd_to_s5k6a3(struct v4l2_subdev *sd)
77{
78 return container_of(sd, struct s5k6a3, subdev);
79}
80
81static const struct v4l2_mbus_framefmt s5k6a3_formats[] = {
82 {
83 .code = V4L2_MBUS_FMT_SGRBG10_1X10,
84 .colorspace = V4L2_COLORSPACE_SRGB,
85 .field = V4L2_FIELD_NONE,
86 }
87};
88
89static const struct v4l2_mbus_framefmt *find_sensor_format(
90 struct v4l2_mbus_framefmt *mf)
91{
92 int i;
93
94 for (i = 0; i < ARRAY_SIZE(s5k6a3_formats); i++)
95 if (mf->code == s5k6a3_formats[i].code)
96 return &s5k6a3_formats[i];
97
98 return &s5k6a3_formats[0];
99}
100
101static int s5k6a3_enum_mbus_code(struct v4l2_subdev *sd,
102 struct v4l2_subdev_fh *fh,
103 struct v4l2_subdev_mbus_code_enum *code)
104{
105 if (code->index >= ARRAY_SIZE(s5k6a3_formats))
106 return -EINVAL;
107
108 code->code = s5k6a3_formats[code->index].code;
109 return 0;
110}
111
112static void s5k6a3_try_format(struct v4l2_mbus_framefmt *mf)
113{
114 const struct v4l2_mbus_framefmt *fmt;
115
116 fmt = find_sensor_format(mf);
117 mf->code = fmt->code;
118 v4l_bound_align_image(&mf->width, S5K6A3_SENSOR_MIN_WIDTH,
119 S5K6A3_SENSOR_MAX_WIDTH, 0,
120 &mf->height, S5K6A3_SENSOR_MIN_HEIGHT,
121 S5K6A3_SENSOR_MAX_HEIGHT, 0, 0);
122}
123
124static struct v4l2_mbus_framefmt *__s5k6a3_get_format(
125 struct s5k6a3 *sensor, struct v4l2_subdev_fh *fh,
126 u32 pad, enum v4l2_subdev_format_whence which)
127{
128 if (which == V4L2_SUBDEV_FORMAT_TRY)
129 return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
130
131 return &sensor->format;
132}
133
134static int s5k6a3_set_fmt(struct v4l2_subdev *sd,
135 struct v4l2_subdev_fh *fh,
136 struct v4l2_subdev_format *fmt)
137{
138 struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
139 struct v4l2_mbus_framefmt *mf;
140
141 s5k6a3_try_format(&fmt->format);
142
143 mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which);
144 if (mf) {
145 mutex_lock(&sensor->lock);
146 if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
147 *mf = fmt->format;
148 mutex_unlock(&sensor->lock);
149 }
150 return 0;
151}
152
153static int s5k6a3_get_fmt(struct v4l2_subdev *sd,
154 struct v4l2_subdev_fh *fh,
155 struct v4l2_subdev_format *fmt)
156{
157 struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
158 struct v4l2_mbus_framefmt *mf;
159
160 mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which);
161
162 mutex_lock(&sensor->lock);
163 fmt->format = *mf;
164 mutex_unlock(&sensor->lock);
165 return 0;
166}
167
168static struct v4l2_subdev_pad_ops s5k6a3_pad_ops = {
169 .enum_mbus_code = s5k6a3_enum_mbus_code,
170 .get_fmt = s5k6a3_get_fmt,
171 .set_fmt = s5k6a3_set_fmt,
172};
173
174static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
175{
176 struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
177
178 *format = s5k6a3_formats[0];
179 format->width = S5K6A3_DEFAULT_WIDTH;
180 format->height = S5K6A3_DEFAULT_HEIGHT;
181
182 return 0;
183}
184
185static const struct v4l2_subdev_internal_ops s5k6a3_sd_internal_ops = {
186 .open = s5k6a3_open,
187};
188
189static int __s5k6a3_power_on(struct s5k6a3 *sensor)
190{
191 int i = S5K6A3_SUPP_VDDA;
192 int ret;
193
194 ret = clk_set_rate(sensor->clock, sensor->clock_frequency);
195 if (ret < 0)
196 return ret;
197
198 ret = pm_runtime_get(sensor->dev);
199 if (ret < 0)
200 return ret;
201
202 ret = regulator_enable(sensor->supplies[i].consumer);
203 if (ret < 0)
204 goto error_rpm_put;
205
206 ret = clk_prepare_enable(sensor->clock);
207 if (ret < 0)
208 goto error_reg_dis;
209
210 for (i++; i < S5K6A3_NUM_SUPPLIES; i++) {
211 ret = regulator_enable(sensor->supplies[i].consumer);
212 if (ret < 0)
213 goto error_reg_dis;
214 }
215
216 gpio_set_value(sensor->gpio_reset, 1);
217 usleep_range(600, 800);
218 gpio_set_value(sensor->gpio_reset, 0);
219 usleep_range(600, 800);
220 gpio_set_value(sensor->gpio_reset, 1);
221
222 /* Delay needed for the sensor initialization */
223 msleep(20);
224 return 0;
225
226error_reg_dis:
227 for (--i; i >= 0; --i)
228 regulator_disable(sensor->supplies[i].consumer);
229error_rpm_put:
230 pm_runtime_put(sensor->dev);
231 return ret;
232}
233
234static int __s5k6a3_power_off(struct s5k6a3 *sensor)
235{
236 int i;
237
238 gpio_set_value(sensor->gpio_reset, 0);
239
240 for (i = S5K6A3_NUM_SUPPLIES - 1; i >= 0; i--)
241 regulator_disable(sensor->supplies[i].consumer);
242
243 clk_disable_unprepare(sensor->clock);
244 pm_runtime_put(sensor->dev);
245 return 0;
246}
247
248static int s5k6a3_s_power(struct v4l2_subdev *sd, int on)
249{
250 struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
251 int ret = 0;
252
253 mutex_lock(&sensor->lock);
254
255 if (sensor->power_count == !on) {
256 if (on)
257 ret = __s5k6a3_power_on(sensor);
258 else
259 ret = __s5k6a3_power_off(sensor);
260
261 if (ret == 0)
262 sensor->power_count += on ? 1 : -1;
263 }
264
265 mutex_unlock(&sensor->lock);
266 return ret;
267}
268
269static struct v4l2_subdev_core_ops s5k6a3_core_ops = {
270 .s_power = s5k6a3_s_power,
271};
272
273static struct v4l2_subdev_ops s5k6a3_subdev_ops = {
274 .core = &s5k6a3_core_ops,
275 .pad = &s5k6a3_pad_ops,
276};
277
278static int s5k6a3_probe(struct i2c_client *client,
279 const struct i2c_device_id *id)
280{
281 struct device *dev = &client->dev;
282 struct s5k6a3 *sensor;
283 struct v4l2_subdev *sd;
284 int gpio, i, ret;
285
286 sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
287 if (!sensor)
288 return -ENOMEM;
289
290 mutex_init(&sensor->lock);
291 sensor->gpio_reset = -EINVAL;
292 sensor->clock = ERR_PTR(-EINVAL);
293 sensor->dev = dev;
294
295 sensor->clock = devm_clk_get(sensor->dev, S5K6A3_CLK_NAME);
296 if (IS_ERR(sensor->clock))
297 return PTR_ERR(sensor->clock);
298
299 gpio = of_get_gpio_flags(dev->of_node, 0, NULL);
300 if (!gpio_is_valid(gpio))
301 return gpio;
302
303 ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW,
304 S5K6A3_DRV_NAME);
305 if (ret < 0)
306 return ret;
307
308 sensor->gpio_reset = gpio;
309
310 if (of_property_read_u32(dev->of_node, "clock-frequency",
311 &sensor->clock_frequency)) {
312 sensor->clock_frequency = S5K6A3_DEFAULT_CLK_FREQ;
313 dev_info(dev, "using default %u Hz clock frequency\n",
314 sensor->clock_frequency);
315 }
316
317 for (i = 0; i < S5K6A3_NUM_SUPPLIES; i++)
318 sensor->supplies[i].supply = s5k6a3_supply_names[i];
319
320 ret = devm_regulator_bulk_get(&client->dev, S5K6A3_NUM_SUPPLIES,
321 sensor->supplies);
322 if (ret < 0)
323 return ret;
324
325 sd = &sensor->subdev;
326 v4l2_i2c_subdev_init(sd, client, &s5k6a3_subdev_ops);
327 sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
328 sd->internal_ops = &s5k6a3_sd_internal_ops;
329
330 sensor->format.code = s5k6a3_formats[0].code;
331 sensor->format.width = S5K6A3_DEFAULT_WIDTH;
332 sensor->format.height = S5K6A3_DEFAULT_HEIGHT;
333
334 sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
335 ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0);
336 if (ret < 0)
337 return ret;
338
339 pm_runtime_no_callbacks(dev);
340 pm_runtime_enable(dev);
341
342 ret = v4l2_async_register_subdev(sd);
343
344 if (ret < 0) {
345 pm_runtime_disable(&client->dev);
346 media_entity_cleanup(&sd->entity);
347 }
348
349 return ret;
350}
351
352static int s5k6a3_remove(struct i2c_client *client)
353{
354 struct v4l2_subdev *sd = i2c_get_clientdata(client);
355
356 pm_runtime_disable(&client->dev);
357 v4l2_async_unregister_subdev(sd);
358 media_entity_cleanup(&sd->entity);
359 return 0;
360}
361
362static const struct i2c_device_id s5k6a3_ids[] = {
363 { }
364};
365
366#ifdef CONFIG_OF
367static const struct of_device_id s5k6a3_of_match[] = {
368 { .compatible = "samsung,s5k6a3" },
369 { /* sentinel */ }
370};
371MODULE_DEVICE_TABLE(of, s5k6a3_of_match);
372#endif
373
374static struct i2c_driver s5k6a3_driver = {
375 .driver = {
376 .of_match_table = of_match_ptr(s5k6a3_of_match),
377 .name = S5K6A3_DRV_NAME,
378 .owner = THIS_MODULE,
379 },
380 .probe = s5k6a3_probe,
381 .remove = s5k6a3_remove,
382 .id_table = s5k6a3_ids,
383};
384
385module_i2c_driver(s5k6a3_driver);
386
387MODULE_DESCRIPTION("S5K6A3 image sensor subdev driver");
388MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
389MODULE_LICENSE("GPL v2");