diff options
Diffstat (limited to 'drivers/media/usb/au0828/au0828-video.c')
-rw-r--r-- | drivers/media/usb/au0828/au0828-video.c | 205 |
1 files changed, 62 insertions, 143 deletions
diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 2fc2b29d2dd9..13f6dab9ccc2 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> @@ -651,81 +653,6 @@ void au0828_usb_v4l2_media_release(struct au0828_dev *dev) #endif } -static int au0828_create_media_graph(struct au0828_dev *dev) -{ -#ifdef CONFIG_MEDIA_CONTROLLER - struct media_device *mdev = dev->media_dev; - struct media_entity *entity; - struct media_entity *tuner = NULL, *decoder = NULL; - int i, ret; - - if (!mdev) - return 0; - - media_device_for_each_entity(entity, mdev) { - switch (entity->function) { - case MEDIA_ENT_F_TUNER: - tuner = entity; - break; - case MEDIA_ENT_F_ATV_DECODER: - decoder = entity; - break; - } - } - - /* Analog setup, using tuner as a link */ - - /* Something bad happened! */ - if (!decoder) - return -EINVAL; - - if (tuner) { - ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT, - decoder, 0, - MEDIA_LNK_FL_ENABLED); - if (ret) - return ret; - } - ret = media_create_pad_link(decoder, 1, &dev->vdev.entity, 0, - MEDIA_LNK_FL_ENABLED); - if (ret) - return ret; - ret = media_create_pad_link(decoder, 2, &dev->vbi_dev.entity, 0, - MEDIA_LNK_FL_ENABLED); - if (ret) - return ret; - - for (i = 0; i < AU0828_MAX_INPUT; i++) { - struct media_entity *ent = &dev->input_ent[i]; - - switch (AUVI_INPUT(i).type) { - case AU0828_VMUX_UNDEFINED: - break; - case AU0828_VMUX_CABLE: - case AU0828_VMUX_TELEVISION: - case AU0828_VMUX_DVB: - if (!tuner) - break; - - ret = media_create_pad_link(ent, 0, tuner, - TUNER_PAD_RF_INPUT, - MEDIA_LNK_FL_ENABLED); - if (ret) - return ret; - break; - case AU0828_VMUX_COMPOSITE: - case AU0828_VMUX_SVIDEO: - /* FIXME: fix the decoder PAD */ - ret = media_create_pad_link(ent, 0, decoder, 0, 0); - if (ret) - return ret; - break; - } - } -#endif - return 0; -} - static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev) { struct au0828_dev *dev = @@ -774,64 +701,6 @@ int au0828_v4l2_device_register(struct usb_interface *interface, return 0; } -static int au0828_enable_analog_tuner(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; - - /* - * 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; - } - } - - if (active_links == 1 || !found_link) - return 0; - - source = found_link->source->entity; - list_for_each_entry(link, &source->links, list) { - struct media_entity *sink; - int flags = 0; - - sink = link->sink->entity; - - if (sink == dev->decoder) - flags = MEDIA_LNK_FL_ENABLED; - - 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"); - } -#endif - return 0; -} - static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) @@ -843,9 +712,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; } @@ -1213,8 +1079,39 @@ static int au0828_v4l2_close(struct file *filp) 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 @@ -1520,9 +1417,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); @@ -1554,6 +1453,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); @@ -1561,9 +1461,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) @@ -1614,10 +1524,16 @@ 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; + ret = v4l_enable_media_source(vfd); + if (ret) + return ret; + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); @@ -2071,6 +1987,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); @@ -2122,14 +2039,16 @@ int au0828_analog_register(struct au0828_dev *dev, ret = -ENODEV; goto err_reg_vbi_dev; } - retval = au0828_create_media_graph(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__); |