Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Samsung S5K6A3 image sensor driver
4 *
5 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
6 * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
7 */
8
9#include <linux/clk.h>
10#include <linux/delay.h>
11#include <linux/device.h>
12#include <linux/err.h>
13#include <linux/errno.h>
14#include <linux/gpio/consumer.h>
15#include <linux/i2c.h>
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/pm_runtime.h>
19#include <linux/regulator/consumer.h>
20#include <linux/slab.h>
21#include <linux/videodev2.h>
22#include <media/v4l2-async.h>
23#include <media/v4l2-subdev.h>
24
25#define S5K6A3_SENSOR_MAX_WIDTH 1412
26#define S5K6A3_SENSOR_MAX_HEIGHT 1412
27#define S5K6A3_SENSOR_MIN_WIDTH 32
28#define S5K6A3_SENSOR_MIN_HEIGHT 32
29
30#define S5K6A3_DEFAULT_WIDTH 1296
31#define S5K6A3_DEFAULT_HEIGHT 732
32
33#define S5K6A3_DRV_NAME "S5K6A3"
34#define S5K6A3_CLK_NAME "extclk"
35#define S5K6A3_DEFAULT_CLK_FREQ 24000000U
36
37enum {
38 S5K6A3_SUPP_VDDA,
39 S5K6A3_SUPP_VDDIO,
40 S5K6A3_SUPP_AFVDD,
41 S5K6A3_NUM_SUPPLIES,
42};
43
44/**
45 * struct s5k6a3 - fimc-is sensor data structure
46 * @dev: pointer to this I2C client device structure
47 * @subdev: the image sensor's v4l2 subdev
48 * @pad: subdev media source pad
49 * @supplies: image sensor's voltage regulator supplies
50 * @gpio_reset: GPIO connected to the sensor's reset pin
51 * @lock: mutex protecting the structure's members below
52 * @format: media bus format at the sensor's source pad
53 * @clock: pointer to &struct clk.
54 * @power_count: stores state if device is powered
55 */
56struct s5k6a3 {
57 struct device *dev;
58 struct v4l2_subdev subdev;
59 struct media_pad pad;
60 struct regulator_bulk_data supplies[S5K6A3_NUM_SUPPLIES];
61 struct gpio_desc *gpio_reset;
62 struct mutex lock;
63 struct v4l2_mbus_framefmt format;
64 struct clk *clock;
65 int power_count;
66};
67
68static const char * const s5k6a3_supply_names[] = {
69 [S5K6A3_SUPP_VDDA] = "svdda",
70 [S5K6A3_SUPP_VDDIO] = "svddio",
71 [S5K6A3_SUPP_AFVDD] = "afvdd",
72};
73
74static inline struct s5k6a3 *sd_to_s5k6a3(struct v4l2_subdev *sd)
75{
76 return container_of(sd, struct s5k6a3, subdev);
77}
78
79static const struct v4l2_mbus_framefmt s5k6a3_formats[] = {
80 {
81 .code = MEDIA_BUS_FMT_SGRBG10_1X10,
82 .colorspace = V4L2_COLORSPACE_SRGB,
83 .field = V4L2_FIELD_NONE,
84 }
85};
86
87static const struct v4l2_mbus_framefmt *find_sensor_format(
88 struct v4l2_mbus_framefmt *mf)
89{
90 int i;
91
92 for (i = 0; i < ARRAY_SIZE(s5k6a3_formats); i++)
93 if (mf->code == s5k6a3_formats[i].code)
94 return &s5k6a3_formats[i];
95
96 return &s5k6a3_formats[0];
97}
98
99static int s5k6a3_enum_mbus_code(struct v4l2_subdev *sd,
100 struct v4l2_subdev_state *sd_state,
101 struct v4l2_subdev_mbus_code_enum *code)
102{
103 if (code->index >= ARRAY_SIZE(s5k6a3_formats))
104 return -EINVAL;
105
106 code->code = s5k6a3_formats[code->index].code;
107 return 0;
108}
109
110static void s5k6a3_try_format(struct v4l2_mbus_framefmt *mf)
111{
112 const struct v4l2_mbus_framefmt *fmt;
113
114 fmt = find_sensor_format(mf);
115 mf->code = fmt->code;
116 mf->field = V4L2_FIELD_NONE;
117 v4l_bound_align_image(&mf->width, S5K6A3_SENSOR_MIN_WIDTH,
118 S5K6A3_SENSOR_MAX_WIDTH, 0,
119 &mf->height, S5K6A3_SENSOR_MIN_HEIGHT,
120 S5K6A3_SENSOR_MAX_HEIGHT, 0, 0);
121}
122
123static struct v4l2_mbus_framefmt *__s5k6a3_get_format(
124 struct s5k6a3 *sensor, struct v4l2_subdev_state *sd_state,
125 u32 pad, enum v4l2_subdev_format_whence which)
126{
127 if (which == V4L2_SUBDEV_FORMAT_TRY)
128 return sd_state ? v4l2_subdev_state_get_format(sd_state, pad) : NULL;
129
130 return &sensor->format;
131}
132
133static int s5k6a3_set_fmt(struct v4l2_subdev *sd,
134 struct v4l2_subdev_state *sd_state,
135 struct v4l2_subdev_format *fmt)
136{
137 struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
138 struct v4l2_mbus_framefmt *mf;
139
140 s5k6a3_try_format(&fmt->format);
141
142 mf = __s5k6a3_get_format(sensor, sd_state, fmt->pad, fmt->which);
143 if (mf) {
144 mutex_lock(&sensor->lock);
145 *mf = fmt->format;
146 mutex_unlock(&sensor->lock);
147 }
148 return 0;
149}
150
151static int s5k6a3_get_fmt(struct v4l2_subdev *sd,
152 struct v4l2_subdev_state *sd_state,
153 struct v4l2_subdev_format *fmt)
154{
155 struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
156 struct v4l2_mbus_framefmt *mf;
157
158 mf = __s5k6a3_get_format(sensor, sd_state, fmt->pad, fmt->which);
159
160 mutex_lock(&sensor->lock);
161 fmt->format = *mf;
162 mutex_unlock(&sensor->lock);
163 return 0;
164}
165
166static const struct v4l2_subdev_pad_ops s5k6a3_pad_ops = {
167 .enum_mbus_code = s5k6a3_enum_mbus_code,
168 .get_fmt = s5k6a3_get_fmt,
169 .set_fmt = s5k6a3_set_fmt,
170};
171
172static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
173{
174 struct v4l2_mbus_framefmt *format = v4l2_subdev_state_get_format(fh->state,
175 0);
176
177 *format = s5k6a3_formats[0];
178 format->width = S5K6A3_DEFAULT_WIDTH;
179 format->height = S5K6A3_DEFAULT_HEIGHT;
180
181 return 0;
182}
183
184static const struct v4l2_subdev_internal_ops s5k6a3_sd_internal_ops = {
185 .open = s5k6a3_open,
186};
187
188static int __s5k6a3_power_on(struct s5k6a3 *sensor)
189{
190 int i = S5K6A3_SUPP_VDDA;
191 int ret;
192
193 ret = pm_runtime_get(sensor->dev);
194 if (ret < 0)
195 goto error_rpm_put;
196
197 ret = regulator_enable(sensor->supplies[i].consumer);
198 if (ret < 0)
199 goto error_rpm_put;
200
201 ret = clk_prepare_enable(sensor->clock);
202 if (ret < 0)
203 goto error_reg_dis;
204
205 for (i++; i < S5K6A3_NUM_SUPPLIES; i++) {
206 ret = regulator_enable(sensor->supplies[i].consumer);
207 if (ret < 0)
208 goto error_clk;
209 }
210
211 gpiod_set_value_cansleep(sensor->gpio_reset, 0);
212 usleep_range(600, 800);
213 gpiod_set_value_cansleep(sensor->gpio_reset, 1);
214 usleep_range(600, 800);
215 gpiod_set_value_cansleep(sensor->gpio_reset, 0);
216
217 /* Delay needed for the sensor initialization */
218 msleep(20);
219 return 0;
220
221error_clk:
222 clk_disable_unprepare(sensor->clock);
223error_reg_dis:
224 for (--i; i >= 0; --i)
225 regulator_disable(sensor->supplies[i].consumer);
226error_rpm_put:
227 pm_runtime_put(sensor->dev);
228 return ret;
229}
230
231static int __s5k6a3_power_off(struct s5k6a3 *sensor)
232{
233 int i;
234
235 gpiod_set_value_cansleep(sensor->gpio_reset, 1);
236
237 for (i = S5K6A3_NUM_SUPPLIES - 1; i >= 0; i--)
238 regulator_disable(sensor->supplies[i].consumer);
239
240 clk_disable_unprepare(sensor->clock);
241 pm_runtime_put(sensor->dev);
242 return 0;
243}
244
245static int s5k6a3_s_power(struct v4l2_subdev *sd, int on)
246{
247 struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
248 int ret = 0;
249
250 mutex_lock(&sensor->lock);
251
252 if (sensor->power_count == !on) {
253 if (on)
254 ret = __s5k6a3_power_on(sensor);
255 else
256 ret = __s5k6a3_power_off(sensor);
257
258 if (ret == 0)
259 sensor->power_count += on ? 1 : -1;
260 }
261
262 mutex_unlock(&sensor->lock);
263 return ret;
264}
265
266static const struct v4l2_subdev_core_ops s5k6a3_core_ops = {
267 .s_power = s5k6a3_s_power,
268};
269
270static const struct v4l2_subdev_ops s5k6a3_subdev_ops = {
271 .core = &s5k6a3_core_ops,
272 .pad = &s5k6a3_pad_ops,
273};
274
275static int s5k6a3_probe(struct i2c_client *client)
276{
277 struct device *dev = &client->dev;
278 struct s5k6a3 *sensor;
279 struct v4l2_subdev *sd;
280 int i, ret;
281
282 sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
283 if (!sensor)
284 return -ENOMEM;
285
286 mutex_init(&sensor->lock);
287 sensor->dev = dev;
288
289 sensor->clock = devm_v4l2_sensor_clk_get_legacy(sensor->dev,
290 S5K6A3_CLK_NAME, false,
291 S5K6A3_DEFAULT_CLK_FREQ);
292 if (IS_ERR(sensor->clock))
293 return dev_err_probe(sensor->dev, PTR_ERR(sensor->clock),
294 "failed to get extclk\n");
295
296 sensor->gpio_reset = devm_gpiod_get(dev, NULL, GPIOD_OUT_HIGH);
297 ret = PTR_ERR_OR_ZERO(sensor->gpio_reset);
298 if (ret)
299 return ret;
300
301 for (i = 0; i < S5K6A3_NUM_SUPPLIES; i++)
302 sensor->supplies[i].supply = s5k6a3_supply_names[i];
303
304 ret = devm_regulator_bulk_get(&client->dev, S5K6A3_NUM_SUPPLIES,
305 sensor->supplies);
306 if (ret < 0)
307 return ret;
308
309 sd = &sensor->subdev;
310 v4l2_i2c_subdev_init(sd, client, &s5k6a3_subdev_ops);
311 sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
312 sd->internal_ops = &s5k6a3_sd_internal_ops;
313
314 sensor->format.code = s5k6a3_formats[0].code;
315 sensor->format.width = S5K6A3_DEFAULT_WIDTH;
316 sensor->format.height = S5K6A3_DEFAULT_HEIGHT;
317
318 sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
319 sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
320 ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad);
321 if (ret < 0)
322 return ret;
323
324 pm_runtime_no_callbacks(dev);
325 pm_runtime_enable(dev);
326
327 ret = v4l2_async_register_subdev(sd);
328
329 if (ret < 0) {
330 pm_runtime_disable(&client->dev);
331 media_entity_cleanup(&sd->entity);
332 }
333
334 return ret;
335}
336
337static void s5k6a3_remove(struct i2c_client *client)
338{
339 struct v4l2_subdev *sd = i2c_get_clientdata(client);
340
341 pm_runtime_disable(&client->dev);
342 v4l2_async_unregister_subdev(sd);
343 media_entity_cleanup(&sd->entity);
344}
345
346static const struct i2c_device_id s5k6a3_ids[] = {
347 { }
348};
349MODULE_DEVICE_TABLE(i2c, s5k6a3_ids);
350
351#ifdef CONFIG_OF
352static const struct of_device_id s5k6a3_of_match[] = {
353 { .compatible = "samsung,s5k6a3" },
354 { /* sentinel */ }
355};
356MODULE_DEVICE_TABLE(of, s5k6a3_of_match);
357#endif
358
359static struct i2c_driver s5k6a3_driver = {
360 .driver = {
361 .of_match_table = of_match_ptr(s5k6a3_of_match),
362 .name = S5K6A3_DRV_NAME,
363 },
364 .probe = s5k6a3_probe,
365 .remove = s5k6a3_remove,
366 .id_table = s5k6a3_ids,
367};
368
369module_i2c_driver(s5k6a3_driver);
370
371MODULE_DESCRIPTION("S5K6A3 image sensor subdev driver");
372MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
373MODULE_LICENSE("GPL v2");