brintos

brintos / linux-shallow public Read only

0
0
Text · 9.2 KiB · 0c26741 Raw
384 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Samsung S5K6A3 image sensor driver4 *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		141226#define S5K6A3_SENSOR_MAX_HEIGHT	141227#define S5K6A3_SENSOR_MIN_WIDTH		3228#define S5K6A3_SENSOR_MIN_HEIGHT	3229 30#define S5K6A3_DEFAULT_WIDTH		129631#define S5K6A3_DEFAULT_HEIGHT		73232 33#define S5K6A3_DRV_NAME			"S5K6A3"34#define S5K6A3_CLK_NAME			"extclk"35#define S5K6A3_DEFAULT_CLK_FREQ		24000000U36 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 structure46 * @dev: pointer to this I2C client device structure47 * @subdev: the image sensor's v4l2 subdev48 * @pad: subdev media source pad49 * @supplies: image sensor's voltage regulator supplies50 * @gpio_reset: GPIO connected to the sensor's reset pin51 * @lock: mutex protecting the structure's members below52 * @format: media bus format at the sensor's source pad53 * @clock: pointer to &struct clk.54 * @clock_frequency: clock frequency55 * @power_count: stores state if device is powered56 */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	struct gpio_desc *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 = MEDIA_BUS_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_state *sd_state,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	mf->field = V4L2_FIELD_NONE;119	v4l_bound_align_image(&mf->width, S5K6A3_SENSOR_MIN_WIDTH,120			      S5K6A3_SENSOR_MAX_WIDTH, 0,121			      &mf->height, S5K6A3_SENSOR_MIN_HEIGHT,122			      S5K6A3_SENSOR_MAX_HEIGHT, 0, 0);123}124 125static struct v4l2_mbus_framefmt *__s5k6a3_get_format(126		struct s5k6a3 *sensor, struct v4l2_subdev_state *sd_state,127		u32 pad, enum v4l2_subdev_format_whence which)128{129	if (which == V4L2_SUBDEV_FORMAT_TRY)130		return sd_state ? v4l2_subdev_state_get_format(sd_state, pad) : NULL;131 132	return &sensor->format;133}134 135static int s5k6a3_set_fmt(struct v4l2_subdev *sd,136				  struct v4l2_subdev_state *sd_state,137				  struct v4l2_subdev_format *fmt)138{139	struct s5k6a3 *sensor = sd_to_s5k6a3(sd);140	struct v4l2_mbus_framefmt *mf;141 142	s5k6a3_try_format(&fmt->format);143 144	mf = __s5k6a3_get_format(sensor, sd_state, fmt->pad, fmt->which);145	if (mf) {146		mutex_lock(&sensor->lock);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_state *sd_state,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, sd_state, 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 const 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_state_get_format(fh->state,177									 0);178 179	*format		= s5k6a3_formats[0];180	format->width	= S5K6A3_DEFAULT_WIDTH;181	format->height	= S5K6A3_DEFAULT_HEIGHT;182 183	return 0;184}185 186static const struct v4l2_subdev_internal_ops s5k6a3_sd_internal_ops = {187	.open = s5k6a3_open,188};189 190static int __s5k6a3_power_on(struct s5k6a3 *sensor)191{192	int i = S5K6A3_SUPP_VDDA;193	int ret;194 195	ret = clk_set_rate(sensor->clock, sensor->clock_frequency);196	if (ret < 0)197		return ret;198 199	ret = pm_runtime_get(sensor->dev);200	if (ret < 0)201		goto error_rpm_put;202 203	ret = regulator_enable(sensor->supplies[i].consumer);204	if (ret < 0)205		goto error_rpm_put;206 207	ret = clk_prepare_enable(sensor->clock);208	if (ret < 0)209		goto error_reg_dis;210 211	for (i++; i < S5K6A3_NUM_SUPPLIES; i++) {212		ret = regulator_enable(sensor->supplies[i].consumer);213		if (ret < 0)214			goto error_clk;215	}216 217	gpiod_set_value_cansleep(sensor->gpio_reset, 0);218	usleep_range(600, 800);219	gpiod_set_value_cansleep(sensor->gpio_reset, 1);220	usleep_range(600, 800);221	gpiod_set_value_cansleep(sensor->gpio_reset, 0);222 223	/* Delay needed for the sensor initialization */224	msleep(20);225	return 0;226 227error_clk:228	clk_disable_unprepare(sensor->clock);229error_reg_dis:230	for (--i; i >= 0; --i)231		regulator_disable(sensor->supplies[i].consumer);232error_rpm_put:233	pm_runtime_put(sensor->dev);234	return ret;235}236 237static int __s5k6a3_power_off(struct s5k6a3 *sensor)238{239	int i;240 241	gpiod_set_value_cansleep(sensor->gpio_reset, 1);242 243	for (i = S5K6A3_NUM_SUPPLIES - 1; i >= 0; i--)244		regulator_disable(sensor->supplies[i].consumer);245 246	clk_disable_unprepare(sensor->clock);247	pm_runtime_put(sensor->dev);248	return 0;249}250 251static int s5k6a3_s_power(struct v4l2_subdev *sd, int on)252{253	struct s5k6a3 *sensor = sd_to_s5k6a3(sd);254	int ret = 0;255 256	mutex_lock(&sensor->lock);257 258	if (sensor->power_count == !on) {259		if (on)260			ret = __s5k6a3_power_on(sensor);261		else262			ret = __s5k6a3_power_off(sensor);263 264		if (ret == 0)265			sensor->power_count += on ? 1 : -1;266	}267 268	mutex_unlock(&sensor->lock);269	return ret;270}271 272static const struct v4l2_subdev_core_ops s5k6a3_core_ops = {273	.s_power = s5k6a3_s_power,274};275 276static const struct v4l2_subdev_ops s5k6a3_subdev_ops = {277	.core = &s5k6a3_core_ops,278	.pad = &s5k6a3_pad_ops,279};280 281static int s5k6a3_probe(struct i2c_client *client)282{283	struct device *dev = &client->dev;284	struct s5k6a3 *sensor;285	struct v4l2_subdev *sd;286	int i, ret;287 288	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);289	if (!sensor)290		return -ENOMEM;291 292	mutex_init(&sensor->lock);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	sensor->gpio_reset = devm_gpiod_get(dev, NULL, GPIOD_OUT_HIGH);300	ret = PTR_ERR_OR_ZERO(sensor->gpio_reset);301	if (ret)302		return ret;303 304	if (of_property_read_u32(dev->of_node, "clock-frequency",305				 &sensor->clock_frequency)) {306		sensor->clock_frequency = S5K6A3_DEFAULT_CLK_FREQ;307		dev_info(dev, "using default %u Hz clock frequency\n",308					sensor->clock_frequency);309	}310 311	for (i = 0; i < S5K6A3_NUM_SUPPLIES; i++)312		sensor->supplies[i].supply = s5k6a3_supply_names[i];313 314	ret = devm_regulator_bulk_get(&client->dev, S5K6A3_NUM_SUPPLIES,315				      sensor->supplies);316	if (ret < 0)317		return ret;318 319	sd = &sensor->subdev;320	v4l2_i2c_subdev_init(sd, client, &s5k6a3_subdev_ops);321	sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;322	sd->internal_ops = &s5k6a3_sd_internal_ops;323 324	sensor->format.code = s5k6a3_formats[0].code;325	sensor->format.width = S5K6A3_DEFAULT_WIDTH;326	sensor->format.height = S5K6A3_DEFAULT_HEIGHT;327 328	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;329	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;330	ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad);331	if (ret < 0)332		return ret;333 334	pm_runtime_no_callbacks(dev);335	pm_runtime_enable(dev);336 337	ret = v4l2_async_register_subdev(sd);338 339	if (ret < 0) {340		pm_runtime_disable(&client->dev);341		media_entity_cleanup(&sd->entity);342	}343 344	return ret;345}346 347static void s5k6a3_remove(struct i2c_client *client)348{349	struct v4l2_subdev *sd = i2c_get_clientdata(client);350 351	pm_runtime_disable(&client->dev);352	v4l2_async_unregister_subdev(sd);353	media_entity_cleanup(&sd->entity);354}355 356static const struct i2c_device_id s5k6a3_ids[] = {357	{ }358};359MODULE_DEVICE_TABLE(i2c, s5k6a3_ids);360 361#ifdef CONFIG_OF362static const struct of_device_id s5k6a3_of_match[] = {363	{ .compatible = "samsung,s5k6a3" },364	{ /* sentinel */ }365};366MODULE_DEVICE_TABLE(of, s5k6a3_of_match);367#endif368 369static struct i2c_driver s5k6a3_driver = {370	.driver = {371		.of_match_table	= of_match_ptr(s5k6a3_of_match),372		.name		= S5K6A3_DRV_NAME,373	},374	.probe		= s5k6a3_probe,375	.remove		= s5k6a3_remove,376	.id_table	= s5k6a3_ids,377};378 379module_i2c_driver(s5k6a3_driver);380 381MODULE_DESCRIPTION("S5K6A3 image sensor subdev driver");382MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");383MODULE_LICENSE("GPL v2");384