18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * Media Controller ancillary functions 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2016 Mauro Carvalho Chehab <mchehab@kernel.org> 78c2ecf20Sopenharmony_ci * Copyright (C) 2016 Shuah Khan <shuahkh@osg.samsung.com> 88c2ecf20Sopenharmony_ci * Copyright (C) 2006-2010 Nokia Corporation 98c2ecf20Sopenharmony_ci * Copyright (c) 2016 Intel Corporation. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/pci.h> 148c2ecf20Sopenharmony_ci#include <linux/usb.h> 158c2ecf20Sopenharmony_ci#include <media/media-device.h> 168c2ecf20Sopenharmony_ci#include <media/media-entity.h> 178c2ecf20Sopenharmony_ci#include <media/v4l2-fh.h> 188c2ecf20Sopenharmony_ci#include <media/v4l2-mc.h> 198c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 208c2ecf20Sopenharmony_ci#include <media/videobuf2-core.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciint v4l2_mc_create_media_graph(struct media_device *mdev) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct media_entity *entity; 268c2ecf20Sopenharmony_ci struct media_entity *if_vid = NULL, *if_aud = NULL; 278c2ecf20Sopenharmony_ci struct media_entity *tuner = NULL, *decoder = NULL; 288c2ecf20Sopenharmony_ci struct media_entity *io_v4l = NULL, *io_vbi = NULL, *io_swradio = NULL; 298c2ecf20Sopenharmony_ci bool is_webcam = false; 308c2ecf20Sopenharmony_ci u32 flags; 318c2ecf20Sopenharmony_ci int ret, pad_sink, pad_source; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (!mdev) 348c2ecf20Sopenharmony_ci return 0; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci media_device_for_each_entity(entity, mdev) { 378c2ecf20Sopenharmony_ci switch (entity->function) { 388c2ecf20Sopenharmony_ci case MEDIA_ENT_F_IF_VID_DECODER: 398c2ecf20Sopenharmony_ci if_vid = entity; 408c2ecf20Sopenharmony_ci break; 418c2ecf20Sopenharmony_ci case MEDIA_ENT_F_IF_AUD_DECODER: 428c2ecf20Sopenharmony_ci if_aud = entity; 438c2ecf20Sopenharmony_ci break; 448c2ecf20Sopenharmony_ci case MEDIA_ENT_F_TUNER: 458c2ecf20Sopenharmony_ci tuner = entity; 468c2ecf20Sopenharmony_ci break; 478c2ecf20Sopenharmony_ci case MEDIA_ENT_F_ATV_DECODER: 488c2ecf20Sopenharmony_ci decoder = entity; 498c2ecf20Sopenharmony_ci break; 508c2ecf20Sopenharmony_ci case MEDIA_ENT_F_IO_V4L: 518c2ecf20Sopenharmony_ci io_v4l = entity; 528c2ecf20Sopenharmony_ci break; 538c2ecf20Sopenharmony_ci case MEDIA_ENT_F_IO_VBI: 548c2ecf20Sopenharmony_ci io_vbi = entity; 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci case MEDIA_ENT_F_IO_SWRADIO: 578c2ecf20Sopenharmony_ci io_swradio = entity; 588c2ecf20Sopenharmony_ci break; 598c2ecf20Sopenharmony_ci case MEDIA_ENT_F_CAM_SENSOR: 608c2ecf20Sopenharmony_ci is_webcam = true; 618c2ecf20Sopenharmony_ci break; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* It should have at least one I/O entity */ 668c2ecf20Sopenharmony_ci if (!io_v4l && !io_vbi && !io_swradio) { 678c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "Didn't find any I/O entity\n"); 688c2ecf20Sopenharmony_ci return -EINVAL; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* 728c2ecf20Sopenharmony_ci * Here, webcams are modelled on a very simple way: the sensor is 738c2ecf20Sopenharmony_ci * connected directly to the I/O entity. All dirty details, like 748c2ecf20Sopenharmony_ci * scaler and crop HW are hidden. While such mapping is not enough 758c2ecf20Sopenharmony_ci * for mc-centric hardware, it is enough for v4l2 interface centric 768c2ecf20Sopenharmony_ci * PC-consumer's hardware. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci if (is_webcam) { 798c2ecf20Sopenharmony_ci if (!io_v4l) { 808c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "Didn't find a MEDIA_ENT_F_IO_V4L\n"); 818c2ecf20Sopenharmony_ci return -EINVAL; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci media_device_for_each_entity(entity, mdev) { 858c2ecf20Sopenharmony_ci if (entity->function != MEDIA_ENT_F_CAM_SENSOR) 868c2ecf20Sopenharmony_ci continue; 878c2ecf20Sopenharmony_ci ret = media_create_pad_link(entity, 0, 888c2ecf20Sopenharmony_ci io_v4l, 0, 898c2ecf20Sopenharmony_ci MEDIA_LNK_FL_ENABLED); 908c2ecf20Sopenharmony_ci if (ret) { 918c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "Failed to create a sensor link\n"); 928c2ecf20Sopenharmony_ci return ret; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci if (!decoder) 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* The device isn't a webcam. So, it should have a decoder */ 1008c2ecf20Sopenharmony_ci if (!decoder) { 1018c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "Decoder not found\n"); 1028c2ecf20Sopenharmony_ci return -EINVAL; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* Link the tuner and IF video output pads */ 1068c2ecf20Sopenharmony_ci if (tuner) { 1078c2ecf20Sopenharmony_ci if (if_vid) { 1088c2ecf20Sopenharmony_ci pad_source = media_get_pad_index(tuner, false, 1098c2ecf20Sopenharmony_ci PAD_SIGNAL_ANALOG); 1108c2ecf20Sopenharmony_ci pad_sink = media_get_pad_index(if_vid, true, 1118c2ecf20Sopenharmony_ci PAD_SIGNAL_ANALOG); 1128c2ecf20Sopenharmony_ci if (pad_source < 0 || pad_sink < 0) { 1138c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "Couldn't get tuner and/or PLL pad(s): (%d, %d)\n", 1148c2ecf20Sopenharmony_ci pad_source, pad_sink); 1158c2ecf20Sopenharmony_ci return -EINVAL; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci ret = media_create_pad_link(tuner, pad_source, 1188c2ecf20Sopenharmony_ci if_vid, pad_sink, 1198c2ecf20Sopenharmony_ci MEDIA_LNK_FL_ENABLED); 1208c2ecf20Sopenharmony_ci if (ret) { 1218c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "Couldn't create tuner->PLL link)\n"); 1228c2ecf20Sopenharmony_ci return ret; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci pad_source = media_get_pad_index(if_vid, false, 1268c2ecf20Sopenharmony_ci PAD_SIGNAL_ANALOG); 1278c2ecf20Sopenharmony_ci pad_sink = media_get_pad_index(decoder, true, 1288c2ecf20Sopenharmony_ci PAD_SIGNAL_ANALOG); 1298c2ecf20Sopenharmony_ci if (pad_source < 0 || pad_sink < 0) { 1308c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "get decoder and/or PLL pad(s): (%d, %d)\n", 1318c2ecf20Sopenharmony_ci pad_source, pad_sink); 1328c2ecf20Sopenharmony_ci return -EINVAL; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci ret = media_create_pad_link(if_vid, pad_source, 1358c2ecf20Sopenharmony_ci decoder, pad_sink, 1368c2ecf20Sopenharmony_ci MEDIA_LNK_FL_ENABLED); 1378c2ecf20Sopenharmony_ci if (ret) { 1388c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "couldn't link PLL to decoder\n"); 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci } else { 1428c2ecf20Sopenharmony_ci pad_source = media_get_pad_index(tuner, false, 1438c2ecf20Sopenharmony_ci PAD_SIGNAL_ANALOG); 1448c2ecf20Sopenharmony_ci pad_sink = media_get_pad_index(decoder, true, 1458c2ecf20Sopenharmony_ci PAD_SIGNAL_ANALOG); 1468c2ecf20Sopenharmony_ci if (pad_source < 0 || pad_sink < 0) { 1478c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "couldn't get tuner and/or decoder pad(s): (%d, %d)\n", 1488c2ecf20Sopenharmony_ci pad_source, pad_sink); 1498c2ecf20Sopenharmony_ci return -EINVAL; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci ret = media_create_pad_link(tuner, pad_source, 1528c2ecf20Sopenharmony_ci decoder, pad_sink, 1538c2ecf20Sopenharmony_ci MEDIA_LNK_FL_ENABLED); 1548c2ecf20Sopenharmony_ci if (ret) 1558c2ecf20Sopenharmony_ci return ret; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (if_aud) { 1598c2ecf20Sopenharmony_ci pad_source = media_get_pad_index(tuner, false, 1608c2ecf20Sopenharmony_ci PAD_SIGNAL_AUDIO); 1618c2ecf20Sopenharmony_ci pad_sink = media_get_pad_index(if_aud, true, 1628c2ecf20Sopenharmony_ci PAD_SIGNAL_AUDIO); 1638c2ecf20Sopenharmony_ci if (pad_source < 0 || pad_sink < 0) { 1648c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "couldn't get tuner and/or decoder pad(s) for audio: (%d, %d)\n", 1658c2ecf20Sopenharmony_ci pad_source, pad_sink); 1668c2ecf20Sopenharmony_ci return -EINVAL; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci ret = media_create_pad_link(tuner, pad_source, 1698c2ecf20Sopenharmony_ci if_aud, pad_sink, 1708c2ecf20Sopenharmony_ci MEDIA_LNK_FL_ENABLED); 1718c2ecf20Sopenharmony_ci if (ret) { 1728c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "couldn't link tuner->audio PLL\n"); 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci } else { 1768c2ecf20Sopenharmony_ci if_aud = tuner; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* Create demod to V4L, VBI and SDR radio links */ 1828c2ecf20Sopenharmony_ci if (io_v4l) { 1838c2ecf20Sopenharmony_ci pad_source = media_get_pad_index(decoder, false, PAD_SIGNAL_DV); 1848c2ecf20Sopenharmony_ci if (pad_source < 0) { 1858c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "couldn't get decoder output pad for V4L I/O\n"); 1868c2ecf20Sopenharmony_ci return -EINVAL; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci ret = media_create_pad_link(decoder, pad_source, 1898c2ecf20Sopenharmony_ci io_v4l, 0, 1908c2ecf20Sopenharmony_ci MEDIA_LNK_FL_ENABLED); 1918c2ecf20Sopenharmony_ci if (ret) { 1928c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "couldn't link decoder output to V4L I/O\n"); 1938c2ecf20Sopenharmony_ci return ret; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (io_swradio) { 1988c2ecf20Sopenharmony_ci pad_source = media_get_pad_index(decoder, false, PAD_SIGNAL_DV); 1998c2ecf20Sopenharmony_ci if (pad_source < 0) { 2008c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "couldn't get decoder output pad for SDR\n"); 2018c2ecf20Sopenharmony_ci return -EINVAL; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci ret = media_create_pad_link(decoder, pad_source, 2048c2ecf20Sopenharmony_ci io_swradio, 0, 2058c2ecf20Sopenharmony_ci MEDIA_LNK_FL_ENABLED); 2068c2ecf20Sopenharmony_ci if (ret) { 2078c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "couldn't link decoder output to SDR\n"); 2088c2ecf20Sopenharmony_ci return ret; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (io_vbi) { 2138c2ecf20Sopenharmony_ci pad_source = media_get_pad_index(decoder, false, PAD_SIGNAL_DV); 2148c2ecf20Sopenharmony_ci if (pad_source < 0) { 2158c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "couldn't get decoder output pad for VBI\n"); 2168c2ecf20Sopenharmony_ci return -EINVAL; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci ret = media_create_pad_link(decoder, pad_source, 2198c2ecf20Sopenharmony_ci io_vbi, 0, 2208c2ecf20Sopenharmony_ci MEDIA_LNK_FL_ENABLED); 2218c2ecf20Sopenharmony_ci if (ret) { 2228c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "couldn't link decoder output to VBI\n"); 2238c2ecf20Sopenharmony_ci return ret; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* Create links for the media connectors */ 2288c2ecf20Sopenharmony_ci flags = MEDIA_LNK_FL_ENABLED; 2298c2ecf20Sopenharmony_ci media_device_for_each_entity(entity, mdev) { 2308c2ecf20Sopenharmony_ci switch (entity->function) { 2318c2ecf20Sopenharmony_ci case MEDIA_ENT_F_CONN_RF: 2328c2ecf20Sopenharmony_ci if (!tuner) 2338c2ecf20Sopenharmony_ci continue; 2348c2ecf20Sopenharmony_ci pad_sink = media_get_pad_index(tuner, true, 2358c2ecf20Sopenharmony_ci PAD_SIGNAL_ANALOG); 2368c2ecf20Sopenharmony_ci if (pad_sink < 0) { 2378c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "couldn't get tuner analog pad sink\n"); 2388c2ecf20Sopenharmony_ci return -EINVAL; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci ret = media_create_pad_link(entity, 0, tuner, 2418c2ecf20Sopenharmony_ci pad_sink, 2428c2ecf20Sopenharmony_ci flags); 2438c2ecf20Sopenharmony_ci break; 2448c2ecf20Sopenharmony_ci case MEDIA_ENT_F_CONN_SVIDEO: 2458c2ecf20Sopenharmony_ci case MEDIA_ENT_F_CONN_COMPOSITE: 2468c2ecf20Sopenharmony_ci pad_sink = media_get_pad_index(decoder, true, 2478c2ecf20Sopenharmony_ci PAD_SIGNAL_ANALOG); 2488c2ecf20Sopenharmony_ci if (pad_sink < 0) { 2498c2ecf20Sopenharmony_ci dev_warn(mdev->dev, "couldn't get tuner analog pad sink\n"); 2508c2ecf20Sopenharmony_ci return -EINVAL; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci ret = media_create_pad_link(entity, 0, decoder, 2538c2ecf20Sopenharmony_ci pad_sink, 2548c2ecf20Sopenharmony_ci flags); 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci default: 2578c2ecf20Sopenharmony_ci continue; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci if (ret) 2608c2ecf20Sopenharmony_ci return ret; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci flags = 0; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ciint v4l_enable_media_source(struct video_device *vdev) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct media_device *mdev = vdev->entity.graph_obj.mdev; 2728c2ecf20Sopenharmony_ci int ret = 0, err; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (!mdev) 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 2788c2ecf20Sopenharmony_ci if (!mdev->enable_source) 2798c2ecf20Sopenharmony_ci goto end; 2808c2ecf20Sopenharmony_ci err = mdev->enable_source(&vdev->entity, &vdev->pipe); 2818c2ecf20Sopenharmony_ci if (err) 2828c2ecf20Sopenharmony_ci ret = -EBUSY; 2838c2ecf20Sopenharmony_ciend: 2848c2ecf20Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 2858c2ecf20Sopenharmony_ci return ret; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l_enable_media_source); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_civoid v4l_disable_media_source(struct video_device *vdev) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct media_device *mdev = vdev->entity.graph_obj.mdev; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (mdev) { 2948c2ecf20Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 2958c2ecf20Sopenharmony_ci if (mdev->disable_source) 2968c2ecf20Sopenharmony_ci mdev->disable_source(&vdev->entity); 2978c2ecf20Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l_disable_media_source); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ciint v4l_vb2q_enable_media_source(struct vb2_queue *q) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct v4l2_fh *fh = q->owner; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (fh && fh->vdev) 3078c2ecf20Sopenharmony_ci return v4l_enable_media_source(fh->vdev); 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ciint v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd, 3138c2ecf20Sopenharmony_ci struct media_pad *sink) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct fwnode_handle *endpoint; 3168c2ecf20Sopenharmony_ci struct v4l2_subdev *sink_sd; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (!(sink->flags & MEDIA_PAD_FL_SINK) || 3198c2ecf20Sopenharmony_ci !is_media_entity_v4l2_subdev(sink->entity)) 3208c2ecf20Sopenharmony_ci return -EINVAL; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci sink_sd = media_entity_to_v4l2_subdev(sink->entity); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci fwnode_graph_for_each_endpoint(dev_fwnode(src_sd->dev), endpoint) { 3258c2ecf20Sopenharmony_ci struct fwnode_handle *remote_ep; 3268c2ecf20Sopenharmony_ci int src_idx, sink_idx, ret; 3278c2ecf20Sopenharmony_ci struct media_pad *src; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci src_idx = media_entity_get_fwnode_pad(&src_sd->entity, 3308c2ecf20Sopenharmony_ci endpoint, 3318c2ecf20Sopenharmony_ci MEDIA_PAD_FL_SOURCE); 3328c2ecf20Sopenharmony_ci if (src_idx < 0) 3338c2ecf20Sopenharmony_ci continue; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci remote_ep = fwnode_graph_get_remote_endpoint(endpoint); 3368c2ecf20Sopenharmony_ci if (!remote_ep) 3378c2ecf20Sopenharmony_ci continue; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* 3408c2ecf20Sopenharmony_ci * ask the sink to verify it owns the remote endpoint, 3418c2ecf20Sopenharmony_ci * and translate to a sink pad. 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_ci sink_idx = media_entity_get_fwnode_pad(&sink_sd->entity, 3448c2ecf20Sopenharmony_ci remote_ep, 3458c2ecf20Sopenharmony_ci MEDIA_PAD_FL_SINK); 3468c2ecf20Sopenharmony_ci fwnode_handle_put(remote_ep); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (sink_idx < 0 || sink_idx != sink->index) 3498c2ecf20Sopenharmony_ci continue; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* 3528c2ecf20Sopenharmony_ci * the source endpoint corresponds to one of its source pads, 3538c2ecf20Sopenharmony_ci * the source endpoint connects to an endpoint at the sink 3548c2ecf20Sopenharmony_ci * entity, and the sink endpoint corresponds to the sink 3558c2ecf20Sopenharmony_ci * pad requested, so we have found an endpoint connection 3568c2ecf20Sopenharmony_ci * that works, create the media link for it. 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci src = &src_sd->entity.pads[src_idx]; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* skip if link already exists */ 3628c2ecf20Sopenharmony_ci if (media_entity_find_link(src, sink)) 3638c2ecf20Sopenharmony_ci continue; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci dev_dbg(sink_sd->dev, "creating link %s:%d -> %s:%d\n", 3668c2ecf20Sopenharmony_ci src_sd->entity.name, src_idx, 3678c2ecf20Sopenharmony_ci sink_sd->entity.name, sink_idx); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci ret = media_create_pad_link(&src_sd->entity, src_idx, 3708c2ecf20Sopenharmony_ci &sink_sd->entity, sink_idx, 0); 3718c2ecf20Sopenharmony_ci if (ret) { 3728c2ecf20Sopenharmony_ci dev_err(sink_sd->dev, 3738c2ecf20Sopenharmony_ci "link %s:%d -> %s:%d failed with %d\n", 3748c2ecf20Sopenharmony_ci src_sd->entity.name, src_idx, 3758c2ecf20Sopenharmony_ci sink_sd->entity.name, sink_idx, ret); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci fwnode_handle_put(endpoint); 3788c2ecf20Sopenharmony_ci return ret; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_create_fwnode_links_to_pad); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ciint v4l2_create_fwnode_links(struct v4l2_subdev *src_sd, 3878c2ecf20Sopenharmony_ci struct v4l2_subdev *sink_sd) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci unsigned int i; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci for (i = 0; i < sink_sd->entity.num_pads; i++) { 3928c2ecf20Sopenharmony_ci struct media_pad *pad = &sink_sd->entity.pads[i]; 3938c2ecf20Sopenharmony_ci int ret; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (!(pad->flags & MEDIA_PAD_FL_SINK)) 3968c2ecf20Sopenharmony_ci continue; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci ret = v4l2_create_fwnode_links_to_pad(src_sd, pad); 3998c2ecf20Sopenharmony_ci if (ret) 4008c2ecf20Sopenharmony_ci return ret; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_create_fwnode_links); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 4088c2ecf20Sopenharmony_ci * Pipeline power management 4098c2ecf20Sopenharmony_ci * 4108c2ecf20Sopenharmony_ci * Entities must be powered up when part of a pipeline that contains at least 4118c2ecf20Sopenharmony_ci * one open video device node. 4128c2ecf20Sopenharmony_ci * 4138c2ecf20Sopenharmony_ci * To achieve this use the entity use_count field to track the number of users. 4148c2ecf20Sopenharmony_ci * For entities corresponding to video device nodes the use_count field stores 4158c2ecf20Sopenharmony_ci * the users count of the node. For entities corresponding to subdevs the 4168c2ecf20Sopenharmony_ci * use_count field stores the total number of users of all video device nodes 4178c2ecf20Sopenharmony_ci * in the pipeline. 4188c2ecf20Sopenharmony_ci * 4198c2ecf20Sopenharmony_ci * The v4l2_pipeline_pm_{get, put}() functions must be called in the open() and 4208c2ecf20Sopenharmony_ci * close() handlers of video device nodes. It increments or decrements the use 4218c2ecf20Sopenharmony_ci * count of all subdev entities in the pipeline. 4228c2ecf20Sopenharmony_ci * 4238c2ecf20Sopenharmony_ci * To react to link management on powered pipelines, the link setup notification 4248c2ecf20Sopenharmony_ci * callback updates the use count of all entities in the source and sink sides 4258c2ecf20Sopenharmony_ci * of the link. 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci/* 4298c2ecf20Sopenharmony_ci * pipeline_pm_use_count - Count the number of users of a pipeline 4308c2ecf20Sopenharmony_ci * @entity: The entity 4318c2ecf20Sopenharmony_ci * 4328c2ecf20Sopenharmony_ci * Return the total number of users of all video device nodes in the pipeline. 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_cistatic int pipeline_pm_use_count(struct media_entity *entity, 4358c2ecf20Sopenharmony_ci struct media_graph *graph) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci int use = 0; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci media_graph_walk_start(graph, entity); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci while ((entity = media_graph_walk_next(graph))) { 4428c2ecf20Sopenharmony_ci if (is_media_entity_v4l2_video_device(entity)) 4438c2ecf20Sopenharmony_ci use += entity->use_count; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return use; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci/* 4508c2ecf20Sopenharmony_ci * pipeline_pm_power_one - Apply power change to an entity 4518c2ecf20Sopenharmony_ci * @entity: The entity 4528c2ecf20Sopenharmony_ci * @change: Use count change 4538c2ecf20Sopenharmony_ci * 4548c2ecf20Sopenharmony_ci * Change the entity use count by @change. If the entity is a subdev update its 4558c2ecf20Sopenharmony_ci * power state by calling the core::s_power operation when the use count goes 4568c2ecf20Sopenharmony_ci * from 0 to != 0 or from != 0 to 0. 4578c2ecf20Sopenharmony_ci * 4588c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code on failure. 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_cistatic int pipeline_pm_power_one(struct media_entity *entity, int change) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev; 4638c2ecf20Sopenharmony_ci int ret; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci subdev = is_media_entity_v4l2_subdev(entity) 4668c2ecf20Sopenharmony_ci ? media_entity_to_v4l2_subdev(entity) : NULL; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (entity->use_count == 0 && change > 0 && subdev != NULL) { 4698c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(subdev, core, s_power, 1); 4708c2ecf20Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) 4718c2ecf20Sopenharmony_ci return ret; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci entity->use_count += change; 4758c2ecf20Sopenharmony_ci WARN_ON(entity->use_count < 0); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (entity->use_count == 0 && change < 0 && subdev != NULL) 4788c2ecf20Sopenharmony_ci v4l2_subdev_call(subdev, core, s_power, 0); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci return 0; 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci/* 4848c2ecf20Sopenharmony_ci * pipeline_pm_power - Apply power change to all entities in a pipeline 4858c2ecf20Sopenharmony_ci * @entity: The entity 4868c2ecf20Sopenharmony_ci * @change: Use count change 4878c2ecf20Sopenharmony_ci * 4888c2ecf20Sopenharmony_ci * Walk the pipeline to update the use count and the power state of all non-node 4898c2ecf20Sopenharmony_ci * entities. 4908c2ecf20Sopenharmony_ci * 4918c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code on failure. 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_cistatic int pipeline_pm_power(struct media_entity *entity, int change, 4948c2ecf20Sopenharmony_ci struct media_graph *graph) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct media_entity *first = entity; 4978c2ecf20Sopenharmony_ci int ret = 0; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (!change) 5008c2ecf20Sopenharmony_ci return 0; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci media_graph_walk_start(graph, entity); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci while (!ret && (entity = media_graph_walk_next(graph))) 5058c2ecf20Sopenharmony_ci if (is_media_entity_v4l2_subdev(entity)) 5068c2ecf20Sopenharmony_ci ret = pipeline_pm_power_one(entity, change); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (!ret) 5098c2ecf20Sopenharmony_ci return ret; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci media_graph_walk_start(graph, first); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci while ((first = media_graph_walk_next(graph)) 5148c2ecf20Sopenharmony_ci && first != entity) 5158c2ecf20Sopenharmony_ci if (is_media_entity_v4l2_subdev(first)) 5168c2ecf20Sopenharmony_ci pipeline_pm_power_one(first, -change); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return ret; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic int v4l2_pipeline_pm_use(struct media_entity *entity, unsigned int use) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct media_device *mdev = entity->graph_obj.mdev; 5248c2ecf20Sopenharmony_ci int change = use ? 1 : -1; 5258c2ecf20Sopenharmony_ci int ret; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* Apply use count to node. */ 5308c2ecf20Sopenharmony_ci entity->use_count += change; 5318c2ecf20Sopenharmony_ci WARN_ON(entity->use_count < 0); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* Apply power change to connected non-nodes. */ 5348c2ecf20Sopenharmony_ci ret = pipeline_pm_power(entity, change, &mdev->pm_count_walk); 5358c2ecf20Sopenharmony_ci if (ret < 0) 5368c2ecf20Sopenharmony_ci entity->use_count -= change; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci return ret; 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ciint v4l2_pipeline_pm_get(struct media_entity *entity) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci return v4l2_pipeline_pm_use(entity, 1); 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_pipeline_pm_get); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_civoid v4l2_pipeline_pm_put(struct media_entity *entity) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci /* Powering off entities shouldn't fail. */ 5528c2ecf20Sopenharmony_ci WARN_ON(v4l2_pipeline_pm_use(entity, 0)); 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_pipeline_pm_put); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ciint v4l2_pipeline_link_notify(struct media_link *link, u32 flags, 5578c2ecf20Sopenharmony_ci unsigned int notification) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct media_graph *graph = &link->graph_obj.mdev->pm_count_walk; 5608c2ecf20Sopenharmony_ci struct media_entity *source = link->source->entity; 5618c2ecf20Sopenharmony_ci struct media_entity *sink = link->sink->entity; 5628c2ecf20Sopenharmony_ci int source_use; 5638c2ecf20Sopenharmony_ci int sink_use; 5648c2ecf20Sopenharmony_ci int ret = 0; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci source_use = pipeline_pm_use_count(source, graph); 5678c2ecf20Sopenharmony_ci sink_use = pipeline_pm_use_count(sink, graph); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && 5708c2ecf20Sopenharmony_ci !(flags & MEDIA_LNK_FL_ENABLED)) { 5718c2ecf20Sopenharmony_ci /* Powering off entities is assumed to never fail. */ 5728c2ecf20Sopenharmony_ci pipeline_pm_power(source, -sink_use, graph); 5738c2ecf20Sopenharmony_ci pipeline_pm_power(sink, -source_use, graph); 5748c2ecf20Sopenharmony_ci return 0; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && 5788c2ecf20Sopenharmony_ci (flags & MEDIA_LNK_FL_ENABLED)) { 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci ret = pipeline_pm_power(source, sink_use, graph); 5818c2ecf20Sopenharmony_ci if (ret < 0) 5828c2ecf20Sopenharmony_ci return ret; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci ret = pipeline_pm_power(sink, source_use, graph); 5858c2ecf20Sopenharmony_ci if (ret < 0) 5868c2ecf20Sopenharmony_ci pipeline_pm_power(source, -sink_use, graph); 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci return ret; 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_pipeline_link_notify); 592