diff options
Diffstat (limited to 'drivers/media/usb/au0828')
-rw-r--r-- | drivers/media/usb/au0828/Kconfig | 8 | ||||
-rw-r--r-- | drivers/media/usb/au0828/au0828-core.c | 196 | ||||
-rw-r--r-- | drivers/media/usb/au0828/au0828-video.c | 20 | ||||
-rw-r--r-- | drivers/media/usb/au0828/au0828.h | 6 |
4 files changed, 173 insertions, 57 deletions
diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig index 65fc067eb864..0ad985542c60 100644 --- a/drivers/media/usb/au0828/Kconfig +++ b/drivers/media/usb/au0828/Kconfig @@ -2,6 +2,8 @@ config VIDEO_AU0828 tristate "Auvitek AU0828 support" depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2 + select MEDIA_CONTROLLER + select MEDIA_CONTROLLER_DVB select I2C_ALGOBIT select VIDEO_TVEEPROM select VIDEOBUF2_VMALLOC if VIDEO_V4L2 @@ -9,7 +11,7 @@ config VIDEO_AU0828 select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT - ---help--- + help This is a hybrid analog/digital tv capture driver for Auvitek's AU0828 USB device. @@ -23,7 +25,7 @@ config VIDEO_AU0828_V4L2 select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT select VIDEO_TUNER default y - ---help--- + help This is a video4linux driver for Auvitek's USB device. Choose Y here to include support for v4l2 analog video @@ -34,5 +36,5 @@ config VIDEO_AU0828_RC depends on RC_CORE depends on !(RC_CORE=m && VIDEO_AU0828=y) depends on VIDEO_AU0828 - ---help--- + help Enables Remote Controller support on au0828 driver. diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index 3f8c92a70116..925a80437822 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -155,9 +155,7 @@ static void au0828_unregister_media_device(struct au0828_dev *dev) dev->media_dev->disable_source = NULL; mutex_unlock(&mdev->graph_mutex); - media_device_unregister(dev->media_dev); - media_device_cleanup(dev->media_dev); - kfree(dev->media_dev); + media_device_delete(dev->media_dev, KBUILD_MODNAME, THIS_MODULE); dev->media_dev = NULL; #endif } @@ -210,14 +208,10 @@ static int au0828_media_device_init(struct au0828_dev *dev, #ifdef CONFIG_MEDIA_CONTROLLER struct media_device *mdev; - mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + mdev = media_device_usb_allocate(udev, KBUILD_MODNAME, THIS_MODULE); if (!mdev) return -ENOMEM; - /* check if media device is already initialized */ - if (!mdev->dev) - media_device_usb_init(mdev, udev, udev->product); - dev->media_dev = mdev; #endif return 0; @@ -278,6 +272,28 @@ create_link: } } +static bool au0828_is_link_shareable(struct media_entity *owner, + struct media_entity *entity) +{ + bool shareable = false; + + /* Tuner link can be shared by audio, video, and VBI */ + switch (owner->function) { + case MEDIA_ENT_F_IO_V4L: + case MEDIA_ENT_F_AUDIO_CAPTURE: + case MEDIA_ENT_F_IO_VBI: + if (entity->function == MEDIA_ENT_F_IO_V4L || + entity->function == MEDIA_ENT_F_AUDIO_CAPTURE || + entity->function == MEDIA_ENT_F_IO_VBI) + shareable = true; + break; + case MEDIA_ENT_F_DTV_DEMOD: + default: + break; + } + return shareable; +} + /* Callers should hold graph_mutex */ static int au0828_enable_source(struct media_entity *entity, struct media_pipeline *pipe) @@ -320,18 +336,20 @@ static int au0828_enable_source(struct media_entity *entity, /* * Default input is tuner and default input_type * is AU0828_VMUX_TELEVISION. - * FIXME: + * * There is a problem when s_input is called to * change the default input. s_input will try to * enable_source before attempting to change the * input on the device, and will end up enabling * default source which is tuner. * - * Additional logic is necessary in au0828 - * to detect that the input has changed and - * enable the right source. + * Additional logic is necessary in au0828 to detect + * that the input has changed and enable the right + * source. au0828 handles this case in its s_input. + * It will disable the old source and enable the new + * source. + * */ - if (dev->input_type == AU0828_VMUX_TELEVISION) find_source = dev->tuner; else if (dev->input_type == AU0828_VMUX_SVIDEO || @@ -344,27 +362,33 @@ static int au0828_enable_source(struct media_entity *entity, } } - /* Is an active link between sink and source */ + /* Is there an active link between sink and source */ if (dev->active_link) { - /* - * If DVB is using the tuner and calling entity is - * audio/video, the following check will be false, - * since sink is different. Result is Busy. - */ - if (dev->active_link->sink->entity == sink && - dev->active_link->source->entity == find_source) { - /* - * Either ALSA or Video own tuner. sink is - * the same for both. Prevent Video stepping - * on ALSA when ALSA owns the source. + if (dev->active_link_owner == entity) { + /* This check is necessary to handle multiple + * enable_source calls from v4l_ioctls during + * the course of video/vbi application run-time. */ - if (dev->active_link_owner != entity && - dev->active_link_owner->function == - MEDIA_ENT_F_AUDIO_CAPTURE) { - pr_debug("ALSA has the tuner\n"); - ret = -EBUSY; - goto end; - } + pr_debug("%s already owns the tuner\n", entity->name); + ret = 0; + goto end; + } else if (au0828_is_link_shareable(dev->active_link_owner, + entity)) { + /* Either ALSA or Video own tuner. Sink is the same + * for both. Allow sharing the active link between + * their common source (tuner) and sink (decoder). + * Starting pipeline between sharing entity and sink + * will fail with pipe mismatch, while owner has an + * active pipeline. Switch pipeline ownership from + * user to owner when owner disables the source. + */ + dev->active_link_shared = true; + /* save the user info to use from disable */ + dev->active_link_user = entity; + dev->active_link_user_pipe = pipe; + pr_debug("%s owns the tuner %s can share!\n", + dev->active_link_owner->name, + entity->name); ret = 0; goto end; } else { @@ -391,7 +415,7 @@ static int au0828_enable_source(struct media_entity *entity, source = found_link->source->entity; ret = __media_entity_setup_link(found_link, MEDIA_LNK_FL_ENABLED); if (ret) { - pr_err("Activate tuner link %s->%s. Error %d\n", + pr_err("Activate link from %s->%s. Error %d\n", source->name, sink->name, ret); goto end; } @@ -401,25 +425,26 @@ static int au0828_enable_source(struct media_entity *entity, pr_err("Start Pipeline: %s->%s Error %d\n", source->name, entity->name, ret); ret = __media_entity_setup_link(found_link, 0); - pr_err("Deactivate link Error %d\n", ret); + if (ret) + pr_err("Deactivate link Error %d\n", ret); goto end; } - /* - * save active link and active link owner to avoid audio - * deactivating video owned link from disable_source and - * vice versa + + /* save link state to allow audio and video share the link + * and not disable the link while the other is using it. + * active_link_owner is used to deactivate the link. */ dev->active_link = found_link; dev->active_link_owner = entity; dev->active_source = source; dev->active_sink = sink; - pr_debug("Enabled Source: %s->%s->%s Ret %d\n", + pr_info("Enabled Source: %s->%s->%s Ret %d\n", dev->active_source->name, dev->active_sink->name, dev->active_link_owner->name, ret); end: - pr_debug("au0828_enable_source() end %s %d %d\n", - entity->name, entity->function, ret); + pr_debug("%s end: ent:%s fnc:%d ret %d\n", + __func__, entity->name, entity->function, ret); return ret; } @@ -438,21 +463,95 @@ static void au0828_disable_source(struct media_entity *entity) if (!dev->active_link) return; - /* link is active - stop pipeline from source (tuner) */ + /* link is active - stop pipeline from source + * (tuner/s-video/Composite) to the entity + * When DVB/s-video/Composite owns tuner, it won't be in + * shared state. + */ if (dev->active_link->sink->entity == dev->active_sink && dev->active_link->source->entity == dev->active_source) { /* - * prevent video from deactivating link when audio - * has active pipeline + * Prevent video from deactivating link when audio + * has active pipeline and vice versa. In addition + * handle the case when more than one video/vbi + * application is sharing the link. */ + bool owner_is_audio = false; + + if (dev->active_link_owner->function == + MEDIA_ENT_F_AUDIO_CAPTURE) + owner_is_audio = true; + + if (dev->active_link_shared) { + pr_debug("Shared link owner %s user %s %d\n", + dev->active_link_owner->name, + entity->name, dev->users); + + /* Handle video device users > 1 + * When audio owns the shared link with + * more than one video users, avoid + * disabling the source and/or switching + * the owner until the last disable_source + * call from video _close(). Use dev->users to + * determine when to switch/disable. + */ + if (dev->active_link_owner != entity) { + /* video device has users > 1 */ + if (owner_is_audio && dev->users > 1) + return; + + dev->active_link_user = NULL; + dev->active_link_user_pipe = NULL; + dev->active_link_shared = false; + return; + } + + /* video owns the link and has users > 1 */ + if (!owner_is_audio && dev->users > 1) + return; + + /* stop pipeline */ + __media_pipeline_stop(dev->active_link_owner); + pr_debug("Pipeline stop for %s\n", + dev->active_link_owner->name); + + ret = __media_pipeline_start( + dev->active_link_user, + dev->active_link_user_pipe); + if (ret) { + pr_err("Start Pipeline: %s->%s %d\n", + dev->active_source->name, + dev->active_link_user->name, + ret); + goto deactivate_link; + } + /* link user is now the owner */ + dev->active_link_owner = dev->active_link_user; + dev->active_link_user = NULL; + dev->active_link_user_pipe = NULL; + dev->active_link_shared = false; + + pr_debug("Pipeline started for %s\n", + dev->active_link_owner->name); + return; + } else if (!owner_is_audio && dev->users > 1) + /* video/vbi owns the link and has users > 1 */ + return; + if (dev->active_link_owner != entity) return; - __media_pipeline_stop(entity); + + /* stop pipeline */ + __media_pipeline_stop(dev->active_link_owner); + pr_debug("Pipeline stop for %s\n", + dev->active_link_owner->name); + +deactivate_link: ret = __media_entity_setup_link(dev->active_link, 0); if (ret) pr_err("Deactivate link Error %d\n", ret); - pr_debug("Disabled Source: %s->%s->%s Ret %d\n", + pr_info("Disabled Source: %s->%s->%s Ret %d\n", dev->active_source->name, dev->active_sink->name, dev->active_link_owner->name, ret); @@ -460,6 +559,8 @@ static void au0828_disable_source(struct media_entity *entity) dev->active_link_owner = NULL; dev->active_source = NULL; dev->active_sink = NULL; + dev->active_link_shared = false; + dev->active_link_user = NULL; } } #endif @@ -480,6 +581,9 @@ static int au0828_media_device_register(struct au0828_dev *dev, /* register media device */ ret = media_device_register(dev->media_dev); if (ret) { + media_device_delete(dev->media_dev, KBUILD_MODNAME, + THIS_MODULE); + dev->media_dev = NULL; dev_err(&udev->dev, "Media Device Register Error: %d\n", ret); return ret; diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 7876c897cc1d..4bde3db83aa2 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -758,6 +758,9 @@ static int au0828_analog_stream_enable(struct au0828_dev *d) dprintk(1, "au0828_analog_stream_enable called\n"); + if (test_bit(DEV_DISCONNECTED, &d->dev_state)) + return -ENODEV; + iface = usb_ifnum_to_if(d->usbdev, 0); if (iface && iface->cur_altsetting->desc.bAlternateSetting != 5) { dprintk(1, "Changing intf#0 to alt 5\n"); @@ -839,9 +842,9 @@ int au0828_start_analog_streaming(struct vb2_queue *vq, unsigned int count) return rc; } + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - v4l2_device_call_all(&dev->v4l2_dev, 0, video, - s_stream, 1); dev->vid_timeout_running = 1; mod_timer(&dev->vid_timeout, jiffies + (HZ / 10)); } else if (vq->type == V4L2_BUF_TYPE_VBI_CAPTURE) { @@ -861,10 +864,11 @@ static void au0828_stop_streaming(struct vb2_queue *vq) dprintk(1, "au0828_stop_streaming called %d\n", dev->streaming_users); - if (dev->streaming_users-- == 1) + if (dev->streaming_users-- == 1) { au0828_uninit_isoc(dev); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); + } - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); dev->vid_timeout_running = 0; del_timer_sync(&dev->vid_timeout); @@ -893,8 +897,10 @@ void au0828_stop_vbi_streaming(struct vb2_queue *vq) dprintk(1, "au0828_stop_vbi_streaming called %d\n", dev->streaming_users); - if (dev->streaming_users-- == 1) + if (dev->streaming_users-- == 1) { au0828_uninit_isoc(dev); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); + } spin_lock_irqsave(&dev->slock, flags); if (dev->isoc_ctl.vbi_buf != NULL) { @@ -1065,7 +1071,7 @@ static int au0828_v4l2_close(struct file *filp) * streaming. * * On most USB devices like au0828 the tuner can - * be safely put in sleep stare here if ALSA isn't + * be safely put in sleep state 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 @@ -1074,7 +1080,7 @@ static int au0828_v4l2_close(struct file *filp) * 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; + * - ALSA and DVB aren't streaming. * - the last V4L2 file handler is closed. * * FIXME: diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index 425c35d16057..b47ecc9affd8 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -31,6 +31,7 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-fh.h> #include <media/media-device.h> +#include <media/media-dev-allocator.h> /* DVB */ #include <media/demux.h> @@ -283,9 +284,12 @@ struct au0828_dev { struct media_entity_notify entity_notify; struct media_entity *tuner; struct media_link *active_link; - struct media_entity *active_link_owner; struct media_entity *active_source; struct media_entity *active_sink; + struct media_entity *active_link_owner; + struct media_entity *active_link_user; + struct media_pipeline *active_link_user_pipe; + bool active_link_shared; #endif }; |