162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Media entity 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010 Nokia Corporation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> 862306a36Sopenharmony_ci * Sakari Ailus <sakari.ailus@iki.fi> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/bitmap.h> 1262306a36Sopenharmony_ci#include <linux/list.h> 1362306a36Sopenharmony_ci#include <linux/property.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <media/media-entity.h> 1662306a36Sopenharmony_ci#include <media/media-device.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic inline const char *intf_type(struct media_interface *intf) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci switch (intf->type) { 2162306a36Sopenharmony_ci case MEDIA_INTF_T_DVB_FE: 2262306a36Sopenharmony_ci return "dvb-frontend"; 2362306a36Sopenharmony_ci case MEDIA_INTF_T_DVB_DEMUX: 2462306a36Sopenharmony_ci return "dvb-demux"; 2562306a36Sopenharmony_ci case MEDIA_INTF_T_DVB_DVR: 2662306a36Sopenharmony_ci return "dvb-dvr"; 2762306a36Sopenharmony_ci case MEDIA_INTF_T_DVB_CA: 2862306a36Sopenharmony_ci return "dvb-ca"; 2962306a36Sopenharmony_ci case MEDIA_INTF_T_DVB_NET: 3062306a36Sopenharmony_ci return "dvb-net"; 3162306a36Sopenharmony_ci case MEDIA_INTF_T_V4L_VIDEO: 3262306a36Sopenharmony_ci return "v4l-video"; 3362306a36Sopenharmony_ci case MEDIA_INTF_T_V4L_VBI: 3462306a36Sopenharmony_ci return "v4l-vbi"; 3562306a36Sopenharmony_ci case MEDIA_INTF_T_V4L_RADIO: 3662306a36Sopenharmony_ci return "v4l-radio"; 3762306a36Sopenharmony_ci case MEDIA_INTF_T_V4L_SUBDEV: 3862306a36Sopenharmony_ci return "v4l-subdev"; 3962306a36Sopenharmony_ci case MEDIA_INTF_T_V4L_SWRADIO: 4062306a36Sopenharmony_ci return "v4l-swradio"; 4162306a36Sopenharmony_ci case MEDIA_INTF_T_V4L_TOUCH: 4262306a36Sopenharmony_ci return "v4l-touch"; 4362306a36Sopenharmony_ci default: 4462306a36Sopenharmony_ci return "unknown-intf"; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic inline const char *link_type_name(struct media_link *link) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci switch (link->flags & MEDIA_LNK_FL_LINK_TYPE) { 5162306a36Sopenharmony_ci case MEDIA_LNK_FL_DATA_LINK: 5262306a36Sopenharmony_ci return "data"; 5362306a36Sopenharmony_ci case MEDIA_LNK_FL_INTERFACE_LINK: 5462306a36Sopenharmony_ci return "interface"; 5562306a36Sopenharmony_ci case MEDIA_LNK_FL_ANCILLARY_LINK: 5662306a36Sopenharmony_ci return "ancillary"; 5762306a36Sopenharmony_ci default: 5862306a36Sopenharmony_ci return "unknown"; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci__must_check int media_entity_enum_init(struct media_entity_enum *ent_enum, 6362306a36Sopenharmony_ci struct media_device *mdev) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci int idx_max; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci idx_max = ALIGN(mdev->entity_internal_idx_max + 1, BITS_PER_LONG); 6862306a36Sopenharmony_ci ent_enum->bmap = bitmap_zalloc(idx_max, GFP_KERNEL); 6962306a36Sopenharmony_ci if (!ent_enum->bmap) 7062306a36Sopenharmony_ci return -ENOMEM; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci ent_enum->idx_max = idx_max; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_enum_init); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_civoid media_entity_enum_cleanup(struct media_entity_enum *ent_enum) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci bitmap_free(ent_enum->bmap); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_enum_cleanup); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/** 8562306a36Sopenharmony_ci * dev_dbg_obj - Prints in debug mode a change on some object 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * @event_name: Name of the event to report. Could be __func__ 8862306a36Sopenharmony_ci * @gobj: Pointer to the object 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * Enabled only if DEBUG or CONFIG_DYNAMIC_DEBUG. Otherwise, it 9162306a36Sopenharmony_ci * won't produce any code. 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_cistatic void dev_dbg_obj(const char *event_name, struct media_gobj *gobj) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci#if defined(DEBUG) || defined (CONFIG_DYNAMIC_DEBUG) 9662306a36Sopenharmony_ci switch (media_type(gobj)) { 9762306a36Sopenharmony_ci case MEDIA_GRAPH_ENTITY: 9862306a36Sopenharmony_ci dev_dbg(gobj->mdev->dev, 9962306a36Sopenharmony_ci "%s id %u: entity '%s'\n", 10062306a36Sopenharmony_ci event_name, media_id(gobj), 10162306a36Sopenharmony_ci gobj_to_entity(gobj)->name); 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci case MEDIA_GRAPH_LINK: 10462306a36Sopenharmony_ci { 10562306a36Sopenharmony_ci struct media_link *link = gobj_to_link(gobj); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci dev_dbg(gobj->mdev->dev, 10862306a36Sopenharmony_ci "%s id %u: %s link id %u ==> id %u\n", 10962306a36Sopenharmony_ci event_name, media_id(gobj), link_type_name(link), 11062306a36Sopenharmony_ci media_id(link->gobj0), 11162306a36Sopenharmony_ci media_id(link->gobj1)); 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci case MEDIA_GRAPH_PAD: 11562306a36Sopenharmony_ci { 11662306a36Sopenharmony_ci struct media_pad *pad = gobj_to_pad(gobj); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci dev_dbg(gobj->mdev->dev, 11962306a36Sopenharmony_ci "%s id %u: %s%spad '%s':%d\n", 12062306a36Sopenharmony_ci event_name, media_id(gobj), 12162306a36Sopenharmony_ci pad->flags & MEDIA_PAD_FL_SINK ? "sink " : "", 12262306a36Sopenharmony_ci pad->flags & MEDIA_PAD_FL_SOURCE ? "source " : "", 12362306a36Sopenharmony_ci pad->entity->name, pad->index); 12462306a36Sopenharmony_ci break; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci case MEDIA_GRAPH_INTF_DEVNODE: 12762306a36Sopenharmony_ci { 12862306a36Sopenharmony_ci struct media_interface *intf = gobj_to_intf(gobj); 12962306a36Sopenharmony_ci struct media_intf_devnode *devnode = intf_to_devnode(intf); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci dev_dbg(gobj->mdev->dev, 13262306a36Sopenharmony_ci "%s id %u: intf_devnode %s - major: %d, minor: %d\n", 13362306a36Sopenharmony_ci event_name, media_id(gobj), 13462306a36Sopenharmony_ci intf_type(intf), 13562306a36Sopenharmony_ci devnode->major, devnode->minor); 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci#endif 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_civoid media_gobj_create(struct media_device *mdev, 14362306a36Sopenharmony_ci enum media_gobj_type type, 14462306a36Sopenharmony_ci struct media_gobj *gobj) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci BUG_ON(!mdev); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci gobj->mdev = mdev; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Create a per-type unique object ID */ 15162306a36Sopenharmony_ci gobj->id = media_gobj_gen_id(type, ++mdev->id); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci switch (type) { 15462306a36Sopenharmony_ci case MEDIA_GRAPH_ENTITY: 15562306a36Sopenharmony_ci list_add_tail(&gobj->list, &mdev->entities); 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci case MEDIA_GRAPH_PAD: 15862306a36Sopenharmony_ci list_add_tail(&gobj->list, &mdev->pads); 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci case MEDIA_GRAPH_LINK: 16162306a36Sopenharmony_ci list_add_tail(&gobj->list, &mdev->links); 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci case MEDIA_GRAPH_INTF_DEVNODE: 16462306a36Sopenharmony_ci list_add_tail(&gobj->list, &mdev->interfaces); 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci mdev->topology_version++; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci dev_dbg_obj(__func__, gobj); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_civoid media_gobj_destroy(struct media_gobj *gobj) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci /* Do nothing if the object is not linked. */ 17662306a36Sopenharmony_ci if (gobj->mdev == NULL) 17762306a36Sopenharmony_ci return; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci dev_dbg_obj(__func__, gobj); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci gobj->mdev->topology_version++; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Remove the object from mdev list */ 18462306a36Sopenharmony_ci list_del(&gobj->list); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci gobj->mdev = NULL; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* 19062306a36Sopenharmony_ci * TODO: Get rid of this. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci#define MEDIA_ENTITY_MAX_PADS 512 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ciint media_entity_pads_init(struct media_entity *entity, u16 num_pads, 19562306a36Sopenharmony_ci struct media_pad *pads) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct media_device *mdev = entity->graph_obj.mdev; 19862306a36Sopenharmony_ci struct media_pad *iter; 19962306a36Sopenharmony_ci unsigned int i = 0; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (num_pads >= MEDIA_ENTITY_MAX_PADS) 20262306a36Sopenharmony_ci return -E2BIG; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci entity->num_pads = num_pads; 20562306a36Sopenharmony_ci entity->pads = pads; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (mdev) 20862306a36Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci media_entity_for_each_pad(entity, iter) { 21162306a36Sopenharmony_ci iter->entity = entity; 21262306a36Sopenharmony_ci iter->index = i++; 21362306a36Sopenharmony_ci if (mdev) 21462306a36Sopenharmony_ci media_gobj_create(mdev, MEDIA_GRAPH_PAD, 21562306a36Sopenharmony_ci &iter->graph_obj); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (mdev) 21962306a36Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_pads_init); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 22662306a36Sopenharmony_ci * Graph traversal 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/** 23062306a36Sopenharmony_ci * media_entity_has_pad_interdep - Check interdependency between two pads 23162306a36Sopenharmony_ci * 23262306a36Sopenharmony_ci * @entity: The entity 23362306a36Sopenharmony_ci * @pad0: The first pad index 23462306a36Sopenharmony_ci * @pad1: The second pad index 23562306a36Sopenharmony_ci * 23662306a36Sopenharmony_ci * This function checks the interdependency inside the entity between @pad0 23762306a36Sopenharmony_ci * and @pad1. If two pads are interdependent they are part of the same pipeline 23862306a36Sopenharmony_ci * and enabling one of the pads means that the other pad will become "locked" 23962306a36Sopenharmony_ci * and doesn't allow configuration changes. 24062306a36Sopenharmony_ci * 24162306a36Sopenharmony_ci * This function uses the &media_entity_operations.has_pad_interdep() operation 24262306a36Sopenharmony_ci * to check the dependency inside the entity between @pad0 and @pad1. If the 24362306a36Sopenharmony_ci * has_pad_interdep operation is not implemented, all pads of the entity are 24462306a36Sopenharmony_ci * considered to be interdependent. 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci * One of @pad0 and @pad1 must be a sink pad and the other one a source pad. 24762306a36Sopenharmony_ci * The function returns false if both pads are sinks or sources. 24862306a36Sopenharmony_ci * 24962306a36Sopenharmony_ci * The caller must hold entity->graph_obj.mdev->mutex. 25062306a36Sopenharmony_ci * 25162306a36Sopenharmony_ci * Return: true if the pads are connected internally and false otherwise. 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_cistatic bool media_entity_has_pad_interdep(struct media_entity *entity, 25462306a36Sopenharmony_ci unsigned int pad0, unsigned int pad1) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci if (pad0 >= entity->num_pads || pad1 >= entity->num_pads) 25762306a36Sopenharmony_ci return false; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (entity->pads[pad0].flags & entity->pads[pad1].flags & 26062306a36Sopenharmony_ci (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) 26162306a36Sopenharmony_ci return false; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (!entity->ops || !entity->ops->has_pad_interdep) 26462306a36Sopenharmony_ci return true; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return entity->ops->has_pad_interdep(entity, pad0, pad1); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic struct media_entity * 27062306a36Sopenharmony_cimedia_entity_other(struct media_entity *entity, struct media_link *link) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci if (link->source->entity == entity) 27362306a36Sopenharmony_ci return link->sink->entity; 27462306a36Sopenharmony_ci else 27562306a36Sopenharmony_ci return link->source->entity; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci/* push an entity to traversal stack */ 27962306a36Sopenharmony_cistatic void stack_push(struct media_graph *graph, 28062306a36Sopenharmony_ci struct media_entity *entity) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci if (graph->top == MEDIA_ENTITY_ENUM_MAX_DEPTH - 1) { 28362306a36Sopenharmony_ci WARN_ON(1); 28462306a36Sopenharmony_ci return; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci graph->top++; 28762306a36Sopenharmony_ci graph->stack[graph->top].link = entity->links.next; 28862306a36Sopenharmony_ci graph->stack[graph->top].entity = entity; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic struct media_entity *stack_pop(struct media_graph *graph) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct media_entity *entity; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci entity = graph->stack[graph->top].entity; 29662306a36Sopenharmony_ci graph->top--; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return entity; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci#define link_top(en) ((en)->stack[(en)->top].link) 30262306a36Sopenharmony_ci#define stack_top(en) ((en)->stack[(en)->top].entity) 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci/** 30562306a36Sopenharmony_ci * media_graph_walk_init - Allocate resources for graph walk 30662306a36Sopenharmony_ci * @graph: Media graph structure that will be used to walk the graph 30762306a36Sopenharmony_ci * @mdev: Media device 30862306a36Sopenharmony_ci * 30962306a36Sopenharmony_ci * Reserve resources for graph walk in media device's current 31062306a36Sopenharmony_ci * state. The memory must be released using 31162306a36Sopenharmony_ci * media_graph_walk_cleanup(). 31262306a36Sopenharmony_ci * 31362306a36Sopenharmony_ci * Returns error on failure, zero on success. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci__must_check int media_graph_walk_init( 31662306a36Sopenharmony_ci struct media_graph *graph, struct media_device *mdev) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci return media_entity_enum_init(&graph->ent_enum, mdev); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_graph_walk_init); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci/** 32362306a36Sopenharmony_ci * media_graph_walk_cleanup - Release resources related to graph walking 32462306a36Sopenharmony_ci * @graph: Media graph structure that was used to walk the graph 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_civoid media_graph_walk_cleanup(struct media_graph *graph) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci media_entity_enum_cleanup(&graph->ent_enum); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_graph_walk_cleanup); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_civoid media_graph_walk_start(struct media_graph *graph, 33362306a36Sopenharmony_ci struct media_entity *entity) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci media_entity_enum_zero(&graph->ent_enum); 33662306a36Sopenharmony_ci media_entity_enum_set(&graph->ent_enum, entity); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci graph->top = 0; 33962306a36Sopenharmony_ci graph->stack[graph->top].entity = NULL; 34062306a36Sopenharmony_ci stack_push(graph, entity); 34162306a36Sopenharmony_ci dev_dbg(entity->graph_obj.mdev->dev, 34262306a36Sopenharmony_ci "begin graph walk at '%s'\n", entity->name); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_graph_walk_start); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic void media_graph_walk_iter(struct media_graph *graph) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct media_entity *entity = stack_top(graph); 34962306a36Sopenharmony_ci struct media_link *link; 35062306a36Sopenharmony_ci struct media_entity *next; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci link = list_entry(link_top(graph), typeof(*link), list); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* If the link is not a data link, don't follow it */ 35562306a36Sopenharmony_ci if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) != MEDIA_LNK_FL_DATA_LINK) { 35662306a36Sopenharmony_ci link_top(graph) = link_top(graph)->next; 35762306a36Sopenharmony_ci return; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* The link is not enabled so we do not follow. */ 36162306a36Sopenharmony_ci if (!(link->flags & MEDIA_LNK_FL_ENABLED)) { 36262306a36Sopenharmony_ci link_top(graph) = link_top(graph)->next; 36362306a36Sopenharmony_ci dev_dbg(entity->graph_obj.mdev->dev, 36462306a36Sopenharmony_ci "walk: skipping disabled link '%s':%u -> '%s':%u\n", 36562306a36Sopenharmony_ci link->source->entity->name, link->source->index, 36662306a36Sopenharmony_ci link->sink->entity->name, link->sink->index); 36762306a36Sopenharmony_ci return; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* Get the entity at the other end of the link. */ 37162306a36Sopenharmony_ci next = media_entity_other(entity, link); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* Has the entity already been visited? */ 37462306a36Sopenharmony_ci if (media_entity_enum_test_and_set(&graph->ent_enum, next)) { 37562306a36Sopenharmony_ci link_top(graph) = link_top(graph)->next; 37662306a36Sopenharmony_ci dev_dbg(entity->graph_obj.mdev->dev, 37762306a36Sopenharmony_ci "walk: skipping entity '%s' (already seen)\n", 37862306a36Sopenharmony_ci next->name); 37962306a36Sopenharmony_ci return; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* Push the new entity to stack and start over. */ 38362306a36Sopenharmony_ci link_top(graph) = link_top(graph)->next; 38462306a36Sopenharmony_ci stack_push(graph, next); 38562306a36Sopenharmony_ci dev_dbg(entity->graph_obj.mdev->dev, "walk: pushing '%s' on stack\n", 38662306a36Sopenharmony_ci next->name); 38762306a36Sopenharmony_ci lockdep_assert_held(&entity->graph_obj.mdev->graph_mutex); 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistruct media_entity *media_graph_walk_next(struct media_graph *graph) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct media_entity *entity; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (stack_top(graph) == NULL) 39562306a36Sopenharmony_ci return NULL; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * Depth first search. Push entity to stack and continue from 39962306a36Sopenharmony_ci * top of the stack until no more entities on the level can be 40062306a36Sopenharmony_ci * found. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci while (link_top(graph) != &stack_top(graph)->links) 40362306a36Sopenharmony_ci media_graph_walk_iter(graph); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci entity = stack_pop(graph); 40662306a36Sopenharmony_ci dev_dbg(entity->graph_obj.mdev->dev, 40762306a36Sopenharmony_ci "walk: returning entity '%s'\n", entity->name); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return entity; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_graph_walk_next); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 41462306a36Sopenharmony_ci * Pipeline management 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/* 41862306a36Sopenharmony_ci * The pipeline traversal stack stores pads that are reached during graph 41962306a36Sopenharmony_ci * traversal, with a list of links to be visited to continue the traversal. 42062306a36Sopenharmony_ci * When a new pad is reached, an entry is pushed on the top of the stack and 42162306a36Sopenharmony_ci * points to the incoming pad and the first link of the entity. 42262306a36Sopenharmony_ci * 42362306a36Sopenharmony_ci * To find further pads in the pipeline, the traversal algorithm follows 42462306a36Sopenharmony_ci * internal pad dependencies in the entity, and then links in the graph. It 42562306a36Sopenharmony_ci * does so by iterating over all links of the entity, and following enabled 42662306a36Sopenharmony_ci * links that originate from a pad that is internally connected to the incoming 42762306a36Sopenharmony_ci * pad, as reported by the media_entity_has_pad_interdep() function. 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci/** 43162306a36Sopenharmony_ci * struct media_pipeline_walk_entry - Entry in the pipeline traversal stack 43262306a36Sopenharmony_ci * 43362306a36Sopenharmony_ci * @pad: The media pad being visited 43462306a36Sopenharmony_ci * @links: Links left to be visited 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_cistruct media_pipeline_walk_entry { 43762306a36Sopenharmony_ci struct media_pad *pad; 43862306a36Sopenharmony_ci struct list_head *links; 43962306a36Sopenharmony_ci}; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci/** 44262306a36Sopenharmony_ci * struct media_pipeline_walk - State used by the media pipeline traversal 44362306a36Sopenharmony_ci * algorithm 44462306a36Sopenharmony_ci * 44562306a36Sopenharmony_ci * @mdev: The media device 44662306a36Sopenharmony_ci * @stack: Depth-first search stack 44762306a36Sopenharmony_ci * @stack.size: Number of allocated entries in @stack.entries 44862306a36Sopenharmony_ci * @stack.top: Index of the top stack entry (-1 if the stack is empty) 44962306a36Sopenharmony_ci * @stack.entries: Stack entries 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_cistruct media_pipeline_walk { 45262306a36Sopenharmony_ci struct media_device *mdev; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci struct { 45562306a36Sopenharmony_ci unsigned int size; 45662306a36Sopenharmony_ci int top; 45762306a36Sopenharmony_ci struct media_pipeline_walk_entry *entries; 45862306a36Sopenharmony_ci } stack; 45962306a36Sopenharmony_ci}; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci#define MEDIA_PIPELINE_STACK_GROW_STEP 16 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic struct media_pipeline_walk_entry * 46462306a36Sopenharmony_cimedia_pipeline_walk_top(struct media_pipeline_walk *walk) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci return &walk->stack.entries[walk->stack.top]; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic bool media_pipeline_walk_empty(struct media_pipeline_walk *walk) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci return walk->stack.top == -1; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci/* Increase the stack size by MEDIA_PIPELINE_STACK_GROW_STEP elements. */ 47562306a36Sopenharmony_cistatic int media_pipeline_walk_resize(struct media_pipeline_walk *walk) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct media_pipeline_walk_entry *entries; 47862306a36Sopenharmony_ci unsigned int new_size; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* Safety check, to avoid stack overflows in case of bugs. */ 48162306a36Sopenharmony_ci if (walk->stack.size >= 256) 48262306a36Sopenharmony_ci return -E2BIG; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci new_size = walk->stack.size + MEDIA_PIPELINE_STACK_GROW_STEP; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci entries = krealloc(walk->stack.entries, 48762306a36Sopenharmony_ci new_size * sizeof(*walk->stack.entries), 48862306a36Sopenharmony_ci GFP_KERNEL); 48962306a36Sopenharmony_ci if (!entries) 49062306a36Sopenharmony_ci return -ENOMEM; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci walk->stack.entries = entries; 49362306a36Sopenharmony_ci walk->stack.size = new_size; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return 0; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/* Push a new entry on the stack. */ 49962306a36Sopenharmony_cistatic int media_pipeline_walk_push(struct media_pipeline_walk *walk, 50062306a36Sopenharmony_ci struct media_pad *pad) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct media_pipeline_walk_entry *entry; 50362306a36Sopenharmony_ci int ret; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (walk->stack.top + 1 >= walk->stack.size) { 50662306a36Sopenharmony_ci ret = media_pipeline_walk_resize(walk); 50762306a36Sopenharmony_ci if (ret) 50862306a36Sopenharmony_ci return ret; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci walk->stack.top++; 51262306a36Sopenharmony_ci entry = media_pipeline_walk_top(walk); 51362306a36Sopenharmony_ci entry->pad = pad; 51462306a36Sopenharmony_ci entry->links = pad->entity->links.next; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci dev_dbg(walk->mdev->dev, 51762306a36Sopenharmony_ci "media pipeline: pushed entry %u: '%s':%u\n", 51862306a36Sopenharmony_ci walk->stack.top, pad->entity->name, pad->index); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci/* 52462306a36Sopenharmony_ci * Move the top entry link cursor to the next link. If all links of the entry 52562306a36Sopenharmony_ci * have been visited, pop the entry itself. Return true if the entry has been 52662306a36Sopenharmony_ci * popped. 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_cistatic bool media_pipeline_walk_pop(struct media_pipeline_walk *walk) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct media_pipeline_walk_entry *entry; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (WARN_ON(walk->stack.top < 0)) 53362306a36Sopenharmony_ci return false; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci entry = media_pipeline_walk_top(walk); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (entry->links->next == &entry->pad->entity->links) { 53862306a36Sopenharmony_ci dev_dbg(walk->mdev->dev, 53962306a36Sopenharmony_ci "media pipeline: entry %u has no more links, popping\n", 54062306a36Sopenharmony_ci walk->stack.top); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci walk->stack.top--; 54362306a36Sopenharmony_ci return true; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci entry->links = entry->links->next; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci dev_dbg(walk->mdev->dev, 54962306a36Sopenharmony_ci "media pipeline: moved entry %u to next link\n", 55062306a36Sopenharmony_ci walk->stack.top); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return false; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci/* Free all memory allocated while walking the pipeline. */ 55662306a36Sopenharmony_cistatic void media_pipeline_walk_destroy(struct media_pipeline_walk *walk) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci kfree(walk->stack.entries); 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/* Add a pad to the pipeline and push it to the stack. */ 56262306a36Sopenharmony_cistatic int media_pipeline_add_pad(struct media_pipeline *pipe, 56362306a36Sopenharmony_ci struct media_pipeline_walk *walk, 56462306a36Sopenharmony_ci struct media_pad *pad) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct media_pipeline_pad *ppad; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci list_for_each_entry(ppad, &pipe->pads, list) { 56962306a36Sopenharmony_ci if (ppad->pad == pad) { 57062306a36Sopenharmony_ci dev_dbg(pad->graph_obj.mdev->dev, 57162306a36Sopenharmony_ci "media pipeline: already contains pad '%s':%u\n", 57262306a36Sopenharmony_ci pad->entity->name, pad->index); 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci ppad = kzalloc(sizeof(*ppad), GFP_KERNEL); 57862306a36Sopenharmony_ci if (!ppad) 57962306a36Sopenharmony_ci return -ENOMEM; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci ppad->pipe = pipe; 58262306a36Sopenharmony_ci ppad->pad = pad; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci list_add_tail(&ppad->list, &pipe->pads); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci dev_dbg(pad->graph_obj.mdev->dev, 58762306a36Sopenharmony_ci "media pipeline: added pad '%s':%u\n", 58862306a36Sopenharmony_ci pad->entity->name, pad->index); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci return media_pipeline_walk_push(walk, pad); 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci/* Explore the next link of the entity at the top of the stack. */ 59462306a36Sopenharmony_cistatic int media_pipeline_explore_next_link(struct media_pipeline *pipe, 59562306a36Sopenharmony_ci struct media_pipeline_walk *walk) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci struct media_pipeline_walk_entry *entry = media_pipeline_walk_top(walk); 59862306a36Sopenharmony_ci struct media_pad *origin; 59962306a36Sopenharmony_ci struct media_link *link; 60062306a36Sopenharmony_ci struct media_pad *local; 60162306a36Sopenharmony_ci struct media_pad *remote; 60262306a36Sopenharmony_ci bool last_link; 60362306a36Sopenharmony_ci int ret; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci origin = entry->pad; 60662306a36Sopenharmony_ci link = list_entry(entry->links, typeof(*link), list); 60762306a36Sopenharmony_ci last_link = media_pipeline_walk_pop(walk); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci dev_dbg(walk->mdev->dev, 61062306a36Sopenharmony_ci "media pipeline: exploring link '%s':%u -> '%s':%u\n", 61162306a36Sopenharmony_ci link->source->entity->name, link->source->index, 61262306a36Sopenharmony_ci link->sink->entity->name, link->sink->index); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* Get the local pad and remote pad. */ 61562306a36Sopenharmony_ci if (link->source->entity == origin->entity) { 61662306a36Sopenharmony_ci local = link->source; 61762306a36Sopenharmony_ci remote = link->sink; 61862306a36Sopenharmony_ci } else { 61962306a36Sopenharmony_ci local = link->sink; 62062306a36Sopenharmony_ci remote = link->source; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* 62462306a36Sopenharmony_ci * Skip links that originate from a different pad than the incoming pad 62562306a36Sopenharmony_ci * that is not connected internally in the entity to the incoming pad. 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_ci if (origin != local && 62862306a36Sopenharmony_ci !media_entity_has_pad_interdep(origin->entity, origin->index, 62962306a36Sopenharmony_ci local->index)) { 63062306a36Sopenharmony_ci dev_dbg(walk->mdev->dev, 63162306a36Sopenharmony_ci "media pipeline: skipping link (no route)\n"); 63262306a36Sopenharmony_ci goto done; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* 63662306a36Sopenharmony_ci * Add the local pad of the link to the pipeline and push it to the 63762306a36Sopenharmony_ci * stack, if not already present. 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_ci ret = media_pipeline_add_pad(pipe, walk, local); 64062306a36Sopenharmony_ci if (ret) 64162306a36Sopenharmony_ci return ret; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* Similarly, add the remote pad, but only if the link is enabled. */ 64462306a36Sopenharmony_ci if (!(link->flags & MEDIA_LNK_FL_ENABLED)) { 64562306a36Sopenharmony_ci dev_dbg(walk->mdev->dev, 64662306a36Sopenharmony_ci "media pipeline: skipping link (disabled)\n"); 64762306a36Sopenharmony_ci goto done; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci ret = media_pipeline_add_pad(pipe, walk, remote); 65162306a36Sopenharmony_ci if (ret) 65262306a36Sopenharmony_ci return ret; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cidone: 65562306a36Sopenharmony_ci /* 65662306a36Sopenharmony_ci * If we're done iterating over links, iterate over pads of the entity. 65762306a36Sopenharmony_ci * This is necessary to discover pads that are not connected with any 65862306a36Sopenharmony_ci * link. Those are dead ends from a pipeline exploration point of view, 65962306a36Sopenharmony_ci * but are still part of the pipeline and need to be added to enable 66062306a36Sopenharmony_ci * proper validation. 66162306a36Sopenharmony_ci */ 66262306a36Sopenharmony_ci if (!last_link) 66362306a36Sopenharmony_ci return 0; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci dev_dbg(walk->mdev->dev, 66662306a36Sopenharmony_ci "media pipeline: adding unconnected pads of '%s'\n", 66762306a36Sopenharmony_ci local->entity->name); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci media_entity_for_each_pad(origin->entity, local) { 67062306a36Sopenharmony_ci /* 67162306a36Sopenharmony_ci * Skip the origin pad (already handled), pad that have links 67262306a36Sopenharmony_ci * (already discovered through iterating over links) and pads 67362306a36Sopenharmony_ci * not internally connected. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ci if (origin == local || !local->num_links || 67662306a36Sopenharmony_ci !media_entity_has_pad_interdep(origin->entity, origin->index, 67762306a36Sopenharmony_ci local->index)) 67862306a36Sopenharmony_ci continue; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci ret = media_pipeline_add_pad(pipe, walk, local); 68162306a36Sopenharmony_ci if (ret) 68262306a36Sopenharmony_ci return ret; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return 0; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic void media_pipeline_cleanup(struct media_pipeline *pipe) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci while (!list_empty(&pipe->pads)) { 69162306a36Sopenharmony_ci struct media_pipeline_pad *ppad; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci ppad = list_first_entry(&pipe->pads, typeof(*ppad), list); 69462306a36Sopenharmony_ci list_del(&ppad->list); 69562306a36Sopenharmony_ci kfree(ppad); 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic int media_pipeline_populate(struct media_pipeline *pipe, 70062306a36Sopenharmony_ci struct media_pad *pad) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct media_pipeline_walk walk = { }; 70362306a36Sopenharmony_ci struct media_pipeline_pad *ppad; 70462306a36Sopenharmony_ci int ret; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* 70762306a36Sopenharmony_ci * Populate the media pipeline by walking the media graph, starting 70862306a36Sopenharmony_ci * from @pad. 70962306a36Sopenharmony_ci */ 71062306a36Sopenharmony_ci INIT_LIST_HEAD(&pipe->pads); 71162306a36Sopenharmony_ci pipe->mdev = pad->graph_obj.mdev; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci walk.mdev = pipe->mdev; 71462306a36Sopenharmony_ci walk.stack.top = -1; 71562306a36Sopenharmony_ci ret = media_pipeline_add_pad(pipe, &walk, pad); 71662306a36Sopenharmony_ci if (ret) 71762306a36Sopenharmony_ci goto done; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* 72062306a36Sopenharmony_ci * Use a depth-first search algorithm: as long as the stack is not 72162306a36Sopenharmony_ci * empty, explore the next link of the top entry. The 72262306a36Sopenharmony_ci * media_pipeline_explore_next_link() function will either move to the 72362306a36Sopenharmony_ci * next link, pop the entry if fully visited, or add new entries on 72462306a36Sopenharmony_ci * top. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci while (!media_pipeline_walk_empty(&walk)) { 72762306a36Sopenharmony_ci ret = media_pipeline_explore_next_link(pipe, &walk); 72862306a36Sopenharmony_ci if (ret) 72962306a36Sopenharmony_ci goto done; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci dev_dbg(pad->graph_obj.mdev->dev, 73362306a36Sopenharmony_ci "media pipeline populated, found pads:\n"); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci list_for_each_entry(ppad, &pipe->pads, list) 73662306a36Sopenharmony_ci dev_dbg(pad->graph_obj.mdev->dev, "- '%s':%u\n", 73762306a36Sopenharmony_ci ppad->pad->entity->name, ppad->pad->index); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci WARN_ON(walk.stack.top != -1); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci ret = 0; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cidone: 74462306a36Sopenharmony_ci media_pipeline_walk_destroy(&walk); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (ret) 74762306a36Sopenharmony_ci media_pipeline_cleanup(pipe); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci return ret; 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci__must_check int __media_pipeline_start(struct media_pad *pad, 75362306a36Sopenharmony_ci struct media_pipeline *pipe) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci struct media_device *mdev = pad->graph_obj.mdev; 75662306a36Sopenharmony_ci struct media_pipeline_pad *err_ppad; 75762306a36Sopenharmony_ci struct media_pipeline_pad *ppad; 75862306a36Sopenharmony_ci int ret; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci lockdep_assert_held(&mdev->graph_mutex); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* 76362306a36Sopenharmony_ci * If the pad is already part of a pipeline, that pipeline must be the 76462306a36Sopenharmony_ci * same as the pipe given to media_pipeline_start(). 76562306a36Sopenharmony_ci */ 76662306a36Sopenharmony_ci if (WARN_ON(pad->pipe && pad->pipe != pipe)) 76762306a36Sopenharmony_ci return -EINVAL; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* 77062306a36Sopenharmony_ci * If the pipeline has already been started, it is guaranteed to be 77162306a36Sopenharmony_ci * valid, so just increase the start count. 77262306a36Sopenharmony_ci */ 77362306a36Sopenharmony_ci if (pipe->start_count) { 77462306a36Sopenharmony_ci pipe->start_count++; 77562306a36Sopenharmony_ci return 0; 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* 77962306a36Sopenharmony_ci * Populate the pipeline. This populates the media_pipeline pads list 78062306a36Sopenharmony_ci * with media_pipeline_pad instances for each pad found during graph 78162306a36Sopenharmony_ci * walk. 78262306a36Sopenharmony_ci */ 78362306a36Sopenharmony_ci ret = media_pipeline_populate(pipe, pad); 78462306a36Sopenharmony_ci if (ret) 78562306a36Sopenharmony_ci return ret; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* 78862306a36Sopenharmony_ci * Now that all the pads in the pipeline have been gathered, perform 78962306a36Sopenharmony_ci * the validation steps. 79062306a36Sopenharmony_ci */ 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci list_for_each_entry(ppad, &pipe->pads, list) { 79362306a36Sopenharmony_ci struct media_pad *pad = ppad->pad; 79462306a36Sopenharmony_ci struct media_entity *entity = pad->entity; 79562306a36Sopenharmony_ci bool has_enabled_link = false; 79662306a36Sopenharmony_ci struct media_link *link; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci dev_dbg(mdev->dev, "Validating pad '%s':%u\n", pad->entity->name, 79962306a36Sopenharmony_ci pad->index); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* 80262306a36Sopenharmony_ci * 1. Ensure that the pad doesn't already belong to a different 80362306a36Sopenharmony_ci * pipeline. 80462306a36Sopenharmony_ci */ 80562306a36Sopenharmony_ci if (pad->pipe) { 80662306a36Sopenharmony_ci dev_dbg(mdev->dev, "Failed to start pipeline: pad '%s':%u busy\n", 80762306a36Sopenharmony_ci pad->entity->name, pad->index); 80862306a36Sopenharmony_ci ret = -EBUSY; 80962306a36Sopenharmony_ci goto error; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* 81362306a36Sopenharmony_ci * 2. Validate all active links whose sink is the current pad. 81462306a36Sopenharmony_ci * Validation of the source pads is performed in the context of 81562306a36Sopenharmony_ci * the connected sink pad to avoid duplicating checks. 81662306a36Sopenharmony_ci */ 81762306a36Sopenharmony_ci for_each_media_entity_data_link(entity, link) { 81862306a36Sopenharmony_ci /* Skip links unrelated to the current pad. */ 81962306a36Sopenharmony_ci if (link->sink != pad && link->source != pad) 82062306a36Sopenharmony_ci continue; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci /* Record if the pad has links and enabled links. */ 82362306a36Sopenharmony_ci if (link->flags & MEDIA_LNK_FL_ENABLED) 82462306a36Sopenharmony_ci has_enabled_link = true; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci /* 82762306a36Sopenharmony_ci * Validate the link if it's enabled and has the 82862306a36Sopenharmony_ci * current pad as its sink. 82962306a36Sopenharmony_ci */ 83062306a36Sopenharmony_ci if (!(link->flags & MEDIA_LNK_FL_ENABLED)) 83162306a36Sopenharmony_ci continue; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (link->sink != pad) 83462306a36Sopenharmony_ci continue; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (!entity->ops || !entity->ops->link_validate) 83762306a36Sopenharmony_ci continue; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci ret = entity->ops->link_validate(link); 84062306a36Sopenharmony_ci if (ret) { 84162306a36Sopenharmony_ci dev_dbg(mdev->dev, 84262306a36Sopenharmony_ci "Link '%s':%u -> '%s':%u failed validation: %d\n", 84362306a36Sopenharmony_ci link->source->entity->name, 84462306a36Sopenharmony_ci link->source->index, 84562306a36Sopenharmony_ci link->sink->entity->name, 84662306a36Sopenharmony_ci link->sink->index, ret); 84762306a36Sopenharmony_ci goto error; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci dev_dbg(mdev->dev, 85162306a36Sopenharmony_ci "Link '%s':%u -> '%s':%u is valid\n", 85262306a36Sopenharmony_ci link->source->entity->name, 85362306a36Sopenharmony_ci link->source->index, 85462306a36Sopenharmony_ci link->sink->entity->name, 85562306a36Sopenharmony_ci link->sink->index); 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* 85962306a36Sopenharmony_ci * 3. If the pad has the MEDIA_PAD_FL_MUST_CONNECT flag set, 86062306a36Sopenharmony_ci * ensure that it has either no link or an enabled link. 86162306a36Sopenharmony_ci */ 86262306a36Sopenharmony_ci if ((pad->flags & MEDIA_PAD_FL_MUST_CONNECT) && 86362306a36Sopenharmony_ci !has_enabled_link) { 86462306a36Sopenharmony_ci dev_dbg(mdev->dev, 86562306a36Sopenharmony_ci "Pad '%s':%u must be connected by an enabled link\n", 86662306a36Sopenharmony_ci pad->entity->name, pad->index); 86762306a36Sopenharmony_ci ret = -ENOLINK; 86862306a36Sopenharmony_ci goto error; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* Validation passed, store the pipe pointer in the pad. */ 87262306a36Sopenharmony_ci pad->pipe = pipe; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci pipe->start_count++; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci return 0; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cierror: 88062306a36Sopenharmony_ci /* 88162306a36Sopenharmony_ci * Link validation on graph failed. We revert what we did and 88262306a36Sopenharmony_ci * return the error. 88362306a36Sopenharmony_ci */ 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci list_for_each_entry(err_ppad, &pipe->pads, list) { 88662306a36Sopenharmony_ci if (err_ppad == ppad) 88762306a36Sopenharmony_ci break; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci err_ppad->pad->pipe = NULL; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci media_pipeline_cleanup(pipe); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci return ret; 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_pipeline_start); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci__must_check int media_pipeline_start(struct media_pad *pad, 89962306a36Sopenharmony_ci struct media_pipeline *pipe) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci struct media_device *mdev = pad->graph_obj.mdev; 90262306a36Sopenharmony_ci int ret; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 90562306a36Sopenharmony_ci ret = __media_pipeline_start(pad, pipe); 90662306a36Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 90762306a36Sopenharmony_ci return ret; 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pipeline_start); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_civoid __media_pipeline_stop(struct media_pad *pad) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci struct media_pipeline *pipe = pad->pipe; 91462306a36Sopenharmony_ci struct media_pipeline_pad *ppad; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* 91762306a36Sopenharmony_ci * If the following check fails, the driver has performed an 91862306a36Sopenharmony_ci * unbalanced call to media_pipeline_stop() 91962306a36Sopenharmony_ci */ 92062306a36Sopenharmony_ci if (WARN_ON(!pipe)) 92162306a36Sopenharmony_ci return; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci if (--pipe->start_count) 92462306a36Sopenharmony_ci return; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci list_for_each_entry(ppad, &pipe->pads, list) 92762306a36Sopenharmony_ci ppad->pad->pipe = NULL; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci media_pipeline_cleanup(pipe); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (pipe->allocated) 93262306a36Sopenharmony_ci kfree(pipe); 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_pipeline_stop); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_civoid media_pipeline_stop(struct media_pad *pad) 93762306a36Sopenharmony_ci{ 93862306a36Sopenharmony_ci struct media_device *mdev = pad->graph_obj.mdev; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 94162306a36Sopenharmony_ci __media_pipeline_stop(pad); 94262306a36Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pipeline_stop); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci__must_check int media_pipeline_alloc_start(struct media_pad *pad) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci struct media_device *mdev = pad->graph_obj.mdev; 94962306a36Sopenharmony_ci struct media_pipeline *new_pipe = NULL; 95062306a36Sopenharmony_ci struct media_pipeline *pipe; 95162306a36Sopenharmony_ci int ret; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* 95662306a36Sopenharmony_ci * Is the pad already part of a pipeline? If not, we need to allocate 95762306a36Sopenharmony_ci * a pipe. 95862306a36Sopenharmony_ci */ 95962306a36Sopenharmony_ci pipe = media_pad_pipeline(pad); 96062306a36Sopenharmony_ci if (!pipe) { 96162306a36Sopenharmony_ci new_pipe = kzalloc(sizeof(*new_pipe), GFP_KERNEL); 96262306a36Sopenharmony_ci if (!new_pipe) { 96362306a36Sopenharmony_ci ret = -ENOMEM; 96462306a36Sopenharmony_ci goto out; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci pipe = new_pipe; 96862306a36Sopenharmony_ci pipe->allocated = true; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci ret = __media_pipeline_start(pad, pipe); 97262306a36Sopenharmony_ci if (ret) 97362306a36Sopenharmony_ci kfree(new_pipe); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ciout: 97662306a36Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci return ret; 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pipeline_alloc_start); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistruct media_pad * 98362306a36Sopenharmony_ci__media_pipeline_pad_iter_next(struct media_pipeline *pipe, 98462306a36Sopenharmony_ci struct media_pipeline_pad_iter *iter, 98562306a36Sopenharmony_ci struct media_pad *pad) 98662306a36Sopenharmony_ci{ 98762306a36Sopenharmony_ci if (!pad) 98862306a36Sopenharmony_ci iter->cursor = pipe->pads.next; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (iter->cursor == &pipe->pads) 99162306a36Sopenharmony_ci return NULL; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci pad = list_entry(iter->cursor, struct media_pipeline_pad, list)->pad; 99462306a36Sopenharmony_ci iter->cursor = iter->cursor->next; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci return pad; 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_pipeline_pad_iter_next); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ciint media_pipeline_entity_iter_init(struct media_pipeline *pipe, 100162306a36Sopenharmony_ci struct media_pipeline_entity_iter *iter) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci return media_entity_enum_init(&iter->ent_enum, pipe->mdev); 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pipeline_entity_iter_init); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_civoid media_pipeline_entity_iter_cleanup(struct media_pipeline_entity_iter *iter) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci media_entity_enum_cleanup(&iter->ent_enum); 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pipeline_entity_iter_cleanup); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistruct media_entity * 101462306a36Sopenharmony_ci__media_pipeline_entity_iter_next(struct media_pipeline *pipe, 101562306a36Sopenharmony_ci struct media_pipeline_entity_iter *iter, 101662306a36Sopenharmony_ci struct media_entity *entity) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci if (!entity) 101962306a36Sopenharmony_ci iter->cursor = pipe->pads.next; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci while (iter->cursor != &pipe->pads) { 102262306a36Sopenharmony_ci struct media_pipeline_pad *ppad; 102362306a36Sopenharmony_ci struct media_entity *entity; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci ppad = list_entry(iter->cursor, struct media_pipeline_pad, list); 102662306a36Sopenharmony_ci entity = ppad->pad->entity; 102762306a36Sopenharmony_ci iter->cursor = iter->cursor->next; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci if (!media_entity_enum_test_and_set(&iter->ent_enum, entity)) 103062306a36Sopenharmony_ci return entity; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci return NULL; 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_pipeline_entity_iter_next); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 103862306a36Sopenharmony_ci * Links management 103962306a36Sopenharmony_ci */ 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_cistatic struct media_link *media_add_link(struct list_head *head) 104262306a36Sopenharmony_ci{ 104362306a36Sopenharmony_ci struct media_link *link; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci link = kzalloc(sizeof(*link), GFP_KERNEL); 104662306a36Sopenharmony_ci if (link == NULL) 104762306a36Sopenharmony_ci return NULL; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci list_add_tail(&link->list, head); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci return link; 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic void __media_entity_remove_link(struct media_entity *entity, 105562306a36Sopenharmony_ci struct media_link *link) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci struct media_link *rlink, *tmp; 105862306a36Sopenharmony_ci struct media_entity *remote; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci /* Remove the reverse links for a data link. */ 106162306a36Sopenharmony_ci if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) == MEDIA_LNK_FL_DATA_LINK) { 106262306a36Sopenharmony_ci link->source->num_links--; 106362306a36Sopenharmony_ci link->sink->num_links--; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci if (link->source->entity == entity) 106662306a36Sopenharmony_ci remote = link->sink->entity; 106762306a36Sopenharmony_ci else 106862306a36Sopenharmony_ci remote = link->source->entity; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci list_for_each_entry_safe(rlink, tmp, &remote->links, list) { 107162306a36Sopenharmony_ci if (rlink != link->reverse) 107262306a36Sopenharmony_ci continue; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci if (link->source->entity == entity) 107562306a36Sopenharmony_ci remote->num_backlinks--; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci /* Remove the remote link */ 107862306a36Sopenharmony_ci list_del(&rlink->list); 107962306a36Sopenharmony_ci media_gobj_destroy(&rlink->graph_obj); 108062306a36Sopenharmony_ci kfree(rlink); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (--remote->num_links == 0) 108362306a36Sopenharmony_ci break; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci list_del(&link->list); 108862306a36Sopenharmony_ci media_gobj_destroy(&link->graph_obj); 108962306a36Sopenharmony_ci kfree(link); 109062306a36Sopenharmony_ci} 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ciint media_get_pad_index(struct media_entity *entity, u32 pad_type, 109362306a36Sopenharmony_ci enum media_pad_signal_type sig_type) 109462306a36Sopenharmony_ci{ 109562306a36Sopenharmony_ci unsigned int i; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci if (!entity) 109862306a36Sopenharmony_ci return -EINVAL; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci for (i = 0; i < entity->num_pads; i++) { 110162306a36Sopenharmony_ci if ((entity->pads[i].flags & 110262306a36Sopenharmony_ci (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) != pad_type) 110362306a36Sopenharmony_ci continue; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci if (entity->pads[i].sig_type == sig_type) 110662306a36Sopenharmony_ci return i; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci return -EINVAL; 110962306a36Sopenharmony_ci} 111062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_get_pad_index); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ciint 111362306a36Sopenharmony_cimedia_create_pad_link(struct media_entity *source, u16 source_pad, 111462306a36Sopenharmony_ci struct media_entity *sink, u16 sink_pad, u32 flags) 111562306a36Sopenharmony_ci{ 111662306a36Sopenharmony_ci struct media_link *link; 111762306a36Sopenharmony_ci struct media_link *backlink; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci if (flags & MEDIA_LNK_FL_LINK_TYPE) 112062306a36Sopenharmony_ci return -EINVAL; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci flags |= MEDIA_LNK_FL_DATA_LINK; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci if (WARN_ON(!source || !sink) || 112562306a36Sopenharmony_ci WARN_ON(source_pad >= source->num_pads) || 112662306a36Sopenharmony_ci WARN_ON(sink_pad >= sink->num_pads)) 112762306a36Sopenharmony_ci return -EINVAL; 112862306a36Sopenharmony_ci if (WARN_ON(!(source->pads[source_pad].flags & MEDIA_PAD_FL_SOURCE))) 112962306a36Sopenharmony_ci return -EINVAL; 113062306a36Sopenharmony_ci if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK))) 113162306a36Sopenharmony_ci return -EINVAL; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci link = media_add_link(&source->links); 113462306a36Sopenharmony_ci if (link == NULL) 113562306a36Sopenharmony_ci return -ENOMEM; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci link->source = &source->pads[source_pad]; 113862306a36Sopenharmony_ci link->sink = &sink->pads[sink_pad]; 113962306a36Sopenharmony_ci link->flags = flags; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci /* Initialize graph object embedded at the new link */ 114262306a36Sopenharmony_ci media_gobj_create(source->graph_obj.mdev, MEDIA_GRAPH_LINK, 114362306a36Sopenharmony_ci &link->graph_obj); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* Create the backlink. Backlinks are used to help graph traversal and 114662306a36Sopenharmony_ci * are not reported to userspace. 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_ci backlink = media_add_link(&sink->links); 114962306a36Sopenharmony_ci if (backlink == NULL) { 115062306a36Sopenharmony_ci __media_entity_remove_link(source, link); 115162306a36Sopenharmony_ci return -ENOMEM; 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci backlink->source = &source->pads[source_pad]; 115562306a36Sopenharmony_ci backlink->sink = &sink->pads[sink_pad]; 115662306a36Sopenharmony_ci backlink->flags = flags; 115762306a36Sopenharmony_ci backlink->is_backlink = true; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci /* Initialize graph object embedded at the new link */ 116062306a36Sopenharmony_ci media_gobj_create(sink->graph_obj.mdev, MEDIA_GRAPH_LINK, 116162306a36Sopenharmony_ci &backlink->graph_obj); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci link->reverse = backlink; 116462306a36Sopenharmony_ci backlink->reverse = link; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci sink->num_backlinks++; 116762306a36Sopenharmony_ci sink->num_links++; 116862306a36Sopenharmony_ci source->num_links++; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci link->source->num_links++; 117162306a36Sopenharmony_ci link->sink->num_links++; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci return 0; 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_create_pad_link); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ciint media_create_pad_links(const struct media_device *mdev, 117862306a36Sopenharmony_ci const u32 source_function, 117962306a36Sopenharmony_ci struct media_entity *source, 118062306a36Sopenharmony_ci const u16 source_pad, 118162306a36Sopenharmony_ci const u32 sink_function, 118262306a36Sopenharmony_ci struct media_entity *sink, 118362306a36Sopenharmony_ci const u16 sink_pad, 118462306a36Sopenharmony_ci u32 flags, 118562306a36Sopenharmony_ci const bool allow_both_undefined) 118662306a36Sopenharmony_ci{ 118762306a36Sopenharmony_ci struct media_entity *entity; 118862306a36Sopenharmony_ci unsigned function; 118962306a36Sopenharmony_ci int ret; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci /* Trivial case: 1:1 relation */ 119262306a36Sopenharmony_ci if (source && sink) 119362306a36Sopenharmony_ci return media_create_pad_link(source, source_pad, 119462306a36Sopenharmony_ci sink, sink_pad, flags); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci /* Worse case scenario: n:n relation */ 119762306a36Sopenharmony_ci if (!source && !sink) { 119862306a36Sopenharmony_ci if (!allow_both_undefined) 119962306a36Sopenharmony_ci return 0; 120062306a36Sopenharmony_ci media_device_for_each_entity(source, mdev) { 120162306a36Sopenharmony_ci if (source->function != source_function) 120262306a36Sopenharmony_ci continue; 120362306a36Sopenharmony_ci media_device_for_each_entity(sink, mdev) { 120462306a36Sopenharmony_ci if (sink->function != sink_function) 120562306a36Sopenharmony_ci continue; 120662306a36Sopenharmony_ci ret = media_create_pad_link(source, source_pad, 120762306a36Sopenharmony_ci sink, sink_pad, 120862306a36Sopenharmony_ci flags); 120962306a36Sopenharmony_ci if (ret) 121062306a36Sopenharmony_ci return ret; 121162306a36Sopenharmony_ci flags &= ~(MEDIA_LNK_FL_ENABLED | 121262306a36Sopenharmony_ci MEDIA_LNK_FL_IMMUTABLE); 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci return 0; 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci /* Handle 1:n and n:1 cases */ 121962306a36Sopenharmony_ci if (source) 122062306a36Sopenharmony_ci function = sink_function; 122162306a36Sopenharmony_ci else 122262306a36Sopenharmony_ci function = source_function; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci media_device_for_each_entity(entity, mdev) { 122562306a36Sopenharmony_ci if (entity->function != function) 122662306a36Sopenharmony_ci continue; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci if (source) 122962306a36Sopenharmony_ci ret = media_create_pad_link(source, source_pad, 123062306a36Sopenharmony_ci entity, sink_pad, flags); 123162306a36Sopenharmony_ci else 123262306a36Sopenharmony_ci ret = media_create_pad_link(entity, source_pad, 123362306a36Sopenharmony_ci sink, sink_pad, flags); 123462306a36Sopenharmony_ci if (ret) 123562306a36Sopenharmony_ci return ret; 123662306a36Sopenharmony_ci flags &= ~(MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci return 0; 123962306a36Sopenharmony_ci} 124062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_create_pad_links); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_civoid __media_entity_remove_links(struct media_entity *entity) 124362306a36Sopenharmony_ci{ 124462306a36Sopenharmony_ci struct media_link *link, *tmp; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci list_for_each_entry_safe(link, tmp, &entity->links, list) 124762306a36Sopenharmony_ci __media_entity_remove_link(entity, link); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci entity->num_links = 0; 125062306a36Sopenharmony_ci entity->num_backlinks = 0; 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_entity_remove_links); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_civoid media_entity_remove_links(struct media_entity *entity) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci struct media_device *mdev = entity->graph_obj.mdev; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci /* Do nothing if the entity is not registered. */ 125962306a36Sopenharmony_ci if (mdev == NULL) 126062306a36Sopenharmony_ci return; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 126362306a36Sopenharmony_ci __media_entity_remove_links(entity); 126462306a36Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_remove_links); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_cistatic int __media_entity_setup_link_notify(struct media_link *link, u32 flags) 126962306a36Sopenharmony_ci{ 127062306a36Sopenharmony_ci int ret; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci /* Notify both entities. */ 127362306a36Sopenharmony_ci ret = media_entity_call(link->source->entity, link_setup, 127462306a36Sopenharmony_ci link->source, link->sink, flags); 127562306a36Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) 127662306a36Sopenharmony_ci return ret; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci ret = media_entity_call(link->sink->entity, link_setup, 127962306a36Sopenharmony_ci link->sink, link->source, flags); 128062306a36Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) { 128162306a36Sopenharmony_ci media_entity_call(link->source->entity, link_setup, 128262306a36Sopenharmony_ci link->source, link->sink, link->flags); 128362306a36Sopenharmony_ci return ret; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci link->flags = flags; 128762306a36Sopenharmony_ci link->reverse->flags = link->flags; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci return 0; 129062306a36Sopenharmony_ci} 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ciint __media_entity_setup_link(struct media_link *link, u32 flags) 129362306a36Sopenharmony_ci{ 129462306a36Sopenharmony_ci const u32 mask = MEDIA_LNK_FL_ENABLED; 129562306a36Sopenharmony_ci struct media_device *mdev; 129662306a36Sopenharmony_ci struct media_pad *source, *sink; 129762306a36Sopenharmony_ci int ret = -EBUSY; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci if (link == NULL) 130062306a36Sopenharmony_ci return -EINVAL; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci /* The non-modifiable link flags must not be modified. */ 130362306a36Sopenharmony_ci if ((link->flags & ~mask) != (flags & ~mask)) 130462306a36Sopenharmony_ci return -EINVAL; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci if (link->flags & MEDIA_LNK_FL_IMMUTABLE) 130762306a36Sopenharmony_ci return link->flags == flags ? 0 : -EINVAL; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (link->flags == flags) 131062306a36Sopenharmony_ci return 0; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci source = link->source; 131362306a36Sopenharmony_ci sink = link->sink; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci if (!(link->flags & MEDIA_LNK_FL_DYNAMIC) && 131662306a36Sopenharmony_ci (media_pad_is_streaming(source) || media_pad_is_streaming(sink))) 131762306a36Sopenharmony_ci return -EBUSY; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci mdev = source->graph_obj.mdev; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci if (mdev->ops && mdev->ops->link_notify) { 132262306a36Sopenharmony_ci ret = mdev->ops->link_notify(link, flags, 132362306a36Sopenharmony_ci MEDIA_DEV_NOTIFY_PRE_LINK_CH); 132462306a36Sopenharmony_ci if (ret < 0) 132562306a36Sopenharmony_ci return ret; 132662306a36Sopenharmony_ci } 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci ret = __media_entity_setup_link_notify(link, flags); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci if (mdev->ops && mdev->ops->link_notify) 133162306a36Sopenharmony_ci mdev->ops->link_notify(link, flags, 133262306a36Sopenharmony_ci MEDIA_DEV_NOTIFY_POST_LINK_CH); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci return ret; 133562306a36Sopenharmony_ci} 133662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_entity_setup_link); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ciint media_entity_setup_link(struct media_link *link, u32 flags) 133962306a36Sopenharmony_ci{ 134062306a36Sopenharmony_ci int ret; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci mutex_lock(&link->graph_obj.mdev->graph_mutex); 134362306a36Sopenharmony_ci ret = __media_entity_setup_link(link, flags); 134462306a36Sopenharmony_ci mutex_unlock(&link->graph_obj.mdev->graph_mutex); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci return ret; 134762306a36Sopenharmony_ci} 134862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_setup_link); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_cistruct media_link * 135162306a36Sopenharmony_cimedia_entity_find_link(struct media_pad *source, struct media_pad *sink) 135262306a36Sopenharmony_ci{ 135362306a36Sopenharmony_ci struct media_link *link; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci for_each_media_entity_data_link(source->entity, link) { 135662306a36Sopenharmony_ci if (link->source->entity == source->entity && 135762306a36Sopenharmony_ci link->source->index == source->index && 135862306a36Sopenharmony_ci link->sink->entity == sink->entity && 135962306a36Sopenharmony_ci link->sink->index == sink->index) 136062306a36Sopenharmony_ci return link; 136162306a36Sopenharmony_ci } 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci return NULL; 136462306a36Sopenharmony_ci} 136562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_find_link); 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_cistruct media_pad *media_pad_remote_pad_first(const struct media_pad *pad) 136862306a36Sopenharmony_ci{ 136962306a36Sopenharmony_ci struct media_link *link; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci for_each_media_entity_data_link(pad->entity, link) { 137262306a36Sopenharmony_ci if (!(link->flags & MEDIA_LNK_FL_ENABLED)) 137362306a36Sopenharmony_ci continue; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci if (link->source == pad) 137662306a36Sopenharmony_ci return link->sink; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci if (link->sink == pad) 137962306a36Sopenharmony_ci return link->source; 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci return NULL; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci} 138562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pad_remote_pad_first); 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_cistruct media_pad * 138862306a36Sopenharmony_cimedia_entity_remote_pad_unique(const struct media_entity *entity, 138962306a36Sopenharmony_ci unsigned int type) 139062306a36Sopenharmony_ci{ 139162306a36Sopenharmony_ci struct media_pad *pad = NULL; 139262306a36Sopenharmony_ci struct media_link *link; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci list_for_each_entry(link, &entity->links, list) { 139562306a36Sopenharmony_ci struct media_pad *local_pad; 139662306a36Sopenharmony_ci struct media_pad *remote_pad; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci if (((link->flags & MEDIA_LNK_FL_LINK_TYPE) != 139962306a36Sopenharmony_ci MEDIA_LNK_FL_DATA_LINK) || 140062306a36Sopenharmony_ci !(link->flags & MEDIA_LNK_FL_ENABLED)) 140162306a36Sopenharmony_ci continue; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci if (type == MEDIA_PAD_FL_SOURCE) { 140462306a36Sopenharmony_ci local_pad = link->sink; 140562306a36Sopenharmony_ci remote_pad = link->source; 140662306a36Sopenharmony_ci } else { 140762306a36Sopenharmony_ci local_pad = link->source; 140862306a36Sopenharmony_ci remote_pad = link->sink; 140962306a36Sopenharmony_ci } 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci if (local_pad->entity == entity) { 141262306a36Sopenharmony_ci if (pad) 141362306a36Sopenharmony_ci return ERR_PTR(-ENOTUNIQ); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci pad = remote_pad; 141662306a36Sopenharmony_ci } 141762306a36Sopenharmony_ci } 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci if (!pad) 142062306a36Sopenharmony_ci return ERR_PTR(-ENOLINK); 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci return pad; 142362306a36Sopenharmony_ci} 142462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_remote_pad_unique); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_cistruct media_pad *media_pad_remote_pad_unique(const struct media_pad *pad) 142762306a36Sopenharmony_ci{ 142862306a36Sopenharmony_ci struct media_pad *found_pad = NULL; 142962306a36Sopenharmony_ci struct media_link *link; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci list_for_each_entry(link, &pad->entity->links, list) { 143262306a36Sopenharmony_ci struct media_pad *remote_pad; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci if (!(link->flags & MEDIA_LNK_FL_ENABLED)) 143562306a36Sopenharmony_ci continue; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci if (link->sink == pad) 143862306a36Sopenharmony_ci remote_pad = link->source; 143962306a36Sopenharmony_ci else if (link->source == pad) 144062306a36Sopenharmony_ci remote_pad = link->sink; 144162306a36Sopenharmony_ci else 144262306a36Sopenharmony_ci continue; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci if (found_pad) 144562306a36Sopenharmony_ci return ERR_PTR(-ENOTUNIQ); 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci found_pad = remote_pad; 144862306a36Sopenharmony_ci } 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci if (!found_pad) 145162306a36Sopenharmony_ci return ERR_PTR(-ENOLINK); 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci return found_pad; 145462306a36Sopenharmony_ci} 145562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pad_remote_pad_unique); 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ciint media_entity_get_fwnode_pad(struct media_entity *entity, 145862306a36Sopenharmony_ci const struct fwnode_handle *fwnode, 145962306a36Sopenharmony_ci unsigned long direction_flags) 146062306a36Sopenharmony_ci{ 146162306a36Sopenharmony_ci struct fwnode_endpoint endpoint; 146262306a36Sopenharmony_ci unsigned int i; 146362306a36Sopenharmony_ci int ret; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci if (!entity->ops || !entity->ops->get_fwnode_pad) { 146662306a36Sopenharmony_ci for (i = 0; i < entity->num_pads; i++) { 146762306a36Sopenharmony_ci if (entity->pads[i].flags & direction_flags) 146862306a36Sopenharmony_ci return i; 146962306a36Sopenharmony_ci } 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci return -ENXIO; 147262306a36Sopenharmony_ci } 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci ret = fwnode_graph_parse_endpoint(fwnode, &endpoint); 147562306a36Sopenharmony_ci if (ret) 147662306a36Sopenharmony_ci return ret; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci ret = entity->ops->get_fwnode_pad(entity, &endpoint); 147962306a36Sopenharmony_ci if (ret < 0) 148062306a36Sopenharmony_ci return ret; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci if (ret >= entity->num_pads) 148362306a36Sopenharmony_ci return -ENXIO; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci if (!(entity->pads[ret].flags & direction_flags)) 148662306a36Sopenharmony_ci return -ENXIO; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci return ret; 148962306a36Sopenharmony_ci} 149062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_get_fwnode_pad); 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_cistruct media_pipeline *media_entity_pipeline(struct media_entity *entity) 149362306a36Sopenharmony_ci{ 149462306a36Sopenharmony_ci struct media_pad *pad; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci media_entity_for_each_pad(entity, pad) { 149762306a36Sopenharmony_ci if (pad->pipe) 149862306a36Sopenharmony_ci return pad->pipe; 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci return NULL; 150262306a36Sopenharmony_ci} 150362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_pipeline); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_cistruct media_pipeline *media_pad_pipeline(struct media_pad *pad) 150662306a36Sopenharmony_ci{ 150762306a36Sopenharmony_ci return pad->pipe; 150862306a36Sopenharmony_ci} 150962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pad_pipeline); 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_cistatic void media_interface_init(struct media_device *mdev, 151262306a36Sopenharmony_ci struct media_interface *intf, 151362306a36Sopenharmony_ci u32 gobj_type, 151462306a36Sopenharmony_ci u32 intf_type, u32 flags) 151562306a36Sopenharmony_ci{ 151662306a36Sopenharmony_ci intf->type = intf_type; 151762306a36Sopenharmony_ci intf->flags = flags; 151862306a36Sopenharmony_ci INIT_LIST_HEAD(&intf->links); 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci media_gobj_create(mdev, gobj_type, &intf->graph_obj); 152162306a36Sopenharmony_ci} 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci/* Functions related to the media interface via device nodes */ 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_cistruct media_intf_devnode *media_devnode_create(struct media_device *mdev, 152662306a36Sopenharmony_ci u32 type, u32 flags, 152762306a36Sopenharmony_ci u32 major, u32 minor) 152862306a36Sopenharmony_ci{ 152962306a36Sopenharmony_ci struct media_intf_devnode *devnode; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci devnode = kzalloc(sizeof(*devnode), GFP_KERNEL); 153262306a36Sopenharmony_ci if (!devnode) 153362306a36Sopenharmony_ci return NULL; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci devnode->major = major; 153662306a36Sopenharmony_ci devnode->minor = minor; 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci media_interface_init(mdev, &devnode->intf, MEDIA_GRAPH_INTF_DEVNODE, 153962306a36Sopenharmony_ci type, flags); 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci return devnode; 154262306a36Sopenharmony_ci} 154362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_devnode_create); 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_civoid media_devnode_remove(struct media_intf_devnode *devnode) 154662306a36Sopenharmony_ci{ 154762306a36Sopenharmony_ci media_remove_intf_links(&devnode->intf); 154862306a36Sopenharmony_ci media_gobj_destroy(&devnode->intf.graph_obj); 154962306a36Sopenharmony_ci kfree(devnode); 155062306a36Sopenharmony_ci} 155162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_devnode_remove); 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_cistruct media_link *media_create_intf_link(struct media_entity *entity, 155462306a36Sopenharmony_ci struct media_interface *intf, 155562306a36Sopenharmony_ci u32 flags) 155662306a36Sopenharmony_ci{ 155762306a36Sopenharmony_ci struct media_link *link; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci link = media_add_link(&intf->links); 156062306a36Sopenharmony_ci if (link == NULL) 156162306a36Sopenharmony_ci return NULL; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci link->intf = intf; 156462306a36Sopenharmony_ci link->entity = entity; 156562306a36Sopenharmony_ci link->flags = flags | MEDIA_LNK_FL_INTERFACE_LINK; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci /* Initialize graph object embedded at the new link */ 156862306a36Sopenharmony_ci media_gobj_create(intf->graph_obj.mdev, MEDIA_GRAPH_LINK, 156962306a36Sopenharmony_ci &link->graph_obj); 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci return link; 157262306a36Sopenharmony_ci} 157362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_create_intf_link); 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_civoid __media_remove_intf_link(struct media_link *link) 157662306a36Sopenharmony_ci{ 157762306a36Sopenharmony_ci list_del(&link->list); 157862306a36Sopenharmony_ci media_gobj_destroy(&link->graph_obj); 157962306a36Sopenharmony_ci kfree(link); 158062306a36Sopenharmony_ci} 158162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_remove_intf_link); 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_civoid media_remove_intf_link(struct media_link *link) 158462306a36Sopenharmony_ci{ 158562306a36Sopenharmony_ci struct media_device *mdev = link->graph_obj.mdev; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci /* Do nothing if the intf is not registered. */ 158862306a36Sopenharmony_ci if (mdev == NULL) 158962306a36Sopenharmony_ci return; 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 159262306a36Sopenharmony_ci __media_remove_intf_link(link); 159362306a36Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 159462306a36Sopenharmony_ci} 159562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_remove_intf_link); 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_civoid __media_remove_intf_links(struct media_interface *intf) 159862306a36Sopenharmony_ci{ 159962306a36Sopenharmony_ci struct media_link *link, *tmp; 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci list_for_each_entry_safe(link, tmp, &intf->links, list) 160262306a36Sopenharmony_ci __media_remove_intf_link(link); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci} 160562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_remove_intf_links); 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_civoid media_remove_intf_links(struct media_interface *intf) 160862306a36Sopenharmony_ci{ 160962306a36Sopenharmony_ci struct media_device *mdev = intf->graph_obj.mdev; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci /* Do nothing if the intf is not registered. */ 161262306a36Sopenharmony_ci if (mdev == NULL) 161362306a36Sopenharmony_ci return; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 161662306a36Sopenharmony_ci __media_remove_intf_links(intf); 161762306a36Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 161862306a36Sopenharmony_ci} 161962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_remove_intf_links); 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_cistruct media_link *media_create_ancillary_link(struct media_entity *primary, 162262306a36Sopenharmony_ci struct media_entity *ancillary) 162362306a36Sopenharmony_ci{ 162462306a36Sopenharmony_ci struct media_link *link; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci link = media_add_link(&primary->links); 162762306a36Sopenharmony_ci if (!link) 162862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci link->gobj0 = &primary->graph_obj; 163162306a36Sopenharmony_ci link->gobj1 = &ancillary->graph_obj; 163262306a36Sopenharmony_ci link->flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED | 163362306a36Sopenharmony_ci MEDIA_LNK_FL_ANCILLARY_LINK; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci /* Initialize graph object embedded in the new link */ 163662306a36Sopenharmony_ci media_gobj_create(primary->graph_obj.mdev, MEDIA_GRAPH_LINK, 163762306a36Sopenharmony_ci &link->graph_obj); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci return link; 164062306a36Sopenharmony_ci} 164162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_create_ancillary_link); 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_cistruct media_link *__media_entity_next_link(struct media_entity *entity, 164462306a36Sopenharmony_ci struct media_link *link, 164562306a36Sopenharmony_ci unsigned long link_type) 164662306a36Sopenharmony_ci{ 164762306a36Sopenharmony_ci link = link ? list_next_entry(link, list) 164862306a36Sopenharmony_ci : list_first_entry(&entity->links, typeof(*link), list); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci list_for_each_entry_from(link, &entity->links, list) 165162306a36Sopenharmony_ci if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) == link_type) 165262306a36Sopenharmony_ci return link; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci return NULL; 165562306a36Sopenharmony_ci} 165662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_entity_next_link); 1657