media-device.c 18.3 KB
Newer Older
L
Laurent Pinchart 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * Media device
 *
 * Copyright (C) 2010 Nokia Corporation
 *
 * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
 *	     Sakari Ailus <sakari.ailus@iki.fi>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

23 24
#include <linux/compat.h>
#include <linux/export.h>
25
#include <linux/idr.h>
L
Laurent Pinchart 已提交
26
#include <linux/ioctl.h>
27
#include <linux/media.h>
28
#include <linux/slab.h>
29
#include <linux/types.h>
L
Laurent Pinchart 已提交
30 31 32

#include <media/media-device.h>
#include <media/media-devnode.h>
33
#include <media/media-entity.h>
L
Laurent Pinchart 已提交
34

35 36
#ifdef CONFIG_MEDIA_CONTROLLER

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
/* -----------------------------------------------------------------------------
 * Userspace API
 */

static int media_device_open(struct file *filp)
{
	return 0;
}

static int media_device_close(struct file *filp)
{
	return 0;
}

static int media_device_get_info(struct media_device *dev,
				 struct media_device_info __user *__info)
{
	struct media_device_info info;

	memset(&info, 0, sizeof(info));

58 59 60 61 62
	if (dev->driver_name[0])
		strlcpy(info.driver, dev->driver_name, sizeof(info.driver));
	else
		strlcpy(info.driver, dev->dev->driver->name, sizeof(info.driver));

63 64 65 66 67 68 69 70
	strlcpy(info.model, dev->model, sizeof(info.model));
	strlcpy(info.serial, dev->serial, sizeof(info.serial));
	strlcpy(info.bus_info, dev->bus_info, sizeof(info.bus_info));

	info.media_version = MEDIA_API_VERSION;
	info.hw_revision = dev->hw_revision;
	info.driver_version = dev->driver_version;

71 72 73
	if (copy_to_user(__info, &info, sizeof(*__info)))
		return -EFAULT;
	return 0;
74 75
}

76 77 78 79 80 81 82 83 84 85
static struct media_entity *find_entity(struct media_device *mdev, u32 id)
{
	struct media_entity *entity;
	int next = id & MEDIA_ENT_ID_FLAG_NEXT;

	id &= ~MEDIA_ENT_ID_FLAG_NEXT;

	spin_lock(&mdev->lock);

	media_device_for_each_entity(entity, mdev) {
86 87
		if (((media_entity_id(entity) == id) && !next) ||
		    ((media_entity_id(entity) > id) && next)) {
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
			spin_unlock(&mdev->lock);
			return entity;
		}
	}

	spin_unlock(&mdev->lock);

	return NULL;
}

static long media_device_enum_entities(struct media_device *mdev,
				       struct media_entity_desc __user *uent)
{
	struct media_entity *ent;
	struct media_entity_desc u_ent;

104
	memset(&u_ent, 0, sizeof(u_ent));
105 106 107 108 109 110 111 112
	if (copy_from_user(&u_ent.id, &uent->id, sizeof(u_ent.id)))
		return -EFAULT;

	ent = find_entity(mdev, u_ent.id);

	if (ent == NULL)
		return -EINVAL;

113
	u_ent.id = media_entity_id(ent);
114 115
	if (ent->name)
		strlcpy(u_ent.name, ent->name, sizeof(u_ent.name));
116
	u_ent.type = ent->function;
117
	u_ent.revision = 0;		/* Unused */
118
	u_ent.flags = ent->flags;
119
	u_ent.group_id = 0;		/* Unused */
120 121
	u_ent.pads = ent->num_pads;
	u_ent.links = ent->num_links - ent->num_backlinks;
122
	memcpy(&u_ent.raw, &ent->info, sizeof(ent->info));
123 124 125 126 127 128 129 130
	if (copy_to_user(uent, &u_ent, sizeof(u_ent)))
		return -EFAULT;
	return 0;
}

static void media_device_kpad_to_upad(const struct media_pad *kpad,
				      struct media_pad_desc *upad)
{
131
	upad->entity = media_entity_id(kpad->entity);
132 133 134 135
	upad->index = kpad->index;
	upad->flags = kpad->flags;
}

136 137
static long __media_device_enum_links(struct media_device *mdev,
				      struct media_links_enum *links)
138 139 140
{
	struct media_entity *entity;

141
	entity = find_entity(mdev, links->entity);
142 143 144
	if (entity == NULL)
		return -EINVAL;

145
	if (links->pads) {
146 147 148 149
		unsigned int p;

		for (p = 0; p < entity->num_pads; p++) {
			struct media_pad_desc pad;
150 151

			memset(&pad, 0, sizeof(pad));
152
			media_device_kpad_to_upad(&entity->pads[p], &pad);
153
			if (copy_to_user(&links->pads[p], &pad, sizeof(pad)))
154 155 156 157
				return -EFAULT;
		}
	}

158
	if (links->links) {
159 160
		struct media_link *link;
		struct media_link_desc __user *ulink_desc = links->links;
161

162 163
		list_for_each_entry(link, &entity->links, list) {
			struct media_link_desc klink_desc;
164 165

			/* Ignore backlinks. */
166
			if (link->source->entity != entity)
167
				continue;
168 169 170 171 172 173 174 175
			memset(&klink_desc, 0, sizeof(klink_desc));
			media_device_kpad_to_upad(link->source,
						  &klink_desc.source);
			media_device_kpad_to_upad(link->sink,
						  &klink_desc.sink);
			klink_desc.flags = link->flags;
			if (copy_to_user(ulink_desc, &klink_desc,
					 sizeof(*ulink_desc)))
176
				return -EFAULT;
177
			ulink_desc++;
178 179
		}
	}
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196

	return 0;
}

static long media_device_enum_links(struct media_device *mdev,
				    struct media_links_enum __user *ulinks)
{
	struct media_links_enum links;
	int rval;

	if (copy_from_user(&links, ulinks, sizeof(links)))
		return -EFAULT;

	rval = __media_device_enum_links(mdev, &links);
	if (rval < 0)
		return rval;

197 198
	if (copy_to_user(ulinks, &links, sizeof(*ulinks)))
		return -EFAULT;
199

200 201 202
	return 0;
}

L
Laurent Pinchart 已提交
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
static long media_device_setup_link(struct media_device *mdev,
				    struct media_link_desc __user *_ulink)
{
	struct media_link *link = NULL;
	struct media_link_desc ulink;
	struct media_entity *source;
	struct media_entity *sink;
	int ret;

	if (copy_from_user(&ulink, _ulink, sizeof(ulink)))
		return -EFAULT;

	/* Find the source and sink entities and link.
	 */
	source = find_entity(mdev, ulink.source.entity);
	sink = find_entity(mdev, ulink.sink.entity);

	if (source == NULL || sink == NULL)
		return -EINVAL;

	if (ulink.source.index >= source->num_pads ||
	    ulink.sink.index >= sink->num_pads)
		return -EINVAL;

	link = media_entity_find_link(&source->pads[ulink.source.index],
				      &sink->pads[ulink.sink.index]);
	if (link == NULL)
		return -EINVAL;

	/* Setup the link on both entities. */
	ret = __media_entity_setup_link(link, ulink.flags);

	if (copy_to_user(_ulink, &ulink, sizeof(ulink)))
		return -EFAULT;

	return ret;
}

241 242 243 244 245 246 247
static long __media_device_get_topology(struct media_device *mdev,
				      struct media_v2_topology *topo)
{
	struct media_entity *entity;
	struct media_interface *intf;
	struct media_pad *pad;
	struct media_link *link;
248 249 250 251
	struct media_v2_entity kentity, *uentity;
	struct media_v2_interface kintf, *uintf;
	struct media_v2_pad kpad, *upad;
	struct media_v2_link klink, *ulink;
252 253
	unsigned int i;
	int ret = 0;
254 255 256 257 258

	topo->topology_version = mdev->topology_version;

	/* Get entities and number of entities */
	i = 0;
259
	uentity = media_get_uptr(topo->ptr_entities);
260 261
	media_device_for_each_entity(entity, mdev) {
		i++;
262
		if (ret || !uentity)
263 264 265 266 267 268 269 270
			continue;

		if (i > topo->num_entities) {
			ret = -ENOSPC;
			continue;
		}

		/* Copy fields to userspace struct if not error */
271 272 273 274 275
		memset(&kentity, 0, sizeof(kentity));
		kentity.id = entity->graph_obj.id;
		kentity.function = entity->function;
		strncpy(kentity.name, entity->name,
			sizeof(kentity.name));
276

277
		if (copy_to_user(uentity, &kentity, sizeof(kentity)))
278
			ret = -EFAULT;
279
		uentity++;
280 281 282 283 284
	}
	topo->num_entities = i;

	/* Get interfaces and number of interfaces */
	i = 0;
285
	uintf = media_get_uptr(topo->ptr_interfaces);
286 287
	media_device_for_each_intf(intf, mdev) {
		i++;
288
		if (ret || !uintf)
289 290 291 292 293 294 295
			continue;

		if (i > topo->num_interfaces) {
			ret = -ENOSPC;
			continue;
		}

296
		memset(&kintf, 0, sizeof(kintf));
297 298

		/* Copy intf fields to userspace struct */
299 300 301
		kintf.id = intf->graph_obj.id;
		kintf.intf_type = intf->type;
		kintf.flags = intf->flags;
302 303 304 305 306 307

		if (media_type(&intf->graph_obj) == MEDIA_GRAPH_INTF_DEVNODE) {
			struct media_intf_devnode *devnode;

			devnode = intf_to_devnode(intf);

308 309
			kintf.devnode.major = devnode->major;
			kintf.devnode.minor = devnode->minor;
310 311
		}

312
		if (copy_to_user(uintf, &kintf, sizeof(kintf)))
313
			ret = -EFAULT;
314
		uintf++;
315 316 317 318 319
	}
	topo->num_interfaces = i;

	/* Get pads and number of pads */
	i = 0;
320
	upad = media_get_uptr(topo->ptr_pads);
321 322
	media_device_for_each_pad(pad, mdev) {
		i++;
323
		if (ret || !upad)
324 325 326 327 328 329 330
			continue;

		if (i > topo->num_pads) {
			ret = -ENOSPC;
			continue;
		}

331
		memset(&kpad, 0, sizeof(kpad));
332 333

		/* Copy pad fields to userspace struct */
334 335 336
		kpad.id = pad->graph_obj.id;
		kpad.entity_id = pad->entity->graph_obj.id;
		kpad.flags = pad->flags;
337

338
		if (copy_to_user(upad, &kpad, sizeof(kpad)))
339
			ret = -EFAULT;
340
		upad++;
341 342 343 344 345
	}
	topo->num_pads = i;

	/* Get links and number of links */
	i = 0;
346
	ulink = media_get_uptr(topo->ptr_links);
347
	media_device_for_each_link(link, mdev) {
348 349 350
		if (link->is_backlink)
			continue;

351 352
		i++;

353
		if (ret || !ulink)
354 355 356 357 358 359 360
			continue;

		if (i > topo->num_links) {
			ret = -ENOSPC;
			continue;
		}

361
		memset(&klink, 0, sizeof(klink));
362 363

		/* Copy link fields to userspace struct */
364 365 366 367
		klink.id = link->graph_obj.id;
		klink.source_id = link->gobj0->id;
		klink.sink_id = link->gobj1->id;
		klink.flags = link->flags;
368

369
		if (copy_to_user(ulink, &klink, sizeof(klink)))
370
			ret = -EFAULT;
371
		ulink++;
372 373 374 375 376 377 378 379 380 381 382 383
	}
	topo->num_links = i;

	return ret;
}

static long media_device_get_topology(struct media_device *mdev,
				      struct media_v2_topology __user *utopo)
{
	struct media_v2_topology ktopo;
	int ret;

384 385
	if (copy_from_user(&ktopo, utopo, sizeof(ktopo)))
		return -EFAULT;
386 387 388 389 390

	ret = __media_device_get_topology(mdev, &ktopo);
	if (ret < 0)
		return ret;

391 392
	if (copy_to_user(utopo, &ktopo, sizeof(*utopo)))
		return -EFAULT;
393

394
	return 0;
395 396
}

397 398 399 400 401 402 403 404 405 406 407 408 409
static long media_device_ioctl(struct file *filp, unsigned int cmd,
			       unsigned long arg)
{
	struct media_devnode *devnode = media_devnode_data(filp);
	struct media_device *dev = to_media_device(devnode);
	long ret;

	switch (cmd) {
	case MEDIA_IOC_DEVICE_INFO:
		ret = media_device_get_info(dev,
				(struct media_device_info __user *)arg);
		break;

410 411 412 413 414 415 416 417 418 419 420 421
	case MEDIA_IOC_ENUM_ENTITIES:
		ret = media_device_enum_entities(dev,
				(struct media_entity_desc __user *)arg);
		break;

	case MEDIA_IOC_ENUM_LINKS:
		mutex_lock(&dev->graph_mutex);
		ret = media_device_enum_links(dev,
				(struct media_links_enum __user *)arg);
		mutex_unlock(&dev->graph_mutex);
		break;

L
Laurent Pinchart 已提交
422 423 424 425 426 427 428
	case MEDIA_IOC_SETUP_LINK:
		mutex_lock(&dev->graph_mutex);
		ret = media_device_setup_link(dev,
				(struct media_link_desc __user *)arg);
		mutex_unlock(&dev->graph_mutex);
		break;

429 430 431 432 433 434
	case MEDIA_IOC_G_TOPOLOGY:
		mutex_lock(&dev->graph_mutex);
		ret = media_device_get_topology(dev,
				(struct media_v2_topology __user *)arg);
		mutex_unlock(&dev->graph_mutex);
		break;
435

436 437 438 439 440 441 442
	default:
		ret = -ENOIOCTLCMD;
	}

	return ret;
}

443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
#ifdef CONFIG_COMPAT

struct media_links_enum32 {
	__u32 entity;
	compat_uptr_t pads; /* struct media_pad_desc * */
	compat_uptr_t links; /* struct media_link_desc * */
	__u32 reserved[4];
};

static long media_device_enum_links32(struct media_device *mdev,
				      struct media_links_enum32 __user *ulinks)
{
	struct media_links_enum links;
	compat_uptr_t pads_ptr, links_ptr;

	memset(&links, 0, sizeof(links));

	if (get_user(links.entity, &ulinks->entity)
	    || get_user(pads_ptr, &ulinks->pads)
	    || get_user(links_ptr, &ulinks->links))
		return -EFAULT;

	links.pads = compat_ptr(pads_ptr);
	links.links = compat_ptr(links_ptr);

	return __media_device_enum_links(mdev, &links);
}

#define MEDIA_IOC_ENUM_LINKS32		_IOWR('|', 0x02, struct media_links_enum32)

static long media_device_compat_ioctl(struct file *filp, unsigned int cmd,
				      unsigned long arg)
{
	struct media_devnode *devnode = media_devnode_data(filp);
	struct media_device *dev = to_media_device(devnode);
	long ret;

	switch (cmd) {
	case MEDIA_IOC_DEVICE_INFO:
	case MEDIA_IOC_ENUM_ENTITIES:
	case MEDIA_IOC_SETUP_LINK:
484
	case MEDIA_IOC_G_TOPOLOGY:
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
		return media_device_ioctl(filp, cmd, arg);

	case MEDIA_IOC_ENUM_LINKS32:
		mutex_lock(&dev->graph_mutex);
		ret = media_device_enum_links32(dev,
				(struct media_links_enum32 __user *)arg);
		mutex_unlock(&dev->graph_mutex);
		break;

	default:
		ret = -ENOIOCTLCMD;
	}

	return ret;
}
#endif /* CONFIG_COMPAT */

L
Laurent Pinchart 已提交
502 503
static const struct media_file_operations media_device_fops = {
	.owner = THIS_MODULE,
504 505
	.open = media_device_open,
	.ioctl = media_device_ioctl,
506 507 508
#ifdef CONFIG_COMPAT
	.compat_ioctl = media_device_compat_ioctl,
#endif /* CONFIG_COMPAT */
509
	.release = media_device_close,
L
Laurent Pinchart 已提交
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
};

/* -----------------------------------------------------------------------------
 * sysfs
 */

static ssize_t show_model(struct device *cd,
			  struct device_attribute *attr, char *buf)
{
	struct media_device *mdev = to_media_device(to_media_devnode(cd));

	return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model);
}

static DEVICE_ATTR(model, S_IRUGO, show_model, NULL);

/* -----------------------------------------------------------------------------
 * Registration/unregistration
 */

static void media_device_release(struct media_devnode *mdev)
{
532
	dev_dbg(mdev->parent, "Media device released\n");
L
Laurent Pinchart 已提交
533 534
}

535 536 537 538 539 540 541 542 543
/**
 * media_device_register_entity - Register an entity with a media device
 * @mdev:	The media device
 * @entity:	The entity
 */
int __must_check media_device_register_entity(struct media_device *mdev,
					      struct media_entity *entity)
{
	unsigned int i;
544
	int ret;
545 546 547 548 549 550 551 552 553 554 555 556 557 558

	if (entity->function == MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN ||
	    entity->function == MEDIA_ENT_F_UNKNOWN)
		dev_warn(mdev->dev,
			 "Entity type for entity %s was not initialized!\n",
			 entity->name);

	/* Warn if we apparently re-register an entity */
	WARN_ON(entity->graph_obj.mdev != NULL);
	entity->graph_obj.mdev = mdev;
	INIT_LIST_HEAD(&entity->links);
	entity->num_links = 0;
	entity->num_backlinks = 0;

559 560 561
	if (!ida_pre_get(&mdev->entity_internal_idx, GFP_KERNEL))
		return -ENOMEM;

562
	spin_lock(&mdev->lock);
563

564 565 566
	ret = ida_get_new_above(&mdev->entity_internal_idx, 1,
				&entity->internal_idx);
	if (ret < 0) {
567
		spin_unlock(&mdev->lock);
568
		return ret;
569 570 571 572 573
	}

	mdev->entity_internal_idx_max =
		max(mdev->entity_internal_idx_max, entity->internal_idx);

574 575 576 577 578 579 580 581 582 583 584 585 586 587
	/* Initialize media_gobj embedded at the entity */
	media_gobj_create(mdev, MEDIA_GRAPH_ENTITY, &entity->graph_obj);

	/* Initialize objects at the pads */
	for (i = 0; i < entity->num_pads; i++)
		media_gobj_create(mdev, MEDIA_GRAPH_PAD,
			       &entity->pads[i].graph_obj);

	spin_unlock(&mdev->lock);

	return 0;
}
EXPORT_SYMBOL_GPL(media_device_register_entity);

588
static void __media_device_unregister_entity(struct media_entity *entity)
589 590 591 592 593 594
{
	struct media_device *mdev = entity->graph_obj.mdev;
	struct media_link *link, *tmp;
	struct media_interface *intf;
	unsigned int i;

595 596
	ida_simple_remove(&mdev->entity_internal_idx, entity->internal_idx);

597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
	/* Remove all interface links pointing to this entity */
	list_for_each_entry(intf, &mdev->interfaces, graph_obj.list) {
		list_for_each_entry_safe(link, tmp, &intf->links, list) {
			if (link->entity == entity)
				__media_remove_intf_link(link);
		}
	}

	/* Remove all data links that belong to this entity */
	__media_entity_remove_links(entity);

	/* Remove all pads that belong to this entity */
	for (i = 0; i < entity->num_pads; i++)
		media_gobj_destroy(&entity->pads[i].graph_obj);

	/* Remove the entity */
	media_gobj_destroy(&entity->graph_obj);

	entity->graph_obj.mdev = NULL;
}
617 618 619 620 621 622 623 624 625 626 627 628

void media_device_unregister_entity(struct media_entity *entity)
{
	struct media_device *mdev = entity->graph_obj.mdev;

	if (mdev == NULL)
		return;

	spin_lock(&mdev->lock);
	__media_device_unregister_entity(entity);
	spin_unlock(&mdev->lock);
}
629 630
EXPORT_SYMBOL_GPL(media_device_unregister_entity);

L
Laurent Pinchart 已提交
631
/**
632
 * media_device_init() - initialize a media device
L
Laurent Pinchart 已提交
633 634 635 636 637 638 639 640
 * @mdev:	The media device
 *
 * The caller is responsible for initializing the media device before
 * registration. The following fields must be set:
 *
 * - dev must point to the parent device
 * - model must be filled with the device model name
 */
641
void media_device_init(struct media_device *mdev)
L
Laurent Pinchart 已提交
642
{
643
	INIT_LIST_HEAD(&mdev->entities);
644
	INIT_LIST_HEAD(&mdev->interfaces);
645 646
	INIT_LIST_HEAD(&mdev->pads);
	INIT_LIST_HEAD(&mdev->links);
647
	spin_lock_init(&mdev->lock);
648
	mutex_init(&mdev->graph_mutex);
649
	ida_init(&mdev->entity_internal_idx);
650

651 652 653 654 655 656
	dev_dbg(mdev->dev, "Media device initialized\n");
}
EXPORT_SYMBOL_GPL(media_device_init);

void media_device_cleanup(struct media_device *mdev)
{
657 658
	ida_destroy(&mdev->entity_internal_idx);
	mdev->entity_internal_idx_max = 0;
659 660 661 662 663 664 665 666 667
	mutex_destroy(&mdev->graph_mutex);
}
EXPORT_SYMBOL_GPL(media_device_cleanup);

int __must_check __media_device_register(struct media_device *mdev,
					 struct module *owner)
{
	int ret;

L
Laurent Pinchart 已提交
668 669 670 671
	/* Register the device node. */
	mdev->devnode.fops = &media_device_fops;
	mdev->devnode.parent = mdev->dev;
	mdev->devnode.release = media_device_release;
672 673 674 675

	/* Set version 0 to indicate user-space that the graph is static */
	mdev->topology_version = 0;

676
	ret = media_devnode_register(&mdev->devnode, owner);
L
Laurent Pinchart 已提交
677 678 679 680 681 682 683 684 685
	if (ret < 0)
		return ret;

	ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
	if (ret < 0) {
		media_devnode_unregister(&mdev->devnode);
		return ret;
	}

686 687
	dev_dbg(mdev->dev, "Media device registered\n");

L
Laurent Pinchart 已提交
688 689
	return 0;
}
690
EXPORT_SYMBOL_GPL(__media_device_register);
L
Laurent Pinchart 已提交
691 692 693

void media_device_unregister(struct media_device *mdev)
{
694 695
	struct media_entity *entity;
	struct media_entity *next;
696 697
	struct media_interface *intf, *tmp_intf;

698 699 700 701 702
	if (mdev == NULL)
		return;

	spin_lock(&mdev->lock);

703
	/* Check if mdev was ever registered at all */
704 705
	if (!media_devnode_is_registered(&mdev->devnode)) {
		spin_unlock(&mdev->lock);
706
		return;
707
	}
708

709 710
	/* Remove all entities from the media device */
	list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
711
		__media_device_unregister_entity(entity);
712

713 714 715 716
	/* Remove all interfaces from the media device */
	list_for_each_entry_safe(intf, tmp_intf, &mdev->interfaces,
				 graph_obj.list) {
		__media_remove_intf_links(intf);
717
		media_gobj_destroy(&intf->graph_obj);
718 719
		kfree(intf);
	}
720

721
	spin_unlock(&mdev->lock);
722

L
Laurent Pinchart 已提交
723 724
	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
	media_devnode_unregister(&mdev->devnode);
725 726

	dev_dbg(mdev->dev, "Media device unregistered\n");
L
Laurent Pinchart 已提交
727 728
}
EXPORT_SYMBOL_GPL(media_device_unregister);
729

730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754
static void media_device_release_devres(struct device *dev, void *res)
{
}

struct media_device *media_device_get_devres(struct device *dev)
{
	struct media_device *mdev;

	mdev = devres_find(dev, media_device_release_devres, NULL, NULL);
	if (mdev)
		return mdev;

	mdev = devres_alloc(media_device_release_devres,
				sizeof(struct media_device), GFP_KERNEL);
	if (!mdev)
		return NULL;
	return devres_get(dev, mdev, NULL, NULL);
}
EXPORT_SYMBOL_GPL(media_device_get_devres);

struct media_device *media_device_find_devres(struct device *dev)
{
	return devres_find(dev, media_device_release_devres, NULL, NULL);
}
EXPORT_SYMBOL_GPL(media_device_find_devres);
755 756

#endif /* CONFIG_MEDIA_CONTROLLER */