File Index
Files and directories
Sign in
FILE

s5k6a3.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/i2c/s5k6a3.c C
  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");
~/ Read-only file preview.