18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Media entity 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Nokia Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> 88c2ecf20Sopenharmony_ci * Sakari Ailus <sakari.ailus@iki.fi> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 128c2ecf20Sopenharmony_ci#include <linux/property.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <media/media-entity.h> 158c2ecf20Sopenharmony_ci#include <media/media-device.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic inline const char *gobj_type(enum media_gobj_type type) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci switch (type) { 208c2ecf20Sopenharmony_ci case MEDIA_GRAPH_ENTITY: 218c2ecf20Sopenharmony_ci return "entity"; 228c2ecf20Sopenharmony_ci case MEDIA_GRAPH_PAD: 238c2ecf20Sopenharmony_ci return "pad"; 248c2ecf20Sopenharmony_ci case MEDIA_GRAPH_LINK: 258c2ecf20Sopenharmony_ci return "link"; 268c2ecf20Sopenharmony_ci case MEDIA_GRAPH_INTF_DEVNODE: 278c2ecf20Sopenharmony_ci return "intf-devnode"; 288c2ecf20Sopenharmony_ci default: 298c2ecf20Sopenharmony_ci return "unknown"; 308c2ecf20Sopenharmony_ci } 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic inline const char *intf_type(struct media_interface *intf) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci switch (intf->type) { 368c2ecf20Sopenharmony_ci case MEDIA_INTF_T_DVB_FE: 378c2ecf20Sopenharmony_ci return "dvb-frontend"; 388c2ecf20Sopenharmony_ci case MEDIA_INTF_T_DVB_DEMUX: 398c2ecf20Sopenharmony_ci return "dvb-demux"; 408c2ecf20Sopenharmony_ci case MEDIA_INTF_T_DVB_DVR: 418c2ecf20Sopenharmony_ci return "dvb-dvr"; 428c2ecf20Sopenharmony_ci case MEDIA_INTF_T_DVB_CA: 438c2ecf20Sopenharmony_ci return "dvb-ca"; 448c2ecf20Sopenharmony_ci case MEDIA_INTF_T_DVB_NET: 458c2ecf20Sopenharmony_ci return "dvb-net"; 468c2ecf20Sopenharmony_ci case MEDIA_INTF_T_V4L_VIDEO: 478c2ecf20Sopenharmony_ci return "v4l-video"; 488c2ecf20Sopenharmony_ci case MEDIA_INTF_T_V4L_VBI: 498c2ecf20Sopenharmony_ci return "v4l-vbi"; 508c2ecf20Sopenharmony_ci case MEDIA_INTF_T_V4L_RADIO: 518c2ecf20Sopenharmony_ci return "v4l-radio"; 528c2ecf20Sopenharmony_ci case MEDIA_INTF_T_V4L_SUBDEV: 538c2ecf20Sopenharmony_ci return "v4l-subdev"; 548c2ecf20Sopenharmony_ci case MEDIA_INTF_T_V4L_SWRADIO: 558c2ecf20Sopenharmony_ci return "v4l-swradio"; 568c2ecf20Sopenharmony_ci case MEDIA_INTF_T_V4L_TOUCH: 578c2ecf20Sopenharmony_ci return "v4l-touch"; 588c2ecf20Sopenharmony_ci default: 598c2ecf20Sopenharmony_ci return "unknown-intf"; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci__must_check int __media_entity_enum_init(struct media_entity_enum *ent_enum, 648c2ecf20Sopenharmony_ci int idx_max) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci idx_max = ALIGN(idx_max, BITS_PER_LONG); 678c2ecf20Sopenharmony_ci ent_enum->bmap = kcalloc(idx_max / BITS_PER_LONG, sizeof(long), 688c2ecf20Sopenharmony_ci GFP_KERNEL); 698c2ecf20Sopenharmony_ci if (!ent_enum->bmap) 708c2ecf20Sopenharmony_ci return -ENOMEM; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci bitmap_zero(ent_enum->bmap, idx_max); 738c2ecf20Sopenharmony_ci ent_enum->idx_max = idx_max; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_entity_enum_init); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_civoid media_entity_enum_cleanup(struct media_entity_enum *ent_enum) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci kfree(ent_enum->bmap); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_enum_cleanup); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/** 868c2ecf20Sopenharmony_ci * dev_dbg_obj - Prints in debug mode a change on some object 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * @event_name: Name of the event to report. Could be __func__ 898c2ecf20Sopenharmony_ci * @gobj: Pointer to the object 908c2ecf20Sopenharmony_ci * 918c2ecf20Sopenharmony_ci * Enabled only if DEBUG or CONFIG_DYNAMIC_DEBUG. Otherwise, it 928c2ecf20Sopenharmony_ci * won't produce any code. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_cistatic void dev_dbg_obj(const char *event_name, struct media_gobj *gobj) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci#if defined(DEBUG) || defined (CONFIG_DYNAMIC_DEBUG) 978c2ecf20Sopenharmony_ci switch (media_type(gobj)) { 988c2ecf20Sopenharmony_ci case MEDIA_GRAPH_ENTITY: 998c2ecf20Sopenharmony_ci dev_dbg(gobj->mdev->dev, 1008c2ecf20Sopenharmony_ci "%s id %u: entity '%s'\n", 1018c2ecf20Sopenharmony_ci event_name, media_id(gobj), 1028c2ecf20Sopenharmony_ci gobj_to_entity(gobj)->name); 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci case MEDIA_GRAPH_LINK: 1058c2ecf20Sopenharmony_ci { 1068c2ecf20Sopenharmony_ci struct media_link *link = gobj_to_link(gobj); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci dev_dbg(gobj->mdev->dev, 1098c2ecf20Sopenharmony_ci "%s id %u: %s link id %u ==> id %u\n", 1108c2ecf20Sopenharmony_ci event_name, media_id(gobj), 1118c2ecf20Sopenharmony_ci media_type(link->gobj0) == MEDIA_GRAPH_PAD ? 1128c2ecf20Sopenharmony_ci "data" : "interface", 1138c2ecf20Sopenharmony_ci media_id(link->gobj0), 1148c2ecf20Sopenharmony_ci media_id(link->gobj1)); 1158c2ecf20Sopenharmony_ci break; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci case MEDIA_GRAPH_PAD: 1188c2ecf20Sopenharmony_ci { 1198c2ecf20Sopenharmony_ci struct media_pad *pad = gobj_to_pad(gobj); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci dev_dbg(gobj->mdev->dev, 1228c2ecf20Sopenharmony_ci "%s id %u: %s%spad '%s':%d\n", 1238c2ecf20Sopenharmony_ci event_name, media_id(gobj), 1248c2ecf20Sopenharmony_ci pad->flags & MEDIA_PAD_FL_SINK ? "sink " : "", 1258c2ecf20Sopenharmony_ci pad->flags & MEDIA_PAD_FL_SOURCE ? "source " : "", 1268c2ecf20Sopenharmony_ci pad->entity->name, pad->index); 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci case MEDIA_GRAPH_INTF_DEVNODE: 1308c2ecf20Sopenharmony_ci { 1318c2ecf20Sopenharmony_ci struct media_interface *intf = gobj_to_intf(gobj); 1328c2ecf20Sopenharmony_ci struct media_intf_devnode *devnode = intf_to_devnode(intf); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci dev_dbg(gobj->mdev->dev, 1358c2ecf20Sopenharmony_ci "%s id %u: intf_devnode %s - major: %d, minor: %d\n", 1368c2ecf20Sopenharmony_ci event_name, media_id(gobj), 1378c2ecf20Sopenharmony_ci intf_type(intf), 1388c2ecf20Sopenharmony_ci devnode->major, devnode->minor); 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci#endif 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_civoid media_gobj_create(struct media_device *mdev, 1468c2ecf20Sopenharmony_ci enum media_gobj_type type, 1478c2ecf20Sopenharmony_ci struct media_gobj *gobj) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci BUG_ON(!mdev); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci gobj->mdev = mdev; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Create a per-type unique object ID */ 1548c2ecf20Sopenharmony_ci gobj->id = media_gobj_gen_id(type, ++mdev->id); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci switch (type) { 1578c2ecf20Sopenharmony_ci case MEDIA_GRAPH_ENTITY: 1588c2ecf20Sopenharmony_ci list_add_tail(&gobj->list, &mdev->entities); 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci case MEDIA_GRAPH_PAD: 1618c2ecf20Sopenharmony_ci list_add_tail(&gobj->list, &mdev->pads); 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci case MEDIA_GRAPH_LINK: 1648c2ecf20Sopenharmony_ci list_add_tail(&gobj->list, &mdev->links); 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci case MEDIA_GRAPH_INTF_DEVNODE: 1678c2ecf20Sopenharmony_ci list_add_tail(&gobj->list, &mdev->interfaces); 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci mdev->topology_version++; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci dev_dbg_obj(__func__, gobj); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_civoid media_gobj_destroy(struct media_gobj *gobj) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci /* Do nothing if the object is not linked. */ 1798c2ecf20Sopenharmony_ci if (gobj->mdev == NULL) 1808c2ecf20Sopenharmony_ci return; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci dev_dbg_obj(__func__, gobj); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci gobj->mdev->topology_version++; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Remove the object from mdev list */ 1878c2ecf20Sopenharmony_ci list_del(&gobj->list); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci gobj->mdev = NULL; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/* 1938c2ecf20Sopenharmony_ci * TODO: Get rid of this. 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci#define MEDIA_ENTITY_MAX_PADS 512 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ciint media_entity_pads_init(struct media_entity *entity, u16 num_pads, 1988c2ecf20Sopenharmony_ci struct media_pad *pads) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct media_device *mdev = entity->graph_obj.mdev; 2018c2ecf20Sopenharmony_ci unsigned int i; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (num_pads >= MEDIA_ENTITY_MAX_PADS) 2048c2ecf20Sopenharmony_ci return -E2BIG; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci entity->num_pads = num_pads; 2078c2ecf20Sopenharmony_ci entity->pads = pads; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (mdev) 2108c2ecf20Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci for (i = 0; i < num_pads; i++) { 2138c2ecf20Sopenharmony_ci pads[i].entity = entity; 2148c2ecf20Sopenharmony_ci pads[i].index = i; 2158c2ecf20Sopenharmony_ci if (mdev) 2168c2ecf20Sopenharmony_ci media_gobj_create(mdev, MEDIA_GRAPH_PAD, 2178c2ecf20Sopenharmony_ci &entity->pads[i].graph_obj); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (mdev) 2218c2ecf20Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_pads_init); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 2288c2ecf20Sopenharmony_ci * Graph traversal 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic struct media_entity * 2328c2ecf20Sopenharmony_cimedia_entity_other(struct media_entity *entity, struct media_link *link) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci if (link->source->entity == entity) 2358c2ecf20Sopenharmony_ci return link->sink->entity; 2368c2ecf20Sopenharmony_ci else 2378c2ecf20Sopenharmony_ci return link->source->entity; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci/* push an entity to traversal stack */ 2418c2ecf20Sopenharmony_cistatic void stack_push(struct media_graph *graph, 2428c2ecf20Sopenharmony_ci struct media_entity *entity) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci if (graph->top == MEDIA_ENTITY_ENUM_MAX_DEPTH - 1) { 2458c2ecf20Sopenharmony_ci WARN_ON(1); 2468c2ecf20Sopenharmony_ci return; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci graph->top++; 2498c2ecf20Sopenharmony_ci graph->stack[graph->top].link = entity->links.next; 2508c2ecf20Sopenharmony_ci graph->stack[graph->top].entity = entity; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic struct media_entity *stack_pop(struct media_graph *graph) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct media_entity *entity; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci entity = graph->stack[graph->top].entity; 2588c2ecf20Sopenharmony_ci graph->top--; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return entity; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci#define link_top(en) ((en)->stack[(en)->top].link) 2648c2ecf20Sopenharmony_ci#define stack_top(en) ((en)->stack[(en)->top].entity) 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/** 2678c2ecf20Sopenharmony_ci * media_graph_walk_init - Allocate resources for graph walk 2688c2ecf20Sopenharmony_ci * @graph: Media graph structure that will be used to walk the graph 2698c2ecf20Sopenharmony_ci * @mdev: Media device 2708c2ecf20Sopenharmony_ci * 2718c2ecf20Sopenharmony_ci * Reserve resources for graph walk in media device's current 2728c2ecf20Sopenharmony_ci * state. The memory must be released using 2738c2ecf20Sopenharmony_ci * media_graph_walk_free(). 2748c2ecf20Sopenharmony_ci * 2758c2ecf20Sopenharmony_ci * Returns error on failure, zero on success. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci__must_check int media_graph_walk_init( 2788c2ecf20Sopenharmony_ci struct media_graph *graph, struct media_device *mdev) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci return media_entity_enum_init(&graph->ent_enum, mdev); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_graph_walk_init); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/** 2858c2ecf20Sopenharmony_ci * media_graph_walk_cleanup - Release resources related to graph walking 2868c2ecf20Sopenharmony_ci * @graph: Media graph structure that was used to walk the graph 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_civoid media_graph_walk_cleanup(struct media_graph *graph) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci media_entity_enum_cleanup(&graph->ent_enum); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_graph_walk_cleanup); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_civoid media_graph_walk_start(struct media_graph *graph, 2958c2ecf20Sopenharmony_ci struct media_entity *entity) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci media_entity_enum_zero(&graph->ent_enum); 2988c2ecf20Sopenharmony_ci media_entity_enum_set(&graph->ent_enum, entity); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci graph->top = 0; 3018c2ecf20Sopenharmony_ci graph->stack[graph->top].entity = NULL; 3028c2ecf20Sopenharmony_ci stack_push(graph, entity); 3038c2ecf20Sopenharmony_ci dev_dbg(entity->graph_obj.mdev->dev, 3048c2ecf20Sopenharmony_ci "begin graph walk at '%s'\n", entity->name); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_graph_walk_start); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic void media_graph_walk_iter(struct media_graph *graph) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci struct media_entity *entity = stack_top(graph); 3118c2ecf20Sopenharmony_ci struct media_link *link; 3128c2ecf20Sopenharmony_ci struct media_entity *next; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci link = list_entry(link_top(graph), typeof(*link), list); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* The link is not enabled so we do not follow. */ 3178c2ecf20Sopenharmony_ci if (!(link->flags & MEDIA_LNK_FL_ENABLED)) { 3188c2ecf20Sopenharmony_ci link_top(graph) = link_top(graph)->next; 3198c2ecf20Sopenharmony_ci dev_dbg(entity->graph_obj.mdev->dev, 3208c2ecf20Sopenharmony_ci "walk: skipping disabled link '%s':%u -> '%s':%u\n", 3218c2ecf20Sopenharmony_ci link->source->entity->name, link->source->index, 3228c2ecf20Sopenharmony_ci link->sink->entity->name, link->sink->index); 3238c2ecf20Sopenharmony_ci return; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* Get the entity in the other end of the link . */ 3278c2ecf20Sopenharmony_ci next = media_entity_other(entity, link); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* Has the entity already been visited? */ 3308c2ecf20Sopenharmony_ci if (media_entity_enum_test_and_set(&graph->ent_enum, next)) { 3318c2ecf20Sopenharmony_ci link_top(graph) = link_top(graph)->next; 3328c2ecf20Sopenharmony_ci dev_dbg(entity->graph_obj.mdev->dev, 3338c2ecf20Sopenharmony_ci "walk: skipping entity '%s' (already seen)\n", 3348c2ecf20Sopenharmony_ci next->name); 3358c2ecf20Sopenharmony_ci return; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* Push the new entity to stack and start over. */ 3398c2ecf20Sopenharmony_ci link_top(graph) = link_top(graph)->next; 3408c2ecf20Sopenharmony_ci stack_push(graph, next); 3418c2ecf20Sopenharmony_ci dev_dbg(entity->graph_obj.mdev->dev, "walk: pushing '%s' on stack\n", 3428c2ecf20Sopenharmony_ci next->name); 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistruct media_entity *media_graph_walk_next(struct media_graph *graph) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct media_entity *entity; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (stack_top(graph) == NULL) 3508c2ecf20Sopenharmony_ci return NULL; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* 3538c2ecf20Sopenharmony_ci * Depth first search. Push entity to stack and continue from 3548c2ecf20Sopenharmony_ci * top of the stack until no more entities on the level can be 3558c2ecf20Sopenharmony_ci * found. 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_ci while (link_top(graph) != &stack_top(graph)->links) 3588c2ecf20Sopenharmony_ci media_graph_walk_iter(graph); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci entity = stack_pop(graph); 3618c2ecf20Sopenharmony_ci dev_dbg(entity->graph_obj.mdev->dev, 3628c2ecf20Sopenharmony_ci "walk: returning entity '%s'\n", entity->name); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return entity; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_graph_walk_next); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ciint media_entity_get_fwnode_pad(struct media_entity *entity, 3698c2ecf20Sopenharmony_ci struct fwnode_handle *fwnode, 3708c2ecf20Sopenharmony_ci unsigned long direction_flags) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct fwnode_endpoint endpoint; 3738c2ecf20Sopenharmony_ci unsigned int i; 3748c2ecf20Sopenharmony_ci int ret; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (!entity->ops || !entity->ops->get_fwnode_pad) { 3778c2ecf20Sopenharmony_ci for (i = 0; i < entity->num_pads; i++) { 3788c2ecf20Sopenharmony_ci if (entity->pads[i].flags & direction_flags) 3798c2ecf20Sopenharmony_ci return i; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return -ENXIO; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci ret = fwnode_graph_parse_endpoint(fwnode, &endpoint); 3868c2ecf20Sopenharmony_ci if (ret) 3878c2ecf20Sopenharmony_ci return ret; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci ret = entity->ops->get_fwnode_pad(entity, &endpoint); 3908c2ecf20Sopenharmony_ci if (ret < 0) 3918c2ecf20Sopenharmony_ci return ret; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (ret >= entity->num_pads) 3948c2ecf20Sopenharmony_ci return -ENXIO; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (!(entity->pads[ret].flags & direction_flags)) 3978c2ecf20Sopenharmony_ci return -ENXIO; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return ret; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_get_fwnode_pad); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 4048c2ecf20Sopenharmony_ci * Pipeline management 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci__must_check int __media_pipeline_start(struct media_entity *entity, 4088c2ecf20Sopenharmony_ci struct media_pipeline *pipe) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct media_device *mdev = entity->graph_obj.mdev; 4118c2ecf20Sopenharmony_ci struct media_graph *graph = &pipe->graph; 4128c2ecf20Sopenharmony_ci struct media_entity *entity_err = entity; 4138c2ecf20Sopenharmony_ci struct media_link *link; 4148c2ecf20Sopenharmony_ci int ret; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (!pipe->streaming_count++) { 4178c2ecf20Sopenharmony_ci ret = media_graph_walk_init(&pipe->graph, mdev); 4188c2ecf20Sopenharmony_ci if (ret) 4198c2ecf20Sopenharmony_ci goto error_graph_walk_start; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci media_graph_walk_start(&pipe->graph, entity); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci while ((entity = media_graph_walk_next(graph))) { 4258c2ecf20Sopenharmony_ci DECLARE_BITMAP(active, MEDIA_ENTITY_MAX_PADS); 4268c2ecf20Sopenharmony_ci DECLARE_BITMAP(has_no_links, MEDIA_ENTITY_MAX_PADS); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci entity->stream_count++; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (entity->pipe && entity->pipe != pipe) { 4318c2ecf20Sopenharmony_ci pr_err("Pipe active for %s. Can't start for %s\n", 4328c2ecf20Sopenharmony_ci entity->name, 4338c2ecf20Sopenharmony_ci entity_err->name); 4348c2ecf20Sopenharmony_ci ret = -EBUSY; 4358c2ecf20Sopenharmony_ci goto error; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci entity->pipe = pipe; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* Already streaming --- no need to check. */ 4418c2ecf20Sopenharmony_ci if (entity->stream_count > 1) 4428c2ecf20Sopenharmony_ci continue; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (!entity->ops || !entity->ops->link_validate) 4458c2ecf20Sopenharmony_ci continue; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci bitmap_zero(active, entity->num_pads); 4488c2ecf20Sopenharmony_ci bitmap_fill(has_no_links, entity->num_pads); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci list_for_each_entry(link, &entity->links, list) { 4518c2ecf20Sopenharmony_ci struct media_pad *pad = link->sink->entity == entity 4528c2ecf20Sopenharmony_ci ? link->sink : link->source; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* Mark that a pad is connected by a link. */ 4558c2ecf20Sopenharmony_ci bitmap_clear(has_no_links, pad->index, 1); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * Pads that either do not need to connect or 4598c2ecf20Sopenharmony_ci * are connected through an enabled link are 4608c2ecf20Sopenharmony_ci * fine. 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_ci if (!(pad->flags & MEDIA_PAD_FL_MUST_CONNECT) || 4638c2ecf20Sopenharmony_ci link->flags & MEDIA_LNK_FL_ENABLED) 4648c2ecf20Sopenharmony_ci bitmap_set(active, pad->index, 1); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* 4678c2ecf20Sopenharmony_ci * Link validation will only take place for 4688c2ecf20Sopenharmony_ci * sink ends of the link that are enabled. 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_ci if (link->sink != pad || 4718c2ecf20Sopenharmony_ci !(link->flags & MEDIA_LNK_FL_ENABLED)) 4728c2ecf20Sopenharmony_ci continue; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci ret = entity->ops->link_validate(link); 4758c2ecf20Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) { 4768c2ecf20Sopenharmony_ci dev_dbg(entity->graph_obj.mdev->dev, 4778c2ecf20Sopenharmony_ci "link validation failed for '%s':%u -> '%s':%u, error %d\n", 4788c2ecf20Sopenharmony_ci link->source->entity->name, 4798c2ecf20Sopenharmony_ci link->source->index, 4808c2ecf20Sopenharmony_ci entity->name, link->sink->index, ret); 4818c2ecf20Sopenharmony_ci goto error; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* Either no links or validated links are fine. */ 4868c2ecf20Sopenharmony_ci bitmap_or(active, active, has_no_links, entity->num_pads); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (!bitmap_full(active, entity->num_pads)) { 4898c2ecf20Sopenharmony_ci ret = -ENOLINK; 4908c2ecf20Sopenharmony_ci dev_dbg(entity->graph_obj.mdev->dev, 4918c2ecf20Sopenharmony_ci "'%s':%u must be connected by an enabled link\n", 4928c2ecf20Sopenharmony_ci entity->name, 4938c2ecf20Sopenharmony_ci (unsigned)find_first_zero_bit( 4948c2ecf20Sopenharmony_ci active, entity->num_pads)); 4958c2ecf20Sopenharmony_ci goto error; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return 0; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cierror: 5028c2ecf20Sopenharmony_ci /* 5038c2ecf20Sopenharmony_ci * Link validation on graph failed. We revert what we did and 5048c2ecf20Sopenharmony_ci * return the error. 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_ci media_graph_walk_start(graph, entity_err); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci while ((entity_err = media_graph_walk_next(graph))) { 5098c2ecf20Sopenharmony_ci /* Sanity check for negative stream_count */ 5108c2ecf20Sopenharmony_ci if (!WARN_ON_ONCE(entity_err->stream_count <= 0)) { 5118c2ecf20Sopenharmony_ci entity_err->stream_count--; 5128c2ecf20Sopenharmony_ci if (entity_err->stream_count == 0) 5138c2ecf20Sopenharmony_ci entity_err->pipe = NULL; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci /* 5178c2ecf20Sopenharmony_ci * We haven't increased stream_count further than this 5188c2ecf20Sopenharmony_ci * so we quit here. 5198c2ecf20Sopenharmony_ci */ 5208c2ecf20Sopenharmony_ci if (entity_err == entity) 5218c2ecf20Sopenharmony_ci break; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cierror_graph_walk_start: 5258c2ecf20Sopenharmony_ci if (!--pipe->streaming_count) 5268c2ecf20Sopenharmony_ci media_graph_walk_cleanup(graph); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return ret; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_pipeline_start); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci__must_check int media_pipeline_start(struct media_entity *entity, 5338c2ecf20Sopenharmony_ci struct media_pipeline *pipe) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci struct media_device *mdev = entity->graph_obj.mdev; 5368c2ecf20Sopenharmony_ci int ret; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 5398c2ecf20Sopenharmony_ci ret = __media_pipeline_start(entity, pipe); 5408c2ecf20Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 5418c2ecf20Sopenharmony_ci return ret; 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pipeline_start); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_civoid __media_pipeline_stop(struct media_entity *entity) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct media_graph *graph = &entity->pipe->graph; 5488c2ecf20Sopenharmony_ci struct media_pipeline *pipe = entity->pipe; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* 5518c2ecf20Sopenharmony_ci * If the following check fails, the driver has performed an 5528c2ecf20Sopenharmony_ci * unbalanced call to media_pipeline_stop() 5538c2ecf20Sopenharmony_ci */ 5548c2ecf20Sopenharmony_ci if (WARN_ON(!pipe)) 5558c2ecf20Sopenharmony_ci return; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci media_graph_walk_start(graph, entity); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci while ((entity = media_graph_walk_next(graph))) { 5608c2ecf20Sopenharmony_ci /* Sanity check for negative stream_count */ 5618c2ecf20Sopenharmony_ci if (!WARN_ON_ONCE(entity->stream_count <= 0)) { 5628c2ecf20Sopenharmony_ci entity->stream_count--; 5638c2ecf20Sopenharmony_ci if (entity->stream_count == 0) 5648c2ecf20Sopenharmony_ci entity->pipe = NULL; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (!--pipe->streaming_count) 5698c2ecf20Sopenharmony_ci media_graph_walk_cleanup(graph); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_pipeline_stop); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_civoid media_pipeline_stop(struct media_entity *entity) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct media_device *mdev = entity->graph_obj.mdev; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 5798c2ecf20Sopenharmony_ci __media_pipeline_stop(entity); 5808c2ecf20Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_pipeline_stop); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 5858c2ecf20Sopenharmony_ci * Links management 5868c2ecf20Sopenharmony_ci */ 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic struct media_link *media_add_link(struct list_head *head) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct media_link *link; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci link = kzalloc(sizeof(*link), GFP_KERNEL); 5938c2ecf20Sopenharmony_ci if (link == NULL) 5948c2ecf20Sopenharmony_ci return NULL; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci list_add_tail(&link->list, head); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci return link; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic void __media_entity_remove_link(struct media_entity *entity, 6028c2ecf20Sopenharmony_ci struct media_link *link) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci struct media_link *rlink, *tmp; 6058c2ecf20Sopenharmony_ci struct media_entity *remote; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (link->source->entity == entity) 6088c2ecf20Sopenharmony_ci remote = link->sink->entity; 6098c2ecf20Sopenharmony_ci else 6108c2ecf20Sopenharmony_ci remote = link->source->entity; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci list_for_each_entry_safe(rlink, tmp, &remote->links, list) { 6138c2ecf20Sopenharmony_ci if (rlink != link->reverse) 6148c2ecf20Sopenharmony_ci continue; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (link->source->entity == entity) 6178c2ecf20Sopenharmony_ci remote->num_backlinks--; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* Remove the remote link */ 6208c2ecf20Sopenharmony_ci list_del(&rlink->list); 6218c2ecf20Sopenharmony_ci media_gobj_destroy(&rlink->graph_obj); 6228c2ecf20Sopenharmony_ci kfree(rlink); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (--remote->num_links == 0) 6258c2ecf20Sopenharmony_ci break; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci list_del(&link->list); 6288c2ecf20Sopenharmony_ci media_gobj_destroy(&link->graph_obj); 6298c2ecf20Sopenharmony_ci kfree(link); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ciint media_get_pad_index(struct media_entity *entity, bool is_sink, 6338c2ecf20Sopenharmony_ci enum media_pad_signal_type sig_type) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci int i; 6368c2ecf20Sopenharmony_ci bool pad_is_sink; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (!entity) 6398c2ecf20Sopenharmony_ci return -EINVAL; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci for (i = 0; i < entity->num_pads; i++) { 6428c2ecf20Sopenharmony_ci if (entity->pads[i].flags & MEDIA_PAD_FL_SINK) 6438c2ecf20Sopenharmony_ci pad_is_sink = true; 6448c2ecf20Sopenharmony_ci else if (entity->pads[i].flags & MEDIA_PAD_FL_SOURCE) 6458c2ecf20Sopenharmony_ci pad_is_sink = false; 6468c2ecf20Sopenharmony_ci else 6478c2ecf20Sopenharmony_ci continue; /* This is an error! */ 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci if (pad_is_sink != is_sink) 6508c2ecf20Sopenharmony_ci continue; 6518c2ecf20Sopenharmony_ci if (entity->pads[i].sig_type == sig_type) 6528c2ecf20Sopenharmony_ci return i; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci return -EINVAL; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_get_pad_index); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ciint 6598c2ecf20Sopenharmony_cimedia_create_pad_link(struct media_entity *source, u16 source_pad, 6608c2ecf20Sopenharmony_ci struct media_entity *sink, u16 sink_pad, u32 flags) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci struct media_link *link; 6638c2ecf20Sopenharmony_ci struct media_link *backlink; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (WARN_ON(!source || !sink) || 6668c2ecf20Sopenharmony_ci WARN_ON(source_pad >= source->num_pads) || 6678c2ecf20Sopenharmony_ci WARN_ON(sink_pad >= sink->num_pads)) 6688c2ecf20Sopenharmony_ci return -EINVAL; 6698c2ecf20Sopenharmony_ci if (WARN_ON(!(source->pads[source_pad].flags & MEDIA_PAD_FL_SOURCE))) 6708c2ecf20Sopenharmony_ci return -EINVAL; 6718c2ecf20Sopenharmony_ci if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK))) 6728c2ecf20Sopenharmony_ci return -EINVAL; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci link = media_add_link(&source->links); 6758c2ecf20Sopenharmony_ci if (link == NULL) 6768c2ecf20Sopenharmony_ci return -ENOMEM; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci link->source = &source->pads[source_pad]; 6798c2ecf20Sopenharmony_ci link->sink = &sink->pads[sink_pad]; 6808c2ecf20Sopenharmony_ci link->flags = flags & ~MEDIA_LNK_FL_INTERFACE_LINK; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* Initialize graph object embedded at the new link */ 6838c2ecf20Sopenharmony_ci media_gobj_create(source->graph_obj.mdev, MEDIA_GRAPH_LINK, 6848c2ecf20Sopenharmony_ci &link->graph_obj); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci /* Create the backlink. Backlinks are used to help graph traversal and 6878c2ecf20Sopenharmony_ci * are not reported to userspace. 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_ci backlink = media_add_link(&sink->links); 6908c2ecf20Sopenharmony_ci if (backlink == NULL) { 6918c2ecf20Sopenharmony_ci __media_entity_remove_link(source, link); 6928c2ecf20Sopenharmony_ci return -ENOMEM; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci backlink->source = &source->pads[source_pad]; 6968c2ecf20Sopenharmony_ci backlink->sink = &sink->pads[sink_pad]; 6978c2ecf20Sopenharmony_ci backlink->flags = flags; 6988c2ecf20Sopenharmony_ci backlink->is_backlink = true; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* Initialize graph object embedded at the new link */ 7018c2ecf20Sopenharmony_ci media_gobj_create(sink->graph_obj.mdev, MEDIA_GRAPH_LINK, 7028c2ecf20Sopenharmony_ci &backlink->graph_obj); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci link->reverse = backlink; 7058c2ecf20Sopenharmony_ci backlink->reverse = link; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci sink->num_backlinks++; 7088c2ecf20Sopenharmony_ci sink->num_links++; 7098c2ecf20Sopenharmony_ci source->num_links++; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci return 0; 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_create_pad_link); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ciint media_create_pad_links(const struct media_device *mdev, 7168c2ecf20Sopenharmony_ci const u32 source_function, 7178c2ecf20Sopenharmony_ci struct media_entity *source, 7188c2ecf20Sopenharmony_ci const u16 source_pad, 7198c2ecf20Sopenharmony_ci const u32 sink_function, 7208c2ecf20Sopenharmony_ci struct media_entity *sink, 7218c2ecf20Sopenharmony_ci const u16 sink_pad, 7228c2ecf20Sopenharmony_ci u32 flags, 7238c2ecf20Sopenharmony_ci const bool allow_both_undefined) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci struct media_entity *entity; 7268c2ecf20Sopenharmony_ci unsigned function; 7278c2ecf20Sopenharmony_ci int ret; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci /* Trivial case: 1:1 relation */ 7308c2ecf20Sopenharmony_ci if (source && sink) 7318c2ecf20Sopenharmony_ci return media_create_pad_link(source, source_pad, 7328c2ecf20Sopenharmony_ci sink, sink_pad, flags); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci /* Worse case scenario: n:n relation */ 7358c2ecf20Sopenharmony_ci if (!source && !sink) { 7368c2ecf20Sopenharmony_ci if (!allow_both_undefined) 7378c2ecf20Sopenharmony_ci return 0; 7388c2ecf20Sopenharmony_ci media_device_for_each_entity(source, mdev) { 7398c2ecf20Sopenharmony_ci if (source->function != source_function) 7408c2ecf20Sopenharmony_ci continue; 7418c2ecf20Sopenharmony_ci media_device_for_each_entity(sink, mdev) { 7428c2ecf20Sopenharmony_ci if (sink->function != sink_function) 7438c2ecf20Sopenharmony_ci continue; 7448c2ecf20Sopenharmony_ci ret = media_create_pad_link(source, source_pad, 7458c2ecf20Sopenharmony_ci sink, sink_pad, 7468c2ecf20Sopenharmony_ci flags); 7478c2ecf20Sopenharmony_ci if (ret) 7488c2ecf20Sopenharmony_ci return ret; 7498c2ecf20Sopenharmony_ci flags &= ~(MEDIA_LNK_FL_ENABLED | 7508c2ecf20Sopenharmony_ci MEDIA_LNK_FL_IMMUTABLE); 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci return 0; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* Handle 1:n and n:1 cases */ 7578c2ecf20Sopenharmony_ci if (source) 7588c2ecf20Sopenharmony_ci function = sink_function; 7598c2ecf20Sopenharmony_ci else 7608c2ecf20Sopenharmony_ci function = source_function; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci media_device_for_each_entity(entity, mdev) { 7638c2ecf20Sopenharmony_ci if (entity->function != function) 7648c2ecf20Sopenharmony_ci continue; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (source) 7678c2ecf20Sopenharmony_ci ret = media_create_pad_link(source, source_pad, 7688c2ecf20Sopenharmony_ci entity, sink_pad, flags); 7698c2ecf20Sopenharmony_ci else 7708c2ecf20Sopenharmony_ci ret = media_create_pad_link(entity, source_pad, 7718c2ecf20Sopenharmony_ci sink, sink_pad, flags); 7728c2ecf20Sopenharmony_ci if (ret) 7738c2ecf20Sopenharmony_ci return ret; 7748c2ecf20Sopenharmony_ci flags &= ~(MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci return 0; 7778c2ecf20Sopenharmony_ci} 7788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_create_pad_links); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_civoid __media_entity_remove_links(struct media_entity *entity) 7818c2ecf20Sopenharmony_ci{ 7828c2ecf20Sopenharmony_ci struct media_link *link, *tmp; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci list_for_each_entry_safe(link, tmp, &entity->links, list) 7858c2ecf20Sopenharmony_ci __media_entity_remove_link(entity, link); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci entity->num_links = 0; 7888c2ecf20Sopenharmony_ci entity->num_backlinks = 0; 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_entity_remove_links); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_civoid media_entity_remove_links(struct media_entity *entity) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci struct media_device *mdev = entity->graph_obj.mdev; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* Do nothing if the entity is not registered. */ 7978c2ecf20Sopenharmony_ci if (mdev == NULL) 7988c2ecf20Sopenharmony_ci return; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 8018c2ecf20Sopenharmony_ci __media_entity_remove_links(entity); 8028c2ecf20Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_remove_links); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cistatic int __media_entity_setup_link_notify(struct media_link *link, u32 flags) 8078c2ecf20Sopenharmony_ci{ 8088c2ecf20Sopenharmony_ci int ret; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci /* Notify both entities. */ 8118c2ecf20Sopenharmony_ci ret = media_entity_call(link->source->entity, link_setup, 8128c2ecf20Sopenharmony_ci link->source, link->sink, flags); 8138c2ecf20Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) 8148c2ecf20Sopenharmony_ci return ret; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci ret = media_entity_call(link->sink->entity, link_setup, 8178c2ecf20Sopenharmony_ci link->sink, link->source, flags); 8188c2ecf20Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) { 8198c2ecf20Sopenharmony_ci media_entity_call(link->source->entity, link_setup, 8208c2ecf20Sopenharmony_ci link->source, link->sink, link->flags); 8218c2ecf20Sopenharmony_ci return ret; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci link->flags = flags; 8258c2ecf20Sopenharmony_ci link->reverse->flags = link->flags; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci return 0; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ciint __media_entity_setup_link(struct media_link *link, u32 flags) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci const u32 mask = MEDIA_LNK_FL_ENABLED; 8338c2ecf20Sopenharmony_ci struct media_device *mdev; 8348c2ecf20Sopenharmony_ci struct media_entity *source, *sink; 8358c2ecf20Sopenharmony_ci int ret = -EBUSY; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (link == NULL) 8388c2ecf20Sopenharmony_ci return -EINVAL; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci /* The non-modifiable link flags must not be modified. */ 8418c2ecf20Sopenharmony_ci if ((link->flags & ~mask) != (flags & ~mask)) 8428c2ecf20Sopenharmony_ci return -EINVAL; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (link->flags & MEDIA_LNK_FL_IMMUTABLE) 8458c2ecf20Sopenharmony_ci return link->flags == flags ? 0 : -EINVAL; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci if (link->flags == flags) 8488c2ecf20Sopenharmony_ci return 0; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci source = link->source->entity; 8518c2ecf20Sopenharmony_ci sink = link->sink->entity; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (!(link->flags & MEDIA_LNK_FL_DYNAMIC) && 8548c2ecf20Sopenharmony_ci (source->stream_count || sink->stream_count)) 8558c2ecf20Sopenharmony_ci return -EBUSY; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci mdev = source->graph_obj.mdev; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (mdev->ops && mdev->ops->link_notify) { 8608c2ecf20Sopenharmony_ci ret = mdev->ops->link_notify(link, flags, 8618c2ecf20Sopenharmony_ci MEDIA_DEV_NOTIFY_PRE_LINK_CH); 8628c2ecf20Sopenharmony_ci if (ret < 0) 8638c2ecf20Sopenharmony_ci return ret; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci ret = __media_entity_setup_link_notify(link, flags); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if (mdev->ops && mdev->ops->link_notify) 8698c2ecf20Sopenharmony_ci mdev->ops->link_notify(link, flags, 8708c2ecf20Sopenharmony_ci MEDIA_DEV_NOTIFY_POST_LINK_CH); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci return ret; 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_entity_setup_link); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ciint media_entity_setup_link(struct media_link *link, u32 flags) 8778c2ecf20Sopenharmony_ci{ 8788c2ecf20Sopenharmony_ci int ret; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci mutex_lock(&link->graph_obj.mdev->graph_mutex); 8818c2ecf20Sopenharmony_ci ret = __media_entity_setup_link(link, flags); 8828c2ecf20Sopenharmony_ci mutex_unlock(&link->graph_obj.mdev->graph_mutex); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci return ret; 8858c2ecf20Sopenharmony_ci} 8868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_setup_link); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistruct media_link * 8898c2ecf20Sopenharmony_cimedia_entity_find_link(struct media_pad *source, struct media_pad *sink) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci struct media_link *link; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci list_for_each_entry(link, &source->entity->links, list) { 8948c2ecf20Sopenharmony_ci if (link->source->entity == source->entity && 8958c2ecf20Sopenharmony_ci link->source->index == source->index && 8968c2ecf20Sopenharmony_ci link->sink->entity == sink->entity && 8978c2ecf20Sopenharmony_ci link->sink->index == sink->index) 8988c2ecf20Sopenharmony_ci return link; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci return NULL; 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_find_link); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistruct media_pad *media_entity_remote_pad(const struct media_pad *pad) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci struct media_link *link; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci list_for_each_entry(link, &pad->entity->links, list) { 9108c2ecf20Sopenharmony_ci if (!(link->flags & MEDIA_LNK_FL_ENABLED)) 9118c2ecf20Sopenharmony_ci continue; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci if (link->source == pad) 9148c2ecf20Sopenharmony_ci return link->sink; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci if (link->sink == pad) 9178c2ecf20Sopenharmony_ci return link->source; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci return NULL; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci} 9238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_entity_remote_pad); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_cistatic void media_interface_init(struct media_device *mdev, 9268c2ecf20Sopenharmony_ci struct media_interface *intf, 9278c2ecf20Sopenharmony_ci u32 gobj_type, 9288c2ecf20Sopenharmony_ci u32 intf_type, u32 flags) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci intf->type = intf_type; 9318c2ecf20Sopenharmony_ci intf->flags = flags; 9328c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&intf->links); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci media_gobj_create(mdev, gobj_type, &intf->graph_obj); 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci/* Functions related to the media interface via device nodes */ 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistruct media_intf_devnode *media_devnode_create(struct media_device *mdev, 9408c2ecf20Sopenharmony_ci u32 type, u32 flags, 9418c2ecf20Sopenharmony_ci u32 major, u32 minor) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci struct media_intf_devnode *devnode; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci devnode = kzalloc(sizeof(*devnode), GFP_KERNEL); 9468c2ecf20Sopenharmony_ci if (!devnode) 9478c2ecf20Sopenharmony_ci return NULL; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci devnode->major = major; 9508c2ecf20Sopenharmony_ci devnode->minor = minor; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci media_interface_init(mdev, &devnode->intf, MEDIA_GRAPH_INTF_DEVNODE, 9538c2ecf20Sopenharmony_ci type, flags); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci return devnode; 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_devnode_create); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_civoid media_devnode_remove(struct media_intf_devnode *devnode) 9608c2ecf20Sopenharmony_ci{ 9618c2ecf20Sopenharmony_ci media_remove_intf_links(&devnode->intf); 9628c2ecf20Sopenharmony_ci media_gobj_destroy(&devnode->intf.graph_obj); 9638c2ecf20Sopenharmony_ci kfree(devnode); 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_devnode_remove); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cistruct media_link *media_create_intf_link(struct media_entity *entity, 9688c2ecf20Sopenharmony_ci struct media_interface *intf, 9698c2ecf20Sopenharmony_ci u32 flags) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci struct media_link *link; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci link = media_add_link(&intf->links); 9748c2ecf20Sopenharmony_ci if (link == NULL) 9758c2ecf20Sopenharmony_ci return NULL; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci link->intf = intf; 9788c2ecf20Sopenharmony_ci link->entity = entity; 9798c2ecf20Sopenharmony_ci link->flags = flags | MEDIA_LNK_FL_INTERFACE_LINK; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci /* Initialize graph object embedded at the new link */ 9828c2ecf20Sopenharmony_ci media_gobj_create(intf->graph_obj.mdev, MEDIA_GRAPH_LINK, 9838c2ecf20Sopenharmony_ci &link->graph_obj); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci return link; 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_create_intf_link); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_civoid __media_remove_intf_link(struct media_link *link) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci list_del(&link->list); 9928c2ecf20Sopenharmony_ci media_gobj_destroy(&link->graph_obj); 9938c2ecf20Sopenharmony_ci kfree(link); 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_remove_intf_link); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_civoid media_remove_intf_link(struct media_link *link) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci struct media_device *mdev = link->graph_obj.mdev; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci /* Do nothing if the intf is not registered. */ 10028c2ecf20Sopenharmony_ci if (mdev == NULL) 10038c2ecf20Sopenharmony_ci return; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 10068c2ecf20Sopenharmony_ci __media_remove_intf_link(link); 10078c2ecf20Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_remove_intf_link); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_civoid __media_remove_intf_links(struct media_interface *intf) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci struct media_link *link, *tmp; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci list_for_each_entry_safe(link, tmp, &intf->links, list) 10168c2ecf20Sopenharmony_ci __media_remove_intf_link(link); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci} 10198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__media_remove_intf_links); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_civoid media_remove_intf_links(struct media_interface *intf) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci struct media_device *mdev = intf->graph_obj.mdev; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci /* Do nothing if the intf is not registered. */ 10268c2ecf20Sopenharmony_ci if (mdev == NULL) 10278c2ecf20Sopenharmony_ci return; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 10308c2ecf20Sopenharmony_ci __media_remove_intf_links(intf); 10318c2ecf20Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 10328c2ecf20Sopenharmony_ci} 10338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_remove_intf_links); 1034