cregit-Linux how code gets into the kernel

Release 4.7 drivers/media/i2c/s5k6a3.c

/*
 * Samsung S5K6A3 image sensor driver
 *
 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
 * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/v4l2-async.h>
#include <media/v4l2-subdev.h>


#define S5K6A3_SENSOR_MAX_WIDTH		1412

#define S5K6A3_SENSOR_MAX_HEIGHT	1412

#define S5K6A3_SENSOR_MIN_WIDTH		32

#define S5K6A3_SENSOR_MIN_HEIGHT	32


#define S5K6A3_DEFAULT_WIDTH		1296

#define S5K6A3_DEFAULT_HEIGHT		732


#define S5K6A3_DRV_NAME			"S5K6A3"

#define S5K6A3_CLK_NAME			"extclk"

#define S5K6A3_DEFAULT_CLK_FREQ		24000000U

enum {
	
S5K6A3_SUPP_VDDA,
	
S5K6A3_SUPP_VDDIO,
	
S5K6A3_SUPP_AFVDD,
	
S5K6A3_NUM_SUPPLIES,
};

/**
 * struct s5k6a3 - fimc-is sensor data structure
 * @dev: pointer to this I2C client device structure
 * @subdev: the image sensor's v4l2 subdev
 * @pad: subdev media source pad
 * @supplies: image sensor's voltage regulator supplies
 * @gpio_reset: GPIO connected to the sensor's reset pin
 * @lock: mutex protecting the structure's members below
 * @format: media bus format at the sensor's source pad
 */

struct s5k6a3 {
	
struct device *dev;
	
struct v4l2_subdev subdev;
	
struct media_pad pad;
	
struct regulator_bulk_data supplies[S5K6A3_NUM_SUPPLIES];
	
int gpio_reset;
	
struct mutex lock;
	
struct v4l2_mbus_framefmt format;
	
struct clk *clock;
	
u32 clock_frequency;
	
int power_count;
};


static const char * const s5k6a3_supply_names[] = {
	[S5K6A3_SUPP_VDDA]	= "svdda",
	[S5K6A3_SUPP_VDDIO]	= "svddio",
	[S5K6A3_SUPP_AFVDD]	= "afvdd",
};


static inline struct s5k6a3 *sd_to_s5k6a3(struct v4l2_subdev *sd) { return container_of(sd, struct s5k6a3, subdev); }

Contributors

PersonTokensPropCommitsCommitProp
sylwester nawrockisylwester nawrocki25100.00%1100.00%
Total25100.00%1100.00%

static const struct v4l2_mbus_framefmt s5k6a3_formats[] = { { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .colorspace = V4L2_COLORSPACE_SRGB, .field = V4L2_FIELD_NONE, } };
static const struct v4l2_mbus_framefmt *find_sensor_format( struct v4l2_mbus_framefmt *mf) { int i; for (i = 0; i < ARRAY_SIZE(s5k6a3_formats); i++) if (mf->code == s5k6a3_formats[i].code) return &s5k6a3_formats[i]; return &s5k6a3_formats[0]; }

Contributors

PersonTokensPropCommitsCommitProp
sylwester nawrockisylwester nawrocki60100.00%1100.00%
Total60100.00%1100.00%


static int s5k6a3_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(s5k6a3_formats)) return -EINVAL; code->code = s5k6a3_formats[code->index].code; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
sylwester nawrockisylwester nawrocki5096.15%150.00%
hans verkuilhans verkuil23.85%150.00%
Total52100.00%2100.00%


static void s5k6a3_try_format(struct v4l2_mbus_framefmt *mf) { const struct v4l2_mbus_framefmt *fmt; fmt = find_sensor_format(mf); mf->code = fmt->code; mf->field = V4L2_FIELD_NONE; v4l_bound_align_image(&mf->width, S5K6A3_SENSOR_MIN_WIDTH, S5K6A3_SENSOR_MAX_WIDTH, 0, &mf->height, S5K6A3_SENSOR_MIN_HEIGHT, S5K6A3_SENSOR_MAX_HEIGHT, 0, 0); }

Contributors

PersonTokensPropCommitsCommitProp
sylwester nawrockisylwester nawrocki5990.77%150.00%
laurent pinchartlaurent pinchart69.23%150.00%
Total65100.00%2100.00%


static struct v4l2_mbus_framefmt *__s5k6a3_get_format( struct s5k6a3 *sensor, struct v4l2_subdev_pad_config *cfg, u32 pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) return cfg ? v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad) : NULL; return &sensor->format; }

Contributors

PersonTokensPropCommitsCommitProp
sylwester nawrockisylwester nawrocki4583.33%150.00%
hans verkuilhans verkuil916.67%150.00%
Total54100.00%2100.00%


static int s5k6a3_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct s5k6a3 *sensor = sd_to_s5k6a3(sd); struct v4l2_mbus_framefmt *mf; s5k6a3_try_format(&fmt->format); mf = __s5k6a3_get_format(sensor, cfg, fmt->pad, fmt->which); if (mf) { mutex_lock(&sensor->lock); *mf = fmt->format; mutex_unlock(&sensor->lock); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
sylwester nawrockisylwester nawrocki9096.77%150.00%
hans verkuilhans verkuil33.23%150.00%
Total93100.00%2100.00%


static int s5k6a3_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct s5k6a3 *sensor = sd_to_s5k6a3(sd); struct v4l2_mbus_framefmt *mf; mf = __s5k6a3_get_format(sensor, cfg, fmt->pad, fmt->which); mutex_lock(&sensor->lock); fmt->format = *mf; mutex_unlock(&sensor->lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
sylwester nawrockisylwester nawrocki7696.20%150.00%
hans verkuilhans verkuil33.80%150.00%
Total79100.00%2100.00%

static struct v4l2_subdev_pad_ops s5k6a3_pad_ops = { .enum_mbus_code = s5k6a3_enum_mbus_code, .get_fmt = s5k6a3_get_fmt, .set_fmt = s5k6a3_set_fmt, };
static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0); *format = s5k6a3_formats[0]; format->width = S5K6A3_DEFAULT_WIDTH; format->height = S5K6A3_DEFAULT_HEIGHT; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
sylwester nawrockisylwester nawrocki5192.73%150.00%
hans verkuilhans verkuil47.27%150.00%
Total55100.00%2100.00%

static const struct v4l2_subdev_internal_ops s5k6a3_sd_internal_ops = { .open = s5k6a3_open, };
static int __s5k6a3_power_on(struct s5k6a3 *sensor) { int i = S5K6A3_SUPP_VDDA; int ret; ret = clk_set_rate(sensor->clock, sensor->clock_frequency); if (ret < 0) return ret; ret = pm_runtime_get(sensor->dev); if (ret < 0) return ret; ret = regulator_enable(sensor->supplies[i].consumer); if (ret < 0) goto error_rpm_put; ret = clk_prepare_enable(sensor->clock); if (ret < 0) goto error_reg_dis; for (i++; i < S5K6A3_NUM_SUPPLIES; i++) { ret = regulator_enable(sensor->supplies[i].consumer); if (ret < 0) goto error_reg_dis; } gpio_set_value(sensor->gpio_reset, 1); usleep_range(600, 800); gpio_set_value(sensor->gpio_reset, 0); usleep_range(600, 800); gpio_set_value(sensor->gpio_reset, 1); /* Delay needed for the sensor initialization */ msleep(20); return 0; error_reg_dis: for (--i; i >= 0; --i) regulator_disable(sensor->supplies[i].consumer); error_rpm_put: pm_runtime_put(sensor->dev); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
sylwester nawrockisylwester nawrocki225100.00%1100.00%
Total225100.00%1100.00%


static int __s5k6a3_power_off(struct s5k6a3 *sensor) { int i; gpio_set_value(sensor->gpio_reset, 0); for (i = S5K6A3_NUM_SUPPLIES - 1; i >= 0; i--) regulator_disable(sensor->supplies[i].consumer); clk_disable_unprepare(sensor->clock); pm_runtime_put(sensor->dev); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
sylwester nawrockisylwester nawrocki67100.00%1100.00%
Total67100.00%1100.00%


static int s5k6a3_s_power(struct v4l2_subdev *sd, int on) { struct s5k6a3 *sensor = sd_to_s5k6a3(sd); int ret = 0; mutex_lock(&sensor->lock); if (sensor->power_count == !on) { if (on) ret = __s5k6a3_power_on(sensor); else ret = __s5k6a3_power_off(sensor); if (ret == 0) sensor->power_count += on ? 1 : -1; } mutex_unlock(&sensor->lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
sylwester nawrockisylwester nawrocki95100.00%1100.00%
Total95100.00%1100.00%

static struct v4l2_subdev_core_ops s5k6a3_core_ops = { .s_power = s5k6a3_s_power, }; static struct v4l2_subdev_ops s5k6a3_subdev_ops = { .core = &s5k6a3_core_ops, .pad = &s5k6a3_pad_ops, };
static int s5k6a3_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; struct s5k6a3 *sensor; struct v4l2_subdev *sd; int gpio, i, ret; sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); if (!sensor) return -ENOMEM; mutex_init(&sensor->lock); sensor->gpio_reset = -EINVAL; sensor->clock = ERR_PTR(-EINVAL); sensor->dev = dev; sensor->clock = devm_clk_get(sensor->dev, S5K6A3_CLK_NAME); if (IS_ERR(sensor->clock)) return PTR_ERR(sensor->clock); gpio = of_get_gpio_flags(dev->of_node, 0, NULL); if (!gpio_is_valid(gpio)) return gpio; ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW, S5K6A3_DRV_NAME); if (ret < 0) return ret; sensor->gpio_reset = gpio; if (of_property_read_u32(dev->of_node, "clock-frequency", &sensor->clock_frequency)) { sensor->clock_frequency = S5K6A3_DEFAULT_CLK_FREQ; dev_info(dev, "using default %u Hz clock frequency\n", sensor->clock_frequency); } for (i = 0; i < S5K6A3_NUM_SUPPLIES; i++) sensor->supplies[i].supply = s5k6a3_supply_names[i]; ret = devm_regulator_bulk_get(&client->dev, S5K6A3_NUM_SUPPLIES, sensor->supplies); if (ret < 0) return ret; sd = &sensor->subdev; v4l2_i2c_subdev_init(sd, client, &s5k6a3_subdev_ops); sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->internal_ops = &s5k6a3_sd_internal_ops; sensor->format.code = s5k6a3_formats[0].code; sensor->format.width = S5K6A3_DEFAULT_WIDTH; sensor->format.height = S5K6A3_DEFAULT_HEIGHT; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad); if (ret < 0) return ret; pm_runtime_no_callbacks(dev); pm_runtime_enable(dev); ret = v4l2_async_register_subdev(sd); if (ret < 0) { pm_runtime_disable(&client->dev); media_entity_cleanup(&sd->entity); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
sylwester nawrockisylwester nawrocki40699.75%150.00%
mauro carvalho chehabmauro carvalho chehab10.25%150.00%
Total407100.00%2100.00%


static int s5k6a3_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); pm_runtime_disable(&client->dev); v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
sylwester nawrockisylwester nawrocki45100.00%1100.00%
Total45100.00%1100.00%

static const struct i2c_device_id s5k6a3_ids[] = { { } }; MODULE_DEVICE_TABLE(i2c, s5k6a3_ids); #ifdef CONFIG_OF static const struct of_device_id s5k6a3_of_match[] = { { .compatible = "samsung,s5k6a3" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, s5k6a3_of_match); #endif static struct i2c_driver s5k6a3_driver = { .driver = { .of_match_table = of_match_ptr(s5k6a3_of_match), .name = S5K6A3_DRV_NAME, .owner = THIS_MODULE, }, .probe = s5k6a3_probe, .remove = s5k6a3_remove, .id_table = s5k6a3_ids, }; module_i2c_driver(s5k6a3_driver); MODULE_DESCRIPTION("S5K6A3 image sensor subdev driver"); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
sylwester nawrockisylwester nawrocki166297.88%116.67%
hans verkuilhans verkuil211.24%116.67%
javier martinez canillasjavier martinez canillas70.41%116.67%
laurent pinchartlaurent pinchart60.35%116.67%
mauro carvalho chehabmauro carvalho chehab10.06%116.67%
boris brezillonboris brezillon10.06%116.67%
Total1698100.00%6100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}