18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Samsung S5K6A3 image sensor driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Samsung Electronics Co., Ltd. 68c2ecf20Sopenharmony_ci * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/gpio.h> 148c2ecf20Sopenharmony_ci#include <linux/i2c.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 188c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 198c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 228c2ecf20Sopenharmony_ci#include <media/v4l2-async.h> 238c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define S5K6A3_SENSOR_MAX_WIDTH 1412 268c2ecf20Sopenharmony_ci#define S5K6A3_SENSOR_MAX_HEIGHT 1412 278c2ecf20Sopenharmony_ci#define S5K6A3_SENSOR_MIN_WIDTH 32 288c2ecf20Sopenharmony_ci#define S5K6A3_SENSOR_MIN_HEIGHT 32 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define S5K6A3_DEFAULT_WIDTH 1296 318c2ecf20Sopenharmony_ci#define S5K6A3_DEFAULT_HEIGHT 732 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define S5K6A3_DRV_NAME "S5K6A3" 348c2ecf20Sopenharmony_ci#define S5K6A3_CLK_NAME "extclk" 358c2ecf20Sopenharmony_ci#define S5K6A3_DEFAULT_CLK_FREQ 24000000U 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cienum { 388c2ecf20Sopenharmony_ci S5K6A3_SUPP_VDDA, 398c2ecf20Sopenharmony_ci S5K6A3_SUPP_VDDIO, 408c2ecf20Sopenharmony_ci S5K6A3_SUPP_AFVDD, 418c2ecf20Sopenharmony_ci S5K6A3_NUM_SUPPLIES, 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/** 458c2ecf20Sopenharmony_ci * struct s5k6a3 - fimc-is sensor data structure 468c2ecf20Sopenharmony_ci * @dev: pointer to this I2C client device structure 478c2ecf20Sopenharmony_ci * @subdev: the image sensor's v4l2 subdev 488c2ecf20Sopenharmony_ci * @pad: subdev media source pad 498c2ecf20Sopenharmony_ci * @supplies: image sensor's voltage regulator supplies 508c2ecf20Sopenharmony_ci * @gpio_reset: GPIO connected to the sensor's reset pin 518c2ecf20Sopenharmony_ci * @lock: mutex protecting the structure's members below 528c2ecf20Sopenharmony_ci * @format: media bus format at the sensor's source pad 538c2ecf20Sopenharmony_ci * @clock: pointer to &struct clk. 548c2ecf20Sopenharmony_ci * @clock_frequency: clock frequency 558c2ecf20Sopenharmony_ci * @power_count: stores state if device is powered 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_cistruct s5k6a3 { 588c2ecf20Sopenharmony_ci struct device *dev; 598c2ecf20Sopenharmony_ci struct v4l2_subdev subdev; 608c2ecf20Sopenharmony_ci struct media_pad pad; 618c2ecf20Sopenharmony_ci struct regulator_bulk_data supplies[S5K6A3_NUM_SUPPLIES]; 628c2ecf20Sopenharmony_ci int gpio_reset; 638c2ecf20Sopenharmony_ci struct mutex lock; 648c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt format; 658c2ecf20Sopenharmony_ci struct clk *clock; 668c2ecf20Sopenharmony_ci u32 clock_frequency; 678c2ecf20Sopenharmony_ci int power_count; 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic const char * const s5k6a3_supply_names[] = { 718c2ecf20Sopenharmony_ci [S5K6A3_SUPP_VDDA] = "svdda", 728c2ecf20Sopenharmony_ci [S5K6A3_SUPP_VDDIO] = "svddio", 738c2ecf20Sopenharmony_ci [S5K6A3_SUPP_AFVDD] = "afvdd", 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic inline struct s5k6a3 *sd_to_s5k6a3(struct v4l2_subdev *sd) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci return container_of(sd, struct s5k6a3, subdev); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic const struct v4l2_mbus_framefmt s5k6a3_formats[] = { 828c2ecf20Sopenharmony_ci { 838c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SGRBG10_1X10, 848c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 858c2ecf20Sopenharmony_ci .field = V4L2_FIELD_NONE, 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic const struct v4l2_mbus_framefmt *find_sensor_format( 908c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci int i; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(s5k6a3_formats); i++) 958c2ecf20Sopenharmony_ci if (mf->code == s5k6a3_formats[i].code) 968c2ecf20Sopenharmony_ci return &s5k6a3_formats[i]; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return &s5k6a3_formats[0]; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int s5k6a3_enum_mbus_code(struct v4l2_subdev *sd, 1028c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 1038c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci if (code->index >= ARRAY_SIZE(s5k6a3_formats)) 1068c2ecf20Sopenharmony_ci return -EINVAL; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci code->code = s5k6a3_formats[code->index].code; 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void s5k6a3_try_format(struct v4l2_mbus_framefmt *mf) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci const struct v4l2_mbus_framefmt *fmt; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci fmt = find_sensor_format(mf); 1178c2ecf20Sopenharmony_ci mf->code = fmt->code; 1188c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 1198c2ecf20Sopenharmony_ci v4l_bound_align_image(&mf->width, S5K6A3_SENSOR_MIN_WIDTH, 1208c2ecf20Sopenharmony_ci S5K6A3_SENSOR_MAX_WIDTH, 0, 1218c2ecf20Sopenharmony_ci &mf->height, S5K6A3_SENSOR_MIN_HEIGHT, 1228c2ecf20Sopenharmony_ci S5K6A3_SENSOR_MAX_HEIGHT, 0, 0); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic struct v4l2_mbus_framefmt *__s5k6a3_get_format( 1268c2ecf20Sopenharmony_ci struct s5k6a3 *sensor, struct v4l2_subdev_pad_config *cfg, 1278c2ecf20Sopenharmony_ci u32 pad, enum v4l2_subdev_format_whence which) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci if (which == V4L2_SUBDEV_FORMAT_TRY) 1308c2ecf20Sopenharmony_ci return cfg ? v4l2_subdev_get_try_format(&sensor->subdev, cfg, pad) : NULL; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return &sensor->format; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int s5k6a3_set_fmt(struct v4l2_subdev *sd, 1368c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 1378c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct s5k6a3 *sensor = sd_to_s5k6a3(sd); 1408c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci s5k6a3_try_format(&fmt->format); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci mf = __s5k6a3_get_format(sensor, cfg, fmt->pad, fmt->which); 1458c2ecf20Sopenharmony_ci if (mf) { 1468c2ecf20Sopenharmony_ci mutex_lock(&sensor->lock); 1478c2ecf20Sopenharmony_ci *mf = fmt->format; 1488c2ecf20Sopenharmony_ci mutex_unlock(&sensor->lock); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int s5k6a3_get_fmt(struct v4l2_subdev *sd, 1548c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 1558c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct s5k6a3 *sensor = sd_to_s5k6a3(sd); 1588c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci mf = __s5k6a3_get_format(sensor, cfg, fmt->pad, fmt->which); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci mutex_lock(&sensor->lock); 1638c2ecf20Sopenharmony_ci fmt->format = *mf; 1648c2ecf20Sopenharmony_ci mutex_unlock(&sensor->lock); 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops s5k6a3_pad_ops = { 1698c2ecf20Sopenharmony_ci .enum_mbus_code = s5k6a3_enum_mbus_code, 1708c2ecf20Sopenharmony_ci .get_fmt = s5k6a3_get_fmt, 1718c2ecf20Sopenharmony_ci .set_fmt = s5k6a3_set_fmt, 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, fh->pad, 0); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci *format = s5k6a3_formats[0]; 1798c2ecf20Sopenharmony_ci format->width = S5K6A3_DEFAULT_WIDTH; 1808c2ecf20Sopenharmony_ci format->height = S5K6A3_DEFAULT_HEIGHT; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops s5k6a3_sd_internal_ops = { 1868c2ecf20Sopenharmony_ci .open = s5k6a3_open, 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int __s5k6a3_power_on(struct s5k6a3 *sensor) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci int i = S5K6A3_SUPP_VDDA; 1928c2ecf20Sopenharmony_ci int ret; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ret = clk_set_rate(sensor->clock, sensor->clock_frequency); 1958c2ecf20Sopenharmony_ci if (ret < 0) 1968c2ecf20Sopenharmony_ci return ret; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci ret = pm_runtime_get(sensor->dev); 1998c2ecf20Sopenharmony_ci if (ret < 0) 2008c2ecf20Sopenharmony_ci goto error_rpm_put; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci ret = regulator_enable(sensor->supplies[i].consumer); 2038c2ecf20Sopenharmony_ci if (ret < 0) 2048c2ecf20Sopenharmony_ci goto error_rpm_put; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sensor->clock); 2078c2ecf20Sopenharmony_ci if (ret < 0) 2088c2ecf20Sopenharmony_ci goto error_reg_dis; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci for (i++; i < S5K6A3_NUM_SUPPLIES; i++) { 2118c2ecf20Sopenharmony_ci ret = regulator_enable(sensor->supplies[i].consumer); 2128c2ecf20Sopenharmony_ci if (ret < 0) 2138c2ecf20Sopenharmony_ci goto error_reg_dis; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci gpio_set_value(sensor->gpio_reset, 1); 2178c2ecf20Sopenharmony_ci usleep_range(600, 800); 2188c2ecf20Sopenharmony_ci gpio_set_value(sensor->gpio_reset, 0); 2198c2ecf20Sopenharmony_ci usleep_range(600, 800); 2208c2ecf20Sopenharmony_ci gpio_set_value(sensor->gpio_reset, 1); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* Delay needed for the sensor initialization */ 2238c2ecf20Sopenharmony_ci msleep(20); 2248c2ecf20Sopenharmony_ci return 0; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cierror_reg_dis: 2278c2ecf20Sopenharmony_ci for (--i; i >= 0; --i) 2288c2ecf20Sopenharmony_ci regulator_disable(sensor->supplies[i].consumer); 2298c2ecf20Sopenharmony_cierror_rpm_put: 2308c2ecf20Sopenharmony_ci pm_runtime_put(sensor->dev); 2318c2ecf20Sopenharmony_ci return ret; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int __s5k6a3_power_off(struct s5k6a3 *sensor) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci int i; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci gpio_set_value(sensor->gpio_reset, 0); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci for (i = S5K6A3_NUM_SUPPLIES - 1; i >= 0; i--) 2418c2ecf20Sopenharmony_ci regulator_disable(sensor->supplies[i].consumer); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci clk_disable_unprepare(sensor->clock); 2448c2ecf20Sopenharmony_ci pm_runtime_put(sensor->dev); 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int s5k6a3_s_power(struct v4l2_subdev *sd, int on) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct s5k6a3 *sensor = sd_to_s5k6a3(sd); 2518c2ecf20Sopenharmony_ci int ret = 0; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci mutex_lock(&sensor->lock); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (sensor->power_count == !on) { 2568c2ecf20Sopenharmony_ci if (on) 2578c2ecf20Sopenharmony_ci ret = __s5k6a3_power_on(sensor); 2588c2ecf20Sopenharmony_ci else 2598c2ecf20Sopenharmony_ci ret = __s5k6a3_power_off(sensor); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (ret == 0) 2628c2ecf20Sopenharmony_ci sensor->power_count += on ? 1 : -1; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci mutex_unlock(&sensor->lock); 2668c2ecf20Sopenharmony_ci return ret; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops s5k6a3_core_ops = { 2708c2ecf20Sopenharmony_ci .s_power = s5k6a3_s_power, 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops s5k6a3_subdev_ops = { 2748c2ecf20Sopenharmony_ci .core = &s5k6a3_core_ops, 2758c2ecf20Sopenharmony_ci .pad = &s5k6a3_pad_ops, 2768c2ecf20Sopenharmony_ci}; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int s5k6a3_probe(struct i2c_client *client) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 2818c2ecf20Sopenharmony_ci struct s5k6a3 *sensor; 2828c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 2838c2ecf20Sopenharmony_ci int gpio, i, ret; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); 2868c2ecf20Sopenharmony_ci if (!sensor) 2878c2ecf20Sopenharmony_ci return -ENOMEM; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci mutex_init(&sensor->lock); 2908c2ecf20Sopenharmony_ci sensor->gpio_reset = -EINVAL; 2918c2ecf20Sopenharmony_ci sensor->clock = ERR_PTR(-EINVAL); 2928c2ecf20Sopenharmony_ci sensor->dev = dev; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci sensor->clock = devm_clk_get(sensor->dev, S5K6A3_CLK_NAME); 2958c2ecf20Sopenharmony_ci if (IS_ERR(sensor->clock)) 2968c2ecf20Sopenharmony_ci return PTR_ERR(sensor->clock); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci gpio = of_get_gpio_flags(dev->of_node, 0, NULL); 2998c2ecf20Sopenharmony_ci if (!gpio_is_valid(gpio)) 3008c2ecf20Sopenharmony_ci return gpio; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW, 3038c2ecf20Sopenharmony_ci S5K6A3_DRV_NAME); 3048c2ecf20Sopenharmony_ci if (ret < 0) 3058c2ecf20Sopenharmony_ci return ret; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci sensor->gpio_reset = gpio; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (of_property_read_u32(dev->of_node, "clock-frequency", 3108c2ecf20Sopenharmony_ci &sensor->clock_frequency)) { 3118c2ecf20Sopenharmony_ci sensor->clock_frequency = S5K6A3_DEFAULT_CLK_FREQ; 3128c2ecf20Sopenharmony_ci dev_info(dev, "using default %u Hz clock frequency\n", 3138c2ecf20Sopenharmony_ci sensor->clock_frequency); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci for (i = 0; i < S5K6A3_NUM_SUPPLIES; i++) 3178c2ecf20Sopenharmony_ci sensor->supplies[i].supply = s5k6a3_supply_names[i]; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci ret = devm_regulator_bulk_get(&client->dev, S5K6A3_NUM_SUPPLIES, 3208c2ecf20Sopenharmony_ci sensor->supplies); 3218c2ecf20Sopenharmony_ci if (ret < 0) 3228c2ecf20Sopenharmony_ci return ret; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci sd = &sensor->subdev; 3258c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(sd, client, &s5k6a3_subdev_ops); 3268c2ecf20Sopenharmony_ci sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 3278c2ecf20Sopenharmony_ci sd->internal_ops = &s5k6a3_sd_internal_ops; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci sensor->format.code = s5k6a3_formats[0].code; 3308c2ecf20Sopenharmony_ci sensor->format.width = S5K6A3_DEFAULT_WIDTH; 3318c2ecf20Sopenharmony_ci sensor->format.height = S5K6A3_DEFAULT_HEIGHT; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; 3348c2ecf20Sopenharmony_ci sensor->pad.flags = MEDIA_PAD_FL_SOURCE; 3358c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad); 3368c2ecf20Sopenharmony_ci if (ret < 0) 3378c2ecf20Sopenharmony_ci return ret; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci pm_runtime_no_callbacks(dev); 3408c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci ret = v4l2_async_register_subdev(sd); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (ret < 0) { 3458c2ecf20Sopenharmony_ci pm_runtime_disable(&client->dev); 3468c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci return ret; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int s5k6a3_remove(struct i2c_client *client) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci pm_runtime_disable(&client->dev); 3578c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(sd); 3588c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic const struct i2c_device_id s5k6a3_ids[] = { 3638c2ecf20Sopenharmony_ci { } 3648c2ecf20Sopenharmony_ci}; 3658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, s5k6a3_ids); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 3688c2ecf20Sopenharmony_cistatic const struct of_device_id s5k6a3_of_match[] = { 3698c2ecf20Sopenharmony_ci { .compatible = "samsung,s5k6a3" }, 3708c2ecf20Sopenharmony_ci { /* sentinel */ } 3718c2ecf20Sopenharmony_ci}; 3728c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, s5k6a3_of_match); 3738c2ecf20Sopenharmony_ci#endif 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic struct i2c_driver s5k6a3_driver = { 3768c2ecf20Sopenharmony_ci .driver = { 3778c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(s5k6a3_of_match), 3788c2ecf20Sopenharmony_ci .name = S5K6A3_DRV_NAME, 3798c2ecf20Sopenharmony_ci }, 3808c2ecf20Sopenharmony_ci .probe_new = s5k6a3_probe, 3818c2ecf20Sopenharmony_ci .remove = s5k6a3_remove, 3828c2ecf20Sopenharmony_ci .id_table = s5k6a3_ids, 3838c2ecf20Sopenharmony_ci}; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cimodule_i2c_driver(s5k6a3_driver); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("S5K6A3 image sensor subdev driver"); 3888c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); 3898c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 390