From bf1805e0c8c4fc05e2a13b0a03b510ff4e523418 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Tue, 29 Jan 2019 15:56:10 +0000 Subject: [PATCH] media:bcm2835-unicam: Power on subdev on open/release, not streaming The driver was powering on the source subdevice as part of STREAMON, and powering it off in STREAMOFF. This isn't so great if there is a significant amount of setup required for your device. Copy the approach taken in the Atmel ISC driver where s_power(1) is called on first file handle open, and s_power(0) is called on the last release. See https://www.raspberrypi.org/forums/viewtopic.php?f=43&t=232437 Signed-off-by: Dave Stevenson --- .../media/platform/bcm2835/bcm2835-unicam.c | 68 +++++++++++++++---- 1 file changed, 54 insertions(+), 14 deletions(-) --- a/drivers/media/platform/bcm2835/bcm2835-unicam.c +++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c @@ -1237,11 +1237,6 @@ static int unicam_start_streaming(struct unicam_err(dev, "Failed to enable CSI clock: %d\n", ret); goto err_pm_put; } - ret = v4l2_subdev_call(dev->sensor, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD) { - unicam_err(dev, "power on failed in subdev\n"); - goto err_clock_unprepare; - } dev->streaming = 1; unicam_start_rx(dev, addr); @@ -1256,8 +1251,6 @@ static int unicam_start_streaming(struct err_disable_unicam: unicam_disable(dev); - v4l2_subdev_call(dev->sensor, core, s_power, 0); -err_clock_unprepare: clk_disable_unprepare(dev->clock); err_pm_put: unicam_runtime_put(dev); @@ -1306,11 +1299,6 @@ static void unicam_stop_streaming(struct dev->next_frm = NULL; spin_unlock_irqrestore(&dev->dma_queue_lock, flags); - if (v4l2_subdev_has_op(dev->sensor, core, s_power)) { - if (v4l2_subdev_call(dev->sensor, core, s_power, 0) < 0) - unicam_err(dev, "power off failed in subdev\n"); - } - clk_disable_unprepare(dev->clock); unicam_runtime_put(dev); } @@ -1543,11 +1531,63 @@ static const struct vb2_ops unicam_video .stop_streaming = unicam_stop_streaming, }; +/* + * unicam_open : This function is based on the v4l2_fh_open helper function. + * It has been augmented to handle sensor subdevice power management, + */ +static int unicam_open(struct file *file) +{ + struct unicam_device *dev = video_drvdata(file); + int ret; + + mutex_lock(&dev->lock); + + ret = v4l2_fh_open(file); + if (ret) { + unicam_err(dev, "v4l2_fh_open failed\n"); + goto unlock; + } + + if (!v4l2_fh_is_singular_file(file)) + goto unlock; + + ret = v4l2_subdev_call(dev->sensor, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + v4l2_fh_release(file); + goto unlock; + } + +unlock: + mutex_unlock(&dev->lock); + return ret; +} + +static int unicam_release(struct file *file) +{ + struct unicam_device *dev = video_drvdata(file); + struct v4l2_subdev *sd = dev->sensor; + bool fh_singular; + int ret; + + mutex_lock(&dev->lock); + + fh_singular = v4l2_fh_is_singular_file(file); + + ret = _vb2_fop_release(file, NULL); + + if (fh_singular) + v4l2_subdev_call(sd, core, s_power, 0); + + mutex_unlock(&dev->lock); + + return ret; +} + /* unicam capture driver file operations */ static const struct v4l2_file_operations unicam_fops = { .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, + .open = unicam_open, + .release = unicam_release, .read = vb2_fop_read, .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2,