diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index c418add65bb5..6257e71362c7 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -996,7 +996,7 @@ static void em28xx_v4l2_media_release(struct em28xx *dev) #ifdef CONFIG_MEDIA_CONTROLLER int i; - if (dev->board.decoder == EM28XX_BUILTIN) { + if (dev->board.decoder == EM28XX_BUILTIN && dev->v4l2->decoder) { media_device_unregister_entity(dev->v4l2->decoder); kfree(dev->v4l2->decoder); dev->v4l2->decoder = NULL; @@ -2305,9 +2305,14 @@ static void em28xx_free_v4l2(struct v4l2_device *v4l2_dev) container_of(v4l2_dev, struct em28xx_v4l2, v4l2_dev); struct em28xx *dev = v4l2->dev; + mutex_lock(&dev->lock); + v4l2_ctrl_handler_free(&v4l2->ctrl_handler); v4l2_device_unregister(v4l2_dev); dev->v4l2 = NULL; + + mutex_unlock(&dev->lock); + kfree(v4l2); kref_put(&dev->ref, em28xx_free_device); } @@ -2359,6 +2364,11 @@ static int em28xx_v4l2_open(struct file *filp) } if (v4l2_fh_is_singular_file(filp)) { + /* First user takes extra refcount of + * video device used by v4l2_fh_init(). + */ + v4l2_device_get(&v4l2->v4l2_dev); + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); if (vdev->vfl_type != VFL_TYPE_RADIO) @@ -2388,25 +2398,24 @@ static int em28xx_v4l2_open(struct file *filp) */ static int em28xx_v4l2_fini(struct em28xx *dev) { - struct em28xx_v4l2 *v4l2 = dev->v4l2; + struct em28xx_v4l2 *v4l2; - if (dev->is_audio_only) { - /* Shouldn't initialize IR for this interface */ - return 0; - } + mutex_lock(&dev->lock); - if (!dev->has_video) { - /* This device does not support the v4l2 extension */ + if (dev->is_audio_only || /* Shouldn't initialize IR for this interface */ + !dev->has_video) { /* This device does not support the v4l2 extension */ + mutex_unlock(&dev->lock); return 0; } - if (!v4l2) + v4l2 = dev->v4l2; + if (!v4l2) { + mutex_unlock(&dev->lock); return 0; + } dev_info(&dev->intf->dev, "Closing video extension\n"); - mutex_lock(&dev->lock); - v4l2_device_disconnect(&v4l2->v4l2_dev); em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); @@ -2504,6 +2513,11 @@ static int em28xx_v4l2_close(struct file *filp) "cannot change alternate number to 0 (error=%i)\n", err); } + + /* Last user drops extra refcount + * taken by em28xx_v4l2_open(). + */ + v4l2_device_put(&v4l2->v4l2_dev); } exit: @@ -2992,13 +3006,19 @@ static int em28xx_v4l2_init(struct em28xx *dev) goto unregister_dev; } + v4l2->vb_vbiq.lock = &v4l2->vb_vbi_queue_lock; + ret = em28xx_vb2_setup(dev); + if (ret) { + dev_err(&dev->intf->dev, "unable to setup device queues, error %d\n", ret); + goto unregister_dev; + } + /* Allocate and fill vbi video_device struct */ if (em28xx_vbi_supported(dev) == 1) { em28xx_vdev_init(dev, &v4l2->vbi_dev, &em28xx_video_template, "vbi"); v4l2->vbi_dev.queue = &v4l2->vb_vbiq; - v4l2->vbi_dev.queue->lock = &v4l2->vb_vbi_queue_lock; v4l2->vbi_dev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; if ((v4l2->vdev.device_caps & V4L2_CAP_TUNER) == 0) @@ -3068,9 +3088,6 @@ static int em28xx_v4l2_init(struct em28xx *dev) /* Save some power by putting tuner to sleep */ v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, standby); - /* initialize videobuf2 stuff */ - em28xx_vb2_setup(dev); - dev_info(&dev->intf->dev, "V4L2 extension successfully initialized\n");