Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/media/i2c/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 9 kB image not shown  

Quelle  s5k6a3.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Samsung S5K6A3 image sensor driver
 *
 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
 * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
 */


#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.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
 * @clock: pointer to &struct clk.
 * @clock_frequency: clock frequency
 * @power_count: stores state if device is powered
 */

struct s5k6a3 {
 struct device *dev;
 struct v4l2_subdev subdev;
 struct media_pad pad;
 struct regulator_bulk_data supplies[S5K6A3_NUM_SUPPLIES];
 struct gpio_desc *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);
}

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];
}

static int s5k6a3_enum_mbus_code(struct v4l2_subdev *sd,
      struct v4l2_subdev_state *sd_state,
      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;
}

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);
}

static struct v4l2_mbus_framefmt *__s5k6a3_get_format(
  struct s5k6a3 *sensor, struct v4l2_subdev_state *sd_state,
  u32 pad, enum v4l2_subdev_format_whence which)
{
 if (which == V4L2_SUBDEV_FORMAT_TRY)
  return sd_state ? v4l2_subdev_state_get_format(sd_state, pad) : NULL;

 return &sensor->format;
}

static int s5k6a3_set_fmt(struct v4l2_subdev *sd,
      struct v4l2_subdev_state *sd_state,
      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, sd_state, fmt->pad, fmt->which);
 if (mf) {
  mutex_lock(&sensor->lock);
  *mf = fmt->format;
  mutex_unlock(&sensor->lock);
 }
 return 0;
}

static int s5k6a3_get_fmt(struct v4l2_subdev *sd,
     struct v4l2_subdev_state *sd_state,
     struct v4l2_subdev_format *fmt)
{
 struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
 struct v4l2_mbus_framefmt *mf;

 mf = __s5k6a3_get_format(sensor, sd_state, fmt->pad, fmt->which);

 mutex_lock(&sensor->lock);
 fmt->format = *mf;
 mutex_unlock(&sensor->lock);
 return 0;
}

static const 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_state_get_format(fh->state,
          0);

 *format  = s5k6a3_formats[0];
 format->width = S5K6A3_DEFAULT_WIDTH;
 format->height = S5K6A3_DEFAULT_HEIGHT;

 return 0;
}

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)
  goto error_rpm_put;

 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_clk;
 }

 gpiod_set_value_cansleep(sensor->gpio_reset, 0);
 usleep_range(600, 800);
 gpiod_set_value_cansleep(sensor->gpio_reset, 1);
 usleep_range(600, 800);
 gpiod_set_value_cansleep(sensor->gpio_reset, 0);

 /* Delay needed for the sensor initialization */
 msleep(20);
 return 0;

error_clk:
 clk_disable_unprepare(sensor->clock);
error_reg_dis:
 for (--i; i >= 0; --i)
  regulator_disable(sensor->supplies[i].consumer);
error_rpm_put:
 pm_runtime_put(sensor->dev);
 return ret;
}

static int __s5k6a3_power_off(struct s5k6a3 *sensor)
{
 int i;

 gpiod_set_value_cansleep(sensor->gpio_reset, 1);

 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;
}

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;
}

static const struct v4l2_subdev_core_ops s5k6a3_core_ops = {
 .s_power = s5k6a3_s_power,
};

static const struct v4l2_subdev_ops s5k6a3_subdev_ops = {
 .core = &s5k6a3_core_ops,
 .pad = &s5k6a3_pad_ops,
};

static int s5k6a3_probe(struct i2c_client *client)
{
 struct device *dev = &client->dev;
 struct s5k6a3 *sensor;
 struct v4l2_subdev *sd;
 int i, ret;

 sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
 if (!sensor)
  return -ENOMEM;

 mutex_init(&sensor->lock);
 sensor->dev = dev;

 sensor->clock = devm_clk_get(sensor->dev, S5K6A3_CLK_NAME);
 if (IS_ERR(sensor->clock))
  return PTR_ERR(sensor->clock);

 sensor->gpio_reset = devm_gpiod_get(dev, NULL, GPIOD_OUT_HIGH);
 ret = PTR_ERR_OR_ZERO(sensor->gpio_reset);
 if (ret)
  return ret;

 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;

 sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
 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;
}

static void 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);
}

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,
 },
 .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 ");
MODULE_LICENSE("GPL v2");

Messung V0.5
C=95 H=94 G=94

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.