[PATCH 1/3] media: ov2640: set default window and format code at probe time

2018-12-07 Thread Akinobu Mita
Set default window and format code at probe time instead of always checking
if they have not been set yet when VIDIOC_SUBDEV_G_FMT ioctl is called.

This change simplifies the next patch (make VIDIOC_SUBDEV_G_FMT ioctl work
with V4L2_SUBDEV_FORMAT_TRY).

Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/ov2640.c | 10 +++---
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c
index d8e91bc..a07e6f2 100644
--- a/drivers/media/i2c/ov2640.c
+++ b/drivers/media/i2c/ov2640.c
@@ -842,9 +842,6 @@ static int ov2640_set_params(struct i2c_client *client,
u8 val;
int ret;
 
-   if (!win)
-   return -EINVAL;
-
switch (code) {
case MEDIA_BUS_FMT_RGB565_2X8_BE:
dev_dbg(>dev, "%s: Selected cfmt RGB565 BE", __func__);
@@ -929,10 +926,6 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd,
if (format->pad)
return -EINVAL;
 
-   if (!priv->win) {
-   priv->win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT);
-   priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8;
-   }
 
mf->width   = priv->win->width;
mf->height  = priv->win->height;
@@ -1193,6 +1186,9 @@ static int ov2640_probe(struct i2c_client *client,
if (ret)
goto err_clk;
 
+   priv->win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT);
+   priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8;
+
v4l2_i2c_subdev_init(>subdev, client, _subdev_ops);
priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
  V4L2_SUBDEV_FL_HAS_EVENTS;
-- 
2.7.4



[PATCH 3/3] media: ov2640: set all mbus format field when G_FMT and S_FMT ioctls

2018-12-07 Thread Akinobu Mita
This driver doesn't set all members of mbus format field when the
VIDIOC_SUBDEV_{S,G}_FMT ioctls are called.

This is detected by v4l2-compliance.

Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/ov2640.c | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c
index 5f888f5..439c6da 100644
--- a/drivers/media/i2c/ov2640.c
+++ b/drivers/media/i2c/ov2640.c
@@ -938,6 +938,9 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd,
mf->code= priv->cfmt_code;
mf->colorspace  = V4L2_COLORSPACE_SRGB;
mf->field   = V4L2_FIELD_NONE;
+   mf->ycbcr_enc   = V4L2_YCBCR_ENC_DEFAULT;
+   mf->quantization = V4L2_QUANTIZATION_DEFAULT;
+   mf->xfer_func   = V4L2_XFER_FUNC_DEFAULT;
 
return 0;
 }
@@ -964,6 +967,9 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd,
 
mf->field   = V4L2_FIELD_NONE;
mf->colorspace  = V4L2_COLORSPACE_SRGB;
+   mf->ycbcr_enc   = V4L2_YCBCR_ENC_DEFAULT;
+   mf->quantization = V4L2_QUANTIZATION_DEFAULT;
+   mf->xfer_func   = V4L2_XFER_FUNC_DEFAULT;
 
switch (mf->code) {
case MEDIA_BUS_FMT_RGB565_2X8_BE:
-- 
2.7.4



[PATCH 2/3] media: ov2640: make VIDIOC_SUBDEV_G_FMT ioctl work with V4L2_SUBDEV_FORMAT_TRY

2018-12-07 Thread Akinobu Mita
The VIDIOC_SUBDEV_G_FMT ioctl for this driver doesn't recognize
V4L2_SUBDEV_FORMAT_TRY and always works as if V4L2_SUBDEV_FORMAT_ACTIVE
is specified.

Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/ov2640.c | 27 +++
 1 file changed, 27 insertions(+)

diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c
index a07e6f2..5f888f5 100644
--- a/drivers/media/i2c/ov2640.c
+++ b/drivers/media/i2c/ov2640.c
@@ -926,6 +926,12 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd,
if (format->pad)
return -EINVAL;
 
+   if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+   mf = v4l2_subdev_get_try_format(sd, cfg, 0);
+   format->format = *mf;
+
+   return 0;
+   }
 
mf->width   = priv->win->width;
mf->height  = priv->win->height;
@@ -992,6 +998,26 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd,
return ret;
 }
 
+static int ov2640_init_cfg(struct v4l2_subdev *sd,
+  struct v4l2_subdev_pad_config *cfg)
+{
+   struct i2c_client *client = v4l2_get_subdevdata(sd);
+   struct ov2640_priv *priv = to_ov2640(client);
+   struct v4l2_mbus_framefmt *try_fmt =
+   v4l2_subdev_get_try_format(sd, cfg, 0);
+
+   try_fmt->width = priv->win->width;
+   try_fmt->height = priv->win->height;
+   try_fmt->code = priv->cfmt_code;
+   try_fmt->colorspace = V4L2_COLORSPACE_SRGB;
+   try_fmt->field = V4L2_FIELD_NONE;
+   try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+   try_fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
+   try_fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+
+   return 0;
+}
+
 static int ov2640_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
@@ -1101,6 +1127,7 @@ static const struct v4l2_subdev_core_ops 
ov2640_subdev_core_ops = {
 };
 
 static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = {
+   .init_cfg   = ov2640_init_cfg,
.enum_mbus_code = ov2640_enum_mbus_code,
.get_selection  = ov2640_get_selection,
.get_fmt= ov2640_get_fmt,
-- 
2.7.4



[PATCH 0/3] media: ov2640: fix two problems

2018-12-07 Thread Akinobu Mita
This patch series contains two bugfixes and a preparatory change for
ov2640 driver.

Akinobu Mita (3):
  media: ov2640: set default window and format code at probe time
  media: ov2640: make VIDIOC_SUBDEV_G_FMT ioctl work with
V4L2_SUBDEV_FORMAT_TRY
  media: ov2640: set all mbus format field when G_FMT and S_FMT ioctls

 drivers/media/i2c/ov2640.c | 41 +++--
 1 file changed, 35 insertions(+), 6 deletions(-)

Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
-- 
2.7.4



Re: [PATCH v3] media: video-i2c: check if chip struct has set_power function

2018-11-25 Thread Akinobu Mita
2018年11月25日(日) 7:03 Matt Ranostay :
>
> Not all future supported video chips will always have power management
> support, and so it is important to check before calling set_power() is
> defined.
>
> Cc: Sakari Ailus 
> Cc: Hans Verkuil 
> Cc: Mauro Carvalho Chehab 
> Cc: Akinobu Mita 
> Signed-off-by: Matt Ranostay 

Looks good.

Reviewed-by: Akinobu Mita 


[PATCH v5] media: video-i2c: support runtime PM

2018-11-22 Thread Akinobu Mita
AMG88xx has a register for setting operating mode.  This adds support
runtime PM by changing the operating mode.

The instruction for changing sleep mode to normal mode is from the
reference specifications.

https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Reviewed-by: Matt Ranostay 
Acked-by: Sakari Ailus 
Signed-off-by: Akinobu Mita 
---
This is an update of "[PATCH v4 6/6] media: video-i2c: support runtime PM"
in the patchset "[PATCH v4 0/6] media: video-i2c: support changing frame
interval and runtime PM".

* v5
- don't use msleep for 1ms - 20ms

 drivers/media/i2c/video-i2c.c | 141 +-
 1 file changed, 139 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index dff218c..77080d7 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -17,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -94,10 +95,23 @@ struct video_i2c_chip {
/* xfer function */
int (*xfer)(struct video_i2c_data *data, char *buf);
 
+   /* power control function */
+   int (*set_power)(struct video_i2c_data *data, bool on);
+
/* hwmon init function */
int (*hwmon_init)(struct video_i2c_data *data);
 };
 
+/* Power control register */
+#define AMG88XX_REG_PCTL   0x00
+#define AMG88XX_PCTL_NORMAL0x00
+#define AMG88XX_PCTL_SLEEP 0x10
+
+/* Reset register */
+#define AMG88XX_REG_RST0x01
+#define AMG88XX_RST_FLAG   0x30
+#define AMG88XX_RST_INIT   0x3f
+
 /* Frame rate register */
 #define AMG88XX_REG_FPSC   0x02
 #define AMG88XX_FPSC_1FPS  BIT(0)
@@ -127,6 +141,59 @@ static int amg88xx_setup(struct video_i2c_data *data)
return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val);
 }
 
+static int amg88xx_set_power_on(struct video_i2c_data *data)
+{
+   int ret;
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, AMG88XX_PCTL_NORMAL);
+   if (ret)
+   return ret;
+
+   msleep(50);
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_INIT);
+   if (ret)
+   return ret;
+
+   usleep_range(2000, 3000);
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_FLAG);
+   if (ret)
+   return ret;
+
+   /*
+* Wait two frames before reading thermistor and temperature registers
+*/
+   msleep(200);
+
+   return 0;
+}
+
+static int amg88xx_set_power_off(struct video_i2c_data *data)
+{
+   int ret;
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, AMG88XX_PCTL_SLEEP);
+   if (ret)
+   return ret;
+   /*
+* Wait for a while to avoid resuming normal mode immediately after
+* entering sleep mode, otherwise the device occasionally goes wrong
+* (thermistor and temperature registers are not updated at all)
+*/
+   msleep(100);
+
+   return 0;
+}
+
+static int amg88xx_set_power(struct video_i2c_data *data, bool on)
+{
+   if (on)
+   return amg88xx_set_power_on(data);
+
+   return amg88xx_set_power_off(data);
+}
+
 #if IS_ENABLED(CONFIG_HWMON)
 
 static const u32 amg88xx_temp_config[] = {
@@ -158,7 +225,15 @@ static int amg88xx_read(struct device *dev, enum 
hwmon_sensor_types type,
__le16 buf;
int tmp;
 
+   tmp = pm_runtime_get_sync(regmap_get_device(data->regmap));
+   if (tmp < 0) {
+   pm_runtime_put_noidle(regmap_get_device(data->regmap));
+   return tmp;
+   }
+
tmp = regmap_bulk_read(data->regmap, AMG88XX_REG_TTHL, , 2);
+   pm_runtime_mark_last_busy(regmap_get_device(data->regmap));
+   pm_runtime_put_autosuspend(regmap_get_device(data->regmap));
if (tmp)
return tmp;
 
@@ -217,6 +292,7 @@ static const struct video_i2c_chip video_i2c_chip[] = {
.regmap_config  = _regmap_config,
.setup  = _setup,
.xfer   = _xfer,
+   .set_power  = amg88xx_set_power,
.hwmon_init = amg88xx_hwmon_init,
},
 };
@@ -343,14 +419,21 @@ static void video_i2c_del_list(struct vb2_queue *vq, enum 
vb2_buffer_state state
 static int start_streaming(struct vb2_queue *vq, unsigned int count)
 {
struct video_i2c_data *data = vb2_get_drv_priv(vq);
+   struct device *dev = regmap_get_device(data->regmap);
int ret;
 
if (data->kthread_vid_cap)
return 0;
 
+   ret = pm_runtime_get_sync(dev);
+   if (ret < 0) {
+   pm_runtime_put_noidle(dev);
+   goto error_del_list;
+   }
+
ret = data->chip-&

[PATCH] media: video-i2c: don't use msleep for 1ms - 20ms

2018-11-20 Thread Akinobu Mita
Documentation/timers/timers-howto.txt says:

"msleep(1~20) may not do what the caller intends, and will often sleep
longer (~20 ms actual sleep for any value given in the 1~20ms range)."

So replace msleep(2) by usleep_range(2000, 3000).

Reported-by: Hans Verkuil 
Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
This fixes "[PATCH v4 6/6] media: video-i2c: support runtime PM" in the
patchset "[PATCH v4 0/6] media: video-i2c: support changing frame interval
and runtime PM".

 drivers/media/i2c/video-i2c.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index 0c82131..77080d7 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -155,7 +155,7 @@ static int amg88xx_set_power_on(struct video_i2c_data *data)
if (ret)
return ret;
 
-   msleep(2);
+   usleep_range(2000, 3000);
 
ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_FLAG);
if (ret)
-- 
2.7.4



Re: [PATCH 3/7] media: ov2640: add V4L2_CID_TEST_PATTERN control

2018-11-13 Thread Akinobu Mita
2018年11月13日(火) 19:37 Sakari Ailus :
>
> On Tue, Nov 13, 2018 at 01:00:50AM +0900, Akinobu Mita wrote:
> > The ov2640 has the test pattern generator features.  This makes use of
> > it through V4L2_CID_TEST_PATTERN control.
> >
> > Cc: Sakari Ailus 
> > Cc: Mauro Carvalho Chehab 
> > Signed-off-by: Akinobu Mita 
> > ---
> >  drivers/media/i2c/ov2640.c | 14 +-
> >  1 file changed, 13 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c
> > index 20a8853..4992d77 100644
> > --- a/drivers/media/i2c/ov2640.c
> > +++ b/drivers/media/i2c/ov2640.c
> > @@ -705,6 +705,11 @@ static int ov2640_reset(struct i2c_client *client)
> >   return ret;
> >  }
> >
> > +static const char * const ov2640_test_pattern_menu[] = {
> > + "Disabled",
> > + "Color bar",
>
> s/b/B/
>
> The smiapp driver uses "Eight Vertical Colour Bars", not sure if that'd fit
> here or not. FYI.

This test pattern shows eight vertical color bars with blending actual
sensor image, although the datasheet tells just 'Color bar'.

So either is fine for me.


[PATCH 5/7] media: ov5640: support log_status ioctl and event interface

2018-11-12 Thread Akinobu Mita
This adds log_status ioctl and event interface for ov5640's v4l2 controls.

Cc: Steve Longerbeam 
Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/ov5640.c | 7 ++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index eaefdb5..a91d917 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -25,6 +25,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 
@@ -2641,6 +2642,9 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int 
enable)
 
 static const struct v4l2_subdev_core_ops ov5640_core_ops = {
.s_power = ov5640_s_power,
+   .log_status = v4l2_ctrl_subdev_log_status,
+   .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+   .unsubscribe_event = v4l2_event_subdev_unsubscribe,
 };
 
 static const struct v4l2_subdev_video_ops ov5640_video_ops = {
@@ -2795,7 +2799,8 @@ static int ov5640_probe(struct i2c_client *client,
 
v4l2_i2c_subdev_init(>sd, client, _subdev_ops);
 
-   sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+   sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+   V4L2_SUBDEV_FL_HAS_EVENTS;
sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
ret = media_entity_pads_init(>sd.entity, 1, >pad);
-- 
2.7.4



[PATCH 6/7] media: ov7670: support log_status ioctl and event interface

2018-11-12 Thread Akinobu Mita
This adds log_status ioctl and event interface for ov7670's v4l2 controls.

Cc: Jonathan Corbet 
Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/ov7670.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index bc68a3a..a70a6ff 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
@@ -20,6 +20,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -1651,6 +1652,9 @@ static int ov7670_open(struct v4l2_subdev *sd, struct 
v4l2_subdev_fh *fh)
 static const struct v4l2_subdev_core_ops ov7670_core_ops = {
.reset = ov7670_reset,
.init = ov7670_init,
+   .log_status = v4l2_ctrl_subdev_log_status,
+   .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+   .unsubscribe_event = v4l2_event_subdev_unsubscribe,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov7670_g_register,
.s_register = ov7670_s_register,
@@ -1773,7 +1777,7 @@ static int ov7670_probe(struct i2c_client *client,
 
 #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
sd->internal_ops = _subdev_internal_ops;
-   sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+   sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
 #endif
 
info->clock_speed = 30; /* default: a guess */
-- 
2.7.4



[PATCH 4/7] media: ov2640: support log_status ioctl and event interface

2018-11-12 Thread Akinobu Mita
This adds log_status ioctl and event interface for ov2640's v4l2 controls.

Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/ov2640.c | 7 ++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c
index 4992d77..d8e91bc 100644
--- a/drivers/media/i2c/ov2640.c
+++ b/drivers/media/i2c/ov2640.c
@@ -26,6 +26,7 @@
 #include 
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -1096,6 +1097,9 @@ static const struct v4l2_ctrl_ops ov2640_ctrl_ops = {
 };
 
 static const struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
+   .log_status = v4l2_ctrl_subdev_log_status,
+   .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+   .unsubscribe_event = v4l2_event_subdev_unsubscribe,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov2640_g_register,
.s_register = ov2640_s_register,
@@ -1190,7 +1194,8 @@ static int ov2640_probe(struct i2c_client *client,
goto err_clk;
 
v4l2_i2c_subdev_init(>subdev, client, _subdev_ops);
-   priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+   priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_HAS_EVENTS;
mutex_init(>lock);
v4l2_ctrl_handler_init(>hdl, 3);
priv->hdl.lock = >lock;
-- 
2.7.4



[PATCH 7/7] media: ov772x: support log_status ioctl and event interface

2018-11-12 Thread Akinobu Mita
This adds log_status ioctl and event interface for ov772x's v4l2 controls.

Cc: Jacopo Mondi 
Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/ov772x.c | 7 ++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index fefff7f..2e9a758 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -30,6 +30,7 @@
 
 #include 
 #include 
+#include 
 #include 
 #include 
 
@@ -1287,6 +1288,9 @@ static const struct v4l2_ctrl_ops ov772x_ctrl_ops = {
 };
 
 static const struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
+   .log_status = v4l2_ctrl_subdev_log_status,
+   .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+   .unsubscribe_event = v4l2_event_subdev_unsubscribe,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov772x_g_register,
.s_register = ov772x_s_register,
@@ -1379,7 +1383,8 @@ static int ov772x_probe(struct i2c_client *client,
mutex_init(>lock);
 
v4l2_i2c_subdev_init(>subdev, client, _subdev_ops);
-   priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+   priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_HAS_EVENTS;
v4l2_ctrl_handler_init(>hdl, 3);
/* Use our mutex for the controls */
priv->hdl.lock = >lock;
-- 
2.7.4



[PATCH 3/7] media: ov2640: add V4L2_CID_TEST_PATTERN control

2018-11-12 Thread Akinobu Mita
The ov2640 has the test pattern generator features.  This makes use of
it through V4L2_CID_TEST_PATTERN control.

Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/ov2640.c | 14 +-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c
index 20a8853..4992d77 100644
--- a/drivers/media/i2c/ov2640.c
+++ b/drivers/media/i2c/ov2640.c
@@ -705,6 +705,11 @@ static int ov2640_reset(struct i2c_client *client)
return ret;
 }
 
+static const char * const ov2640_test_pattern_menu[] = {
+   "Disabled",
+   "Color bar",
+};
+
 /*
  * functions
  */
@@ -740,6 +745,9 @@ static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_HFLIP:
val = ctrl->val ? REG04_HFLIP_IMG : 0x00;
return ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val);
+   case V4L2_CID_TEST_PATTERN:
+   val = ctrl->val ? COM7_COLOR_BAR_TEST : 0x00;
+   return ov2640_mask_set(client, COM7, COM7_COLOR_BAR_TEST, val);
}
 
return -EINVAL;
@@ -1184,12 +1192,16 @@ static int ov2640_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(>subdev, client, _subdev_ops);
priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
mutex_init(>lock);
-   v4l2_ctrl_handler_init(>hdl, 2);
+   v4l2_ctrl_handler_init(>hdl, 3);
priv->hdl.lock = >lock;
v4l2_ctrl_new_std(>hdl, _ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(>hdl, _ctrl_ops,
V4L2_CID_HFLIP, 0, 1, 1, 0);
+   v4l2_ctrl_new_std_menu_items(>hdl, _ctrl_ops,
+   V4L2_CID_TEST_PATTERN,
+   ARRAY_SIZE(ov2640_test_pattern_menu) - 1, 0, 0,
+   ov2640_test_pattern_menu);
priv->subdev.ctrl_handler = >hdl;
if (priv->hdl.error) {
ret = priv->hdl.error;
-- 
2.7.4



[PATCH 2/7] media: mt9m111: add V4L2_CID_COLORFX control

2018-11-12 Thread Akinobu Mita
The mt9m111 has special camera effects feature.  This makes use of
it through V4L2_CID_COLORFX control.

Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/mt9m111.c | 37 -
 1 file changed, 36 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c
index f4fc459..58d134d 100644
--- a/drivers/media/i2c/mt9m111.c
+++ b/drivers/media/i2c/mt9m111.c
@@ -102,6 +102,7 @@
 #define MT9M111_REDUCER_XSIZE_A0x1a7
 #define MT9M111_REDUCER_YZOOM_A0x1a9
 #define MT9M111_REDUCER_YSIZE_A0x1aa
+#define MT9M111_EFFECTS_MODE   0x1e2
 
 #define MT9M111_OUTPUT_FORMAT_CTRL2_A  0x13a
 #define MT9M111_OUTPUT_FORMAT_CTRL2_B  0x19b
@@ -127,6 +128,7 @@
 #define MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN (1 << 1)
 #define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B(1 << 0)
 #define MT9M111_TPG_SEL_MASK   GENMASK(2, 0)
+#define MT9M111_EFFECTS_MODE_MASK  GENMASK(2, 0)
 
 /*
  * Camera control register addresses (0x200..0x2ff not implemented)
@@ -727,6 +729,29 @@ static int mt9m111_set_test_pattern(struct mt9m111 
*mt9m111, int val)
MT9M111_TPG_SEL_MASK);
 }
 
+static int mt9m111_set_colorfx(struct mt9m111 *mt9m111, int val)
+{
+   struct i2c_client *client = v4l2_get_subdevdata(>subdev);
+   static const struct v4l2_control colorfx[] = {
+   { V4L2_COLORFX_NONE,0 },
+   { V4L2_COLORFX_BW,  1 },
+   { V4L2_COLORFX_SEPIA,   2 },
+   { V4L2_COLORFX_NEGATIVE,3 },
+   { V4L2_COLORFX_SOLARIZATION,4 },
+   };
+   int i;
+
+   for (i = 0; i < ARRAY_SIZE(colorfx); i++) {
+   if (colorfx[i].id == val) {
+   return mt9m111_reg_mask(client, MT9M111_EFFECTS_MODE,
+   colorfx[i].value,
+   MT9M111_EFFECTS_MODE_MASK);
+   }
+   }
+
+   return -EINVAL;
+}
+
 static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl)
 {
struct mt9m111 *mt9m111 = container_of(ctrl->handler,
@@ -747,6 +772,8 @@ static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl)
return mt9m111_set_autowhitebalance(mt9m111, ctrl->val);
case V4L2_CID_TEST_PATTERN:
return mt9m111_set_test_pattern(mt9m111, ctrl->val);
+   case V4L2_CID_COLORFX:
+   return mt9m111_set_colorfx(mt9m111, ctrl->val);
}
 
return -EINVAL;
@@ -983,7 +1010,7 @@ static int mt9m111_probe(struct i2c_client *client,
mt9m111->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
 V4L2_SUBDEV_FL_HAS_EVENTS;
 
-   v4l2_ctrl_handler_init(>hdl, 5);
+   v4l2_ctrl_handler_init(>hdl, 7);
v4l2_ctrl_new_std(>hdl, _ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(>hdl, _ctrl_ops,
@@ -999,6 +1026,14 @@ static int mt9m111_probe(struct i2c_client *client,
_ctrl_ops, V4L2_CID_TEST_PATTERN,
ARRAY_SIZE(mt9m111_test_pattern_menu) - 1, 0, 0,
mt9m111_test_pattern_menu);
+   v4l2_ctrl_new_std_menu(>hdl, _ctrl_ops,
+   V4L2_CID_COLORFX, V4L2_COLORFX_SOLARIZATION,
+   ~(BIT(V4L2_COLORFX_NONE) |
+   BIT(V4L2_COLORFX_BW) |
+   BIT(V4L2_COLORFX_SEPIA) |
+   BIT(V4L2_COLORFX_NEGATIVE) |
+   BIT(V4L2_COLORFX_SOLARIZATION)),
+   V4L2_COLORFX_NONE);
mt9m111->subdev.ctrl_handler = >hdl;
if (mt9m111->hdl.error) {
ret = mt9m111->hdl.error;
-- 
2.7.4



[PATCH 1/7] media: mt9m111: support log_status ioctl and event interface

2018-11-12 Thread Akinobu Mita
This adds log_status ioctl and event interface for mt9m111's v4l2 controls.

Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/mt9m111.c | 7 ++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c
index 1395986..f4fc459 100644
--- a/drivers/media/i2c/mt9m111.c
+++ b/drivers/media/i2c/mt9m111.c
@@ -21,6 +21,7 @@
 #include 
 #include 
 #include 
+#include 
 
 /*
  * MT9M111, MT9M112 and MT9M131:
@@ -862,6 +863,9 @@ static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = {
 
 static const struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
.s_power= mt9m111_s_power,
+   .log_status = v4l2_ctrl_subdev_log_status,
+   .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+   .unsubscribe_event = v4l2_event_subdev_unsubscribe,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9m111_g_register,
.s_register = mt9m111_s_register,
@@ -976,7 +980,8 @@ static int mt9m111_probe(struct i2c_client *client,
mt9m111->ctx = _b;
 
v4l2_i2c_subdev_init(>subdev, client, _subdev_ops);
-   mt9m111->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+   mt9m111->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+V4L2_SUBDEV_FL_HAS_EVENTS;
 
v4l2_ctrl_handler_init(>hdl, 5);
v4l2_ctrl_new_std(>hdl, _ctrl_ops,
-- 
2.7.4



[PATCH 0/7] media: i2c: small enhancements for camera sensor drivers

2018-11-12 Thread Akinobu Mita
This patchset addds relatively small enhancements (log_status ioctl, event
interface, V4L2_CID_TEST_PATTERN control, and V4L2_CID_COLORFX control) for
mt9m111, ov2640, ov5640, ov7670, and ov772x drivers.  I have these devices
so these patches are tested with real devices.

Akinobu Mita (7):
  media: mt9m111: support log_status ioctl and event interface
  media: mt9m111: add V4L2_CID_COLORFX control
  media: ov2640: add V4L2_CID_TEST_PATTERN control
  media: ov2640: support log_status ioctl and event interface
  media: ov5640: support log_status ioctl and event interface
  media: ov7670: support log_status ioctl and event interface
  media: ov772x: support log_status ioctl and event interface

 drivers/media/i2c/mt9m111.c | 44 ++--
 drivers/media/i2c/ov2640.c  | 21 +++--
 drivers/media/i2c/ov5640.c  |  7 ++-
 drivers/media/i2c/ov7670.c  |  6 +-
 drivers/media/i2c/ov772x.c  |  7 ++-
 5 files changed, 78 insertions(+), 7 deletions(-)

Cc: Steve Longerbeam 
Cc: Jonathan Corbet 
Cc: Jacopo Mondi 
Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
-- 
2.7.4



[PATCH] media: xilinx-video: fix bad of_node_put() on endpoint error

2018-11-04 Thread Akinobu Mita
The fwnode_graph_get_next_endpoint() returns an 'endpoint' node pointer
with refcount incremented, and refcount of the passed as a previous
'endpoint' node is decremented.

So when iterating over all nodes using fwnode_graph_get_next_endpoint(),
we don't need to call fwnode_handle_put() for each node except for error
exit paths.  Otherwise we get "OF: ERROR: Bad of_node_put() on ..."
messages.

Fixes: d079f94c9046 ("media: platform: Switch to 
v4l2_async_notifier_add_subdev")
Cc: Steve Longerbeam 
Cc: Hyun Kwon 
Cc: Laurent Pinchart 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/platform/xilinx/xilinx-vipp.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c 
b/drivers/media/platform/xilinx/xilinx-vipp.c
index 574614d..26b13fd 100644
--- a/drivers/media/platform/xilinx/xilinx-vipp.c
+++ b/drivers/media/platform/xilinx/xilinx-vipp.c
@@ -377,8 +377,6 @@ static int xvip_graph_parse_one(struct 
xvip_composite_device *xdev,
goto err_notifier_cleanup;
}
 
-   fwnode_handle_put(ep);
-
/* Skip entities that we have already processed. */
if (remote == of_fwnode_handle(xdev->dev->of_node) ||
xvip_graph_find_entity(xdev, remote)) {
-- 
2.7.4



Re: [PATCH v4 3/6] media: v4l2-common: add V4L2_FRACT_COMPARE

2018-10-28 Thread Akinobu Mita
2018年10月28日(日) 12:49 Matt Ranostay :
>
> On Sat, Oct 20, 2018 at 7:26 AM Akinobu Mita  wrote:
> >
> > Add macro to compare two v4l2_fract values in v4l2 common internal API.
> > The same macro FRACT_CMP() is used by vivid and bcm2835-camera.  This just
> > renames it to V4L2_FRACT_COMPARE in order to avoid namespace collision.
> >
> > Cc: Matt Ranostay 
> > Cc: Sakari Ailus 
> > Cc: Hans Verkuil 
> > Cc: Mauro Carvalho Chehab 
> > Acked-by: Sakari Ailus 
> > Signed-off-by: Akinobu Mita 
> > ---
> > * v4
> > - No changes from v3
> >
> >  include/media/v4l2-common.h | 5 +
> >  1 file changed, 5 insertions(+)
> >
> > diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
> > index cdc87ec..eafb8a3 100644
> > --- a/include/media/v4l2-common.h
> > +++ b/include/media/v4l2-common.h
> > @@ -384,4 +384,9 @@ int v4l2_g_parm_cap(struct video_device *vdev,
> >  int v4l2_s_parm_cap(struct video_device *vdev,
> > struct v4l2_subdev *sd, struct v4l2_streamparm *a);
> >
> > +/* Compare two v4l2_fract structs */
> > +#define V4L2_FRACT_COMPARE(a, OP, b)   \
> > +   ((u64)(a).numerator * (b).denominator OP\
> > +   (u64)(b).numerator * (a).denominator)
> > +
>
> Noticed a few issues today when testing another thermal camera that
> can do 0.5 fps to 64 fps with this macro..

I expect your new thermal camera's frame_intervals will be something
like below.

static const struct v4l2_fract frame_intervals[] = {
{ 1, 64 },  /* 64 fps */
{ 1, 4 },   /* 4 fps */
{ 1, 2 },   /* 2 fps */
{ 2, 1 },   /* 0.5 fps */
};

> 1) This can have collision easily when numerator and denominators
> multiplied have the same product, example is 0.5hz and 2hz have the
> same output as 2

I think V4L2_FRACT_COMPARE() can correctly compare with 0.5hz and 2hz.

V4L2_FRACT_COMPARE({ 1, 2 }, <=, { 2, 1 }); // -->  true
V4L2_FRACT_COMPARE({ 2, 1 }, <=, { 1, 2 }); //-->  false

> 2) Also this doesn't reduce fractions so I am seeing 400 compared
> with 4 for instance with a 4hz frame interval.

I think this works fine, too.

V4L2_FRACT_COMPARE({ 1, 400 }, <=, { 1, 4 }); //-->  true
V4L2_FRACT_COMPARE({ 1, 4 }, <=, { 1, 400 }); //-->  false

Or, do I misunderstand your problem?


Re: [PATCH v4 5/6] media: video-i2c: support changing frame interval

2018-10-28 Thread Akinobu Mita
2018年10月28日(日) 12:39 Matt Ranostay :
>
> On Sat, Oct 20, 2018 at 7:26 AM Akinobu Mita  wrote:
> >
> > AMG88xx has a register for setting frame rate 1 or 10 FPS.
> > This adds support changing frame interval.
> >
> > Reference specifications:
> > https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf
> >
> > Cc: Matt Ranostay 
> > Cc: Sakari Ailus 
> > Cc: Hans Verkuil 
> > Cc: Mauro Carvalho Chehab 
> > Acked-by: Matt Ranostay 
> > Acked-by: Sakari Ailus 
> > Signed-off-by: Akinobu Mita 
> > ---
> > * v4
> > - No changes from v3
> >
> >  drivers/media/i2c/video-i2c.c | 78 
> > ---
> >  1 file changed, 66 insertions(+), 12 deletions(-)
> >
> > diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
> > index f23cb91..3334fc2 100644
> > --- a/drivers/media/i2c/video-i2c.c
> > +++ b/drivers/media/i2c/video-i2c.c
> > @@ -52,6 +52,8 @@ struct video_i2c_data {
> >
> > struct task_struct *kthread_vid_cap;
> > struct list_head vid_cap_active;
> > +
> > +   struct v4l2_fract frame_interval;
> >  };
> >
> >  static const struct v4l2_fmtdesc amg88xx_format = {
> > @@ -74,8 +76,9 @@ struct video_i2c_chip {
> > const struct v4l2_fmtdesc *format;
> > const struct v4l2_frmsize_discrete *size;
> >
> > -   /* max frames per second */
> > -   unsigned int max_fps;
> > +   /* available frame intervals */
> > +   const struct v4l2_fract *frame_intervals;
> > +   unsigned int num_frame_intervals;
> >
> > /* pixel buffer size */
> > unsigned int buffer_size;
> > @@ -85,6 +88,9 @@ struct video_i2c_chip {
> >
> > const struct regmap_config *regmap_config;
> >
> > +   /* setup function */
> > +   int (*setup)(struct video_i2c_data *data);
> > +
> > /* xfer function */
> > int (*xfer)(struct video_i2c_data *data, char *buf);
> >
> > @@ -92,6 +98,10 @@ struct video_i2c_chip {
> > int (*hwmon_init)(struct video_i2c_data *data);
> >  };
> >
> > +/* Frame rate register */
> > +#define AMG88XX_REG_FPSC   0x02
> > +#define AMG88XX_FPSC_1FPS  BIT(0)
> > +
> >  /* Thermistor register */
> >  #define AMG88XX_REG_TTHL   0x0e
> >
> > @@ -104,6 +114,19 @@ static int amg88xx_xfer(struct video_i2c_data *data, 
> > char *buf)
> > data->chip->buffer_size);
> >  }
> >
> > +static int amg88xx_setup(struct video_i2c_data *data)
> > +{
> > +   unsigned int mask = AMG88XX_FPSC_1FPS;
> > +   unsigned int val;
> > +
> > +   if (data->frame_interval.numerator == 
> > data->frame_interval.denominator)
> > +   val = mask;
> > +   else
> > +   val = 0;
> > +
> > +   return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, 
> > val);
> > +}
> > +
> >  #if IS_ENABLED(CONFIG_HWMON)
> >
> >  static const u32 amg88xx_temp_config[] = {
> > @@ -178,14 +201,21 @@ static int amg88xx_hwmon_init(struct video_i2c_data 
> > *data)
> >
> >  #define AMG88XX0
> >
> > +static const struct v4l2_fract amg88xx_frame_intervals[] = {
> > +   { 1, 10 },
> > +   { 1, 1 },
> > +};
> > +
> >  static const struct video_i2c_chip video_i2c_chip[] = {
> > [AMG88XX] = {
> > .size   = _size,
> > .format = _format,
> > -   .max_fps= 10,
> > +   .frame_intervals= amg88xx_frame_intervals,
> > +   .num_frame_intervals= 
> > ARRAY_SIZE(amg88xx_frame_intervals),
> > .buffer_size= 128,
> > .bpp= 16,
> > .regmap_config  = _regmap_config,
> > +   .setup  = _setup,
> > .xfer   = _xfer,
> > .hwmon_init = amg88xx_hwmon_init,
> > },
> > @@ -250,7 +280,8 @@ static void buffer_queue(struct vb2_buffer *vb)
> >  static int video_i2c_thread_vid_cap(void *priv)
> >  {
> > struct video_i2c_data *data = priv;
> > -   unsigned int delay = msecs_to_jiffies(1000 / data->chip->max_fps);
> > +   unsigned int delay = mult_frac(HZ, data->frame_interval.numerator,
> > +  

[PATCH v4 2/6] media: video-i2c: use i2c regmap

2018-10-20 Thread Akinobu Mita
Use regmap for i2c register access.  This simplifies register accesses and
chooses suitable access commands based on the functionality that the
adapter supports.

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Acked-by: Matt Ranostay 
Acked-by: Sakari Ailus 
Signed-off-by: Akinobu Mita 
---
* v4
- No changes from v3

 drivers/media/i2c/video-i2c.c | 68 ++-
 1 file changed, 41 insertions(+), 27 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index f27d294..f23cb91 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -17,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -38,7 +39,7 @@ struct video_i2c_buffer {
 };
 
 struct video_i2c_data {
-   struct i2c_client *client;
+   struct regmap *regmap;
const struct video_i2c_chip *chip;
struct mutex lock;
spinlock_t slock;
@@ -62,6 +63,12 @@ static const struct v4l2_frmsize_discrete amg88xx_size = {
.height = 8,
 };
 
+static const struct regmap_config amg88xx_regmap_config = {
+   .reg_bits = 8,
+   .val_bits = 8,
+   .max_register = 0xff
+};
+
 struct video_i2c_chip {
/* video dimensions */
const struct v4l2_fmtdesc *format;
@@ -76,6 +83,8 @@ struct video_i2c_chip {
/* pixel size in bits */
unsigned int bpp;
 
+   const struct regmap_config *regmap_config;
+
/* xfer function */
int (*xfer)(struct video_i2c_data *data, char *buf);
 
@@ -83,26 +92,16 @@ struct video_i2c_chip {
int (*hwmon_init)(struct video_i2c_data *data);
 };
 
-static int amg88xx_xfer(struct video_i2c_data *data, char *buf)
-{
-   struct i2c_client *client = data->client;
-   struct i2c_msg msg[2];
-   u8 reg = 0x80;
-   int ret;
-
-   msg[0].addr = client->addr;
-   msg[0].flags = 0;
-   msg[0].len = 1;
-   msg[0].buf  = (char *)
+/* Thermistor register */
+#define AMG88XX_REG_TTHL   0x0e
 
-   msg[1].addr = client->addr;
-   msg[1].flags = I2C_M_RD;
-   msg[1].len = data->chip->buffer_size;
-   msg[1].buf = (char *)buf;
+/* Temperature register */
+#define AMG88XX_REG_T01L   0x80
 
-   ret = i2c_transfer(client->adapter, msg, 2);
-
-   return (ret == 2) ? 0 : -EIO;
+static int amg88xx_xfer(struct video_i2c_data *data, char *buf)
+{
+   return regmap_bulk_read(data->regmap, AMG88XX_REG_T01L, buf,
+   data->chip->buffer_size);
 }
 
 #if IS_ENABLED(CONFIG_HWMON)
@@ -133,12 +132,15 @@ static int amg88xx_read(struct device *dev, enum 
hwmon_sensor_types type,
u32 attr, int channel, long *val)
 {
struct video_i2c_data *data = dev_get_drvdata(dev);
-   struct i2c_client *client = data->client;
-   int tmp = i2c_smbus_read_word_data(client, 0x0e);
+   __le16 buf;
+   int tmp;
 
-   if (tmp < 0)
+   tmp = regmap_bulk_read(data->regmap, AMG88XX_REG_TTHL, , 2);
+   if (tmp)
return tmp;
 
+   tmp = le16_to_cpu(buf);
+
/*
 * Check for sign bit, this isn't a two's complement value but an
 * absolute temperature that needs to be inverted in the case of being
@@ -164,8 +166,9 @@ static const struct hwmon_chip_info amg88xx_chip_info = {
 
 static int amg88xx_hwmon_init(struct video_i2c_data *data)
 {
-   void *hwmon = devm_hwmon_device_register_with_info(>client->dev,
-   "amg88xx", data, _chip_info, NULL);
+   struct device *dev = regmap_get_device(data->regmap);
+   void *hwmon = devm_hwmon_device_register_with_info(dev, "amg88xx", data,
+   _chip_info, NULL);
 
return PTR_ERR_OR_ZERO(hwmon);
 }
@@ -182,6 +185,7 @@ static const struct video_i2c_chip video_i2c_chip[] = {
.max_fps= 10,
.buffer_size= 128,
.bpp= 16,
+   .regmap_config  = _regmap_config,
.xfer   = _xfer,
.hwmon_init = amg88xx_hwmon_init,
},
@@ -350,7 +354,8 @@ static int video_i2c_querycap(struct file *file, void  
*priv,
struct v4l2_capability *vcap)
 {
struct video_i2c_data *data = video_drvdata(file);
-   struct i2c_client *client = data->client;
+   struct device *dev = regmap_get_device(data->regmap);
+   struct i2c_client *client = to_i2c_client(dev);
 
strlcpy(vcap->driver, data->v4l2_dev.name, sizeof(vcap->driver));
strlcpy(vcap->card, data->vdev.name, sizeof(vcap->card));
@@ -515,6 +520,7 @@ static void video_i2c_release(struct video_device *vdev)
v4l2_device_unregister(>v4l2_dev);
mutex_destroy(>lock);
mutex_destroy(>queue_lo

[PATCH v4 6/6] media: video-i2c: support runtime PM

2018-10-20 Thread Akinobu Mita
AMG88xx has a register for setting operating mode.  This adds support
runtime PM by changing the operating mode.

The instruction for changing sleep mode to normal mode is from the
reference specifications.

https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Reviewed-by: Matt Ranostay 
Acked-by: Sakari Ailus 
Signed-off-by: Akinobu Mita 
---
* v4
- Move set_power() call into release() callback

 drivers/media/i2c/video-i2c.c | 141 +-
 1 file changed, 139 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index 3334fc2..384a046 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -17,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -94,10 +95,23 @@ struct video_i2c_chip {
/* xfer function */
int (*xfer)(struct video_i2c_data *data, char *buf);
 
+   /* power control function */
+   int (*set_power)(struct video_i2c_data *data, bool on);
+
/* hwmon init function */
int (*hwmon_init)(struct video_i2c_data *data);
 };
 
+/* Power control register */
+#define AMG88XX_REG_PCTL   0x00
+#define AMG88XX_PCTL_NORMAL0x00
+#define AMG88XX_PCTL_SLEEP 0x10
+
+/* Reset register */
+#define AMG88XX_REG_RST0x01
+#define AMG88XX_RST_FLAG   0x30
+#define AMG88XX_RST_INIT   0x3f
+
 /* Frame rate register */
 #define AMG88XX_REG_FPSC   0x02
 #define AMG88XX_FPSC_1FPS  BIT(0)
@@ -127,6 +141,59 @@ static int amg88xx_setup(struct video_i2c_data *data)
return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val);
 }
 
+static int amg88xx_set_power_on(struct video_i2c_data *data)
+{
+   int ret;
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, AMG88XX_PCTL_NORMAL);
+   if (ret)
+   return ret;
+
+   msleep(50);
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_INIT);
+   if (ret)
+   return ret;
+
+   msleep(2);
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_FLAG);
+   if (ret)
+   return ret;
+
+   /*
+* Wait two frames before reading thermistor and temperature registers
+*/
+   msleep(200);
+
+   return 0;
+}
+
+static int amg88xx_set_power_off(struct video_i2c_data *data)
+{
+   int ret;
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, AMG88XX_PCTL_SLEEP);
+   if (ret)
+   return ret;
+   /*
+* Wait for a while to avoid resuming normal mode immediately after
+* entering sleep mode, otherwise the device occasionally goes wrong
+* (thermistor and temperature registers are not updated at all)
+*/
+   msleep(100);
+
+   return 0;
+}
+
+static int amg88xx_set_power(struct video_i2c_data *data, bool on)
+{
+   if (on)
+   return amg88xx_set_power_on(data);
+
+   return amg88xx_set_power_off(data);
+}
+
 #if IS_ENABLED(CONFIG_HWMON)
 
 static const u32 amg88xx_temp_config[] = {
@@ -158,7 +225,15 @@ static int amg88xx_read(struct device *dev, enum 
hwmon_sensor_types type,
__le16 buf;
int tmp;
 
+   tmp = pm_runtime_get_sync(regmap_get_device(data->regmap));
+   if (tmp < 0) {
+   pm_runtime_put_noidle(regmap_get_device(data->regmap));
+   return tmp;
+   }
+
tmp = regmap_bulk_read(data->regmap, AMG88XX_REG_TTHL, , 2);
+   pm_runtime_mark_last_busy(regmap_get_device(data->regmap));
+   pm_runtime_put_autosuspend(regmap_get_device(data->regmap));
if (tmp)
return tmp;
 
@@ -217,6 +292,7 @@ static const struct video_i2c_chip video_i2c_chip[] = {
.regmap_config  = _regmap_config,
.setup  = _setup,
.xfer   = _xfer,
+   .set_power  = amg88xx_set_power,
.hwmon_init = amg88xx_hwmon_init,
},
 };
@@ -343,14 +419,21 @@ static void video_i2c_del_list(struct vb2_queue *vq, enum 
vb2_buffer_state state
 static int start_streaming(struct vb2_queue *vq, unsigned int count)
 {
struct video_i2c_data *data = vb2_get_drv_priv(vq);
+   struct device *dev = regmap_get_device(data->regmap);
int ret;
 
if (data->kthread_vid_cap)
return 0;
 
+   ret = pm_runtime_get_sync(dev);
+   if (ret < 0) {
+   pm_runtime_put_noidle(dev);
+   goto error_del_list;
+   }
+
ret = data->chip->setup(data);
if (ret)
-   goto error_del_list;
+   goto error_rpm_put;
 
data->sequence = 0;
data->kthread_vid_cap = kthread_run(video_i2

[PATCH v4 4/6] media: vivid: use V4L2_FRACT_COMPARE

2018-10-20 Thread Akinobu Mita
Now the equivalent of FRACT_CMP() is added in v4l2 common internal API
header.

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Acked-by: Sakari Ailus 
Signed-off-by: Akinobu Mita 
---
* v4
- No changes from v3

 drivers/media/platform/vivid/vivid-vid-cap.c | 9 +++--
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c 
b/drivers/media/platform/vivid/vivid-vid-cap.c
index 1599159..f19c701 100644
--- a/drivers/media/platform/vivid/vivid-vid-cap.c
+++ b/drivers/media/platform/vivid/vivid-vid-cap.c
@@ -1824,9 +1824,6 @@ int vivid_vid_cap_g_parm(struct file *file, void *priv,
return 0;
 }
 
-#define FRACT_CMP(a, OP, b)\
-   ((u64)(a).numerator * (b).denominator  OP  (u64)(b).numerator * 
(a).denominator)
-
 int vivid_vid_cap_s_parm(struct file *file, void *priv,
  struct v4l2_streamparm *parm)
 {
@@ -1847,14 +1844,14 @@ int vivid_vid_cap_s_parm(struct file *file, void *priv,
if (tpf.denominator == 0)
tpf = webcam_intervals[ival_sz - 1];
for (i = 0; i < ival_sz; i++)
-   if (FRACT_CMP(tpf, >=, webcam_intervals[i]))
+   if (V4L2_FRACT_COMPARE(tpf, >=, webcam_intervals[i]))
break;
if (i == ival_sz)
i = ival_sz - 1;
dev->webcam_ival_idx = i;
tpf = webcam_intervals[dev->webcam_ival_idx];
-   tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf;
-   tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf;
+   tpf = V4L2_FRACT_COMPARE(tpf, <, tpf_min) ? tpf_min : tpf;
+   tpf = V4L2_FRACT_COMPARE(tpf, >, tpf_max) ? tpf_max : tpf;
 
/* resync the thread's timings */
dev->cap_seq_resync = true;
-- 
2.7.4



[PATCH v4 5/6] media: video-i2c: support changing frame interval

2018-10-20 Thread Akinobu Mita
AMG88xx has a register for setting frame rate 1 or 10 FPS.
This adds support changing frame interval.

Reference specifications:
https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Acked-by: Matt Ranostay 
Acked-by: Sakari Ailus 
Signed-off-by: Akinobu Mita 
---
* v4
- No changes from v3

 drivers/media/i2c/video-i2c.c | 78 ---
 1 file changed, 66 insertions(+), 12 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index f23cb91..3334fc2 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -52,6 +52,8 @@ struct video_i2c_data {
 
struct task_struct *kthread_vid_cap;
struct list_head vid_cap_active;
+
+   struct v4l2_fract frame_interval;
 };
 
 static const struct v4l2_fmtdesc amg88xx_format = {
@@ -74,8 +76,9 @@ struct video_i2c_chip {
const struct v4l2_fmtdesc *format;
const struct v4l2_frmsize_discrete *size;
 
-   /* max frames per second */
-   unsigned int max_fps;
+   /* available frame intervals */
+   const struct v4l2_fract *frame_intervals;
+   unsigned int num_frame_intervals;
 
/* pixel buffer size */
unsigned int buffer_size;
@@ -85,6 +88,9 @@ struct video_i2c_chip {
 
const struct regmap_config *regmap_config;
 
+   /* setup function */
+   int (*setup)(struct video_i2c_data *data);
+
/* xfer function */
int (*xfer)(struct video_i2c_data *data, char *buf);
 
@@ -92,6 +98,10 @@ struct video_i2c_chip {
int (*hwmon_init)(struct video_i2c_data *data);
 };
 
+/* Frame rate register */
+#define AMG88XX_REG_FPSC   0x02
+#define AMG88XX_FPSC_1FPS  BIT(0)
+
 /* Thermistor register */
 #define AMG88XX_REG_TTHL   0x0e
 
@@ -104,6 +114,19 @@ static int amg88xx_xfer(struct video_i2c_data *data, char 
*buf)
data->chip->buffer_size);
 }
 
+static int amg88xx_setup(struct video_i2c_data *data)
+{
+   unsigned int mask = AMG88XX_FPSC_1FPS;
+   unsigned int val;
+
+   if (data->frame_interval.numerator == data->frame_interval.denominator)
+   val = mask;
+   else
+   val = 0;
+
+   return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val);
+}
+
 #if IS_ENABLED(CONFIG_HWMON)
 
 static const u32 amg88xx_temp_config[] = {
@@ -178,14 +201,21 @@ static int amg88xx_hwmon_init(struct video_i2c_data *data)
 
 #define AMG88XX0
 
+static const struct v4l2_fract amg88xx_frame_intervals[] = {
+   { 1, 10 },
+   { 1, 1 },
+};
+
 static const struct video_i2c_chip video_i2c_chip[] = {
[AMG88XX] = {
.size   = _size,
.format = _format,
-   .max_fps= 10,
+   .frame_intervals= amg88xx_frame_intervals,
+   .num_frame_intervals= ARRAY_SIZE(amg88xx_frame_intervals),
.buffer_size= 128,
.bpp= 16,
.regmap_config  = _regmap_config,
+   .setup  = _setup,
.xfer   = _xfer,
.hwmon_init = amg88xx_hwmon_init,
},
@@ -250,7 +280,8 @@ static void buffer_queue(struct vb2_buffer *vb)
 static int video_i2c_thread_vid_cap(void *priv)
 {
struct video_i2c_data *data = priv;
-   unsigned int delay = msecs_to_jiffies(1000 / data->chip->max_fps);
+   unsigned int delay = mult_frac(HZ, data->frame_interval.numerator,
+  data->frame_interval.denominator);
 
set_freezable();
 
@@ -312,19 +343,26 @@ static void video_i2c_del_list(struct vb2_queue *vq, enum 
vb2_buffer_state state
 static int start_streaming(struct vb2_queue *vq, unsigned int count)
 {
struct video_i2c_data *data = vb2_get_drv_priv(vq);
+   int ret;
 
if (data->kthread_vid_cap)
return 0;
 
+   ret = data->chip->setup(data);
+   if (ret)
+   goto error_del_list;
+
data->sequence = 0;
data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data,
"%s-vid-cap", data->v4l2_dev.name);
-   if (!IS_ERR(data->kthread_vid_cap))
+   ret = PTR_ERR_OR_ZERO(data->kthread_vid_cap);
+   if (!ret)
return 0;
 
+error_del_list:
video_i2c_del_list(vq, VB2_BUF_STATE_QUEUED);
 
-   return PTR_ERR(data->kthread_vid_cap);
+   return ret;
 }
 
 static void stop_streaming(struct vb2_queue *vq)
@@ -431,15 +469,14 @@ static int video_i2c_enum_frameintervals(struct file 
*file, void *priv,
const struct video_i2c_data *data = video_drvdata(file);
const struct v4l2_frmsize_discrete *size = data->chip->

[PATCH v4 3/6] media: v4l2-common: add V4L2_FRACT_COMPARE

2018-10-20 Thread Akinobu Mita
Add macro to compare two v4l2_fract values in v4l2 common internal API.
The same macro FRACT_CMP() is used by vivid and bcm2835-camera.  This just
renames it to V4L2_FRACT_COMPARE in order to avoid namespace collision.

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Acked-by: Sakari Ailus 
Signed-off-by: Akinobu Mita 
---
* v4
- No changes from v3

 include/media/v4l2-common.h | 5 +
 1 file changed, 5 insertions(+)

diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index cdc87ec..eafb8a3 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -384,4 +384,9 @@ int v4l2_g_parm_cap(struct video_device *vdev,
 int v4l2_s_parm_cap(struct video_device *vdev,
struct v4l2_subdev *sd, struct v4l2_streamparm *a);
 
+/* Compare two v4l2_fract structs */
+#define V4L2_FRACT_COMPARE(a, OP, b)   \
+   ((u64)(a).numerator * (b).denominator OP\
+   (u64)(b).numerator * (a).denominator)
+
 #endif /* V4L2_COMMON_H_ */
-- 
2.7.4



[PATCH v4 1/6] media: video-i2c: avoid accessing released memory area when removing driver

2018-10-20 Thread Akinobu Mita
The video device release() callback for video-i2c driver frees the whole
struct video_i2c_data.  If there is no user left for the video device
when video_unregister_device() is called, the release callback is executed.

However, in video_i2c_remove() some fields (v4l2_dev, lock, and queue_lock)
in struct video_i2c_data are still accessed after video_unregister_device()
is called.

This fixes the use after free by moving the code from video_i2c_remove()
to the release() callback.

Fixes: 5cebaac60974 ("media: video-i2c: add video-i2c driver")
Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Reviewed-by: Matt Ranostay 
Signed-off-by: Akinobu Mita 
---
* v4
- Add Reviewed-by line

 drivers/media/i2c/video-i2c.c | 11 ++-
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index 06d29d8..f27d294 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -510,7 +510,12 @@ static const struct v4l2_ioctl_ops video_i2c_ioctl_ops = {
 
 static void video_i2c_release(struct video_device *vdev)
 {
-   kfree(video_get_drvdata(vdev));
+   struct video_i2c_data *data = video_get_drvdata(vdev);
+
+   v4l2_device_unregister(>v4l2_dev);
+   mutex_destroy(>lock);
+   mutex_destroy(>queue_lock);
+   kfree(data);
 }
 
 static int video_i2c_probe(struct i2c_client *client,
@@ -608,10 +613,6 @@ static int video_i2c_remove(struct i2c_client *client)
struct video_i2c_data *data = i2c_get_clientdata(client);
 
video_unregister_device(>vdev);
-   v4l2_device_unregister(>v4l2_dev);
-
-   mutex_destroy(>lock);
-   mutex_destroy(>queue_lock);
 
return 0;
 }
-- 
2.7.4



[PATCH v4 0/6] media: video-i2c: support changing frame interval and runtime PM

2018-10-20 Thread Akinobu Mita
This patchset adds support for changing frame interval and runtime PM for
video-i2c driver.  This also adds an helper macro to v4l2 common
internal API that is used to to find a suitable frame interval.

There are a couple of unrelated changes that are included for simplifying
driver initialization code and register accesses.

* v4
- Add Reviewed-by line
- Move set_power() call into release() callback

* v3
- Append result of v4l2-compliance in cover-letter
- Move the code causing use-after-free from video_i2c_remove() to the
  video device release() callback.
- Use regmap_init_i2c() instead of devm_regmap_init_i2c() and call
  regmap_exit_i2c() in video device release() callback in order to
  avoid releasing regmap when the driver is unbound.
- Add Acked-by lines

* v2
- Add Acked-by and Reviewed-by tags
- Update commit log to clarify the use after free
- Add thermistor and termperature register address definisions
- Stop adding v4l2_find_closest_fract() in v4l2 common internal API
- Add V4L2_FRACT_COMPARE() macro in v4l2 common internal API
- Use V4L2_FRACT_COMPARE() to find suitable frame interval instead of
  v4l2_find_closest_fract()
- Add comment for register address definisions

Akinobu Mita (6):
  media: video-i2c: avoid accessing released memory area when removing
driver
  media: video-i2c: use i2c regmap
  media: v4l2-common: add V4L2_FRACT_COMPARE
  media: vivid: use V4L2_FRACT_COMPARE
  media: video-i2c: support changing frame interval
  media: video-i2c: support runtime PM

 drivers/media/i2c/video-i2c.c| 286 +++
 drivers/media/platform/vivid/vivid-vid-cap.c |   9 +-
 include/media/v4l2-common.h  |   5 +
 3 files changed, 254 insertions(+), 46 deletions(-)

v4l2-compliance SHA: c36dbbdfa8b30b2badd4f893b59d0bd4f0bd12aa, 32 bits

Compliance test for device /dev/video2:

Driver Info:
Driver name  : video-i2c
Card type: I2C 1-104 Transport Video
Bus info : I2C:1-104
Driver version   : 4.19.0
Capabilities : 0x8521
Video Capture
Read/Write
Streaming
Extended Pix Format
Device Capabilities
Device Caps  : 0x0521
Video Capture
Read/Write
Streaming
Extended Pix Format

Required ioctls:
test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
test second /dev/video2 open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK

Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK
test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 1 Audio Inputs: 0 Tuners: 0

Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
test VIDIOC_G/S_EDID: OK (Not Supported)

Control ioctls (Input 0):
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
test VIDIOC_QUERYCTRL: OK (Not Supported)
test VIDIOC_G/S_CTRL: OK (Not Supported)
test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 0 Private Controls: 0

Format ioctls (Input 0):
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
test VIDIOC_TRY_FMT: OK
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)

Codec ioctls (Input 0):
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

Buffer ioctls (Input 0):
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
test VIDIOC_EXPBUF: OK (Not Supported)

Total: 43, Succeeded: 43, Failed: 0, Warnings: 0

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro

Re: [PATCH v3 6/6] media: video-i2c: support runtime PM

2018-10-17 Thread Akinobu Mita
2018年10月17日(水) 16:19 Sakari Ailus :
>
> On Wed, Oct 17, 2018 at 12:07:50AM +0900, Akinobu Mita wrote:
> > 2018年10月16日(火) 0:31 Sakari Ailus :
> > >
> > > Hi Mita-san,
> > >
> > > On Sun, Oct 14, 2018 at 03:02:39AM +0900, Akinobu Mita wrote:
> > > > AMG88xx has a register for setting operating mode.  This adds support
> > > > runtime PM by changing the operating mode.
> > > >
> > > > The instruction for changing sleep mode to normal mode is from the
> > > > reference specifications.
> > > >
> > > > https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf
> > > >
> > > > Cc: Matt Ranostay 
> > > > Cc: Sakari Ailus 
> > > > Cc: Hans Verkuil 
> > > > Cc: Mauro Carvalho Chehab 
> > > > Reviewed-by: Matt Ranostay 
> > > > Acked-by: Sakari Ailus 
> > > > Signed-off-by: Akinobu Mita 
> > > > ---
> > > > * v3
> > > > - Move chip->set_power() call to the video device release() callback.
> > > > - Add Acked-by line
> > > >
> > > >  drivers/media/i2c/video-i2c.c | 141 
> > > > +-
> > > >  1 file changed, 139 insertions(+), 2 deletions(-)
> > > >
> > > > diff --git a/drivers/media/i2c/video-i2c.c 
> > > > b/drivers/media/i2c/video-i2c.c
> > > > index 3334fc2..22fdc43 100644
> > > > --- a/drivers/media/i2c/video-i2c.c
> > > > +++ b/drivers/media/i2c/video-i2c.c
> > > > @@ -17,6 +17,7 @@
> > > >  #include 
> > > >  #include 
> > > >  #include 
> > > > +#include 
> > > >  #include 
> > > >  #include 
> > > >  #include 
> > > > @@ -94,10 +95,23 @@ struct video_i2c_chip {
> > > >   /* xfer function */
> > > >   int (*xfer)(struct video_i2c_data *data, char *buf);
> > > >
> > > > + /* power control function */
> > > > + int (*set_power)(struct video_i2c_data *data, bool on);
> > > > +
> > > >   /* hwmon init function */
> > > >   int (*hwmon_init)(struct video_i2c_data *data);
> > > >  };
> > > >
> > > > +/* Power control register */
> > > > +#define AMG88XX_REG_PCTL 0x00
> > > > +#define AMG88XX_PCTL_NORMAL  0x00
> > > > +#define AMG88XX_PCTL_SLEEP   0x10
> > > > +
> > > > +/* Reset register */
> > > > +#define AMG88XX_REG_RST  0x01
> > > > +#define AMG88XX_RST_FLAG 0x30
> > > > +#define AMG88XX_RST_INIT 0x3f
> > > > +
> > > >  /* Frame rate register */
> > > >  #define AMG88XX_REG_FPSC 0x02
> > > >  #define AMG88XX_FPSC_1FPSBIT(0)
> > > > @@ -127,6 +141,59 @@ static int amg88xx_setup(struct video_i2c_data 
> > > > *data)
> > > >   return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, 
> > > > val);
> > > >  }
> > > >
> > > > +static int amg88xx_set_power_on(struct video_i2c_data *data)
> > > > +{
> > > > + int ret;
> > > > +
> > > > + ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, 
> > > > AMG88XX_PCTL_NORMAL);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + msleep(50);
> > > > +
> > > > + ret = regmap_write(data->regmap, AMG88XX_REG_RST, 
> > > > AMG88XX_RST_INIT);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + msleep(2);
> > > > +
> > > > + ret = regmap_write(data->regmap, AMG88XX_REG_RST, 
> > > > AMG88XX_RST_FLAG);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + /*
> > > > +  * Wait two frames before reading thermistor and temperature 
> > > > registers
> > > > +  */
> > > > + msleep(200);
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int amg88xx_set_power_off(struct video_i2c_data *data)
> > > > +{
> > > > + int ret;
> > > > +
> > > > + ret = regmap_write(data->regma

Re: [PATCH v3 6/6] media: video-i2c: support runtime PM

2018-10-16 Thread Akinobu Mita
2018年10月16日(火) 0:31 Sakari Ailus :
>
> Hi Mita-san,
>
> On Sun, Oct 14, 2018 at 03:02:39AM +0900, Akinobu Mita wrote:
> > AMG88xx has a register for setting operating mode.  This adds support
> > runtime PM by changing the operating mode.
> >
> > The instruction for changing sleep mode to normal mode is from the
> > reference specifications.
> >
> > https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf
> >
> > Cc: Matt Ranostay 
> > Cc: Sakari Ailus 
> > Cc: Hans Verkuil 
> > Cc: Mauro Carvalho Chehab 
> > Reviewed-by: Matt Ranostay 
> > Acked-by: Sakari Ailus 
> > Signed-off-by: Akinobu Mita 
> > ---
> > * v3
> > - Move chip->set_power() call to the video device release() callback.
> > - Add Acked-by line
> >
> >  drivers/media/i2c/video-i2c.c | 141 
> > +-
> >  1 file changed, 139 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
> > index 3334fc2..22fdc43 100644
> > --- a/drivers/media/i2c/video-i2c.c
> > +++ b/drivers/media/i2c/video-i2c.c
> > @@ -17,6 +17,7 @@
> >  #include 
> >  #include 
> >  #include 
> > +#include 
> >  #include 
> >  #include 
> >  #include 
> > @@ -94,10 +95,23 @@ struct video_i2c_chip {
> >   /* xfer function */
> >   int (*xfer)(struct video_i2c_data *data, char *buf);
> >
> > + /* power control function */
> > + int (*set_power)(struct video_i2c_data *data, bool on);
> > +
> >   /* hwmon init function */
> >   int (*hwmon_init)(struct video_i2c_data *data);
> >  };
> >
> > +/* Power control register */
> > +#define AMG88XX_REG_PCTL 0x00
> > +#define AMG88XX_PCTL_NORMAL  0x00
> > +#define AMG88XX_PCTL_SLEEP   0x10
> > +
> > +/* Reset register */
> > +#define AMG88XX_REG_RST  0x01
> > +#define AMG88XX_RST_FLAG 0x30
> > +#define AMG88XX_RST_INIT 0x3f
> > +
> >  /* Frame rate register */
> >  #define AMG88XX_REG_FPSC 0x02
> >  #define AMG88XX_FPSC_1FPSBIT(0)
> > @@ -127,6 +141,59 @@ static int amg88xx_setup(struct video_i2c_data *data)
> >   return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val);
> >  }
> >
> > +static int amg88xx_set_power_on(struct video_i2c_data *data)
> > +{
> > + int ret;
> > +
> > + ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, 
> > AMG88XX_PCTL_NORMAL);
> > + if (ret)
> > + return ret;
> > +
> > + msleep(50);
> > +
> > + ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_INIT);
> > + if (ret)
> > + return ret;
> > +
> > + msleep(2);
> > +
> > + ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_FLAG);
> > + if (ret)
> > + return ret;
> > +
> > + /*
> > +  * Wait two frames before reading thermistor and temperature registers
> > +  */
> > + msleep(200);
> > +
> > + return 0;
> > +}
> > +
> > +static int amg88xx_set_power_off(struct video_i2c_data *data)
> > +{
> > + int ret;
> > +
> > + ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, 
> > AMG88XX_PCTL_SLEEP);
> > + if (ret)
> > + return ret;
> > + /*
> > +  * Wait for a while to avoid resuming normal mode immediately after
> > +  * entering sleep mode, otherwise the device occasionally goes wrong
> > +  * (thermistor and temperature registers are not updated at all)
> > +  */
> > + msleep(100);
> > +
> > + return 0;
> > +}
> > +
> > +static int amg88xx_set_power(struct video_i2c_data *data, bool on)
> > +{
> > + if (on)
> > + return amg88xx_set_power_on(data);
> > +
> > + return amg88xx_set_power_off(data);
> > +}
> > +
> >  #if IS_ENABLED(CONFIG_HWMON)
> >
> >  static const u32 amg88xx_temp_config[] = {
> > @@ -158,7 +225,15 @@ static int amg88xx_read(struct device *dev, enum 
> > hwmon_sensor_types type,
> >   __le16 buf;
> >   int tmp;
> >
> > + tmp = pm_runtime_get_sync(regmap_get_device(data->regmap));
> > + if (tmp < 0) {
> > + pm_runtime_put_noidle(regmap_get_device(data->regmap));
> > + return tmp;
> > + }

[PATCH v3 5/6] media: video-i2c: support changing frame interval

2018-10-13 Thread Akinobu Mita
AMG88xx has a register for setting frame rate 1 or 10 FPS.
This adds support changing frame interval.

Reference specifications:
https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Acked-by: Matt Ranostay 
Acked-by: Sakari Ailus 
Signed-off-by: Akinobu Mita 
---
* v3
- Add Acked-by line

 drivers/media/i2c/video-i2c.c | 78 ---
 1 file changed, 66 insertions(+), 12 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index f23cb91..3334fc2 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -52,6 +52,8 @@ struct video_i2c_data {
 
struct task_struct *kthread_vid_cap;
struct list_head vid_cap_active;
+
+   struct v4l2_fract frame_interval;
 };
 
 static const struct v4l2_fmtdesc amg88xx_format = {
@@ -74,8 +76,9 @@ struct video_i2c_chip {
const struct v4l2_fmtdesc *format;
const struct v4l2_frmsize_discrete *size;
 
-   /* max frames per second */
-   unsigned int max_fps;
+   /* available frame intervals */
+   const struct v4l2_fract *frame_intervals;
+   unsigned int num_frame_intervals;
 
/* pixel buffer size */
unsigned int buffer_size;
@@ -85,6 +88,9 @@ struct video_i2c_chip {
 
const struct regmap_config *regmap_config;
 
+   /* setup function */
+   int (*setup)(struct video_i2c_data *data);
+
/* xfer function */
int (*xfer)(struct video_i2c_data *data, char *buf);
 
@@ -92,6 +98,10 @@ struct video_i2c_chip {
int (*hwmon_init)(struct video_i2c_data *data);
 };
 
+/* Frame rate register */
+#define AMG88XX_REG_FPSC   0x02
+#define AMG88XX_FPSC_1FPS  BIT(0)
+
 /* Thermistor register */
 #define AMG88XX_REG_TTHL   0x0e
 
@@ -104,6 +114,19 @@ static int amg88xx_xfer(struct video_i2c_data *data, char 
*buf)
data->chip->buffer_size);
 }
 
+static int amg88xx_setup(struct video_i2c_data *data)
+{
+   unsigned int mask = AMG88XX_FPSC_1FPS;
+   unsigned int val;
+
+   if (data->frame_interval.numerator == data->frame_interval.denominator)
+   val = mask;
+   else
+   val = 0;
+
+   return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val);
+}
+
 #if IS_ENABLED(CONFIG_HWMON)
 
 static const u32 amg88xx_temp_config[] = {
@@ -178,14 +201,21 @@ static int amg88xx_hwmon_init(struct video_i2c_data *data)
 
 #define AMG88XX0
 
+static const struct v4l2_fract amg88xx_frame_intervals[] = {
+   { 1, 10 },
+   { 1, 1 },
+};
+
 static const struct video_i2c_chip video_i2c_chip[] = {
[AMG88XX] = {
.size   = _size,
.format = _format,
-   .max_fps= 10,
+   .frame_intervals= amg88xx_frame_intervals,
+   .num_frame_intervals= ARRAY_SIZE(amg88xx_frame_intervals),
.buffer_size= 128,
.bpp= 16,
.regmap_config  = _regmap_config,
+   .setup  = _setup,
.xfer   = _xfer,
.hwmon_init = amg88xx_hwmon_init,
},
@@ -250,7 +280,8 @@ static void buffer_queue(struct vb2_buffer *vb)
 static int video_i2c_thread_vid_cap(void *priv)
 {
struct video_i2c_data *data = priv;
-   unsigned int delay = msecs_to_jiffies(1000 / data->chip->max_fps);
+   unsigned int delay = mult_frac(HZ, data->frame_interval.numerator,
+  data->frame_interval.denominator);
 
set_freezable();
 
@@ -312,19 +343,26 @@ static void video_i2c_del_list(struct vb2_queue *vq, enum 
vb2_buffer_state state
 static int start_streaming(struct vb2_queue *vq, unsigned int count)
 {
struct video_i2c_data *data = vb2_get_drv_priv(vq);
+   int ret;
 
if (data->kthread_vid_cap)
return 0;
 
+   ret = data->chip->setup(data);
+   if (ret)
+   goto error_del_list;
+
data->sequence = 0;
data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data,
"%s-vid-cap", data->v4l2_dev.name);
-   if (!IS_ERR(data->kthread_vid_cap))
+   ret = PTR_ERR_OR_ZERO(data->kthread_vid_cap);
+   if (!ret)
return 0;
 
+error_del_list:
video_i2c_del_list(vq, VB2_BUF_STATE_QUEUED);
 
-   return PTR_ERR(data->kthread_vid_cap);
+   return ret;
 }
 
 static void stop_streaming(struct vb2_queue *vq)
@@ -431,15 +469,14 @@ static int video_i2c_enum_frameintervals(struct file 
*file, void *priv,
const struct video_i2c_data *data = video_drvdata(file);
const struct v4l2_frmsize_discrete *size = data->chip->

[PATCH v3 2/6] media: video-i2c: use i2c regmap

2018-10-13 Thread Akinobu Mita
Use regmap for i2c register access.  This simplifies register accesses and
chooses suitable access commands based on the functionality that the
adapter supports.

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Acked-by: Matt Ranostay 
Acked-by: Sakari Ailus 
Signed-off-by: Akinobu Mita 
---
* v3
- Use regmap_init_i2c() instead of devm_regmap_init_i2c() and call
  regmap_exit_i2c() in video device release() callback in order to
  avoid releasing regmap when the driver is unbound.
- Add Acked-by lines

 drivers/media/i2c/video-i2c.c | 68 ++-
 1 file changed, 41 insertions(+), 27 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index f27d294..f23cb91 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -17,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -38,7 +39,7 @@ struct video_i2c_buffer {
 };
 
 struct video_i2c_data {
-   struct i2c_client *client;
+   struct regmap *regmap;
const struct video_i2c_chip *chip;
struct mutex lock;
spinlock_t slock;
@@ -62,6 +63,12 @@ static const struct v4l2_frmsize_discrete amg88xx_size = {
.height = 8,
 };
 
+static const struct regmap_config amg88xx_regmap_config = {
+   .reg_bits = 8,
+   .val_bits = 8,
+   .max_register = 0xff
+};
+
 struct video_i2c_chip {
/* video dimensions */
const struct v4l2_fmtdesc *format;
@@ -76,6 +83,8 @@ struct video_i2c_chip {
/* pixel size in bits */
unsigned int bpp;
 
+   const struct regmap_config *regmap_config;
+
/* xfer function */
int (*xfer)(struct video_i2c_data *data, char *buf);
 
@@ -83,26 +92,16 @@ struct video_i2c_chip {
int (*hwmon_init)(struct video_i2c_data *data);
 };
 
-static int amg88xx_xfer(struct video_i2c_data *data, char *buf)
-{
-   struct i2c_client *client = data->client;
-   struct i2c_msg msg[2];
-   u8 reg = 0x80;
-   int ret;
-
-   msg[0].addr = client->addr;
-   msg[0].flags = 0;
-   msg[0].len = 1;
-   msg[0].buf  = (char *)
+/* Thermistor register */
+#define AMG88XX_REG_TTHL   0x0e
 
-   msg[1].addr = client->addr;
-   msg[1].flags = I2C_M_RD;
-   msg[1].len = data->chip->buffer_size;
-   msg[1].buf = (char *)buf;
+/* Temperature register */
+#define AMG88XX_REG_T01L   0x80
 
-   ret = i2c_transfer(client->adapter, msg, 2);
-
-   return (ret == 2) ? 0 : -EIO;
+static int amg88xx_xfer(struct video_i2c_data *data, char *buf)
+{
+   return regmap_bulk_read(data->regmap, AMG88XX_REG_T01L, buf,
+   data->chip->buffer_size);
 }
 
 #if IS_ENABLED(CONFIG_HWMON)
@@ -133,12 +132,15 @@ static int amg88xx_read(struct device *dev, enum 
hwmon_sensor_types type,
u32 attr, int channel, long *val)
 {
struct video_i2c_data *data = dev_get_drvdata(dev);
-   struct i2c_client *client = data->client;
-   int tmp = i2c_smbus_read_word_data(client, 0x0e);
+   __le16 buf;
+   int tmp;
 
-   if (tmp < 0)
+   tmp = regmap_bulk_read(data->regmap, AMG88XX_REG_TTHL, , 2);
+   if (tmp)
return tmp;
 
+   tmp = le16_to_cpu(buf);
+
/*
 * Check for sign bit, this isn't a two's complement value but an
 * absolute temperature that needs to be inverted in the case of being
@@ -164,8 +166,9 @@ static const struct hwmon_chip_info amg88xx_chip_info = {
 
 static int amg88xx_hwmon_init(struct video_i2c_data *data)
 {
-   void *hwmon = devm_hwmon_device_register_with_info(>client->dev,
-   "amg88xx", data, _chip_info, NULL);
+   struct device *dev = regmap_get_device(data->regmap);
+   void *hwmon = devm_hwmon_device_register_with_info(dev, "amg88xx", data,
+   _chip_info, NULL);
 
return PTR_ERR_OR_ZERO(hwmon);
 }
@@ -182,6 +185,7 @@ static const struct video_i2c_chip video_i2c_chip[] = {
.max_fps= 10,
.buffer_size= 128,
.bpp= 16,
+   .regmap_config  = _regmap_config,
.xfer   = _xfer,
.hwmon_init = amg88xx_hwmon_init,
},
@@ -350,7 +354,8 @@ static int video_i2c_querycap(struct file *file, void  
*priv,
struct v4l2_capability *vcap)
 {
struct video_i2c_data *data = video_drvdata(file);
-   struct i2c_client *client = data->client;
+   struct device *dev = regmap_get_device(data->regmap);
+   struct i2c_client *client = to_i2c_client(dev);
 
strlcpy(vcap->driver, data->v4l2_dev.name, sizeof(vcap->driver));
strlcpy(vcap->card, data->vdev.name, sizeof(vca

[PATCH v3 1/6] media: video-i2c: avoid accessing released memory area when removing driver

2018-10-13 Thread Akinobu Mita
The video device release() callback for video-i2c driver frees the whole
struct video_i2c_data.  If there is no user left for the video device
when video_unregister_device() is called, the release callback is executed.

However, in video_i2c_remove() some fields (v4l2_dev, lock, and queue_lock)
in struct video_i2c_data are still accessed after video_unregister_device()
is called.

This fixes the use after free by moving the code from video_i2c_remove()
to the release() callback.

Fixes: 5cebaac60974 ("media: video-i2c: add video-i2c driver")
Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
* v3
- Move the code causing use-after-free from video_i2c_remove() to the
  video device release() callback.
- Remove Acked-by line as there are enough changes from previous version

 drivers/media/i2c/video-i2c.c | 11 ++-
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index 06d29d8..f27d294 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -510,7 +510,12 @@ static const struct v4l2_ioctl_ops video_i2c_ioctl_ops = {
 
 static void video_i2c_release(struct video_device *vdev)
 {
-   kfree(video_get_drvdata(vdev));
+   struct video_i2c_data *data = video_get_drvdata(vdev);
+
+   v4l2_device_unregister(>v4l2_dev);
+   mutex_destroy(>lock);
+   mutex_destroy(>queue_lock);
+   kfree(data);
 }
 
 static int video_i2c_probe(struct i2c_client *client,
@@ -608,10 +613,6 @@ static int video_i2c_remove(struct i2c_client *client)
struct video_i2c_data *data = i2c_get_clientdata(client);
 
video_unregister_device(>vdev);
-   v4l2_device_unregister(>v4l2_dev);
-
-   mutex_destroy(>lock);
-   mutex_destroy(>queue_lock);
 
return 0;
 }
-- 
2.7.4



[PATCH v3 3/6] media: v4l2-common: add V4L2_FRACT_COMPARE

2018-10-13 Thread Akinobu Mita
Add macro to compare two v4l2_fract values in v4l2 common internal API.
The same macro FRACT_CMP() is used by vivid and bcm2835-camera.  This just
renames it to V4L2_FRACT_COMPARE in order to avoid namespace collision.

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Acked-by: Sakari Ailus 
Signed-off-by: Akinobu Mita 
---
* v3
- Add Acked-by line

 include/media/v4l2-common.h | 5 +
 1 file changed, 5 insertions(+)

diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index cdc87ec..eafb8a3 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -384,4 +384,9 @@ int v4l2_g_parm_cap(struct video_device *vdev,
 int v4l2_s_parm_cap(struct video_device *vdev,
struct v4l2_subdev *sd, struct v4l2_streamparm *a);
 
+/* Compare two v4l2_fract structs */
+#define V4L2_FRACT_COMPARE(a, OP, b)   \
+   ((u64)(a).numerator * (b).denominator OP\
+   (u64)(b).numerator * (a).denominator)
+
 #endif /* V4L2_COMMON_H_ */
-- 
2.7.4



[PATCH v3 6/6] media: video-i2c: support runtime PM

2018-10-13 Thread Akinobu Mita
AMG88xx has a register for setting operating mode.  This adds support
runtime PM by changing the operating mode.

The instruction for changing sleep mode to normal mode is from the
reference specifications.

https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Reviewed-by: Matt Ranostay 
Acked-by: Sakari Ailus 
Signed-off-by: Akinobu Mita 
---
* v3
- Move chip->set_power() call to the video device release() callback.
- Add Acked-by line

 drivers/media/i2c/video-i2c.c | 141 +-
 1 file changed, 139 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index 3334fc2..22fdc43 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -17,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -94,10 +95,23 @@ struct video_i2c_chip {
/* xfer function */
int (*xfer)(struct video_i2c_data *data, char *buf);
 
+   /* power control function */
+   int (*set_power)(struct video_i2c_data *data, bool on);
+
/* hwmon init function */
int (*hwmon_init)(struct video_i2c_data *data);
 };
 
+/* Power control register */
+#define AMG88XX_REG_PCTL   0x00
+#define AMG88XX_PCTL_NORMAL0x00
+#define AMG88XX_PCTL_SLEEP 0x10
+
+/* Reset register */
+#define AMG88XX_REG_RST0x01
+#define AMG88XX_RST_FLAG   0x30
+#define AMG88XX_RST_INIT   0x3f
+
 /* Frame rate register */
 #define AMG88XX_REG_FPSC   0x02
 #define AMG88XX_FPSC_1FPS  BIT(0)
@@ -127,6 +141,59 @@ static int amg88xx_setup(struct video_i2c_data *data)
return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val);
 }
 
+static int amg88xx_set_power_on(struct video_i2c_data *data)
+{
+   int ret;
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, AMG88XX_PCTL_NORMAL);
+   if (ret)
+   return ret;
+
+   msleep(50);
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_INIT);
+   if (ret)
+   return ret;
+
+   msleep(2);
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_FLAG);
+   if (ret)
+   return ret;
+
+   /*
+* Wait two frames before reading thermistor and temperature registers
+*/
+   msleep(200);
+
+   return 0;
+}
+
+static int amg88xx_set_power_off(struct video_i2c_data *data)
+{
+   int ret;
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, AMG88XX_PCTL_SLEEP);
+   if (ret)
+   return ret;
+   /*
+* Wait for a while to avoid resuming normal mode immediately after
+* entering sleep mode, otherwise the device occasionally goes wrong
+* (thermistor and temperature registers are not updated at all)
+*/
+   msleep(100);
+
+   return 0;
+}
+
+static int amg88xx_set_power(struct video_i2c_data *data, bool on)
+{
+   if (on)
+   return amg88xx_set_power_on(data);
+
+   return amg88xx_set_power_off(data);
+}
+
 #if IS_ENABLED(CONFIG_HWMON)
 
 static const u32 amg88xx_temp_config[] = {
@@ -158,7 +225,15 @@ static int amg88xx_read(struct device *dev, enum 
hwmon_sensor_types type,
__le16 buf;
int tmp;
 
+   tmp = pm_runtime_get_sync(regmap_get_device(data->regmap));
+   if (tmp < 0) {
+   pm_runtime_put_noidle(regmap_get_device(data->regmap));
+   return tmp;
+   }
+
tmp = regmap_bulk_read(data->regmap, AMG88XX_REG_TTHL, , 2);
+   pm_runtime_mark_last_busy(regmap_get_device(data->regmap));
+   pm_runtime_put_autosuspend(regmap_get_device(data->regmap));
if (tmp)
return tmp;
 
@@ -217,6 +292,7 @@ static const struct video_i2c_chip video_i2c_chip[] = {
.regmap_config  = _regmap_config,
.setup  = _setup,
.xfer   = _xfer,
+   .set_power  = amg88xx_set_power,
.hwmon_init = amg88xx_hwmon_init,
},
 };
@@ -343,14 +419,21 @@ static void video_i2c_del_list(struct vb2_queue *vq, enum 
vb2_buffer_state state
 static int start_streaming(struct vb2_queue *vq, unsigned int count)
 {
struct video_i2c_data *data = vb2_get_drv_priv(vq);
+   struct device *dev = regmap_get_device(data->regmap);
int ret;
 
if (data->kthread_vid_cap)
return 0;
 
+   ret = pm_runtime_get_sync(dev);
+   if (ret < 0) {
+   pm_runtime_put_noidle(dev);
+   goto error_del_list;
+   }
+
ret = data->chip->setup(data);
if (ret)
-   goto error_del_list;
+   goto error_rpm_put;
 
data->sequence = 0;
data->kth

[PATCH v3 4/6] media: vivid: use V4L2_FRACT_COMPARE

2018-10-13 Thread Akinobu Mita
Now the equivalent of FRACT_CMP() is added in v4l2 common internal API
header.

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Acked-by: Sakari Ailus 
Signed-off-by: Akinobu Mita 
---
* v3
- Add Acked-by line

 drivers/media/platform/vivid/vivid-vid-cap.c | 9 +++--
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c 
b/drivers/media/platform/vivid/vivid-vid-cap.c
index 1599159..f19c701 100644
--- a/drivers/media/platform/vivid/vivid-vid-cap.c
+++ b/drivers/media/platform/vivid/vivid-vid-cap.c
@@ -1824,9 +1824,6 @@ int vivid_vid_cap_g_parm(struct file *file, void *priv,
return 0;
 }
 
-#define FRACT_CMP(a, OP, b)\
-   ((u64)(a).numerator * (b).denominator  OP  (u64)(b).numerator * 
(a).denominator)
-
 int vivid_vid_cap_s_parm(struct file *file, void *priv,
  struct v4l2_streamparm *parm)
 {
@@ -1847,14 +1844,14 @@ int vivid_vid_cap_s_parm(struct file *file, void *priv,
if (tpf.denominator == 0)
tpf = webcam_intervals[ival_sz - 1];
for (i = 0; i < ival_sz; i++)
-   if (FRACT_CMP(tpf, >=, webcam_intervals[i]))
+   if (V4L2_FRACT_COMPARE(tpf, >=, webcam_intervals[i]))
break;
if (i == ival_sz)
i = ival_sz - 1;
dev->webcam_ival_idx = i;
tpf = webcam_intervals[dev->webcam_ival_idx];
-   tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf;
-   tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf;
+   tpf = V4L2_FRACT_COMPARE(tpf, <, tpf_min) ? tpf_min : tpf;
+   tpf = V4L2_FRACT_COMPARE(tpf, >, tpf_max) ? tpf_max : tpf;
 
/* resync the thread's timings */
dev->cap_seq_resync = true;
-- 
2.7.4



[PATCH v3 0/6] media: video-i2c: support changing frame interval and runtime PM

2018-10-13 Thread Akinobu Mita
This patchset adds support for changing frame interval and runtime PM for
video-i2c driver.  This also adds an helper macro to v4l2 common
internal API that is used to to find a suitable frame interval.

There are a couple of unrelated changes that are included for simplifying
driver initialization code and register accesses.

* v3
- Append result of v4l2-compliance in cover-letter
- Move the code causing use-after-free from video_i2c_remove() to the
  video device release() callback.
- Use regmap_init_i2c() instead of devm_regmap_init_i2c() and call
  regmap_exit_i2c() in video device release() callback in order to
  avoid releasing regmap when the driver is unbound.
- Add Acked-by lines

* v2
- Add Acked-by and Reviewed-by tags
- Update commit log to clarify the use after free
- Add thermistor and termperature register address definisions
- Stop adding v4l2_find_closest_fract() in v4l2 common internal API
- Add V4L2_FRACT_COMPARE() macro in v4l2 common internal API
- Use V4L2_FRACT_COMPARE() to find suitable frame interval instead of
  v4l2_find_closest_fract()
- Add comment for register address definisions

Akinobu Mita (6):
  media: video-i2c: avoid accessing released memory area when removing
driver
  media: video-i2c: use i2c regmap
  media: v4l2-common: add V4L2_FRACT_COMPARE
  media: vivid: use V4L2_FRACT_COMPARE
  media: video-i2c: support changing frame interval
  media: video-i2c: support runtime PM

 drivers/media/i2c/video-i2c.c| 286 +++
 drivers/media/platform/vivid/vivid-vid-cap.c |   9 +-
 include/media/v4l2-common.h  |   5 +
 3 files changed, 254 insertions(+), 46 deletions(-)

v4l2-compliance SHA: 7bde5ef172bd4a09b9544788ba9c5dbb1aa9994a, 32 bits

Compliance test for device /dev/video2:

Driver Info:
Driver name  : video-i2c
Card type: I2C 1-104 Transport Video
Bus info : I2C:1-104
Driver version   : 4.19.0
Capabilities : 0x8521
Video Capture
Read/Write
Streaming
Extended Pix Format
Device Capabilities
Device Caps  : 0x0521
Video Capture
Read/Write
Streaming
Extended Pix Format

Required ioctls:
test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
test second /dev/video2 open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK

Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK
test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 1 Audio Inputs: 0 Tuners: 0

Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
test VIDIOC_G/S_EDID: OK (Not Supported)

Control ioctls (Input 0):
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
test VIDIOC_QUERYCTRL: OK (Not Supported)
test VIDIOC_G/S_CTRL: OK (Not Supported)
test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 0 Private Controls: 0

Format ioctls (Input 0):
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
test VIDIOC_TRY_FMT: OK
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)

Codec ioctls (Input 0):
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

Buffer ioctls (Input 0):
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
test VIDIOC_EXPBUF: OK (Not Supported)

Total: 43, Succeeded: 43, Failed: 0, Warnings: 0

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
-- 
2.7.4



Re: [PATCH v2 1/6] media: video-i2c: avoid accessing released memory area when removing driver

2018-10-08 Thread Akinobu Mita
2018年10月8日(月) 20:21 Hans Verkuil :
>
> On 10/05/2018 04:59 PM, Akinobu Mita wrote:
> > 2018年10月5日(金) 18:36 Sakari Ailus :
> >>
> >> Hi Hans,
> >>
> >> On Mon, Oct 01, 2018 at 11:40:00AM +0200, Hans Verkuil wrote:
> >>> On 09/23/2018 06:34 PM, Akinobu Mita wrote:
> >>>> The video_i2c_data is allocated by kzalloc and released by the video
> >>>> device's release callback.  The release callback is called when
> >>>> video_unregister_device() is called, but it will still be accessed after
> >>>> calling video_unregister_device().
> >>>>
> >>>> Fix the use after free by allocating video_i2c_data by devm_kzalloc() 
> >>>> with
> >>>> i2c_client->dev so that it will automatically be released when the i2c
> >>>> driver is removed.
> >>>
> >>> Hmm. The patch is right, but the explanation isn't. The core problem is
> >>> that vdev.release was set to video_i2c_release, but that should only be
> >>> used if struct video_device was kzalloc'ed. But in this case it is 
> >>> embedded
> >>> in a larger struct, and then vdev.release should always be set to
> >>> video_device_release_empty.
> >>
> >> When the driver is unbound, what's acquired using the devm_() family of
> >> functions is released. At the same time, the user still holds a file
> >> handle, and can issue IOCTLs --- but the device's data structures no longer
> >> exist.
> >>
> >> That's not ok, and also the reason why we have the release callback.
> >>
> >> While there are issues elsewhere, this bit of the V4L2 / MC frameworks is
> >> fine.
> >>
> >> Or am I missing something?
> >
> > How about moving the lines causing use-after-free to release callback
> > like below?
> >
> > static void video_i2c_release(struct video_device *vdev)
> > {
> > struct video_i2c_data *data = video_get_drvdata(vdev);
> >
> > v4l2_device_unregister(>v4l2_dev);
> > mutex_destroy(>lock);
> > mutex_destroy(>queue_lock);
> > kfree(data);
> > }
> >
>
> You can test this with v4l2-ctl:
>
> v4l2-ctl --sleep 10
>
> This sleeps 10s, then calls QUERYCAP and closes the file handle.
>
> In another shell you can unbind the driver while v4l2-ctl is sleeping.
>
> Hopefully this works without crashing anything :-)

I tried that and the command finished without crash.

$ v4l2-ctl --sleep 10 -d /dev/video2
Test VIDIOC_QUERYCAP:
VIDIOC_QUERYCAP returned -1 (No such device)
VIDIOC_QUERYCAP: No such device

This -ENODEV should be ok as V4L2_FL_REGISTERED flag has already been
cleared by video_unregister_device().


Re: [PATCH v2 1/6] media: video-i2c: avoid accessing released memory area when removing driver

2018-10-05 Thread Akinobu Mita
2018年10月5日(金) 18:36 Sakari Ailus :
>
> Hi Hans,
>
> On Mon, Oct 01, 2018 at 11:40:00AM +0200, Hans Verkuil wrote:
> > On 09/23/2018 06:34 PM, Akinobu Mita wrote:
> > > The video_i2c_data is allocated by kzalloc and released by the video
> > > device's release callback.  The release callback is called when
> > > video_unregister_device() is called, but it will still be accessed after
> > > calling video_unregister_device().
> > >
> > > Fix the use after free by allocating video_i2c_data by devm_kzalloc() with
> > > i2c_client->dev so that it will automatically be released when the i2c
> > > driver is removed.
> >
> > Hmm. The patch is right, but the explanation isn't. The core problem is
> > that vdev.release was set to video_i2c_release, but that should only be
> > used if struct video_device was kzalloc'ed. But in this case it is embedded
> > in a larger struct, and then vdev.release should always be set to
> > video_device_release_empty.
>
> When the driver is unbound, what's acquired using the devm_() family of
> functions is released. At the same time, the user still holds a file
> handle, and can issue IOCTLs --- but the device's data structures no longer
> exist.
>
> That's not ok, and also the reason why we have the release callback.
>
> While there are issues elsewhere, this bit of the V4L2 / MC frameworks is
> fine.
>
> Or am I missing something?

How about moving the lines causing use-after-free to release callback
like below?

static void video_i2c_release(struct video_device *vdev)
{
struct video_i2c_data *data = video_get_drvdata(vdev);

v4l2_device_unregister(>v4l2_dev);
mutex_destroy(>lock);
mutex_destroy(>queue_lock);
kfree(data);
}


Re: [PATCH v2 1/6] media: video-i2c: avoid accessing released memory area when removing driver

2018-10-02 Thread Akinobu Mita
2018年10月1日(月) 18:40 Hans Verkuil :
>
> On 09/23/2018 06:34 PM, Akinobu Mita wrote:
> > The video_i2c_data is allocated by kzalloc and released by the video
> > device's release callback.  The release callback is called when
> > video_unregister_device() is called, but it will still be accessed after
> > calling video_unregister_device().
> >
> > Fix the use after free by allocating video_i2c_data by devm_kzalloc() with
> > i2c_client->dev so that it will automatically be released when the i2c
> > driver is removed.
>
> Hmm. The patch is right, but the explanation isn't. The core problem is
> that vdev.release was set to video_i2c_release, but that should only be
> used if struct video_device was kzalloc'ed. But in this case it is embedded
> in a larger struct, and then vdev.release should always be set to
> video_device_release_empty.
>
> That was the real reason for the invalid access.

How about the commit log below?

struct video_device for video-i2c is embedded in a struct video_i2c_data,
and then vdev.release should always be set to video_device_release_empty.

However, the vdev.release is currently set to video_i2c_release which
frees the whole struct video_i2c_data.  In video_i2c_remove(), some fields
(v4l2_dev, lock, and queue_lock) in struct video_i2c_data are still
accessed after video_unregister_device().

This fixes the use after free by setting the vdev.release to
video_device_release_empty.  Also, convert to use devm_kzalloc() for
allocating a struct video_i2c_data in order to simplify the code.


Re: [PATCH v2 0/6] media: video-i2c: support changing frame interval and runtime PM

2018-10-01 Thread Akinobu Mita
2018年10月1日(月) 18:41 Hans Verkuil :
>
> On 09/23/2018 06:34 PM, Akinobu Mita wrote:
> > This patchset adds support for changing frame interval and runtime PM for
> > video-i2c driver.  This also adds an helper macro to v4l2 common
> > internal API that is used to to find a suitable frame interval.
> >
> > There are a couple of unrelated changes that are included for simplifying
> > driver initialization code and register accesses.
> >
> > * v2
> > - Add Acked-by and Reviewed-by tags
> > - Update commit log to clarify the use after free
> > - Add thermistor and termperature register address definisions
> > - Stop adding v4l2_find_closest_fract() in v4l2 common internal API
> > - Add V4L2_FRACT_COMPARE() macro in v4l2 common internal API
> > - Use V4L2_FRACT_COMPARE() to find suitable frame interval instead of
> >   v4l2_find_closest_fract()
>
> 1) What's wrong with v4l2_find_closest_fract()?

My implementation of v4l2_find_closest_fract() in v1 didn't care about
u32 overflow.  Even if the overflow problem is fixed, there are only a
few drivers (video-i2c and vivid) that can make use of it.  So I feel
it adds more lines of code than it can remove.

> 2) Please test this with the latest v4l2-compliance: I recently improved
>the S_PARM checks, so I want to make sure this driver passes those tests.

v4l2-compliance SHA: 7bde5ef172bd4a09b9544788ba9c5dbb1aa9994a, 32 bits

Compliance test for device /dev/video2:

Driver Info:
Driver name  : video-i2c
Card type: I2C 1-104 Transport Video
Bus info : I2C:1-104
Driver version   : 4.19.0
Capabilities : 0x8521
Video Capture
Read/Write
Streaming
Extended Pix Format
Device Capabilities
Device Caps  : 0x0521
Video Capture
Read/Write
Streaming
Extended Pix Format

Required ioctls:
test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
test second /dev/video2 open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK

Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK
test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 1 Audio Inputs: 0 Tuners: 0

Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
test VIDIOC_G/S_EDID: OK (Not Supported)

Control ioctls (Input 0):
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
test VIDIOC_QUERYCTRL: OK (Not Supported)
test VIDIOC_G/S_CTRL: OK (Not Supported)
test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 0 Private Controls: 0

Format ioctls (Input 0):
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
test VIDIOC_TRY_FMT: OK
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)

Codec ioctls (Input 0):
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

Buffer ioctls (Input 0):
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
test VIDIOC_EXPBUF: OK (Not Supported)

Total: 43, Succeeded: 43, Failed: 0, Warnings: 0


[PATCH v2 1/6] media: video-i2c: avoid accessing released memory area when removing driver

2018-09-23 Thread Akinobu Mita
The video_i2c_data is allocated by kzalloc and released by the video
device's release callback.  The release callback is called when
video_unregister_device() is called, but it will still be accessed after
calling video_unregister_device().

Fix the use after free by allocating video_i2c_data by devm_kzalloc() with
i2c_client->dev so that it will automatically be released when the i2c
driver is removed.

Fixes: 5cebaac60974 ("media: video-i2c: add video-i2c driver")
Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Acked-by: Matt Ranostay 
Signed-off-by: Akinobu Mita 
---
* v2
- Update commit log to clarify the use after free
- Add Acked-by tag

 drivers/media/i2c/video-i2c.c | 18 +-
 1 file changed, 5 insertions(+), 13 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index 06d29d8..b7a2af9 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -508,20 +508,15 @@ static const struct v4l2_ioctl_ops video_i2c_ioctl_ops = {
.vidioc_streamoff   = vb2_ioctl_streamoff,
 };
 
-static void video_i2c_release(struct video_device *vdev)
-{
-   kfree(video_get_drvdata(vdev));
-}
-
 static int video_i2c_probe(struct i2c_client *client,
 const struct i2c_device_id *id)
 {
struct video_i2c_data *data;
struct v4l2_device *v4l2_dev;
struct vb2_queue *queue;
-   int ret = -ENODEV;
+   int ret;
 
-   data = kzalloc(sizeof(*data), GFP_KERNEL);
+   data = devm_kzalloc(>dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
 
@@ -530,7 +525,7 @@ static int video_i2c_probe(struct i2c_client *client,
else if (id)
data->chip = _i2c_chip[id->driver_data];
else
-   goto error_free_device;
+   return -ENODEV;
 
data->client = client;
v4l2_dev = >v4l2_dev;
@@ -538,7 +533,7 @@ static int video_i2c_probe(struct i2c_client *client,
 
ret = v4l2_device_register(>dev, v4l2_dev);
if (ret < 0)
-   goto error_free_device;
+   return ret;
 
mutex_init(>lock);
mutex_init(>queue_lock);
@@ -568,7 +563,7 @@ static int video_i2c_probe(struct i2c_client *client,
data->vdev.fops = _i2c_fops;
data->vdev.lock = >lock;
data->vdev.ioctl_ops = _i2c_ioctl_ops;
-   data->vdev.release = video_i2c_release;
+   data->vdev.release = video_device_release_empty;
data->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE |
 V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
 
@@ -597,9 +592,6 @@ static int video_i2c_probe(struct i2c_client *client,
mutex_destroy(>lock);
mutex_destroy(>queue_lock);
 
-error_free_device:
-   kfree(data);
-
return ret;
 }
 
-- 
2.7.4



[PATCH v2 0/6] media: video-i2c: support changing frame interval and runtime PM

2018-09-23 Thread Akinobu Mita
This patchset adds support for changing frame interval and runtime PM for
video-i2c driver.  This also adds an helper macro to v4l2 common
internal API that is used to to find a suitable frame interval.

There are a couple of unrelated changes that are included for simplifying
driver initialization code and register accesses.

* v2
- Add Acked-by and Reviewed-by tags
- Update commit log to clarify the use after free
- Add thermistor and termperature register address definisions
- Stop adding v4l2_find_closest_fract() in v4l2 common internal API
- Add V4L2_FRACT_COMPARE() macro in v4l2 common internal API
- Use V4L2_FRACT_COMPARE() to find suitable frame interval instead of
  v4l2_find_closest_fract()
- Add comment for register address definisions

Akinobu Mita (6):
  media: video-i2c: avoid accessing released memory area when removing
driver
  media: video-i2c: use i2c regmap
  media: v4l2-common: add V4L2_FRACT_COMPARE
  media: vivid: use V4L2_FRACT_COMPARE
  media: video-i2c: support changing frame interval
  media: video-i2c: support runtime PM

 drivers/media/i2c/video-i2c.c| 286 ++-
 drivers/media/platform/vivid/vivid-vid-cap.c |   9 +-
 include/media/v4l2-common.h  |   5 +
 3 files changed, 247 insertions(+), 53 deletions(-)

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
-- 
2.7.4



[PATCH v2 3/6] media: v4l2-common: add V4L2_FRACT_COMPARE

2018-09-23 Thread Akinobu Mita
Add macro to compare two v4l2_fract values in v4l2 common internal API.
The same macro FRACT_CMP() is used by vivid and bcm2835-camera.  This just
renames it to V4L2_FRACT_COMPARE in order to avoid namespace collision.

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
* v2
- New patch

 include/media/v4l2-common.h | 5 +
 1 file changed, 5 insertions(+)

diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index cdc87ec..eafb8a3 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -384,4 +384,9 @@ int v4l2_g_parm_cap(struct video_device *vdev,
 int v4l2_s_parm_cap(struct video_device *vdev,
struct v4l2_subdev *sd, struct v4l2_streamparm *a);
 
+/* Compare two v4l2_fract structs */
+#define V4L2_FRACT_COMPARE(a, OP, b)   \
+   ((u64)(a).numerator * (b).denominator OP\
+   (u64)(b).numerator * (a).denominator)
+
 #endif /* V4L2_COMMON_H_ */
-- 
2.7.4



[PATCH v2 6/6] media: video-i2c: support runtime PM

2018-09-23 Thread Akinobu Mita
AMG88xx has a register for setting operating mode.  This adds support
runtime PM by changing the operating mode.

The instruction for changing sleep mode to normal mode is from the
reference specifications.

https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Reviewed-by: Matt Ranostay 
Signed-off-by: Akinobu Mita 
---
* v2
- Add Reviewed-by tag
- Add comment for register address definisions

 drivers/media/i2c/video-i2c.c | 142 +-
 1 file changed, 140 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index 6dd1929..f7058cf 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -17,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -94,10 +95,23 @@ struct video_i2c_chip {
/* xfer function */
int (*xfer)(struct video_i2c_data *data, char *buf);
 
+   /* power control function */
+   int (*set_power)(struct video_i2c_data *data, bool on);
+
/* hwmon init function */
int (*hwmon_init)(struct video_i2c_data *data);
 };
 
+/* Power control register */
+#define AMG88XX_REG_PCTL   0x00
+#define AMG88XX_PCTL_NORMAL0x00
+#define AMG88XX_PCTL_SLEEP 0x10
+
+/* Reset register */
+#define AMG88XX_REG_RST0x01
+#define AMG88XX_RST_FLAG   0x30
+#define AMG88XX_RST_INIT   0x3f
+
 /* Frame rate register */
 #define AMG88XX_REG_FPSC   0x02
 #define AMG88XX_FPSC_1FPS  BIT(0)
@@ -127,6 +141,59 @@ static int amg88xx_setup(struct video_i2c_data *data)
return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val);
 }
 
+static int amg88xx_set_power_on(struct video_i2c_data *data)
+{
+   int ret;
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, AMG88XX_PCTL_NORMAL);
+   if (ret)
+   return ret;
+
+   msleep(50);
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_INIT);
+   if (ret)
+   return ret;
+
+   msleep(2);
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_FLAG);
+   if (ret)
+   return ret;
+
+   /*
+* Wait two frames before reading thermistor and temperature registers
+*/
+   msleep(200);
+
+   return 0;
+}
+
+static int amg88xx_set_power_off(struct video_i2c_data *data)
+{
+   int ret;
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, AMG88XX_PCTL_SLEEP);
+   if (ret)
+   return ret;
+   /*
+* Wait for a while to avoid resuming normal mode immediately after
+* entering sleep mode, otherwise the device occasionally goes wrong
+* (thermistor and temperature registers are not updated at all)
+*/
+   msleep(100);
+
+   return 0;
+}
+
+static int amg88xx_set_power(struct video_i2c_data *data, bool on)
+{
+   if (on)
+   return amg88xx_set_power_on(data);
+
+   return amg88xx_set_power_off(data);
+}
+
 #if IS_ENABLED(CONFIG_HWMON)
 
 static const u32 amg88xx_temp_config[] = {
@@ -158,7 +225,15 @@ static int amg88xx_read(struct device *dev, enum 
hwmon_sensor_types type,
__le16 buf;
int tmp;
 
+   tmp = pm_runtime_get_sync(regmap_get_device(data->regmap));
+   if (tmp < 0) {
+   pm_runtime_put_noidle(regmap_get_device(data->regmap));
+   return tmp;
+   }
+
tmp = regmap_bulk_read(data->regmap, AMG88XX_REG_TTHL, , 2);
+   pm_runtime_mark_last_busy(regmap_get_device(data->regmap));
+   pm_runtime_put_autosuspend(regmap_get_device(data->regmap));
if (tmp)
return tmp;
 
@@ -217,6 +292,7 @@ static const struct video_i2c_chip video_i2c_chip[] = {
.regmap_config  = _regmap_config,
.setup  = _setup,
.xfer   = _xfer,
+   .set_power  = amg88xx_set_power,
.hwmon_init = amg88xx_hwmon_init,
},
 };
@@ -343,14 +419,21 @@ static void video_i2c_del_list(struct vb2_queue *vq, enum 
vb2_buffer_state state
 static int start_streaming(struct vb2_queue *vq, unsigned int count)
 {
struct video_i2c_data *data = vb2_get_drv_priv(vq);
+   struct device *dev = regmap_get_device(data->regmap);
int ret;
 
if (data->kthread_vid_cap)
return 0;
 
+   ret = pm_runtime_get_sync(dev);
+   if (ret < 0) {
+   pm_runtime_put_noidle(dev);
+   goto error_del_list;
+   }
+
ret = data->chip->setup(data);
if (ret)
-   goto error_del_list;
+   goto error_rpm_put;
 
data->sequence = 0;
data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap

[PATCH v2 5/6] media: video-i2c: support changing frame interval

2018-09-23 Thread Akinobu Mita
AMG88xx has a register for setting frame rate 1 or 10 FPS.
This adds support changing frame interval.

Reference specifications:
https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Acked-by: Matt Ranostay 
Signed-off-by: Akinobu Mita 
---
* v2
- Add Acked-by tag
- Add comment for frame rate register address definision
- Use V4L2_FRACT_COMPARE() to find suitable frame interval

 drivers/media/i2c/video-i2c.c | 78 ---
 1 file changed, 66 insertions(+), 12 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index fb8509e..6dd1929 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -52,6 +52,8 @@ struct video_i2c_data {
 
struct task_struct *kthread_vid_cap;
struct list_head vid_cap_active;
+
+   struct v4l2_fract frame_interval;
 };
 
 static const struct v4l2_fmtdesc amg88xx_format = {
@@ -74,8 +76,9 @@ struct video_i2c_chip {
const struct v4l2_fmtdesc *format;
const struct v4l2_frmsize_discrete *size;
 
-   /* max frames per second */
-   unsigned int max_fps;
+   /* available frame intervals */
+   const struct v4l2_fract *frame_intervals;
+   unsigned int num_frame_intervals;
 
/* pixel buffer size */
unsigned int buffer_size;
@@ -85,6 +88,9 @@ struct video_i2c_chip {
 
const struct regmap_config *regmap_config;
 
+   /* setup function */
+   int (*setup)(struct video_i2c_data *data);
+
/* xfer function */
int (*xfer)(struct video_i2c_data *data, char *buf);
 
@@ -92,6 +98,10 @@ struct video_i2c_chip {
int (*hwmon_init)(struct video_i2c_data *data);
 };
 
+/* Frame rate register */
+#define AMG88XX_REG_FPSC   0x02
+#define AMG88XX_FPSC_1FPS  BIT(0)
+
 /* Thermistor register */
 #define AMG88XX_REG_TTHL   0x0e
 
@@ -104,6 +114,19 @@ static int amg88xx_xfer(struct video_i2c_data *data, char 
*buf)
data->chip->buffer_size);
 }
 
+static int amg88xx_setup(struct video_i2c_data *data)
+{
+   unsigned int mask = AMG88XX_FPSC_1FPS;
+   unsigned int val;
+
+   if (data->frame_interval.numerator == data->frame_interval.denominator)
+   val = mask;
+   else
+   val = 0;
+
+   return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val);
+}
+
 #if IS_ENABLED(CONFIG_HWMON)
 
 static const u32 amg88xx_temp_config[] = {
@@ -178,14 +201,21 @@ static int amg88xx_hwmon_init(struct video_i2c_data *data)
 
 #define AMG88XX0
 
+static const struct v4l2_fract amg88xx_frame_intervals[] = {
+   { 1, 10 },
+   { 1, 1 },
+};
+
 static const struct video_i2c_chip video_i2c_chip[] = {
[AMG88XX] = {
.size   = _size,
.format = _format,
-   .max_fps= 10,
+   .frame_intervals= amg88xx_frame_intervals,
+   .num_frame_intervals= ARRAY_SIZE(amg88xx_frame_intervals),
.buffer_size= 128,
.bpp= 16,
.regmap_config  = _regmap_config,
+   .setup  = _setup,
.xfer   = _xfer,
.hwmon_init = amg88xx_hwmon_init,
},
@@ -250,7 +280,8 @@ static void buffer_queue(struct vb2_buffer *vb)
 static int video_i2c_thread_vid_cap(void *priv)
 {
struct video_i2c_data *data = priv;
-   unsigned int delay = msecs_to_jiffies(1000 / data->chip->max_fps);
+   unsigned int delay = mult_frac(HZ, data->frame_interval.numerator,
+  data->frame_interval.denominator);
 
set_freezable();
 
@@ -312,19 +343,26 @@ static void video_i2c_del_list(struct vb2_queue *vq, enum 
vb2_buffer_state state
 static int start_streaming(struct vb2_queue *vq, unsigned int count)
 {
struct video_i2c_data *data = vb2_get_drv_priv(vq);
+   int ret;
 
if (data->kthread_vid_cap)
return 0;
 
+   ret = data->chip->setup(data);
+   if (ret)
+   goto error_del_list;
+
data->sequence = 0;
data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data,
"%s-vid-cap", data->v4l2_dev.name);
-   if (!IS_ERR(data->kthread_vid_cap))
+   ret = PTR_ERR_OR_ZERO(data->kthread_vid_cap);
+   if (!ret)
return 0;
 
+error_del_list:
video_i2c_del_list(vq, VB2_BUF_STATE_QUEUED);
 
-   return PTR_ERR(data->kthread_vid_cap);
+   return ret;
 }
 
 static void stop_streaming(struct vb2_queue *vq)
@@ -431,15 +469,14 @@ static int video_i2c_enum_frameintervals(struct file 
*file, void *priv,
const struct video_i2c_data *data = video_drvdat

[PATCH v2 4/6] media: vivid: use V4L2_FRACT_COMPARE

2018-09-23 Thread Akinobu Mita
Now the equivalent of FRACT_CMP() is added in v4l2 common internal API
header.

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
* v2
- New patch

 drivers/media/platform/vivid/vivid-vid-cap.c | 9 +++--
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c 
b/drivers/media/platform/vivid/vivid-vid-cap.c
index 1599159..f19c701 100644
--- a/drivers/media/platform/vivid/vivid-vid-cap.c
+++ b/drivers/media/platform/vivid/vivid-vid-cap.c
@@ -1824,9 +1824,6 @@ int vivid_vid_cap_g_parm(struct file *file, void *priv,
return 0;
 }
 
-#define FRACT_CMP(a, OP, b)\
-   ((u64)(a).numerator * (b).denominator  OP  (u64)(b).numerator * 
(a).denominator)
-
 int vivid_vid_cap_s_parm(struct file *file, void *priv,
  struct v4l2_streamparm *parm)
 {
@@ -1847,14 +1844,14 @@ int vivid_vid_cap_s_parm(struct file *file, void *priv,
if (tpf.denominator == 0)
tpf = webcam_intervals[ival_sz - 1];
for (i = 0; i < ival_sz; i++)
-   if (FRACT_CMP(tpf, >=, webcam_intervals[i]))
+   if (V4L2_FRACT_COMPARE(tpf, >=, webcam_intervals[i]))
break;
if (i == ival_sz)
i = ival_sz - 1;
dev->webcam_ival_idx = i;
tpf = webcam_intervals[dev->webcam_ival_idx];
-   tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf;
-   tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf;
+   tpf = V4L2_FRACT_COMPARE(tpf, <, tpf_min) ? tpf_min : tpf;
+   tpf = V4L2_FRACT_COMPARE(tpf, >, tpf_max) ? tpf_max : tpf;
 
/* resync the thread's timings */
dev->cap_seq_resync = true;
-- 
2.7.4



[PATCH v2 2/6] media: video-i2c: use i2c regmap

2018-09-23 Thread Akinobu Mita
Use regmap for i2c register access.  This simplifies register accesses and
chooses suitable access commands based on the functionality that the
adapter supports.

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
* v2
- Add thermistor and termperature register address definisions

 drivers/media/i2c/video-i2c.c | 60 ---
 1 file changed, 34 insertions(+), 26 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index b7a2af9..fb8509e 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -17,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -38,7 +39,7 @@ struct video_i2c_buffer {
 };
 
 struct video_i2c_data {
-   struct i2c_client *client;
+   struct regmap *regmap;
const struct video_i2c_chip *chip;
struct mutex lock;
spinlock_t slock;
@@ -62,6 +63,12 @@ static const struct v4l2_frmsize_discrete amg88xx_size = {
.height = 8,
 };
 
+static const struct regmap_config amg88xx_regmap_config = {
+   .reg_bits = 8,
+   .val_bits = 8,
+   .max_register = 0xff
+};
+
 struct video_i2c_chip {
/* video dimensions */
const struct v4l2_fmtdesc *format;
@@ -76,6 +83,8 @@ struct video_i2c_chip {
/* pixel size in bits */
unsigned int bpp;
 
+   const struct regmap_config *regmap_config;
+
/* xfer function */
int (*xfer)(struct video_i2c_data *data, char *buf);
 
@@ -83,26 +92,16 @@ struct video_i2c_chip {
int (*hwmon_init)(struct video_i2c_data *data);
 };
 
-static int amg88xx_xfer(struct video_i2c_data *data, char *buf)
-{
-   struct i2c_client *client = data->client;
-   struct i2c_msg msg[2];
-   u8 reg = 0x80;
-   int ret;
-
-   msg[0].addr = client->addr;
-   msg[0].flags = 0;
-   msg[0].len = 1;
-   msg[0].buf  = (char *)
-
-   msg[1].addr = client->addr;
-   msg[1].flags = I2C_M_RD;
-   msg[1].len = data->chip->buffer_size;
-   msg[1].buf = (char *)buf;
+/* Thermistor register */
+#define AMG88XX_REG_TTHL   0x0e
 
-   ret = i2c_transfer(client->adapter, msg, 2);
+/* Temperature register */
+#define AMG88XX_REG_T01L   0x80
 
-   return (ret == 2) ? 0 : -EIO;
+static int amg88xx_xfer(struct video_i2c_data *data, char *buf)
+{
+   return regmap_bulk_read(data->regmap, AMG88XX_REG_T01L, buf,
+   data->chip->buffer_size);
 }
 
 #if IS_ENABLED(CONFIG_HWMON)
@@ -133,12 +132,15 @@ static int amg88xx_read(struct device *dev, enum 
hwmon_sensor_types type,
u32 attr, int channel, long *val)
 {
struct video_i2c_data *data = dev_get_drvdata(dev);
-   struct i2c_client *client = data->client;
-   int tmp = i2c_smbus_read_word_data(client, 0x0e);
+   __le16 buf;
+   int tmp;
 
-   if (tmp < 0)
+   tmp = regmap_bulk_read(data->regmap, AMG88XX_REG_TTHL, , 2);
+   if (tmp)
return tmp;
 
+   tmp = le16_to_cpu(buf);
+
/*
 * Check for sign bit, this isn't a two's complement value but an
 * absolute temperature that needs to be inverted in the case of being
@@ -164,8 +166,9 @@ static const struct hwmon_chip_info amg88xx_chip_info = {
 
 static int amg88xx_hwmon_init(struct video_i2c_data *data)
 {
-   void *hwmon = devm_hwmon_device_register_with_info(>client->dev,
-   "amg88xx", data, _chip_info, NULL);
+   struct device *dev = regmap_get_device(data->regmap);
+   void *hwmon = devm_hwmon_device_register_with_info(dev, "amg88xx", data,
+   _chip_info, NULL);
 
return PTR_ERR_OR_ZERO(hwmon);
 }
@@ -182,6 +185,7 @@ static const struct video_i2c_chip video_i2c_chip[] = {
.max_fps= 10,
.buffer_size= 128,
.bpp= 16,
+   .regmap_config  = _regmap_config,
.xfer   = _xfer,
.hwmon_init = amg88xx_hwmon_init,
},
@@ -350,7 +354,8 @@ static int video_i2c_querycap(struct file *file, void  
*priv,
struct v4l2_capability *vcap)
 {
struct video_i2c_data *data = video_drvdata(file);
-   struct i2c_client *client = data->client;
+   struct device *dev = regmap_get_device(data->regmap);
+   struct i2c_client *client = to_i2c_client(dev);
 
strlcpy(vcap->driver, data->v4l2_dev.name, sizeof(vcap->driver));
strlcpy(vcap->card, data->vdev.name, sizeof(vcap->card));
@@ -527,7 +532,10 @@ static int video_i2c_probe(struct i2c_client *client,
else
return -ENODEV;
 
-   data->client = client;
+   data->regmap = devm_regmap_init_i

Re: [PATCH 3/5] media: v4l2-common: add v4l2_find_closest_fract()

2018-09-19 Thread Akinobu Mita
2018年9月19日(水) 20:18 Sakari Ailus :
>
> Hi Mita-san,
>
> On Tue, Sep 18, 2018 at 01:03:09AM +0900, Akinobu Mita wrote:
> > Add a function to locate the closest element in a sorted v4l2_fract array.
> >
> > The implementation is based on find_closest() macro in linux/util_macros.h
> > and the way to compare two v4l2_fract in vivid_vid_cap_s_parm in
> > drivers/media/platform/vivid/vivid-vid-cap.c.
> >
> > Cc: Matt Ranostay 
> > Cc: Sakari Ailus 
> > Cc: Hans Verkuil 
> > Cc: Mauro Carvalho Chehab 
> > Signed-off-by: Akinobu Mita 
> > ---
> >  drivers/media/v4l2-core/v4l2-common.c | 26 ++
> >  include/media/v4l2-common.h   | 12 
> >  2 files changed, 38 insertions(+)
> >
> > diff --git a/drivers/media/v4l2-core/v4l2-common.c 
> > b/drivers/media/v4l2-core/v4l2-common.c
> > index b518b92..91bd460 100644
> > --- a/drivers/media/v4l2-core/v4l2-common.c
> > +++ b/drivers/media/v4l2-core/v4l2-common.c
> > @@ -387,6 +387,32 @@ __v4l2_find_nearest_size(const void *array, size_t 
> > array_size,
> >  }
> >  EXPORT_SYMBOL_GPL(__v4l2_find_nearest_size);
> >
> > +#define FRACT_CMP(a, OP, b)  \
> > + ((u64)(a).numerator * (b).denominator OP\
> > +  (u64)(b).numerator * (a).denominator)
> > +
> > +int v4l2_find_closest_fract(struct v4l2_fract x, const struct v4l2_fract 
> > *array,
>
> unsigned int ?

As you noticed below, this function may lead to an overflow.  So I planned
to make it return -EOVERFLOW with help of linux/overflow.h.

But now I'm start thinking that finding closest (rounding) value is
overkill.  Instead finding smallest (ceiling) or largest (floor) value is
enough just like in vivid_vid_cap_s_parm() in vivid-vid-cap.c and we don't
need to be bothered with overflows.

> > + size_t num)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < num - 1; i++) {
> > + struct v4l2_fract a = array[i];
> > + struct v4l2_fract b = array[i + 1];
> > + struct v4l2_fract midpoint = {
> > + .numerator = a.numerator * b.denominator +
> > +  b.numerator * a.denominator,
>
> Assuming the entire range could be in use, this may lead to an overflow.
> Same on the line below.
>
> I also wonder if e.g. a binary search would be more effective than going
> through the entire list.

The video-i2c driver will use this function with an array of 2 objects
and the vivid driver may also use this function with an array of 5 objects.
So simple linear search is enough for now, but it can be changed to
bsearch without changing external interface if needed sometime.

> > + .denominator = 2 * a.denominator * b.denominator,
> > + };
> > +
> > + if (FRACT_CMP(x, <=, midpoint))
> > + break;
> > + }
> > +
> > + return i;
> > +}
> > +EXPORT_SYMBOL_GPL(v4l2_find_closest_fract);
> > +
> >  void v4l2_get_timestamp(struct timeval *tv)
> >  {
> >   struct timespec ts;
> > diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
> > index cdc87ec..e388f4e 100644
> > --- a/include/media/v4l2-common.h
> > +++ b/include/media/v4l2-common.h
> > @@ -350,6 +350,18 @@ __v4l2_find_nearest_size(const void *array, size_t 
> > array_size,
> >size_t height_offset, s32 width, s32 height);
> >
> >  /**
> > + * v4l2_find_closest_fract - locate the closest element in a sorted array
> > + * @x: The reference value.
> > + * @array: The array in which to look for the closest element. Must be 
> > sorted
> > + *  in ascending order.
> > + * @num: number of elements in 'array'.
> > + *
> > + * Returns the index of the element closest to 'x'.
> > + */
> > +int v4l2_find_closest_fract(struct v4l2_fract x, const struct v4l2_fract 
> > *array,
> > + size_t num);
> > +
> > +/**
> >   * v4l2_get_timestamp - helper routine to get a timestamp to be used when
> >   *   filling streaming metadata. Internally, it uses ktime_get_ts(),
> >   *   which is the recommended way to get it.
>
> --
> Regards,
>
> Sakari Ailus
> sakari.ai...@linux.intel.com


Re: [PATCH 1/5] media: video-i2c: avoid accessing released memory area when removing driver

2018-09-19 Thread Akinobu Mita
2018年9月19日(水) 19:35 Sakari Ailus :
>
> Hi Mita-san,
>
> On Tue, Sep 18, 2018 at 01:03:07AM +0900, Akinobu Mita wrote:
> > The struct video_i2c_data is released when video_unregister_device() is
> > called, but it will still be accessed after calling
> > video_unregister_device().
> >
> > Use devm_kzalloc() and let the memory be automatically released on driver
> > detach.
> >
> > Fixes: 5cebaac60974 ("media: video-i2c: add video-i2c driver")
> > Cc: Matt Ranostay 
> > Cc: Sakari Ailus 
> > Cc: Hans Verkuil 
> > Cc: Mauro Carvalho Chehab 
> > Signed-off-by: Akinobu Mita 
> > ---
> >  drivers/media/i2c/video-i2c.c | 18 +-
> >  1 file changed, 5 insertions(+), 13 deletions(-)
> >
> > diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
> > index 06d29d8..b7a2af9 100644
> > --- a/drivers/media/i2c/video-i2c.c
> > +++ b/drivers/media/i2c/video-i2c.c
> > @@ -508,20 +508,15 @@ static const struct v4l2_ioctl_ops 
> > video_i2c_ioctl_ops = {
> >   .vidioc_streamoff   = vb2_ioctl_streamoff,
> >  };
> >
> > -static void video_i2c_release(struct video_device *vdev)
> > -{
> > - kfree(video_get_drvdata(vdev));
>
> This is actually correct: it ensures that that the device data stays in
> place as long as the device is being accessed. Allocating device data with
> devm_kzalloc() no longer guarantees that, and is not the right thing to do
> for that reason.

I have actually inserted printk() each line in video_i2_remove().  When
rmmod this driver, video_i2c_release() (and also kfree) is called while
executing video_unregister_device().  Because video_unregister_device()
releases the last reference to data->vdev.dev, then v4l2_device_release()
callback executes data->vdev.release.

So use after freeing video_i2c_data actually happened.

In this patch, devm_kzalloc() is called with client->dev (not with vdev->dev).
So the allocated memory is released when the last user of client->dev
is gone (maybe just after video_i2_remove() is finished).

> > -}
> > -
> >  static int video_i2c_probe(struct i2c_client *client,
> >const struct i2c_device_id *id)
> >  {
> >   struct video_i2c_data *data;
> >   struct v4l2_device *v4l2_dev;
> >   struct vb2_queue *queue;
> > - int ret = -ENODEV;
> > + int ret;
> >
> > - data = kzalloc(sizeof(*data), GFP_KERNEL);
> > + data = devm_kzalloc(>dev, sizeof(*data), GFP_KERNEL);
> >   if (!data)
> >   return -ENOMEM;
> >
> > @@ -530,7 +525,7 @@ static int video_i2c_probe(struct i2c_client *client,
> >   else if (id)
> >   data->chip = _i2c_chip[id->driver_data];
> >   else
> > - goto error_free_device;
> > + return -ENODEV;
> >
> >   data->client = client;
> >   v4l2_dev = >v4l2_dev;
> > @@ -538,7 +533,7 @@ static int video_i2c_probe(struct i2c_client *client,
> >
> >   ret = v4l2_device_register(>dev, v4l2_dev);
> >   if (ret < 0)
> > - goto error_free_device;
> > + return ret;
> >
> >   mutex_init(>lock);
> >   mutex_init(>queue_lock);
> > @@ -568,7 +563,7 @@ static int video_i2c_probe(struct i2c_client *client,
> >   data->vdev.fops = _i2c_fops;
> >   data->vdev.lock = >lock;
> >   data->vdev.ioctl_ops = _i2c_ioctl_ops;
> > - data->vdev.release = video_i2c_release;
> > + data->vdev.release = video_device_release_empty;
> >   data->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE |
> >V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
> >
> > @@ -597,9 +592,6 @@ static int video_i2c_probe(struct i2c_client *client,
> >   mutex_destroy(>lock);
> >   mutex_destroy(>queue_lock);
> >
> > -error_free_device:
> > - kfree(data);
> > -
> >   return ret;
> >  }
> >
>
> --
> Regards,
>
> Sakari Ailus
> sakari.ai...@linux.intel.com


Re: [PATCH 5/5] media: video-i2c: support runtime PM

2018-09-18 Thread Akinobu Mita
2018年9月18日(火) 15:23 Matt Ranostay :
>
> On Mon, Sep 17, 2018 at 6:03 PM Akinobu Mita  wrote:
> >
> > AMG88xx has a register for setting operating mode.  This adds support
> > runtime PM by changing the operating mode.
> >
> > The instruction for changing sleep mode to normal mode is from the
> > reference specifications.
> >
> > https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf
> >
> > Cc: Matt Ranostay 
> > Cc: Sakari Ailus 
> > Cc: Hans Verkuil 
> > Cc: Mauro Carvalho Chehab 
> > Signed-off-by: Akinobu Mita 
> > ---
> >  drivers/media/i2c/video-i2c.c | 140 
> > +-
> >  1 file changed, 138 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
> > index 916f36e..93822f4 100644
> > --- a/drivers/media/i2c/video-i2c.c
> > +++ b/drivers/media/i2c/video-i2c.c
> > @@ -17,6 +17,7 @@
> >  #include 
> >  #include 
> >  #include 
> > +#include 
> >  #include 
> >  #include 
> >  #include 
> > @@ -94,6 +95,9 @@ struct video_i2c_chip {
> > /* xfer function */
> > int (*xfer)(struct video_i2c_data *data, char *buf);
> >
> > +   /* power control function */
> > +   int (*set_power)(struct video_i2c_data *data, bool on);
> > +
> > /* hwmon init function */
> > int (*hwmon_init)(struct video_i2c_data *data);
> >  };
> > @@ -104,6 +108,14 @@ static int amg88xx_xfer(struct video_i2c_data *data, 
> > char *buf)
> > data->chip->buffer_size);
> >  }
> >
> > +#define AMG88XX_REG_PCTL   0x00
> > +#define AMG88XX_PCTL_NORMAL0x00
> > +#define AMG88XX_PCTL_SLEEP 0x10
> > +
> > +#define AMG88XX_REG_RST0x01
> > +#define AMG88XX_RST_FLAG   0x30
> > +#define AMG88XX_RST_INIT   0x3f
> > +
> >  #define AMG88XX_REG_FPSC   0x02
> >  #define AMG88XX_FPSC_1FPS  BIT(0)
> >
> > @@ -120,6 +132,59 @@ static int amg88xx_setup(struct video_i2c_data *data)
> > return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, 
> > val);
> >  }
> >
> > +static int amg88xx_set_power_on(struct video_i2c_data *data)
> > +{
> > +   int ret;
> > +
> > +   ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, 
> > AMG88XX_PCTL_NORMAL);
> > +   if (ret)
> > +   return ret;
> > +
> > +   msleep(50);
> > +
> > +   ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_INIT);
> > +   if (ret)
> > +   return ret;
> > +
> > +   msleep(2);
> > +
> > +   ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_FLAG);
> > +   if (ret)
> > +   return ret;
> > +
> > +   /*
> > +* Wait two frames before reading thermistor and temperature 
> > registers
> > +*/
> > +   msleep(200);
> > +
> > +   return 0;
> > +}
> > +
> > +static int amg88xx_set_power_off(struct video_i2c_data *data)
> > +{
> > +   int ret;
> > +
> > +   ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, 
> > AMG88XX_PCTL_SLEEP);
> > +   if (ret)
> > +   return ret;
> > +   /*
> > +* Wait for a while to avoid resuming normal mode immediately after
> > +* entering sleep mode, otherwise the device occasionally goes wrong
> > +* (thermistor and temperature registers are not updated at all)
> > +*/
> > +   msleep(100);
> > +
> > +   return 0;
> > +}
> > +
> > +static int amg88xx_set_power(struct video_i2c_data *data, bool on)
> > +{
> > +   if (on)
> > +   return amg88xx_set_power_on(data);
> > +
> > +   return amg88xx_set_power_off(data);
> > +}
> > +
> >  #if IS_ENABLED(CONFIG_HWMON)
> >
> >  static const u32 amg88xx_temp_config[] = {
> > @@ -151,7 +216,15 @@ static int amg88xx_read(struct device *dev, enum 
> > hwmon_sensor_types type,
> > __le16 buf;
> > int tmp;
> >
> > +   tmp = pm_runtime_get_sync(regmap_get_device(data->regmap));
> > +   if (tmp < 0) {
> > +   pm_runtime_put_noidle(regmap_get_device(data->regmap));
> > +   return tmp;
> >

Re: [PATCH 2/5] media: video-i2c: use i2c regmap

2018-09-18 Thread Akinobu Mita
2018年9月18日(火) 12:19 Matt Ranostay :
>
> On Mon, Sep 17, 2018 at 9:03 AM Akinobu Mita  wrote:
> >
> > Use regmap for i2c register access.  This simplifies register accesses and
> > chooses suitable access commands based on the functionality that the
> > adapter supports.
> >
> > Cc: Matt Ranostay 
> > Cc: Sakari Ailus 
> > Cc: Hans Verkuil 
> > Cc: Mauro Carvalho Chehab 
> > Signed-off-by: Akinobu Mita 
> > ---
> >  drivers/media/i2c/video-i2c.c | 54 
> > ++-
> >  1 file changed, 28 insertions(+), 26 deletions(-)
> >
> > diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
> > index b7a2af9..90d389b 100644
> > --- a/drivers/media/i2c/video-i2c.c
> > +++ b/drivers/media/i2c/video-i2c.c
> > @@ -17,6 +17,7 @@
> >  #include 
> >  #include 
> >  #include 
> > +#include 
> >  #include 
> >  #include 
> >  #include 
> > @@ -38,7 +39,7 @@ struct video_i2c_buffer {
> >  };
> >
> >  struct video_i2c_data {
> > -   struct i2c_client *client;
> > +   struct regmap *regmap;
> > const struct video_i2c_chip *chip;
> > struct mutex lock;
> > spinlock_t slock;
> > @@ -62,6 +63,12 @@ static const struct v4l2_frmsize_discrete amg88xx_size = 
> > {
> > .height = 8,
> >  };
> >
> > +static const struct regmap_config amg88xx_regmap_config = {
> > +   .reg_bits = 8,
> > +   .val_bits = 8,
> > +   .max_register = 0xff
> > +};
> > +
> >  struct video_i2c_chip {
> > /* video dimensions */
> > const struct v4l2_fmtdesc *format;
> > @@ -76,6 +83,8 @@ struct video_i2c_chip {
> > /* pixel size in bits */
> > unsigned int bpp;
> >
> > +   const struct regmap_config *regmap_config;
> > +
> > /* xfer function */
> > int (*xfer)(struct video_i2c_data *data, char *buf);
> >
> > @@ -85,24 +94,8 @@ struct video_i2c_chip {
> >
> >  static int amg88xx_xfer(struct video_i2c_data *data, char *buf)
> >  {
> > -   struct i2c_client *client = data->client;
> > -   struct i2c_msg msg[2];
> > -   u8 reg = 0x80;
> > -   int ret;
> > -
> > -   msg[0].addr = client->addr;
> > -   msg[0].flags = 0;
> > -   msg[0].len = 1;
> > -   msg[0].buf  = (char *)
> > -
> > -   msg[1].addr = client->addr;
> > -   msg[1].flags = I2C_M_RD;
> > -   msg[1].len = data->chip->buffer_size;
> > -   msg[1].buf = (char *)buf;
> > -
> > -   ret = i2c_transfer(client->adapter, msg, 2);
> > -
> > -   return (ret == 2) ? 0 : -EIO;
> > +   return regmap_bulk_read(data->regmap, 0x80, buf,
> > +   data->chip->buffer_size);
>
> May as well make 0x80 into a AMG88XX_REG_* define as in the later
> patch in this series

Sounds good. I'll do in v2.

> >  }
> >
> >  #if IS_ENABLED(CONFIG_HWMON)
> > @@ -133,12 +126,15 @@ static int amg88xx_read(struct device *dev, enum 
> > hwmon_sensor_types type,
> > u32 attr, int channel, long *val)
> >  {
> > struct video_i2c_data *data = dev_get_drvdata(dev);
> > -   struct i2c_client *client = data->client;
> > -   int tmp = i2c_smbus_read_word_data(client, 0x0e);
> > +   __le16 buf;
> > +   int tmp;
> >
> > -   if (tmp < 0)
> > +   tmp = regmap_bulk_read(data->regmap, 0x0e, , 2);
>
> Same with 0x0e

OK.


[PATCH 1/5] media: video-i2c: avoid accessing released memory area when removing driver

2018-09-17 Thread Akinobu Mita
The struct video_i2c_data is released when video_unregister_device() is
called, but it will still be accessed after calling
video_unregister_device().

Use devm_kzalloc() and let the memory be automatically released on driver
detach.

Fixes: 5cebaac60974 ("media: video-i2c: add video-i2c driver")
Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/video-i2c.c | 18 +-
 1 file changed, 5 insertions(+), 13 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index 06d29d8..b7a2af9 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -508,20 +508,15 @@ static const struct v4l2_ioctl_ops video_i2c_ioctl_ops = {
.vidioc_streamoff   = vb2_ioctl_streamoff,
 };
 
-static void video_i2c_release(struct video_device *vdev)
-{
-   kfree(video_get_drvdata(vdev));
-}
-
 static int video_i2c_probe(struct i2c_client *client,
 const struct i2c_device_id *id)
 {
struct video_i2c_data *data;
struct v4l2_device *v4l2_dev;
struct vb2_queue *queue;
-   int ret = -ENODEV;
+   int ret;
 
-   data = kzalloc(sizeof(*data), GFP_KERNEL);
+   data = devm_kzalloc(>dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
 
@@ -530,7 +525,7 @@ static int video_i2c_probe(struct i2c_client *client,
else if (id)
data->chip = _i2c_chip[id->driver_data];
else
-   goto error_free_device;
+   return -ENODEV;
 
data->client = client;
v4l2_dev = >v4l2_dev;
@@ -538,7 +533,7 @@ static int video_i2c_probe(struct i2c_client *client,
 
ret = v4l2_device_register(>dev, v4l2_dev);
if (ret < 0)
-   goto error_free_device;
+   return ret;
 
mutex_init(>lock);
mutex_init(>queue_lock);
@@ -568,7 +563,7 @@ static int video_i2c_probe(struct i2c_client *client,
data->vdev.fops = _i2c_fops;
data->vdev.lock = >lock;
data->vdev.ioctl_ops = _i2c_ioctl_ops;
-   data->vdev.release = video_i2c_release;
+   data->vdev.release = video_device_release_empty;
data->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE |
 V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
 
@@ -597,9 +592,6 @@ static int video_i2c_probe(struct i2c_client *client,
mutex_destroy(>lock);
mutex_destroy(>queue_lock);
 
-error_free_device:
-   kfree(data);
-
return ret;
 }
 
-- 
2.7.4



[PATCH 3/5] media: v4l2-common: add v4l2_find_closest_fract()

2018-09-17 Thread Akinobu Mita
Add a function to locate the closest element in a sorted v4l2_fract array.

The implementation is based on find_closest() macro in linux/util_macros.h
and the way to compare two v4l2_fract in vivid_vid_cap_s_parm in
drivers/media/platform/vivid/vivid-vid-cap.c.

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/v4l2-core/v4l2-common.c | 26 ++
 include/media/v4l2-common.h   | 12 
 2 files changed, 38 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-common.c 
b/drivers/media/v4l2-core/v4l2-common.c
index b518b92..91bd460 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -387,6 +387,32 @@ __v4l2_find_nearest_size(const void *array, size_t 
array_size,
 }
 EXPORT_SYMBOL_GPL(__v4l2_find_nearest_size);
 
+#define FRACT_CMP(a, OP, b)\
+   ((u64)(a).numerator * (b).denominator OP\
+(u64)(b).numerator * (a).denominator)
+
+int v4l2_find_closest_fract(struct v4l2_fract x, const struct v4l2_fract 
*array,
+   size_t num)
+{
+   int i;
+
+   for (i = 0; i < num - 1; i++) {
+   struct v4l2_fract a = array[i];
+   struct v4l2_fract b = array[i + 1];
+   struct v4l2_fract midpoint = {
+   .numerator = a.numerator * b.denominator +
+b.numerator * a.denominator,
+   .denominator = 2 * a.denominator * b.denominator,
+   };
+
+   if (FRACT_CMP(x, <=, midpoint))
+   break;
+   }
+
+   return i;
+}
+EXPORT_SYMBOL_GPL(v4l2_find_closest_fract);
+
 void v4l2_get_timestamp(struct timeval *tv)
 {
struct timespec ts;
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index cdc87ec..e388f4e 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -350,6 +350,18 @@ __v4l2_find_nearest_size(const void *array, size_t 
array_size,
 size_t height_offset, s32 width, s32 height);
 
 /**
+ * v4l2_find_closest_fract - locate the closest element in a sorted array
+ * @x: The reference value.
+ * @array: The array in which to look for the closest element. Must be sorted
+ *  in ascending order.
+ * @num: number of elements in 'array'.
+ *
+ * Returns the index of the element closest to 'x'.
+ */
+int v4l2_find_closest_fract(struct v4l2_fract x, const struct v4l2_fract 
*array,
+   size_t num);
+
+/**
  * v4l2_get_timestamp - helper routine to get a timestamp to be used when
  * filling streaming metadata. Internally, it uses ktime_get_ts(),
  * which is the recommended way to get it.
-- 
2.7.4



[PATCH 5/5] media: video-i2c: support runtime PM

2018-09-17 Thread Akinobu Mita
AMG88xx has a register for setting operating mode.  This adds support
runtime PM by changing the operating mode.

The instruction for changing sleep mode to normal mode is from the
reference specifications.

https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/video-i2c.c | 140 +-
 1 file changed, 138 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index 916f36e..93822f4 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -17,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -94,6 +95,9 @@ struct video_i2c_chip {
/* xfer function */
int (*xfer)(struct video_i2c_data *data, char *buf);
 
+   /* power control function */
+   int (*set_power)(struct video_i2c_data *data, bool on);
+
/* hwmon init function */
int (*hwmon_init)(struct video_i2c_data *data);
 };
@@ -104,6 +108,14 @@ static int amg88xx_xfer(struct video_i2c_data *data, char 
*buf)
data->chip->buffer_size);
 }
 
+#define AMG88XX_REG_PCTL   0x00
+#define AMG88XX_PCTL_NORMAL0x00
+#define AMG88XX_PCTL_SLEEP 0x10
+
+#define AMG88XX_REG_RST0x01
+#define AMG88XX_RST_FLAG   0x30
+#define AMG88XX_RST_INIT   0x3f
+
 #define AMG88XX_REG_FPSC   0x02
 #define AMG88XX_FPSC_1FPS  BIT(0)
 
@@ -120,6 +132,59 @@ static int amg88xx_setup(struct video_i2c_data *data)
return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val);
 }
 
+static int amg88xx_set_power_on(struct video_i2c_data *data)
+{
+   int ret;
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, AMG88XX_PCTL_NORMAL);
+   if (ret)
+   return ret;
+
+   msleep(50);
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_INIT);
+   if (ret)
+   return ret;
+
+   msleep(2);
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_FLAG);
+   if (ret)
+   return ret;
+
+   /*
+* Wait two frames before reading thermistor and temperature registers
+*/
+   msleep(200);
+
+   return 0;
+}
+
+static int amg88xx_set_power_off(struct video_i2c_data *data)
+{
+   int ret;
+
+   ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, AMG88XX_PCTL_SLEEP);
+   if (ret)
+   return ret;
+   /*
+* Wait for a while to avoid resuming normal mode immediately after
+* entering sleep mode, otherwise the device occasionally goes wrong
+* (thermistor and temperature registers are not updated at all)
+*/
+   msleep(100);
+
+   return 0;
+}
+
+static int amg88xx_set_power(struct video_i2c_data *data, bool on)
+{
+   if (on)
+   return amg88xx_set_power_on(data);
+
+   return amg88xx_set_power_off(data);
+}
+
 #if IS_ENABLED(CONFIG_HWMON)
 
 static const u32 amg88xx_temp_config[] = {
@@ -151,7 +216,15 @@ static int amg88xx_read(struct device *dev, enum 
hwmon_sensor_types type,
__le16 buf;
int tmp;
 
+   tmp = pm_runtime_get_sync(regmap_get_device(data->regmap));
+   if (tmp < 0) {
+   pm_runtime_put_noidle(regmap_get_device(data->regmap));
+   return tmp;
+   }
+
tmp = regmap_bulk_read(data->regmap, 0x0e, , 2);
+   pm_runtime_mark_last_busy(regmap_get_device(data->regmap));
+   pm_runtime_put_autosuspend(regmap_get_device(data->regmap));
if (tmp)
return tmp;
 
@@ -210,6 +283,7 @@ static const struct video_i2c_chip video_i2c_chip[] = {
.regmap_config  = _regmap_config,
.setup  = _setup,
.xfer   = _xfer,
+   .set_power  = amg88xx_set_power,
.hwmon_init = amg88xx_hwmon_init,
},
 };
@@ -336,14 +410,21 @@ static void video_i2c_del_list(struct vb2_queue *vq, enum 
vb2_buffer_state state
 static int start_streaming(struct vb2_queue *vq, unsigned int count)
 {
struct video_i2c_data *data = vb2_get_drv_priv(vq);
+   struct device *dev = regmap_get_device(data->regmap);
int ret;
 
if (data->kthread_vid_cap)
return 0;
 
+   ret = pm_runtime_get_sync(dev);
+   if (ret < 0) {
+   pm_runtime_put_noidle(dev);
+   goto error_del_list;
+   }
+
ret = data->chip->setup(data);
if (ret)
-   goto error_del_list;
+   goto error_rpm_put;
 
data->sequence = 0;
data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data,
@@ -352

[PATCH 0/5] media: video-i2c: support changing frame interval and runtime PM

2018-09-17 Thread Akinobu Mita
This patchset adds support for changing frame interval and runtime PM for
video-i2c driver.  This also adds an helper function to v4l2 common
internal API that is used to to find a suitable frame interval.

There are a couple of unrelated changes that are included for simplifying
driver initialization code and register accesses.

Akinobu Mita (5):
  media: video-i2c: avoid accessing released memory area when removing
driver
  media: video-i2c: use i2c regmap
  media: v4l2-common: add v4l2_find_closest_fract()
  media: video-i2c: support changing frame interval
  media: video-i2c: support runtime PM

 drivers/media/i2c/video-i2c.c | 276 --
 drivers/media/v4l2-core/v4l2-common.c |  26 
 include/media/v4l2-common.h   |  12 ++
 3 files changed, 267 insertions(+), 47 deletions(-)

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
-- 
2.7.4



[PATCH 2/5] media: video-i2c: use i2c regmap

2018-09-17 Thread Akinobu Mita
Use regmap for i2c register access.  This simplifies register accesses and
chooses suitable access commands based on the functionality that the
adapter supports.

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/video-i2c.c | 54 ++-
 1 file changed, 28 insertions(+), 26 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index b7a2af9..90d389b 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -17,6 +17,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -38,7 +39,7 @@ struct video_i2c_buffer {
 };
 
 struct video_i2c_data {
-   struct i2c_client *client;
+   struct regmap *regmap;
const struct video_i2c_chip *chip;
struct mutex lock;
spinlock_t slock;
@@ -62,6 +63,12 @@ static const struct v4l2_frmsize_discrete amg88xx_size = {
.height = 8,
 };
 
+static const struct regmap_config amg88xx_regmap_config = {
+   .reg_bits = 8,
+   .val_bits = 8,
+   .max_register = 0xff
+};
+
 struct video_i2c_chip {
/* video dimensions */
const struct v4l2_fmtdesc *format;
@@ -76,6 +83,8 @@ struct video_i2c_chip {
/* pixel size in bits */
unsigned int bpp;
 
+   const struct regmap_config *regmap_config;
+
/* xfer function */
int (*xfer)(struct video_i2c_data *data, char *buf);
 
@@ -85,24 +94,8 @@ struct video_i2c_chip {
 
 static int amg88xx_xfer(struct video_i2c_data *data, char *buf)
 {
-   struct i2c_client *client = data->client;
-   struct i2c_msg msg[2];
-   u8 reg = 0x80;
-   int ret;
-
-   msg[0].addr = client->addr;
-   msg[0].flags = 0;
-   msg[0].len = 1;
-   msg[0].buf  = (char *)
-
-   msg[1].addr = client->addr;
-   msg[1].flags = I2C_M_RD;
-   msg[1].len = data->chip->buffer_size;
-   msg[1].buf = (char *)buf;
-
-   ret = i2c_transfer(client->adapter, msg, 2);
-
-   return (ret == 2) ? 0 : -EIO;
+   return regmap_bulk_read(data->regmap, 0x80, buf,
+   data->chip->buffer_size);
 }
 
 #if IS_ENABLED(CONFIG_HWMON)
@@ -133,12 +126,15 @@ static int amg88xx_read(struct device *dev, enum 
hwmon_sensor_types type,
u32 attr, int channel, long *val)
 {
struct video_i2c_data *data = dev_get_drvdata(dev);
-   struct i2c_client *client = data->client;
-   int tmp = i2c_smbus_read_word_data(client, 0x0e);
+   __le16 buf;
+   int tmp;
 
-   if (tmp < 0)
+   tmp = regmap_bulk_read(data->regmap, 0x0e, , 2);
+   if (tmp)
return tmp;
 
+   tmp = le16_to_cpu(buf);
+
/*
 * Check for sign bit, this isn't a two's complement value but an
 * absolute temperature that needs to be inverted in the case of being
@@ -164,8 +160,9 @@ static const struct hwmon_chip_info amg88xx_chip_info = {
 
 static int amg88xx_hwmon_init(struct video_i2c_data *data)
 {
-   void *hwmon = devm_hwmon_device_register_with_info(>client->dev,
-   "amg88xx", data, _chip_info, NULL);
+   struct device *dev = regmap_get_device(data->regmap);
+   void *hwmon = devm_hwmon_device_register_with_info(dev, "amg88xx", data,
+   _chip_info, NULL);
 
return PTR_ERR_OR_ZERO(hwmon);
 }
@@ -182,6 +179,7 @@ static const struct video_i2c_chip video_i2c_chip[] = {
.max_fps= 10,
.buffer_size= 128,
.bpp= 16,
+   .regmap_config  = _regmap_config,
.xfer   = _xfer,
.hwmon_init = amg88xx_hwmon_init,
},
@@ -350,7 +348,8 @@ static int video_i2c_querycap(struct file *file, void  
*priv,
struct v4l2_capability *vcap)
 {
struct video_i2c_data *data = video_drvdata(file);
-   struct i2c_client *client = data->client;
+   struct device *dev = regmap_get_device(data->regmap);
+   struct i2c_client *client = to_i2c_client(dev);
 
strlcpy(vcap->driver, data->v4l2_dev.name, sizeof(vcap->driver));
strlcpy(vcap->card, data->vdev.name, sizeof(vcap->card));
@@ -527,7 +526,10 @@ static int video_i2c_probe(struct i2c_client *client,
else
return -ENODEV;
 
-   data->client = client;
+   data->regmap = devm_regmap_init_i2c(client, data->chip->regmap_config);
+   if (IS_ERR(data->regmap))
+   return PTR_ERR(data->regmap);
+
v4l2_dev = >v4l2_dev;
strlcpy(v4l2_dev->name, VIDEO_I2C_DRIVER, sizeof(v4l2_dev->name));
 
-- 
2.7.4



[PATCH 4/5] media: video-i2c: support changing frame interval

2018-09-17 Thread Akinobu Mita
AMG88xx has a register for setting frame rate 1 or 10 FPS.
This adds support changing frame interval.

Reference specifications:
https://docid81hrs3j1.cloudfront.net/medialibrary/2017/11/PANA-S-A0002141979-1.pdf

Cc: Matt Ranostay 
Cc: Sakari Ailus 
Cc: Hans Verkuil 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/video-i2c.c | 76 ---
 1 file changed, 64 insertions(+), 12 deletions(-)

diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index 90d389b..916f36e 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -52,6 +52,8 @@ struct video_i2c_data {
 
struct task_struct *kthread_vid_cap;
struct list_head vid_cap_active;
+
+   struct v4l2_fract frame_interval;
 };
 
 static const struct v4l2_fmtdesc amg88xx_format = {
@@ -74,8 +76,9 @@ struct video_i2c_chip {
const struct v4l2_fmtdesc *format;
const struct v4l2_frmsize_discrete *size;
 
-   /* max frames per second */
-   unsigned int max_fps;
+   /* available frame intervals */
+   const struct v4l2_fract *frame_intervals;
+   unsigned int num_frame_intervals;
 
/* pixel buffer size */
unsigned int buffer_size;
@@ -85,6 +88,9 @@ struct video_i2c_chip {
 
const struct regmap_config *regmap_config;
 
+   /* setup function */
+   int (*setup)(struct video_i2c_data *data);
+
/* xfer function */
int (*xfer)(struct video_i2c_data *data, char *buf);
 
@@ -98,6 +104,22 @@ static int amg88xx_xfer(struct video_i2c_data *data, char 
*buf)
data->chip->buffer_size);
 }
 
+#define AMG88XX_REG_FPSC   0x02
+#define AMG88XX_FPSC_1FPS  BIT(0)
+
+static int amg88xx_setup(struct video_i2c_data *data)
+{
+   unsigned int mask = AMG88XX_FPSC_1FPS;
+   unsigned int val;
+
+   if (data->frame_interval.numerator == data->frame_interval.denominator)
+   val = mask;
+   else
+   val = 0;
+
+   return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val);
+}
+
 #if IS_ENABLED(CONFIG_HWMON)
 
 static const u32 amg88xx_temp_config[] = {
@@ -172,14 +194,21 @@ static int amg88xx_hwmon_init(struct video_i2c_data *data)
 
 #define AMG88XX0
 
+static const struct v4l2_fract amg88xx_frame_intervals[] = {
+   { 1, 10 },
+   { 1, 1 },
+};
+
 static const struct video_i2c_chip video_i2c_chip[] = {
[AMG88XX] = {
.size   = _size,
.format = _format,
-   .max_fps= 10,
+   .frame_intervals= amg88xx_frame_intervals,
+   .num_frame_intervals= ARRAY_SIZE(amg88xx_frame_intervals),
.buffer_size= 128,
.bpp= 16,
.regmap_config  = _regmap_config,
+   .setup  = _setup,
.xfer   = _xfer,
.hwmon_init = amg88xx_hwmon_init,
},
@@ -244,7 +273,8 @@ static void buffer_queue(struct vb2_buffer *vb)
 static int video_i2c_thread_vid_cap(void *priv)
 {
struct video_i2c_data *data = priv;
-   unsigned int delay = msecs_to_jiffies(1000 / data->chip->max_fps);
+   unsigned int delay = mult_frac(HZ, data->frame_interval.numerator,
+  data->frame_interval.denominator);
 
set_freezable();
 
@@ -306,19 +336,26 @@ static void video_i2c_del_list(struct vb2_queue *vq, enum 
vb2_buffer_state state
 static int start_streaming(struct vb2_queue *vq, unsigned int count)
 {
struct video_i2c_data *data = vb2_get_drv_priv(vq);
+   int ret;
 
if (data->kthread_vid_cap)
return 0;
 
+   ret = data->chip->setup(data);
+   if (ret)
+   goto error_del_list;
+
data->sequence = 0;
data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data,
"%s-vid-cap", data->v4l2_dev.name);
-   if (!IS_ERR(data->kthread_vid_cap))
+   ret = PTR_ERR_OR_ZERO(data->kthread_vid_cap);
+   if (!ret)
return 0;
 
+error_del_list:
video_i2c_del_list(vq, VB2_BUF_STATE_QUEUED);
 
-   return PTR_ERR(data->kthread_vid_cap);
+   return ret;
 }
 
 static void stop_streaming(struct vb2_queue *vq)
@@ -425,15 +462,14 @@ static int video_i2c_enum_frameintervals(struct file 
*file, void *priv,
const struct video_i2c_data *data = video_drvdata(file);
const struct v4l2_frmsize_discrete *size = data->chip->size;
 
-   if (fe->index > 0)
+   if (fe->index >= data->chip->num_frame_intervals)
return -EINVAL;
 
if (fe->width != size->width || fe->height != size->height)
return -EINVAL;
 
fe->

Re: [RFC 1/1] v4l: samsung, ov9650: Rely on V4L2-set sub-device names

2018-08-31 Thread Akinobu Mita
2018年8月29日(水) 19:58 Sakari Ailus :
>
> v4l2_i2c_subdev_init() sets the name of the sub-devices (as well as
> entities) to what is fairly certainly known to be unique in the system,
> even if there were more devices of the same kind.
>
> These drivers (m5mols, noon010pc30, ov9650, s5c73m3, s5k4ecgx, s5k6aa) set
> the name to the name of the driver or the module while omitting the
> device's I²C address and bus, leaving the devices with a static name and
> effectively limiting the number of such devices in a media device to 1.
>
> Address this by using the name set by the V4L2 framework.
>
> Signed-off-by: Sakari Ailus 
> ---
> Hi,
>
> I'm a bit uncertain about this one. I discussed the matter with Hans and
> his view was this is a bug (I don't disagree), but this bug affects uAPI.
> Also these devices tend to be a few years old and might not see much use
> in newer devices, so why bother? The naming convention musn't be copied to
> newer drivers though.
>
> Any opinions?

The change for the ov9650 driver looks OK to me.

My media device setup script needs to be updated by this change, but
it is not a big deal.

Reviewed-by: Akinobu Mita 


Re: [PATCH -next v3 2/2] media: ov772x: use SCCB helpers

2018-07-10 Thread Akinobu Mita
2018年7月10日(火) 6:23 Sebastian Reichel :
>
> Hi,
>
> On Mon, Jul 09, 2018 at 06:14:43PM +0200, Wolfram Sang wrote:
> > >  static int ov772x_read(struct i2c_client *client, u8 addr)
> > >  {
> > > -   int ret;
> > > -   u8 val;
> > > -
> > > -   ret = i2c_master_send(client, , 1);
> > > -   if (ret < 0)
> > > -   return ret;
> > > -   ret = i2c_master_recv(client, , 1);
> > > -   if (ret < 0)
> > > -   return ret;
> > > -
> > > -   return val;
> > > +   return sccb_read_byte(client, addr);
> > >  }
> > >
> > >  static inline int ov772x_write(struct i2c_client *client, u8 addr, u8 
> > > value)
> > >  {
> > > -   return i2c_smbus_write_byte_data(client, addr, value);
> > > +   return sccb_write_byte(client, addr, value);
> > >  }
>
> Reviewed-by: Sebastian Reichel 
>
> > Minor nit: I'd rather drop these two functions and use the
> > sccb-accessors directly.
> >
> > However, I really like how this looks here: It is totally clear we are
> > doing SCCB and hide away all the details.
>
> I think it would be even better to introduce a SSCB regmap layer
> and use that.

I'm fine with introducing a SCCB regmap layer.

But do we need to provide both a SCCB regmap and these SCCB helpers?

If we have a regmap_init_sccb(), I feel these three SCCB helper functions
don't need to be exported anymore.


[PATCH -next v3 1/2] i2c: add SCCB helpers

2018-07-09 Thread Akinobu Mita
This adds Serial Camera Control Bus (SCCB) helpers (sccb_is_available,
sccb_read_byte, and sccb_write_byte) that are intended to be used by some
of Omnivision sensor drivers.

The ov772x driver is going to use these helpers.

It was previously only worked with the i2c controller drivers that support
I2C_FUNC_PROTOCOL_MANGLING, because the ov772x device doesn't support
repeated starts.  After commit 0b964d183cbf ("media: ov772x: allow i2c
controllers without I2C_FUNC_PROTOCOL_MANGLING"), reading ov772x register
is replaced with issuing two separated i2c messages in order to avoid
repeated start.  Using SCCB helpers hides the implementation detail.

Cc: Peter Rosin 
Cc: Sebastian Reichel 
Cc: Wolfram Sang 
Cc: Jacopo Mondi 
Cc: Laurent Pinchart 
Cc: Hans Verkuil 
Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 include/linux/i2c-sccb.h | 77 
 1 file changed, 77 insertions(+)
 create mode 100644 include/linux/i2c-sccb.h

diff --git a/include/linux/i2c-sccb.h b/include/linux/i2c-sccb.h
new file mode 100644
index 000..61dece9
--- /dev/null
+++ b/include/linux/i2c-sccb.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Serial Camera Control Bus (SCCB) helper functions
+ */
+
+#ifndef _LINUX_I2C_SCCB_H
+#define _LINUX_I2C_SCCB_H
+
+#include 
+
+/**
+ * sccb_is_available - Check if the adapter supports SCCB protocol
+ * @adap: I2C adapter
+ *
+ * Return true if the I2C adapter is capable of using SCCB helper functions,
+ * false otherwise.
+ */
+static inline bool sccb_is_available(struct i2c_adapter *adap)
+{
+   u32 needed_funcs = I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
+
+#if 0
+   /*
+* sccb_xfer not needed yet, since there is no driver support currently.
+* Just showing how it should be done if we ever need it.
+*/
+   if (adap->algo->sccb_xfer)
+   return true;
+#endif
+
+   return (i2c_get_functionality(adap) & needed_funcs) == needed_funcs;
+}
+
+/**
+ * sccb_read_byte - Read data from SCCB slave device
+ * @client: Handle to slave device
+ * @addr: Register to be read from
+ *
+ * This executes the 2-phase write transmission cycle that is followed by a
+ * 2-phase read transmission cycle, returning negative errno else a data byte
+ * received from the device.
+ */
+static inline int sccb_read_byte(struct i2c_client *client, u8 addr)
+{
+   int ret;
+   union i2c_smbus_data data;
+
+   i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
+
+   ret = __i2c_smbus_xfer(client->adapter, client->addr, client->flags,
+   I2C_SMBUS_WRITE, addr, I2C_SMBUS_BYTE, NULL);
+   if (ret < 0)
+   goto out;
+
+   ret = __i2c_smbus_xfer(client->adapter, client->addr, client->flags,
+   I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, );
+out:
+   i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
+
+   return ret < 0 ? ret : data.byte;
+}
+
+/**
+ * sccb_write_byte - Write data to SCCB slave device
+ * @client: Handle to slave device
+ * @addr: Register to write to
+ * @data: Value to be written
+ *
+ * This executes the SCCB 3-phase write transmission cycle, returning negative
+ * errno else zero on success.
+ */
+static inline int sccb_write_byte(struct i2c_client *client, u8 addr, u8 data)
+{
+   return i2c_smbus_write_byte_data(client, addr, data);
+}
+
+#endif /* _LINUX_I2C_SCCB_H */
-- 
2.7.4



[PATCH -next v3 2/2] media: ov772x: use SCCB helpers

2018-07-09 Thread Akinobu Mita
Convert ov772x register access to use SCCB helpers.

Cc: Peter Rosin 
Cc: Sebastian Reichel 
Cc: Wolfram Sang 
Cc: Jacopo Mondi 
Cc: Laurent Pinchart 
Cc: Hans Verkuil 
Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/ov772x.c | 20 +---
 1 file changed, 5 insertions(+), 15 deletions(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 7158c31..8a9a9ca 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -17,7 +17,7 @@
 #include 
 #include 
 #include 
-#include 
+#include 
 #include 
 #include 
 #include 
@@ -551,22 +551,12 @@ static struct ov772x_priv *to_ov772x(struct v4l2_subdev 
*sd)
 
 static int ov772x_read(struct i2c_client *client, u8 addr)
 {
-   int ret;
-   u8 val;
-
-   ret = i2c_master_send(client, , 1);
-   if (ret < 0)
-   return ret;
-   ret = i2c_master_recv(client, , 1);
-   if (ret < 0)
-   return ret;
-
-   return val;
+   return sccb_read_byte(client, addr);
 }
 
 static inline int ov772x_write(struct i2c_client *client, u8 addr, u8 value)
 {
-   return i2c_smbus_write_byte_data(client, addr, value);
+   return sccb_write_byte(client, addr, value);
 }
 
 static int ov772x_mask_set(struct i2c_client *client, u8  command, u8  mask,
@@ -1395,9 +1385,9 @@ static int ov772x_probe(struct i2c_client *client,
return -EINVAL;
}
 
-   if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+   if (!sccb_is_available(adapter)) {
dev_err(>dev,
-   "I2C-Adapter doesn't support SMBUS_BYTE_DATA\n");
+   "I2C-Adapter doesn't support SCCB\n");
return -EIO;
}
 
-- 
2.7.4



[PATCH -next v3 0/2] introduce SCCB helpers

2018-07-09 Thread Akinobu Mita
This patchset introduces Serial Camera Control Bus (SCCB) helper functions
and convert ov772x driver to use the helpers.

* v3
- Rewrite the helpers based on the code provided by Wolfram
- Convert ov772x driver to use SCCB helpers

 v2
- Convert all helpers into static inline functions, and remove C source
  and Kconfig option.
- Acquire i2c adapter lock while issuing two requests for sccb_read_byte

Akinobu Mita (2):
  i2c: add SCCB helpers
  media: ov772x: use SCCB helpers

 drivers/media/i2c/ov772x.c | 20 +++-
 include/linux/i2c-sccb.h   | 77 ++
 2 files changed, 82 insertions(+), 15 deletions(-)
 create mode 100644 include/linux/i2c-sccb.h

Cc: Peter Rosin 
Cc: Sebastian Reichel 
Cc: Wolfram Sang 
Cc: Jacopo Mondi 
Cc: Laurent Pinchart 
Cc: Hans Verkuil 
Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
-- 
2.7.4



[RFC PATCH v2] media: i2c: add SCCB helpers

2018-06-12 Thread Akinobu Mita
(This is 2nd version of SCCB helpers patch.  After 1st version was
submitted, I sent alternative patch titled "i2c: add I2C_M_FORCE_STOP".
But it wasn't accepted because it makes the I2C core code unreadable.
I couldn't find out a way to untangle it, so I returned to the original
approach.)

This adds Serial Camera Control Bus (SCCB) helper functions (sccb_read_byte
and sccb_write_byte) that are intended to be used by some of Omnivision
sensor drivers.

The ov772x driver is going to use these functions in order to make it work
with most i2c controllers.

As the ov772x device doesn't support repeated starts, this driver currently
requires I2C_FUNC_PROTOCOL_MANGLING that is not supported by many i2c
controller drivers.

With the sccb_read_byte() that issues two separated requests in order to
avoid repeated start, the driver doesn't require I2C_FUNC_PROTOCOL_MANGLING.

Cc: Sebastian Reichel 
Cc: Wolfram Sang 
Cc: Jacopo Mondi 
Cc: Laurent Pinchart 
Cc: Hans Verkuil 
Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
* v2
- Convert all helpers into static inline functions, and remove C source
  and Kconfig option.
- Acquire i2c adapter lock while issuing two requests for sccb_read_byte

 drivers/media/i2c/sccb.h | 74 
 1 file changed, 74 insertions(+)
 create mode 100644 drivers/media/i2c/sccb.h

diff --git a/drivers/media/i2c/sccb.h b/drivers/media/i2c/sccb.h
new file mode 100644
index 000..a531fdc
--- /dev/null
+++ b/drivers/media/i2c/sccb.h
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Serial Camera Control Bus (SCCB) helper functions
+ */
+
+#ifndef __SCCB_H__
+#define __SCCB_H__
+
+#include 
+
+/**
+ * sccb_read_byte - Read data from SCCB slave device
+ * @client: Handle to slave device
+ * @addr: Register to be read from
+ *
+ * This executes the 2-phase write transmission cycle that is followed by a
+ * 2-phase read transmission cycle, returning negative errno else a data byte
+ * received from the device.
+ */
+static inline int sccb_read_byte(struct i2c_client *client, u8 addr)
+{
+   u8 val;
+   struct i2c_msg msg[] = {
+   {
+   .addr = client->addr,
+   .len = 1,
+   .buf = ,
+   },
+   {
+   .addr = client->addr,
+   .flags = I2C_M_RD,
+   .len = 1,
+   .buf = ,
+   },
+   };
+   int ret;
+   int i;
+
+   i2c_lock_adapter(client->adapter);
+
+   /* Issue two separated requests in order to avoid repeated start */
+   for (i = 0; i < 2; i++) {
+   ret = __i2c_transfer(client->adapter, [i], 1);
+   if (ret != 1)
+   break;
+   }
+
+   i2c_unlock_adapter(client->adapter);
+
+   return i == 2 ? val : ret;
+}
+
+/**
+ * sccb_write_byte - Write data to SCCB slave device
+ * @client: Handle to slave device
+ * @addr: Register to write to
+ * @data: Value to be written
+ *
+ * This executes the SCCB 3-phase write transmission cycle, returning negative
+ * errno else zero on success.
+ */
+static inline int sccb_write_byte(struct i2c_client *client, u8 addr, u8 data)
+{
+   int ret;
+   unsigned char msgbuf[] = { addr, data };
+
+   ret = i2c_master_send(client, msgbuf, 2);
+   if (ret < 0)
+   return ret;
+
+   return 0;
+}
+
+#endif /* __SCCB_H__ */
-- 
2.7.4



[PATCH] media: s3c-camif: ignore -ENOIOCTLCMD from v4l2_subdev_call for s_power

2018-06-10 Thread Akinobu Mita
When the subdevice doesn't provide s_power core ops callback, the
v4l2_subdev_call for s_power returns -ENOIOCTLCMD.  If the subdevice
doesn't have the special handling for its power saving mode, the s_power
isn't required.  So -ENOIOCTLCMD from the v4l2_subdev_call should be
ignored.

Cc: Sylwester Nawrocki 
Cc: Hans Verkuil 
Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/platform/s3c-camif/camif-capture.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/media/platform/s3c-camif/camif-capture.c 
b/drivers/media/platform/s3c-camif/camif-capture.c
index 9ab8e7e..b1d9f38 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -117,6 +117,8 @@ static int sensor_set_power(struct camif_dev *camif, int on)
 
if (camif->sensor.power_count == !on)
err = v4l2_subdev_call(sensor->sd, core, s_power, on);
+   if (err == -ENOIOCTLCMD)
+   err = 0;
if (!err)
sensor->power_count += on ? 1 : -1;
 
-- 
2.7.4



[PATCH] media: soc_camera: ov772x: correct setting of banding filter

2018-06-10 Thread Akinobu Mita
The banding filter ON/OFF is controlled via bit 5 of COM8 register.  It
is attempted to be enabled in ov772x_set_params() by the following line.

ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1);

But this unexpectedly results disabling the banding filter, because the
mask and set bits are exclusive.

On the other hand, ov772x_s_ctrl() correctly sets the bit by:

ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF);

The same fix was already applied to non-soc_camera version of ov772x
driver in the commit commit a024ee14cd36 ("media: ov772x: correct setting
of banding filter")

Cc: Jacopo Mondi 
Cc: Laurent Pinchart 
Cc: Hans Verkuil 
Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/i2c/soc_camera/ov772x.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/i2c/soc_camera/ov772x.c 
b/drivers/media/i2c/soc_camera/ov772x.c
index 8063835..14377af 100644
--- a/drivers/media/i2c/soc_camera/ov772x.c
+++ b/drivers/media/i2c/soc_camera/ov772x.c
@@ -834,7 +834,7 @@ static int ov772x_set_params(struct ov772x_priv *priv,
 * set COM8
 */
if (priv->band_filter) {
-   ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1);
+   ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF);
if (!ret)
ret = ov772x_mask_set(client, BDBASE,
  0xff, 256 - priv->band_filter);
-- 
2.7.4



[PATCH] media: pxa_camera: ignore -ENOIOCTLCMD from v4l2_subdev_call for s_power

2018-06-03 Thread Akinobu Mita
When the subdevice doesn't provide s_power core ops callback, the
v4l2_subdev_call for s_power returns -ENOIOCTLCMD.  If the subdevice
doesn't have the special handling for its power saving mode, the s_power
isn't required.  So -ENOIOCTLCMD from the v4l2_subdev_call should be
ignored.

Actually the -ENOIOCTLCMD is ignored in this driver's suspend/resume,
but the others treat the -ENOIOCTLCMD as an error.

This prepares a wrapper function to ignore -ENOIOCTLCMD and replaces
all s_power calls with it.

This also adds warning message when s_power() is failed.

Cc: Hans Verkuil 
Cc: Sakari Ailus 
Cc: Mauro Carvalho Chehab 
Signed-off-by: Akinobu Mita 
---
 drivers/media/platform/pxa_camera.c | 35 +++
 1 file changed, 23 insertions(+), 12 deletions(-)

diff --git a/drivers/media/platform/pxa_camera.c 
b/drivers/media/platform/pxa_camera.c
index c792cb1..4d5a26b 100644
--- a/drivers/media/platform/pxa_camera.c
+++ b/drivers/media/platform/pxa_camera.c
@@ -2030,6 +2030,22 @@ static int pxac_vidioc_s_input(struct file *file, void 
*priv, unsigned int i)
return 0;
 }
 
+static int pxac_sensor_set_power(struct pxa_camera_dev *pcdev, int on)
+{
+   int ret;
+
+   ret = sensor_call(pcdev, core, s_power, on);
+   if (ret == -ENOIOCTLCMD)
+   ret = 0;
+   if (ret) {
+   dev_warn(pcdev_to_dev(pcdev),
+"Failed to put subdevice in %s mode: %d\n",
+on ? "normal operation" : "power saving", ret);
+   }
+
+   return ret;
+}
+
 static int pxac_fops_camera_open(struct file *filp)
 {
struct pxa_camera_dev *pcdev = video_drvdata(filp);
@@ -2043,7 +2059,7 @@ static int pxac_fops_camera_open(struct file *filp)
if (!v4l2_fh_is_singular_file(filp))
goto out;
 
-   ret = sensor_call(pcdev, core, s_power, 1);
+   ret = pxac_sensor_set_power(pcdev, 1);
if (ret)
v4l2_fh_release(filp);
 out:
@@ -2064,7 +2080,7 @@ static int pxac_fops_camera_release(struct file *filp)
ret = _vb2_fop_release(filp, NULL);
 
if (fh_singular)
-   ret = sensor_call(pcdev, core, s_power, 0);
+   ret = pxac_sensor_set_power(pcdev, 0);
 
mutex_unlock(>mlock);
 
@@ -2167,7 +2183,7 @@ static int pxa_camera_sensor_bound(struct 
v4l2_async_notifier *notifier,
pix->pixelformat = pcdev->current_fmt->host_fmt->fourcc;
v4l2_fill_mbus_format(mf, pix, pcdev->current_fmt->code);
 
-   err = sensor_call(pcdev, core, s_power, 1);
+   err = pxac_sensor_set_power(pcdev, 1);
if (err)
goto out;
 
@@ -2194,7 +2210,7 @@ static int pxa_camera_sensor_bound(struct 
v4l2_async_notifier *notifier,
}
 
 out_sensor_poweroff:
-   err = sensor_call(pcdev, core, s_power, 0);
+   err = pxac_sensor_set_power(pcdev, 0);
 out:
mutex_unlock(>mlock);
return err;
@@ -2249,11 +2265,8 @@ static int pxa_camera_suspend(struct device *dev)
pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3);
pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4);
 
-   if (pcdev->sensor) {
-   ret = sensor_call(pcdev, core, s_power, 0);
-   if (ret == -ENOIOCTLCMD)
-   ret = 0;
-   }
+   if (pcdev->sensor)
+   ret = pxac_sensor_set_power(pcdev, 0);
 
return ret;
 }
@@ -2270,9 +2283,7 @@ static int pxa_camera_resume(struct device *dev)
__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4);
 
if (pcdev->sensor) {
-   ret = sensor_call(pcdev, core, s_power, 1);
-   if (ret == -ENOIOCTLCMD)
-   ret = 0;
+   ret = pxac_sensor_set_power(pcdev, 1);
}
 
/* Restart frame capture if active buffer exists */
-- 
2.7.4



Re: [PATCH v2] media: pxa_camera: avoid duplicate s_power calls

2018-05-29 Thread Akinobu Mita
2018-05-29 15:17 GMT+09:00 Hans Verkuil :
> Hi Akinobu,
>
> On 05/27/2018 05:30 PM, Akinobu Mita wrote:
>> The open() operation for the pxa_camera driver always calls s_power()
>> operation to put its subdevice sensor in normal operation mode, and the
>> release() operation always call s_power() operation to put the subdevice
>> in power saving mode.
>>
>> This requires the subdevice sensor driver to keep track of its power
>> state in order to avoid putting the subdevice in power saving mode while
>> the device is still opened by some users.
>>
>> Many subdevice drivers handle it by the boilerplate code that increments
>> and decrements an internal counter in s_power() like below:
>>
>>   /*
>>* If the power count is modified from 0 to != 0 or from != 0 to 0,
>>* update the power state.
>>*/
>>   if (sensor->power_count == !on) {
>>   ret = ov5640_set_power(sensor, !!on);
>>   if (ret)
>>   goto out;
>>   }
>>
>>   /* Update the power count. */
>>   sensor->power_count += on ? 1 : -1;
>>
>> However, some subdevice drivers don't handle it and may cause a problem
>> with the pxa_camera driver if the video device is opened by more than
>> two users at the same time.
>>
>> Instead of propagating the boilerplate code for each subdevice driver
>> that implement s_power, this introduces an trick that many V4L2 drivers
>> are using with v4l2_fh_is_singular_file().
>>
>> Cc: Sakari Ailus 
>> Cc: Mauro Carvalho Chehab 
>> Signed-off-by: Akinobu Mita 
>> ---
>> * v2
>> - Print warning message when s_power() is failed. (not printing warning
>>   when _vb2_fop_release() is failed as it always returns zero for now)
>
> Please note that v1 has already been merged, so if you can make a v3 rebased
> on top of the latest media_tree master branch, then I'll queue that up for
> 4.18.

OK.  There are several calls to s_power in this driver, so I'll make
a patch to add a wrapper function that prints warning message and
replace s_power calls with it.

I realized that s_power calls in suspend/resume ignore -ENOIOCTLCMD
error and other s_power calls also should ignore it.  So I'll include
the check in the wrapper function.


[PATCH v2] media: pxa_camera: avoid duplicate s_power calls

2018-05-27 Thread Akinobu Mita
The open() operation for the pxa_camera driver always calls s_power()
operation to put its subdevice sensor in normal operation mode, and the
release() operation always call s_power() operation to put the subdevice
in power saving mode.

This requires the subdevice sensor driver to keep track of its power
state in order to avoid putting the subdevice in power saving mode while
the device is still opened by some users.

Many subdevice drivers handle it by the boilerplate code that increments
and decrements an internal counter in s_power() like below:

/*
 * If the power count is modified from 0 to != 0 or from != 0 to 0,
 * update the power state.
 */
if (sensor->power_count == !on) {
ret = ov5640_set_power(sensor, !!on);
if (ret)
goto out;
}

/* Update the power count. */
sensor->power_count += on ? 1 : -1;

However, some subdevice drivers don't handle it and may cause a problem
with the pxa_camera driver if the video device is opened by more than
two users at the same time.

Instead of propagating the boilerplate code for each subdevice driver
that implement s_power, this introduces an trick that many V4L2 drivers
are using with v4l2_fh_is_singular_file().

Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@kernel.org>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v2
- Print warning message when s_power() is failed. (not printing warning
  when _vb2_fop_release() is failed as it always returns zero for now)

 drivers/media/platform/pxa_camera.c | 23 ++-
 1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/drivers/media/platform/pxa_camera.c 
b/drivers/media/platform/pxa_camera.c
index c71a007..a35f461 100644
--- a/drivers/media/platform/pxa_camera.c
+++ b/drivers/media/platform/pxa_camera.c
@@ -2040,6 +2040,9 @@ static int pxac_fops_camera_open(struct file *filp)
if (ret < 0)
goto out;
 
+   if (!v4l2_fh_is_singular_file(filp))
+   goto out;
+
ret = sensor_call(pcdev, core, s_power, 1);
if (ret)
v4l2_fh_release(filp);
@@ -2052,13 +2055,23 @@ static int pxac_fops_camera_release(struct file *filp)
 {
struct pxa_camera_dev *pcdev = video_drvdata(filp);
int ret;
-
-   ret = vb2_fop_release(filp);
-   if (ret < 0)
-   return ret;
+   bool fh_singular;
 
mutex_lock(>mlock);
-   ret = sensor_call(pcdev, core, s_power, 0);
+
+   fh_singular = v4l2_fh_is_singular_file(filp);
+
+   ret = _vb2_fop_release(filp, NULL);
+
+   if (fh_singular) {
+   ret = sensor_call(pcdev, core, s_power, 0);
+   if (ret) {
+   dev_warn(pcdev_to_dev(pcdev),
+"Failed to put subdevice in power saving mode: 
%d\n",
+ret);
+   }
+   }
+
mutex_unlock(>mlock);
 
return ret;
-- 
2.7.4



Re: [PATCH] media: pxa_camera: avoid duplicate s_power calls

2018-05-24 Thread Akinobu Mita
2018-05-22 22:59 GMT+09:00 Sakari Ailus <sakari.ai...@linux.intel.com>:
> Dear Mita-san,
>
> On Mon, May 21, 2018 at 12:40:38AM +0900, Akinobu Mita wrote:
>> The open() operation for the pxa_camera driver always calls s_power()
>> operation to put its subdevice sensor in normal operation mode, and the
>> release() operation always call s_power() operation to put the subdevice
>> in power saving mode.
>>
>> This requires the subdevice sensor driver to keep track of its power
>> state in order to avoid putting the subdevice in power saving mode while
>> the device is still opened by some users.
>>
>> Many subdevice drivers handle it by the boilerplate code that increments
>> and decrements an internal counter in s_power() like below:
>>
>>   /*
>>* If the power count is modified from 0 to != 0 or from != 0 to 0,
>>* update the power state.
>>*/
>>   if (sensor->power_count == !on) {
>>   ret = ov5640_set_power(sensor, !!on);
>>   if (ret)
>>   goto out;
>>   }
>>
>>   /* Update the power count. */
>>   sensor->power_count += on ? 1 : -1;
>>
>> However, some subdevice drivers don't handle it and may cause a problem
>> with the pxa_camera driver if the video device is opened by more than
>> two users at the same time.
>>
>> Instead of propagating the boilerplate code for each subdevice driver
>> that implement s_power, this introduces an trick that many V4L2 drivers
>> are using with v4l2_fh_is_singular_file().
>
> I'd rather like that the sub-device drivers would move to use runtime PM
> instead of depending on the s_power() callback. It's much cleaner that way.

Sounds good.
I'll look into whether some sensor drivers can be converted to use it.

> It's not a near-term solution though. The approach seems fine, please see
> comments below though.
>
>>
>> Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
>> Cc: Mauro Carvalho Chehab <mche...@kernel.org>
>> Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
>> ---
>>  drivers/media/platform/pxa_camera.c | 17 -
>>  1 file changed, 12 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/media/platform/pxa_camera.c 
>> b/drivers/media/platform/pxa_camera.c
>> index c71a007..c792cb1 100644
>> --- a/drivers/media/platform/pxa_camera.c
>> +++ b/drivers/media/platform/pxa_camera.c
>> @@ -2040,6 +2040,9 @@ static int pxac_fops_camera_open(struct file *filp)
>>   if (ret < 0)
>>   goto out;
>>
>> + if (!v4l2_fh_is_singular_file(filp))
>> + goto out;
>> +
>>   ret = sensor_call(pcdev, core, s_power, 1);
>>   if (ret)
>>   v4l2_fh_release(filp);
>> @@ -2052,13 +2055,17 @@ static int pxac_fops_camera_release(struct file 
>> *filp)
>>  {
>>   struct pxa_camera_dev *pcdev = video_drvdata(filp);
>>   int ret;
>> -
>> - ret = vb2_fop_release(filp);
>> - if (ret < 0)
>> - return ret;
>> + bool fh_singular;
>>
>>   mutex_lock(>mlock);
>> - ret = sensor_call(pcdev, core, s_power, 0);
>> +
>> + fh_singular = v4l2_fh_is_singular_file(filp);
>> +
>> + ret = _vb2_fop_release(filp, NULL);
>
> I'm not sure whether using the return value to return an error from release
> is really useful. If you want to use it, I'd shout loud instead.

What is the best way to handle these errors in release?

AFAICS, vb2_fop_release() always returns zero for now and most platform
drivers don't use return value from s_power() calling with on == 0.

So ignoring both of vb2_fop_release error and s_power error makes sense?

>> +
>> + if (fh_singular)
>
> ret assigned previously is overwritten here without checking.
>
>> + ret = sensor_call(pcdev, core, s_power, 0);
>> +
>>   mutex_unlock(>mlock);
>>
>>   return ret;
>
> --
> Sakari Ailus
> sakari.ai...@linux.intel.com


[PATCH] media: pxa_camera: avoid duplicate s_power calls

2018-05-20 Thread Akinobu Mita
The open() operation for the pxa_camera driver always calls s_power()
operation to put its subdevice sensor in normal operation mode, and the
release() operation always call s_power() operation to put the subdevice
in power saving mode.

This requires the subdevice sensor driver to keep track of its power
state in order to avoid putting the subdevice in power saving mode while
the device is still opened by some users.

Many subdevice drivers handle it by the boilerplate code that increments
and decrements an internal counter in s_power() like below:

/*
 * If the power count is modified from 0 to != 0 or from != 0 to 0,
 * update the power state.
 */
if (sensor->power_count == !on) {
ret = ov5640_set_power(sensor, !!on);
if (ret)
goto out;
}

/* Update the power count. */
sensor->power_count += on ? 1 : -1;

However, some subdevice drivers don't handle it and may cause a problem
with the pxa_camera driver if the video device is opened by more than
two users at the same time.

Instead of propagating the boilerplate code for each subdevice driver
that implement s_power, this introduces an trick that many V4L2 drivers
are using with v4l2_fh_is_singular_file().

Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@kernel.org>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
 drivers/media/platform/pxa_camera.c | 17 -
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/drivers/media/platform/pxa_camera.c 
b/drivers/media/platform/pxa_camera.c
index c71a007..c792cb1 100644
--- a/drivers/media/platform/pxa_camera.c
+++ b/drivers/media/platform/pxa_camera.c
@@ -2040,6 +2040,9 @@ static int pxac_fops_camera_open(struct file *filp)
if (ret < 0)
goto out;
 
+   if (!v4l2_fh_is_singular_file(filp))
+   goto out;
+
ret = sensor_call(pcdev, core, s_power, 1);
if (ret)
v4l2_fh_release(filp);
@@ -2052,13 +2055,17 @@ static int pxac_fops_camera_release(struct file *filp)
 {
struct pxa_camera_dev *pcdev = video_drvdata(filp);
int ret;
-
-   ret = vb2_fop_release(filp);
-   if (ret < 0)
-   return ret;
+   bool fh_singular;
 
mutex_lock(>mlock);
-   ret = sensor_call(pcdev, core, s_power, 0);
+
+   fh_singular = v4l2_fh_is_singular_file(filp);
+
+   ret = _vb2_fop_release(filp, NULL);
+
+   if (fh_singular)
+   ret = sensor_call(pcdev, core, s_power, 0);
+
mutex_unlock(>mlock);
 
return ret;
-- 
2.7.4



Re: [RFC PATCH] media: i2c: add SCCB helpers

2018-05-09 Thread Akinobu Mita
2018-05-05 23:51 GMT+09:00 Mauro Carvalho Chehab <mchehab+sams...@kernel.org>:
> Em Fri, 27 Apr 2018 01:13:32 +0900
> Akinobu Mita <akinobu.m...@gmail.com> escreveu:
>
>> (This patch is in prototype stage)
>>
>> This adds SCCB helper functions (sccb_read_byte and sccb_write_byte) that
>> are intended to be used by some of Omnivision sensor drivers.
>
> What do you mean by "SCCB"?

Serial Camera Control Bus (SCCB).  I'll write SCCB and the non
abbreviation together in the comment block and commit log.

>>
>> The ov772x driver is going to use these functions in order to make it work
>> with most i2c controllers.
>>
>> As the ov772x device doesn't support repeated starts, this driver currently
>> requires I2C_FUNC_PROTOCOL_MANGLING that is not supported by many i2c
>> controller drivers.
>>
>> With the sccb_read_byte() that issues two separated requests in order to
>> avoid repeated start, the driver doesn't require I2C_FUNC_PROTOCOL_MANGLING.
>>
>> Cc: Wolfram Sang <w...@the-dreams.de>
>> Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
>> Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
>> Cc: Hans Verkuil <hans.verk...@cisco.com>
>> Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
>> Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
>> Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
>> ---
>>  drivers/media/i2c/Kconfig  |  4 
>>  drivers/media/i2c/Makefile |  1 +
>>  drivers/media/i2c/sccb.c   | 35 +++
>>  drivers/media/i2c/sccb.h   | 14 ++
>>  4 files changed, 54 insertions(+)
>>  create mode 100644 drivers/media/i2c/sccb.c
>>  create mode 100644 drivers/media/i2c/sccb.h
>>
>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>> index 541f0d28..19e5601 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -569,6 +569,9 @@ config VIDEO_THS8200
>>
>>  comment "Camera sensor devices"
>>
>> +config SCCB
>> + bool
>> +
>>  config VIDEO_APTINA_PLL
>>   tristate
>>
>> @@ -692,6 +695,7 @@ config VIDEO_OV772X
>>   tristate "OmniVision OV772x sensor support"
>>   depends on I2C && VIDEO_V4L2
>>   depends on MEDIA_CAMERA_SUPPORT
>> + select SCCB
>>   ---help---
>> This is a Video4Linux2 sensor-level driver for the OmniVision
>> OV772x camera.
>> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
>> index ea34aee..82fbd78 100644
>> --- a/drivers/media/i2c/Makefile
>> +++ b/drivers/media/i2c/Makefile
>> @@ -62,6 +62,7 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o
>>  obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o
>>  obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
>>  obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
>> +obj-$(CONFIG_SCCB) += sccb.o
>>  obj-$(CONFIG_VIDEO_OV2640) += ov2640.o
>>  obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
>>  obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
>> diff --git a/drivers/media/i2c/sccb.c b/drivers/media/i2c/sccb.c
>> new file mode 100644
>> index 000..80a3fb7
>> --- /dev/null
>> +++ b/drivers/media/i2c/sccb.c
>> @@ -0,0 +1,35 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +#include 
>> +
>> +int sccb_read_byte(struct i2c_client *client, u8 addr)
>> +{
>> + int ret;
>> + u8 val;
>> +
>> + /* Issue two separated requests in order to avoid repeated start */
>> +
>> + ret = i2c_master_send(client, , 1);
>> + if (ret < 0)
>> + return ret;
>> +
>> + ret = i2c_master_recv(client, , 1);
>> + if (ret < 0)
>> + return ret;
>
> Handling it this way is a very bad idea, as you may have an operation
> between those two, as you're locking/unlocking for each i2c_master
> call, e. g. the code should be, instead:
>
> i2c_lock_adapter();
> __i2c_transfer(); /* Send */
> __i2c_transfer(); /* Receive */
> i2c_unlock_adapter();
>
> Also, if the problem is just due to I2C not supporting REPEAT, IMHO,
> the best would be to add some IRC flag to indicate that.

I sent a patch using this approach.

> Btw, this is not the first device that doesn't support repeats.
> A good hint of drivers with similar issues is:
>
> $ git grep i2c_lock_adapter drivers/media/
> drivers/media/dvb-frontends/af9013.c:   
> i2c_lock_adapter(client->adapter);
> drivers/media/dvb-frontends/af9013.c:   
> i2c_lock_adapter(client->adapter);
> drivers/media/dvb-frontends/drxk_hard.c:i2c_lock_adapter(state->i2c);
> drivers/media/dvb-frontends/rtl2830.c:  i2c_lock_adapter(client->adapter);
> drivers/media/dvb-frontends/rtl2830.c:  i2c_lock_adapter(client->adapter);
> drivers/media/dvb-frontends/rtl2830.c:  i2c_lock_adapter(client->adapter);
> drivers/media/dvb-frontends/tda1004x.c: i2c_lock_adapter(state->i2c);
> drivers/media/tuners/tda18271-common.c: 
> i2c_lock_adapter(priv->i2c_props.adap);
> drivers/media/tuners/tda18271-common.c: 
> i2c_lock_adapter(priv->i2c_props.adap);
>
> Regards,
> Mauro


[RFC PATCH] i2c: add I2C_M_FORCE_STOP

2018-05-09 Thread Akinobu Mita
This adds a new I2C_M_FORCE_STOP flag that forces a stop condition after
the message in a combined transaction.

This flag is intended to be used by the devices that don't support
repeated starts like SCCB (Serial Camera Control Bus) devices.

Here is an example usage for ov772x driver that needs to issue two
separated I2C messages as the ov772x device doesn't support repeated
starts.

static int ov772x_read(struct i2c_client *client, u8 addr)
{
u8 val;
struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = I2C_M_FORCE_STOP,
.len = 1,
.buf = ,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = 1,
.buf = ,
},
};
int ret;

ret = i2c_transfer(client->adapter, msg, 2);
if (ret != 2)
return (ret < 0) ? ret : -EIO;

return val;
}

This is another approach based on Mauro's advise for the initial attempt
(http://patchwork.ozlabs.org/patch/905192/).

Cc: Sebastian Reichel <sebastian.reic...@collabora.co.uk>
Cc: Wolfram Sang <w...@the-dreams.de>
Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
 drivers/i2c/i2c-core-base.c | 46 ++---
 include/uapi/linux/i2c.h|  1 +
 2 files changed, 36 insertions(+), 11 deletions(-)

diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 1ba40bb..6b73484 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -1828,6 +1828,25 @@ static int i2c_check_for_quirks(struct i2c_adapter 
*adap, struct i2c_msg *msgs,
return 0;
 }
 
+static int i2c_transfer_nolock(struct i2c_adapter *adap, struct i2c_msg *msgs,
+  int num)
+{
+   unsigned long orig_jiffies;
+   int ret, try;
+
+   /* Retry automatically on arbitration loss */
+   orig_jiffies = jiffies;
+   for (ret = 0, try = 0; try <= adap->retries; try++) {
+   ret = adap->algo->master_xfer(adap, msgs, num);
+   if (ret != -EAGAIN)
+   break;
+   if (time_after(jiffies, orig_jiffies + adap->timeout))
+   break;
+   }
+
+   return ret;
+}
+
 /**
  * __i2c_transfer - unlocked flavor of i2c_transfer
  * @adap: Handle to I2C bus
@@ -1842,8 +1861,8 @@ static int i2c_check_for_quirks(struct i2c_adapter *adap, 
struct i2c_msg *msgs,
  */
 int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 {
-   unsigned long orig_jiffies;
-   int ret, try;
+   int ret;
+   int i, n;
 
if (WARN_ON(!msgs || num < 1))
return -EINVAL;
@@ -1857,7 +1876,6 @@ int __i2c_transfer(struct i2c_adapter *adap, struct 
i2c_msg *msgs, int num)
 * being executed when not needed.
 */
if (static_branch_unlikely(_trace_msg_key)) {
-   int i;
for (i = 0; i < num; i++)
if (msgs[i].flags & I2C_M_RD)
trace_i2c_read(adap, [i], i);
@@ -1865,18 +1883,24 @@ int __i2c_transfer(struct i2c_adapter *adap, struct 
i2c_msg *msgs, int num)
trace_i2c_write(adap, [i], i);
}
 
-   /* Retry automatically on arbitration loss */
-   orig_jiffies = jiffies;
-   for (ret = 0, try = 0; try <= adap->retries; try++) {
-   ret = adap->algo->master_xfer(adap, msgs, num);
-   if (ret != -EAGAIN)
-   break;
-   if (time_after(jiffies, orig_jiffies + adap->timeout))
+   for (i = 0; i < num; i += n) {
+   for (n = 0; i + n < num; n++) {
+   if (msgs[i + n].flags & I2C_M_FORCE_STOP) {
+   n++;
+   break;
+   }
+   }
+
+   ret = i2c_transfer_nolock(adap, [i], n);
+   if (ret != n) {
+   if (i > 0)
+   ret = (ret < 0) ? i : i + ret;
break;
+   }
+   ret = i + n;
}
 
if (static_branch_unlikely(_trace_msg_key)) {
-   int i;
for (i = 0; i < ret; i++)
if (msgs[i].flags & I2C_M_RD)
trace_i2c_reply(adap, [i], i);
diff --git a/include/uapi/linux/i2c.h b/include/uapi/linux/i2c.h
index f71a17

Re: [PATCH v5 08/14] media: ov772x: support device tree probing

2018-05-07 Thread Akinobu Mita
2018-05-07 18:26 GMT+09:00 Sakari Ailus <sakari.ai...@linux.intel.com>:
> Dear Mita-san,
>
> On Sun, May 06, 2018 at 11:19:23PM +0900, Akinobu Mita wrote:
>> The ov772x driver currently only supports legacy platform data probe.
>> This change enables device tree probing.
>>
>> Note that the platform data probe can select auto or manual edge control
>> mode, but the device tree probling can only select auto edge control mode
>> for now.
>>
>> Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
>> Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
>> Cc: Hans Verkuil <hans.verk...@cisco.com>
>> Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
>> Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
>> Reviewed-by: Jacopo Mondi <jac...@jmondi.org>
>> Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
>> ---
>> * v5
>> - Remove unnecessary space
>>
>>  drivers/media/i2c/ov772x.c | 64 
>> --
>>  1 file changed, 45 insertions(+), 19 deletions(-)
>>
>> diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
>> index f939e28..2b02411 100644
>> --- a/drivers/media/i2c/ov772x.c
>> +++ b/drivers/media/i2c/ov772x.c
>> @@ -749,13 +749,13 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
>>   case V4L2_CID_VFLIP:
>>   val = ctrl->val ? VFLIP_IMG : 0x00;
>>   priv->flag_vflip = ctrl->val;
>> - if (priv->info->flags & OV772X_FLAG_VFLIP)
>> + if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP))
>>   val ^= VFLIP_IMG;
>>   return ov772x_mask_set(client, COM3, VFLIP_IMG, val);
>>   case V4L2_CID_HFLIP:
>>   val = ctrl->val ? HFLIP_IMG : 0x00;
>>   priv->flag_hflip = ctrl->val;
>> - if (priv->info->flags & OV772X_FLAG_HFLIP)
>> + if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP))
>>   val ^= HFLIP_IMG;
>>   return ov772x_mask_set(client, COM3, HFLIP_IMG, val);
>>   case V4L2_CID_BAND_STOP_FILTER:
>> @@ -914,19 +914,14 @@ static void ov772x_select_params(const struct 
>> v4l2_mbus_framefmt *mf,
>>   *win = ov772x_select_win(mf->width, mf->height);
>>  }
>>
>> -static int ov772x_set_params(struct ov772x_priv *priv,
>> -  const struct ov772x_color_format *cfmt,
>> -  const struct ov772x_win_size *win)
>> +static int ov772x_edgectrl(struct ov772x_priv *priv)
>>  {
>>   struct i2c_client *client = v4l2_get_subdevdata(>subdev);
>> - struct v4l2_fract tpf;
>>   int ret;
>> - u8  val;
>>
>> - /* Reset hardware. */
>> - ov772x_reset(client);
>> + if (!priv->info)
>> + return 0;
>>
>> - /* Edge Ctrl. */
>>   if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) {
>>   /*
>>* Manual Edge Control Mode.
>> @@ -937,19 +932,19 @@ static int ov772x_set_params(struct ov772x_priv *priv,
>>
>>   ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00);
>>   if (ret < 0)
>> - goto ov772x_set_fmt_error;
>> + return ret;
>>
>>   ret = ov772x_mask_set(client,
>> EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK,
>> priv->info->edgectrl.threshold);
>>   if (ret < 0)
>> - goto ov772x_set_fmt_error;
>> + return ret;
>>
>>   ret = ov772x_mask_set(client,
>> EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK,
>> priv->info->edgectrl.strength);
>>   if (ret < 0)
>> - goto ov772x_set_fmt_error;
>> + return ret;
>>
>>   } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) {
>>   /*
>> @@ -961,15 +956,35 @@ static int ov772x_set_params(struct ov772x_priv *priv,
>> EDGE_UPPER, OV772X_EDGE_UPPER_MASK,
>> priv->info->edgectrl.upper);
>>   if (ret < 0)
>> - goto ov772x_set_fm

[PATCH v5 09/14] media: ov772x: handle nested s_power() calls

2018-05-06 Thread Akinobu Mita
Depending on the v4l2 driver, calling s_power() could be nested.  So the
actual transitions between power saving mode and normal operation mode
should only happen at the first power on and the last power off.

This adds an s_power() nesting counter and updates the power state if the
counter is modified from 0 to != 0 or from != 0 to 0.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Reviewed-by: Jacopo Mondi <jac...@jmondi.org>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v5
- No changes

 drivers/media/i2c/ov772x.c | 34 ++
 1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 2b02411..6c0c792 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -424,6 +424,9 @@ struct ov772x_priv {
/* band_filter = COM8[5] ? 256 - BDBASE : 0 */
unsigned shortband_filter;
unsigned int  fps;
+   /* lock to protect power_count */
+   struct mutex  lock;
+   int   power_count;
 #ifdef CONFIG_MEDIA_CONTROLLER
struct media_pad pad;
 #endif
@@ -871,9 +874,26 @@ static int ov772x_power_off(struct ov772x_priv *priv)
 static int ov772x_s_power(struct v4l2_subdev *sd, int on)
 {
struct ov772x_priv *priv = to_ov772x(sd);
+   int ret = 0;
+
+   mutex_lock(>lock);
+
+   /* If the power count is modified from 0 to != 0 or from != 0 to 0,
+* update the power state.
+*/
+   if (priv->power_count == !on)
+   ret = on ? ov772x_power_on(priv) : ov772x_power_off(priv);
+
+   if (!ret) {
+   /* Update the power count. */
+   priv->power_count += on ? 1 : -1;
+   WARN(priv->power_count < 0, "Unbalanced power count\n");
+   WARN(priv->power_count > 1, "Duplicated s_power call\n");
+   }
+
+   mutex_unlock(>lock);
 
-   return on ? ov772x_power_on(priv) :
-   ov772x_power_off(priv);
+   return ret;
 }
 
 static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height)
@@ -1303,6 +1323,7 @@ static int ov772x_probe(struct i2c_client *client,
return -ENOMEM;
 
priv->info = client->dev.platform_data;
+   mutex_init(>lock);
 
v4l2_i2c_subdev_init(>subdev, client, _subdev_ops);
v4l2_ctrl_handler_init(>hdl, 3);
@@ -1313,8 +1334,10 @@ static int ov772x_probe(struct i2c_client *client,
v4l2_ctrl_new_std(>hdl, _ctrl_ops,
  V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0);
priv->subdev.ctrl_handler = >hdl;
-   if (priv->hdl.error)
-   return priv->hdl.error;
+   if (priv->hdl.error) {
+   ret = priv->hdl.error;
+   goto error_mutex_destroy;
+   }
 
priv->clk = clk_get(>dev, NULL);
if (IS_ERR(priv->clk)) {
@@ -1362,6 +1385,8 @@ static int ov772x_probe(struct i2c_client *client,
clk_put(priv->clk);
 error_ctrl_free:
v4l2_ctrl_handler_free(>hdl);
+error_mutex_destroy:
+   mutex_destroy(>lock);
 
return ret;
 }
@@ -1376,6 +1401,7 @@ static int ov772x_remove(struct i2c_client *client)
gpiod_put(priv->pwdn_gpio);
v4l2_async_unregister_subdev(>subdev);
v4l2_ctrl_handler_free(>hdl);
+   mutex_destroy(>lock);
 
return 0;
 }
-- 
2.7.4



[PATCH v5 10/14] media: ov772x: reconstruct s_frame_interval()

2018-05-06 Thread Akinobu Mita
This splits the s_frame_interval() in subdev video ops into selecting the
frame interval and setting up the registers.

This is a preparatory change to avoid accessing registers under power
saving mode.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v5
- Align arguments to open parenthesis
- Sort variable declarations

 drivers/media/i2c/ov772x.c | 56 +-
 1 file changed, 35 insertions(+), 21 deletions(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 6c0c792..92ad13f 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -617,25 +617,16 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int 
enable)
return 0;
 }
 
-static int ov772x_set_frame_rate(struct ov772x_priv *priv,
-struct v4l2_fract *tpf,
-const struct ov772x_color_format *cfmt,
-const struct ov772x_win_size *win)
+static unsigned int ov772x_select_fps(struct ov772x_priv *priv,
+ struct v4l2_fract *tpf)
 {
-   struct i2c_client *client = v4l2_get_subdevdata(>subdev);
-   unsigned long fin = clk_get_rate(priv->clk);
unsigned int fps = tpf->numerator ?
   tpf->denominator / tpf->numerator :
   tpf->denominator;
unsigned int best_diff;
-   unsigned int fsize;
-   unsigned int pclk;
unsigned int diff;
unsigned int idx;
unsigned int i;
-   u8 clkrc = 0;
-   u8 com4 = 0;
-   int ret;
 
/* Approximate to the closest supported frame interval. */
best_diff = ~0L;
@@ -646,7 +637,25 @@ static int ov772x_set_frame_rate(struct ov772x_priv *priv,
best_diff = diff;
}
}
-   fps = ov772x_frame_intervals[idx];
+
+   return ov772x_frame_intervals[idx];
+}
+
+static int ov772x_set_frame_rate(struct ov772x_priv *priv,
+unsigned int fps,
+const struct ov772x_color_format *cfmt,
+const struct ov772x_win_size *win)
+{
+   struct i2c_client *client = v4l2_get_subdevdata(>subdev);
+   unsigned long fin = clk_get_rate(priv->clk);
+   unsigned int best_diff;
+   unsigned int fsize;
+   unsigned int pclk;
+   unsigned int diff;
+   unsigned int i;
+   u8 clkrc = 0;
+   u8 com4 = 0;
+   int ret;
 
/* Use image size (with blankings) to calculate desired pixel clock. */
switch (cfmt->com7 & OFMT_MASK) {
@@ -711,10 +720,6 @@ static int ov772x_set_frame_rate(struct ov772x_priv *priv,
if (ret < 0)
return ret;
 
-   tpf->numerator = 1;
-   tpf->denominator = fps;
-   priv->fps = tpf->denominator;
-
return 0;
 }
 
@@ -735,8 +740,20 @@ static int ov772x_s_frame_interval(struct v4l2_subdev *sd,
 {
struct ov772x_priv *priv = to_ov772x(sd);
struct v4l2_fract *tpf = >interval;
+   unsigned int fps;
+   int ret;
+
+   fps = ov772x_select_fps(priv, tpf);
+
+   ret = ov772x_set_frame_rate(priv, fps, priv->cfmt, priv->win);
+   if (ret)
+   return ret;
 
-   return ov772x_set_frame_rate(priv, tpf, priv->cfmt, priv->win);
+   tpf->numerator = 1;
+   tpf->denominator = fps;
+   priv->fps = fps;
+
+   return 0;
 }
 
 static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -993,7 +1010,6 @@ static int ov772x_set_params(struct ov772x_priv *priv,
 const struct ov772x_win_size *win)
 {
struct i2c_client *client = v4l2_get_subdevdata(>subdev);
-   struct v4l2_fract tpf;
int ret;
u8  val;
 
@@ -1075,9 +1091,7 @@ static int ov772x_set_params(struct ov772x_priv *priv,
goto ov772x_set_fmt_error;
 
/* COM4, CLKRC: Set pixel clock and framerate. */
-   tpf.numerator = 1;
-   tpf.denominator = priv->fps;
-   ret = ov772x_set_frame_rate(priv, , cfmt, win);
+   ret = ov772x_set_frame_rate(priv, priv->fps, cfmt, win);
if (ret < 0)
goto ov772x_set_fmt_error;
 
-- 
2.7.4



[PATCH v5 08/14] media: ov772x: support device tree probing

2018-05-06 Thread Akinobu Mita
The ov772x driver currently only supports legacy platform data probe.
This change enables device tree probing.

Note that the platform data probe can select auto or manual edge control
mode, but the device tree probling can only select auto edge control mode
for now.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Reviewed-by: Jacopo Mondi <jac...@jmondi.org>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v5
- Remove unnecessary space

 drivers/media/i2c/ov772x.c | 64 --
 1 file changed, 45 insertions(+), 19 deletions(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index f939e28..2b02411 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -749,13 +749,13 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_VFLIP:
val = ctrl->val ? VFLIP_IMG : 0x00;
priv->flag_vflip = ctrl->val;
-   if (priv->info->flags & OV772X_FLAG_VFLIP)
+   if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP))
val ^= VFLIP_IMG;
return ov772x_mask_set(client, COM3, VFLIP_IMG, val);
case V4L2_CID_HFLIP:
val = ctrl->val ? HFLIP_IMG : 0x00;
priv->flag_hflip = ctrl->val;
-   if (priv->info->flags & OV772X_FLAG_HFLIP)
+   if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP))
val ^= HFLIP_IMG;
return ov772x_mask_set(client, COM3, HFLIP_IMG, val);
case V4L2_CID_BAND_STOP_FILTER:
@@ -914,19 +914,14 @@ static void ov772x_select_params(const struct 
v4l2_mbus_framefmt *mf,
*win = ov772x_select_win(mf->width, mf->height);
 }
 
-static int ov772x_set_params(struct ov772x_priv *priv,
-const struct ov772x_color_format *cfmt,
-const struct ov772x_win_size *win)
+static int ov772x_edgectrl(struct ov772x_priv *priv)
 {
struct i2c_client *client = v4l2_get_subdevdata(>subdev);
-   struct v4l2_fract tpf;
int ret;
-   u8  val;
 
-   /* Reset hardware. */
-   ov772x_reset(client);
+   if (!priv->info)
+   return 0;
 
-   /* Edge Ctrl. */
if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) {
/*
 * Manual Edge Control Mode.
@@ -937,19 +932,19 @@ static int ov772x_set_params(struct ov772x_priv *priv,
 
ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00);
if (ret < 0)
-   goto ov772x_set_fmt_error;
+   return ret;
 
ret = ov772x_mask_set(client,
  EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK,
  priv->info->edgectrl.threshold);
if (ret < 0)
-   goto ov772x_set_fmt_error;
+   return ret;
 
ret = ov772x_mask_set(client,
  EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK,
  priv->info->edgectrl.strength);
if (ret < 0)
-   goto ov772x_set_fmt_error;
+   return ret;
 
} else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) {
/*
@@ -961,15 +956,35 @@ static int ov772x_set_params(struct ov772x_priv *priv,
  EDGE_UPPER, OV772X_EDGE_UPPER_MASK,
  priv->info->edgectrl.upper);
if (ret < 0)
-   goto ov772x_set_fmt_error;
+   return ret;
 
ret = ov772x_mask_set(client,
  EDGE_LOWER, OV772X_EDGE_LOWER_MASK,
  priv->info->edgectrl.lower);
if (ret < 0)
-   goto ov772x_set_fmt_error;
+   return ret;
}
 
+   return 0;
+}
+
+static int ov772x_set_params(struct ov772x_priv *priv,
+const struct ov772x_color_format *cfmt,
+const struct ov772x_win_size *win)
+{
+   struct i2c_client *client = v4l2_get_subdevdata(>subdev);
+   struct v4l2_fract tpf;
+   int ret;
+   u8  val;
+
+   /* Reset hardware. */
+   ov772x_reset(client);
+
+   /* Edge Ctrl. */
+   ret = ov772x_edgectrl(priv);
+   if (ret < 0)
+   return ret;
+
/* Fo

[PATCH v5 11/14] media: ov772x: use v4l2_ctrl to get current control value

2018-05-06 Thread Akinobu Mita
The ov772x driver provides three V4L2 controls and the current value of
each control is saved as a variable in the private data structure.

We don't need to keep track of the current value by ourself, if we use
v4l2_ctrl returned from v4l2_ctrl_new_std() instead.

This is a preparatory change to avoid accessing registers under power
saving mode.  This simplifies s_ctrl() by making it just return without
saving the current control value in private area when it is called under
power saving mode.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v5
- No changes

 drivers/media/i2c/ov772x.c | 34 +-
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 92ad13f..9292a18 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -419,10 +419,10 @@ struct ov772x_priv {
struct gpio_desc *rstb_gpio;
const struct ov772x_color_format *cfmt;
const struct ov772x_win_size *win;
-   unsigned shortflag_vflip:1;
-   unsigned shortflag_hflip:1;
+   struct v4l2_ctrl *vflip_ctrl;
+   struct v4l2_ctrl *hflip_ctrl;
/* band_filter = COM8[5] ? 256 - BDBASE : 0 */
-   unsigned shortband_filter;
+   struct v4l2_ctrl *band_filter_ctrl;
unsigned int  fps;
/* lock to protect power_count */
struct mutex  lock;
@@ -768,13 +768,11 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_VFLIP:
val = ctrl->val ? VFLIP_IMG : 0x00;
-   priv->flag_vflip = ctrl->val;
if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP))
val ^= VFLIP_IMG;
return ov772x_mask_set(client, COM3, VFLIP_IMG, val);
case V4L2_CID_HFLIP:
val = ctrl->val ? HFLIP_IMG : 0x00;
-   priv->flag_hflip = ctrl->val;
if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP))
val ^= HFLIP_IMG;
return ov772x_mask_set(client, COM3, HFLIP_IMG, val);
@@ -794,8 +792,7 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
ret = ov772x_mask_set(client, BDBASE,
  0xff, val);
}
-   if (!ret)
-   priv->band_filter = ctrl->val;
+
return ret;
}
 
@@ -1075,9 +1072,9 @@ static int ov772x_set_params(struct ov772x_priv *priv,
val |= VFLIP_IMG;
if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP))
val |= HFLIP_IMG;
-   if (priv->flag_vflip)
+   if (priv->vflip_ctrl->val)
val ^= VFLIP_IMG;
-   if (priv->flag_hflip)
+   if (priv->hflip_ctrl->val)
val ^= HFLIP_IMG;
 
ret = ov772x_mask_set(client,
@@ -1096,11 +1093,13 @@ static int ov772x_set_params(struct ov772x_priv *priv,
goto ov772x_set_fmt_error;
 
/* Set COM8. */
-   if (priv->band_filter) {
+   if (priv->band_filter_ctrl->val) {
+   unsigned short band_filter = priv->band_filter_ctrl->val;
+
ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF);
if (!ret)
ret = ov772x_mask_set(client, BDBASE,
- 0xff, 256 - priv->band_filter);
+ 0xff, 256 - band_filter);
if (ret < 0)
goto ov772x_set_fmt_error;
}
@@ -1341,12 +1340,13 @@ static int ov772x_probe(struct i2c_client *client,
 
v4l2_i2c_subdev_init(>subdev, client, _subdev_ops);
v4l2_ctrl_handler_init(>hdl, 3);
-   v4l2_ctrl_new_std(>hdl, _ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
-   v4l2_ctrl_new_std(>hdl, _ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
-   v4l2_ctrl_new_std(>hdl, _ctrl_ops,
- V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0);
+   priv->vflip_ctrl = v4l2_ctrl_new_std(>hdl, _ctrl_ops,
+V4L2_CID_VFLIP, 0, 1, 1, 0);
+   priv->hflip_ctrl = v4l2_ctrl_new_std(>hdl, _ctrl_ops,
+ 

[PATCH v5 13/14] media: ov772x: make set_fmt() and s_frame_interval() return -EBUSY while streaming

2018-05-06 Thread Akinobu Mita
The ov772x driver is going to offer a V4L2 sub-device interface, so
changing the output data format and the frame interval on this sub-device
can be made anytime.  However, these requests are preferred to fail while
the video stream on the device is active.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v5:
- Make s_frame_interval() return -EBUSY while streaming

 drivers/media/i2c/ov772x.c | 43 +--
 1 file changed, 33 insertions(+), 10 deletions(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 262a7e5..4b479f9 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -424,9 +424,10 @@ struct ov772x_priv {
/* band_filter = COM8[5] ? 256 - BDBASE : 0 */
struct v4l2_ctrl *band_filter_ctrl;
unsigned int  fps;
-   /* lock to protect power_count */
+   /* lock to protect power_count and streaming */
struct mutex  lock;
int   power_count;
+   int   streaming;
 #ifdef CONFIG_MEDIA_CONTROLLER
struct media_pad pad;
 #endif
@@ -603,18 +604,28 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int 
enable)
 {
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov772x_priv *priv = to_ov772x(sd);
+   int ret = 0;
 
-   if (!enable) {
-   ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
-   return 0;
-   }
+   mutex_lock(>lock);
 
-   ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0);
+   if (priv->streaming == enable)
+   goto done;
 
-   dev_dbg(>dev, "format %d, win %s\n",
-   priv->cfmt->code, priv->win->name);
+   ret = ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE,
+ enable ? 0 : SOFT_SLEEP_MODE);
+   if (ret)
+   goto done;
 
-   return 0;
+   if (enable) {
+   dev_dbg(>dev, "format %d, win %s\n",
+   priv->cfmt->code, priv->win->name);
+   }
+   priv->streaming = enable;
+
+done:
+   mutex_unlock(>lock);
+
+   return ret;
 }
 
 static unsigned int ov772x_select_fps(struct ov772x_priv *priv,
@@ -743,9 +754,15 @@ static int ov772x_s_frame_interval(struct v4l2_subdev *sd,
unsigned int fps;
int ret = 0;
 
+   mutex_lock(>lock);
+
+   if (priv->streaming) {
+   ret = -EBUSY;
+   goto error;
+   }
+
fps = ov772x_select_fps(priv, tpf);
 
-   mutex_lock(>lock);
/*
 * If the device is not powered up by the host driver do
 * not apply any changes to H/W at this time. Instead
@@ -1222,6 +1239,12 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd,
}
 
mutex_lock(>lock);
+
+   if (priv->streaming) {
+   ret = -EBUSY;
+   goto error;
+   }
+
/*
 * If the device is not powered up by the host driver do
 * not apply any changes to H/W at this time. Instead
-- 
2.7.4



[PATCH v5 12/14] media: ov772x: avoid accessing registers under power saving mode

2018-05-06 Thread Akinobu Mita
The set_fmt() in subdev pad ops, the s_ctrl() for subdev control handler,
and the s_frame_interval() in subdev video ops could be called when the
device is under power saving mode.  These callbacks for ov772x driver
cause updating H/W registers that will fail under power saving mode.

This avoids it by not apply any changes to H/W if the device is not powered
up.  Instead the changes will be restored right after power-up.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v5
- No changes

 drivers/media/i2c/ov772x.c | 79 +-
 1 file changed, 64 insertions(+), 15 deletions(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 9292a18..262a7e5 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -741,19 +741,30 @@ static int ov772x_s_frame_interval(struct v4l2_subdev *sd,
struct ov772x_priv *priv = to_ov772x(sd);
struct v4l2_fract *tpf = >interval;
unsigned int fps;
-   int ret;
+   int ret = 0;
 
fps = ov772x_select_fps(priv, tpf);
 
-   ret = ov772x_set_frame_rate(priv, fps, priv->cfmt, priv->win);
-   if (ret)
-   return ret;
+   mutex_lock(>lock);
+   /*
+* If the device is not powered up by the host driver do
+* not apply any changes to H/W at this time. Instead
+* the frame rate will be restored right after power-up.
+*/
+   if (priv->power_count > 0) {
+   ret = ov772x_set_frame_rate(priv, fps, priv->cfmt, priv->win);
+   if (ret)
+   goto error;
+   }
 
tpf->numerator = 1;
tpf->denominator = fps;
priv->fps = fps;
 
-   return 0;
+error:
+   mutex_unlock(>lock);
+
+   return ret;
 }
 
 static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -765,6 +776,16 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
int ret = 0;
u8 val;
 
+   /* v4l2_ctrl_lock() locks our own mutex */
+
+   /*
+* If the device is not powered up by the host driver do
+* not apply any controls to H/W at this time. Instead
+* the controls will be restored right after power-up.
+*/
+   if (priv->power_count == 0)
+   return 0;
+
switch (ctrl->id) {
case V4L2_CID_VFLIP:
val = ctrl->val ? VFLIP_IMG : 0x00;
@@ -885,6 +906,10 @@ static int ov772x_power_off(struct ov772x_priv *priv)
return 0;
 }
 
+static int ov772x_set_params(struct ov772x_priv *priv,
+const struct ov772x_color_format *cfmt,
+const struct ov772x_win_size *win);
+
 static int ov772x_s_power(struct v4l2_subdev *sd, int on)
 {
struct ov772x_priv *priv = to_ov772x(sd);
@@ -895,8 +920,20 @@ static int ov772x_s_power(struct v4l2_subdev *sd, int on)
/* If the power count is modified from 0 to != 0 or from != 0 to 0,
 * update the power state.
 */
-   if (priv->power_count == !on)
-   ret = on ? ov772x_power_on(priv) : ov772x_power_off(priv);
+   if (priv->power_count == !on) {
+   if (on) {
+   ret = ov772x_power_on(priv);
+   /*
+* Restore the format, the frame rate, and
+* the controls
+*/
+   if (!ret)
+   ret = ov772x_set_params(priv, priv->cfmt,
+   priv->win);
+   } else {
+   ret = ov772x_power_off(priv);
+   }
+   }
 
if (!ret) {
/* Update the power count. */
@@ -1163,7 +1200,7 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf = >format;
const struct ov772x_color_format *cfmt;
const struct ov772x_win_size *win;
-   int ret;
+   int ret = 0;
 
if (format->pad)
return -EINVAL;
@@ -1184,14 +1221,24 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd,
return 0;
}
 
-   ret = ov772x_set_params(priv, cfmt, win);
-   if (ret < 0)
-   return ret;
-
+   mutex_lock(>lock);
+   /*
+* If the device is not powered up by the host driver do
+* not apply any changes to H/W at this time. Instead
+* the format will be restored right after power-up.
+*/
+   if (priv->power_count > 0) {
+   ret = ov772x_set_params(priv, cfmt, win);
+   if (ret < 0)
+ 

[PATCH v5 14/14] media: ov772x: create subdevice device node

2018-05-06 Thread Akinobu Mita
Set the V4L2_SUBDEV_FL_HAS_DEVNODE flag for the subdevice so that the
subdevice device node is created.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v5
- No changes

 drivers/media/i2c/ov772x.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 4b479f9..f7f4fe6 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -1409,6 +1409,7 @@ static int ov772x_probe(struct i2c_client *client,
mutex_init(>lock);
 
v4l2_i2c_subdev_init(>subdev, client, _subdev_ops);
+   priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
v4l2_ctrl_handler_init(>hdl, 3);
/* Use our mutex for the controls */
priv->hdl.lock = >lock;
-- 
2.7.4



[PATCH v5 05/14] media: ov772x: add media controller support

2018-05-06 Thread Akinobu Mita
Create a source pad and set the media controller type to the sensor.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Reviewed-by: Jacopo Mondi <jac...@jmondi.org>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v5
- No changes

 drivers/media/i2c/ov772x.c | 16 +++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 3fdbe64..bb5327f 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -424,6 +424,9 @@ struct ov772x_priv {
/* band_filter = COM8[5] ? 256 - BDBASE : 0 */
unsigned shortband_filter;
unsigned int  fps;
+#ifdef CONFIG_MEDIA_CONTROLLER
+   struct media_pad pad;
+#endif
 };
 
 /*
@@ -1316,16 +1319,26 @@ static int ov772x_probe(struct i2c_client *client,
if (ret < 0)
goto error_gpio_put;
 
+#ifdef CONFIG_MEDIA_CONTROLLER
+   priv->pad.flags = MEDIA_PAD_FL_SOURCE;
+   priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+   ret = media_entity_pads_init(>subdev.entity, 1, >pad);
+   if (ret < 0)
+   goto error_gpio_put;
+#endif
+
priv->cfmt = _cfmts[0];
priv->win = _win_sizes[0];
priv->fps = 15;
 
ret = v4l2_async_register_subdev(>subdev);
if (ret)
-   goto error_gpio_put;
+   goto error_entity_cleanup;
 
return 0;
 
+error_entity_cleanup:
+   media_entity_cleanup(>subdev.entity);
 error_gpio_put:
if (priv->pwdn_gpio)
gpiod_put(priv->pwdn_gpio);
@@ -1341,6 +1354,7 @@ static int ov772x_remove(struct i2c_client *client)
 {
struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client));
 
+   media_entity_cleanup(>subdev.entity);
clk_put(priv->clk);
if (priv->pwdn_gpio)
gpiod_put(priv->pwdn_gpio);
-- 
2.7.4



[PATCH v5 06/14] media: ov772x: use generic names for reset and powerdown gpios

2018-05-06 Thread Akinobu Mita
The ov772x driver uses "rstb-gpios" and "pwdn-gpios" for reset and
powerdown pins.  However, using generic names for these gpios is
preferred.  ("reset-gpios" and "powerdown-gpios" respectively)

There is only one mainline user for these gpios, so rename to generic
names.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Reviewed-by: Jacopo Mondi <jac...@jmondi.org>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v5
- No changes

 arch/sh/boards/mach-migor/setup.c | 5 +++--
 drivers/media/i2c/ov772x.c| 8 
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/arch/sh/boards/mach-migor/setup.c 
b/arch/sh/boards/mach-migor/setup.c
index 271dfc2..73b9ee4 100644
--- a/arch/sh/boards/mach-migor/setup.c
+++ b/arch/sh/boards/mach-migor/setup.c
@@ -351,8 +351,9 @@ static struct platform_device migor_ceu_device = {
 static struct gpiod_lookup_table ov7725_gpios = {
.dev_id = "0-0021",
.table  = {
-   GPIO_LOOKUP("sh7722_pfc", GPIO_PTT0, "pwdn", GPIO_ACTIVE_HIGH),
-   GPIO_LOOKUP("sh7722_pfc", GPIO_PTT3, "rstb", GPIO_ACTIVE_LOW),
+   GPIO_LOOKUP("sh7722_pfc", GPIO_PTT0, "powerdown",
+   GPIO_ACTIVE_HIGH),
+   GPIO_LOOKUP("sh7722_pfc", GPIO_PTT3, "reset", GPIO_ACTIVE_LOW),
},
 };
 
diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index bb5327f..97a65ce 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -837,10 +837,10 @@ static int ov772x_power_on(struct ov772x_priv *priv)
 * available to handle this cleanly, request the GPIO temporarily
 * to avoid conflicts.
 */
-   priv->rstb_gpio = gpiod_get_optional(>dev, "rstb",
+   priv->rstb_gpio = gpiod_get_optional(>dev, "reset",
 GPIOD_OUT_LOW);
if (IS_ERR(priv->rstb_gpio)) {
-   dev_info(>dev, "Unable to get GPIO \"rstb\"");
+   dev_info(>dev, "Unable to get GPIO \"reset\"");
return PTR_ERR(priv->rstb_gpio);
}
 
@@ -1307,10 +1307,10 @@ static int ov772x_probe(struct i2c_client *client,
goto error_ctrl_free;
}
 
-   priv->pwdn_gpio = gpiod_get_optional(>dev, "pwdn",
+   priv->pwdn_gpio = gpiod_get_optional(>dev, "powerdown",
 GPIOD_OUT_LOW);
if (IS_ERR(priv->pwdn_gpio)) {
-   dev_info(>dev, "Unable to get GPIO \"pwdn\"");
+   dev_info(>dev, "Unable to get GPIO \"powerdown\"");
ret = PTR_ERR(priv->pwdn_gpio);
goto error_clk_put;
}
-- 
2.7.4



[PATCH v5 07/14] media: ov772x: omit consumer ID when getting clock reference

2018-05-06 Thread Akinobu Mita
Currently the ov772x driver obtains a clock with a specific consumer ID.
As there's a single clock for this driver, we could omit clock-names
property in device tree by passing NULL as a consumer ID to clk_get().

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Suggested-by: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Tested-by: Jacopo Mondi <jacopo+rene...@jmondi.org>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v5
- No changes

 arch/sh/boards/mach-migor/setup.c | 2 +-
 drivers/media/i2c/ov772x.c| 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/sh/boards/mach-migor/setup.c 
b/arch/sh/boards/mach-migor/setup.c
index 73b9ee4..11f8001 100644
--- a/arch/sh/boards/mach-migor/setup.c
+++ b/arch/sh/boards/mach-migor/setup.c
@@ -593,7 +593,7 @@ static int __init migor_devices_setup(void)
}
 
/* Add a clock alias for ov7725 xclk source. */
-   clk_add_alias("xclk", "0-0021", "video_clk", NULL);
+   clk_add_alias(NULL, "0-0021", "video_clk", NULL);
 
/* Register GPIOs for video sources. */
gpiod_add_lookup_table(_gpios);
diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 97a65ce..f939e28 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -1300,7 +1300,7 @@ static int ov772x_probe(struct i2c_client *client,
if (priv->hdl.error)
return priv->hdl.error;
 
-   priv->clk = clk_get(>dev, "xclk");
+   priv->clk = clk_get(>dev, NULL);
if (IS_ERR(priv->clk)) {
dev_err(>dev, "Unable to get xclk clock\n");
ret = PTR_ERR(priv->clk);
-- 
2.7.4



[PATCH v5 03/14] media: ov772x: allow i2c controllers without I2C_FUNC_PROTOCOL_MANGLING

2018-05-06 Thread Akinobu Mita
The ov772x driver only works when the i2c controller have
I2C_FUNC_PROTOCOL_MANGLING.  However, many i2c controller drivers don't
support it.

The reason that the ov772x requires I2C_FUNC_PROTOCOL_MANGLING is that
it doesn't support repeated starts.

This changes the reading ov772x register method so that it doesn't
require I2C_FUNC_PROTOCOL_MANGLING by calling two separated i2c messages.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Cc: Wolfram Sang <w...@the-dreams.de>
Reviewed-by: Jacopo Mondi <jacopo+rene...@jmondi.org>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v5
- Add Reviewed-by: line

 drivers/media/i2c/ov772x.c | 20 ++--
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index e255070..b6223bf 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -542,9 +542,19 @@ static struct ov772x_priv *to_ov772x(struct v4l2_subdev 
*sd)
return container_of(sd, struct ov772x_priv, subdev);
 }
 
-static inline int ov772x_read(struct i2c_client *client, u8 addr)
+static int ov772x_read(struct i2c_client *client, u8 addr)
 {
-   return i2c_smbus_read_byte_data(client, addr);
+   int ret;
+   u8 val;
+
+   ret = i2c_master_send(client, , 1);
+   if (ret < 0)
+   return ret;
+   ret = i2c_master_recv(client, , 1);
+   if (ret < 0)
+   return ret;
+
+   return val;
 }
 
 static inline int ov772x_write(struct i2c_client *client, u8 addr, u8 value)
@@ -1255,13 +1265,11 @@ static int ov772x_probe(struct i2c_client *client,
return -EINVAL;
}
 
-   if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_PROTOCOL_MANGLING)) {
+   if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(>dev,
-   "I2C-Adapter doesn't support SMBUS_BYTE_DATA or 
PROTOCOL_MANGLING\n");
+   "I2C-Adapter doesn't support SMBUS_BYTE_DATA\n");
return -EIO;
}
-   client->flags |= I2C_CLIENT_SCCB;
 
priv = devm_kzalloc(>dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
-- 
2.7.4



[PATCH v5 01/14] media: dt-bindings: ov772x: add device tree binding

2018-05-06 Thread Akinobu Mita
This adds a device tree binding documentation for OV7720/OV7725 sensor.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Cc: Rob Herring <robh...@kernel.org>
Reviewed-by: Rob Herring <r...@kernel.org>
Reviewed-by: Jacopo Mondi <jac...@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v5
- No changes

 .../devicetree/bindings/media/i2c/ov772x.txt   | 40 ++
 MAINTAINERS|  1 +
 2 files changed, 41 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ov772x.txt

diff --git a/Documentation/devicetree/bindings/media/i2c/ov772x.txt 
b/Documentation/devicetree/bindings/media/i2c/ov772x.txt
new file mode 100644
index 000..0b3ede5
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ov772x.txt
@@ -0,0 +1,40 @@
+* Omnivision OV7720/OV7725 CMOS sensor
+
+The Omnivision OV7720/OV7725 sensor supports multiple resolutions output,
+such as VGA, QVGA, and any size scaling down from CIF to 40x30. It also can
+support the YUV422, RGB565/555/444, GRB422 or raw RGB output formats.
+
+Required Properties:
+- compatible: shall be one of
+   "ovti,ov7720"
+   "ovti,ov7725"
+- clocks: reference to the xclk input clock.
+
+Optional Properties:
+- reset-gpios: reference to the GPIO connected to the RSTB pin which is
+  active low, if any.
+- powerdown-gpios: reference to the GPIO connected to the PWDN pin which is
+  active high, if any.
+
+The device node shall contain one 'port' child node with one child 'endpoint'
+subnode for its digital output video port, in accordance with the video
+interface bindings defined in Documentation/devicetree/bindings/media/
+video-interfaces.txt.
+
+Example:
+
+ {
+   ov772x: camera@21 {
+   compatible = "ovti,ov7725";
+   reg = <0x21>;
+   reset-gpios = <_gpio_0 0 GPIO_ACTIVE_LOW>;
+   powerdown-gpios = <_gpio_0 1 GPIO_ACTIVE_LOW>;
+   clocks = <>;
+
+   port {
+   ov772x_0: endpoint {
+   remote-endpoint = <_in0>;
+   };
+   };
+   };
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index df6e9bb..cd4c270 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10356,6 +10356,7 @@ T:  git git://linuxtv.org/media_tree.git
 S: Odd fixes
 F: drivers/media/i2c/ov772x.c
 F: include/media/i2c/ov772x.h
+F: Documentation/devicetree/bindings/media/i2c/ov772x.txt
 
 OMNIVISION OV7740 SENSOR DRIVER
 M: Wenyou Yang <wenyou.y...@microchip.com>
-- 
2.7.4



[PATCH v5 04/14] media: ov772x: add checks for register read errors

2018-05-06 Thread Akinobu Mita
This change adds checks for register read errors and returns correct
error code.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Reviewed-by: Jacopo Mondi <jac...@jmondi.org>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v5
- No changes

 drivers/media/i2c/ov772x.c | 20 ++--
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index b6223bf..3fdbe64 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -1146,7 +1146,7 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd,
 static int ov772x_video_probe(struct ov772x_priv *priv)
 {
struct i2c_client  *client = v4l2_get_subdevdata(>subdev);
-   u8  pid, ver;
+   int pid, ver, midh, midl;
const char *devname;
int ret;
 
@@ -1156,7 +1156,11 @@ static int ov772x_video_probe(struct ov772x_priv *priv)
 
/* Check and show product ID and manufacturer ID. */
pid = ov772x_read(client, PID);
+   if (pid < 0)
+   return pid;
ver = ov772x_read(client, VER);
+   if (ver < 0)
+   return ver;
 
switch (VERSION(pid, ver)) {
case OV7720:
@@ -1172,13 +1176,17 @@ static int ov772x_video_probe(struct ov772x_priv *priv)
goto done;
}
 
+   midh = ov772x_read(client, MIDH);
+   if (midh < 0)
+   return midh;
+   midl = ov772x_read(client, MIDL);
+   if (midl < 0)
+   return midl;
+
dev_info(>dev,
 "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
-devname,
-pid,
-ver,
-ov772x_read(client, MIDH),
-ov772x_read(client, MIDL));
+devname, pid, ver, midh, midl);
+
ret = v4l2_ctrl_handler_setup(>hdl);
 
 done:
-- 
2.7.4



[PATCH v5 00/14] media: ov772x: support media controller, device tree probing, etc.

2018-05-06 Thread Akinobu Mita
This patchset includes support media controller, sub-device interface,
device tree probing and other miscellanuous changes for ov772x driver.

* v5 (thanks to Jacopo Mondi)
- Add Acked-by: line
- Add Reviewed-by: line
- Remove unnecessary space
- Align arguments to open parenthesis
- Sort variable declarations
- Make s_frame_interval() return -EBUSY while streaming

* v4 (thanks to Laurent Pinchart)
- Add Reviewed-by: lines
- Correct setting of banding filter (New)
- Omit consumer ID when getting clock reference (New)
- Use v4l2_ctrl to get current control value (New)
- Correctly restore the controls changed under power saving mode

* v3 (thanks to Sakari Ailus and Jacopo Mondi)
- Reorder the patches
- Add Reviewed-by: lines
- Remove I2C_CLIENT_SCCB flag set as it isn't needed anymore
- Fix typo in the commit log
- Return without resetting if ov772x_edgectrl() failed
- Update the error message for missing platform data
- Rename mutex name from power_lock to lock
- Add warning for duplicated s_power call
- Add newlines before labels
- Remove __v4l2_ctrl_handler_setup in s_power() as it causes duplicated
  register settings
- Make set_fmt() return -EBUSY while streaming (New)

* v2 (thanks to Jacopo Mondi)
- Replace the implementation of ov772x_read() instead of adding an
  alternative method
- Assign the ov772x_read() return value to pid and ver directly
- Do the same for MIDH and MIDL
- Move video_probe() before the entity initialization and remove the #ifdef
  around the media_entity_cleanup()
- Use generic names for reset and powerdown gpios (New)
- Add "dt-bindings:" in the subject
- Add a brief description of the sensor
- Update the GPIO names
- Indicate the GPIO active level
- Add missing NULL checks for priv->info
- Leave the check for the missing platform data if legacy platform data
  probe is used.
- Handle nested s_power() calls (New)
- Reconstruct s_frame_interval() (New)
- Avoid accessing registers

Akinobu Mita (14):
  media: dt-bindings: ov772x: add device tree binding
  media: ov772x: correct setting of banding filter
  media: ov772x: allow i2c controllers without
I2C_FUNC_PROTOCOL_MANGLING
  media: ov772x: add checks for register read errors
  media: ov772x: add media controller support
  media: ov772x: use generic names for reset and powerdown gpios
  media: ov772x: omit consumer ID when getting clock reference
  media: ov772x: support device tree probing
  media: ov772x: handle nested s_power() calls
  media: ov772x: reconstruct s_frame_interval()
  media: ov772x: use v4l2_ctrl to get current control value
  media: ov772x: avoid accessing registers under power saving mode
  media: ov772x: make set_fmt() and s_frame_interval() return -EBUSY
while streaming
  media: ov772x: create subdevice device node

 .../devicetree/bindings/media/i2c/ov772x.txt   |  40 +++
 MAINTAINERS|   1 +
 arch/sh/boards/mach-migor/setup.c  |   7 +-
 drivers/media/i2c/ov772x.c | 357 +++--
 4 files changed, 308 insertions(+), 97 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ov772x.txt

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Cc: Rob Herring <robh...@kernel.org>
Cc: Wolfram Sang <w...@the-dreams.de>
-- 
2.7.4



[PATCH v5 02/14] media: ov772x: correct setting of banding filter

2018-05-06 Thread Akinobu Mita
The banding filter ON/OFF is controlled via bit 5 of COM8 register.  It
is attempted to be enabled in ov772x_set_params() by the following line.

ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1);

But this unexpectedly results disabling the banding filter, because the
mask and set bits are exclusive.

On the other hand, ov772x_s_ctrl() correctly sets the bit by:

ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF);

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Acked-by: Jacopo Mondi <jacopo+rene...@jmondi.org>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v5
- Add Acked-by: line

 drivers/media/i2c/ov772x.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index b62860c..e255070 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -1035,7 +1035,7 @@ static int ov772x_set_params(struct ov772x_priv *priv,
 
/* Set COM8. */
if (priv->band_filter) {
-   ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1);
+   ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF);
if (!ret)
ret = ov772x_mask_set(client, BDBASE,
  0xff, 256 - priv->band_filter);
-- 
2.7.4



Re: [PATCH v4 10/14] media: ov772x: reconstruct s_frame_interval()

2018-05-04 Thread Akinobu Mita
2018-05-04 5:29 GMT+09:00 jacopo mondi <jac...@jmondi.org>:
> Hi Akinobu,
>thank you for the patch
>
> On Mon, Apr 30, 2018 at 02:13:09AM +0900, Akinobu Mita wrote:
>> This splits the s_frame_interval() in subdev video ops into selecting the
>> frame interval and setting up the registers.
>>
>> This is a preparatory change to avoid accessing registers under power
>> saving mode.
>>
>> Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
>> Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
>> Cc: Hans Verkuil <hans.verk...@cisco.com>
>> Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
>> Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
>> Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
>> ---
>> * v4
>> - No changes
>>
>>  drivers/media/i2c/ov772x.c | 56 
>> +-
>>  1 file changed, 35 insertions(+), 21 deletions(-)
>>
>> diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
>> index edc013d..7ea157e 100644
>> --- a/drivers/media/i2c/ov772x.c
>> +++ b/drivers/media/i2c/ov772x.c
>> @@ -617,25 +617,16 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int 
>> enable)
>>   return 0;
>>  }
>>
>> -static int ov772x_set_frame_rate(struct ov772x_priv *priv,
>> -  struct v4l2_fract *tpf,
>> -  const struct ov772x_color_format *cfmt,
>> -  const struct ov772x_win_size *win)
>> +static unsigned int ov772x_select_fps(struct ov772x_priv *priv,
>> +  struct v4l2_fract *tpf)
>
> Please align to the open brace, as all the other functions in the
> driver.

OK.

> Also, is it worth making a function out of this? It is called from one
> place only... see below.
>
>>  {
>> - struct i2c_client *client = v4l2_get_subdevdata(>subdev);
>> - unsigned long fin = clk_get_rate(priv->clk);
>>   unsigned int fps = tpf->numerator ?
>>  tpf->denominator / tpf->numerator :
>>  tpf->denominator;
>>   unsigned int best_diff;
>> - unsigned int fsize;
>> - unsigned int pclk;
>>   unsigned int diff;
>>   unsigned int idx;
>>   unsigned int i;
>> - u8 clkrc = 0;
>> - u8 com4 = 0;
>> - int ret;
>>
>>   /* Approximate to the closest supported frame interval. */
>>   best_diff = ~0L;
>> @@ -646,7 +637,25 @@ static int ov772x_set_frame_rate(struct ov772x_priv 
>> *priv,
>>   best_diff = diff;
>>   }
>>   }
>> - fps = ov772x_frame_intervals[idx];
>> +
>> + return ov772x_frame_intervals[idx];
>> +}
>> +
>> +static int ov772x_set_frame_rate(struct ov772x_priv *priv,
>> +  unsigned int fps,
>> +  const struct ov772x_color_format *cfmt,
>> +  const struct ov772x_win_size *win)
>> +{
>> + struct i2c_client *client = v4l2_get_subdevdata(>subdev);
>> + unsigned long fin = clk_get_rate(priv->clk);
>> + unsigned int fsize;
>> + unsigned int pclk;
>> + unsigned int best_diff;
>
> Please keep variable declarations sorted as in other functions in the
> driver.

OK.

>> + unsigned int diff;
>> + unsigned int i;
>> + u8 clkrc = 0;
>> + u8 com4 = 0;
>> + int ret;
>>
>>   /* Use image size (with blankings) to calculate desired pixel clock. */
>>   switch (cfmt->com7 & OFMT_MASK) {
>> @@ -711,10 +720,6 @@ static int ov772x_set_frame_rate(struct ov772x_priv 
>> *priv,
>>   if (ret < 0)
>>   return ret;
>>
>> - tpf->numerator = 1;
>> - tpf->denominator = fps;
>> - priv->fps = tpf->denominator;
>> -
>>   return 0;
>>  }
>>
>> @@ -735,8 +740,20 @@ static int ov772x_s_frame_interval(struct v4l2_subdev 
>> *sd,
>>  {
>>   struct ov772x_priv *priv = to_ov772x(sd);
>>   struct v4l2_fract *tpf = >interval;
>> + unsigned int fps;
>> + int ret;
>> +
>> + fps = ov772x_select_fps(priv, tpf);
>
> That's the only caller of this function. I'm fine if you want to keep
> it as it is though.

I would like to keep ov772x_select_fps() because the function name is
self descriptive and imples it doesn't change HW registers.  So I 

Re: [PATCH v4 12/14] media: ov772x: avoid accessing registers under power saving mode

2018-05-04 Thread Akinobu Mita
2018-05-04 6:03 GMT+09:00 jacopo mondi <jac...@jmondi.org>:
> Hi Akinobu,
>   let me see if I got this right...
>
> On Mon, Apr 30, 2018 at 02:13:11AM +0900, Akinobu Mita wrote:
>> The set_fmt() in subdev pad ops, the s_ctrl() for subdev control handler,
>> and the s_frame_interval() in subdev video ops could be called when the
>> device is under power saving mode.  These callbacks for ov772x driver
>> cause updating H/W registers that will fail under power saving mode.
>>
>> This avoids it by not apply any changes to H/W if the device is not powered
>> up.  Instead the changes will be restored right after power-up.
>>
>> Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
>> Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
>> Cc: Hans Verkuil <hans.verk...@cisco.com>
>> Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
>> Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
>> Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
>> ---
>> * v4
>> - No changes
>>
>>  drivers/media/i2c/ov772x.c | 79 
>> +-
>>  1 file changed, 64 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
>> index 3e6ca98..bd37169 100644
>> --- a/drivers/media/i2c/ov772x.c
>> +++ b/drivers/media/i2c/ov772x.c
>> @@ -741,19 +741,30 @@ static int ov772x_s_frame_interval(struct v4l2_subdev 
>> *sd,
>>   struct ov772x_priv *priv = to_ov772x(sd);
>>   struct v4l2_fract *tpf = >interval;
>>   unsigned int fps;
>> - int ret;
>> + int ret = 0;
>>
>>   fps = ov772x_select_fps(priv, tpf);
>>
>> - ret = ov772x_set_frame_rate(priv, fps, priv->cfmt, priv->win);
>> - if (ret)
>> - return ret;
>> + mutex_lock(>lock);
>> + /*
>> +  * If the device is not powered up by the host driver do
>> +  * not apply any changes to H/W at this time. Instead
>> +  * the frame rate will be restored right after power-up.
>> +  */
>> + if (priv->power_count > 0) {
>
> If I'm not wrong this is not protected when the device is
> streaming.
>
> Since Hans' series frame control is called by g/s_parm (until a new
> ioctl is not introduced) and I've looked at the call stack and it
> seems to me it does not check for active streaming[1]. I -think-
> this is even worse when this is called on the subdev, as there is
> no shared notion of active streaming. Am I wrong?
>
> If you have to check for active streaming here (I see some other
> drivers doing that, eg ov5640), then I see two ways, or you just
> return -EBUSY, or you save the desired FPS for later, but then it gets
> messy as you won't be able to just restore paramters at power_on()
> time, but you would need to do that also at start streaming time.
>
> My opinion is that you should check for streaming active (as you're
> doing for the set_fmt() function in [13/14], do you agree?

I agree.  I would like to make ov772x_s_frame_interval() return -EBUSY
without saving the desired FPS for later when the device is streaming.

I'm going to prepare v5 patches that address the above and other issues
you found in v4 unless you prefer the incremental patch series.

> [1] This calls for a device wise 'streaming' state handler. Maybe it's
> there but I failed to find checks for that.


[PATCH v4 14/14] media: ov772x: create subdevice device node

2018-04-29 Thread Akinobu Mita
Set the V4L2_SUBDEV_FL_HAS_DEVNODE flag for the subdevice so that the
subdevice device node is created.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v4
- No changes

 drivers/media/i2c/ov772x.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index f3c4f78..ec45eed 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -1403,6 +1403,7 @@ static int ov772x_probe(struct i2c_client *client,
mutex_init(>lock);
 
v4l2_i2c_subdev_init(>subdev, client, _subdev_ops);
+   priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
v4l2_ctrl_handler_init(>hdl, 3);
/* Use our mutex for the controls */
priv->hdl.lock = >lock;
-- 
2.7.4



[PATCH v4 06/14] media: ov772x: use generic names for reset and powerdown gpios

2018-04-29 Thread Akinobu Mita
The ov772x driver uses "rstb-gpios" and "pwdn-gpios" for reset and
powerdown pins.  However, using generic names for these gpios is
preferred.  ("reset-gpios" and "powerdown-gpios" respectively)

There is only one mainline user for these gpios, so rename to generic
names.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Reviewed-by: Jacopo Mondi <jac...@jmondi.org>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v4
- No changes

 arch/sh/boards/mach-migor/setup.c | 5 +++--
 drivers/media/i2c/ov772x.c| 8 
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/arch/sh/boards/mach-migor/setup.c 
b/arch/sh/boards/mach-migor/setup.c
index 271dfc2..73b9ee4 100644
--- a/arch/sh/boards/mach-migor/setup.c
+++ b/arch/sh/boards/mach-migor/setup.c
@@ -351,8 +351,9 @@ static struct platform_device migor_ceu_device = {
 static struct gpiod_lookup_table ov7725_gpios = {
.dev_id = "0-0021",
.table  = {
-   GPIO_LOOKUP("sh7722_pfc", GPIO_PTT0, "pwdn", GPIO_ACTIVE_HIGH),
-   GPIO_LOOKUP("sh7722_pfc", GPIO_PTT3, "rstb", GPIO_ACTIVE_LOW),
+   GPIO_LOOKUP("sh7722_pfc", GPIO_PTT0, "powerdown",
+   GPIO_ACTIVE_HIGH),
+   GPIO_LOOKUP("sh7722_pfc", GPIO_PTT3, "reset", GPIO_ACTIVE_LOW),
},
 };
 
diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index bb5327f..97a65ce 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -837,10 +837,10 @@ static int ov772x_power_on(struct ov772x_priv *priv)
 * available to handle this cleanly, request the GPIO temporarily
 * to avoid conflicts.
 */
-   priv->rstb_gpio = gpiod_get_optional(>dev, "rstb",
+   priv->rstb_gpio = gpiod_get_optional(>dev, "reset",
 GPIOD_OUT_LOW);
if (IS_ERR(priv->rstb_gpio)) {
-   dev_info(>dev, "Unable to get GPIO \"rstb\"");
+   dev_info(>dev, "Unable to get GPIO \"reset\"");
return PTR_ERR(priv->rstb_gpio);
}
 
@@ -1307,10 +1307,10 @@ static int ov772x_probe(struct i2c_client *client,
goto error_ctrl_free;
}
 
-   priv->pwdn_gpio = gpiod_get_optional(>dev, "pwdn",
+   priv->pwdn_gpio = gpiod_get_optional(>dev, "powerdown",
 GPIOD_OUT_LOW);
if (IS_ERR(priv->pwdn_gpio)) {
-   dev_info(>dev, "Unable to get GPIO \"pwdn\"");
+   dev_info(>dev, "Unable to get GPIO \"powerdown\"");
ret = PTR_ERR(priv->pwdn_gpio);
goto error_clk_put;
}
-- 
2.7.4



[PATCH v4 12/14] media: ov772x: avoid accessing registers under power saving mode

2018-04-29 Thread Akinobu Mita
The set_fmt() in subdev pad ops, the s_ctrl() for subdev control handler,
and the s_frame_interval() in subdev video ops could be called when the
device is under power saving mode.  These callbacks for ov772x driver
cause updating H/W registers that will fail under power saving mode.

This avoids it by not apply any changes to H/W if the device is not powered
up.  Instead the changes will be restored right after power-up.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v4
- No changes

 drivers/media/i2c/ov772x.c | 79 +-
 1 file changed, 64 insertions(+), 15 deletions(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 3e6ca98..bd37169 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -741,19 +741,30 @@ static int ov772x_s_frame_interval(struct v4l2_subdev *sd,
struct ov772x_priv *priv = to_ov772x(sd);
struct v4l2_fract *tpf = >interval;
unsigned int fps;
-   int ret;
+   int ret = 0;
 
fps = ov772x_select_fps(priv, tpf);
 
-   ret = ov772x_set_frame_rate(priv, fps, priv->cfmt, priv->win);
-   if (ret)
-   return ret;
+   mutex_lock(>lock);
+   /*
+* If the device is not powered up by the host driver do
+* not apply any changes to H/W at this time. Instead
+* the frame rate will be restored right after power-up.
+*/
+   if (priv->power_count > 0) {
+   ret = ov772x_set_frame_rate(priv, fps, priv->cfmt, priv->win);
+   if (ret)
+   goto error;
+   }
 
tpf->numerator = 1;
tpf->denominator = fps;
priv->fps = fps;
 
-   return 0;
+error:
+   mutex_unlock(>lock);
+
+   return ret;
 }
 
 static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -765,6 +776,16 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
int ret = 0;
u8 val;
 
+   /* v4l2_ctrl_lock() locks our own mutex */
+
+   /*
+* If the device is not powered up by the host driver do
+* not apply any controls to H/W at this time. Instead
+* the controls will be restored right after power-up.
+*/
+   if (priv->power_count == 0)
+   return 0;
+
switch (ctrl->id) {
case V4L2_CID_VFLIP:
val = ctrl->val ? VFLIP_IMG : 0x00;
@@ -885,6 +906,10 @@ static int ov772x_power_off(struct ov772x_priv *priv)
return 0;
 }
 
+static int ov772x_set_params(struct ov772x_priv *priv,
+const struct ov772x_color_format *cfmt,
+const struct ov772x_win_size *win);
+
 static int ov772x_s_power(struct v4l2_subdev *sd, int on)
 {
struct ov772x_priv *priv = to_ov772x(sd);
@@ -895,8 +920,20 @@ static int ov772x_s_power(struct v4l2_subdev *sd, int on)
/* If the power count is modified from 0 to != 0 or from != 0 to 0,
 * update the power state.
 */
-   if (priv->power_count == !on)
-   ret = on ? ov772x_power_on(priv) : ov772x_power_off(priv);
+   if (priv->power_count == !on) {
+   if (on) {
+   ret = ov772x_power_on(priv);
+   /*
+* Restore the format, the frame rate, and
+* the controls
+*/
+   if (!ret)
+   ret = ov772x_set_params(priv, priv->cfmt,
+   priv->win);
+   } else {
+   ret = ov772x_power_off(priv);
+   }
+   }
 
if (!ret) {
/* Update the power count. */
@@ -1163,7 +1200,7 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf = >format;
const struct ov772x_color_format *cfmt;
const struct ov772x_win_size *win;
-   int ret;
+   int ret = 0;
 
if (format->pad)
return -EINVAL;
@@ -1184,14 +1221,24 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd,
return 0;
}
 
-   ret = ov772x_set_params(priv, cfmt, win);
-   if (ret < 0)
-   return ret;
-
+   mutex_lock(>lock);
+   /*
+* If the device is not powered up by the host driver do
+* not apply any changes to H/W at this time. Instead
+* the format will be restored right after power-up.
+*/
+   if (priv->power_count > 0) {
+   ret = ov772x_set_params(priv, cfmt, win);
+   if (ret < 0)
+ 

[PATCH v4 11/14] media: ov772x: use v4l2_ctrl to get current control value

2018-04-29 Thread Akinobu Mita
The ov772x driver provides three V4L2 controls and the current value of
each control is saved as a variable in the private data structure.

We don't need to keep track of the current value by ourself, if we use
v4l2_ctrl returned from v4l2_ctrl_new_std() instead.

This is a preparatory change to avoid accessing registers under power
saving mode.  This simplifies s_ctrl() by making it just return without
saving the current control value in private area when it is called under
power saving mode.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v4
- New patch

 drivers/media/i2c/ov772x.c | 34 +-
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 7ea157e..3e6ca98 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -419,10 +419,10 @@ struct ov772x_priv {
struct gpio_desc *rstb_gpio;
const struct ov772x_color_format *cfmt;
const struct ov772x_win_size *win;
-   unsigned shortflag_vflip:1;
-   unsigned shortflag_hflip:1;
+   struct v4l2_ctrl *vflip_ctrl;
+   struct v4l2_ctrl *hflip_ctrl;
/* band_filter = COM8[5] ? 256 - BDBASE : 0 */
-   unsigned shortband_filter;
+   struct v4l2_ctrl *band_filter_ctrl;
unsigned int  fps;
/* lock to protect power_count */
struct mutex  lock;
@@ -768,13 +768,11 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_VFLIP:
val = ctrl->val ? VFLIP_IMG : 0x00;
-   priv->flag_vflip = ctrl->val;
if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP))
val ^= VFLIP_IMG;
return ov772x_mask_set(client, COM3, VFLIP_IMG, val);
case V4L2_CID_HFLIP:
val = ctrl->val ? HFLIP_IMG : 0x00;
-   priv->flag_hflip = ctrl->val;
if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP))
val ^= HFLIP_IMG;
return ov772x_mask_set(client, COM3, HFLIP_IMG, val);
@@ -794,8 +792,7 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
ret = ov772x_mask_set(client, BDBASE,
  0xff, val);
}
-   if (!ret)
-   priv->band_filter = ctrl->val;
+
return ret;
}
 
@@ -1075,9 +1072,9 @@ static int ov772x_set_params(struct ov772x_priv *priv,
val |= VFLIP_IMG;
if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP))
val |= HFLIP_IMG;
-   if (priv->flag_vflip)
+   if (priv->vflip_ctrl->val)
val ^= VFLIP_IMG;
-   if (priv->flag_hflip)
+   if (priv->hflip_ctrl->val)
val ^= HFLIP_IMG;
 
ret = ov772x_mask_set(client,
@@ -1096,11 +1093,13 @@ static int ov772x_set_params(struct ov772x_priv *priv,
goto ov772x_set_fmt_error;
 
/* Set COM8. */
-   if (priv->band_filter) {
+   if (priv->band_filter_ctrl->val) {
+   unsigned short band_filter = priv->band_filter_ctrl->val;
+
ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF);
if (!ret)
ret = ov772x_mask_set(client, BDBASE,
- 0xff, 256 - priv->band_filter);
+ 0xff, 256 - band_filter);
if (ret < 0)
goto ov772x_set_fmt_error;
}
@@ -1341,12 +1340,13 @@ static int ov772x_probe(struct i2c_client *client,
 
v4l2_i2c_subdev_init(>subdev, client, _subdev_ops);
v4l2_ctrl_handler_init(>hdl, 3);
-   v4l2_ctrl_new_std(>hdl, _ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
-   v4l2_ctrl_new_std(>hdl, _ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
-   v4l2_ctrl_new_std(>hdl, _ctrl_ops,
- V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0);
+   priv->vflip_ctrl = v4l2_ctrl_new_std(>hdl, _ctrl_ops,
+V4L2_CID_VFLIP, 0, 1, 1, 0);
+   priv->hflip_ctrl = v4l2_ctrl_new_std(>hdl, _ctrl_ops,
+ 

[PATCH v4 05/14] media: ov772x: add media controller support

2018-04-29 Thread Akinobu Mita
Create a source pad and set the media controller type to the sensor.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Reviewed-by: Jacopo Mondi <jac...@jmondi.org>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v4
- No changes

 drivers/media/i2c/ov772x.c | 16 +++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 3fdbe64..bb5327f 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -424,6 +424,9 @@ struct ov772x_priv {
/* band_filter = COM8[5] ? 256 - BDBASE : 0 */
unsigned shortband_filter;
unsigned int  fps;
+#ifdef CONFIG_MEDIA_CONTROLLER
+   struct media_pad pad;
+#endif
 };
 
 /*
@@ -1316,16 +1319,26 @@ static int ov772x_probe(struct i2c_client *client,
if (ret < 0)
goto error_gpio_put;
 
+#ifdef CONFIG_MEDIA_CONTROLLER
+   priv->pad.flags = MEDIA_PAD_FL_SOURCE;
+   priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+   ret = media_entity_pads_init(>subdev.entity, 1, >pad);
+   if (ret < 0)
+   goto error_gpio_put;
+#endif
+
priv->cfmt = _cfmts[0];
priv->win = _win_sizes[0];
priv->fps = 15;
 
ret = v4l2_async_register_subdev(>subdev);
if (ret)
-   goto error_gpio_put;
+   goto error_entity_cleanup;
 
return 0;
 
+error_entity_cleanup:
+   media_entity_cleanup(>subdev.entity);
 error_gpio_put:
if (priv->pwdn_gpio)
gpiod_put(priv->pwdn_gpio);
@@ -1341,6 +1354,7 @@ static int ov772x_remove(struct i2c_client *client)
 {
struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client));
 
+   media_entity_cleanup(>subdev.entity);
clk_put(priv->clk);
if (priv->pwdn_gpio)
gpiod_put(priv->pwdn_gpio);
-- 
2.7.4



[PATCH v4 09/14] media: ov772x: handle nested s_power() calls

2018-04-29 Thread Akinobu Mita
Depending on the v4l2 driver, calling s_power() could be nested.  So the
actual transitions between power saving mode and normal operation mode
should only happen at the first power on and the last power off.

This adds an s_power() nesting counter and updates the power state if the
counter is modified from 0 to != 0 or from != 0 to 0.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Reviewed-by: Jacopo Mondi <jac...@jmondi.org>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v4
- Add Reviewed-by: line

 drivers/media/i2c/ov772x.c | 34 ++
 1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 621149a..edc013d 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -424,6 +424,9 @@ struct ov772x_priv {
/* band_filter = COM8[5] ? 256 - BDBASE : 0 */
unsigned shortband_filter;
unsigned int  fps;
+   /* lock to protect power_count */
+   struct mutex  lock;
+   int   power_count;
 #ifdef CONFIG_MEDIA_CONTROLLER
struct media_pad pad;
 #endif
@@ -871,9 +874,26 @@ static int ov772x_power_off(struct ov772x_priv *priv)
 static int ov772x_s_power(struct v4l2_subdev *sd, int on)
 {
struct ov772x_priv *priv = to_ov772x(sd);
+   int ret = 0;
+
+   mutex_lock(>lock);
+
+   /* If the power count is modified from 0 to != 0 or from != 0 to 0,
+* update the power state.
+*/
+   if (priv->power_count == !on)
+   ret = on ? ov772x_power_on(priv) : ov772x_power_off(priv);
+
+   if (!ret) {
+   /* Update the power count. */
+   priv->power_count += on ? 1 : -1;
+   WARN(priv->power_count < 0, "Unbalanced power count\n");
+   WARN(priv->power_count > 1, "Duplicated s_power call\n");
+   }
+
+   mutex_unlock(>lock);
 
-   return on ? ov772x_power_on(priv) :
-   ov772x_power_off(priv);
+   return ret;
 }
 
 static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height)
@@ -1303,6 +1323,7 @@ static int ov772x_probe(struct i2c_client *client,
return -ENOMEM;
 
priv->info = client->dev.platform_data;
+   mutex_init(>lock);
 
v4l2_i2c_subdev_init(>subdev, client, _subdev_ops);
v4l2_ctrl_handler_init(>hdl, 3);
@@ -1313,8 +1334,10 @@ static int ov772x_probe(struct i2c_client *client,
v4l2_ctrl_new_std(>hdl, _ctrl_ops,
  V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0);
priv->subdev.ctrl_handler = >hdl;
-   if (priv->hdl.error)
-   return priv->hdl.error;
+   if (priv->hdl.error) {
+   ret = priv->hdl.error;
+   goto error_mutex_destroy;
+   }
 
priv->clk = clk_get(>dev, NULL);
if (IS_ERR(priv->clk)) {
@@ -1362,6 +1385,8 @@ static int ov772x_probe(struct i2c_client *client,
clk_put(priv->clk);
 error_ctrl_free:
v4l2_ctrl_handler_free(>hdl);
+error_mutex_destroy:
+   mutex_destroy(>lock);
 
return ret;
 }
@@ -1376,6 +1401,7 @@ static int ov772x_remove(struct i2c_client *client)
gpiod_put(priv->pwdn_gpio);
v4l2_async_unregister_subdev(>subdev);
v4l2_ctrl_handler_free(>hdl);
+   mutex_destroy(>lock);
 
return 0;
 }
-- 
2.7.4



[PATCH v4 08/14] media: ov772x: support device tree probing

2018-04-29 Thread Akinobu Mita
The ov772x driver currently only supports legacy platform data probe.
This change enables device tree probing.

Note that the platform data probe can select auto or manual edge control
mode, but the device tree probling can only select auto edge control mode
for now.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Reviewed-by: Jacopo Mondi <jac...@jmondi.org>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v4
- No changes

 drivers/media/i2c/ov772x.c | 64 --
 1 file changed, 45 insertions(+), 19 deletions(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index f939e28..621149a 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -749,13 +749,13 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_VFLIP:
val = ctrl->val ? VFLIP_IMG : 0x00;
priv->flag_vflip = ctrl->val;
-   if (priv->info->flags & OV772X_FLAG_VFLIP)
+   if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP))
val ^= VFLIP_IMG;
return ov772x_mask_set(client, COM3, VFLIP_IMG, val);
case V4L2_CID_HFLIP:
val = ctrl->val ? HFLIP_IMG : 0x00;
priv->flag_hflip = ctrl->val;
-   if (priv->info->flags & OV772X_FLAG_HFLIP)
+   if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP))
val ^= HFLIP_IMG;
return ov772x_mask_set(client, COM3, HFLIP_IMG, val);
case V4L2_CID_BAND_STOP_FILTER:
@@ -914,19 +914,14 @@ static void ov772x_select_params(const struct 
v4l2_mbus_framefmt *mf,
*win = ov772x_select_win(mf->width, mf->height);
 }
 
-static int ov772x_set_params(struct ov772x_priv *priv,
-const struct ov772x_color_format *cfmt,
-const struct ov772x_win_size *win)
+static int ov772x_edgectrl(struct ov772x_priv *priv)
 {
struct i2c_client *client = v4l2_get_subdevdata(>subdev);
-   struct v4l2_fract tpf;
int ret;
-   u8  val;
 
-   /* Reset hardware. */
-   ov772x_reset(client);
+   if (!priv->info)
+   return 0;
 
-   /* Edge Ctrl. */
if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) {
/*
 * Manual Edge Control Mode.
@@ -937,19 +932,19 @@ static int ov772x_set_params(struct ov772x_priv *priv,
 
ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00);
if (ret < 0)
-   goto ov772x_set_fmt_error;
+   return ret;
 
ret = ov772x_mask_set(client,
  EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK,
  priv->info->edgectrl.threshold);
if (ret < 0)
-   goto ov772x_set_fmt_error;
+   return ret;
 
ret = ov772x_mask_set(client,
  EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK,
  priv->info->edgectrl.strength);
if (ret < 0)
-   goto ov772x_set_fmt_error;
+   return ret;
 
} else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) {
/*
@@ -961,15 +956,35 @@ static int ov772x_set_params(struct ov772x_priv *priv,
  EDGE_UPPER, OV772X_EDGE_UPPER_MASK,
  priv->info->edgectrl.upper);
if (ret < 0)
-   goto ov772x_set_fmt_error;
+   return ret;
 
ret = ov772x_mask_set(client,
  EDGE_LOWER, OV772X_EDGE_LOWER_MASK,
  priv->info->edgectrl.lower);
if (ret < 0)
-   goto ov772x_set_fmt_error;
+   return ret;
}
 
+   return 0;
+}
+
+static int ov772x_set_params(struct ov772x_priv *priv,
+const struct ov772x_color_format *cfmt,
+const struct ov772x_win_size *win)
+{
+   struct i2c_client *client = v4l2_get_subdevdata(>subdev);
+   struct v4l2_fract tpf;
+   int ret;
+   u8  val;
+
+   /* Reset hardware. */
+   ov772x_reset(client);
+
+   /* Edge Ctrl. */
+   ret =  ov772x_edgectrl(priv);
+   if (ret < 0)
+   return ret;
+
/* Format 

[PATCH v4 07/14] media: ov772x: omit consumer ID when getting clock reference

2018-04-29 Thread Akinobu Mita
Currently the ov772x driver obtains a clock with a specific consumer ID.
As there's a single clock for this driver, we could omit clock-names
property in device tree by passing NULL as a consumer ID to clk_get().

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Suggested-by: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Tested-by: Jacopo Mondi <jacopo+rene...@jmondi.org>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v4
- New patch

 arch/sh/boards/mach-migor/setup.c | 2 +-
 drivers/media/i2c/ov772x.c| 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/sh/boards/mach-migor/setup.c 
b/arch/sh/boards/mach-migor/setup.c
index 73b9ee4..11f8001 100644
--- a/arch/sh/boards/mach-migor/setup.c
+++ b/arch/sh/boards/mach-migor/setup.c
@@ -593,7 +593,7 @@ static int __init migor_devices_setup(void)
}
 
/* Add a clock alias for ov7725 xclk source. */
-   clk_add_alias("xclk", "0-0021", "video_clk", NULL);
+   clk_add_alias(NULL, "0-0021", "video_clk", NULL);
 
/* Register GPIOs for video sources. */
gpiod_add_lookup_table(_gpios);
diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 97a65ce..f939e28 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -1300,7 +1300,7 @@ static int ov772x_probe(struct i2c_client *client,
if (priv->hdl.error)
return priv->hdl.error;
 
-   priv->clk = clk_get(>dev, "xclk");
+   priv->clk = clk_get(>dev, NULL);
if (IS_ERR(priv->clk)) {
dev_err(>dev, "Unable to get xclk clock\n");
ret = PTR_ERR(priv->clk);
-- 
2.7.4



[PATCH v4 03/14] media: ov772x: allow i2c controllers without I2C_FUNC_PROTOCOL_MANGLING

2018-04-29 Thread Akinobu Mita
The ov772x driver only works when the i2c controller have
I2C_FUNC_PROTOCOL_MANGLING.  However, many i2c controller drivers don't
support it.

The reason that the ov772x requires I2C_FUNC_PROTOCOL_MANGLING is that
it doesn't support repeated starts.

This changes the reading ov772x register method so that it doesn't
require I2C_FUNC_PROTOCOL_MANGLING by calling two separated i2c messages.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Cc: Wolfram Sang <w...@the-dreams.de>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v4
- No changes

 drivers/media/i2c/ov772x.c | 20 ++--
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index e255070..b6223bf 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -542,9 +542,19 @@ static struct ov772x_priv *to_ov772x(struct v4l2_subdev 
*sd)
return container_of(sd, struct ov772x_priv, subdev);
 }
 
-static inline int ov772x_read(struct i2c_client *client, u8 addr)
+static int ov772x_read(struct i2c_client *client, u8 addr)
 {
-   return i2c_smbus_read_byte_data(client, addr);
+   int ret;
+   u8 val;
+
+   ret = i2c_master_send(client, , 1);
+   if (ret < 0)
+   return ret;
+   ret = i2c_master_recv(client, , 1);
+   if (ret < 0)
+   return ret;
+
+   return val;
 }
 
 static inline int ov772x_write(struct i2c_client *client, u8 addr, u8 value)
@@ -1255,13 +1265,11 @@ static int ov772x_probe(struct i2c_client *client,
return -EINVAL;
}
 
-   if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_PROTOCOL_MANGLING)) {
+   if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(>dev,
-   "I2C-Adapter doesn't support SMBUS_BYTE_DATA or 
PROTOCOL_MANGLING\n");
+   "I2C-Adapter doesn't support SMBUS_BYTE_DATA\n");
return -EIO;
}
-   client->flags |= I2C_CLIENT_SCCB;
 
priv = devm_kzalloc(>dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
-- 
2.7.4



[PATCH v4 13/14] media: ov772x: make set_fmt() return -EBUSY while streaming

2018-04-29 Thread Akinobu Mita
The ov772x driver is going to offer a V4L2 sub-device interface, so
changing the output data format on this sub-device can be made anytime.
However, the request is preferred to fail while the video stream on the
device is active.

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v4
- No changes

 drivers/media/i2c/ov772x.c | 35 ++-
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index bd37169..f3c4f78 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -424,9 +424,10 @@ struct ov772x_priv {
/* band_filter = COM8[5] ? 256 - BDBASE : 0 */
struct v4l2_ctrl *band_filter_ctrl;
unsigned int  fps;
-   /* lock to protect power_count */
+   /* lock to protect power_count and streaming */
struct mutex  lock;
int   power_count;
+   int   streaming;
 #ifdef CONFIG_MEDIA_CONTROLLER
struct media_pad pad;
 #endif
@@ -603,18 +604,28 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int 
enable)
 {
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov772x_priv *priv = to_ov772x(sd);
+   int ret = 0;
 
-   if (!enable) {
-   ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
-   return 0;
-   }
+   mutex_lock(>lock);
 
-   ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0);
+   if (priv->streaming == enable)
+   goto done;
 
-   dev_dbg(>dev, "format %d, win %s\n",
-   priv->cfmt->code, priv->win->name);
+   ret = ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE,
+ enable ? 0 : SOFT_SLEEP_MODE);
+   if (ret)
+   goto done;
 
-   return 0;
+   if (enable) {
+   dev_dbg(>dev, "format %d, win %s\n",
+   priv->cfmt->code, priv->win->name);
+   }
+   priv->streaming = enable;
+
+done:
+   mutex_unlock(>lock);
+
+   return ret;
 }
 
 static unsigned int ov772x_select_fps(struct ov772x_priv *priv,
@@ -1222,6 +1233,12 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd,
}
 
mutex_lock(>lock);
+
+   if (priv->streaming) {
+   ret = -EBUSY;
+   goto error;
+   }
+
/*
 * If the device is not powered up by the host driver do
 * not apply any changes to H/W at this time. Instead
-- 
2.7.4



[PATCH v4 02/14] media: ov772x: correct setting of banding filter

2018-04-29 Thread Akinobu Mita
The banding filter ON/OFF is controlled via bit 5 of COM8 register.  It
is attempted to be enabled in ov772x_set_params() by the following line.

ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1);

But this unexpectedly results disabling the banding filter, because the
mask and set bits are exclusive.

On the other hand, ov772x_s_ctrl() correctly sets the bit by:

ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF);

Cc: Jacopo Mondi <jacopo+rene...@jmondi.org>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Hans Verkuil <hans.verk...@cisco.com>
Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
---
* v4
- New patch

 drivers/media/i2c/ov772x.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index b62860c..e255070 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -1035,7 +1035,7 @@ static int ov772x_set_params(struct ov772x_priv *priv,
 
/* Set COM8. */
if (priv->band_filter) {
-   ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1);
+   ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF);
if (!ret)
ret = ov772x_mask_set(client, BDBASE,
  0xff, 256 - priv->band_filter);
-- 
2.7.4



  1   2   3   >