linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/2] media: Introduce Omnivision OV2680 driver
@ 2018-03-13 11:33 Rui Miguel Silva
  2018-03-13 11:33 ` [PATCH v3 1/2] media: ov2680: dt: Add bindings for OV2680 Rui Miguel Silva
  2018-03-13 11:33 ` [PATCH v3 2/2] media: ov2680: Add Omnivision OV2680 sensor driver Rui Miguel Silva
  0 siblings, 2 replies; 10+ messages in thread
From: Rui Miguel Silva @ 2018-03-13 11:33 UTC (permalink / raw)
  To: mchehab, sakari.ailus, hverkuil
  Cc: linux-media, linux-kernel, Ryan Harkin, Rui Miguel Silva

Add driver and bindings for the OV2680 2 megapixel CMOS 1/5" sensor, which has
a single MIPI lane interface and output format of 10-bit Raw RGB.

Features supported are described in PATCH 2/2.

v2->v3:
Rob Herring:
    - add Reviewed-by tag to dts PATCH 1/1

Sakari Ailus:
    - align register values with bracket
    - redone the {write|read}_reg i2c functions
    - add bayer order handling with flip and mirror controls
    - fix error path in probe release resources
    - remove i2c_device_id and use probe_new

Myself:
    - remove ; at the end of macros
    
v1->v2:
Fabio Estevam:
    - s/OV5640/OV2680 in PATCH 1/2 changelog

Sakari Ailus:
    - add description on endpoint properties in bindings
    - add single endpoint in bindings
    - drop OF dependency
    - cleanup includes
    - fix case in Color Bars
    - remove frame rate selection
    - 8/16/24 bit register access in the same transaction
    - merge _reset and _soft_reset to _enable and rename it to power_on 
    - _gain_set use only the gain value (drop & 0x7ff)
    - _gain_get remove the (0x377)
    - single write/read at _exposure_set/get use write_reg24/read_reg24
    - move mode_set_direct to _mode_set
    - _mode_set set auto exposure/gain based on ctrl value
    - s_frame_interval equal to g_frame_interval
    - use closest match from: v4l: common: Add a function to obtain best size from a list 
    - check v4l2_ctrl_new_std return in _init

    - fix gain manual value in auto_cluster

Cheers,
    Rui

Rui Miguel Silva (2):
  media: ov2680: dt: Add bindings for OV2680
  media: ov2680: Add Omnivision OV2680 sensor driver

 .../devicetree/bindings/media/i2c/ov2680.txt       |   40 +
 drivers/media/i2c/Kconfig                          |   12 +
 drivers/media/i2c/Makefile                         |    1 +
 drivers/media/i2c/ov2680.c                         | 1130 ++++++++++++++++++++
 4 files changed, 1183 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ov2680.txt
 create mode 100644 drivers/media/i2c/ov2680.c

-- 
2.16.2

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v3 1/2] media: ov2680: dt: Add bindings for OV2680
  2018-03-13 11:33 [PATCH v3 0/2] media: Introduce Omnivision OV2680 driver Rui Miguel Silva
@ 2018-03-13 11:33 ` Rui Miguel Silva
  2018-03-13 11:33 ` [PATCH v3 2/2] media: ov2680: Add Omnivision OV2680 sensor driver Rui Miguel Silva
  1 sibling, 0 replies; 10+ messages in thread
From: Rui Miguel Silva @ 2018-03-13 11:33 UTC (permalink / raw)
  To: mchehab, sakari.ailus, hverkuil
  Cc: linux-media, linux-kernel, Ryan Harkin, Rui Miguel Silva, devicetree

Add device tree binding documentation for the OV2680 camera sensor.

Reviewed-by: Rob Herring <robh@kernel.org>
CC: devicetree@vger.kernel.org
Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org>
---
 .../devicetree/bindings/media/i2c/ov2680.txt       | 40 ++++++++++++++++++++++
 1 file changed, 40 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ov2680.txt

diff --git a/Documentation/devicetree/bindings/media/i2c/ov2680.txt b/Documentation/devicetree/bindings/media/i2c/ov2680.txt
new file mode 100644
index 000000000000..0e29f1a113c0
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ov2680.txt
@@ -0,0 +1,40 @@
+* Omnivision OV2680 MIPI CSI-2 sensor
+
+Required Properties:
+- compatible: should be "ovti,ov2680".
+- clocks: reference to the xvclk input clock.
+- clock-names: should be "xvclk".
+
+Optional Properties:
+- powerdown-gpios: reference to the GPIO connected to the powerdown pin,
+		     if any. This is an active high signal to the OV2680.
+
+The device node must contain one 'port' child node for its digital output
+video port, and this port must have a single endpoint in accordance with
+ the video interface bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Endpoint node required properties for CSI-2 connection are:
+- remote-endpoint: a phandle to the bus receiver's endpoint node.
+- clock-lanes: should be set to <0> (clock lane on hardware lane 0).
+- data-lanes: should be set to <1> (one CSI-2 lane supported).
+ 
+Example:
+
+&i2c2 {
+	ov2680: camera-sensor@36 {
+		compatible = "ovti,ov2680";
+		reg = <0x36>;
+		clocks = <&osc>;
+		clock-names = "xvclk";
+		powerdown-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>;
+
+		port {
+			ov2680_mipi_ep: endpoint {
+				remote-endpoint = <&mipi_sensor_ep>;
+				clock-lanes = <0>;
+				data-lanes = <1>;
+			};
+		};
+	};
+};
-- 
2.16.2

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v3 2/2] media: ov2680: Add Omnivision OV2680 sensor driver
  2018-03-13 11:33 [PATCH v3 0/2] media: Introduce Omnivision OV2680 driver Rui Miguel Silva
  2018-03-13 11:33 ` [PATCH v3 1/2] media: ov2680: dt: Add bindings for OV2680 Rui Miguel Silva
@ 2018-03-13 11:33 ` Rui Miguel Silva
  2018-03-14 19:39   ` kbuild test robot
                     ` (2 more replies)
  1 sibling, 3 replies; 10+ messages in thread
From: Rui Miguel Silva @ 2018-03-13 11:33 UTC (permalink / raw)
  To: mchehab, sakari.ailus, hverkuil
  Cc: linux-media, linux-kernel, Ryan Harkin, Rui Miguel Silva

This patch adds V4L2 sub-device driver for OV2680 image sensor.
The OV2680 is a 1/5" CMOS color sensor from Omnivision.
Supports output format: 10-bit Raw RGB.
The OV2680 has a single lane MIPI interface.

The driver exposes following V4L2 controls:
- auto/manual exposure,
- exposure,
- auto/manual gain,
- gain,
- horizontal/vertical flip,
- test pattern menu.
Supported resolution are only: QUXGA, 720P, UXGA.

Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org>
---
 drivers/media/i2c/Kconfig  |   12 +
 drivers/media/i2c/Makefile |    1 +
 drivers/media/i2c/ov2680.c | 1130 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1143 insertions(+)
 create mode 100644 drivers/media/i2c/ov2680.c

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 9f18cd296841..39dc9f236ffa 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -586,6 +586,18 @@ config VIDEO_OV2659
 	  To compile this driver as a module, choose M here: the
 	  module will be called ov2659.
 
+config VIDEO_OV2680
+	tristate "OmniVision OV2680 sensor support"
+	depends on GPIOLIB && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+	depends on MEDIA_CAMERA_SUPPORT
+	select V4L2_FWNODE
+	---help---
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV2680 camera sensor with a MIPI CSI-2 interface.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ov2680.
+
 config VIDEO_OV5640
 	tristate "OmniVision OV5640 sensor support"
 	depends on OF
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index c0f94cd8d56d..d0aba4d37b8d 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o
 obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
 obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
 obj-$(CONFIG_VIDEO_OV2640) += ov2640.o
+obj-$(CONFIG_VIDEO_OV2680) += ov2680.o
 obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
 obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
 obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c
new file mode 100644
index 000000000000..a17971ce71bc
--- /dev/null
+++ b/drivers/media/i2c/ov2680.c
@@ -0,0 +1,1130 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Omnivision OV2680 CMOS Image Sensor driver
+ *
+ * Copyright (C) 2018 Linaro Ltd
+ *
+ * Based on OV5640 Sensor Driver
+ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2014-2017 Mentor Graphics Inc.
+ *
+ */
+
+#include <asm/unaligned.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/gpio/consumer.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#define OV2680_XVCLK_MIN	6000000
+#define OV2680_XVCLK_MAX	24000000
+
+#define OV2680_CHIP_ID		0x2680
+
+#define OV2680_REG_STREAM_CTRL		0x0100
+#define OV2680_REG_SOFT_RESET		0x0103
+
+#define OV2680_REG_CHIP_ID_HIGH		0x300a
+#define OV2680_REG_CHIP_ID_LOW		0x300b
+
+#define OV2680_REG_R_MANUAL		0x3503
+#define OV2680_REG_GAIN_PK		0x350a
+#define OV2680_REG_EXPOSURE_PK_HIGH	0x3500
+#define OV2680_REG_TIMING_HTS		0x380c
+#define OV2680_REG_TIMING_VTS		0x380e
+#define OV2680_REG_FORMAT1		0x3820
+#define OV2680_REG_FORMAT2		0x3821
+
+#define OV2680_REG_ISP_CTRL00		0x5080
+
+#define OV2680_FRAME_RATE		30
+
+#define OV2680_REG_VALUE_8BIT		1
+#define OV2680_REG_VALUE_16BIT		2
+#define OV2680_REG_VALUE_24BIT		3
+
+enum ov2680_mode_id {
+	OV2680_MODE_QUXGA_800_600,
+	OV2680_MODE_720P_1280_720,
+	OV2680_MODE_UXGA_1600_1200,
+	OV2680_MODE_MAX,
+};
+
+struct reg_value {
+	u16 reg_addr;
+	u8 val;
+};
+
+struct ov2680_mode_info {
+	const char *name;
+	enum ov2680_mode_id id;
+	u32 width;
+	u32 height;
+	const struct reg_value *reg_data;
+	u32 reg_data_size;
+};
+
+struct ov2680_ctrls {
+	struct v4l2_ctrl_handler handler;
+	struct {
+		struct v4l2_ctrl *auto_exp;
+		struct v4l2_ctrl *exposure;
+	};
+	struct {
+		struct v4l2_ctrl *auto_gain;
+		struct v4l2_ctrl *gain;
+	};
+
+	struct v4l2_ctrl *hflip;
+	struct v4l2_ctrl *vflip;
+	struct v4l2_ctrl *test_pattern;
+};
+
+struct ov2680_dev {
+	struct i2c_client		*i2c_client;
+	struct v4l2_subdev		sd;
+
+	struct media_pad		pad;
+	struct clk			*xvclk;
+	u32				xvclk_freq;
+
+	struct gpio_desc		*pwdn_gpio;
+	struct mutex			lock; /* protect members */
+
+	bool				mode_pending_changes;
+	bool				is_enabled;
+	bool				is_streaming;
+
+	struct ov2680_ctrls		ctrls;
+	struct v4l2_mbus_framefmt	fmt;
+	struct v4l2_fract		frame_interval;
+
+	const struct ov2680_mode_info	*current_mode;
+};
+
+static const char * const test_pattern_menu[] = {
+	"Disabled",
+	"Color Bars",
+	"Random Data",
+	"Square",
+	"Black Image",
+};
+
+static const int ov2680_hv_flip_bayer_order[] = {
+	MEDIA_BUS_FMT_SBGGR10_1X10,
+	MEDIA_BUS_FMT_SGRBG10_1X10,
+	MEDIA_BUS_FMT_SGBRG10_1X10,
+	MEDIA_BUS_FMT_SRGGB10_1X10,
+};
+
+static const struct reg_value ov2680_setting_30fps_QUXGA_800_600[] = {
+	{0x3086, 0x01}, {0x370a, 0x23}, {0x3808, 0x03}, {0x3809, 0x20},
+	{0x380a, 0x02}, {0x380b, 0x58}, {0x380c, 0x06}, {0x380d, 0xac},
+	{0x380e, 0x02}, {0x380f, 0x84}, {0x3811, 0x04}, {0x3813, 0x04},
+	{0x3814, 0x31}, {0x3815, 0x31}, {0x3820, 0xc0}, {0x4008, 0x00},
+	{0x4009, 0x03}, {0x4837, 0x1e}, {0x3501, 0x4e}, {0x3502, 0xe0},
+};
+
+static const struct reg_value ov2680_setting_30fps_720P_1280_720[] = {
+	{0x3086, 0x00}, {0x3808, 0x05}, {0x3809, 0x00}, {0x380a, 0x02},
+	{0x380b, 0xd0}, {0x380c, 0x06}, {0x380d, 0xa8}, {0x380e, 0x05},
+	{0x380f, 0x0e}, {0x3811, 0x08}, {0x3813, 0x06}, {0x3814, 0x11},
+	{0x3815, 0x11}, {0x3820, 0xc0}, {0x4008, 0x00},
+};
+
+static const struct reg_value ov2680_setting_30fps_UXGA_1600_1200[] = {
+	{0x3086, 0x00}, {0x3501, 0x4e}, {0x3502, 0xe0}, {0x3808, 0x06},
+	{0x3809, 0x40}, {0x380a, 0x04}, {0x380b, 0xb0}, {0x380c, 0x06},
+	{0x380d, 0xa8}, {0x380e, 0x05}, {0x380f, 0x0e}, {0x3811, 0x00},
+	{0x3813, 0x00}, {0x3814, 0x11}, {0x3815, 0x11}, {0x3820, 0xc0},
+	{0x4008, 0x00}, {0x4837, 0x18}
+};
+
+static const struct ov2680_mode_info ov2680_mode_init_data = {
+	"mode_quxga_800_600", OV2680_MODE_QUXGA_800_600, 800, 600,
+	ov2680_setting_30fps_QUXGA_800_600,
+	ARRAY_SIZE(ov2680_setting_30fps_QUXGA_800_600),
+};
+
+static const struct ov2680_mode_info ov2680_mode_data[OV2680_MODE_MAX] = {
+	{"mode_quxga_800_600", OV2680_MODE_QUXGA_800_600,
+	 800, 600, ov2680_setting_30fps_QUXGA_800_600,
+	 ARRAY_SIZE(ov2680_setting_30fps_QUXGA_800_600)},
+	{"mode_720p_1280_720", OV2680_MODE_720P_1280_720,
+	 1280, 720, ov2680_setting_30fps_720P_1280_720,
+	 ARRAY_SIZE(ov2680_setting_30fps_720P_1280_720)},
+	{"mode_uxga_1600_1200", OV2680_MODE_UXGA_1600_1200,
+	 1600, 1200, ov2680_setting_30fps_UXGA_1600_1200,
+	 ARRAY_SIZE(ov2680_setting_30fps_UXGA_1600_1200)},
+};
+
+static struct ov2680_dev *to_ov2680_dev(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct ov2680_dev, sd);
+}
+
+static struct device *ov2680_to_dev(struct ov2680_dev *sensor)
+{
+	return &sensor->i2c_client->dev;
+}
+
+static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
+{
+	return &container_of(ctrl->handler, struct ov2680_dev,
+			     ctrls.handler)->sd;
+}
+
+static int __ov2680_write_reg(struct ov2680_dev *sensor, u16 reg,
+			      unsigned int len, u32 val)
+{
+	struct i2c_client *client = sensor->i2c_client;
+	u8 buf[6];
+	int ret;
+
+	if (len > 4)
+		return -EINVAL;
+
+	put_unaligned_be16(reg, buf);
+	put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
+	ret = i2c_master_send(client, buf, len + 2);
+	if (ret != len + 2) {
+		dev_err(&client->dev, "write error: reg=0x%4x: %d\n", reg, ret);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+#define ov2680_write_reg(s, r, v) \
+	__ov2680_write_reg(s, r, OV2680_REG_VALUE_8BIT, v)
+
+#define ov2680_write_reg16(s, r, v) \
+	__ov2680_write_reg(s, r, OV2680_REG_VALUE_16BIT, v)
+
+#define ov2680_write_reg24(s, r, v) \
+	__ov2680_write_reg(s, r, OV2680_REG_VALUE_24BIT, v)
+
+static int __ov2680_read_reg(struct ov2680_dev *sensor, u16 reg,
+			     unsigned int len, u32 *val)
+{
+	struct i2c_client *client = sensor->i2c_client;
+	struct i2c_msg msgs[2];
+	u8 addr_buf[2] = { reg >> 8, reg & 0xff };
+	u8 data_buf[4] = { 0, };
+	int ret;
+
+	if (len > 4)
+		return -EINVAL;
+
+	msgs[0].addr = client->addr;
+	msgs[0].flags = 0;
+	msgs[0].len = ARRAY_SIZE(addr_buf);
+	msgs[0].buf = addr_buf;
+
+	msgs[1].addr = client->addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = len;
+	msgs[1].buf = &data_buf[4 - len];
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs)) {
+		dev_err(&client->dev, "read error: reg=0x%4x: %d\n", reg, ret);
+		return -EIO;
+	}
+
+	*val = get_unaligned_be32(data_buf);
+
+	return 0;
+}
+
+#define ov2680_read_reg(s, r, v) \
+	__ov2680_read_reg(s, r, OV2680_REG_VALUE_8BIT, v)
+
+#define ov2680_read_reg16(s, r, v) \
+	__ov2680_read_reg(s, r, OV2680_REG_VALUE_16BIT, v)
+
+#define ov2680_read_reg24(s, r, v) \
+	__ov2680_read_reg(s, r, OV2680_REG_VALUE_24BIT, v)
+
+static int ov2680_mod_reg(struct ov2680_dev *sensor, u16 reg, u8 mask, u8 val)
+{
+	u32 readval;
+	int ret;
+
+	ret = ov2680_read_reg(sensor, reg, &readval);
+	if (ret < 0)
+		return ret;
+
+	readval &= ~mask;
+	val &= mask;
+	val |= readval;
+
+	return ov2680_write_reg(sensor, reg, val);
+}
+
+static int ov2680_load_regs(struct ov2680_dev *sensor,
+			    const struct ov2680_mode_info *mode)
+{
+	const struct reg_value *regs = mode->reg_data;
+	unsigned int i;
+	int ret = 0;
+	u16 reg_addr;
+	u8 val;
+
+	for (i = 0; i < mode->reg_data_size; ++i, ++regs) {
+		reg_addr = regs->reg_addr;
+		val = regs->val;
+
+		ret = ov2680_write_reg(sensor, reg_addr, val);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static void ov2680_power_up(struct ov2680_dev *sensor)
+{
+	if (!sensor->pwdn_gpio)
+		return;
+
+	gpiod_set_value(sensor->pwdn_gpio, 1);
+	usleep_range(5000, 10000);
+}
+
+static void ov2680_power_down(struct ov2680_dev *sensor)
+{
+	if (!sensor->pwdn_gpio)
+		return;
+
+	gpiod_set_value(sensor->pwdn_gpio, 0);
+	usleep_range(5000, 10000);
+}
+
+static int ov2680_bayer_order(struct ov2680_dev *sensor)
+{
+	u32 format1;
+	u32 format2;
+	u32 hv_flip;
+	int ret;
+
+	ret = ov2680_read_reg(sensor, OV2680_REG_FORMAT1, &format1);
+	if (ret < 0)
+		return ret;
+
+	ret = ov2680_read_reg(sensor, OV2680_REG_FORMAT2, &format2);
+	if (ret < 0)
+		return ret;
+
+	hv_flip = (format2 & BIT(2)  << 1) | (format1 & BIT(2));
+
+	sensor->fmt.code = ov2680_hv_flip_bayer_order[hv_flip];
+
+	return 0;
+}
+
+static int ov2680_vflip_enable(struct ov2680_dev *sensor)
+{
+	int ret;
+
+	ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT1, BIT(2), BIT(2));
+	if (ret < 0)
+		return ret;
+
+	return ov2680_bayer_order(sensor);
+}
+
+static int ov2680_vflip_disable(struct ov2680_dev *sensor)
+{
+	int ret;
+
+	ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT1, BIT(2), BIT(0));
+	if (ret < 0)
+		return ret;
+
+	return ov2680_bayer_order(sensor);
+}
+
+static int ov2680_hflip_enable(struct ov2680_dev *sensor)
+{
+	int ret;
+
+	ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT2, BIT(2), BIT(2));
+	if (ret < 0)
+		return ret;
+
+	return ov2680_bayer_order(sensor);
+}
+
+static int ov2680_hflip_disable(struct ov2680_dev *sensor)
+{
+	int ret;
+
+	ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT2, BIT(2), BIT(0));
+	if (ret < 0)
+		return ret;
+
+	return ov2680_bayer_order(sensor);
+}
+
+static int ov2680_test_pattern_set(struct ov2680_dev *sensor, int value)
+{
+	int ret;
+
+	if (!value)
+		return ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, BIT(7), 0);
+
+	ret = ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, 0x03, value - 1);
+	if (ret < 0)
+		return ret;
+
+	ret = ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, BIT(7), BIT(7));
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int ov2680_gain_set(struct ov2680_dev *sensor, bool auto_gain)
+{
+	struct ov2680_ctrls *ctrls = &sensor->ctrls;
+	u32 gain;
+	int ret;
+
+	ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(1),
+			     auto_gain ? 0 : BIT(1));
+	if (ret < 0)
+		return ret;
+
+	if (auto_gain || !ctrls->gain->is_new)
+		return 0;
+
+	gain = ctrls->gain->val;
+
+	ret = ov2680_write_reg16(sensor, OV2680_REG_GAIN_PK, gain);
+
+	return 0;
+}
+
+static int ov2680_gain_get(struct ov2680_dev *sensor)
+{
+	u32 gain;
+	int ret;
+
+	ret = ov2680_read_reg16(sensor, OV2680_REG_GAIN_PK, &gain);
+	if (ret)
+		return ret;
+
+	return gain;
+}
+
+static int ov2680_auto_gain_enable(struct ov2680_dev *sensor)
+{
+	return ov2680_gain_set(sensor, true);
+}
+
+static int ov2680_auto_gain_disable(struct ov2680_dev *sensor)
+{
+	return ov2680_gain_set(sensor, false);
+}
+
+static int ov2680_exposure_set(struct ov2680_dev *sensor, bool auto_exp)
+{
+	struct ov2680_ctrls *ctrls = &sensor->ctrls;
+	u32 exp;
+	int ret;
+
+	ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(0),
+			     auto_exp ? 0 : BIT(0));
+	if (ret < 0)
+		return ret;
+
+	if (auto_exp || !ctrls->exposure->is_new)
+		return 0;
+
+	exp = (u32)ctrls->exposure->val;
+	exp <<= 4;
+
+	return ov2680_write_reg24(sensor, OV2680_REG_EXPOSURE_PK_HIGH, exp);
+}
+
+static int ov2680_exposure_get(struct ov2680_dev *sensor)
+{
+	int ret;
+	u32 exp;
+
+	ret = ov2680_read_reg24(sensor, OV2680_REG_EXPOSURE_PK_HIGH, &exp);
+	if (ret)
+		return ret;
+
+	return exp >> 4;
+}
+
+static int ov2680_auto_exposure_enable(struct ov2680_dev *sensor)
+{
+	return ov2680_exposure_set(sensor, true);
+}
+
+static int ov2680_auto_exposure_disable(struct ov2680_dev *sensor)
+{
+	return ov2680_exposure_set(sensor, false);
+}
+
+static int ov2680_stream_enable(struct ov2680_dev *sensor)
+{
+	return ov2680_write_reg(sensor, OV2680_REG_STREAM_CTRL, 1);
+}
+
+static int ov2680_stream_disable(struct ov2680_dev *sensor)
+{
+	return ov2680_write_reg(sensor, OV2680_REG_STREAM_CTRL, 0);
+}
+
+static int ov2680_mode_set(struct ov2680_dev *sensor)
+{
+	struct ov2680_ctrls *ctrls = &sensor->ctrls;
+	int ret;
+
+	ret = ov2680_auto_gain_disable(sensor);
+	if (ret < 0)
+		return ret;
+
+	ret = ov2680_auto_exposure_disable(sensor);
+	if (ret < 0)
+		return ret;
+
+	ret = ov2680_load_regs(sensor, sensor->current_mode);
+	if (ret < 0)
+		return ret;
+
+	if (ctrls->auto_gain->val) {
+		ret = ov2680_auto_gain_enable(sensor);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (ctrls->auto_exp->val == V4L2_EXPOSURE_AUTO) {
+		ret = ov2680_auto_exposure_enable(sensor);
+		if (ret < 0)
+			return ret;
+	}
+
+	sensor->mode_pending_changes = false;
+
+	return 0;
+}
+
+static int ov2680_mode_restore(struct ov2680_dev *sensor)
+{
+	int ret;
+
+	ret = ov2680_load_regs(sensor, &ov2680_mode_init_data);
+	if (ret < 0)
+		return ret;
+
+	return ov2680_mode_set(sensor);
+}
+
+static int ov2680_power_off(struct ov2680_dev *sensor)
+{
+	if (!sensor->is_enabled)
+		return 0;
+
+	clk_disable_unprepare(sensor->xvclk);
+	ov2680_power_down(sensor);
+	sensor->is_enabled = false;
+
+	return 0;
+}
+
+static int ov2680_power_on(struct ov2680_dev *sensor)
+{
+	struct device *dev = ov2680_to_dev(sensor);
+	int ret;
+
+	if (sensor->is_enabled)
+		return 0;
+
+	if (!sensor->pwdn_gpio) {
+		ret = ov2680_write_reg(sensor, OV2680_REG_SOFT_RESET, 0x01);
+		if (ret != 0) {
+			dev_err(dev, "sensor soft reset failed\n");
+			return ret;
+		}
+		usleep_range(1000, 2000);
+	} else {
+		ov2680_power_down(sensor);
+		ov2680_power_up(sensor);
+	}
+
+	ret = clk_prepare_enable(sensor->xvclk);
+	if (ret < 0)
+		return ret;
+
+	ret = ov2680_mode_restore(sensor);
+	if (ret < 0)
+		goto disable;
+
+	sensor->is_enabled = true;
+
+	/* Set clock lane into LP-11 state */
+	ov2680_stream_enable(sensor);
+	usleep_range(1000, 2000);
+	ov2680_stream_disable(sensor);
+
+	return 0;
+
+disable:
+	dev_err(dev, "failed to enable sensor: %d\n", ret);
+	ov2680_power_off(sensor);
+
+	return ret;
+}
+
+static int ov2680_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct ov2680_dev *sensor = to_ov2680_dev(sd);
+	int ret = 0;
+
+	mutex_lock(&sensor->lock);
+
+	if (on)
+		ret = ov2680_power_on(sensor);
+	else
+		ret = ov2680_power_off(sensor);
+
+	mutex_unlock(&sensor->lock);
+
+	if (on && ret == 0) {
+		ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
+		if (ret < 0)
+			return ret;
+	}
+
+	return ret;
+}
+
+static int ov2680_s_g_frame_interval(struct v4l2_subdev *sd,
+				     struct v4l2_subdev_frame_interval *fi)
+{
+	struct ov2680_dev *sensor = to_ov2680_dev(sd);
+
+	mutex_lock(&sensor->lock);
+	fi->interval = sensor->frame_interval;
+	mutex_unlock(&sensor->lock);
+
+	return 0;
+}
+
+static int ov2680_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct ov2680_dev *sensor = to_ov2680_dev(sd);
+	int ret = 0;
+
+	mutex_lock(&sensor->lock);
+
+	if (sensor->is_streaming == !!enable)
+		goto unlock;
+
+	if (enable && sensor->mode_pending_changes) {
+		ret = ov2680_mode_set(sensor);
+		if (ret < 0)
+			goto unlock;
+	}
+
+	if (enable)
+		ret = ov2680_stream_enable(sensor);
+	else
+		ret = ov2680_stream_disable(sensor);
+
+	sensor->is_streaming = !!enable;
+
+unlock:
+	mutex_unlock(&sensor->lock);
+
+	return ret;
+}
+
+static int ov2680_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct ov2680_dev *sensor = to_ov2680_dev(sd);
+
+	if (code->pad != 0 || code->index != 0)
+		return -EINVAL;
+
+	code->code = sensor->fmt.code;
+
+	return 0;
+}
+
+static int ov2680_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct ov2680_dev *sensor = to_ov2680_dev(sd);
+	struct v4l2_mbus_framefmt *fmt;
+
+	if (format->pad != 0)
+		return -EINVAL;
+
+	mutex_lock(&sensor->lock);
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		fmt = v4l2_subdev_get_try_format(&sensor->sd, cfg, format->pad);
+	else
+		fmt = &sensor->fmt;
+
+	format->format = *fmt;
+
+	mutex_unlock(&sensor->lock);
+
+	return 0;
+}
+
+static int ov2680_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct ov2680_dev *sensor = to_ov2680_dev(sd);
+	struct v4l2_mbus_framefmt *fmt = &format->format;
+	const struct ov2680_mode_info *mode;
+	int ret = 0;
+
+	if (format->pad != 0)
+		return -EINVAL;
+
+	mutex_lock(&sensor->lock);
+
+	if (sensor->is_streaming) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	mode = v4l2_find_nearest_size(ov2680_mode_data,
+				      ARRAY_SIZE(ov2680_mode_data), width,
+				      height, fmt->width, fmt->height);
+	if (!mode) {
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+		fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
+
+		*fmt = format->format;
+		goto unlock;
+	}
+
+	fmt->width = mode->width;
+	fmt->height = mode->height;
+	fmt->code = sensor->fmt.code;
+	fmt->colorspace = sensor->fmt.colorspace;
+
+	sensor->current_mode = mode;
+	sensor->fmt = format->format;
+	sensor->mode_pending_changes = true;
+
+unlock:
+	mutex_unlock(&sensor->lock);
+
+	return ret;
+}
+
+static int ov2680_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+	int index = fse->index;
+
+	if (index >= OV2680_MODE_MAX)
+		return -EINVAL;
+
+	fse->min_width = ov2680_mode_data[index].width;
+	fse->min_height = ov2680_mode_data[index].height;
+	fse->max_width = ov2680_mode_data[index].width;
+	fse->max_height = ov2680_mode_data[index].height;
+
+	return 0;
+}
+
+static int ov2680_enum_frame_interval(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_pad_config *cfg,
+			      struct v4l2_subdev_frame_interval_enum *fie)
+{
+	struct v4l2_fract tpf;
+
+	tpf.denominator = OV2680_FRAME_RATE;
+	tpf.numerator = 1;
+
+	fie->interval = tpf;
+
+	return 0;
+}
+
+static int ov2680_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+	struct ov2680_dev *sensor = to_ov2680_dev(sd);
+	int val;
+
+	if (!sensor->is_enabled)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTOGAIN:
+		if (!ctrl->val)
+			return 0;
+		val = ov2680_gain_get(sensor);
+		if (val < 0)
+			return val;
+		sensor->ctrls.gain->val = val;
+		break;
+	case V4L2_CID_EXPOSURE_AUTO:
+		if (ctrl->val == V4L2_EXPOSURE_MANUAL)
+			return 0;
+		val = ov2680_exposure_get(sensor);
+		if (val < 0)
+			return val;
+		sensor->ctrls.exposure->val = val;
+		break;
+	}
+
+	return 0;
+}
+
+static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+	struct ov2680_dev *sensor = to_ov2680_dev(sd);
+
+	if (!sensor->is_enabled)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUTOGAIN:
+		return ov2680_gain_set(sensor, !!ctrl->val);
+	case V4L2_CID_EXPOSURE_AUTO:
+		return ov2680_exposure_set(sensor, !!ctrl->val);
+	case V4L2_CID_VFLIP:
+		if (sensor->is_streaming)
+			return -EBUSY;
+		if (ctrl->val)
+			return ov2680_vflip_enable(sensor);
+		else
+			return ov2680_vflip_disable(sensor);
+	case V4L2_CID_HFLIP:
+		if (ctrl->val)
+			return ov2680_hflip_enable(sensor);
+		else
+			return ov2680_hflip_disable(sensor);
+	case V4L2_CID_TEST_PATTERN:
+		return ov2680_test_pattern_set(sensor, ctrl->val);
+	default:
+		break;
+	}
+
+	return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops ov2680_ctrl_ops = {
+	.g_volatile_ctrl = ov2680_g_volatile_ctrl,
+	.s_ctrl = ov2680_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops ov2680_core_ops = {
+	.s_power = ov2680_s_power,
+};
+
+static const struct v4l2_subdev_video_ops ov2680_video_ops = {
+	.g_frame_interval	= ov2680_s_g_frame_interval,
+	.s_frame_interval	= ov2680_s_g_frame_interval,
+	.s_stream		= ov2680_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ov2680_pad_ops = {
+	.enum_mbus_code		= ov2680_enum_mbus_code,
+	.get_fmt		= ov2680_get_fmt,
+	.set_fmt		= ov2680_set_fmt,
+	.enum_frame_size	= ov2680_enum_frame_size,
+	.enum_frame_interval	= ov2680_enum_frame_interval,
+};
+
+static const struct v4l2_subdev_ops ov2680_subdev_ops = {
+	.core	= &ov2680_core_ops,
+	.video	= &ov2680_video_ops,
+	.pad	= &ov2680_pad_ops,
+};
+
+static int ov2680_mode_init(struct ov2680_dev *sensor)
+{
+	const struct ov2680_mode_info *init_mode;
+
+	/* set initial mode */
+	sensor->fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+	sensor->fmt.width = 800;
+	sensor->fmt.height = 600;
+	sensor->fmt.field = V4L2_FIELD_NONE;
+	sensor->fmt.colorspace = V4L2_COLORSPACE_SRGB;
+
+	sensor->frame_interval.denominator = OV2680_FRAME_RATE;
+	sensor->frame_interval.numerator = 1;
+
+	init_mode = &ov2680_mode_init_data;
+
+	sensor->current_mode = init_mode;
+
+	sensor->mode_pending_changes = true;
+
+	return 0;
+}
+
+static int ov2680_v4l2_init(struct ov2680_dev *sensor)
+{
+	const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops;
+	struct ov2680_ctrls *ctrls = &sensor->ctrls;
+	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+	int ret = 0;
+
+	v4l2_i2c_subdev_init(&sensor->sd, sensor->i2c_client,
+			     &ov2680_subdev_ops);
+
+	sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+	ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
+	if (ret < 0)
+		return ret;
+
+	v4l2_ctrl_handler_init(hdl, 32);
+
+	hdl->lock = &sensor->lock;
+
+	ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+	ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+	ctrls->test_pattern = v4l2_ctrl_new_std_menu_items(hdl,
+					&ov2680_ctrl_ops,
+					V4L2_CID_TEST_PATTERN,
+					ARRAY_SIZE(test_pattern_menu) - 1,
+					0, 0, test_pattern_menu);
+
+	ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
+						 V4L2_CID_EXPOSURE_AUTO,
+						 V4L2_EXPOSURE_MANUAL, 0,
+						 V4L2_EXPOSURE_AUTO);
+
+	ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
+					    0, 32767, 1, 0);
+
+	ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
+					     0, 1, 1, 1);
+	ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 0, 2047, 1, 0);
+
+	if (hdl->error) {
+		ret = hdl->error;
+		goto cleanup_entity;
+	}
+
+	ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
+	ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+	v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
+	v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
+
+	sensor->sd.ctrl_handler = hdl;
+
+	ret = v4l2_async_register_subdev(&sensor->sd);
+	if (ret < 0)
+		goto cleanup_entity;
+
+	return 0;
+
+cleanup_entity:
+	media_entity_cleanup(&sensor->sd.entity);
+	v4l2_ctrl_handler_free(hdl);
+
+	return ret;
+}
+
+static int ov2680_check_id(struct ov2680_dev *sensor)
+{
+	struct device *dev = ov2680_to_dev(sensor);
+	u32 chip_id;
+	int ret;
+
+	ov2680_power_on(sensor);
+
+	ret = ov2680_read_reg16(sensor, OV2680_REG_CHIP_ID_HIGH, &chip_id);
+	if (ret < 0) {
+		dev_err(dev, "failed to read chip id high\n");
+		return -ENODEV;
+	}
+
+	if (chip_id != OV2680_CHIP_ID) {
+		dev_err(dev, "chip id: 0x%04x does not match expected 0x%04x\n",
+			chip_id, OV2680_CHIP_ID);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int ov2860_parse_dt(struct ov2680_dev *sensor)
+{
+	struct device *dev = ov2680_to_dev(sensor);
+	int ret;
+
+	sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
+						    GPIOD_OUT_HIGH);
+	ret = PTR_ERR_OR_ZERO(sensor->pwdn_gpio);
+	if (ret < 0) {
+		dev_dbg(dev, "error while getting powerdown gpio: %d\n", ret);
+		return ret;
+	}
+
+	sensor->xvclk = devm_clk_get(dev, "xvclk");
+	if (IS_ERR(sensor->xvclk)) {
+		dev_err(dev, "xvclk clock missing or invalid\n");
+		return PTR_ERR(sensor->xvclk);
+	}
+
+	sensor->xvclk_freq = clk_get_rate(sensor->xvclk);
+	if (sensor->xvclk_freq < OV2680_XVCLK_MIN ||
+	    sensor->xvclk_freq > OV2680_XVCLK_MAX) {
+		dev_err(dev, "xvclk frequency out of range: %d Hz\n",
+			sensor->xvclk_freq);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ov2680_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct ov2680_dev *sensor;
+	int ret;
+
+	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+	if (!sensor)
+		return -ENOMEM;
+
+	sensor->i2c_client = client;
+
+	ret = ov2860_parse_dt(sensor);
+	if (ret < 0)
+		return -EINVAL;
+
+	ret = ov2680_mode_init(sensor);
+	if (ret < 0)
+		return ret;
+
+	mutex_init(&sensor->lock);
+
+	ret = ov2680_v4l2_init(sensor);
+	if (ret < 0)
+		goto lock_destroy;
+
+	ret = ov2680_check_id(sensor);
+	if (ret < 0)
+		goto error_cleanup;
+
+	dev_info(dev, "ov2680 init correctly\n");
+
+	return 0;
+
+error_cleanup:
+	dev_err(dev, "ov2680 init fail: %d\n", ret);
+
+	media_entity_cleanup(&sensor->sd.entity);
+	v4l2_async_unregister_subdev(&sensor->sd);
+	v4l2_ctrl_handler_free(&sensor->ctrls.handler);
+
+lock_destroy:
+	mutex_destroy(&sensor->lock);
+
+	return ret;
+}
+
+static int ov2680_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov2680_dev *sensor = to_ov2680_dev(sd);
+
+	v4l2_async_unregister_subdev(&sensor->sd);
+	mutex_destroy(&sensor->lock);
+	media_entity_cleanup(&sensor->sd.entity);
+	v4l2_ctrl_handler_free(&sensor->ctrls.handler);
+
+	return 0;
+}
+
+static int __maybe_unused ov2680_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov2680_dev *sensor = to_ov2680_dev(sd);
+
+	if (sensor->is_streaming)
+		ov2680_stream_disable(sensor);
+
+	return 0;
+}
+
+static int __maybe_unused ov2680_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov2680_dev *sensor = to_ov2680_dev(sd);
+	int ret;
+
+	if (sensor->is_streaming) {
+		ret = ov2680_stream_enable(sensor);
+		if (ret < 0)
+			goto stream_disable;
+	}
+
+	return 0;
+
+stream_disable:
+	ov2680_stream_disable(sensor);
+	sensor->is_streaming = false;
+
+	return ret;
+}
+
+static const struct dev_pm_ops ov2680_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(ov2680_suspend, ov2680_resume)
+};
+
+static const struct of_device_id ov2680_dt_ids[] = {
+	{ .compatible = "ovti,ov2680" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ov2680_dt_ids);
+
+static struct i2c_driver ov2680_i2c_driver = {
+	.driver = {
+		.name  = "ov2680",
+		.pm = &ov2680_pm_ops,
+		.of_match_table	= of_match_ptr(ov2680_dt_ids),
+	},
+	.probe_new	= ov2680_probe,
+	.remove		= ov2680_remove,
+};
+module_i2c_driver(ov2680_i2c_driver);
+
+MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>");
+MODULE_DESCRIPTION("OV2680 CMOS Image Sensor driver");
+MODULE_LICENSE("GPL v2");
-- 
2.16.2

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 2/2] media: ov2680: Add Omnivision OV2680 sensor driver
  2018-03-13 11:33 ` [PATCH v3 2/2] media: ov2680: Add Omnivision OV2680 sensor driver Rui Miguel Silva
@ 2018-03-14 19:39   ` kbuild test robot
  2018-03-15  9:29     ` Rui Miguel Silva
  2018-03-14 21:36   ` kbuild test robot
  2018-03-24 10:57   ` Sakari Ailus
  2 siblings, 1 reply; 10+ messages in thread
From: kbuild test robot @ 2018-03-14 19:39 UTC (permalink / raw)
  To: Rui Miguel Silva
  Cc: kbuild-all, mchehab, sakari.ailus, hverkuil, linux-media,
	linux-kernel, Ryan Harkin, Rui Miguel Silva

[-- Attachment #1: Type: text/plain, Size: 3254 bytes --]

Hi Rui,

I love your patch! Yet something to improve:

[auto build test ERROR on v4.16-rc4]
[cannot apply to next-20180314]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Rui-Miguel-Silva/media-Introduce-Omnivision-OV2680-driver/20180315-020617
config: sh-allmodconfig (attached as .config)
compiler: sh4-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=sh 

All errors (new ones prefixed by >>):

   drivers/media/i2c/ov2680.c: In function 'ov2680_set_fmt':
>> drivers/media/i2c/ov2680.c:713:9: error: implicit declaration of function 'v4l2_find_nearest_size'; did you mean 'v4l2_find_nearest_format'? [-Werror=implicit-function-declaration]
     mode = v4l2_find_nearest_size(ov2680_mode_data,
            ^~~~~~~~~~~~~~~~~~~~~~
            v4l2_find_nearest_format
>> drivers/media/i2c/ov2680.c:714:41: error: 'width' undeclared (first use in this function)
              ARRAY_SIZE(ov2680_mode_data), width,
                                            ^~~~~
   drivers/media/i2c/ov2680.c:714:41: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/media/i2c/ov2680.c:715:11: error: 'height' undeclared (first use in this function); did you mean 'hweight8'?
              height, fmt->width, fmt->height);
              ^~~~~~
              hweight8
   cc1: some warnings being treated as errors

vim +713 drivers/media/i2c/ov2680.c

   693	
   694	static int ov2680_set_fmt(struct v4l2_subdev *sd,
   695				  struct v4l2_subdev_pad_config *cfg,
   696				  struct v4l2_subdev_format *format)
   697	{
   698		struct ov2680_dev *sensor = to_ov2680_dev(sd);
   699		struct v4l2_mbus_framefmt *fmt = &format->format;
   700		const struct ov2680_mode_info *mode;
   701		int ret = 0;
   702	
   703		if (format->pad != 0)
   704			return -EINVAL;
   705	
   706		mutex_lock(&sensor->lock);
   707	
   708		if (sensor->is_streaming) {
   709			ret = -EBUSY;
   710			goto unlock;
   711		}
   712	
 > 713		mode = v4l2_find_nearest_size(ov2680_mode_data,
 > 714					      ARRAY_SIZE(ov2680_mode_data), width,
 > 715					      height, fmt->width, fmt->height);
   716		if (!mode) {
   717			ret = -EINVAL;
   718			goto unlock;
   719		}
   720	
   721		if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
   722			fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
   723	
   724			*fmt = format->format;
   725			goto unlock;
   726		}
   727	
   728		fmt->width = mode->width;
   729		fmt->height = mode->height;
   730		fmt->code = sensor->fmt.code;
   731		fmt->colorspace = sensor->fmt.colorspace;
   732	
   733		sensor->current_mode = mode;
   734		sensor->fmt = format->format;
   735		sensor->mode_pending_changes = true;
   736	
   737	unlock:
   738		mutex_unlock(&sensor->lock);
   739	
   740		return ret;
   741	}
   742	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 48070 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 2/2] media: ov2680: Add Omnivision OV2680 sensor driver
  2018-03-13 11:33 ` [PATCH v3 2/2] media: ov2680: Add Omnivision OV2680 sensor driver Rui Miguel Silva
  2018-03-14 19:39   ` kbuild test robot
@ 2018-03-14 21:36   ` kbuild test robot
  2018-03-24 10:57   ` Sakari Ailus
  2 siblings, 0 replies; 10+ messages in thread
From: kbuild test robot @ 2018-03-14 21:36 UTC (permalink / raw)
  To: Rui Miguel Silva
  Cc: kbuild-all, mchehab, sakari.ailus, hverkuil, linux-media,
	linux-kernel, Ryan Harkin, Rui Miguel Silva

[-- Attachment #1: Type: text/plain, Size: 3251 bytes --]

Hi Rui,

I love your patch! Yet something to improve:

[auto build test ERROR on v4.16-rc4]
[cannot apply to next-20180314]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Rui-Miguel-Silva/media-Introduce-Omnivision-OV2680-driver/20180315-020617
config: i386-allyesconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

   drivers/media/i2c/ov2680.c: In function 'ov2680_set_fmt':
   drivers/media/i2c/ov2680.c:713:9: error: implicit declaration of function 'v4l2_find_nearest_size'; did you mean 'v4l2_find_nearest_format'? [-Werror=implicit-function-declaration]
     mode = v4l2_find_nearest_size(ov2680_mode_data,
            ^~~~~~~~~~~~~~~~~~~~~~
            v4l2_find_nearest_format
   drivers/media/i2c/ov2680.c:714:195: error: 'width' undeclared (first use in this function)
              ARRAY_SIZE(ov2680_mode_data), width,
                                                                                                                                                                                                      ^    
   drivers/media/i2c/ov2680.c:714:195: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/media/i2c/ov2680.c:715:11: error: 'height' undeclared (first use in this function); did you mean '__iget'?
              height, fmt->width, fmt->height);
              ^~~~~~
              __iget
   cc1: some warnings being treated as errors

vim +715 drivers/media/i2c/ov2680.c

   693	
   694	static int ov2680_set_fmt(struct v4l2_subdev *sd,
   695				  struct v4l2_subdev_pad_config *cfg,
   696				  struct v4l2_subdev_format *format)
   697	{
   698		struct ov2680_dev *sensor = to_ov2680_dev(sd);
   699		struct v4l2_mbus_framefmt *fmt = &format->format;
   700		const struct ov2680_mode_info *mode;
   701		int ret = 0;
   702	
   703		if (format->pad != 0)
   704			return -EINVAL;
   705	
   706		mutex_lock(&sensor->lock);
   707	
   708		if (sensor->is_streaming) {
   709			ret = -EBUSY;
   710			goto unlock;
   711		}
   712	
 > 713		mode = v4l2_find_nearest_size(ov2680_mode_data,
   714					      ARRAY_SIZE(ov2680_mode_data), width,
 > 715					      height, fmt->width, fmt->height);
   716		if (!mode) {
   717			ret = -EINVAL;
   718			goto unlock;
   719		}
   720	
   721		if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
   722			fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
   723	
   724			*fmt = format->format;
   725			goto unlock;
   726		}
   727	
   728		fmt->width = mode->width;
   729		fmt->height = mode->height;
   730		fmt->code = sensor->fmt.code;
   731		fmt->colorspace = sensor->fmt.colorspace;
   732	
   733		sensor->current_mode = mode;
   734		sensor->fmt = format->format;
   735		sensor->mode_pending_changes = true;
   736	
   737	unlock:
   738		mutex_unlock(&sensor->lock);
   739	
   740		return ret;
   741	}
   742	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 62379 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 2/2] media: ov2680: Add Omnivision OV2680 sensor driver
  2018-03-14 19:39   ` kbuild test robot
@ 2018-03-15  9:29     ` Rui Miguel Silva
  2018-03-16 16:10       ` Sakari Ailus
  0 siblings, 1 reply; 10+ messages in thread
From: Rui Miguel Silva @ 2018-03-15  9:29 UTC (permalink / raw)
  To: kbuild test robot
  Cc: kbuild-all, mchehab, sakari.ailus, hverkuil, linux-media,
	linux-kernel, Ryan Harkin, Rui Miguel Silva

Hi,
On Wed 14 Mar 2018 at 19:39, kbuild test robot wrote:
> Hi Rui,
>
> I love your patch! Yet something to improve:
>
> [auto build test ERROR on v4.16-rc4]
> [cannot apply to next-20180314]
> [if your patch is applied to the wrong git tree, please drop us 
> a note to help improve the system]
>
> url: 
> https://github.com/0day-ci/linux/commits/Rui-Miguel-Silva/media-Introduce-Omnivision-OV2680-driver/20180315-020617
> config: sh-allmodconfig (attached as .config)
> compiler: sh4-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
> reproduce:
>         wget 
>         https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross 
>         -O ~/bin/make.cross
>         chmod +x ~/bin/make.cross
>         # save the attached .config to linux build tree
>         make.cross ARCH=sh 
>
> All errors (new ones prefixed by >>):
>
>    drivers/media/i2c/ov2680.c: In function 'ov2680_set_fmt':
>>> drivers/media/i2c/ov2680.c:713:9: error: implicit declaration 
>>> of function 'v4l2_find_nearest_size'; did you mean 
>>> 'v4l2_find_nearest_format'? 
>>> [-Werror=implicit-function-declaration]
>      mode = v4l2_find_nearest_size(ov2680_mode_data,
>             ^~~~~~~~~~~~~~~~~~~~~~
>             v4l2_find_nearest_format

As requested by maintainer this series depend on this patch [0], 
which
introduce this macro. I am not sure of the status of that patch 
though.

---
Cheers,
	Rui

[0] https://patchwork.kernel.org/patch/10207087/

>>> drivers/media/i2c/ov2680.c:714:41: error: 'width' undeclared 
>>> (first use in this function)
>               ARRAY_SIZE(ov2680_mode_data), width,
>                                             ^~~~~
>    drivers/media/i2c/ov2680.c:714:41: note: each undeclared 
>    identifier is reported only once for each function it appears 
>    in
>>> drivers/media/i2c/ov2680.c:715:11: error: 'height' undeclared 
>>> (first use in this function); did you mean 'hweight8'?
>               height, fmt->width, fmt->height);
>               ^~~~~~
>               hweight8
>    cc1: some warnings being treated as errors
>
> vim +713 drivers/media/i2c/ov2680.c
>
>    693	
>    694	static int ov2680_set_fmt(struct v4l2_subdev *sd,
>    695				  struct 
>    v4l2_subdev_pad_config *cfg,
>    696				  struct 
>    v4l2_subdev_format *format)
>    697	{
>    698		struct ov2680_dev *sensor = 
>    to_ov2680_dev(sd);
>    699		struct v4l2_mbus_framefmt *fmt = 
>    &format->format;
>    700		const struct ov2680_mode_info *mode;
>    701		int ret = 0;
>    702	
>    703		if (format->pad != 0)
>    704			return -EINVAL;
>    705	
>    706		mutex_lock(&sensor->lock);
>    707	
>    708		if (sensor->is_streaming) {
>    709			ret = -EBUSY;
>    710			goto unlock;
>    711		}
>    712	
>  > 713		mode = 
>  > v4l2_find_nearest_size(ov2680_mode_data,
>  > 714 
>  > ARRAY_SIZE(ov2680_mode_data), width,
>  > 715					      height, 
>  > fmt->width, fmt->height);
>    716		if (!mode) {
>    717			ret = -EINVAL;
>    718			goto unlock;
>    719		}
>    720	
>    721		if (format->which == 
>    V4L2_SUBDEV_FORMAT_TRY) {
>    722			fmt = 
>    v4l2_subdev_get_try_format(sd, cfg, 0);
>    723	
>    724			*fmt = format->format;
>    725			goto unlock;
>    726		}
>    727	
>    728		fmt->width = mode->width;
>    729		fmt->height = mode->height;
>    730		fmt->code = sensor->fmt.code;
>    731		fmt->colorspace = sensor->fmt.colorspace;
>    732	
>    733		sensor->current_mode = mode;
>    734		sensor->fmt = format->format;
>    735		sensor->mode_pending_changes = true;
>    736	
>    737	unlock:
>    738		mutex_unlock(&sensor->lock);
>    739	
>    740		return ret;
>    741	}
>    742	
>
> ---
> 0-DAY kernel test infrastructure                Open Source 
> Technology Center
> https://lists.01.org/pipermail/kbuild-all 
> Intel Corporation

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 2/2] media: ov2680: Add Omnivision OV2680 sensor driver
  2018-03-15  9:29     ` Rui Miguel Silva
@ 2018-03-16 16:10       ` Sakari Ailus
  2018-03-16 16:51         ` Rui Miguel Silva
  0 siblings, 1 reply; 10+ messages in thread
From: Sakari Ailus @ 2018-03-16 16:10 UTC (permalink / raw)
  To: Rui Miguel Silva
  Cc: kbuild test robot, kbuild-all, mchehab, hverkuil, linux-media,
	linux-kernel, Ryan Harkin

On Thu, Mar 15, 2018 at 09:29:33AM +0000, Rui Miguel Silva wrote:
> Hi,
> On Wed 14 Mar 2018 at 19:39, kbuild test robot wrote:
> > Hi Rui,
> > 
> > I love your patch! Yet something to improve:
> > 
> > [auto build test ERROR on v4.16-rc4]
> > [cannot apply to next-20180314]
> > [if your patch is applied to the wrong git tree, please drop us a note
> > to help improve the system]
> > 
> > url: https://github.com/0day-ci/linux/commits/Rui-Miguel-Silva/media-Introduce-Omnivision-OV2680-driver/20180315-020617
> > config: sh-allmodconfig (attached as .config)
> > compiler: sh4-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
> > reproduce:
> >         wget
> > https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross
> > -O ~/bin/make.cross
> >         chmod +x ~/bin/make.cross
> >         # save the attached .config to linux build tree
> >         make.cross ARCH=sh
> > 
> > All errors (new ones prefixed by >>):
> > 
> >    drivers/media/i2c/ov2680.c: In function 'ov2680_set_fmt':
> > > > drivers/media/i2c/ov2680.c:713:9: error: implicit declaration of
> > > > function 'v4l2_find_nearest_size'; did you mean
> > > > 'v4l2_find_nearest_format'?
> > > > [-Werror=implicit-function-declaration]
> >      mode = v4l2_find_nearest_size(ov2680_mode_data,
> >             ^~~~~~~~~~~~~~~~~~~~~~
> >             v4l2_find_nearest_format
> 
> As requested by maintainer this series depend on this patch [0], which
> introduce this macro. I am not sure of the status of that patch though.

No need to worry about that, the sensor driver will just be merged after
the dependencies are in. Mauro said he'd handle the pull request early next
week.

-- 
Sakari Ailus
sakari.ailus@linux.intel.com

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 2/2] media: ov2680: Add Omnivision OV2680 sensor driver
  2018-03-16 16:10       ` Sakari Ailus
@ 2018-03-16 16:51         ` Rui Miguel Silva
  0 siblings, 0 replies; 10+ messages in thread
From: Rui Miguel Silva @ 2018-03-16 16:51 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Rui Miguel Silva, kbuild test robot, kbuild-all, mchehab,
	hverkuil, linux-media, linux-kernel, Ryan Harkin

Hi Sakari,
On Fri 16 Mar 2018 at 16:10, Sakari Ailus wrote:
> On Thu, Mar 15, 2018 at 09:29:33AM +0000, Rui Miguel Silva 
> wrote:
>> Hi,
>> On Wed 14 Mar 2018 at 19:39, kbuild test robot wrote:
>> > Hi Rui,
>> > 
>> > I love your patch! Yet something to improve:
>> > 
>> > [auto build test ERROR on v4.16-rc4]
>> > [cannot apply to next-20180314]
>> > [if your patch is applied to the wrong git tree, please drop 
>> > us a note
>> > to help improve the system]
>> > 
>> > url: 
>> > https://github.com/0day-ci/linux/commits/Rui-Miguel-Silva/media-Introduce-Omnivision-OV2680-driver/20180315-020617
>> > config: sh-allmodconfig (attached as .config)
>> > compiler: sh4-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
>> > reproduce:
>> >         wget
>> > https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross
>> > -O ~/bin/make.cross
>> >         chmod +x ~/bin/make.cross
>> >         # save the attached .config to linux build tree
>> >         make.cross ARCH=sh
>> > 
>> > All errors (new ones prefixed by >>):
>> > 
>> >    drivers/media/i2c/ov2680.c: In function 'ov2680_set_fmt':
>> > > > drivers/media/i2c/ov2680.c:713:9: error: implicit 
>> > > > declaration of
>> > > > function 'v4l2_find_nearest_size'; did you mean
>> > > > 'v4l2_find_nearest_format'?
>> > > > [-Werror=implicit-function-declaration]
>> >      mode = v4l2_find_nearest_size(ov2680_mode_data,
>> >             ^~~~~~~~~~~~~~~~~~~~~~
>> >             v4l2_find_nearest_format
>> 
>> As requested by maintainer this series depend on this patch 
>> [0], which
>> introduce this macro. I am not sure of the status of that patch 
>> though.
>
> No need to worry about that, the sensor driver will just be 
> merged after
> the dependencies are in. Mauro said he'd handle the pull request 
> early next
> week.

Great, Many thanks for everything.

---
Cheers,
	Rui

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 2/2] media: ov2680: Add Omnivision OV2680 sensor driver
  2018-03-13 11:33 ` [PATCH v3 2/2] media: ov2680: Add Omnivision OV2680 sensor driver Rui Miguel Silva
  2018-03-14 19:39   ` kbuild test robot
  2018-03-14 21:36   ` kbuild test robot
@ 2018-03-24 10:57   ` Sakari Ailus
  2018-03-27 12:39     ` Rui Miguel Silva
  2 siblings, 1 reply; 10+ messages in thread
From: Sakari Ailus @ 2018-03-24 10:57 UTC (permalink / raw)
  To: Rui Miguel Silva
  Cc: mchehab, sakari.ailus, hverkuil, linux-media, linux-kernel, Ryan Harkin

Hi Rui,

I wanted to go through the code the last time and I'd have a few review
comments below...

On Tue, Mar 13, 2018 at 11:33:11AM +0000, Rui Miguel Silva wrote:
...
> +static int ov2680_gain_set(struct ov2680_dev *sensor, bool auto_gain)
> +{
> +	struct ov2680_ctrls *ctrls = &sensor->ctrls;
> +	u32 gain;
> +	int ret;
> +
> +	ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(1),
> +			     auto_gain ? 0 : BIT(1));
> +	if (ret < 0)
> +		return ret;
> +
> +	if (auto_gain || !ctrls->gain->is_new)
> +		return 0;
> +
> +	gain = ctrls->gain->val;
> +
> +	ret = ov2680_write_reg16(sensor, OV2680_REG_GAIN_PK, gain);
> +
> +	return 0;
> +}
> +
> +static int ov2680_gain_get(struct ov2680_dev *sensor)
> +{
> +	u32 gain;
> +	int ret;
> +
> +	ret = ov2680_read_reg16(sensor, OV2680_REG_GAIN_PK, &gain);
> +	if (ret)
> +		return ret;
> +
> +	return gain;
> +}
> +
> +static int ov2680_auto_gain_enable(struct ov2680_dev *sensor)
> +{
> +	return ov2680_gain_set(sensor, true);
> +}
> +
> +static int ov2680_auto_gain_disable(struct ov2680_dev *sensor)
> +{
> +	return ov2680_gain_set(sensor, false);
> +}
> +
> +static int ov2680_exposure_set(struct ov2680_dev *sensor, bool auto_exp)
> +{
> +	struct ov2680_ctrls *ctrls = &sensor->ctrls;
> +	u32 exp;
> +	int ret;
> +
> +	ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(0),
> +			     auto_exp ? 0 : BIT(0));
> +	if (ret < 0)
> +		return ret;
> +
> +	if (auto_exp || !ctrls->exposure->is_new)
> +		return 0;
> +
> +	exp = (u32)ctrls->exposure->val;
> +	exp <<= 4;
> +
> +	return ov2680_write_reg24(sensor, OV2680_REG_EXPOSURE_PK_HIGH, exp);
> +}
> +
> +static int ov2680_exposure_get(struct ov2680_dev *sensor)
> +{
> +	int ret;
> +	u32 exp;
> +
> +	ret = ov2680_read_reg24(sensor, OV2680_REG_EXPOSURE_PK_HIGH, &exp);
> +	if (ret)
> +		return ret;
> +
> +	return exp >> 4;
> +}
> +
> +static int ov2680_auto_exposure_enable(struct ov2680_dev *sensor)
> +{
> +	return ov2680_exposure_set(sensor, true);
> +}
> +
> +static int ov2680_auto_exposure_disable(struct ov2680_dev *sensor)
> +{
> +	return ov2680_exposure_set(sensor, false);
> +}

I still think you could call the function actually doing the work, and pass
the bool parameter. That'd be much clearer. Please see the comments below,
too; they're related. Same for gain.

...

> +static int ov2680_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
> +	struct ov2680_dev *sensor = to_ov2680_dev(sd);
> +	int val;
> +
> +	if (!sensor->is_enabled)
> +		return 0;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_AUTOGAIN:
> +		if (!ctrl->val)
> +			return 0;
> +		val = ov2680_gain_get(sensor);
> +		if (val < 0)
> +			return val;
> +		sensor->ctrls.gain->val = val;
> +		break;
> +	case V4L2_CID_EXPOSURE_AUTO:

I reckon the purpose of implementing volatile controls here would be to
provide the exposure and gain values to the user. But the controls here are
the ones enabling or disabling the automatic behaviour, not the value of
those settings themselves.

> +		if (ctrl->val == V4L2_EXPOSURE_MANUAL)
> +			return 0;
> +		val = ov2680_exposure_get(sensor);
> +		if (val < 0)
> +			return val;
> +		sensor->ctrls.exposure->val = val;
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
> +	struct ov2680_dev *sensor = to_ov2680_dev(sd);
> +
> +	if (!sensor->is_enabled)
> +		return 0;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_AUTOGAIN:
> +		return ov2680_gain_set(sensor, !!ctrl->val);
> +	case V4L2_CID_EXPOSURE_AUTO:
> +		return ov2680_exposure_set(sensor, !!ctrl->val);

With this, you can enable or disable automatic exposure and gain, but you
cannot manually set the values. Are such controls useful?

Unless I'm mistaken, exposure or gain are not settable, so you should make
them read-only controls. Or better, allow setting them if automatic control
is disabled.

> +	case V4L2_CID_VFLIP:
> +		if (sensor->is_streaming)
> +			return -EBUSY;
> +		if (ctrl->val)
> +			return ov2680_vflip_enable(sensor);
> +		else
> +			return ov2680_vflip_disable(sensor);
> +	case V4L2_CID_HFLIP:
> +		if (ctrl->val)
> +			return ov2680_hflip_enable(sensor);
> +		else
> +			return ov2680_hflip_disable(sensor);
> +	case V4L2_CID_TEST_PATTERN:
> +		return ov2680_test_pattern_set(sensor, ctrl->val);
> +	default:
> +		break;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static const struct v4l2_ctrl_ops ov2680_ctrl_ops = {
> +	.g_volatile_ctrl = ov2680_g_volatile_ctrl,
> +	.s_ctrl = ov2680_s_ctrl,
> +};
> +
> +static const struct v4l2_subdev_core_ops ov2680_core_ops = {
> +	.s_power = ov2680_s_power,
> +};
> +
> +static const struct v4l2_subdev_video_ops ov2680_video_ops = {
> +	.g_frame_interval	= ov2680_s_g_frame_interval,
> +	.s_frame_interval	= ov2680_s_g_frame_interval,
> +	.s_stream		= ov2680_s_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops ov2680_pad_ops = {
> +	.enum_mbus_code		= ov2680_enum_mbus_code,
> +	.get_fmt		= ov2680_get_fmt,
> +	.set_fmt		= ov2680_set_fmt,
> +	.enum_frame_size	= ov2680_enum_frame_size,
> +	.enum_frame_interval	= ov2680_enum_frame_interval,
> +};
> +
> +static const struct v4l2_subdev_ops ov2680_subdev_ops = {
> +	.core	= &ov2680_core_ops,
> +	.video	= &ov2680_video_ops,
> +	.pad	= &ov2680_pad_ops,
> +};
> +
> +static int ov2680_mode_init(struct ov2680_dev *sensor)
> +{
> +	const struct ov2680_mode_info *init_mode;
> +
> +	/* set initial mode */
> +	sensor->fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10;
> +	sensor->fmt.width = 800;
> +	sensor->fmt.height = 600;
> +	sensor->fmt.field = V4L2_FIELD_NONE;
> +	sensor->fmt.colorspace = V4L2_COLORSPACE_SRGB;
> +
> +	sensor->frame_interval.denominator = OV2680_FRAME_RATE;
> +	sensor->frame_interval.numerator = 1;
> +
> +	init_mode = &ov2680_mode_init_data;
> +
> +	sensor->current_mode = init_mode;
> +
> +	sensor->mode_pending_changes = true;
> +
> +	return 0;
> +}
> +
> +static int ov2680_v4l2_init(struct ov2680_dev *sensor)
> +{
> +	const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops;
> +	struct ov2680_ctrls *ctrls = &sensor->ctrls;
> +	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
> +	int ret = 0;
> +
> +	v4l2_i2c_subdev_init(&sensor->sd, sensor->i2c_client,
> +			     &ov2680_subdev_ops);
> +
> +	sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
> +	sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +
> +	ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
> +	if (ret < 0)
> +		return ret;
> +
> +	v4l2_ctrl_handler_init(hdl, 32);

You have only 7 controls.

> +
> +	hdl->lock = &sensor->lock;
> +
> +	ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
> +	ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
> +
> +	ctrls->test_pattern = v4l2_ctrl_new_std_menu_items(hdl,
> +					&ov2680_ctrl_ops,
> +					V4L2_CID_TEST_PATTERN,
> +					ARRAY_SIZE(test_pattern_menu) - 1,
> +					0, 0, test_pattern_menu);
> +
> +	ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
> +						 V4L2_CID_EXPOSURE_AUTO,
> +						 V4L2_EXPOSURE_MANUAL, 0,
> +						 V4L2_EXPOSURE_AUTO);
> +
> +	ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
> +					    0, 32767, 1, 0);
> +
> +	ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
> +					     0, 1, 1, 1);
> +	ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 0, 2047, 1, 0);
> +
> +	if (hdl->error) {
> +		ret = hdl->error;
> +		goto cleanup_entity;
> +	}
> +
> +	ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
> +	ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
> +
> +	v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
> +	v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
> +
> +	sensor->sd.ctrl_handler = hdl;
> +
> +	ret = v4l2_async_register_subdev(&sensor->sd);
> +	if (ret < 0)
> +		goto cleanup_entity;
> +
> +	return 0;
> +
> +cleanup_entity:
> +	media_entity_cleanup(&sensor->sd.entity);
> +	v4l2_ctrl_handler_free(hdl);
> +
> +	return ret;
> +}
> +
> +static int ov2680_check_id(struct ov2680_dev *sensor)
> +{
> +	struct device *dev = ov2680_to_dev(sensor);
> +	u32 chip_id;
> +	int ret;
> +
> +	ov2680_power_on(sensor);
> +
> +	ret = ov2680_read_reg16(sensor, OV2680_REG_CHIP_ID_HIGH, &chip_id);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to read chip id high\n");
> +		return -ENODEV;
> +	}
> +
> +	if (chip_id != OV2680_CHIP_ID) {
> +		dev_err(dev, "chip id: 0x%04x does not match expected 0x%04x\n",
> +			chip_id, OV2680_CHIP_ID);
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ov2860_parse_dt(struct ov2680_dev *sensor)
> +{
> +	struct device *dev = ov2680_to_dev(sensor);
> +	int ret;
> +
> +	sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
> +						    GPIOD_OUT_HIGH);
> +	ret = PTR_ERR_OR_ZERO(sensor->pwdn_gpio);
> +	if (ret < 0) {
> +		dev_dbg(dev, "error while getting powerdown gpio: %d\n", ret);
> +		return ret;
> +	}
> +
> +	sensor->xvclk = devm_clk_get(dev, "xvclk");
> +	if (IS_ERR(sensor->xvclk)) {
> +		dev_err(dev, "xvclk clock missing or invalid\n");
> +		return PTR_ERR(sensor->xvclk);
> +	}
> +
> +	sensor->xvclk_freq = clk_get_rate(sensor->xvclk);
> +	if (sensor->xvclk_freq < OV2680_XVCLK_MIN ||
> +	    sensor->xvclk_freq > OV2680_XVCLK_MAX) {

What's the frequency the register configuration requires? I think you
should check the exact frequency here, rather than a wide range. The
register lists would need to be adapted for other frequencies.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 2/2] media: ov2680: Add Omnivision OV2680 sensor driver
  2018-03-24 10:57   ` Sakari Ailus
@ 2018-03-27 12:39     ` Rui Miguel Silva
  0 siblings, 0 replies; 10+ messages in thread
From: Rui Miguel Silva @ 2018-03-27 12:39 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Rui Miguel Silva, mchehab, sakari.ailus, hverkuil, linux-media,
	linux-kernel, Ryan Harkin

Hi Sakari,
On Sat 24 Mar 2018 at 10:57, Sakari Ailus wrote:
> Hi Rui,
>
> I wanted to go through the code the last time and I'd have a few 
> review
> comments below...

Thanks for the review.

>
> On Tue, Mar 13, 2018 at 11:33:11AM +0000, Rui Miguel Silva 
> wrote:
> ...
>> +static int ov2680_gain_set(struct ov2680_dev *sensor, bool 
>> auto_gain)
>> +{
>> +	struct ov2680_ctrls *ctrls = &sensor->ctrls;
>> +	u32 gain;
>> +	int ret;
>> +
>> +	ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(1),
>> +			     auto_gain ? 0 : BIT(1));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	if (auto_gain || !ctrls->gain->is_new)
>> +		return 0;
>> +
>> +	gain = ctrls->gain->val;
>> +
>> +	ret = ov2680_write_reg16(sensor, OV2680_REG_GAIN_PK, 
>> gain);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov2680_gain_get(struct ov2680_dev *sensor)
>> +{
>> +	u32 gain;
>> +	int ret;
>> +
>> +	ret = ov2680_read_reg16(sensor, OV2680_REG_GAIN_PK, 
>> &gain);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return gain;
>> +}
>> +
>> +static int ov2680_auto_gain_enable(struct ov2680_dev *sensor)
>> +{
>> +	return ov2680_gain_set(sensor, true);
>> +}
>> +
>> +static int ov2680_auto_gain_disable(struct ov2680_dev *sensor)
>> +{
>> +	return ov2680_gain_set(sensor, false);
>> +}
>> +
>> +static int ov2680_exposure_set(struct ov2680_dev *sensor, bool 
>> auto_exp)
>> +{
>> +	struct ov2680_ctrls *ctrls = &sensor->ctrls;
>> +	u32 exp;
>> +	int ret;
>> +
>> +	ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(0),
>> +			     auto_exp ? 0 : BIT(0));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	if (auto_exp || !ctrls->exposure->is_new)
>> +		return 0;
>> +
>> +	exp = (u32)ctrls->exposure->val;
>> +	exp <<= 4;
>> +
>> +	return ov2680_write_reg24(sensor, 
>> OV2680_REG_EXPOSURE_PK_HIGH, exp);
>> +}
>> +
>> +static int ov2680_exposure_get(struct ov2680_dev *sensor)
>> +{
>> +	int ret;
>> +	u32 exp;
>> +
>> +	ret = ov2680_read_reg24(sensor, 
>> OV2680_REG_EXPOSURE_PK_HIGH, &exp);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return exp >> 4;
>> +}
>> +
>> +static int ov2680_auto_exposure_enable(struct ov2680_dev 
>> *sensor)
>> +{
>> +	return ov2680_exposure_set(sensor, true);
>> +}
>> +
>> +static int ov2680_auto_exposure_disable(struct ov2680_dev 
>> *sensor)
>> +{
>> +	return ov2680_exposure_set(sensor, false);
>> +}
>
> I still think you could call the function actually doing the 
> work, and pass
> the bool parameter. That'd be much clearer. Please see the 
> comments below,
> too; they're related. Same for gain.

Ok, no problem, will change that in v4.

>
> ...
>
>> +static int ov2680_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
>> +	struct ov2680_dev *sensor = to_ov2680_dev(sd);
>> +	int val;
>> +
>> +	if (!sensor->is_enabled)
>> +		return 0;
>> +
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_AUTOGAIN:
>> +		if (!ctrl->val)
>> +			return 0;
>> +		val = ov2680_gain_get(sensor);
>> +		if (val < 0)
>> +			return val;
>> +		sensor->ctrls.gain->val = val;
>> +		break;
>> +	case V4L2_CID_EXPOSURE_AUTO:
>
> I reckon the purpose of implementing volatile controls here 
> would be to
> provide the exposure and gain values to the user. But the 
> controls here are
> the ones enabling or disabling the automatic behaviour, not the 
> value of
> those settings themselves.
>
>> +		if (ctrl->val == V4L2_EXPOSURE_MANUAL)
>> +			return 0;
>> +		val = ov2680_exposure_get(sensor);
>> +		if (val < 0)
>> +			return val;
>> +		sensor->ctrls.exposure->val = val;
>> +		break;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
>> +	struct ov2680_dev *sensor = to_ov2680_dev(sd);
>> +
>> +	if (!sensor->is_enabled)
>> +		return 0;
>> +
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_AUTOGAIN:
>> +		return ov2680_gain_set(sensor, !!ctrl->val);
>> +	case V4L2_CID_EXPOSURE_AUTO:
>> +		return ov2680_exposure_set(sensor, !!ctrl->val);
>
> With this, you can enable or disable automatic exposure and 
> gain, but you
> cannot manually set the values. Are such controls useful?
>
> Unless I'm mistaken, exposure or gain are not settable, so you 
> should make
> them read-only controls. Or better, allow setting them if 
> automatic control
> is disabled.

Yeah, I could definitely change the values of exposure and gain 
also,
but that may came how the ctrl group work internally. But I will 
change
and add them.

>
>> +	case V4L2_CID_VFLIP:
>> +		if (sensor->is_streaming)
>> +			return -EBUSY;
>> +		if (ctrl->val)
>> +			return ov2680_vflip_enable(sensor);
>> +		else
>> +			return ov2680_vflip_disable(sensor);
>> +	case V4L2_CID_HFLIP:
>> +		if (ctrl->val)
>> +			return ov2680_hflip_enable(sensor);
>> +		else
>> +			return ov2680_hflip_disable(sensor);
>> +	case V4L2_CID_TEST_PATTERN:
>> +		return ov2680_test_pattern_set(sensor, ctrl->val);
>> +	default:
>> +		break;
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct v4l2_ctrl_ops ov2680_ctrl_ops = {
>> +	.g_volatile_ctrl = ov2680_g_volatile_ctrl,
>> +	.s_ctrl = ov2680_s_ctrl,
>> +};
>> +
>> +static const struct v4l2_subdev_core_ops ov2680_core_ops = {
>> +	.s_power = ov2680_s_power,
>> +};
>> +
>> +static const struct v4l2_subdev_video_ops ov2680_video_ops = {
>> +	.g_frame_interval	= ov2680_s_g_frame_interval,
>> +	.s_frame_interval	= ov2680_s_g_frame_interval,
>> +	.s_stream		= ov2680_s_stream,
>> +};
>> +
>> +static const struct v4l2_subdev_pad_ops ov2680_pad_ops = {
>> +	.enum_mbus_code		= ov2680_enum_mbus_code,
>> +	.get_fmt		= ov2680_get_fmt,
>> +	.set_fmt		= ov2680_set_fmt,
>> +	.enum_frame_size	= ov2680_enum_frame_size,
>> +	.enum_frame_interval	= ov2680_enum_frame_interval,
>> +};
>> +
>> +static const struct v4l2_subdev_ops ov2680_subdev_ops = {
>> +	.core	= &ov2680_core_ops,
>> +	.video	= &ov2680_video_ops,
>> +	.pad	= &ov2680_pad_ops,
>> +};
>> +
>> +static int ov2680_mode_init(struct ov2680_dev *sensor)
>> +{
>> +	const struct ov2680_mode_info *init_mode;
>> +
>> +	/* set initial mode */
>> +	sensor->fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10;
>> +	sensor->fmt.width = 800;
>> +	sensor->fmt.height = 600;
>> +	sensor->fmt.field = V4L2_FIELD_NONE;
>> +	sensor->fmt.colorspace = V4L2_COLORSPACE_SRGB;
>> +
>> +	sensor->frame_interval.denominator = OV2680_FRAME_RATE;
>> +	sensor->frame_interval.numerator = 1;
>> +
>> +	init_mode = &ov2680_mode_init_data;
>> +
>> +	sensor->current_mode = init_mode;
>> +
>> +	sensor->mode_pending_changes = true;
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov2680_v4l2_init(struct ov2680_dev *sensor)
>> +{
>> +	const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops;
>> +	struct ov2680_ctrls *ctrls = &sensor->ctrls;
>> +	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
>> +	int ret = 0;
>> +
>> +	v4l2_i2c_subdev_init(&sensor->sd, sensor->i2c_client,
>> +			     &ov2680_subdev_ops);
>> +
>> +	sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
>> +	sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
>> +
>> +	ret = media_entity_pads_init(&sensor->sd.entity, 1, 
>> &sensor->pad);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	v4l2_ctrl_handler_init(hdl, 32);
>
> You have only 7 controls.

Yes, good catch.

>
>> +
>> +	hdl->lock = &sensor->lock;
>> +
>> +	ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 
>> 0, 1, 1, 0);
>> +	ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 
>> 0, 1, 1, 0);
>> +
>> +	ctrls->test_pattern = v4l2_ctrl_new_std_menu_items(hdl,
>> +					&ov2680_ctrl_ops,
>> +					V4L2_CID_TEST_PATTERN,
>> + 
>> ARRAY_SIZE(test_pattern_menu) - 1,
>> +					0, 0, test_pattern_menu);
>> +
>> +	ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
>> + 
>> V4L2_CID_EXPOSURE_AUTO,
>> + 
>> V4L2_EXPOSURE_MANUAL, 0,
>> + 
>> V4L2_EXPOSURE_AUTO);
>> +
>> +	ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, 
>> V4L2_CID_EXPOSURE,
>> +					    0, 32767, 1, 0);
>> +
>> +	ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, 
>> V4L2_CID_AUTOGAIN,
>> +					     0, 1, 1, 1);
>> +	ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 
>> 0, 2047, 1, 0);
>> +
>> +	if (hdl->error) {
>> +		ret = hdl->error;
>> +		goto cleanup_entity;
>> +	}
>> +
>> +	ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
>> +	ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
>> +
>> +	v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
>> +	v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
>> +
>> +	sensor->sd.ctrl_handler = hdl;
>> +
>> +	ret = v4l2_async_register_subdev(&sensor->sd);
>> +	if (ret < 0)
>> +		goto cleanup_entity;
>> +
>> +	return 0;
>> +
>> +cleanup_entity:
>> +	media_entity_cleanup(&sensor->sd.entity);
>> +	v4l2_ctrl_handler_free(hdl);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ov2680_check_id(struct ov2680_dev *sensor)
>> +{
>> +	struct device *dev = ov2680_to_dev(sensor);
>> +	u32 chip_id;
>> +	int ret;
>> +
>> +	ov2680_power_on(sensor);
>> +
>> +	ret = ov2680_read_reg16(sensor, OV2680_REG_CHIP_ID_HIGH, 
>> &chip_id);
>> +	if (ret < 0) {
>> +		dev_err(dev, "failed to read chip id high\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	if (chip_id != OV2680_CHIP_ID) {
>> +		dev_err(dev, "chip id: 0x%04x does not match 
>> expected 0x%04x\n",
>> +			chip_id, OV2680_CHIP_ID);
>> +		return -ENODEV;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov2860_parse_dt(struct ov2680_dev *sensor)
>> +{
>> +	struct device *dev = ov2680_to_dev(sensor);
>> +	int ret;
>> +
>> +	sensor->pwdn_gpio = devm_gpiod_get_optional(dev, 
>> "powerdown",
>> + 
>> GPIOD_OUT_HIGH);
>> +	ret = PTR_ERR_OR_ZERO(sensor->pwdn_gpio);
>> +	if (ret < 0) {
>> +		dev_dbg(dev, "error while getting powerdown gpio: 
>> %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	sensor->xvclk = devm_clk_get(dev, "xvclk");
>> +	if (IS_ERR(sensor->xvclk)) {
>> +		dev_err(dev, "xvclk clock missing or invalid\n");
>> +		return PTR_ERR(sensor->xvclk);
>> +	}
>> +
>> +	sensor->xvclk_freq = clk_get_rate(sensor->xvclk);
>> +	if (sensor->xvclk_freq < OV2680_XVCLK_MIN ||
>> +	    sensor->xvclk_freq > OV2680_XVCLK_MAX) {
>
> What's the frequency the register configuration requires? I 
> think you
> should check the exact frequency here, rather than a wide range. 
> The
> register lists would need to be adapted for other frequencies.

Makes sense, will change also in v4  that will go shortly.

---
Cheers,
	Rui

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2018-03-27 12:39 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-13 11:33 [PATCH v3 0/2] media: Introduce Omnivision OV2680 driver Rui Miguel Silva
2018-03-13 11:33 ` [PATCH v3 1/2] media: ov2680: dt: Add bindings for OV2680 Rui Miguel Silva
2018-03-13 11:33 ` [PATCH v3 2/2] media: ov2680: Add Omnivision OV2680 sensor driver Rui Miguel Silva
2018-03-14 19:39   ` kbuild test robot
2018-03-15  9:29     ` Rui Miguel Silva
2018-03-16 16:10       ` Sakari Ailus
2018-03-16 16:51         ` Rui Miguel Silva
2018-03-14 21:36   ` kbuild test robot
2018-03-24 10:57   ` Sakari Ailus
2018-03-27 12:39     ` Rui Miguel Silva

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).