brintos

brintos / linux-shallow public Read only

0
0
Text · 15.8 KiB · 4bb9135 Raw
615 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2 3/*4 * Media Controller ancillary functions5 *6 * Copyright (c) 2016 Mauro Carvalho Chehab <mchehab@kernel.org>7 * Copyright (C) 2016 Shuah Khan <shuahkh@osg.samsung.com>8 * Copyright (C) 2006-2010 Nokia Corporation9 * Copyright (c) 2016 Intel Corporation.10 */11 12#include <linux/module.h>13#include <linux/pci.h>14#include <linux/usb.h>15#include <media/media-device.h>16#include <media/media-entity.h>17#include <media/v4l2-fh.h>18#include <media/v4l2-mc.h>19#include <media/v4l2-subdev.h>20#include <media/videobuf2-core.h>21 22int v4l2_mc_create_media_graph(struct media_device *mdev)23 24{25	struct media_entity *entity;26	struct media_entity *if_vid = NULL, *if_aud = NULL;27	struct media_entity *tuner = NULL, *decoder = NULL;28	struct media_entity *io_v4l = NULL, *io_vbi = NULL, *io_swradio = NULL;29	bool is_webcam = false;30	u32 flags;31	int ret, pad_sink, pad_source;32 33	if (!mdev)34		return 0;35 36	media_device_for_each_entity(entity, mdev) {37		switch (entity->function) {38		case MEDIA_ENT_F_IF_VID_DECODER:39			if_vid = entity;40			break;41		case MEDIA_ENT_F_IF_AUD_DECODER:42			if_aud = entity;43			break;44		case MEDIA_ENT_F_TUNER:45			tuner = entity;46			break;47		case MEDIA_ENT_F_ATV_DECODER:48			decoder = entity;49			break;50		case MEDIA_ENT_F_IO_V4L:51			io_v4l = entity;52			break;53		case MEDIA_ENT_F_IO_VBI:54			io_vbi = entity;55			break;56		case MEDIA_ENT_F_IO_SWRADIO:57			io_swradio = entity;58			break;59		case MEDIA_ENT_F_CAM_SENSOR:60			is_webcam = true;61			break;62		}63	}64 65	/* It should have at least one I/O entity */66	if (!io_v4l && !io_vbi && !io_swradio) {67		dev_warn(mdev->dev, "Didn't find any I/O entity\n");68		return -EINVAL;69	}70 71	/*72	 * Here, webcams are modelled on a very simple way: the sensor is73	 * connected directly to the I/O entity. All dirty details, like74	 * scaler and crop HW are hidden. While such mapping is not enough75	 * for mc-centric hardware, it is enough for v4l2 interface centric76	 * PC-consumer's hardware.77	 */78	if (is_webcam) {79		if (!io_v4l) {80			dev_warn(mdev->dev, "Didn't find a MEDIA_ENT_F_IO_V4L\n");81			return -EINVAL;82		}83 84		media_device_for_each_entity(entity, mdev) {85			if (entity->function != MEDIA_ENT_F_CAM_SENSOR)86				continue;87			ret = media_create_pad_link(entity, 0,88						    io_v4l, 0,89						    MEDIA_LNK_FL_ENABLED);90			if (ret) {91				dev_warn(mdev->dev, "Failed to create a sensor link\n");92				return ret;93			}94		}95		if (!decoder)96			return 0;97	}98 99	/* The device isn't a webcam. So, it should have a decoder */100	if (!decoder) {101		dev_warn(mdev->dev, "Decoder not found\n");102		return -EINVAL;103	}104 105	/* Link the tuner and IF video output pads */106	if (tuner) {107		if (if_vid) {108			pad_source = media_get_pad_index(tuner,109							 MEDIA_PAD_FL_SOURCE,110							 PAD_SIGNAL_ANALOG);111			pad_sink = media_get_pad_index(if_vid,112						       MEDIA_PAD_FL_SINK,113						       PAD_SIGNAL_ANALOG);114			if (pad_source < 0 || pad_sink < 0) {115				dev_warn(mdev->dev, "Couldn't get tuner and/or PLL pad(s): (%d, %d)\n",116					 pad_source, pad_sink);117				return -EINVAL;118			}119			ret = media_create_pad_link(tuner, pad_source,120						    if_vid, pad_sink,121						    MEDIA_LNK_FL_ENABLED);122			if (ret) {123				dev_warn(mdev->dev, "Couldn't create tuner->PLL link)\n");124				return ret;125			}126 127			pad_source = media_get_pad_index(if_vid,128							 MEDIA_PAD_FL_SOURCE,129							 PAD_SIGNAL_ANALOG);130			pad_sink = media_get_pad_index(decoder,131						       MEDIA_PAD_FL_SINK,132						       PAD_SIGNAL_ANALOG);133			if (pad_source < 0 || pad_sink < 0) {134				dev_warn(mdev->dev, "get decoder and/or PLL pad(s): (%d, %d)\n",135					 pad_source, pad_sink);136				return -EINVAL;137			}138			ret = media_create_pad_link(if_vid, pad_source,139						    decoder, pad_sink,140						    MEDIA_LNK_FL_ENABLED);141			if (ret) {142				dev_warn(mdev->dev, "couldn't link PLL to decoder\n");143				return ret;144			}145		} else {146			pad_source = media_get_pad_index(tuner,147							 MEDIA_PAD_FL_SOURCE,148							 PAD_SIGNAL_ANALOG);149			pad_sink = media_get_pad_index(decoder,150						       MEDIA_PAD_FL_SINK,151						       PAD_SIGNAL_ANALOG);152			if (pad_source < 0 || pad_sink < 0) {153				dev_warn(mdev->dev, "couldn't get tuner and/or decoder pad(s): (%d, %d)\n",154					 pad_source, pad_sink);155				return -EINVAL;156			}157			ret = media_create_pad_link(tuner, pad_source,158						    decoder, pad_sink,159						    MEDIA_LNK_FL_ENABLED);160			if (ret)161				return ret;162		}163 164		if (if_aud) {165			pad_source = media_get_pad_index(tuner,166							 MEDIA_PAD_FL_SOURCE,167							 PAD_SIGNAL_AUDIO);168			pad_sink = media_get_pad_index(if_aud,169						       MEDIA_PAD_FL_SINK,170						       PAD_SIGNAL_AUDIO);171			if (pad_source < 0 || pad_sink < 0) {172				dev_warn(mdev->dev, "couldn't get tuner and/or decoder pad(s) for audio: (%d, %d)\n",173					 pad_source, pad_sink);174				return -EINVAL;175			}176			ret = media_create_pad_link(tuner, pad_source,177						    if_aud, pad_sink,178						    MEDIA_LNK_FL_ENABLED);179			if (ret) {180				dev_warn(mdev->dev, "couldn't link tuner->audio PLL\n");181				return ret;182			}183		} else {184			if_aud = tuner;185		}186 187	}188 189	/* Create demod to V4L, VBI and SDR radio links */190	if (io_v4l) {191		pad_source = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE,192						 PAD_SIGNAL_DV);193		if (pad_source < 0) {194			dev_warn(mdev->dev, "couldn't get decoder output pad for V4L I/O\n");195			return -EINVAL;196		}197		ret = media_create_pad_link(decoder, pad_source,198					    io_v4l, 0,199					    MEDIA_LNK_FL_ENABLED);200		if (ret) {201			dev_warn(mdev->dev, "couldn't link decoder output to V4L I/O\n");202			return ret;203		}204	}205 206	if (io_swradio) {207		pad_source = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE,208						 PAD_SIGNAL_DV);209		if (pad_source < 0) {210			dev_warn(mdev->dev, "couldn't get decoder output pad for SDR\n");211			return -EINVAL;212		}213		ret = media_create_pad_link(decoder, pad_source,214					    io_swradio, 0,215					    MEDIA_LNK_FL_ENABLED);216		if (ret) {217			dev_warn(mdev->dev, "couldn't link decoder output to SDR\n");218			return ret;219		}220	}221 222	if (io_vbi) {223		pad_source = media_get_pad_index(decoder, MEDIA_PAD_FL_SOURCE,224						 PAD_SIGNAL_DV);225		if (pad_source < 0) {226			dev_warn(mdev->dev, "couldn't get decoder output pad for VBI\n");227			return -EINVAL;228		}229		ret = media_create_pad_link(decoder, pad_source,230					    io_vbi, 0,231					    MEDIA_LNK_FL_ENABLED);232		if (ret) {233			dev_warn(mdev->dev, "couldn't link decoder output to VBI\n");234			return ret;235		}236	}237 238	/* Create links for the media connectors */239	flags = MEDIA_LNK_FL_ENABLED;240	media_device_for_each_entity(entity, mdev) {241		switch (entity->function) {242		case MEDIA_ENT_F_CONN_RF:243			if (!tuner)244				continue;245			pad_sink = media_get_pad_index(tuner, MEDIA_PAD_FL_SINK,246						       PAD_SIGNAL_ANALOG);247			if (pad_sink < 0) {248				dev_warn(mdev->dev, "couldn't get tuner analog pad sink\n");249				return -EINVAL;250			}251			ret = media_create_pad_link(entity, 0, tuner,252						    pad_sink,253						    flags);254			break;255		case MEDIA_ENT_F_CONN_SVIDEO:256		case MEDIA_ENT_F_CONN_COMPOSITE:257			pad_sink = media_get_pad_index(decoder,258						       MEDIA_PAD_FL_SINK,259						       PAD_SIGNAL_ANALOG);260			if (pad_sink < 0) {261				dev_warn(mdev->dev, "couldn't get decoder analog pad sink\n");262				return -EINVAL;263			}264			ret = media_create_pad_link(entity, 0, decoder,265						    pad_sink,266						    flags);267			break;268		default:269			continue;270		}271		if (ret)272			return ret;273 274		flags = 0;275	}276 277	return 0;278}279EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph);280 281int v4l_enable_media_source(struct video_device *vdev)282{283	struct media_device *mdev = vdev->entity.graph_obj.mdev;284	int ret = 0, err;285 286	if (!mdev)287		return 0;288 289	mutex_lock(&mdev->graph_mutex);290	if (!mdev->enable_source)291		goto end;292	err = mdev->enable_source(&vdev->entity, &vdev->pipe);293	if (err)294		ret = -EBUSY;295end:296	mutex_unlock(&mdev->graph_mutex);297	return ret;298}299EXPORT_SYMBOL_GPL(v4l_enable_media_source);300 301void v4l_disable_media_source(struct video_device *vdev)302{303	struct media_device *mdev = vdev->entity.graph_obj.mdev;304 305	if (mdev) {306		mutex_lock(&mdev->graph_mutex);307		if (mdev->disable_source)308			mdev->disable_source(&vdev->entity);309		mutex_unlock(&mdev->graph_mutex);310	}311}312EXPORT_SYMBOL_GPL(v4l_disable_media_source);313 314int v4l_vb2q_enable_media_source(struct vb2_queue *q)315{316	struct v4l2_fh *fh = q->owner;317 318	if (fh && fh->vdev)319		return v4l_enable_media_source(fh->vdev);320	return 0;321}322EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source);323 324int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd,325				    struct media_pad *sink, u32 flags)326{327	struct fwnode_handle *endpoint;328 329	if (!(sink->flags & MEDIA_PAD_FL_SINK))330		return -EINVAL;331 332	fwnode_graph_for_each_endpoint(dev_fwnode(src_sd->dev), endpoint) {333		struct fwnode_handle *remote_ep;334		int src_idx, sink_idx, ret;335		struct media_pad *src;336 337		src_idx = media_entity_get_fwnode_pad(&src_sd->entity,338						      endpoint,339						      MEDIA_PAD_FL_SOURCE);340		if (src_idx < 0) {341			dev_dbg(src_sd->dev, "no source pad found for %pfw\n",342				endpoint);343			continue;344		}345 346		remote_ep = fwnode_graph_get_remote_endpoint(endpoint);347		if (!remote_ep) {348			dev_dbg(src_sd->dev, "no remote ep found for %pfw\n",349				endpoint);350			continue;351		}352 353		/*354		 * ask the sink to verify it owns the remote endpoint,355		 * and translate to a sink pad.356		 */357		sink_idx = media_entity_get_fwnode_pad(sink->entity,358						       remote_ep,359						       MEDIA_PAD_FL_SINK);360		fwnode_handle_put(remote_ep);361 362		if (sink_idx < 0 || sink_idx != sink->index) {363			dev_dbg(src_sd->dev,364				"sink pad index mismatch or error (is %d, expected %u)\n",365				sink_idx, sink->index);366			continue;367		}368 369		/*370		 * the source endpoint corresponds to one of its source pads,371		 * the source endpoint connects to an endpoint at the sink372		 * entity, and the sink endpoint corresponds to the sink373		 * pad requested, so we have found an endpoint connection374		 * that works, create the media link for it.375		 */376 377		src = &src_sd->entity.pads[src_idx];378 379		/* skip if link already exists */380		if (media_entity_find_link(src, sink)) {381			dev_dbg(src_sd->dev,382				"link %s:%d -> %s:%d already exists\n",383				src_sd->entity.name, src_idx,384				sink->entity->name, sink_idx);385			continue;386		}387 388		dev_dbg(src_sd->dev, "creating link %s:%d -> %s:%d\n",389			src_sd->entity.name, src_idx,390			sink->entity->name, sink_idx);391 392		ret = media_create_pad_link(&src_sd->entity, src_idx,393					    sink->entity, sink_idx, flags);394		if (ret) {395			dev_err(src_sd->dev,396				"link %s:%d -> %s:%d failed with %d\n",397				src_sd->entity.name, src_idx,398				sink->entity->name, sink_idx, ret);399 400			fwnode_handle_put(endpoint);401			return ret;402		}403	}404 405	return 0;406}407EXPORT_SYMBOL_GPL(v4l2_create_fwnode_links_to_pad);408 409int v4l2_create_fwnode_links(struct v4l2_subdev *src_sd,410			     struct v4l2_subdev *sink_sd)411{412	unsigned int i;413 414	for (i = 0; i < sink_sd->entity.num_pads; i++) {415		struct media_pad *pad = &sink_sd->entity.pads[i];416		int ret;417 418		if (!(pad->flags & MEDIA_PAD_FL_SINK))419			continue;420 421		ret = v4l2_create_fwnode_links_to_pad(src_sd, pad, 0);422		if (ret)423			return ret;424	}425 426	return 0;427}428EXPORT_SYMBOL_GPL(v4l2_create_fwnode_links);429 430/* -----------------------------------------------------------------------------431 * Pipeline power management432 *433 * Entities must be powered up when part of a pipeline that contains at least434 * one open video device node.435 *436 * To achieve this use the entity use_count field to track the number of users.437 * For entities corresponding to video device nodes the use_count field stores438 * the users count of the node. For entities corresponding to subdevs the439 * use_count field stores the total number of users of all video device nodes440 * in the pipeline.441 *442 * The v4l2_pipeline_pm_{get, put}() functions must be called in the open() and443 * close() handlers of video device nodes. It increments or decrements the use444 * count of all subdev entities in the pipeline.445 *446 * To react to link management on powered pipelines, the link setup notification447 * callback updates the use count of all entities in the source and sink sides448 * of the link.449 */450 451/*452 * pipeline_pm_use_count - Count the number of users of a pipeline453 * @entity: The entity454 *455 * Return the total number of users of all video device nodes in the pipeline.456 */457static int pipeline_pm_use_count(struct media_entity *entity,458	struct media_graph *graph)459{460	int use = 0;461 462	media_graph_walk_start(graph, entity);463 464	while ((entity = media_graph_walk_next(graph))) {465		if (is_media_entity_v4l2_video_device(entity))466			use += entity->use_count;467	}468 469	return use;470}471 472/*473 * pipeline_pm_power_one - Apply power change to an entity474 * @entity: The entity475 * @change: Use count change476 *477 * Change the entity use count by @change. If the entity is a subdev update its478 * power state by calling the core::s_power operation when the use count goes479 * from 0 to != 0 or from != 0 to 0.480 *481 * Return 0 on success or a negative error code on failure.482 */483static int pipeline_pm_power_one(struct media_entity *entity, int change)484{485	struct v4l2_subdev *subdev;486	int ret;487 488	subdev = is_media_entity_v4l2_subdev(entity)489	       ? media_entity_to_v4l2_subdev(entity) : NULL;490 491	if (entity->use_count == 0 && change > 0 && subdev != NULL) {492		ret = v4l2_subdev_call(subdev, core, s_power, 1);493		if (ret < 0 && ret != -ENOIOCTLCMD)494			return ret;495	}496 497	entity->use_count += change;498	WARN_ON(entity->use_count < 0);499 500	if (entity->use_count == 0 && change < 0 && subdev != NULL)501		v4l2_subdev_call(subdev, core, s_power, 0);502 503	return 0;504}505 506/*507 * pipeline_pm_power - Apply power change to all entities in a pipeline508 * @entity: The entity509 * @change: Use count change510 *511 * Walk the pipeline to update the use count and the power state of all non-node512 * entities.513 *514 * Return 0 on success or a negative error code on failure.515 */516static int pipeline_pm_power(struct media_entity *entity, int change,517	struct media_graph *graph)518{519	struct media_entity *first = entity;520	int ret = 0;521 522	if (!change)523		return 0;524 525	media_graph_walk_start(graph, entity);526 527	while (!ret && (entity = media_graph_walk_next(graph)))528		if (is_media_entity_v4l2_subdev(entity))529			ret = pipeline_pm_power_one(entity, change);530 531	if (!ret)532		return ret;533 534	media_graph_walk_start(graph, first);535 536	while ((first = media_graph_walk_next(graph))537	       && first != entity)538		if (is_media_entity_v4l2_subdev(first))539			pipeline_pm_power_one(first, -change);540 541	return ret;542}543 544static int v4l2_pipeline_pm_use(struct media_entity *entity, unsigned int use)545{546	struct media_device *mdev = entity->graph_obj.mdev;547	int change = use ? 1 : -1;548	int ret;549 550	mutex_lock(&mdev->graph_mutex);551 552	/* Apply use count to node. */553	entity->use_count += change;554	WARN_ON(entity->use_count < 0);555 556	/* Apply power change to connected non-nodes. */557	ret = pipeline_pm_power(entity, change, &mdev->pm_count_walk);558	if (ret < 0)559		entity->use_count -= change;560 561	mutex_unlock(&mdev->graph_mutex);562 563	return ret;564}565 566int v4l2_pipeline_pm_get(struct media_entity *entity)567{568	return v4l2_pipeline_pm_use(entity, 1);569}570EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_get);571 572void v4l2_pipeline_pm_put(struct media_entity *entity)573{574	/* Powering off entities shouldn't fail. */575	WARN_ON(v4l2_pipeline_pm_use(entity, 0));576}577EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_put);578 579int v4l2_pipeline_link_notify(struct media_link *link, u32 flags,580			      unsigned int notification)581{582	struct media_graph *graph = &link->graph_obj.mdev->pm_count_walk;583	struct media_entity *source = link->source->entity;584	struct media_entity *sink = link->sink->entity;585	int source_use;586	int sink_use;587	int ret = 0;588 589	source_use = pipeline_pm_use_count(source, graph);590	sink_use = pipeline_pm_use_count(sink, graph);591 592	if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&593	    !(flags & MEDIA_LNK_FL_ENABLED)) {594		/* Powering off entities is assumed to never fail. */595		pipeline_pm_power(source, -sink_use, graph);596		pipeline_pm_power(sink, -source_use, graph);597		return 0;598	}599 600	if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&601		(flags & MEDIA_LNK_FL_ENABLED)) {602 603		ret = pipeline_pm_power(source, sink_use, graph);604		if (ret < 0)605			return ret;606 607		ret = pipeline_pm_power(sink, source_use, graph);608		if (ret < 0)609			pipeline_pm_power(source, -sink_use, graph);610	}611 612	return ret;613}614EXPORT_SYMBOL_GPL(v4l2_pipeline_link_notify);615