diff options
Diffstat (limited to 'drivers/media/usb/au0828/au0828-video.c')
-rw-r--r-- | drivers/media/usb/au0828/au0828-video.c | 253 |
1 files changed, 163 insertions, 90 deletions
diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index a13625722848..32d7db96479c 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -28,12 +28,14 @@ */ #include "au0828.h" +#include "au8522.h" #include <linux/module.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/device.h> #include <media/v4l2-common.h> +#include <media/v4l2-mc.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-event.h> #include <media/tuner.h> @@ -104,14 +106,13 @@ static inline void print_err_status(struct au0828_dev *dev, static int check_dev(struct au0828_dev *dev) { - if (dev->dev_state & DEV_DISCONNECTED) { + if (test_bit(DEV_DISCONNECTED, &dev->dev_state)) { pr_info("v4l2 ioctl: device not present\n"); return -ENODEV; } - if (dev->dev_state & DEV_MISCONFIGURED) { - pr_info("v4l2 ioctl: device is misconfigured; " - "close and open it again\n"); + if (test_bit(DEV_MISCONFIGURED, &dev->dev_state)) { + pr_info("v4l2 ioctl: device is misconfigured; close and open it again\n"); return -EIO; } return 0; @@ -519,8 +520,8 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) if (!dev) return 0; - if ((dev->dev_state & DEV_DISCONNECTED) || - (dev->dev_state & DEV_MISCONFIGURED)) + if (test_bit(DEV_DISCONNECTED, &dev->dev_state) || + test_bit(DEV_MISCONFIGURED, &dev->dev_state)) return 0; if (urb->status < 0) { @@ -638,61 +639,64 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) return rc; } -static int au0828_enable_analog_tuner(struct au0828_dev *dev) +void au0828_usb_v4l2_media_release(struct au0828_dev *dev) { #ifdef CONFIG_MEDIA_CONTROLLER - struct media_device *mdev = dev->media_dev; - struct media_entity *source; - struct media_link *link, *found_link = NULL; - int ret, active_links = 0; - - if (!mdev || !dev->decoder) - return 0; + int i; - /* - * This will find the tuner that is connected into the decoder. - * Technically, this is not 100% correct, as the device may be - * using an analog input instead of the tuner. However, as we can't - * do DVB streaming while the DMA engine is being used for V4L2, - * this should be enough for the actual needs. - */ - list_for_each_entry(link, &dev->decoder->links, list) { - if (link->sink->entity == dev->decoder) { - found_link = link; - if (link->flags & MEDIA_LNK_FL_ENABLED) - active_links++; - break; - } + for (i = 0; i < AU0828_MAX_INPUT; i++) { + if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED) + return; + media_device_unregister_entity(&dev->input_ent[i]); } +#endif +} - if (active_links == 1 || !found_link) - return 0; +static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev) +{ + struct au0828_dev *dev = + container_of(v4l2_dev, struct au0828_dev, v4l2_dev); + + v4l2_ctrl_handler_free(&dev->v4l2_ctrl_hdl); + v4l2_device_unregister(&dev->v4l2_dev); + au0828_usb_v4l2_media_release(dev); + au0828_usb_release(dev); +} - source = found_link->source->entity; - list_for_each_entry(link, &source->links, list) { - struct media_entity *sink; - int flags = 0; +int au0828_v4l2_device_register(struct usb_interface *interface, + struct au0828_dev *dev) +{ + int retval; - sink = link->sink->entity; + if (AUVI_INPUT(0).type == AU0828_VMUX_UNDEFINED) + return 0; - if (sink == dev->decoder) - flags = MEDIA_LNK_FL_ENABLED; + /* Create the v4l2_device */ +#ifdef CONFIG_MEDIA_CONTROLLER + dev->v4l2_dev.mdev = dev->media_dev; +#endif + retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); + if (retval) { + pr_err("%s() v4l2_device_register failed\n", + __func__); + mutex_unlock(&dev->lock); + kfree(dev); + return retval; + } - ret = media_entity_setup_link(link, flags); - if (ret) { - pr_err( - "Couldn't change link %s->%s to %s. Error %d\n", - source->name, sink->name, - flags ? "enabled" : "disabled", - ret); - return ret; - } else - au0828_isocdbg( - "link %s->%s was %s\n", - source->name, sink->name, - flags ? "ENABLED" : "disabled"); + dev->v4l2_dev.release = au0828_usb_v4l2_release; + + /* This control handler will inherit the controls from au8522 */ + retval = v4l2_ctrl_handler_init(&dev->v4l2_ctrl_hdl, 4); + if (retval) { + pr_err("%s() v4l2_ctrl_handler_init failed\n", + __func__); + mutex_unlock(&dev->lock); + kfree(dev); + return retval; } -#endif + dev->v4l2_dev.ctrl_handler = &dev->v4l2_ctrl_hdl; + return 0; } @@ -707,9 +711,6 @@ static int queue_setup(struct vb2_queue *vq, return sizes[0] < size ? -EINVAL : 0; *nplanes = 1; sizes[0] = size; - - au0828_enable_analog_tuner(dev); - return 0; } @@ -822,10 +823,10 @@ static int au0828_stream_interrupt(struct au0828_dev *dev) int ret = 0; dev->stream_state = STREAM_INTERRUPT; - if (dev->dev_state == DEV_DISCONNECTED) + if (test_bit(DEV_DISCONNECTED, &dev->dev_state)) return -ENODEV; else if (ret) { - dev->dev_state = DEV_MISCONFIGURED; + set_bit(DEV_MISCONFIGURED, &dev->dev_state); dprintk(1, "%s device is misconfigured!\n", __func__); return ret; } @@ -949,13 +950,23 @@ static struct vb2_ops au0828_video_qops = { * au0828_analog_unregister * unregister v4l2 devices */ -void au0828_analog_unregister(struct au0828_dev *dev) +int au0828_analog_unregister(struct au0828_dev *dev) { dprintk(1, "au0828_analog_unregister called\n"); + + /* No analog TV */ + if (AUVI_INPUT(0).type == AU0828_VMUX_UNDEFINED) + return 0; + mutex_lock(&au0828_sysfs_lock); video_unregister_device(&dev->vdev); video_unregister_device(&dev->vbi_dev); mutex_unlock(&au0828_sysfs_lock); + + v4l2_device_disconnect(&dev->v4l2_dev); + v4l2_device_put(&dev->v4l2_dev); + + return 1; } /* This function ensures that video frames continue to be delivered even if @@ -1014,7 +1025,7 @@ static int au0828_v4l2_open(struct file *filp) int ret; dprintk(1, - "%s called std_set %d dev_state %d stream users %d users %d\n", + "%s called std_set %d dev_state %ld stream users %d users %d\n", __func__, dev->std_set_in_tuner_core, dev->dev_state, dev->streaming_users, dev->users); @@ -1033,7 +1044,7 @@ static int au0828_v4l2_open(struct file *filp) au0828_analog_stream_enable(dev); au0828_analog_stream_reset(dev); dev->stream_state = STREAM_OFF; - dev->dev_state |= DEV_INITIALIZED; + set_bit(DEV_INITIALIZED, &dev->dev_state); } dev->users++; mutex_unlock(&dev->lock); @@ -1047,7 +1058,7 @@ static int au0828_v4l2_close(struct file *filp) struct video_device *vdev = video_devdata(filp); dprintk(1, - "%s called std_set %d dev_state %d stream users %d users %d\n", + "%s called std_set %d dev_state %ld stream users %d users %d\n", __func__, dev->std_set_in_tuner_core, dev->dev_state, dev->streaming_users, dev->users); @@ -1063,12 +1074,43 @@ static int au0828_v4l2_close(struct file *filp) del_timer_sync(&dev->vbi_timeout); } - if (dev->dev_state == DEV_DISCONNECTED) + if (test_bit(DEV_DISCONNECTED, &dev->dev_state)) goto end; if (dev->users == 1) { - /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); + /* + * Avoid putting tuner in sleep if DVB or ALSA are + * streaming. + * + * On most USB devices like au0828 the tuner can + * be safely put in sleep stare here if ALSA isn't + * streaming. Exceptions are some very old USB tuner + * models such as em28xx-based WinTV USB2 which have + * a separate audio output jack. The devices that have + * a separate audio output jack have analog tuners, + * like Philips FM1236. Those devices are always on, + * so the s_power callback are silently ignored. + * So, the current logic here does the following: + * Disable (put tuner to sleep) when + * - ALSA and DVB aren't not streaming; + * - the last V4L2 file handler is closed. + * + * FIXME: + * + * Additionally, this logic could be improved to + * disable the media source if the above conditions + * are met and if the device: + * - doesn't have a separate audio out plug (or + * - doesn't use a silicon tuner like xc2028/3028/4000/5000). + * + * Once this additional logic is in place, a callback + * is needed to enable the media source and power on + * the tuner, for radio to work. + */ + ret = v4l_enable_media_source(vdev); + if (ret == 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 @@ -1092,7 +1134,7 @@ static void au0828_init_tuner(struct au0828_dev *dev) .type = V4L2_TUNER_ANALOG_TV, }; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); if (dev->std_set_in_tuner_core) @@ -1164,7 +1206,7 @@ static int vidioc_querycap(struct file *file, void *priv, struct video_device *vdev = video_devdata(file); struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); strlcpy(cap->driver, "au0828", sizeof(cap->driver)); @@ -1207,7 +1249,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); f->fmt.pix.width = dev->width; @@ -1226,7 +1268,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); return au0828_set_format(dev, VIDIOC_TRY_FMT, f); @@ -1238,7 +1280,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct au0828_dev *dev = video_drvdata(file); int rc; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); rc = check_dev(dev); @@ -1260,7 +1302,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); if (norm == dev->std) @@ -1292,7 +1334,7 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); *norm = dev->std; @@ -1312,10 +1354,9 @@ static int vidioc_enum_input(struct file *file, void *priv, [AU0828_VMUX_CABLE] = "Cable TV", [AU0828_VMUX_TELEVISION] = "Television", [AU0828_VMUX_DVB] = "DVB", - [AU0828_VMUX_DEBUG] = "tv debug" }; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); tmp = input->index; @@ -1345,7 +1386,7 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); *i = dev->ctrl_input; @@ -1356,7 +1397,7 @@ static void au0828_s_input(struct au0828_dev *dev, int index) { int i; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); switch (AUVI_INPUT(index).type) { @@ -1375,9 +1416,11 @@ static void au0828_s_input(struct au0828_dev *dev, int index) default: dprintk(1, "unknown input type set [%d]\n", AUVI_INPUT(index).type); - break; + return; } + dev->ctrl_input = index; + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, AUVI_INPUT(index).vmux, 0, 0); @@ -1409,6 +1452,7 @@ static void au0828_s_input(struct au0828_dev *dev, int index) static int vidioc_s_input(struct file *file, void *priv, unsigned int index) { struct au0828_dev *dev = video_drvdata(file); + struct video_device *vfd = video_devdata(file); dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__, index); @@ -1416,9 +1460,19 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int index) return -EINVAL; if (AUVI_INPUT(index).type == 0) return -EINVAL; - dev->ctrl_input = index; + + if (dev->ctrl_input == index) + return 0; + au0828_s_input(dev, index); - return 0; + + /* + * Input has been changed. Disable the media source + * associated with the old input and enable source + * for the newly set input + */ + v4l_disable_media_source(vfd); + return v4l_enable_media_source(vfd); } static int vidioc_enumaudio(struct file *file, void *priv, struct v4l2_audio *a) @@ -1441,7 +1495,7 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); a->index = dev->ctrl_ainput; @@ -1461,7 +1515,7 @@ static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio if (a->index != dev->ctrl_ainput) return -EINVAL; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); return 0; } @@ -1469,11 +1523,17 @@ static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { struct au0828_dev *dev = video_drvdata(file); + struct video_device *vfd = video_devdata(file); + int ret; if (t->index != 0) return -EINVAL; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + ret = v4l_enable_media_source(vfd); + if (ret) + return ret; + + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); strcpy(t->name, "Auvitek tuner"); @@ -1493,7 +1553,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, if (t->index != 0) return -EINVAL; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); au0828_init_tuner(dev); @@ -1515,7 +1575,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, if (freq->tuner != 0) return -EINVAL; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); freq->frequency = dev->ctrl_freq; return 0; @@ -1530,7 +1590,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (freq->tuner != 0) return -EINVAL; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); au0828_init_tuner(dev); @@ -1556,7 +1616,7 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); format->fmt.vbi.samples_per_line = dev->vbi_width; @@ -1582,7 +1642,7 @@ static int vidioc_cropcap(struct file *file, void *priv, if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); cc->bounds.left = 0; @@ -1604,7 +1664,7 @@ static int vidioc_g_register(struct file *file, void *priv, { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); reg->val = au0828_read(dev, reg->reg); @@ -1617,7 +1677,7 @@ static int vidioc_s_register(struct file *file, void *priv, { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); return au0828_writereg(dev, reg->reg, reg->val); @@ -1804,7 +1864,6 @@ static void au0828_analog_create_entities(struct au0828_dev *dev) [AU0828_VMUX_CABLE] = "Cable TV", [AU0828_VMUX_TELEVISION] = "Television", [AU0828_VMUX_DVB] = "DVB", - [AU0828_VMUX_DEBUG] = "tv debug" }; int ret, i; @@ -1840,10 +1899,9 @@ static void au0828_analog_create_entities(struct au0828_dev *dev) case AU0828_VMUX_CABLE: case AU0828_VMUX_TELEVISION: case AU0828_VMUX_DVB: + default: /* Just to shut up a warning */ ent->function = MEDIA_ENT_F_CONN_RF; break; - default: /* AU0828_VMUX_DEBUG */ - continue; } ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]); @@ -1870,6 +1928,10 @@ int au0828_analog_register(struct au0828_dev *dev, dprintk(1, "au0828_analog_register called for intf#%d!\n", interface->cur_altsetting->desc.bInterfaceNumber); + /* No analog TV */ + if (AUVI_INPUT(0).type == AU0828_VMUX_UNDEFINED) + return 0; + /* set au0828 usb interface0 to as5 */ retval = usb_set_interface(dev->usbdev, interface->cur_altsetting->desc.bInterfaceNumber, 5); @@ -1924,6 +1986,7 @@ int au0828_analog_register(struct au0828_dev *dev, dev->ctrl_ainput = 0; dev->ctrl_freq = 960; dev->std = V4L2_STD_NTSC_M; + /* Default input is TV Tuner */ au0828_s_input(dev, 0); mutex_init(&dev->vb_queue_lock); @@ -1976,6 +2039,16 @@ int au0828_analog_register(struct au0828_dev *dev, goto err_reg_vbi_dev; } +#ifdef CONFIG_MEDIA_CONTROLLER + retval = v4l2_mc_create_media_graph(dev->media_dev); + if (retval) { + pr_err("%s() au0282_dev_register failed to create graph\n", + __func__); + ret = -ENODEV; + goto err_reg_vbi_dev; + } +#endif + dprintk(1, "%s completed!\n", __func__); return 0; |