summaryrefslogtreecommitdiffstats
path: root/drivers/media/usb
diff options
context:
space:
mode:
authorRafael Lourenço de Lima Chehab <chehabrafael@gmail.com>2015-06-08 22:20:46 -0300
committerMauro Carvalho Chehab <mchehab@osg.samsung.com>2016-01-11 12:18:39 -0200
commitbed6919665072b1e5bad31a013d53798394e097c (patch)
treebaed884274ac819b315d5ffe7042a57ada05bfbd /drivers/media/usb
parente576d60bb21e7add884f052ff0e5c28ebf7b7461 (diff)
downloadlinux-bed6919665072b1e5bad31a013d53798394e097c.tar.bz2
[media] au0828: Add support for media controller
Add support for analog and dvb tv using media controller. Signed-off-by: Rafael Lourenço de Lima Chehab <chehabrafael@gmail.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Diffstat (limited to 'drivers/media/usb')
-rw-r--r--drivers/media/usb/au0828/au0828-core.c98
-rw-r--r--drivers/media/usb/au0828/au0828-dvb.c10
-rw-r--r--drivers/media/usb/au0828/au0828-video.c83
-rw-r--r--drivers/media/usb/au0828/au0828.h6
4 files changed, 197 insertions, 0 deletions
diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c
index 0934024fb89d..0378a2c99ebb 100644
--- a/drivers/media/usb/au0828/au0828-core.c
+++ b/drivers/media/usb/au0828/au0828-core.c
@@ -127,8 +127,22 @@ static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
return status;
}
+static void au0828_unregister_media_device(struct au0828_dev *dev)
+{
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ if (dev->media_dev) {
+ media_device_unregister(dev->media_dev);
+ kfree(dev->media_dev);
+ dev->media_dev = NULL;
+ }
+#endif
+}
+
static void au0828_usb_release(struct au0828_dev *dev)
{
+ au0828_unregister_media_device(dev);
+
/* I2C */
au0828_i2c_unregister(dev);
@@ -161,6 +175,8 @@ static void au0828_usb_disconnect(struct usb_interface *interface)
*/
dev->dev_state = DEV_DISCONNECTED;
+ au0828_unregister_media_device(dev);
+
au0828_rc_unregister(dev);
/* Digital TV */
au0828_dvb_unregister(dev);
@@ -180,6 +196,81 @@ static void au0828_usb_disconnect(struct usb_interface *interface)
au0828_usb_release(dev);
}
+static void au0828_media_device_register(struct au0828_dev *dev,
+ struct usb_device *udev)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device *mdev;
+ int ret;
+
+ mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+ if (!mdev)
+ return;
+
+ mdev->dev = &udev->dev;
+
+ if (!dev->board.name)
+ strlcpy(mdev->model, "unknown au0828", sizeof(mdev->model));
+ else
+ strlcpy(mdev->model, dev->board.name, sizeof(mdev->model));
+ if (udev->serial)
+ strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial));
+ strcpy(mdev->bus_info, udev->devpath);
+ mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
+ mdev->driver_version = LINUX_VERSION_CODE;
+
+ ret = media_device_register(mdev);
+ if (ret) {
+ pr_err(
+ "Couldn't create a media device. Error: %d\n",
+ ret);
+ kfree(mdev);
+ return;
+ }
+
+ dev->media_dev = mdev;
+#endif
+}
+
+
+static void 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;
+
+ if (!mdev)
+ return;
+
+ media_device_for_each_entity(entity, mdev) {
+ switch (entity->type) {
+ case MEDIA_ENT_T_V4L2_SUBDEV_TUNER:
+ tuner = entity;
+ break;
+ case MEDIA_ENT_T_V4L2_SUBDEV_DECODER:
+ decoder = entity;
+ break;
+ }
+ }
+
+ /* Analog setup, using tuner as a link */
+
+ if (!decoder)
+ return;
+
+ if (tuner)
+ media_entity_create_link(tuner, 0, decoder, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (dev->vdev.entity.links)
+ media_entity_create_link(decoder, 1, &dev->vdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (dev->vbi_dev.entity.links)
+ media_entity_create_link(decoder, 2, &dev->vbi_dev.entity, 0,
+ MEDIA_LNK_FL_ENABLED);
+#endif
+}
+
static int au0828_usb_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
@@ -224,11 +315,16 @@ static int au0828_usb_probe(struct usb_interface *interface,
dev->boardnr = id->driver_info;
dev->board = au0828_boards[dev->boardnr];
+ /* Register the media controller */
+ au0828_media_device_register(dev, usbdev);
#ifdef CONFIG_VIDEO_AU0828_V4L2
dev->v4l2_dev.release = au0828_usb_v4l2_release;
/* 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",
@@ -287,6 +383,8 @@ static int au0828_usb_probe(struct usb_interface *interface,
mutex_unlock(&dev->lock);
+ au0828_create_media_graph(dev);
+
return retval;
}
diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c
index c267d76f5b3c..c01772c4f9f0 100644
--- a/drivers/media/usb/au0828/au0828-dvb.c
+++ b/drivers/media/usb/au0828/au0828-dvb.c
@@ -415,6 +415,11 @@ static int dvb_register(struct au0828_dev *dev)
result);
goto fail_adapter;
}
+
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ dvb->adapter.mdev = dev->media_dev;
+#endif
+
dvb->adapter.priv = dev;
/* register frontend */
@@ -480,6 +485,11 @@ static int dvb_register(struct au0828_dev *dev)
dvb->start_count = 0;
dvb->stop_count = 0;
+
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ dvb_create_media_graph(&dvb->adapter);
+#endif
+
return 0;
fail_fe_conn:
diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c
index 0a725a161dd6..7aa7d509885b 100644
--- a/drivers/media/usb/au0828/au0828-video.c
+++ b/drivers/media/usb/au0828/au0828-video.c
@@ -638,6 +638,75 @@ 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)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device *mdev = dev->media_dev;
+ struct media_entity *entity, *decoder = NULL, *source;
+ struct media_link *link, *found_link = NULL;
+ int i, ret, active_links = 0;
+
+ if (!mdev)
+ 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.
+ */
+ media_device_for_each_entity(entity, mdev) {
+ if (entity->type == MEDIA_ENT_T_V4L2_SUBDEV_DECODER) {
+ decoder = entity;
+ break;
+ }
+ }
+ if (!decoder)
+ return 0;
+
+ for (i = 0; i < decoder->num_links; i++) {
+ link = &decoder->links[i];
+ if (link->sink->entity == 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;
+ for (i = 0; i < source->num_links; i++) {
+ struct media_entity *sink;
+ int flags = 0;
+
+ link = &source->links[i];
+ sink = link->sink->entity;
+
+ if (sink == entity)
+ 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[])
@@ -650,6 +719,8 @@ static int queue_setup(struct vb2_queue *vq,
*nplanes = 1;
sizes[0] = size;
+ au0828_enable_analog_tuner(dev);
+
return 0;
}
@@ -1823,6 +1894,18 @@ int au0828_analog_register(struct au0828_dev *dev,
dev->vbi_dev.queue->lock = &dev->vb_vbi_queue_lock;
strcpy(dev->vbi_dev.name, "au0828a vbi");
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ dev->video_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_init(&dev->vdev.entity, 1, &dev->video_pad, 0);
+ if (ret < 0)
+ pr_err("failed to initialize video media entity!\n");
+
+ dev->vbi_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_init(&dev->vbi_dev.entity, 1, &dev->vbi_pad, 0);
+ if (ret < 0)
+ pr_err("failed to initialize vbi media entity!\n");
+#endif
+
/* initialize videobuf2 stuff */
retval = au0828_vb2_setup(dev);
if (retval != 0) {
diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h
index 60b59391ea2a..7d175d8b8a7a 100644
--- a/drivers/media/usb/au0828/au0828.h
+++ b/drivers/media/usb/au0828/au0828.h
@@ -33,6 +33,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
+#include <media/media-device.h>
/* DVB */
#include "demux.h"
@@ -276,6 +277,11 @@ struct au0828_dev {
/* Preallocated transfer digital transfer buffers */
char *dig_transfer_buffer[URB_COUNT];
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device *media_dev;
+ struct media_pad video_pad, vbi_pad;
+#endif
};