aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSylwester Nawrocki <s.nawrocki@samsung.com>2013-03-11 14:38:29 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2013-04-04 19:14:01 -0400
commitb8d9834a1258460311a7b318cd851eb323dd63cd (patch)
tree387f867c1694c260806862a33e48a467678c568c
parent294781db46155b8a64976649567538ada91131f8 (diff)
[media] exynos4-is: Add common FIMC-IS image sensor driver
This patch adds a common image sensor driver and Makefile/Kconfig to enable compilation of the whole IS driver. The sensor subdev driver currently only handles an image sensor's power supplies and reset signal. There is no I2C communication as it is handled by the ISP's firmware. Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/media/platform/exynos4-is/Kconfig12
-rw-r--r--drivers/media/platform/exynos4-is/Makefile3
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-sensor.c322
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-sensor.h83
4 files changed, 420 insertions, 0 deletions
diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig
index ae579208565b..6ff99b5849f9 100644
--- a/drivers/media/platform/exynos4-is/Kconfig
+++ b/drivers/media/platform/exynos4-is/Kconfig
@@ -46,4 +46,16 @@ config VIDEO_EXYNOS_FIMC_LITE
46 module will be called exynos-fimc-lite. 46 module will be called exynos-fimc-lite.
47endif 47endif
48 48
49config VIDEO_EXYNOS4_FIMC_IS
50 tristate "EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver"
51 select VIDEOBUF2_DMA_CONTIG
52 depends on OF
53 select FW_LOADER
54 help
55 This is a V4L2 driver for Samsung EXYNOS4x12 SoC series
56 FIMC-IS (Imaging Subsystem).
57
58 To compile this driver as a module, choose M here: the
59 module will be called exynos4-fimc-is.
60
49endif # VIDEO_SAMSUNG_S5P_FIMC 61endif # VIDEO_SAMSUNG_S5P_FIMC
diff --git a/drivers/media/platform/exynos4-is/Makefile b/drivers/media/platform/exynos4-is/Makefile
index 8c67441558f7..f25f46377399 100644
--- a/drivers/media/platform/exynos4-is/Makefile
+++ b/drivers/media/platform/exynos4-is/Makefile
@@ -1,7 +1,10 @@
1s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o media-dev.o 1s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o media-dev.o
2exynos-fimc-lite-objs += fimc-lite-reg.o fimc-lite.o 2exynos-fimc-lite-objs += fimc-lite-reg.o fimc-lite.o
3exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o
4exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o
3s5p-csis-objs := mipi-csis.o 5s5p-csis-objs := mipi-csis.o
4 6
5obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o 7obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o
6obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o 8obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o
9obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS) += exynos-fimc-is.o
7obj-$(CONFIG_VIDEO_S5P_FIMC) += s5p-fimc.o 10obj-$(CONFIG_VIDEO_S5P_FIMC) += s5p-fimc.o
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.c b/drivers/media/platform/exynos4-is/fimc-is-sensor.c
new file mode 100644
index 000000000000..02b27190d2ae
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.c
@@ -0,0 +1,322 @@
1/*
2 * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
3 *
4 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
5 *
6 * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#include <linux/delay.h>
13#include <linux/device.h>
14#include <linux/errno.h>
15#include <linux/gpio.h>
16#include <linux/i2c.h>
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/of_gpio.h>
20#include <linux/pm_runtime.h>
21#include <linux/regulator/consumer.h>
22#include <linux/slab.h>
23#include <media/v4l2-subdev.h>
24
25#include "fimc-is.h"
26#include "fimc-is-sensor.h"
27
28#define DRIVER_NAME "FIMC-IS-SENSOR"
29
30static const char * const sensor_supply_names[] = {
31 "svdda",
32 "svddio",
33};
34
35static const struct v4l2_mbus_framefmt fimc_is_sensor_formats[] = {
36 {
37 .code = V4L2_MBUS_FMT_SGRBG10_1X10,
38 .colorspace = V4L2_COLORSPACE_SRGB,
39 .field = V4L2_FIELD_NONE,
40 }
41};
42
43static struct fimc_is_sensor *sd_to_fimc_is_sensor(struct v4l2_subdev *sd)
44{
45 return container_of(sd, struct fimc_is_sensor, subdev);
46}
47
48static const struct v4l2_mbus_framefmt *find_sensor_format(
49 struct v4l2_mbus_framefmt *mf)
50{
51 int i;
52
53 for (i = 0; i < ARRAY_SIZE(fimc_is_sensor_formats); i++)
54 if (mf->code == fimc_is_sensor_formats[i].code)
55 return &fimc_is_sensor_formats[i];
56
57 return &fimc_is_sensor_formats[0];
58}
59
60static int fimc_is_sensor_enum_mbus_code(struct v4l2_subdev *sd,
61 struct v4l2_subdev_fh *fh,
62 struct v4l2_subdev_mbus_code_enum *code)
63{
64 if (code->index >= ARRAY_SIZE(fimc_is_sensor_formats))
65 return -EINVAL;
66
67 code->code = fimc_is_sensor_formats[code->index].code;
68 return 0;
69}
70
71static void fimc_is_sensor_try_format(struct fimc_is_sensor *sensor,
72 struct v4l2_mbus_framefmt *mf)
73{
74 const struct sensor_drv_data *dd = sensor->drvdata;
75 const struct v4l2_mbus_framefmt *fmt;
76
77 fmt = find_sensor_format(mf);
78 mf->code = fmt->code;
79 v4l_bound_align_image(&mf->width, 16 + 8, dd->width, 0,
80 &mf->height, 12 + 8, dd->height, 0, 0);
81}
82
83static struct v4l2_mbus_framefmt *__fimc_is_sensor_get_format(
84 struct fimc_is_sensor *sensor, struct v4l2_subdev_fh *fh,
85 u32 pad, enum v4l2_subdev_format_whence which)
86{
87 if (which == V4L2_SUBDEV_FORMAT_TRY)
88 return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
89
90 return &sensor->format;
91}
92
93static int fimc_is_sensor_set_fmt(struct v4l2_subdev *sd,
94 struct v4l2_subdev_fh *fh,
95 struct v4l2_subdev_format *fmt)
96{
97 struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
98 struct v4l2_mbus_framefmt *mf;
99
100 fimc_is_sensor_try_format(sensor, &fmt->format);
101
102 mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which);
103 if (mf) {
104 mutex_lock(&sensor->lock);
105 if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
106 *mf = fmt->format;
107 mutex_unlock(&sensor->lock);
108 }
109 return 0;
110}
111
112static int fimc_is_sensor_get_fmt(struct v4l2_subdev *sd,
113 struct v4l2_subdev_fh *fh,
114 struct v4l2_subdev_format *fmt)
115{
116 struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
117 struct v4l2_mbus_framefmt *mf;
118
119 mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which);
120
121 mutex_lock(&sensor->lock);
122 fmt->format = *mf;
123 mutex_unlock(&sensor->lock);
124 return 0;
125}
126
127static struct v4l2_subdev_pad_ops fimc_is_sensor_pad_ops = {
128 .enum_mbus_code = fimc_is_sensor_enum_mbus_code,
129 .get_fmt = fimc_is_sensor_get_fmt,
130 .set_fmt = fimc_is_sensor_set_fmt,
131};
132
133static int fimc_is_sensor_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
134{
135 struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
136
137 *format = fimc_is_sensor_formats[0];
138 format->width = FIMC_IS_SENSOR_DEF_PIX_WIDTH;
139 format->height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT;
140
141 return 0;
142}
143
144static const struct v4l2_subdev_internal_ops fimc_is_sensor_sd_internal_ops = {
145 .open = fimc_is_sensor_open,
146};
147
148static int fimc_is_sensor_s_power(struct v4l2_subdev *sd, int on)
149{
150 struct fimc_is_sensor *sensor = v4l2_get_subdevdata(sd);
151 int gpio = sensor->gpio_reset;
152 int ret;
153
154 if (on) {
155 ret = pm_runtime_get(sensor->dev);
156 if (ret < 0)
157 return ret;
158
159 ret = regulator_bulk_enable(SENSOR_NUM_SUPPLIES,
160 sensor->supplies);
161 if (ret < 0) {
162 pm_runtime_put(sensor->dev);
163 return ret;
164 }
165 if (gpio_is_valid(gpio)) {
166 gpio_set_value(gpio, 1);
167 usleep_range(600, 800);
168 gpio_set_value(gpio, 0);
169 usleep_range(10000, 11000);
170 gpio_set_value(gpio, 1);
171 }
172
173 /* A delay needed for the sensor initialization. */
174 msleep(20);
175 } else {
176 if (gpio_is_valid(gpio))
177 gpio_set_value(gpio, 0);
178
179 ret = regulator_bulk_disable(SENSOR_NUM_SUPPLIES,
180 sensor->supplies);
181 if (!ret)
182 pm_runtime_put(sensor->dev);
183 }
184
185 pr_info("%s:%d: on: %d, ret: %d\n", __func__, __LINE__, on, ret);
186
187 return ret;
188}
189
190static struct v4l2_subdev_core_ops fimc_is_sensor_core_ops = {
191 .s_power = fimc_is_sensor_s_power,
192};
193
194static struct v4l2_subdev_ops fimc_is_sensor_subdev_ops = {
195 .core = &fimc_is_sensor_core_ops,
196 .pad = &fimc_is_sensor_pad_ops,
197};
198
199static const struct of_device_id fimc_is_sensor_of_match[];
200
201static int fimc_is_sensor_probe(struct i2c_client *client,
202 const struct i2c_device_id *id)
203{
204 struct device *dev = &client->dev;
205 struct fimc_is_sensor *sensor;
206 const struct of_device_id *of_id;
207 struct v4l2_subdev *sd;
208 int gpio, i, ret;
209
210 sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
211 if (!sensor)
212 return -ENOMEM;
213
214 mutex_init(&sensor->lock);
215 sensor->gpio_reset = -EINVAL;
216
217 gpio = of_get_gpio_flags(dev->of_node, 0, NULL);
218 if (gpio_is_valid(gpio)) {
219 ret = gpio_request_one(gpio, GPIOF_OUT_INIT_LOW, DRIVER_NAME);
220 if (ret < 0)
221 return ret;
222 }
223 sensor->gpio_reset = gpio;
224
225 for (i = 0; i < SENSOR_NUM_SUPPLIES; i++)
226 sensor->supplies[i].supply = sensor_supply_names[i];
227
228 ret = devm_regulator_bulk_get(&client->dev, SENSOR_NUM_SUPPLIES,
229 sensor->supplies);
230 if (ret < 0)
231 goto err_gpio;
232
233 of_id = of_match_node(fimc_is_sensor_of_match, dev->of_node);
234 if (!of_id) {
235 ret = -ENODEV;
236 goto err_reg;
237 }
238
239 sensor->drvdata = of_id->data;
240 sensor->dev = dev;
241
242 sd = &sensor->subdev;
243 v4l2_i2c_subdev_init(sd, client, &fimc_is_sensor_subdev_ops);
244 snprintf(sd->name, sizeof(sd->name), sensor->drvdata->subdev_name);
245 sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
246
247 sensor->format.code = fimc_is_sensor_formats[0].code;
248 sensor->format.width = FIMC_IS_SENSOR_DEF_PIX_WIDTH;
249 sensor->format.height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT;
250
251 sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
252 ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0);
253 if (ret < 0)
254 goto err_reg;
255
256 v4l2_set_subdevdata(sd, sensor);
257 pm_runtime_no_callbacks(dev);
258 pm_runtime_enable(dev);
259
260 return 0;
261err_reg:
262 regulator_bulk_free(SENSOR_NUM_SUPPLIES, sensor->supplies);
263err_gpio:
264 if (gpio_is_valid(sensor->gpio_reset))
265 gpio_free(sensor->gpio_reset);
266 return ret;
267}
268
269static int fimc_is_sensor_remove(struct i2c_client *client)
270{
271 struct fimc_is_sensor *sensor;
272
273 regulator_bulk_free(SENSOR_NUM_SUPPLIES, sensor->supplies);
274 media_entity_cleanup(&sensor->subdev.entity);
275
276 return 0;
277}
278
279static const struct i2c_device_id fimc_is_sensor_ids[] = {
280 { }
281};
282
283static const struct sensor_drv_data s5k6a3_drvdata = {
284 .id = FIMC_IS_SENSOR_ID_S5K6A3,
285 .subdev_name = "S5K6A3",
286 .width = S5K6A3_SENSOR_WIDTH,
287 .height = S5K6A3_SENSOR_HEIGHT,
288};
289
290static const struct of_device_id fimc_is_sensor_of_match[] = {
291 {
292 .compatible = "samsung,s5k6a3",
293 .data = &s5k6a3_drvdata,
294 },
295 { }
296};
297MODULE_DEVICE_TABLE(of, fimc_is_sensor_of_match);
298
299static struct i2c_driver fimc_is_sensor_driver = {
300 .driver = {
301 .of_match_table = fimc_is_sensor_of_match,
302 .name = DRIVER_NAME,
303 .owner = THIS_MODULE,
304 },
305 .probe = fimc_is_sensor_probe,
306 .remove = fimc_is_sensor_remove,
307 .id_table = fimc_is_sensor_ids,
308};
309
310int fimc_is_register_sensor_driver(void)
311{
312 return i2c_add_driver(&fimc_is_sensor_driver);
313}
314
315void fimc_is_unregister_sensor_driver(void)
316{
317 i2c_del_driver(&fimc_is_sensor_driver);
318}
319
320MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
321MODULE_DESCRIPTION("Exynos4x12 FIMC-IS image sensor subdev driver");
322MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.h b/drivers/media/platform/exynos4-is/fimc-is-sensor.h
new file mode 100644
index 000000000000..50b8e4d59d62
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.h
@@ -0,0 +1,83 @@
1/*
2 * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
3 *
4 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
5 *
6 * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com>
7 * Younghwan Joo <yhwan.joo@samsung.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13#ifndef FIMC_IS_SENSOR_H_
14#define FIMC_IS_SENSOR_H_
15
16#include <linux/clk.h>
17#include <linux/device.h>
18#include <linux/kernel.h>
19#include <linux/platform_device.h>
20#include <linux/regulator/consumer.h>
21#include <linux/videodev2.h>
22#include <media/v4l2-subdev.h>
23
24#define FIMC_IS_SENSOR_OPEN_TIMEOUT 2000 /* ms */
25
26#define FIMC_IS_SENSOR_DEF_PIX_WIDTH 1296
27#define FIMC_IS_SENSOR_DEF_PIX_HEIGHT 732
28
29#define S5K6A3_SENSOR_WIDTH 1392
30#define S5K6A3_SENSOR_HEIGHT 1392
31
32#define SENSOR_NUM_SUPPLIES 2
33
34enum fimc_is_sensor_id {
35 FIMC_IS_SENSOR_ID_S5K3H2 = 1,
36 FIMC_IS_SENSOR_ID_S5K6A3,
37 FIMC_IS_SENSOR_ID_S5K4E5,
38 FIMC_IS_SENSOR_ID_S5K3H7,
39 FIMC_IS_SENSOR_ID_CUSTOM,
40 FIMC_IS_SENSOR_ID_END
41};
42
43#define IS_SENSOR_CTRL_BUS_I2C0 0
44#define IS_SENSOR_CTRL_BUS_I2C1 1
45
46struct sensor_drv_data {
47 enum fimc_is_sensor_id id;
48 const char * const subdev_name;
49 unsigned int width;
50 unsigned int height;
51};
52
53/**
54 * struct fimc_is_sensor - fimc-is sensor data structure
55 * @dev: pointer to this I2C client device structure
56 * @subdev: the image sensor's v4l2 subdev
57 * @pad: subdev media source pad
58 * @supplies: image sensor's voltage regulator supplies
59 * @gpio_reset: GPIO connected to the sensor's reset pin
60 * @drvdata: a pointer to the sensor's parameters data structure
61 * @i2c_bus: ISP I2C bus index (0...1)
62 * @test_pattern: true to enable video test pattern
63 * @lock: mutex protecting the structure's members below
64 * @format: media bus format at the sensor's source pad
65 */
66struct fimc_is_sensor {
67 struct device *dev;
68 struct v4l2_subdev subdev;
69 struct media_pad pad;
70 struct regulator_bulk_data supplies[SENSOR_NUM_SUPPLIES];
71 int gpio_reset;
72 const struct sensor_drv_data *drvdata;
73 unsigned int i2c_bus;
74 bool test_pattern;
75
76 struct mutex lock;
77 struct v4l2_mbus_framefmt format;
78};
79
80int fimc_is_register_sensor_driver(void);
81void fimc_is_unregister_sensor_driver(void);
82
83#endif /* FIMC_IS_SENSOR_H_ */