|
2 | 2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
|
3 | 3 | *
|
4 | 4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd.
|
5 |
| - * |
6 | 5 | * Author: Sylwester Nawrocki <[email protected]>
|
7 | 6 | *
|
8 | 7 | * This program is free software; you can redistribute it and/or modify
|
9 | 8 | * it under the terms of the GNU General Public License version 2 as
|
10 | 9 | * published by the Free Software Foundation.
|
11 | 10 | */
|
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 | 11 |
|
25 |
| -#include "fimc-is.h" |
26 | 12 | #include "fimc-is-sensor.h"
|
27 | 13 |
|
28 |
| -#define DRIVER_NAME "FIMC-IS-SENSOR" |
29 |
| - |
30 |
| -static const char * const sensor_supply_names[] = { |
31 |
| - "svdda", |
32 |
| - "svddio", |
33 |
| -}; |
34 |
| - |
35 |
| -static 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 |
| - |
43 |
| -static const struct v4l2_mbus_framefmt *find_sensor_format( |
44 |
| - struct v4l2_mbus_framefmt *mf) |
45 |
| -{ |
46 |
| - int i; |
47 |
| - |
48 |
| - for (i = 0; i < ARRAY_SIZE(fimc_is_sensor_formats); i++) |
49 |
| - if (mf->code == fimc_is_sensor_formats[i].code) |
50 |
| - return &fimc_is_sensor_formats[i]; |
51 |
| - |
52 |
| - return &fimc_is_sensor_formats[0]; |
53 |
| -} |
54 |
| - |
55 |
| -static int fimc_is_sensor_enum_mbus_code(struct v4l2_subdev *sd, |
56 |
| - struct v4l2_subdev_fh *fh, |
57 |
| - struct v4l2_subdev_mbus_code_enum *code) |
58 |
| -{ |
59 |
| - if (code->index >= ARRAY_SIZE(fimc_is_sensor_formats)) |
60 |
| - return -EINVAL; |
61 |
| - |
62 |
| - code->code = fimc_is_sensor_formats[code->index].code; |
63 |
| - return 0; |
64 |
| -} |
65 |
| - |
66 |
| -static void fimc_is_sensor_try_format(struct fimc_is_sensor *sensor, |
67 |
| - struct v4l2_mbus_framefmt *mf) |
68 |
| -{ |
69 |
| - const struct sensor_drv_data *dd = sensor->drvdata; |
70 |
| - const struct v4l2_mbus_framefmt *fmt; |
71 |
| - |
72 |
| - fmt = find_sensor_format(mf); |
73 |
| - mf->code = fmt->code; |
74 |
| - v4l_bound_align_image(&mf->width, 16 + 8, dd->width, 0, |
75 |
| - &mf->height, 12 + 8, dd->height, 0, 0); |
76 |
| -} |
77 |
| - |
78 |
| -static struct v4l2_mbus_framefmt *__fimc_is_sensor_get_format( |
79 |
| - struct fimc_is_sensor *sensor, struct v4l2_subdev_fh *fh, |
80 |
| - u32 pad, enum v4l2_subdev_format_whence which) |
81 |
| -{ |
82 |
| - if (which == V4L2_SUBDEV_FORMAT_TRY) |
83 |
| - return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL; |
84 |
| - |
85 |
| - return &sensor->format; |
86 |
| -} |
87 |
| - |
88 |
| -static int fimc_is_sensor_set_fmt(struct v4l2_subdev *sd, |
89 |
| - struct v4l2_subdev_fh *fh, |
90 |
| - struct v4l2_subdev_format *fmt) |
91 |
| -{ |
92 |
| - struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); |
93 |
| - struct v4l2_mbus_framefmt *mf; |
94 |
| - |
95 |
| - fimc_is_sensor_try_format(sensor, &fmt->format); |
96 |
| - |
97 |
| - mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which); |
98 |
| - if (mf) { |
99 |
| - mutex_lock(&sensor->lock); |
100 |
| - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) |
101 |
| - *mf = fmt->format; |
102 |
| - mutex_unlock(&sensor->lock); |
103 |
| - } |
104 |
| - return 0; |
105 |
| -} |
106 |
| - |
107 |
| -static int fimc_is_sensor_get_fmt(struct v4l2_subdev *sd, |
108 |
| - struct v4l2_subdev_fh *fh, |
109 |
| - struct v4l2_subdev_format *fmt) |
110 |
| -{ |
111 |
| - struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); |
112 |
| - struct v4l2_mbus_framefmt *mf; |
113 |
| - |
114 |
| - mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which); |
115 |
| - |
116 |
| - mutex_lock(&sensor->lock); |
117 |
| - fmt->format = *mf; |
118 |
| - mutex_unlock(&sensor->lock); |
119 |
| - return 0; |
120 |
| -} |
121 |
| - |
122 |
| -static struct v4l2_subdev_pad_ops fimc_is_sensor_pad_ops = { |
123 |
| - .enum_mbus_code = fimc_is_sensor_enum_mbus_code, |
124 |
| - .get_fmt = fimc_is_sensor_get_fmt, |
125 |
| - .set_fmt = fimc_is_sensor_set_fmt, |
126 |
| -}; |
127 |
| - |
128 |
| -static int fimc_is_sensor_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) |
129 |
| -{ |
130 |
| - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); |
131 |
| - |
132 |
| - *format = fimc_is_sensor_formats[0]; |
133 |
| - format->width = FIMC_IS_SENSOR_DEF_PIX_WIDTH; |
134 |
| - format->height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT; |
135 |
| - |
136 |
| - return 0; |
137 |
| -} |
138 |
| - |
139 |
| -static const struct v4l2_subdev_internal_ops fimc_is_sensor_sd_internal_ops = { |
140 |
| - .open = fimc_is_sensor_open, |
141 |
| -}; |
142 |
| - |
143 |
| -static int fimc_is_sensor_s_power(struct v4l2_subdev *sd, int on) |
144 |
| -{ |
145 |
| - struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); |
146 |
| - int gpio = sensor->gpio_reset; |
147 |
| - int ret; |
148 |
| - |
149 |
| - if (on) { |
150 |
| - ret = pm_runtime_get(sensor->dev); |
151 |
| - if (ret < 0) |
152 |
| - return ret; |
153 |
| - |
154 |
| - ret = regulator_bulk_enable(SENSOR_NUM_SUPPLIES, |
155 |
| - sensor->supplies); |
156 |
| - if (ret < 0) { |
157 |
| - pm_runtime_put(sensor->dev); |
158 |
| - return ret; |
159 |
| - } |
160 |
| - if (gpio_is_valid(gpio)) { |
161 |
| - gpio_set_value(gpio, 1); |
162 |
| - usleep_range(600, 800); |
163 |
| - gpio_set_value(gpio, 0); |
164 |
| - usleep_range(10000, 11000); |
165 |
| - gpio_set_value(gpio, 1); |
166 |
| - } |
167 |
| - |
168 |
| - /* A delay needed for the sensor initialization. */ |
169 |
| - msleep(20); |
170 |
| - } else { |
171 |
| - if (gpio_is_valid(gpio)) |
172 |
| - gpio_set_value(gpio, 0); |
173 |
| - |
174 |
| - ret = regulator_bulk_disable(SENSOR_NUM_SUPPLIES, |
175 |
| - sensor->supplies); |
176 |
| - if (!ret) |
177 |
| - pm_runtime_put(sensor->dev); |
178 |
| - } |
179 |
| - |
180 |
| - pr_info("%s:%d: on: %d, ret: %d\n", __func__, __LINE__, on, ret); |
181 |
| - |
182 |
| - return ret; |
183 |
| -} |
184 |
| - |
185 |
| -static struct v4l2_subdev_core_ops fimc_is_sensor_core_ops = { |
186 |
| - .s_power = fimc_is_sensor_s_power, |
187 |
| -}; |
188 |
| - |
189 |
| -static struct v4l2_subdev_ops fimc_is_sensor_subdev_ops = { |
190 |
| - .core = &fimc_is_sensor_core_ops, |
191 |
| - .pad = &fimc_is_sensor_pad_ops, |
192 |
| -}; |
193 |
| - |
194 |
| -static const struct of_device_id fimc_is_sensor_of_match[]; |
195 |
| - |
196 |
| -static int fimc_is_sensor_probe(struct i2c_client *client, |
197 |
| - const struct i2c_device_id *id) |
198 |
| -{ |
199 |
| - struct device *dev = &client->dev; |
200 |
| - struct fimc_is_sensor *sensor; |
201 |
| - const struct of_device_id *of_id; |
202 |
| - struct v4l2_subdev *sd; |
203 |
| - int gpio, i, ret; |
204 |
| - |
205 |
| - sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); |
206 |
| - if (!sensor) |
207 |
| - return -ENOMEM; |
208 |
| - |
209 |
| - mutex_init(&sensor->lock); |
210 |
| - sensor->gpio_reset = -EINVAL; |
211 |
| - |
212 |
| - gpio = of_get_gpio_flags(dev->of_node, 0, NULL); |
213 |
| - if (gpio_is_valid(gpio)) { |
214 |
| - ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW, |
215 |
| - DRIVER_NAME); |
216 |
| - if (ret < 0) |
217 |
| - return ret; |
218 |
| - } |
219 |
| - sensor->gpio_reset = gpio; |
220 |
| - |
221 |
| - for (i = 0; i < SENSOR_NUM_SUPPLIES; i++) |
222 |
| - sensor->supplies[i].supply = sensor_supply_names[i]; |
223 |
| - |
224 |
| - ret = devm_regulator_bulk_get(&client->dev, SENSOR_NUM_SUPPLIES, |
225 |
| - sensor->supplies); |
226 |
| - if (ret < 0) |
227 |
| - return ret; |
228 |
| - |
229 |
| - of_id = of_match_node(fimc_is_sensor_of_match, dev->of_node); |
230 |
| - if (!of_id) |
231 |
| - return -ENODEV; |
232 |
| - |
233 |
| - sensor->drvdata = of_id->data; |
234 |
| - sensor->dev = dev; |
235 |
| - |
236 |
| - sd = &sensor->subdev; |
237 |
| - v4l2_i2c_subdev_init(sd, client, &fimc_is_sensor_subdev_ops); |
238 |
| - snprintf(sd->name, sizeof(sd->name), sensor->drvdata->subdev_name); |
239 |
| - sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; |
240 |
| - |
241 |
| - sensor->format.code = fimc_is_sensor_formats[0].code; |
242 |
| - sensor->format.width = FIMC_IS_SENSOR_DEF_PIX_WIDTH; |
243 |
| - sensor->format.height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT; |
244 |
| - |
245 |
| - sensor->pad.flags = MEDIA_PAD_FL_SOURCE; |
246 |
| - ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0); |
247 |
| - if (ret < 0) |
248 |
| - return ret; |
249 |
| - |
250 |
| - pm_runtime_no_callbacks(dev); |
251 |
| - pm_runtime_enable(dev); |
252 |
| - |
253 |
| - return ret; |
254 |
| -} |
255 |
| - |
256 |
| -static int fimc_is_sensor_remove(struct i2c_client *client) |
257 |
| -{ |
258 |
| - struct v4l2_subdev *sd = i2c_get_clientdata(client); |
259 |
| - media_entity_cleanup(&sd->entity); |
260 |
| - return 0; |
261 |
| -} |
262 |
| - |
263 |
| -static const struct i2c_device_id fimc_is_sensor_ids[] = { |
264 |
| - { } |
265 |
| -}; |
266 |
| - |
267 | 14 | static const struct sensor_drv_data s5k6a3_drvdata = {
|
268 | 15 | .id = FIMC_IS_SENSOR_ID_S5K6A3,
|
269 |
| - .subdev_name = "S5K6A3", |
270 |
| - .width = S5K6A3_SENSOR_WIDTH, |
271 |
| - .height = S5K6A3_SENSOR_HEIGHT, |
| 16 | + .open_timeout = S5K6A3_OPEN_TIMEOUT, |
272 | 17 | };
|
273 | 18 |
|
274 |
| -static const struct of_device_id fimc_is_sensor_of_match[] = { |
| 19 | +static const struct of_device_id fimc_is_sensor_of_ids[] = { |
275 | 20 | {
|
276 | 21 | .compatible = "samsung,s5k6a3",
|
277 | 22 | .data = &s5k6a3_drvdata,
|
278 | 23 | },
|
279 | 24 | { }
|
280 | 25 | };
|
281 | 26 |
|
282 |
| -static struct i2c_driver fimc_is_sensor_driver = { |
283 |
| - .driver = { |
284 |
| - .of_match_table = fimc_is_sensor_of_match, |
285 |
| - .name = DRIVER_NAME, |
286 |
| - .owner = THIS_MODULE, |
287 |
| - }, |
288 |
| - .probe = fimc_is_sensor_probe, |
289 |
| - .remove = fimc_is_sensor_remove, |
290 |
| - .id_table = fimc_is_sensor_ids, |
291 |
| -}; |
292 |
| - |
293 |
| -int fimc_is_register_sensor_driver(void) |
| 27 | +const struct sensor_drv_data *fimc_is_sensor_get_drvdata( |
| 28 | + struct device_node *node) |
294 | 29 | {
|
295 |
| - return i2c_add_driver(&fimc_is_sensor_driver); |
296 |
| -} |
| 30 | + const struct of_device_id *of_id; |
297 | 31 |
|
298 |
| -void fimc_is_unregister_sensor_driver(void) |
299 |
| -{ |
300 |
| - i2c_del_driver(&fimc_is_sensor_driver); |
| 32 | + of_id = of_match_node(fimc_is_sensor_of_ids, node); |
| 33 | + return of_id ? of_id->data : NULL; |
301 | 34 | }
|
302 |
| - |
303 |
| -MODULE_AUTHOR( "Sylwester Nawrocki <[email protected]>"); |
304 |
| -MODULE_DESCRIPTION("Exynos4x12 FIMC-IS image sensor subdev driver"); |
305 |
| -MODULE_LICENSE("GPL"); |
0 commit comments