All of lore.kernel.org
 help / color / mirror / Atom feed
From: Arun Kumar K <arun.kk@samsung.com>
To: linux-media@vger.kernel.org, linux-samsung-soc@vger.kernel.org,
	devicetree-discuss@lists.ozlabs.org
Cc: s.nawrocki@samsung.com, kgene.kim@samsung.com,
	kilyeon.im@samsung.com, arunkk.samsung@gmail.com
Subject: [RFC 06/12] exynos-fimc-is: Adds the sensor subdev
Date: Fri, 08 Mar 2013 09:59:19 -0500	[thread overview]
Message-ID: <1362754765-2651-7-git-send-email-arun.kk@samsung.com> (raw)
In-Reply-To: <1362754765-2651-1-git-send-email-arun.kk@samsung.com>

FIMC-IS uses certain sensors which are exclusively controlled
from the IS firmware. This patch adds the sensor subdev for the
fimc-is sensors.

Signed-off-by: Arun Kumar K <arun.kk@samsung.com>
Signed-off-by: Kilyeon Im <kilyeon.im@samsung.com>
---
 drivers/media/platform/exynos5-is/fimc-is-sensor.c |  337 ++++++++++++++++++++
 drivers/media/platform/exynos5-is/fimc-is-sensor.h |  170 ++++++++++
 2 files changed, 507 insertions(+)
 create mode 100644 drivers/media/platform/exynos5-is/fimc-is-sensor.c
 create mode 100644 drivers/media/platform/exynos5-is/fimc-is-sensor.h

diff --git a/drivers/media/platform/exynos5-is/fimc-is-sensor.c b/drivers/media/platform/exynos5-is/fimc-is-sensor.c
new file mode 100644
index 0000000..c031493
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-sensor.c
@@ -0,0 +1,337 @@
+/*
+ * Samsung EXYNOS5250 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Arun Kumar K <arun.kk@samsung.com>
+ * Kil-yeon Lim <kilyeon.im@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/gpio.h>
+#include "fimc-is-sensor.h"
+#include "fimc-is.h"
+
+static char *sensor_clock_name[] = {
+	[SCLK_BAYER]	= "sclk_bayer",
+	[SCLK_CAM0]	= "sclk_cam0",
+	[SCLK_CAM1]	= "sclk_cam1",
+};
+
+static struct fimc_is_sensor_info sensor_info[] = {
+	[SENSOR_S5K4E5] = {
+		.sensor_id = SENSOR_S5K4E5,
+		.sensor_name = "samsung,s5k4e5",
+		.pixel_width = SENSOR_4E5_WIDTH + 16,
+		.pixel_height = SENSOR_4E5_HEIGHT + 10,
+		.active_width = SENSOR_4E5_WIDTH,
+		.active_height = SENSOR_4E5_HEIGHT,
+		.max_framerate = 30,
+		.setfile_name = "setfile_4e5.bin",
+		.ext = {
+			.actuator_con = {
+				.product_name = ACTUATOR_NAME_DWXXXX,
+				.peri_type = SE_I2C,
+				.peri_setting.i2c.channel = SENSOR_CONTROL_I2C0,
+			},
+			.flash_con = {
+				.product_name = FLADRV_NAME_KTD267,
+				.peri_type = SE_GPIO,
+				.peri_setting.gpio.first_gpio_port_no = 1,
+				.peri_setting.gpio.second_gpio_port_no = 2,
+			},
+			.from_con.product_name = FROMDRV_NAME_NOTHING,
+			.mclk = 0,
+			.mipi_lane_num = 0,
+			.mipi_speed = 0,
+			.fast_open_sensor = 0,
+			.self_calibration_mode = 0,
+		},
+
+	},
+	[SENSOR_S5K6A3] = {
+		.sensor_id = SENSOR_S5K6A3,
+		.sensor_name = "samsung,s5k6a3",
+		.pixel_width = SENSOR_6A3_WIDTH + 16,
+		.pixel_height = SENSOR_6A3_HEIGHT + 10,
+		.active_width = SENSOR_6A3_WIDTH,
+		.active_height = SENSOR_6A3_HEIGHT,
+		.max_framerate = 30,
+		.setfile_name = "setfile_6a3.bin",
+	},
+};
+
+/* Sensor supported formats */
+static struct v4l2_mbus_framefmt sensor_formats[FIMC_IS_MAX_SENSORS] = {
+	[SENSOR_S5K4E5] = {
+		.width          = SENSOR_4E5_WIDTH + 16,
+		.height         = SENSOR_4E5_HEIGHT + 10,
+		.code           = V4L2_MBUS_FMT_SGRBG10_1X10,
+		.field          = V4L2_FIELD_NONE,
+		.colorspace     = V4L2_COLORSPACE_SRGB,
+	},
+	[SENSOR_S5K6A3] = {
+		.width          = SENSOR_6A3_WIDTH + 16,
+		.height         = SENSOR_6A3_HEIGHT + 10,
+		.code           = V4L2_MBUS_FMT_SGRBG10_1X10,
+		.field          = V4L2_FIELD_NONE,
+		.colorspace     = V4L2_COLORSPACE_SRGB,
+	},
+};
+
+static struct fimc_is_sensor *sd_to_fimc_is_sensor(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct fimc_is_sensor, subdev);
+}
+
+static void sensor_clk_put(struct fimc_is_sensor *sensor)
+{
+	int i;
+
+	for (i = 0; i < SCLK_MAX_NUM; i++) {
+		if (IS_ERR_OR_NULL(sensor->clock[i]))
+			continue;
+		clk_unprepare(sensor->clock[i]);
+		clk_put(sensor->clock[i]);
+		sensor->clock[i] = NULL;
+	}
+}
+
+static int sensor_clk_init(struct fimc_is_sensor *sensor)
+{
+	int i, ret;
+
+	/* Get CAM clocks */
+	for (i = 0; i < SCLK_MAX_NUM; i++) {
+		sensor->clock[i] = clk_get(NULL, sensor_clock_name[i]);
+		if (IS_ERR(sensor->clock[i]))
+			goto err;
+		ret = clk_prepare(sensor->clock[i]);
+		if (ret < 0) {
+			clk_put(sensor->clock[i]);
+			sensor->clock[i] = NULL;
+			goto err;
+		}
+	}
+
+	/* Set clock rates */
+	ret = clk_set_rate(sensor->clock[SCLK_CAM0], 24 * 1000000);
+	ret |= clk_set_rate(sensor->clock[SCLK_BAYER], 24 * 1000000);
+	if (ret) {
+		is_err("Failed to set cam clock rates\n");
+		goto err;
+	}
+	return 0;
+err:
+	sensor_clk_put(sensor);
+	is_err("Failed to init sensor clock\n");
+	return -ENXIO;
+}
+
+static int sensor_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_fh *fh,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
+	struct fimc_is_sensor_info *sinfo = sensor->sensor_info;
+
+	if (!code)
+		return -EINVAL;
+
+	code->code = sensor_formats[sinfo->sensor_id].code;
+	return 0;
+}
+
+static int sensor_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_fh *fh,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
+	struct fimc_is_sensor_info *sinfo = sensor->sensor_info;
+	struct v4l2_mbus_framefmt *sfmt = &fmt->format;
+
+	if ((sfmt->width != sensor_formats[sinfo->sensor_id].width) ||
+		(sfmt->height != sensor_formats[sinfo->sensor_id].height) ||
+		(sfmt->code != sensor_formats[sinfo->sensor_id].code))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int sensor_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_fh *fh,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
+	struct fimc_is_sensor_info *sinfo = sensor->sensor_info;
+
+	fmt->format = sensor_formats[sinfo->sensor_id];
+	return 0;
+}
+
+static struct v4l2_subdev_pad_ops sensor_pad_ops = {
+	.enum_mbus_code		= sensor_enum_mbus_code,
+	.get_fmt		= sensor_get_fmt,
+	.set_fmt		= sensor_set_fmt,
+};
+
+static int sensor_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	return 0;
+}
+
+static const struct v4l2_subdev_internal_ops sensor_sd_internal_ops = {
+	.open = sensor_open,
+};
+
+static int sensor_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
+	struct fimc_is_sensor_data *sdata = sensor->sensor_data;
+
+	if (on) {
+		/* Power on sensor */
+		sensor_clk_init(sensor);
+		gpio_request(sdata->gpios[0], "fimc_is_sensor");
+		gpio_direction_output(sdata->gpios[0], 1);
+		gpio_free(sdata->gpios[0]);
+	} else {
+		/* Power off sensor */
+		gpio_request(sdata->gpios[0], "fimc_is_sensor");
+		gpio_direction_output(sdata->gpios[0], 0);
+		gpio_free(sdata->gpios[0]);
+		sensor_clk_put(sensor);
+	}
+	return 0;
+}
+
+static struct v4l2_subdev_core_ops sensor_core_ops = {
+	.s_power = sensor_s_power,
+};
+
+static int sensor_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd);
+	int ret;
+
+	if (enable) {
+		is_dbg(3, "Stream ON\n");
+		/* Open pipeline */
+		ret = fimc_is_pipeline_open(sensor->pipeline, sensor);
+		if (ret < 0) {
+			is_err("Pipeline already opened.\n");
+			return -EBUSY;
+		}
+
+		/* Start IS pipeline */
+		ret = fimc_is_pipeline_start(sensor->pipeline);
+		if (ret < 0) {
+			is_err("Pipeline start failed.\n");
+			return -EINVAL;
+		}
+	} else {
+		is_dbg(3, "Stream OFF\n");
+		/* Stop IS pipeline */
+		ret = fimc_is_pipeline_stop(sensor->pipeline);
+		if (ret < 0) {
+			is_err("Pipeline stop failed.\n");
+			return -EINVAL;
+		}
+
+		/* Close pipeline */
+		ret = fimc_is_pipeline_close(sensor->pipeline);
+		if (ret < 0) {
+			is_err("Pipeline close failed\n");
+			return -EBUSY;
+		}
+	}
+
+	return 0;
+}
+
+static const struct v4l2_subdev_video_ops sensor_video_ops = {
+	.s_stream       = sensor_s_stream,
+};
+
+static struct v4l2_subdev_ops sensor_subdev_ops = {
+	.core = &sensor_core_ops,
+	.pad = &sensor_pad_ops,
+	.video = &sensor_video_ops,
+};
+
+static int sensor_init(struct fimc_is_sensor *sensor)
+{
+	struct fimc_is_sensor_data *sensor_data;
+
+	sensor_data = sensor->sensor_data;
+	if (strcmp(sensor_data->name,
+			sensor_info[SENSOR_S5K4E5].sensor_name) == 0)
+		sensor->sensor_info = &sensor_info[SENSOR_S5K4E5];
+	else if (strcmp(sensor_data->name,
+			sensor_info[SENSOR_S5K6A3].sensor_name) == 0)
+		sensor->sensor_info = &sensor_info[SENSOR_S5K6A3];
+	else
+		sensor->sensor_info = NULL;
+
+	if (!sensor->sensor_info)
+		return -EINVAL;
+
+	sensor->sensor_info->csi_ch = sensor->sensor_info->i2c_ch =
+		(sensor_data->csi_id >> 2) & 0x1;
+	is_dbg(3, "Sensor csi channel : %d\n", sensor->sensor_info->csi_ch);
+
+	return 0;
+}
+
+int fimc_is_sensor_subdev_create(struct fimc_is_sensor *sensor,
+		struct fimc_is_sensor_data *sensor_data,
+		struct fimc_is_pipeline *pipeline)
+{
+	struct v4l2_subdev *sd = &sensor->subdev;
+	int ret;
+
+	is_err("\n");
+	if (!sensor_data->enabled) {
+		/* Sensor not present */
+		return -EINVAL;
+	}
+
+	sensor->sensor_data = sensor_data;
+	sensor->pipeline = pipeline;
+
+	v4l2_subdev_init(sd, &sensor_subdev_ops);
+	sensor->subdev.owner = THIS_MODULE;
+	if (strcmp(sensor_data->name,
+			sensor_info[SENSOR_S5K4E5].sensor_name) == 0)
+		strlcpy(sd->name, "fimc-is-sensor-4e5", sizeof(sd->name));
+	else if (strcmp(sensor_data->name,
+			sensor_info[SENSOR_S5K6A3].sensor_name) == 0)
+		strlcpy(sd->name, "fimc-is-sensor-6a3", sizeof(sd->name));
+	else
+		strlcpy(sd->name, "fimc-is-sensor-???", sizeof(sd->name));
+	sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0);
+	if (ret < 0)
+		goto exit;
+
+	v4l2_set_subdevdata(sd, sensor);
+
+	/* Init sensor data */
+	ret = sensor_init(sensor);
+	if (ret < 0) {
+		is_err("Sensor init failed.\n");
+		goto exit;
+	}
+
+	return 0;
+exit:
+	return ret;
+}
+
+void fimc_is_sensor_subdev_destroy(struct fimc_is_sensor *sensor)
+{
+	media_entity_cleanup(&sensor->subdev.entity);
+}
diff --git a/drivers/media/platform/exynos5-is/fimc-is-sensor.h b/drivers/media/platform/exynos5-is/fimc-is-sensor.h
new file mode 100644
index 0000000..d147ff8
--- /dev/null
+++ b/drivers/media/platform/exynos5-is/fimc-is-sensor.h
@@ -0,0 +1,170 @@
+/*
+ * Samsung EXYNOS5250 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Arun Kumar K <arun.kk@samsung.com>
+ * Kil-yeon Lim <kilyeon.im@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.
+ */
+#ifndef FIMC_IS_SENSOR_H_
+#define FIMC_IS_SENSOR_H_
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-subdev.h>
+
+#include "fimc-is-pipeline.h"
+
+#define FIMC_IS_MAX_CAMIF_CLIENTS	2
+#define FIMC_IS_MAX_NAME_LEN		32
+#define FIMC_IS_MAX_GPIO_NUM		32
+#define UART_ISP_SEL			0
+#define UART_ISP_RATIO			1
+
+#define FIMC_IS_MAX_SENSORS		4
+
+#define SENSOR_4E5_WIDTH		2560
+#define SENSOR_4E5_HEIGHT		1920
+#define SENSOR_6A3_WIDTH		1392
+#define SENSOR_6A3_HEIGHT		1392
+
+enum sensor_id {
+	SENSOR_S5K3H2	= 1,
+	SENSOR_S5K6A3	= 2,
+	SENSOR_S5K4E5	= 3,
+	SENSOR_S5K3H7	= 4,
+	SENSOR_CUSTOM	= 100,
+	SENSOR_END
+};
+
+enum sensor_channel {
+	SENSOR_CONTROL_I2C0	 = 0,
+	SENSOR_CONTROL_I2C1	 = 1
+};
+
+enum actuator_name {
+	ACTUATOR_NAME_AD5823	= 1,
+	ACTUATOR_NAME_DWXXXX	= 2,
+	ACTUATOR_NAME_AK7343	= 3,
+	ACTUATOR_NAME_HYBRIDVCA	= 4,
+	ACTUATOR_NAME_NOTHING	= 100,
+	ACTUATOR_NAME_END
+};
+
+enum flash_drv_name {
+	FLADRV_NAME_KTD267	= 1,
+	FLADRV_NAME_NOTHING	= 100,
+	FLADRV_NAME_END
+};
+
+enum from_name {
+	FROMDRV_NAME_W25Q80BW	= 1,
+	FROMDRV_NAME_NOTHING
+};
+
+enum sensor_peri_type {
+	SE_I2C,
+	SE_SPI,
+	SE_GPIO,
+	SE_MPWM,
+	SE_ADC,
+	SE_NULL
+};
+
+struct i2c_type {
+	u32 channel;
+	u32 slave_address;
+	u32 speed;
+};
+
+struct spi_type {
+	u32 channel;
+};
+
+struct gpio_type {
+	u32 first_gpio_port_no;
+	u32 second_gpio_port_no;
+};
+
+union sensor_peri_format {
+	struct i2c_type i2c;
+	struct spi_type spi;
+	struct gpio_type gpio;
+};
+
+struct sensor_protocol {
+	unsigned int product_name;
+	enum sensor_peri_type peri_type;
+	union sensor_peri_format peri_setting;
+};
+
+struct fimc_is_sensor_ext {
+	struct sensor_protocol actuator_con;
+	struct sensor_protocol flash_con;
+	struct sensor_protocol from_con;
+
+	unsigned int mclk;
+	unsigned int mipi_lane_num;
+	unsigned int mipi_speed;
+	unsigned int fast_open_sensor;
+	unsigned int self_calibration_mode;
+};
+
+struct fimc_is_sensor_info {
+	unsigned int	sensor_id;
+	char		*sensor_name;
+	unsigned int	pixel_width;
+	unsigned int	pixel_height;
+	unsigned int	active_width;
+	unsigned int	active_height;
+	unsigned int	max_framerate;
+	unsigned int	csi_ch;
+	unsigned int	flite_ch;
+	unsigned int	i2c_ch;
+	struct fimc_is_sensor_ext ext;
+	char		*setfile_name;
+};
+
+enum sensor_clks {
+	SCLK_BAYER,
+	SCLK_CAM0,
+	SCLK_CAM1,
+	SCLK_MAX_NUM,
+};
+
+struct sensor_pix_format {
+	enum v4l2_mbus_pixelcode code;
+};
+
+/**
+ * struct fimc_is_sensor - fimc-is sensor context
+ * @pad: media pad
+ * @subdev: sensor subdev
+ * @clock: sensor clocks array
+ * @pipeline: is pipeline context pointer
+ * @sensor_info: fimc-is sensor config information
+ * @sensor_data: platform data received for sensor
+ */
+struct fimc_is_sensor {
+	struct media_pad		pad;
+	struct v4l2_subdev		subdev;
+	struct clk			*clock[SCLK_MAX_NUM];
+
+	struct fimc_is_pipeline		*pipeline;
+	struct fimc_is_sensor_info	*sensor_info;
+	struct fimc_is_sensor_data	*sensor_data;
+};
+
+int fimc_is_sensor_subdev_create(struct fimc_is_sensor *sensor,
+		struct fimc_is_sensor_data *sensor_data,
+		struct fimc_is_pipeline *pipeline);
+void fimc_is_sensor_subdev_destroy(struct fimc_is_sensor *sensor);
+
+#endif /* FIMC_IS_SENSOR_H_ */
-- 
1.7.9.5

  parent reply	other threads:[~2013-03-08 14:59 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-03-08 14:59 [RFC 00/12] Exynos5 FIMC-IS driver Arun Kumar K
2013-03-08 14:59 ` [RFC 01/12] exynos-fimc-is: Adding device tree nodes Arun Kumar K
2013-03-23 13:14   ` Sylwester Nawrocki
2013-03-26 12:17     ` Arun Kumar K
2013-03-26 22:51       ` Sylwester Nawrocki
2013-03-27  4:31         ` Arun Kumar K
2013-03-27 13:47           ` Sylwester Nawrocki
2013-03-28  5:10             ` Arun Kumar K
2013-03-29  0:30               ` Sylwester Nawrocki
2013-04-10  4:32                 ` Arun Kumar K
2013-03-08 14:59 ` [RFC 02/12] exynos-fimc-is: Adding ARCH support for fimc-is Arun Kumar K
2013-03-08 14:59 ` [RFC 03/12] exynos-fimc-is: Adds fimc-is driver core files Arun Kumar K
2013-03-23 13:41   ` Sylwester Nawrocki
2013-03-08 14:59 ` [RFC 04/12] exynos-fimc-is: Adds common driver header files Arun Kumar K
2013-03-23 14:05   ` Sylwester Nawrocki
2013-03-08 14:59 ` [RFC 05/12] exynos-fimc-is: Adds the register definition and context header Arun Kumar K
2013-03-23 14:20   ` Sylwester Nawrocki
2013-03-08 14:59 ` Arun Kumar K [this message]
2013-03-23 14:48   ` [RFC 06/12] exynos-fimc-is: Adds the sensor subdev Sylwester Nawrocki
2013-03-08 14:59 ` [RFC 07/12] exynos-fimc-is: Adds isp subdev Arun Kumar K
2013-03-23 18:38   ` Sylwester Nawrocki
2013-03-08 14:59 ` [RFC 08/12] exynos-fimc-is: Adds scaler subdev Arun Kumar K
2013-03-08 14:59 ` [RFC 09/12] exynos-fimc-is: Adds the hardware pipeline control Arun Kumar K
2013-03-08 14:59 ` [RFC 10/12] exynos-fimc-is: Adds the hardware interface module Arun Kumar K
2013-03-23 19:01   ` Sylwester Nawrocki
2013-03-08 14:59 ` [RFC 11/12] exynos-fimc-is: Adds the Kconfig and Makefile Arun Kumar K
2013-03-23 19:02   ` Sylwester Nawrocki
2013-03-08 14:59 ` [RFC 12/12] mipi-csis: Enable all interrupts for fimc-is usage Arun Kumar K
2013-03-12 16:01   ` Sylwester Nawrocki
2013-03-13  4:09     ` Arun Kumar K
2013-04-03 12:31       ` Sylwester Nawrocki

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1362754765-2651-7-git-send-email-arun.kk@samsung.com \
    --to=arun.kk@samsung.com \
    --cc=arunkk.samsung@gmail.com \
    --cc=devicetree-discuss@lists.ozlabs.org \
    --cc=kgene.kim@samsung.com \
    --cc=kilyeon.im@samsung.com \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-samsung-soc@vger.kernel.org \
    --cc=s.nawrocki@samsung.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.