Commit ea86968f authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

[media] au0828: improve firmware loading & locking

- open/close/read and poll need to take the core lock as well.
- when the tuner goes to sleep we should set std_set_in_tuner_core
  to 0 since the tuner loses the firmware at that time.
- initialize the tuner if std_set_in_tuner_core == 0 whenever:
  1) g/s_tuner, s_std or s_frequency is called
  2) read or poll is called
  3) streamon is called
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Reviewed-by: default avatarDevin Heitmueller <dheitmueller@kernellabs.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 56230d1e
...@@ -998,11 +998,17 @@ static int au0828_v4l2_open(struct file *filp) ...@@ -998,11 +998,17 @@ static int au0828_v4l2_open(struct file *filp)
v4l2_fh_init(&fh->fh, vdev); v4l2_fh_init(&fh->fh, vdev);
filp->private_data = fh; filp->private_data = fh;
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { if (mutex_lock_interruptible(&dev->lock)) {
kfree(fh);
return -ERESTARTSYS;
}
if (dev->users == 0) {
/* set au0828 interface0 to AS5 here again */ /* set au0828 interface0 to AS5 here again */
ret = usb_set_interface(dev->usbdev, 0, 5); ret = usb_set_interface(dev->usbdev, 0, 5);
if (ret < 0) { if (ret < 0) {
mutex_unlock(&dev->lock);
printk(KERN_INFO "Au0828 can't set alternate to 5!\n"); printk(KERN_INFO "Au0828 can't set alternate to 5!\n");
kfree(fh);
return -EBUSY; return -EBUSY;
} }
...@@ -1017,6 +1023,7 @@ static int au0828_v4l2_open(struct file *filp) ...@@ -1017,6 +1023,7 @@ static int au0828_v4l2_open(struct file *filp)
} }
dev->users++; dev->users++;
mutex_unlock(&dev->lock);
videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops, videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops,
NULL, &dev->slock, NULL, &dev->slock,
...@@ -1044,6 +1051,7 @@ static int au0828_v4l2_close(struct file *filp) ...@@ -1044,6 +1051,7 @@ static int au0828_v4l2_close(struct file *filp)
v4l2_fh_del(&fh->fh); v4l2_fh_del(&fh->fh);
v4l2_fh_exit(&fh->fh); v4l2_fh_exit(&fh->fh);
mutex_lock(&dev->lock);
if (res_check(fh, AU0828_RESOURCE_VIDEO)) { if (res_check(fh, AU0828_RESOURCE_VIDEO)) {
/* Cancel timeout thread in case they didn't call streamoff */ /* Cancel timeout thread in case they didn't call streamoff */
dev->vid_timeout_running = 0; dev->vid_timeout_running = 0;
...@@ -1069,6 +1077,7 @@ static int au0828_v4l2_close(struct file *filp) ...@@ -1069,6 +1077,7 @@ static int au0828_v4l2_close(struct file *filp)
/* Save some power by putting tuner to sleep */ /* Save some power by putting tuner to sleep */
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
dev->std_set_in_tuner_core = 0;
/* When close the device, set the usb intf0 into alt0 to free /* When close the device, set the usb intf0 into alt0 to free
USB bandwidth */ USB bandwidth */
...@@ -1076,6 +1085,7 @@ static int au0828_v4l2_close(struct file *filp) ...@@ -1076,6 +1085,7 @@ static int au0828_v4l2_close(struct file *filp)
if (ret < 0) if (ret < 0)
printk(KERN_INFO "Au0828 can't set alternate to 0!\n"); printk(KERN_INFO "Au0828 can't set alternate to 0!\n");
} }
mutex_unlock(&dev->lock);
videobuf_mmap_free(&fh->vb_vidq); videobuf_mmap_free(&fh->vb_vidq);
videobuf_mmap_free(&fh->vb_vbiq); videobuf_mmap_free(&fh->vb_vbiq);
...@@ -1085,6 +1095,26 @@ static int au0828_v4l2_close(struct file *filp) ...@@ -1085,6 +1095,26 @@ static int au0828_v4l2_close(struct file *filp)
return 0; return 0;
} }
/* Must be called with dev->lock held */
static void au0828_init_tuner(struct au0828_dev *dev)
{
struct v4l2_frequency f = {
.frequency = dev->ctrl_freq,
.type = V4L2_TUNER_ANALOG_TV,
};
if (dev->std_set_in_tuner_core)
return;
dev->std_set_in_tuner_core = 1;
i2c_gate_ctrl(dev, 1);
/* If we've never sent the standard in tuner core, do so now.
We don't do this at device probe because we don't want to
incur the cost of a firmware load */
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->std);
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
i2c_gate_ctrl(dev, 0);
}
static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf, static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf,
size_t count, loff_t *pos) size_t count, loff_t *pos)
{ {
...@@ -1096,6 +1126,11 @@ static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf, ...@@ -1096,6 +1126,11 @@ static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf,
if (rc < 0) if (rc < 0)
return rc; return rc;
if (mutex_lock_interruptible(&dev->lock))
return -ERESTARTSYS;
au0828_init_tuner(dev);
mutex_unlock(&dev->lock);
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
if (res_locked(dev, AU0828_RESOURCE_VIDEO)) if (res_locked(dev, AU0828_RESOURCE_VIDEO))
return -EBUSY; return -EBUSY;
...@@ -1136,6 +1171,11 @@ static unsigned int au0828_v4l2_poll(struct file *filp, poll_table *wait) ...@@ -1136,6 +1171,11 @@ static unsigned int au0828_v4l2_poll(struct file *filp, poll_table *wait)
if (!(req_events & (POLLIN | POLLRDNORM))) if (!(req_events & (POLLIN | POLLRDNORM)))
return res; return res;
if (mutex_lock_interruptible(&dev->lock))
return -ERESTARTSYS;
au0828_init_tuner(dev);
mutex_unlock(&dev->lock);
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
if (!res_get(fh, AU0828_RESOURCE_VIDEO)) if (!res_get(fh, AU0828_RESOURCE_VIDEO))
return POLLERR; return POLLERR;
...@@ -1319,6 +1359,10 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) ...@@ -1319,6 +1359,10 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
struct au0828_fh *fh = priv; struct au0828_fh *fh = priv;
struct au0828_dev *dev = fh->dev; struct au0828_dev *dev = fh->dev;
dev->std = norm;
au0828_init_tuner(dev);
i2c_gate_ctrl(dev, 1); i2c_gate_ctrl(dev, 1);
/* FIXME: when we support something other than NTSC, we are going to /* FIXME: when we support something other than NTSC, we are going to
...@@ -1326,10 +1370,8 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) ...@@ -1326,10 +1370,8 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
buffer, which is currently hardcoded at 720x480 */ buffer, which is currently hardcoded at 720x480 */
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, norm); v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, norm);
dev->std_set_in_tuner_core = 1;
i2c_gate_ctrl(dev, 0); i2c_gate_ctrl(dev, 0);
dev->std = norm;
return 0; return 0;
} }
...@@ -1506,7 +1548,11 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) ...@@ -1506,7 +1548,11 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
return -EINVAL; return -EINVAL;
strcpy(t->name, "Auvitek tuner"); strcpy(t->name, "Auvitek tuner");
au0828_init_tuner(dev);
i2c_gate_ctrl(dev, 1);
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
i2c_gate_ctrl(dev, 0);
return 0; return 0;
} }
...@@ -1519,10 +1565,9 @@ static int vidioc_s_tuner(struct file *file, void *priv, ...@@ -1519,10 +1565,9 @@ static int vidioc_s_tuner(struct file *file, void *priv,
if (t->index != 0) if (t->index != 0)
return -EINVAL; return -EINVAL;
au0828_init_tuner(dev);
i2c_gate_ctrl(dev, 1); i2c_gate_ctrl(dev, 1);
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
i2c_gate_ctrl(dev, 0); i2c_gate_ctrl(dev, 0);
dprintk(1, "VIDIOC_S_TUNER: signal = %x, afc = %x\n", t->signal, dprintk(1, "VIDIOC_S_TUNER: signal = %x, afc = %x\n", t->signal,
...@@ -1554,17 +1599,9 @@ static int vidioc_s_frequency(struct file *file, void *priv, ...@@ -1554,17 +1599,9 @@ static int vidioc_s_frequency(struct file *file, void *priv,
if (freq->tuner != 0) if (freq->tuner != 0)
return -EINVAL; return -EINVAL;
au0828_init_tuner(dev);
i2c_gate_ctrl(dev, 1); i2c_gate_ctrl(dev, 1);
if (dev->std_set_in_tuner_core == 0) {
/* If we've never sent the standard in tuner core, do so now.
We don't do this at device probe because we don't want to
incur the cost of a firmware load */
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std,
dev->vdev->tvnorms);
dev->std_set_in_tuner_core = 1;
}
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, freq); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, freq);
/* Get the actual set (and possibly clamped) frequency */ /* Get the actual set (and possibly clamped) frequency */
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, &new_freq); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, &new_freq);
...@@ -1663,6 +1700,7 @@ static int vidioc_streamon(struct file *file, void *priv, ...@@ -1663,6 +1700,7 @@ static int vidioc_streamon(struct file *file, void *priv,
if (unlikely(!res_get(fh, get_ressource(fh)))) if (unlikely(!res_get(fh, get_ressource(fh))))
return -EBUSY; return -EBUSY;
au0828_init_tuner(dev);
if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
au0828_analog_stream_enable(dev); au0828_analog_stream_enable(dev);
v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment