diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index 2bd7b27f8553..121db1549244 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -94,6 +94,7 @@ MEDIA_IOC_DEVICE_INFO"> MEDIA_IOC_ENUM_ENTITIES"> MEDIA_IOC_ENUM_LINKS"> +MEDIA_IOC_SETUP_LINK"> v4l2_std_id"> @@ -348,6 +349,7 @@ + diff --git a/Documentation/DocBook/v4l/media-controller.xml b/Documentation/DocBook/v4l/media-controller.xml index 2c4fd2b27683..2dc25e1d4089 100644 --- a/Documentation/DocBook/v4l/media-controller.xml +++ b/Documentation/DocBook/v4l/media-controller.xml @@ -85,4 +85,5 @@ &sub-media-ioc-device-info; &sub-media-ioc-enum-entities; &sub-media-ioc-enum-links; + &sub-media-ioc-setup-link; diff --git a/Documentation/DocBook/v4l/media-ioc-setup-link.xml b/Documentation/DocBook/v4l/media-ioc-setup-link.xml new file mode 100644 index 000000000000..09ab3d2b3a52 --- /dev/null +++ b/Documentation/DocBook/v4l/media-ioc-setup-link.xml @@ -0,0 +1,90 @@ + + + ioctl MEDIA_IOC_SETUP_LINK + &manvol; + + + + MEDIA_IOC_SETUP_LINK + Modify the properties of a link + + + + + + int ioctl + int fd + int request + struct media_link_desc *argp + + + + + + Arguments + + + + fd + + File descriptor returned by + open(). + + + + request + + MEDIA_IOC_ENUM_LINKS + + + + argp + + + + + + + + + Description + + To change link properties applications fill a &media-link-desc; with + link identification information (source and sink pad) and the new requested + link flags. They then call the MEDIA_IOC_SETUP_LINK ioctl with a pointer to + that structure. + The only configurable property is the ENABLED + link flag to enable/disable a link. Links marked with the + IMMUTABLE link flag can not be enabled or disabled. + + Link configuration has no side effect on other links. If an enabled + link at the sink pad prevents the link from being enabled, the driver + returns with an &EBUSY;. + If the specified link can't be found the driver returns with an + &EINVAL;. + + + + &return-value; + + + + EBUSY + + The link properties can't be changed because the link is + currently busy. This can be caused, for instance, by an active media + stream (audio or video) on the link. The ioctl shouldn't be retried if + no other action is performed before to fix the problem. + + + + EINVAL + + The &media-link-desc; references a non-existing link, or the + link is immutable and an attempt to modify its configuration was made. + + + + + + diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt index 78ae02095372..4809221c0ff9 100644 --- a/Documentation/media-framework.txt +++ b/Documentation/media-framework.txt @@ -259,6 +259,16 @@ When the graph traversal is complete the function will return NULL. Graph traversal can be interrupted at any moment. No cleanup function call is required and the graph structure can be freed normally. +Helper functions can be used to find a link between two given pads, or a pad +connected to another pad through an enabled link + + media_entity_find_link(struct media_pad *source, + struct media_pad *sink); + + media_entity_remote_source(struct media_pad *pad); + +Refer to the kerneldoc documentation for more information. + Use count and power handling ---------------------------- @@ -271,3 +281,35 @@ track the number of users of every entity for power management needs. The use_count field is owned by media drivers and must not be touched by entity drivers. Access to the field must be protected by the media device graph_mutex lock. + + +Links setup +----------- + +Link properties can be modified at runtime by calling + + media_entity_setup_link(struct media_link *link, u32 flags); + +The flags argument contains the requested new link flags. + +The only configurable property is the ENABLED link flag to enable/disable a +link. Links marked with the IMMUTABLE link flag can not be enabled or disabled. + +When a link is enabled or disabled, the media framework calls the +link_setup operation for the two entities at the source and sink of the link, +in that order. If the second link_setup call fails, another link_setup call is +made on the first entity to restore the original link flags. + +Media device drivers can be notified of link setup operations by setting the +media_device::link_notify pointer to a callback function. If provided, the +notification callback will be called before enabling and after disabling +links. + +Entity drivers must implement the link_setup operation if any of their links +is non-immutable. The operation must either configure the hardware or store +the configuration information to be applied later. + +Link configuration must not have any side effect on other links. If an enabled +link at a sink pad prevents another link at the same pad from being disabled, +the link_setup operation must return -EBUSY and can't implicitly disable the +first enabled link. diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 648a9d892ac1..16b70b4412f7 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -172,6 +172,44 @@ static long media_device_enum_links(struct media_device *mdev, return 0; } +static long media_device_setup_link(struct media_device *mdev, + struct media_link_desc __user *_ulink) +{ + struct media_link *link = NULL; + struct media_link_desc ulink; + struct media_entity *source; + struct media_entity *sink; + int ret; + + if (copy_from_user(&ulink, _ulink, sizeof(ulink))) + return -EFAULT; + + /* Find the source and sink entities and link. + */ + source = find_entity(mdev, ulink.source.entity); + sink = find_entity(mdev, ulink.sink.entity); + + if (source == NULL || sink == NULL) + return -EINVAL; + + if (ulink.source.index >= source->num_pads || + ulink.sink.index >= sink->num_pads) + return -EINVAL; + + link = media_entity_find_link(&source->pads[ulink.source.index], + &sink->pads[ulink.sink.index]); + if (link == NULL) + return -EINVAL; + + /* Setup the link on both entities. */ + ret = __media_entity_setup_link(link, ulink.flags); + + if (copy_to_user(_ulink, &ulink, sizeof(ulink))) + return -EFAULT; + + return ret; +} + static long media_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -197,6 +235,13 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, mutex_unlock(&dev->graph_mutex); break; + case MEDIA_IOC_SETUP_LINK: + mutex_lock(&dev->graph_mutex); + ret = media_device_setup_link(dev, + (struct media_link_desc __user *)arg); + mutex_unlock(&dev->graph_mutex); + break; + default: ret = -ENOIOCTLCMD; } diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 3e7e2d569cec..6795c920d460 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -306,3 +306,158 @@ media_entity_create_link(struct media_entity *source, u16 source_pad, return 0; } EXPORT_SYMBOL_GPL(media_entity_create_link); + +static int __media_entity_setup_link_notify(struct media_link *link, u32 flags) +{ + const u32 mask = MEDIA_LNK_FL_ENABLED; + int ret; + + /* Notify both entities. */ + ret = media_entity_call(link->source->entity, link_setup, + link->source, link->sink, flags); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + ret = media_entity_call(link->sink->entity, link_setup, + link->sink, link->source, flags); + if (ret < 0 && ret != -ENOIOCTLCMD) { + media_entity_call(link->source->entity, link_setup, + link->source, link->sink, link->flags); + return ret; + } + + link->flags = (link->flags & ~mask) | (flags & mask); + link->reverse->flags = link->flags; + + return 0; +} + +/** + * __media_entity_setup_link - Configure a media link + * @link: The link being configured + * @flags: Link configuration flags + * + * The bulk of link setup is handled by the two entities connected through the + * link. This function notifies both entities of the link configuration change. + * + * If the link is immutable or if the current and new configuration are + * identical, return immediately. + * + * The user is expected to hold link->source->parent->mutex. If not, + * media_entity_setup_link() should be used instead. + */ +int __media_entity_setup_link(struct media_link *link, u32 flags) +{ + struct media_device *mdev; + struct media_entity *source, *sink; + int ret = -EBUSY; + + if (link == NULL) + return -EINVAL; + + if (link->flags & MEDIA_LNK_FL_IMMUTABLE) + return link->flags == flags ? 0 : -EINVAL; + + if (link->flags == flags) + return 0; + + source = link->source->entity; + sink = link->sink->entity; + + mdev = source->parent; + + if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify) { + ret = mdev->link_notify(link->source, link->sink, + MEDIA_LNK_FL_ENABLED); + if (ret < 0) + return ret; + } + + ret = __media_entity_setup_link_notify(link, flags); + if (ret < 0) + goto err; + + if (!(flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify) + mdev->link_notify(link->source, link->sink, 0); + + return 0; + +err: + if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify) + mdev->link_notify(link->source, link->sink, 0); + + return ret; +} + +int media_entity_setup_link(struct media_link *link, u32 flags) +{ + int ret; + + mutex_lock(&link->source->entity->parent->graph_mutex); + ret = __media_entity_setup_link(link, flags); + mutex_unlock(&link->source->entity->parent->graph_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(media_entity_setup_link); + +/** + * media_entity_find_link - Find a link between two pads + * @source: Source pad + * @sink: Sink pad + * + * Return a pointer to the link between the two entities. If no such link + * exists, return NULL. + */ +struct media_link * +media_entity_find_link(struct media_pad *source, struct media_pad *sink) +{ + struct media_link *link; + unsigned int i; + + for (i = 0; i < source->entity->num_links; ++i) { + link = &source->entity->links[i]; + + if (link->source->entity == source->entity && + link->source->index == source->index && + link->sink->entity == sink->entity && + link->sink->index == sink->index) + return link; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(media_entity_find_link); + +/** + * media_entity_remote_source - Find the source pad at the remote end of a link + * @pad: Sink pad at the local end of the link + * + * Search for a remote source pad connected to the given sink pad by iterating + * over all links originating or terminating at that pad until an enabled link + * is found. + * + * Return a pointer to the pad at the remote end of the first found enabled + * link, or NULL if no enabled link has been found. + */ +struct media_pad *media_entity_remote_source(struct media_pad *pad) +{ + unsigned int i; + + for (i = 0; i < pad->entity->num_links; i++) { + struct media_link *link = &pad->entity->links[i]; + + if (!(link->flags & MEDIA_LNK_FL_ENABLED)) + continue; + + if (link->source == pad) + return link->sink; + + if (link->sink == pad) + return link->source; + } + + return NULL; + +} +EXPORT_SYMBOL_GPL(media_entity_remote_source); diff --git a/include/linux/media.h b/include/linux/media.h index 17c93a413677..7c69913c0ad2 100644 --- a/include/linux/media.h +++ b/include/linux/media.h @@ -126,5 +126,6 @@ struct media_links_enum { #define MEDIA_IOC_DEVICE_INFO _IOWR('M', 1, struct media_device_info) #define MEDIA_IOC_ENUM_ENTITIES _IOWR('M', 2, struct media_entity_desc) #define MEDIA_IOC_ENUM_LINKS _IOWR('M', 3, struct media_links_enum) +#define MEDIA_IOC_SETUP_LINK _IOWR('M', 4, struct media_link_desc) #endif /* __LINUX_MEDIA_H */ diff --git a/include/media/media-device.h b/include/media/media-device.h index 5d2bff4fc9e0..6a27d916c250 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -73,6 +73,9 @@ struct media_device { spinlock_t lock; /* Serializes graph operations. */ struct mutex graph_mutex; + + int (*link_notify)(struct media_pad *source, + struct media_pad *sink, u32 flags); }; /* media_devnode to media_device */ diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 51bdafce72c7..d889dcc67d0d 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -39,6 +39,12 @@ struct media_pad { unsigned long flags; /* Pad flags (MEDIA_PAD_FL_*) */ }; +struct media_entity_operations { + int (*link_setup)(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags); +}; + struct media_entity { struct list_head list; struct media_device *parent; /* Media device this entity belongs to*/ @@ -59,6 +65,8 @@ struct media_entity { struct media_pad *pads; /* Pads array (num_pads elements) */ struct media_link *links; /* Links array (max_links elements)*/ + const struct media_entity_operations *ops; /* Entity operations */ + /* Reference counts must never be negative, but are signed integers on * purpose: a simple WARN_ON(<0) check can be used to detect reference * count bugs that would make them negative. @@ -112,6 +120,11 @@ int media_entity_init(struct media_entity *entity, u16 num_pads, void media_entity_cleanup(struct media_entity *entity); int media_entity_create_link(struct media_entity *source, u16 source_pad, struct media_entity *sink, u16 sink_pad, u32 flags); +int __media_entity_setup_link(struct media_link *link, u32 flags); +int media_entity_setup_link(struct media_link *link, u32 flags); +struct media_link *media_entity_find_link(struct media_pad *source, + struct media_pad *sink); +struct media_pad *media_entity_remote_source(struct media_pad *pad); struct media_entity *media_entity_get(struct media_entity *entity); void media_entity_put(struct media_entity *entity); @@ -121,4 +134,8 @@ void media_entity_graph_walk_start(struct media_entity_graph *graph, struct media_entity * media_entity_graph_walk_next(struct media_entity_graph *graph); +#define media_entity_call(entity, operation, args...) \ + (((entity)->ops && (entity)->ops->operation) ? \ + (entity)->ops->operation((entity) , ##args) : -ENOIOCTLCMD) + #endif