diff options
Diffstat (limited to 'drivers')
115 files changed, 2979 insertions, 1212 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 9264ea73b3be..a8518fb3bca7 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -97,7 +97,6 @@ config MEDIA_CONTROLLER config MEDIA_CONTROLLER_DVB bool "Enable Media controller for DVB (EXPERIMENTAL)" depends on MEDIA_CONTROLLER - depends on BROKEN ---help--- Enable the media controller API support for DVB. diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c index f4305ae800f4..d31f468830cf 100644 --- a/drivers/media/common/siano/smsdvb-main.c +++ b/drivers/media/common/siano/smsdvb-main.c @@ -617,6 +617,7 @@ static void smsdvb_media_device_unregister(struct smsdvb_client_t *client) if (!coredev->media_dev) return; media_device_unregister(coredev->media_dev); + media_device_cleanup(coredev->media_dev); kfree(coredev->media_dev); coredev->media_dev = NULL; #endif @@ -1183,7 +1184,11 @@ static int smsdvb_hotplug(struct smscore_device_t *coredev, if (smsdvb_debugfs_create(client) < 0) pr_info("failed to create debugfs node\n"); - dvb_create_media_graph(&client->adapter); + rc = dvb_create_media_graph(&client->adapter, true); + if (rc < 0) { + pr_err("dvb_create_media_graph failed %d\n", rc); + goto client_error; + } pr_info("DVB interface registered.\n"); return 0; diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c index ea9abde902e9..a168cbe1c998 100644 --- a/drivers/media/dvb-core/dmxdev.c +++ b/drivers/media/dvb-core/dmxdev.c @@ -1244,9 +1244,9 @@ int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter) } dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev, - DVB_DEVICE_DEMUX); + DVB_DEVICE_DEMUX, dmxdev->filternum); dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr, - dmxdev, DVB_DEVICE_DVR); + dmxdev, DVB_DEVICE_DVR, dmxdev->filternum); dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192); diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index fb66184dc9b6..f82cd1ff4f3a 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -1695,7 +1695,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, pubca->private = ca; /* register the DVB device */ - ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA); + ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA, 0); if (ret) goto free_slot_info; diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index b64f33776b74..40080645341e 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -622,7 +622,7 @@ static int dvb_enable_media_tuner(struct dvb_frontend *fe) struct media_device *mdev = adapter->mdev; struct media_entity *entity, *source; struct media_link *link, *found_link = NULL; - int i, ret, n_links = 0, active_links = 0; + int ret, n_links = 0, active_links = 0; fepriv->pipe_start_entity = NULL; @@ -632,8 +632,7 @@ static int dvb_enable_media_tuner(struct dvb_frontend *fe) entity = fepriv->dvbdev->entity; fepriv->pipe_start_entity = entity; - for (i = 0; i < entity->num_links; i++) { - link = &entity->links[i]; + list_for_each_entry(link, &entity->links, list) { if (link->sink->entity == entity) { found_link = link; n_links++; @@ -659,13 +658,11 @@ static int dvb_enable_media_tuner(struct dvb_frontend *fe) source = found_link->source->entity; fepriv->pipe_start_entity = source; - for (i = 0; i < source->num_links; i++) { + list_for_each_entry(link, &source->links, list) { struct media_entity *sink; int flags = 0; - link = &source->links[i]; sink = link->sink->entity; - if (sink == entity) flags = MEDIA_LNK_FL_ENABLED; @@ -2762,7 +2759,7 @@ int dvb_register_frontend(struct dvb_adapter* dvb, fe->dvb->num, fe->id, fe->ops.info.name); dvb_register_device (fe->dvb, &fepriv->dvbdev, &dvbdev_template, - fe, DVB_DEVICE_FRONTEND); + fe, DVB_DEVICE_FRONTEND, 0); /* * Initialize the cache to the proper values according with the diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index ce4332e80a91..ce6a711b42d4 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -1502,6 +1502,6 @@ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet, dvbnet->state[i] = 0; return dvb_register_device(adap, &dvbnet->dvbdev, &dvbdev_net, - dvbnet, DVB_DEVICE_NET); + dvbnet, DVB_DEVICE_NET, 0); } EXPORT_SYMBOL(dvb_net_init); diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c index 13bb57f0457f..560450a0b32a 100644 --- a/drivers/media/dvb-core/dvbdev.c +++ b/drivers/media/dvb-core/dvbdev.c @@ -34,6 +34,9 @@ #include <linux/mutex.h> #include "dvbdev.h" +/* Due to enum tuner_pad_index */ +#include <media/tuner.h> + static DEFINE_MUTEX(dvbdev_mutex); static int dvbdev_debug; @@ -180,102 +183,255 @@ skip: return -ENFILE; } -static void dvb_register_media_device(struct dvb_device *dvbdev, - int type, int minor) +static void dvb_media_device_free(struct dvb_device *dvbdev) { #if defined(CONFIG_MEDIA_CONTROLLER_DVB) - int ret = 0, npads; + if (dvbdev->entity) { + media_device_unregister_entity(dvbdev->entity); + kfree(dvbdev->entity); + kfree(dvbdev->pads); + dvbdev->entity = NULL; + dvbdev->pads = NULL; + } - if (!dvbdev->adapter->mdev) - return; + if (dvbdev->tsout_entity) { + int i; - dvbdev->entity = kzalloc(sizeof(*dvbdev->entity), GFP_KERNEL); - if (!dvbdev->entity) - return; + for (i = 0; i < dvbdev->tsout_num_entities; i++) { + media_device_unregister_entity(&dvbdev->tsout_entity[i]); + kfree(dvbdev->tsout_entity[i].name); + } + kfree(dvbdev->tsout_entity); + kfree(dvbdev->tsout_pads); + dvbdev->tsout_entity = NULL; + dvbdev->tsout_pads = NULL; - dvbdev->entity->info.dev.major = DVB_MAJOR; - dvbdev->entity->info.dev.minor = minor; - dvbdev->entity->name = dvbdev->name; + dvbdev->tsout_num_entities = 0; + } + + if (dvbdev->intf_devnode) { + media_devnode_remove(dvbdev->intf_devnode); + dvbdev->intf_devnode = NULL; + } + + if (dvbdev->adapter->conn) { + media_device_unregister_entity(dvbdev->adapter->conn); + dvbdev->adapter->conn = NULL; + kfree(dvbdev->adapter->conn_pads); + dvbdev->adapter->conn_pads = NULL; + } +#endif +} + +#if defined(CONFIG_MEDIA_CONTROLLER_DVB) +static int dvb_create_tsout_entity(struct dvb_device *dvbdev, + const char *name, int npads) +{ + int i, ret = 0; + + dvbdev->tsout_pads = kcalloc(npads, sizeof(*dvbdev->tsout_pads), + GFP_KERNEL); + if (!dvbdev->tsout_pads) + return -ENOMEM; + + dvbdev->tsout_entity = kcalloc(npads, sizeof(*dvbdev->tsout_entity), + GFP_KERNEL); + if (!dvbdev->tsout_entity) + return -ENOMEM; + + dvbdev->tsout_num_entities = npads; + + for (i = 0; i < npads; i++) { + struct media_pad *pads = &dvbdev->tsout_pads[i]; + struct media_entity *entity = &dvbdev->tsout_entity[i]; + + entity->name = kasprintf(GFP_KERNEL, "%s #%d", name, i); + if (!entity->name) + return -ENOMEM; + + entity->function = MEDIA_ENT_F_IO_DTV; + pads->flags = MEDIA_PAD_FL_SINK; + + ret = media_entity_pads_init(entity, 1, pads); + if (ret < 0) + return ret; + + ret = media_device_register_entity(dvbdev->adapter->mdev, + entity); + if (ret < 0) + return ret; + } + return 0; +} + +#define DEMUX_TSOUT "demux-tsout" +#define DVR_TSOUT "dvr-tsout" + +static int dvb_create_media_entity(struct dvb_device *dvbdev, + int type, int demux_sink_pads) +{ + int i, ret, npads; switch (type) { - case DVB_DEVICE_CA: - case DVB_DEVICE_DEMUX: case DVB_DEVICE_FRONTEND: npads = 2; break; - case DVB_DEVICE_NET: - npads = 0; + case DVB_DEVICE_DVR: + ret = dvb_create_tsout_entity(dvbdev, DVR_TSOUT, + demux_sink_pads); + return ret; + case DVB_DEVICE_DEMUX: + npads = 1 + demux_sink_pads; + ret = dvb_create_tsout_entity(dvbdev, DEMUX_TSOUT, + demux_sink_pads); + if (ret < 0) + return ret; + break; + case DVB_DEVICE_CA: + npads = 2; break; + case DVB_DEVICE_NET: + /* + * We should be creating entities for the MPE/ULE + * decapsulation hardware (or software implementation). + * + * However, the number of for the MPE/ULE decaps may not be + * fixed. As we don't have yet dynamic support for PADs at + * the Media Controller, let's not create the decap + * entities yet. + */ + return 0; default: - npads = 1; + return 0; } + dvbdev->entity = kzalloc(sizeof(*dvbdev->entity), GFP_KERNEL); + if (!dvbdev->entity) + return -ENOMEM; + + dvbdev->entity->name = dvbdev->name; + if (npads) { dvbdev->pads = kcalloc(npads, sizeof(*dvbdev->pads), GFP_KERNEL); - if (!dvbdev->pads) { - kfree(dvbdev->entity); - return; - } + if (!dvbdev->pads) + return -ENOMEM; } switch (type) { case DVB_DEVICE_FRONTEND: - dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_FE; + dvbdev->entity->function = MEDIA_ENT_F_DTV_DEMOD; dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK; dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE; break; case DVB_DEVICE_DEMUX: - dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_DEMUX; - dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK; - dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE; - break; - case DVB_DEVICE_DVR: - dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_DVR; + dvbdev->entity->function = MEDIA_ENT_F_TS_DEMUX; dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK; + for (i = 1; i < npads; i++) + dvbdev->pads[i].flags = MEDIA_PAD_FL_SOURCE; break; case DVB_DEVICE_CA: - dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_CA; + dvbdev->entity->function = MEDIA_ENT_F_DTV_CA; dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK; dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE; break; - case DVB_DEVICE_NET: - dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_NET; - break; default: + /* Should never happen, as the first switch prevents it */ kfree(dvbdev->entity); + kfree(dvbdev->pads); dvbdev->entity = NULL; - return; + dvbdev->pads = NULL; + return 0; } - if (npads) - ret = media_entity_init(dvbdev->entity, npads, dvbdev->pads, 0); - if (!ret) - ret = media_device_register_entity(dvbdev->adapter->mdev, - dvbdev->entity); - if (ret < 0) { - printk(KERN_ERR - "%s: media_device_register_entity failed for %s\n", - __func__, dvbdev->entity->name); - kfree(dvbdev->pads); - kfree(dvbdev->entity); - dvbdev->entity = NULL; - return; + if (npads) { + ret = media_entity_pads_init(dvbdev->entity, npads, dvbdev->pads); + if (ret) + return ret; } + ret = media_device_register_entity(dvbdev->adapter->mdev, + dvbdev->entity); + if (ret) + return (ret); - printk(KERN_DEBUG "%s: media device '%s' registered.\n", + printk(KERN_DEBUG "%s: media entity '%s' registered.\n", __func__, dvbdev->entity->name); + + return 0; +} +#endif + +static int dvb_register_media_device(struct dvb_device *dvbdev, + int type, int minor, + unsigned demux_sink_pads) +{ +#if defined(CONFIG_MEDIA_CONTROLLER_DVB) + struct media_link *link; + u32 intf_type; + int ret; + + if (!dvbdev->adapter->mdev) + return 0; + + ret = dvb_create_media_entity(dvbdev, type, demux_sink_pads); + if (ret) + return ret; + + switch (type) { + case DVB_DEVICE_FRONTEND: + intf_type = MEDIA_INTF_T_DVB_FE; + break; + case DVB_DEVICE_DEMUX: + intf_type = MEDIA_INTF_T_DVB_DEMUX; + break; + case DVB_DEVICE_DVR: + intf_type = MEDIA_INTF_T_DVB_DVR; + break; + case DVB_DEVICE_CA: + intf_type = MEDIA_INTF_T_DVB_CA; + break; + case DVB_DEVICE_NET: + intf_type = MEDIA_INTF_T_DVB_NET; + break; + default: + return 0; + } + + dvbdev->intf_devnode = media_devnode_create(dvbdev->adapter->mdev, + intf_type, 0, + DVB_MAJOR, minor); + + if (!dvbdev->intf_devnode) + return -ENOMEM; + + /* + * Create the "obvious" link, e. g. the ones that represent + * a direct association between an interface and an entity. + * Other links should be created elsewhere, like: + * DVB FE intf -> tuner + * DVB demux intf -> dvr + */ + + if (!dvbdev->entity) + return 0; + + link = media_create_intf_link(dvbdev->entity, &dvbdev->intf_devnode->intf, + MEDIA_LNK_FL_ENABLED); + if (!link) + return -ENOMEM; #endif + return 0; } int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, - const struct dvb_device *template, void *priv, int type) + const struct dvb_device *template, void *priv, int type, + int demux_sink_pads) { struct dvb_device *dvbdev; struct file_operations *dvbdevfops; struct device *clsdev; int minor; - int id; + int id, ret; mutex_lock(&dvbdev_register_lock); @@ -286,7 +442,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, return -ENFILE; } - *pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL); + *pdvbdev = dvbdev = kzalloc(sizeof(*dvbdev), GFP_KERNEL); if (!dvbdev){ mutex_unlock(&dvbdev_register_lock); @@ -335,6 +491,20 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, dvb_minors[minor] = dvbdev; up_write(&minor_rwsem); + ret = dvb_register_media_device(dvbdev, type, minor, demux_sink_pads); + if (ret) { + printk(KERN_ERR + "%s: dvb_register_media_device failed to create the mediagraph\n", + __func__); + + dvb_media_device_free(dvbdev); + kfree(dvbdevfops); + kfree(dvbdev); + up_write(&minor_rwsem); + mutex_unlock(&dvbdev_register_lock); + return ret; + } + mutex_unlock(&dvbdev_register_lock); clsdev = device_create(dvb_class, adap->device, @@ -348,8 +518,6 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n", adap->num, dnames[type], id, minor, minor); - dvb_register_media_device(dvbdev, type, minor); - return 0; } EXPORT_SYMBOL(dvb_register_device); @@ -364,15 +532,9 @@ void dvb_unregister_device(struct dvb_device *dvbdev) dvb_minors[dvbdev->minor] = NULL; up_write(&minor_rwsem); - device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor)); + dvb_media_device_free(dvbdev); -#if defined(CONFIG_MEDIA_CONTROLLER_DVB) - if (dvbdev->entity) { - media_device_unregister_entity(dvbdev->entity); - kfree(dvbdev->entity); - kfree(dvbdev->pads); - } -#endif + device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor)); list_del (&dvbdev->list_head); kfree (dvbdev->fops); @@ -382,46 +544,212 @@ EXPORT_SYMBOL(dvb_unregister_device); #ifdef CONFIG_MEDIA_CONTROLLER_DVB -void dvb_create_media_graph(struct dvb_adapter *adap) + +static int dvb_create_io_intf_links(struct dvb_adapter *adap, + struct media_interface *intf, + char *name) +{ + struct media_device *mdev = adap->mdev; + struct media_entity *entity; + struct media_link *link; + + media_device_for_each_entity(entity, mdev) { + if (entity->function == MEDIA_ENT_F_IO_DTV) { + if (strncmp(entity->name, name, strlen(name))) + continue; + link = media_create_intf_link(entity, intf, + MEDIA_LNK_FL_ENABLED); + if (!link) + return -ENOMEM; + } + } + return 0; +} + +int dvb_create_media_graph(struct dvb_adapter *adap, + bool create_rf_connector) { struct media_device *mdev = adap->mdev; - struct media_entity *entity, *tuner = NULL, *fe = NULL; - struct media_entity *demux = NULL, *dvr = NULL, *ca = NULL; + struct media_entity *entity, *tuner = NULL, *demod = NULL, *conn; + struct media_entity *demux = NULL, *ca = NULL; + struct media_link *link; + struct media_interface *intf; + unsigned demux_pad = 0; + unsigned dvr_pad = 0; + unsigned ntuner = 0, ndemod = 0; + int ret; + static const char *connector_name = "Television"; if (!mdev) - return; + return 0; media_device_for_each_entity(entity, mdev) { - switch (entity->type) { - case MEDIA_ENT_T_V4L2_SUBDEV_TUNER: + switch (entity->function) { + case MEDIA_ENT_F_TUNER: tuner = entity; + ntuner++; break; - case MEDIA_ENT_T_DEVNODE_DVB_FE: - fe = entity; + case MEDIA_ENT_F_DTV_DEMOD: + demod = entity; + ndemod++; break; - case MEDIA_ENT_T_DEVNODE_DVB_DEMUX: + case MEDIA_ENT_F_TS_DEMUX: demux = entity; break; - case MEDIA_ENT_T_DEVNODE_DVB_DVR: - dvr = entity; - break; - case MEDIA_ENT_T_DEVNODE_DVB_CA: + case MEDIA_ENT_F_DTV_CA: ca = entity; break; } } - if (tuner && fe) - media_entity_create_link(tuner, 0, fe, 0, 0); + /* + * Prepare to signalize to media_create_pad_links() that multiple + * entities of the same type exists and a 1:n or n:1 links need to be + * created. + * NOTE: if both tuner and demod have multiple instances, it is up + * to the caller driver to create such links. + */ + if (ntuner > 1) + tuner = NULL; + if (ndemod > 1) + demod = NULL; + + if (create_rf_connector) { + conn = kzalloc(sizeof(*conn), GFP_KERNEL); + if (!conn) + return -ENOMEM; + adap->conn = conn; + + adap->conn_pads = kcalloc(1, sizeof(*adap->conn_pads), + GFP_KERNEL); + if (!adap->conn_pads) + return -ENOMEM; + + conn->flags = MEDIA_ENT_FL_CONNECTOR; + conn->function = MEDIA_ENT_F_CONN_RF; + conn->name = connector_name; + adap->conn_pads->flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(conn, 1, adap->conn_pads); + if (ret) + return ret; + + ret = media_device_register_entity(mdev, conn); + if (ret) + return ret; + + if (!ntuner) + ret = media_create_pad_links(mdev, + MEDIA_ENT_F_CONN_RF, + conn, 0, + MEDIA_ENT_F_DTV_DEMOD, + demod, 0, + MEDIA_LNK_FL_ENABLED, + false); + else + ret = media_create_pad_links(mdev, + MEDIA_ENT_F_CONN_RF, + conn, 0, + MEDIA_ENT_F_TUNER, + tuner, TUNER_PAD_RF_INPUT, + MEDIA_LNK_FL_ENABLED, + false); + if (ret) + return ret; + } + + if (ntuner && ndemod) { + ret = media_create_pad_links(mdev, + MEDIA_ENT_F_TUNER, + tuner, TUNER_PAD_IF_OUTPUT, + MEDIA_ENT_F_DTV_DEMOD, + demod, 0, MEDIA_LNK_FL_ENABLED, + false); + if (ret) + return ret; + } + + if (ndemod && demux) { + ret = media_create_pad_links(mdev, + MEDIA_ENT_F_DTV_DEMOD, + demod, 1, + MEDIA_ENT_F_TS_DEMUX, + demux, 0, MEDIA_LNK_FL_ENABLED, + false); + if (ret) + return -ENOMEM; + } + if (demux && ca) { + ret = media_create_pad_link(demux, 1, ca, + 0, MEDIA_LNK_FL_ENABLED); + if (!ret) + return -ENOMEM; + } - if (fe && demux) - media_entity_create_link(fe, 1, demux, 0, MEDIA_LNK_FL_ENABLED); + /* Create demux links for each ringbuffer/pad */ + if (demux) { + media_device_for_each_entity(entity, mdev) { + if (entity->function == MEDIA_ENT_F_IO_DTV) { + if (!strncmp(entity->name, DVR_TSOUT, + strlen(DVR_TSOUT))) { + ret = media_create_pad_link(demux, + ++dvr_pad, + entity, 0, 0); + if (ret) + return ret; + } + if (!strncmp(entity->name, DEMUX_TSOUT, + strlen(DEMUX_TSOUT))) { + ret = media_create_pad_link(demux, + ++demux_pad, + entity, 0, 0); + if (ret) + return ret; + } + } + } + } - if (demux && dvr) - media_entity_create_link(demux, 1, dvr, 0, MEDIA_LNK_FL_ENABLED); + /* Create interface links for FE->tuner, DVR->demux and CA->ca */ + media_device_for_each_intf(intf, mdev) { + if (intf->type == MEDIA_INTF_T_DVB_CA && ca) { + link = media_create_intf_link(ca, intf, + MEDIA_LNK_FL_ENABLED); + if (!link) + return -ENOMEM; + } - if (demux && ca) - media_entity_create_link(demux, 1, ca, 0, MEDIA_LNK_FL_ENABLED); + if (intf->type == MEDIA_INTF_T_DVB_FE && tuner) { + link = media_create_intf_link(tuner, intf, + MEDIA_LNK_FL_ENABLED); + if (!link) + return -ENOMEM; + } +#if 0 + /* + * Indirect link - let's not create yet, as we don't know how + * to handle indirect links, nor if this will + * actually be needed. + */ + if (intf->type == MEDIA_INTF_T_DVB_DVR && demux) { + link = media_create_intf_link(demux, intf, + MEDIA_LNK_FL_ENABLED); + if (!link) + return -ENOMEM; + } +#endif + if (intf->type == MEDIA_INTF_T_DVB_DVR) { + ret = dvb_create_io_intf_links(adap, intf, DVR_TSOUT); + if (ret) + return ret; + } + if (intf->type == MEDIA_INTF_T_DVB_DEMUX) { + ret = dvb_create_io_intf_links(adap, intf, DEMUX_TSOUT); + if (ret) + return ret; + } + } + return 0; } EXPORT_SYMBOL_GPL(dvb_create_media_graph); #endif diff --git a/drivers/media/dvb-core/dvbdev.h b/drivers/media/dvb-core/dvbdev.h index 1069a776bbdb..4aff7bd3dea8 100644 --- a/drivers/media/dvb-core/dvbdev.h +++ b/drivers/media/dvb-core/dvbdev.h @@ -75,6 +75,9 @@ struct dvb_frontend; * used. * @mdev: pointer to struct media_device, used when the media * controller is used. + * @conn: RF connector. Used only if the device has no separate + * tuner. + * @conn_pads: pointer to struct media_pad associated with @conn; */ struct dvb_adapter { int num; @@ -94,6 +97,8 @@ struct dvb_adapter { #if defined(CONFIG_MEDIA_CONTROLLER_DVB) struct media_device *mdev; + struct media_entity *conn; + struct media_pad *conn_pads; #endif }; @@ -120,6 +125,11 @@ struct dvb_adapter { * @entity: pointer to struct media_entity associated with the device node * @pads: pointer to struct media_pad associated with @entity; * @priv: private data + * @intf_devnode: Pointer to media_intf_devnode. Used by the dvbdev core to + * store the MC device node interface + * @tsout_num_entities: Number of Transport Stream output entities + * @tsout_entity: array with MC entities associated to each TS output node + * @tsout_pads: array with the source pads for each @tsout_entity * * This structure is used by the DVB core (frontend, CA, net, demux) in * order to create the device nodes. Usually, driver should not initialize @@ -148,8 +158,11 @@ struct dvb_device { const char *name; /* Allocated and filled inside dvbdev.c */ - struct media_entity *entity; - struct media_pad *pads; + struct media_intf_devnode *intf_devnode; + + unsigned tsout_num_entities; + struct media_entity *entity, *tsout_entity; + struct media_pad *pads, *tsout_pads; #endif void *priv; @@ -185,14 +198,18 @@ int dvb_unregister_adapter(struct dvb_adapter *adap); * stored * @template: Template used to create &pdvbdev; * @priv: private data - * @type: type of the device: DVB_DEVICE_SEC, DVB_DEVICE_FRONTEND, - * DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, DVB_DEVICE_CA, DVB_DEVICE_NET + * @type: type of the device: %DVB_DEVICE_SEC, %DVB_DEVICE_FRONTEND, + * %DVB_DEVICE_DEMUX, %DVB_DEVICE_DVR, %DVB_DEVICE_CA, + * %DVB_DEVICE_NET + * @demux_sink_pads: Number of demux outputs, to be used to create the TS + * outputs via the Media Controller. */ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, const struct dvb_device *template, void *priv, - int type); + int type, + int demux_sink_pads); /** * dvb_unregister_device - Unregisters a DVB device @@ -202,16 +219,43 @@ int dvb_register_device(struct dvb_adapter *adap, void dvb_unregister_device(struct dvb_device *dvbdev); #ifdef CONFIG_MEDIA_CONTROLLER_DVB -void dvb_create_media_graph(struct dvb_adapter *adap); +/** + * dvb_create_media_graph - Creates media graph for the Digital TV part of the + * device. + * + * @adap: pointer to struct dvb_adapter + * @create_rf_connector: if true, it creates the RF connector too + * + * This function checks all DVB-related functions at the media controller + * entities and creates the needed links for the media graph. It is + * capable of working with multiple tuners or multiple frontends, but it + * won't create links if the device has multiple tuners and multiple frontends + * or if the device has multiple muxes. In such case, the caller driver should + * manually create the remaining links. + */ +__must_check int dvb_create_media_graph(struct dvb_adapter *adap, + bool create_rf_connector); + static inline void dvb_register_media_controller(struct dvb_adapter *adap, struct media_device *mdev) { adap->mdev = mdev; } +static inline struct media_device +*dvb_get_media_controller(struct dvb_adapter *adap) +{ + return adap->mdev; +} #else -static inline void dvb_create_media_graph(struct dvb_adapter *adap) {} +static inline +int dvb_create_media_graph(struct dvb_adapter *adap, + bool create_rf_connector) +{ + return 0; +}; #define dvb_register_media_controller(a, b) {} +#define dvb_get_media_controller(a) NULL #endif int dvb_generic_open (struct inode *inode, struct file *file); diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index c8f13d8370e5..73612c5353d1 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -730,6 +730,9 @@ static int au8522_probe(struct i2c_client *client, struct v4l2_ctrl_handler *hdl; struct v4l2_subdev *sd; int instance; +#ifdef CONFIG_MEDIA_CONTROLLER + int ret; +#endif /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, @@ -758,6 +761,20 @@ static int au8522_probe(struct i2c_client *client, sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &au8522_ops); +#if defined(CONFIG_MEDIA_CONTROLLER) + + state->pads[AU8522_PAD_INPUT].flags = MEDIA_PAD_FL_SINK; + state->pads[AU8522_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; + state->pads[AU8522_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_ATV_DECODER; + + ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(state->pads), + state->pads); + if (ret < 0) { + v4l_info(client, "failed to initialize media entity!\n"); + return ret; + } +#endif hdl = &state->hdl; v4l2_ctrl_handler_init(hdl, 4); diff --git a/drivers/media/dvb-frontends/au8522_priv.h b/drivers/media/dvb-frontends/au8522_priv.h index ee330c61aa61..404a0cb0ed8d 100644 --- a/drivers/media/dvb-frontends/au8522_priv.h +++ b/drivers/media/dvb-frontends/au8522_priv.h @@ -39,6 +39,14 @@ #define AU8522_DIGITAL_MODE 1 #define AU8522_SUSPEND_MODE 2 +enum au8522_media_pads { + AU8522_PAD_INPUT, + AU8522_PAD_VID_OUT, + AU8522_PAD_VBI_OUT, + + AU8522_NUM_PADS +}; + struct au8522_state { struct i2c_client *c; struct i2c_adapter *i2c; @@ -68,6 +76,10 @@ struct au8522_state { u32 id; u32 rev; struct v4l2_ctrl_handler hdl; + +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_pad pads[AU8522_NUM_PADS]; +#endif }; /* These are routines shared by both the VSB/QAM demodulator and the analog diff --git a/drivers/media/firewire/firedtv-ci.c b/drivers/media/firewire/firedtv-ci.c index e63f582378bf..edbb30fdd9d9 100644 --- a/drivers/media/firewire/firedtv-ci.c +++ b/drivers/media/firewire/firedtv-ci.c @@ -241,7 +241,7 @@ int fdtv_ca_register(struct firedtv *fdtv) return -EFAULT; err = dvb_register_device(&fdtv->adapter, &fdtv->cadev, - &fdtv_ca, fdtv, DVB_DEVICE_CA); + &fdtv_ca, fdtv, DVB_DEVICE_CA, 0); if (stat.ca_application_info == 0) dev_err(fdtv->device, "CaApplicationInfo is not set\n"); diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 0494a7896aa2..788967dadd29 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -1158,7 +1158,7 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id * state->rgb_quantization_range_ctrl->is_private = true; state->pad.flags = MEDIA_PAD_FL_SINK; - err = media_entity_init(&sd->entity, 1, &state->pad, 0); + err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err) goto err_hdl; diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c index f00745bbe471..7e9cbf757e95 100644 --- a/drivers/media/i2c/adp1653.c +++ b/drivers/media/i2c/adp1653.c @@ -512,11 +512,11 @@ static int adp1653_probe(struct i2c_client *client, if (ret) goto free_and_quit; - ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); + ret = media_entity_pads_init(&flash->subdev.entity, 0, NULL); if (ret < 0) goto free_and_quit; - flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + flash->subdev.entity.function = MEDIA_ENT_F_FLASH; return 0; diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 3c3c4bfe3866..ff57c1dcb8af 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -1213,8 +1213,8 @@ static int adv7180_probe(struct i2c_client *client, goto err_unregister_vpp_client; state->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER; - ret = media_entity_init(&sd->entity, 1, &state->pad, 0); + sd->entity.flags |= MEDIA_ENT_F_ATV_DECODER; + ret = media_entity_pads_init(&sd->entity, 1, &state->pad); if (ret) goto err_free_ctrl; diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index eeb2cd823c4d..471fd23b5c5c 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -1482,7 +1482,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * state->rgb_quantization_range_ctrl->is_private = true; state->pad.flags = MEDIA_PAD_FL_SINK; - err = media_entity_init(&sd->entity, 1, &state->pad, 0); + err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err) goto err_hdl; diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 745286225655..f8dd7505b529 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -3208,8 +3208,8 @@ static int adv76xx_probe(struct i2c_client *client, state->pads[i].flags = MEDIA_PAD_FL_SINK; state->pads[state->source_pad].flags = MEDIA_PAD_FL_SOURCE; - err = media_entity_init(&sd->entity, state->source_pad + 1, - state->pads, 0); + err = media_entity_pads_init(&sd->entity, state->source_pad + 1, + state->pads); if (err) goto err_work_queues; diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 69378e4914b6..5fbb788e7b59 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -3309,7 +3309,7 @@ static int adv7842_probe(struct i2c_client *client, adv7842_delayed_work_enable_hotplug); state->pad.flags = MEDIA_PAD_FL_SOURCE; - err = media_entity_init(&sd->entity, 1, &state->pad, 0); + err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err) goto err_work_queues; diff --git a/drivers/media/i2c/as3645a.c b/drivers/media/i2c/as3645a.c index 29a2e7034aa6..2e90e4094b79 100644 --- a/drivers/media/i2c/as3645a.c +++ b/drivers/media/i2c/as3645a.c @@ -827,11 +827,11 @@ static int as3645a_probe(struct i2c_client *client, if (ret < 0) goto done; - ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); + ret = media_entity_pads_init(&flash->subdev.entity, 0, NULL); if (ret < 0) goto done; - flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + flash->subdev.entity.function = MEDIA_ENT_F_FLASH; mutex_init(&flash->power_lock); diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index f2e2c34ddbbd..07a3e7173144 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -5211,10 +5211,10 @@ static int cx25840_probe(struct i2c_client *client, state->pads[CX25840_PAD_INPUT].flags = MEDIA_PAD_FL_SINK; state->pads[CX25840_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; state->pads[CX25840_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_DECODER; + sd->entity.function = MEDIA_ENT_F_ATV_DECODER; - ret = media_entity_init(&sd->entity, ARRAY_SIZE(state->pads), - state->pads, 0); + ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(state->pads), + state->pads); if (ret < 0) { v4l_info(client, "failed to initialize media entity!\n"); return ret; diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index 19ecb8801064..251a2aaf98c3 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -365,10 +365,10 @@ static int lm3560_subdev_init(struct lm3560_flash *flash, rval = lm3560_init_controls(flash, led_no); if (rval) goto err_out; - rval = media_entity_init(&flash->subdev_led[led_no].entity, 0, NULL, 0); + rval = media_entity_pads_init(&flash->subdev_led[led_no].entity, 0, NULL); if (rval < 0) goto err_out; - flash->subdev_led[led_no].entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + flash->subdev_led[led_no].entity.function = MEDIA_ENT_F_FLASH; return rval; diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c index 7fbe6ff1c4f4..7e9967af36ec 100644 --- a/drivers/media/i2c/lm3646.c +++ b/drivers/media/i2c/lm3646.c @@ -282,10 +282,10 @@ static int lm3646_subdev_init(struct lm3646_flash *flash) rval = lm3646_init_controls(flash); if (rval) goto err_out; - rval = media_entity_init(&flash->subdev_led.entity, 0, NULL, 0); + rval = media_entity_pads_init(&flash->subdev_led.entity, 0, NULL); if (rval < 0) goto err_out; - flash->subdev_led.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + flash->subdev_led.entity.function = MEDIA_ENT_F_FLASH; return rval; err_out: diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c index f8993933416e..acb804bceccb 100644 --- a/drivers/media/i2c/m5mols/m5mols_core.c +++ b/drivers/media/i2c/m5mols/m5mols_core.c @@ -975,10 +975,10 @@ static int m5mols_probe(struct i2c_client *client, sd->internal_ops = &m5mols_subdev_internal_ops; info->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, 1, &info->pad, 0); + ret = media_entity_pads_init(&sd->entity, 1, &info->pad); if (ret < 0) return ret; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; init_waitqueue_head(&info->irq_waitq); mutex_init(&info->lock); diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c index 101cb26f9330..da076796999e 100644 --- a/drivers/media/i2c/mt9m032.c +++ b/drivers/media/i2c/mt9m032.c @@ -799,7 +799,7 @@ static int mt9m032_probe(struct i2c_client *client, sensor->subdev.ctrl_handler = &sensor->ctrls; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0); + ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad); if (ret < 0) goto error_ctrl; diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index a3da0e977d0b..237737fec09c 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -1112,7 +1112,7 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops; mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0); + ret = media_entity_pads_init(&mt9p031->subdev.entity, 1, &mt9p031->pad); if (ret < 0) goto done; diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c index b28fdff1d310..702d562f8e39 100644 --- a/drivers/media/i2c/mt9t001.c +++ b/drivers/media/i2c/mt9t001.c @@ -933,7 +933,7 @@ static int mt9t001_probe(struct i2c_client *client, mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&mt9t001->subdev.entity, 1, &mt9t001->pad, 0); + ret = media_entity_pads_init(&mt9t001->subdev.entity, 1, &mt9t001->pad); done: if (ret < 0) { diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 1dbbd23fdfb0..2e1d116a64e7 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -1046,7 +1046,7 @@ static int mt9v032_probe(struct i2c_client *client, mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0); + ret = media_entity_pads_init(&mt9v032->subdev.entity, 1, &mt9v032->pad); if (ret < 0) goto err; diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c index 69e4f3031d8b..30cb90b88d75 100644 --- a/drivers/media/i2c/noon010pc30.c +++ b/drivers/media/i2c/noon010pc30.c @@ -779,8 +779,8 @@ static int noon010_probe(struct i2c_client *client, goto np_err; info->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &info->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &info->pad); if (ret < 0) goto np_err; diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 82c7ac1cc88e..02b9a3440557 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1445,8 +1445,8 @@ static int ov2659_probe(struct i2c_client *client, #if defined(CONFIG_MEDIA_CONTROLLER) ov2659->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &ov2659->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov2659->pad); if (ret < 0) { v4l2_ctrl_handler_free(&ov2659->ctrls); return ret; diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index 9fe9006474b2..a0b3c9bde53d 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -1500,8 +1500,8 @@ static int ov965x_probe(struct i2c_client *client, return ret; ov965x->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &ov965x->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov965x->pad); if (ret < 0) return ret; diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index 25f5e79dc9bc..57b3d27993a4 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1482,11 +1482,11 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd) return ret; } - ret = media_entity_create_link(&state->sensor_sd.entity, + ret = media_create_pad_link(&state->sensor_sd.entity, S5C73M3_ISP_PAD, &state->oif_sd.entity, OIF_ISP_PAD, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); - ret = media_entity_create_link(&state->sensor_sd.entity, + ret = media_create_pad_link(&state->sensor_sd.entity, S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); @@ -1688,10 +1688,10 @@ static int s5c73m3_probe(struct i2c_client *client, state->sensor_pads[S5C73M3_JPEG_PAD].flags = MEDIA_PAD_FL_SOURCE; state->sensor_pads[S5C73M3_ISP_PAD].flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; - ret = media_entity_init(&sd->entity, S5C73M3_NUM_PADS, - state->sensor_pads, 0); + ret = media_entity_pads_init(&sd->entity, S5C73M3_NUM_PADS, + state->sensor_pads); if (ret < 0) return ret; @@ -1704,10 +1704,10 @@ static int s5c73m3_probe(struct i2c_client *client, state->oif_pads[OIF_ISP_PAD].flags = MEDIA_PAD_FL_SINK; state->oif_pads[OIF_JPEG_PAD].flags = MEDIA_PAD_FL_SINK; state->oif_pads[OIF_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE; - oif_sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + oif_sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; - ret = media_entity_init(&oif_sd->entity, OIF_NUM_PADS, - state->oif_pads, 0); + ret = media_entity_pads_init(&oif_sd->entity, OIF_NUM_PADS, + state->oif_pads); if (ret < 0) return ret; diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c index 6757aca2cdab..8a0f22da590f 100644 --- a/drivers/media/i2c/s5k4ecgx.c +++ b/drivers/media/i2c/s5k4ecgx.c @@ -961,8 +961,8 @@ static int s5k4ecgx_probe(struct i2c_client *client, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; priv->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &priv->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &priv->pad); if (ret) return ret; diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 774e0d0c94cb..fc3a5a8e6c9c 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -408,7 +408,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) static inline bool s5k5baf_is_cis_subdev(struct v4l2_subdev *sd) { - return sd->entity.type == MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + return sd->entity.function == MEDIA_ENT_F_CAM_SENSOR; } static inline struct s5k5baf *to_s5k5baf(struct v4l2_subdev *sd) @@ -1756,7 +1756,7 @@ static int s5k5baf_registered(struct v4l2_subdev *sd) v4l2_err(sd, "failed to register subdev %s\n", state->cis_sd.name); else - ret = media_entity_create_link(&state->cis_sd.entity, PAD_CIS, + ret = media_create_pad_link(&state->cis_sd.entity, PAD_CIS, &state->sd.entity, PAD_CIS, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); @@ -1904,8 +1904,8 @@ static int s5k5baf_configure_subdevs(struct s5k5baf *state, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; state->cis_pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, NUM_CIS_PADS, &state->cis_pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, NUM_CIS_PADS, &state->cis_pad); if (ret < 0) goto err; @@ -1919,8 +1919,8 @@ static int s5k5baf_configure_subdevs(struct s5k5baf *state, state->pads[PAD_CIS].flags = MEDIA_PAD_FL_SINK; state->pads[PAD_OUT].flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; - ret = media_entity_init(&sd->entity, NUM_ISP_PADS, state->pads, 0); + sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; + ret = media_entity_pads_init(&sd->entity, NUM_ISP_PADS, state->pads); if (!ret) return 0; diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c index b1b1574dfb95..b9e43ffa5085 100644 --- a/drivers/media/i2c/s5k6a3.c +++ b/drivers/media/i2c/s5k6a3.c @@ -333,7 +333,7 @@ static int s5k6a3_probe(struct i2c_client *client, sensor->format.height = S5K6A3_DEFAULT_HEIGHT; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0); + ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad); if (ret < 0) return ret; diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c index 60aaff7190d2..faee11383cb7 100644 --- a/drivers/media/i2c/s5k6aa.c +++ b/drivers/media/i2c/s5k6aa.c @@ -1577,8 +1577,8 @@ static int s5k6aa_probe(struct i2c_client *client, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; - ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0); + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &s5k6aa->pad); if (ret) return ret; diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index fb39dfd55e75..a215efe7a8ba 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2487,31 +2487,31 @@ static int smiapp_register_subdevs(struct smiapp_sensor *sensor) if (!last) continue; - rval = media_entity_init(&this->sd.entity, - this->npads, this->pads, 0); + rval = media_entity_pads_init(&this->sd.entity, + this->npads, this->pads); if (rval) { dev_err(&client->dev, - "media_entity_init failed\n"); + "media_entity_pads_init failed\n"); return rval; } - rval = media_entity_create_link(&this->sd.entity, - this->source_pad, - &last->sd.entity, - last->sink_pad, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); + rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, + &this->sd); if (rval) { dev_err(&client->dev, - "media_entity_create_link failed\n"); + "v4l2_device_register_subdev failed\n"); return rval; } - rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, - &this->sd); + rval = media_create_pad_link(&this->sd.entity, + this->source_pad, + &last->sd.entity, + last->sink_pad, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (rval) { dev_err(&client->dev, - "v4l2_device_register_subdev failed\n"); + "media_create_pad_link failed\n"); return rval; } } @@ -2763,7 +2763,7 @@ static int smiapp_init(struct smiapp_sensor *sensor) dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile); - sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + sensor->pixel_array->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; /* final steps */ smiapp_read_frame_fmt(sensor); @@ -3077,8 +3077,8 @@ static int smiapp_probe(struct i2c_client *client, sensor->src->sensor = sensor; sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE; - rval = media_entity_init(&sensor->src->sd.entity, 2, - sensor->src->pads, 0); + rval = media_entity_pads_init(&sensor->src->sd.entity, 2, + sensor->src->pads); if (rval < 0) return rval; diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 77b801152ea5..3397eb99c67b 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1889,7 +1889,7 @@ static int tc358743_probe(struct i2c_client *client, } state->pad.flags = MEDIA_PAD_FL_SOURCE; - err = media_entity_init(&sd->entity, 1, &state->pad, 0); + err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err < 0) goto err_hdl; diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index b5dba5b7ce3a..7fa5f1e4fe37 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -1095,9 +1095,9 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) #if defined(CONFIG_MEDIA_CONTROLLER) decoder->pad.flags = MEDIA_PAD_FL_SOURCE; decoder->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - decoder->sd.entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER; + decoder->sd.entity.flags |= MEDIA_ENT_F_ATV_DECODER; - ret = media_entity_init(&decoder->sd.entity, 1, &decoder->pad, 0); + ret = media_entity_pads_init(&decoder->sd.entity, 1, &decoder->pad); if (ret < 0) { v4l2_err(sd, "%s decoder driver failed to register !!\n", sd->name); diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index 772a3043ae3b..83c79fa5f61d 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -1012,9 +1012,9 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) #if defined(CONFIG_MEDIA_CONTROLLER) device->pad.flags = MEDIA_PAD_FL_SOURCE; device->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - device->sd.entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER; + device->sd.entity.flags |= MEDIA_ENT_F_ATV_DECODER; - error = media_entity_init(&device->sd.entity, 1, &device->pad, 0); + error = media_entity_pads_init(&device->sd.entity, 1, &device->pad); if (error < 0) return error; #endif diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 7b39440192d6..7dae0ac0f3ae 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -22,14 +22,18 @@ #include <linux/compat.h> #include <linux/export.h> +#include <linux/idr.h> #include <linux/ioctl.h> #include <linux/media.h> +#include <linux/slab.h> #include <linux/types.h> #include <media/media-device.h> #include <media/media-devnode.h> #include <media/media-entity.h> +#ifdef CONFIG_MEDIA_CONTROLLER + /* ----------------------------------------------------------------------------- * Userspace API */ @@ -75,8 +79,8 @@ static struct media_entity *find_entity(struct media_device *mdev, u32 id) spin_lock(&mdev->lock); media_device_for_each_entity(entity, mdev) { - if ((entity->id == id && !next) || - (entity->id > id && next)) { + if (((media_entity_id(entity) == id) && !next) || + ((media_entity_id(entity) > id) && next)) { spin_unlock(&mdev->lock); return entity; } @@ -102,13 +106,13 @@ static long media_device_enum_entities(struct media_device *mdev, if (ent == NULL) return -EINVAL; - u_ent.id = ent->id; + u_ent.id = media_entity_id(ent); if (ent->name) strlcpy(u_ent.name, ent->name, sizeof(u_ent.name)); - u_ent.type = ent->type; - u_ent.revision = ent->revision; + u_ent.type = ent->function; + u_ent.revision = 0; /* Unused */ u_ent.flags = ent->flags; - u_ent.group_id = ent->group_id; + u_ent.group_id = 0; /* Unused */ u_ent.pads = ent->num_pads; u_ent.links = ent->num_links - ent->num_backlinks; memcpy(&u_ent.raw, &ent->info, sizeof(ent->info)); @@ -120,7 +124,7 @@ static long media_device_enum_entities(struct media_device *mdev, static void media_device_kpad_to_upad(const struct media_pad *kpad, struct media_pad_desc *upad) { - upad->entity = kpad->entity->id; + upad->entity = media_entity_id(kpad->entity); upad->index = kpad->index; upad->flags = kpad->flags; } @@ -148,25 +152,25 @@ static long __media_device_enum_links(struct media_device *mdev, } if (links->links) { - struct media_link_desc __user *ulink; - unsigned int l; + struct media_link *link; + struct media_link_desc __user *ulink_desc = links->links; - for (l = 0, ulink = links->links; l < entity->num_links; l++) { - struct media_link_desc link; + list_for_each_entry(link, &entity->links, list) { + struct media_link_desc klink_desc; /* Ignore backlinks. */ - if (entity->links[l].source->entity != entity) + if (link->source->entity != entity) continue; - - memset(&link, 0, sizeof(link)); - media_device_kpad_to_upad(entity->links[l].source, - &link.source); - media_device_kpad_to_upad(entity->links[l].sink, - &link.sink); - link.flags = entity->links[l].flags; - if (copy_to_user(ulink, &link, sizeof(*ulink))) + 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))) return -EFAULT; - ulink++; + ulink_desc++; } } @@ -230,6 +234,164 @@ static long media_device_setup_link(struct media_device *mdev, return ret; } +#if 0 /* Let's postpone it to Kernel 4.6 */ +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; + struct media_v2_entity kentity, *uentity; + struct media_v2_interface kintf, *uintf; + struct media_v2_pad kpad, *upad; + struct media_v2_link klink, *ulink; + unsigned int i; + int ret = 0; + + topo->topology_version = mdev->topology_version; + + /* Get entities and number of entities */ + i = 0; + uentity = media_get_uptr(topo->ptr_entities); + media_device_for_each_entity(entity, mdev) { + i++; + if (ret || !uentity) + continue; + + if (i > topo->num_entities) { + ret = -ENOSPC; + continue; + } + + /* Copy fields to userspace struct if not error */ + memset(&kentity, 0, sizeof(kentity)); + kentity.id = entity->graph_obj.id; + kentity.function = entity->function; + strncpy(kentity.name, entity->name, + sizeof(kentity.name)); + + if (copy_to_user(uentity, &kentity, sizeof(kentity))) + ret = -EFAULT; + uentity++; + } + topo->num_entities = i; + + /* Get interfaces and number of interfaces */ + i = 0; + uintf = media_get_uptr(topo->ptr_interfaces); + media_device_for_each_intf(intf, mdev) { + i++; + if (ret || !uintf) + continue; + + if (i > topo->num_interfaces) { + ret = -ENOSPC; + continue; + } + + memset(&kintf, 0, sizeof(kintf)); + + /* Copy intf fields to userspace struct */ + kintf.id = intf->graph_obj.id; + kintf.intf_type = intf->type; + kintf.flags = intf->flags; + + if (media_type(&intf->graph_obj) == MEDIA_GRAPH_INTF_DEVNODE) { + struct media_intf_devnode *devnode; + + devnode = intf_to_devnode(intf); + + kintf.devnode.major = devnode->major; + kintf.devnode.minor = devnode->minor; + } + + if (copy_to_user(uintf, &kintf, sizeof(kintf))) + ret = -EFAULT; + uintf++; + } + topo->num_interfaces = i; + + /* Get pads and number of pads */ + i = 0; + upad = media_get_uptr(topo->ptr_pads); + media_device_for_each_pad(pad, mdev) { + i++; + if (ret || !upad) + continue; + + if (i > topo->num_pads) { + ret = -ENOSPC; + continue; + } + + memset(&kpad, 0, sizeof(kpad)); + + /* Copy pad fields to userspace struct */ + kpad.id = pad->graph_obj.id; + kpad.entity_id = pad->entity->graph_obj.id; + kpad.flags = pad->flags; + + if (copy_to_user(upad, &kpad, sizeof(kpad))) + ret = -EFAULT; + upad++; + } + topo->num_pads = i; + + /* Get links and number of links */ + i = 0; + ulink = media_get_uptr(topo->ptr_links); + media_device_for_each_link(link, mdev) { + if (link->is_backlink) + continue; + + i++; + + if (ret || !ulink) + continue; + + if (i > topo->num_links) { + ret = -ENOSPC; + continue; + } + + memset(&klink, 0, sizeof(klink)); + + /* Copy link fields to userspace struct */ + klink.id = link->graph_obj.id; + klink.source_id = link->gobj0->id; + klink.sink_id = link->gobj1->id; + klink.flags = link->flags; + + if (copy_to_user(ulink, &klink, sizeof(klink))) + ret = -EFAULT; + ulink++; + } + 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; + + if (copy_from_user(&ktopo, utopo, sizeof(ktopo))) + return -EFAULT; + + ret = __media_device_get_topology(mdev, &ktopo); + if (ret < 0) + return ret; + + if (copy_to_user(utopo, &ktopo, sizeof(*utopo))) + return -EFAULT; + + return 0; +} +#endif + static long media_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -262,6 +424,14 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, mutex_unlock(&dev->graph_mutex); break; +#if 0 /* Let's postpone it to Kernel 4.6 */ + 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; +#endif default: ret = -ENOIOCTLCMD; } @@ -310,6 +480,9 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd, case MEDIA_IOC_DEVICE_INFO: case MEDIA_IOC_ENUM_ENTITIES: case MEDIA_IOC_SETUP_LINK: +#if 0 /* Let's postpone it to Kernel 4.6 */ + case MEDIA_IOC_G_TOPOLOGY: +#endif return media_device_ioctl(filp, cmd, arg); case MEDIA_IOC_ENUM_LINKS32: @@ -357,10 +530,107 @@ static DEVICE_ATTR(model, S_IRUGO, show_model, NULL); static void media_device_release(struct media_devnode *mdev) { + dev_dbg(mdev->parent, "Media device released\n"); +} + +/** + * 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; + int ret; + + 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; + + if (!ida_pre_get(&mdev->entity_internal_idx, GFP_KERNEL)) + return -ENOMEM; + + spin_lock(&mdev->lock); + + ret = ida_get_new_above(&mdev->entity_internal_idx, 1, + &entity->internal_idx); + if (ret < 0) { + spin_unlock(&mdev->lock); + return ret; + } + + mdev->entity_internal_idx_max = + max(mdev->entity_internal_idx_max, entity->internal_idx); + + /* 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); + +static void __media_device_unregister_entity(struct media_entity *entity) +{ + struct media_device *mdev = entity->graph_obj.mdev; + struct media_link *link, *tmp; + struct media_interface *intf; + unsigned int i; + + ida_simple_remove(&mdev->entity_internal_idx, entity->internal_idx); + + /* 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; } +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); +} +EXPORT_SYMBOL_GPL(media_device_unregister_entity); + /** - * media_device_register - register a media device + * media_device_init() - initialize a media device * @mdev: The media device * * The caller is responsible for initializing the media device before @@ -369,23 +639,41 @@ static void media_device_release(struct media_devnode *mdev) * - dev must point to the parent device * - model must be filled with the device model name */ -int __must_check __media_device_register(struct media_device *mdev, - struct module *owner) +void media_device_init(struct media_device *mdev) { - int ret; - - if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0)) - return -EINVAL; - - mdev->entity_id = 1; INIT_LIST_HEAD(&mdev->entities); + INIT_LIST_HEAD(&mdev->interfaces); + INIT_LIST_HEAD(&mdev->pads); + INIT_LIST_HEAD(&mdev->links); spin_lock_init(&mdev->lock); mutex_init(&mdev->graph_mutex); + ida_init(&mdev->entity_internal_idx); + + dev_dbg(mdev->dev, "Media device initialized\n"); +} +EXPORT_SYMBOL_GPL(media_device_init); + +void media_device_cleanup(struct media_device *mdev) +{ + ida_destroy(&mdev->entity_internal_idx); + mdev->entity_internal_idx_max = 0; + 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; /* Register the device node. */ mdev->devnode.fops = &media_device_fops; mdev->devnode.parent = mdev->dev; mdev->devnode.release = media_device_release; + + /* Set version 0 to indicate user-space that the graph is static */ + mdev->topology_version = 0; + ret = media_devnode_register(&mdev->devnode, owner); if (ret < 0) return ret; @@ -396,69 +684,74 @@ int __must_check __media_device_register(struct media_device *mdev, return ret; } + dev_dbg(mdev->dev, "Media device registered\n"); + return 0; } EXPORT_SYMBOL_GPL(__media_device_register); -/** - * media_device_unregister - unregister a media device - * @mdev: The media device - * - */ void media_device_unregister(struct media_device *mdev) { struct media_entity *entity; struct media_entity *next; + struct media_interface *intf, *tmp_intf; - list_for_each_entry_safe(entity, next, &mdev->entities, list) - media_device_unregister_entity(entity); + if (mdev == NULL) + return; + + spin_lock(&mdev->lock); + + /* Check if mdev was ever registered at all */ + if (!media_devnode_is_registered(&mdev->devnode)) { + spin_unlock(&mdev->lock); + return; + } + + /* Remove all entities from the media device */ + list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list) + __media_device_unregister_entity(entity); + + /* 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); + media_gobj_destroy(&intf->graph_obj); + kfree(intf); + } + + spin_unlock(&mdev->lock); device_remove_file(&mdev->devnode.dev, &dev_attr_model); media_devnode_unregister(&mdev->devnode); + + dev_dbg(mdev->dev, "Media device unregistered\n"); } EXPORT_SYMBOL_GPL(media_device_unregister); -/** - * 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) +static void media_device_release_devres(struct device *dev, void *res) { - /* Warn if we apparently re-register an entity */ - WARN_ON(entity->parent != NULL); - entity->parent = mdev; - - spin_lock(&mdev->lock); - if (entity->id == 0) - entity->id = mdev->entity_id++; - else - mdev->entity_id = max(entity->id + 1, mdev->entity_id); - list_add_tail(&entity->list, &mdev->entities); - spin_unlock(&mdev->lock); - - return 0; } -EXPORT_SYMBOL_GPL(media_device_register_entity); -/** - * media_device_unregister_entity - Unregister an entity - * @entity: The entity - * - * If the entity has never been registered this function will return - * immediately. - */ -void media_device_unregister_entity(struct media_entity *entity) +struct media_device *media_device_get_devres(struct device *dev) { - struct media_device *mdev = entity->parent; + struct media_device *mdev; - if (mdev == NULL) - return; + mdev = devres_find(dev, media_device_release_devres, NULL, NULL); + if (mdev) + return mdev; - spin_lock(&mdev->lock); - list_del(&entity->list); - spin_unlock(&mdev->lock); - entity->parent = NULL; + 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_unregister_entity); +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); + +#endif /* CONFIG_MEDIA_CONTROLLER */ diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index ebf9626e5ae5..cea35bf20011 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -217,20 +217,6 @@ static const struct file_operations media_devnode_fops = { .llseek = no_llseek, }; -/** - * media_devnode_register - register a media device node - * @mdev: media device node structure we want to register - * - * The registration code assigns minor numbers and registers the new device node - * with the kernel. An error is returned if no free minor number can be found, - * or if the registration of the device node fails. - * - * Zero is returned on success. - * - * Note that if the media_devnode_register call fails, the release() callback of - * the media_devnode structure is *not* called, so the caller is responsible for - * freeing any data. - */ int __must_check media_devnode_register(struct media_devnode *mdev, struct module *owner) { @@ -285,16 +271,6 @@ error: return ret; } -/** - * media_devnode_unregister - unregister a media device node - * @mdev: the device node to unregister - * - * This unregisters the passed device. Future open calls will be met with - * errors. - * - * This function can safely be called if the device node has never been - * registered or has already been unregistered. - */ void media_devnode_unregister(struct media_devnode *mdev) { /* Check if mdev was ever registered at all */ diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 767fe55ba08e..e89d85a7d31b 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -26,65 +26,198 @@ #include <media/media-entity.h> #include <media/media-device.h> +static inline const char *gobj_type(enum media_gobj_type type) +{ + switch (type) { + case MEDIA_GRAPH_ENTITY: + return "entity"; + case MEDIA_GRAPH_PAD: + return "pad"; + case MEDIA_GRAPH_LINK: + return "link"; + case MEDIA_GRAPH_INTF_DEVNODE: + return "intf-devnode"; + default: + return "unknown"; + } +} + +static inline const char *intf_type(struct media_interface *intf) +{ + switch (intf->type) { + case MEDIA_INTF_T_DVB_FE: + return "frontend"; + case MEDIA_INTF_T_DVB_DEMUX: + return "demux"; + case MEDIA_INTF_T_DVB_DVR: + return "DVR"; + case MEDIA_INTF_T_DVB_CA: + return "CA"; + case MEDIA_INTF_T_DVB_NET: + return "dvbnet"; + case MEDIA_INTF_T_V4L_VIDEO: + return "video"; + case MEDIA_INTF_T_V4L_VBI: + return "vbi"; + case MEDIA_INTF_T_V4L_RADIO: + return "radio"; + case MEDIA_INTF_T_V4L_SUBDEV: + return "v4l2-subdev"; + case MEDIA_INTF_T_V4L_SWRADIO: + return "swradio"; + default: + return "unknown-intf"; + } +}; + +__must_check int __media_entity_enum_init(struct media_entity_enum *ent_enum, + int idx_max) +{ + ent_enum->bmap = kcalloc(DIV_ROUND_UP(idx_max, BITS_PER_LONG), + sizeof(long), GFP_KERNEL); + if (!ent_enum->bmap) + return -ENOMEM; + + bitmap_zero(ent_enum->bmap, idx_max); + ent_enum->idx_max = idx_max; + + return 0; +} +EXPORT_SYMBOL_GPL(__media_entity_enum_init); + +void media_entity_enum_cleanup(struct media_entity_enum *ent_enum) +{ + kfree(ent_enum->bmap); +} +EXPORT_SYMBOL_GPL(media_entity_enum_cleanup); + /** - * media_entity_init - Initialize a media entity - * - * @num_pads: Total number of sink and source pads. - * @extra_links: Initial estimate of the number of extra links. - * @pads: Array of 'num_pads' pads. - * - * The total number of pads is an intrinsic property of entities known by the - * entity driver, while the total number of links depends on hardware design - * and is an extrinsic property unknown to the entity driver. However, in most - * use cases the entity driver can guess the number of links which can safely - * be assumed to be equal to or larger than the number of pads. - * - * For those reasons the links array can be preallocated based on the entity - * driver guess and will be reallocated later if extra links need to be - * created. + * dev_dbg_obj - Prints in debug mode a change on some object * - * This function allocates a links array with enough space to hold at least - * 'num_pads' + 'extra_links' elements. The media_entity::max_links field will - * be set to the number of allocated elements. + * @event_name: Name of the event to report. Could be __func__ + * @gobj: Pointer to the object * - * The pads array is managed by the entity driver and passed to - * media_entity_init() where its pointer will be stored in the entity structure. + * Enabled only if DEBUG or CONFIG_DYNAMIC_DEBUG. Otherwise, it + * won't produce any code. */ -int -media_entity_init(struct media_entity *entity, u16 num_pads, - struct media_pad *pads, u16 extra_links) +static void dev_dbg_obj(const char *event_name, struct media_gobj *gobj) { - struct media_link *links; - unsigned int max_links = num_pads + extra_links; - unsigned int i; +#if defined(DEBUG) || defined (CONFIG_DYNAMIC_DEBUG) + switch (media_type(gobj)) { + case MEDIA_GRAPH_ENTITY: + dev_dbg(gobj->mdev->dev, + "%s id %u: entity '%s'\n", + event_name, media_id(gobj), + gobj_to_entity(gobj)->name); + break; + case MEDIA_GRAPH_LINK: + { + struct media_link *link = gobj_to_link(gobj); + + dev_dbg(gobj->mdev->dev, + "%s id %u: %s link id %u ==> id %u\n", + event_name, media_id(gobj), + media_type(link->gobj0) == MEDIA_GRAPH_PAD ? + "data" : "interface", + media_id(link->gobj0), + media_id(link->gobj1)); + break; + } + case MEDIA_GRAPH_PAD: + { + struct media_pad *pad = gobj_to_pad(gobj); + + dev_dbg(gobj->mdev->dev, + "%s id %u: %s%spad '%s':%d\n", + event_name, media_id(gobj), + pad->flags & MEDIA_PAD_FL_SINK ? "sink " : "", + pad->flags & MEDIA_PAD_FL_SOURCE ? "source " : "", + pad->entity->name, pad->index); + break; + } + case MEDIA_GRAPH_INTF_DEVNODE: + { + struct media_interface *intf = gobj_to_intf(gobj); + struct media_intf_devnode *devnode = intf_to_devnode(intf); + + dev_dbg(gobj->mdev->dev, + "%s id %u: intf_devnode %s - major: %d, minor: %d\n", + event_name, media_id(gobj), + intf_type(intf), + devnode->major, devnode->minor); + break; + } + } +#endif +} - links = kzalloc(max_links * sizeof(links[0]), GFP_KERNEL); - if (links == NULL) - return -ENOMEM; +void media_gobj_create(struct media_device *mdev, + enum media_gobj_type type, + struct media_gobj *gobj) +{ + BUG_ON(!mdev); + + gobj->mdev = mdev; + + /* Create a per-type unique object ID */ + gobj->id = media_gobj_gen_id(type, ++mdev->id); + + switch (type) { + case MEDIA_GRAPH_ENTITY: + list_add_tail(&gobj->list, &mdev->entities); + break; + case MEDIA_GRAPH_PAD: + list_add_tail(&gobj->list, &mdev->pads); + break; + case MEDIA_GRAPH_LINK: + list_add_tail(&gobj->list, &mdev->links); + break; + case MEDIA_GRAPH_INTF_DEVNODE: + list_add_tail(&gobj->list, &mdev->interfaces); + break; + } + + mdev->topology_version++; + + dev_dbg_obj(__func__, gobj); +} + +void media_gobj_destroy(struct media_gobj *gobj) +{ + dev_dbg_obj(__func__, gobj); + + gobj->mdev->topology_version++; + + /* Remove the object from mdev list */ + list_del(&gobj->list); +} + +int media_entity_pads_init(struct media_entity *entity, u16 num_pads, + struct media_pad *pads) +{ + struct media_device *mdev = entity->graph_obj.mdev; + unsigned int i; - entity->group_id = 0; - entity->max_links = max_links; - entity->num_links = 0; - entity->num_backlinks = 0; entity->num_pads = num_pads; entity->pads = pads; - entity->links = links; + + if (mdev) + spin_lock(&mdev->lock); for (i = 0; i < num_pads; i++) { pads[i].entity = entity; pads[i].index = i; + if (mdev) + media_gobj_create(mdev, MEDIA_GRAPH_PAD, + &entity->pads[i].graph_obj); } - return 0; -} -EXPORT_SYMBOL_GPL(media_entity_init); + if (mdev) + spin_unlock(&mdev->lock); -void -media_entity_cleanup(struct media_entity *entity) -{ - kfree(entity->links); + return 0; } -EXPORT_SYMBOL_GPL(media_entity_cleanup); +EXPORT_SYMBOL_GPL(media_entity_pads_init); /* ----------------------------------------------------------------------------- * Graph traversal @@ -108,7 +241,7 @@ static void stack_push(struct media_entity_graph *graph, return; } graph->top++; - graph->stack[graph->top].link = 0; + graph->stack[graph->top].link = entity->links.next; graph->stack[graph->top].entity = entity; } @@ -125,43 +258,51 @@ static struct media_entity *stack_pop(struct media_entity_graph *graph) #define link_top(en) ((en)->stack[(en)->top].link) #define stack_top(en) ((en)->stack[(en)->top].entity) +/* + * TODO: Get rid of this. + */ +#define MEDIA_ENTITY_MAX_PADS 512 + /** - * media_entity_graph_walk_start - Start walking the media graph at a given entity + * media_entity_graph_walk_init - Allocate resources for graph walk * @graph: Media graph structure that will be used to walk the graph - * @entity: Starting entity + * @mdev: Media device * - * This function initializes the graph traversal structure to walk the entities - * graph starting at the given entity. The traversal structure must not be - * modified by the caller during graph traversal. When done the structure can - * safely be freed. + * Reserve resources for graph walk in media device's current + * state. The memory must be released using + * media_entity_graph_walk_free(). + * + * Returns error on failure, zero on success. */ +__must_check int media_entity_graph_walk_init( + struct media_entity_graph *graph, struct media_device *mdev) +{ + return media_entity_enum_init(&graph->ent_enum, mdev); +} +EXPORT_SYMBOL_GPL(media_entity_graph_walk_init); + +/** + * media_entity_graph_walk_cleanup - Release resources related to graph walking + * @graph: Media graph structure that was used to walk the graph + */ +void media_entity_graph_walk_cleanup(struct media_entity_graph *graph) +{ + media_entity_enum_cleanup(&graph->ent_enum); +} +EXPORT_SYMBOL_GPL(media_entity_graph_walk_cleanup); + void media_entity_graph_walk_start(struct media_entity_graph *graph, struct media_entity *entity) { + media_entity_enum_zero(&graph->ent_enum); + media_entity_enum_set(&graph->ent_enum, entity); + graph->top = 0; graph->stack[graph->top].entity = NULL; - bitmap_zero(graph->entities, MEDIA_ENTITY_ENUM_MAX_ID); - - if (WARN_ON(entity->id >= MEDIA_ENTITY_ENUM_MAX_ID)) - return; - - __set_bit(entity->id, graph->entities); stack_push(graph, entity); } EXPORT_SYMBOL_GPL(media_entity_graph_walk_start); -/** - * media_entity_graph_walk_next - Get the next entity in the graph - * @graph: Media graph structure - * - * Perform a depth-first traversal of the given media entities graph. - * - * The graph structure must have been previously initialized with a call to - * media_entity_graph_walk_start(). - * - * Return the next entity in the graph or NULL if the whole graph have been - * traversed. - */ struct media_entity * media_entity_graph_walk_next(struct media_entity_graph *graph) { @@ -173,30 +314,30 @@ media_entity_graph_walk_next(struct media_entity_graph *graph) * top of the stack until no more entities on the level can be * found. */ - while (link_top(graph) < stack_top(graph)->num_links) { + while (link_top(graph) != &stack_top(graph)->links) { struct media_entity *entity = stack_top(graph); - struct media_link *link = &entity->links[link_top(graph)]; + struct media_link *link; struct media_entity *next; + link = list_entry(link_top(graph), typeof(*link), list); + /* The link is not enabled so we do not follow. */ if (!(link->flags & MEDIA_LNK_FL_ENABLED)) { - link_top(graph)++; + link_top(graph) = link_top(graph)->next; continue; } /* Get the entity in the other end of the link . */ next = media_entity_other(entity, link); - if (WARN_ON(next->id >= MEDIA_ENTITY_ENUM_MAX_ID)) - return NULL; /* Has the entity already been visited? */ - if (__test_and_set_bit(next->id, graph->entities)) { - link_top(graph)++; + if (media_entity_enum_test_and_set(&graph->ent_enum, next)) { + link_top(graph) = link_top(graph)->next; continue; } /* Push the new entity to stack and start over. */ - link_top(graph)++; + link_top(graph) = link_top(graph)->next; stack_push(graph, next); } @@ -208,39 +349,36 @@ EXPORT_SYMBOL_GPL(media_entity_graph_walk_next); * Pipeline management */ -/** - * media_entity_pipeline_start - Mark a pipeline as streaming - * @entity: Starting entity - * @pipe: Media pipeline to be assigned to all entities in the pipeline. - * - * Mark all entities connected to a given entity through enabled links, either - * directly or indirectly, as streaming. The given pipeline object is assigned to - * every entity in the pipeline and stored in the media_entity pipe field. - * - * Calls to this function can be nested, in which case the same number of - * media_entity_pipeline_stop() calls will be required to stop streaming. The - * pipeline pointer must be identical for all nested calls to - * media_entity_pipeline_start(). - */ __must_check int media_entity_pipeline_start(struct media_entity *entity, struct media_pipeline *pipe) { - struct media_device *mdev = entity->parent; - struct media_entity_graph graph; + struct media_device *mdev = entity->graph_obj.mdev; + struct media_entity_graph *graph = &pipe->graph; struct media_entity *entity_err = entity; + struct media_link *link; int ret; mutex_lock(&mdev->graph_mutex); - media_entity_graph_walk_start(&graph, entity); + if (!pipe->streaming_count++) { + ret = media_entity_graph_walk_init(&pipe->graph, mdev); + if (ret) + goto error_graph_walk_start; + } + + media_entity_graph_walk_start(&pipe->graph, entity); - while ((entity = media_entity_graph_walk_next(&graph))) { + while ((entity = media_entity_graph_walk_next(graph))) { DECLARE_BITMAP(active, MEDIA_ENTITY_MAX_PADS); DECLARE_BITMAP(has_no_links, MEDIA_ENTITY_MAX_PADS); - unsigned int i; entity->stream_count++; - WARN_ON(entity->pipe && entity->pipe != pipe); + + if (WARN_ON(entity->pipe && entity->pipe != pipe)) { + ret = -EBUSY; + goto error; + } + entity->pipe = pipe; /* Already streaming --- no need to check. */ @@ -253,8 +391,7 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity, bitmap_zero(active, entity->num_pads); bitmap_fill(has_no_links, entity->num_pads); - for (i = 0; i < entity->num_links; i++) { - struct media_link *link = &entity->links[i]; + list_for_each_entry(link, &entity->links, list) { struct media_pad *pad = link->sink->entity == entity ? link->sink : link->source; @@ -280,7 +417,7 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity, ret = entity->ops->link_validate(link); if (ret < 0 && ret != -ENOIOCTLCMD) { - dev_dbg(entity->parent->dev, + dev_dbg(entity->graph_obj.mdev->dev, "link validation failed for \"%s\":%u -> \"%s\":%u, error %d\n", link->source->entity->name, link->source->index, @@ -294,7 +431,7 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity, if (!bitmap_full(active, entity->num_pads)) { ret = -EPIPE; - dev_dbg(entity->parent->dev, + dev_dbg(entity->graph_obj.mdev->dev, "\"%s\":%u must be connected by an enabled link\n", entity->name, (unsigned)find_first_zero_bit( @@ -312,9 +449,9 @@ error: * Link validation on graph failed. We revert what we did and * return the error. */ - media_entity_graph_walk_start(&graph, entity_err); + media_entity_graph_walk_start(graph, entity_err); - while ((entity_err = media_entity_graph_walk_next(&graph))) { + while ((entity_err = media_entity_graph_walk_next(graph))) { entity_err->stream_count--; if (entity_err->stream_count == 0) entity_err->pipe = NULL; @@ -327,39 +464,36 @@ error: break; } +error_graph_walk_start: + if (!--pipe->streaming_count) + media_entity_graph_walk_cleanup(graph); + mutex_unlock(&mdev->graph_mutex); return ret; } EXPORT_SYMBOL_GPL(media_entity_pipeline_start); -/** - * media_entity_pipeline_stop - Mark a pipeline as not streaming - * @entity: Starting entity - * - * Mark all entities connected to a given entity through enabled links, either - * directly or indirectly, as not streaming. The media_entity pipe field is - * reset to NULL. - * - * If multiple calls to media_entity_pipeline_start() have been made, the same - * number of calls to this function are required to mark the pipeline as not - * streaming. - */ void media_entity_pipeline_stop(struct media_entity *entity) { - struct media_device *mdev = entity->parent; - struct media_entity_graph graph; + struct media_device *mdev = entity->graph_obj.mdev; + struct media_entity_graph *graph = &entity->pipe->graph; + struct media_pipeline *pipe = entity->pipe; mutex_lock(&mdev->graph_mutex); - media_entity_graph_walk_start(&graph, entity); + WARN_ON(!pipe->streaming_count); + media_entity_graph_walk_start(graph, entity); - while ((entity = media_entity_graph_walk_next(&graph))) { + while ((entity = media_entity_graph_walk_next(graph))) { entity->stream_count--; if (entity->stream_count == 0) entity->pipe = NULL; } + if (!--pipe->streaming_count) + media_entity_graph_walk_cleanup(graph); + mutex_unlock(&mdev->graph_mutex); } EXPORT_SYMBOL_GPL(media_entity_pipeline_stop); @@ -368,44 +502,26 @@ EXPORT_SYMBOL_GPL(media_entity_pipeline_stop); * Module use count */ -/* - * media_entity_get - Get a reference to the parent module - * @entity: The entity - * - * Get a reference to the parent media device module. - * - * The function will return immediately if @entity is NULL. - * - * Return a pointer to the entity on success or NULL on failure. - */ struct media_entity *media_entity_get(struct media_entity *entity) { if (entity == NULL) return NULL; - if (entity->parent->dev && - !try_module_get(entity->parent->dev->driver->owner)) + if (entity->graph_obj.mdev->dev && + !try_module_get(entity->graph_obj.mdev->dev->driver->owner)) return NULL; return entity; } EXPORT_SYMBOL_GPL(media_entity_get); -/* - * media_entity_put - Release the reference to the parent module - * @entity: The entity - * - * Release the reference count acquired by media_entity_get(). - * - * The function will return immediately if @entity is NULL. - */ void media_entity_put(struct media_entity *entity) { if (entity == NULL) return; - if (entity->parent->dev) - module_put(entity->parent->dev->driver->owner); + if (entity->graph_obj.mdev->dev) + module_put(entity->graph_obj.mdev->dev->driver->owner); } EXPORT_SYMBOL_GPL(media_entity_put); @@ -413,29 +529,52 @@ EXPORT_SYMBOL_GPL(media_entity_put); * Links management */ -static struct media_link *media_entity_add_link(struct media_entity *entity) +static struct media_link *media_add_link(struct list_head *head) { - if (entity->num_links >= entity->max_links) { - struct media_link *links = entity->links; - unsigned int max_links = entity->max_links + 2; - unsigned int i; + struct media_link *link; + + link = kzalloc(sizeof(*link), GFP_KERNEL); + if (link == NULL) + return NULL; - links = krealloc(links, max_links * sizeof(*links), GFP_KERNEL); - if (links == NULL) - return NULL; + list_add_tail(&link->list, head); - for (i = 0; i < entity->num_links; i++) - links[i].reverse->reverse = &links[i]; + return link; +} - entity->max_links = max_links; - entity->links = links; - } +static void __media_entity_remove_link(struct media_entity *entity, + struct media_link *link) +{ + struct media_link *rlink, *tmp; + struct media_entity *remote; + + if (link->source->entity == entity) + remote = link->sink->entity; + else + remote = link->source->entity; - return &entity->links[entity->num_links++]; + list_for_each_entry_safe(rlink, tmp, &remote->links, list) { + if (rlink != link->reverse) + continue; + + if (link->source->entity == entity) + remote->num_backlinks--; + + /* Remove the remote link */ + list_del(&rlink->list); + media_gobj_destroy(&rlink->graph_obj); + kfree(rlink); + + if (--remote->num_links == 0) + break; + } + list_del(&link->list); + media_gobj_destroy(&link->graph_obj); + kfree(link); } int -media_entity_create_link(struct media_entity *source, u16 source_pad, +media_create_pad_link(struct media_entity *source, u16 source_pad, struct media_entity *sink, u16 sink_pad, u32 flags) { struct media_link *link; @@ -445,68 +584,118 @@ media_entity_create_link(struct media_entity *source, u16 source_pad, BUG_ON(source_pad >= source->num_pads); BUG_ON(sink_pad >= sink->num_pads); - link = media_entity_add_link(source); + link = media_add_link(&source->links); if (link == NULL) return -ENOMEM; link->source = &source->pads[source_pad]; link->sink = &sink->pads[sink_pad]; - link->flags = flags; + link->flags = flags & ~MEDIA_LNK_FL_INTERFACE_LINK; + + /* Initialize graph object embedded at the new link */ + media_gobj_create(source->graph_obj.mdev, MEDIA_GRAPH_LINK, + &link->graph_obj); /* Create the backlink. Backlinks are used to help graph traversal and * are not reported to userspace. */ - backlink = media_entity_add_link(sink); + backlink = media_add_link(&sink->links); if (backlink == NULL) { - source->num_links--; + __media_entity_remove_link(source, link); return -ENOMEM; } backlink->source = &source->pads[source_pad]; backlink->sink = &sink->pads[sink_pad]; backlink->flags = flags; + backlink->is_backlink = true; + + /* Initialize graph object embedded at the new link */ + media_gobj_create(sink->graph_obj.mdev, MEDIA_GRAPH_LINK, + &backlink->graph_obj); link->reverse = backlink; backlink->reverse = link; sink->num_backlinks++; + sink->num_links++; + source->num_links++; return 0; } -EXPORT_SYMBOL_GPL(media_entity_create_link); - -void __media_entity_remove_links(struct media_entity *entity) +EXPORT_SYMBOL_GPL(media_create_pad_link); + +int media_create_pad_links(const struct media_device *mdev, + const u32 source_function, + struct media_entity *source, + const u16 source_pad, + const u32 sink_function, + struct media_entity *sink, + const u16 sink_pad, + u32 flags, + const bool allow_both_undefined) { - unsigned int i; - - for (i = 0; i < entity->num_links; i++) { - struct media_link *link = &entity->links[i]; - struct media_entity *remote; - unsigned int r = 0; - - if (link->source->entity == entity) - remote = link->sink->entity; - else - remote = link->source->entity; - - while (r < remote->num_links) { - struct media_link *rlink = &remote->links[r]; + struct media_entity *entity; + unsigned function; + int ret; - if (rlink != link->reverse) { - r++; + /* Trivial case: 1:1 relation */ + if (source && sink) + return media_create_pad_link(source, source_pad, + sink, sink_pad, flags); + + /* Worse case scenario: n:n relation */ + if (!source && !sink) { + if (!allow_both_undefined) + return 0; + media_device_for_each_entity(source, mdev) { + if (source->function != source_function) continue; + media_device_for_each_entity(sink, mdev) { + if (sink->function != sink_function) + continue; + ret = media_create_pad_link(source, source_pad, + sink, sink_pad, + flags); + if (ret) + return ret; + flags &= ~(MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); } + } + return 0; + } - if (link->source->entity == entity) - remote->num_backlinks--; + /* Handle 1:n and n:1 cases */ + if (source) + function = sink_function; + else + function = source_function; - if (--remote->num_links == 0) - break; + media_device_for_each_entity(entity, mdev) { + if (entity->function != function) + continue; - /* Insert last entry in place of the dropped link. */ - *rlink = remote->links[remote->num_links]; - } + if (source) + ret = media_create_pad_link(source, source_pad, + entity, sink_pad, flags); + else + ret = media_create_pad_link(entity, source_pad, + sink, sink_pad, flags); + if (ret) + return ret; + flags &= ~(MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); } + return 0; +} +EXPORT_SYMBOL_GPL(media_create_pad_links); + +void __media_entity_remove_links(struct media_entity *entity) +{ + struct media_link *link, *tmp; + + list_for_each_entry_safe(link, tmp, &entity->links, list) + __media_entity_remove_link(entity, link); entity->num_links = 0; entity->num_backlinks = 0; @@ -515,13 +704,15 @@ EXPORT_SYMBOL_GPL(__media_entity_remove_links); void media_entity_remove_links(struct media_entity *entity) { + struct media_device *mdev = entity->graph_obj.mdev; + /* Do nothing if the entity is not registered. */ - if (entity->parent == NULL) + if (mdev == NULL) return; - mutex_lock(&entity->parent->graph_mutex); + spin_lock(&mdev->lock); __media_entity_remove_links(entity); - mutex_unlock(&entity->parent->graph_mutex); + spin_unlock(&mdev->lock); } EXPORT_SYMBOL_GPL(media_entity_remove_links); @@ -549,20 +740,6 @@ static int __media_entity_setup_link_notify(struct media_link *link, u32 flags) return 0; } -/** - * __media_entity_setup_link - Configure a media link - * @link: The link being configured - * @flags: Link configuration flags - * - * The bulk of link setup is handled by the two entities connected through the - * link. This function notifies both entities of the link configuration change. - * - * If the link is immutable or if the current and new configuration are - * identical, return immediately. - * - * The user is expected to hold link->source->parent->mutex. If not, - * media_entity_setup_link() should be used instead. - */ int __media_entity_setup_link(struct media_link *link, u32 flags) { const u32 mask = MEDIA_LNK_FL_ENABLED; @@ -590,7 +767,7 @@ int __media_entity_setup_link(struct media_link *link, u32 flags) (source->stream_count || sink->stream_count)) return -EBUSY; - mdev = source->parent; + mdev = source->graph_obj.mdev; if (mdev->link_notify) { ret = mdev->link_notify(link, flags, @@ -611,31 +788,20 @@ int media_entity_setup_link(struct media_link *link, u32 flags) { int ret; - mutex_lock(&link->source->entity->parent->graph_mutex); + mutex_lock(&link->graph_obj.mdev->graph_mutex); ret = __media_entity_setup_link(link, flags); - mutex_unlock(&link->source->entity->parent->graph_mutex); + mutex_unlock(&link->graph_obj.mdev->graph_mutex); return ret; } EXPORT_SYMBOL_GPL(media_entity_setup_link); -/** - * media_entity_find_link - Find a link between two pads - * @source: Source pad - * @sink: Sink pad - * - * Return a pointer to the link between the two entities. If no such link - * exists, return NULL. - */ struct media_link * media_entity_find_link(struct media_pad *source, struct media_pad *sink) { struct media_link *link; - unsigned int i; - - for (i = 0; i < source->entity->num_links; ++i) { - link = &source->entity->links[i]; + list_for_each_entry(link, &source->entity->links, list) { if (link->source->entity == source->entity && link->source->index == source->index && link->sink->entity == sink->entity && @@ -647,23 +813,11 @@ media_entity_find_link(struct media_pad *source, struct media_pad *sink) } EXPORT_SYMBOL_GPL(media_entity_find_link); -/** - * media_entity_remote_pad - Find the pad at the remote end of a link - * @pad: Pad at the local end of the link - * - * Search for a remote pad connected to the given pad by iterating over all - * links originating or terminating at that pad until an enabled link is found. - * - * Return a pointer to the pad at the remote end of the first found enabled - * link, or NULL if no enabled link has been found. - */ struct media_pad *media_entity_remote_pad(struct media_pad *pad) { - unsigned int i; - - for (i = 0; i < pad->entity->num_links; i++) { - struct media_link *link = &pad->entity->links[i]; + struct media_link *link; + list_for_each_entry(link, &pad->entity->links, list) { if (!(link->flags & MEDIA_LNK_FL_ENABLED)) continue; @@ -678,3 +832,113 @@ struct media_pad *media_entity_remote_pad(struct media_pad *pad) } EXPORT_SYMBOL_GPL(media_entity_remote_pad); + +static void media_interface_init(struct media_device *mdev, + struct media_interface *intf, + u32 gobj_type, + u32 intf_type, u32 flags) +{ + intf->type = intf_type; + intf->flags = flags; + INIT_LIST_HEAD(&intf->links); + + media_gobj_create(mdev, gobj_type, &intf->graph_obj); +} + +/* Functions related to the media interface via device nodes */ + +struct media_intf_devnode *media_devnode_create(struct media_device *mdev, + u32 type, u32 flags, + u32 major, u32 minor) +{ + struct media_intf_devnode *devnode; + + devnode = kzalloc(sizeof(*devnode), GFP_KERNEL); + if (!devnode) + return NULL; + + devnode->major = major; + devnode->minor = minor; + + media_interface_init(mdev, &devnode->intf, MEDIA_GRAPH_INTF_DEVNODE, + type, flags); + + return devnode; +} +EXPORT_SYMBOL_GPL(media_devnode_create); + +void media_devnode_remove(struct media_intf_devnode *devnode) +{ + media_remove_intf_links(&devnode->intf); + media_gobj_destroy(&devnode->intf.graph_obj); + kfree(devnode); +} +EXPORT_SYMBOL_GPL(media_devnode_remove); + +struct media_link *media_create_intf_link(struct media_entity *entity, + struct media_interface *intf, + u32 flags) +{ + struct media_link *link; + + link = media_add_link(&intf->links); + if (link == NULL) + return NULL; + + link->intf = intf; + link->entity = entity; + link->flags = flags | MEDIA_LNK_FL_INTERFACE_LINK; + + /* Initialize graph object embedded at the new link */ + media_gobj_create(intf->graph_obj.mdev, MEDIA_GRAPH_LINK, + &link->graph_obj); + + return link; +} +EXPORT_SYMBOL_GPL(media_create_intf_link); + +void __media_remove_intf_link(struct media_link *link) +{ + list_del(&link->list); + media_gobj_destroy(&link->graph_obj); + kfree(link); +} +EXPORT_SYMBOL_GPL(__media_remove_intf_link); + +void media_remove_intf_link(struct media_link *link) +{ + struct media_device *mdev = link->graph_obj.mdev; + + /* Do nothing if the intf is not registered. */ + if (mdev == NULL) + return; + + spin_lock(&mdev->lock); + __media_remove_intf_link(link); + spin_unlock(&mdev->lock); +} +EXPORT_SYMBOL_GPL(media_remove_intf_link); + +void __media_remove_intf_links(struct media_interface *intf) +{ + struct media_link *link, *tmp; + + list_for_each_entry_safe(link, tmp, &intf->links, list) + __media_remove_intf_link(link); + +} +EXPORT_SYMBOL_GPL(__media_remove_intf_links); + +void media_remove_intf_links(struct media_interface *intf) +{ + struct media_device *mdev = intf->graph_obj.mdev; + + /* Do nothing if the intf is not registered. */ + if (mdev == NULL) + return; + + spin_lock(&mdev->lock); + __media_remove_intf_links(intf); + spin_unlock(&mdev->lock); +} +EXPORT_SYMBOL_GPL(media_remove_intf_links); diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c index c5cc14ef8347..da8b414fd824 100644 --- a/drivers/media/pci/bt8xx/dst_ca.c +++ b/drivers/media/pci/bt8xx/dst_ca.c @@ -705,7 +705,8 @@ struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_ struct dvb_device *dvbdev; dprintk(verbose, DST_CA_ERROR, 1, "registering DST-CA device"); - if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA) == 0) { + if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, + DVB_DEVICE_CA, 0) == 0) { dst->dst_ca = dvbdev; return dst->dst_ca; } diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index fba5b40a869c..9d5b314142f1 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -1065,7 +1065,7 @@ static int ddb_ci_attach(struct ddb_port *port) port->en, 0, 1); ret = dvb_register_device(&port->output->adap, &port->output->dev, &dvbdev_ci, (void *) port->output, - DVB_DEVICE_SEC); + DVB_DEVICE_SEC, 0); return ret; } diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c index 1b92d836a564..4e924e2d1638 100644 --- a/drivers/media/pci/ngene/ngene-core.c +++ b/drivers/media/pci/ngene/ngene-core.c @@ -1513,7 +1513,7 @@ static int init_channel(struct ngene_channel *chan) set_transfer(&chan->dev->channel[2], 1); dvb_register_device(adapter, &chan->ci_dev, &ngene_dvbdev_ci, (void *) chan, - DVB_DEVICE_SEC); + DVB_DEVICE_SEC, 0); if (!chan->ci_dev) goto err; } diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index 5e18b6796ed9..a69dc6a0752b 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -1358,7 +1358,7 @@ static int av7110_register(struct av7110 *av7110) #ifdef CONFIG_DVB_AV7110_OSD dvb_register_device(&av7110->dvb_adapter, &av7110->osd_dev, - &dvbdev_osd, av7110, DVB_DEVICE_OSD); + &dvbdev_osd, av7110, DVB_DEVICE_OSD, 0); #endif dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net, &dvbdemux->dmx); diff --git a/drivers/media/pci/ttpci/av7110_av.c b/drivers/media/pci/ttpci/av7110_av.c index 6fc748e22017..26c5696c193b 100644 --- a/drivers/media/pci/ttpci/av7110_av.c +++ b/drivers/media/pci/ttpci/av7110_av.c @@ -1594,10 +1594,10 @@ int av7110_av_register(struct av7110 *av7110) memset(&av7110->video_size, 0, sizeof (video_size_t)); dvb_register_device(&av7110->dvb_adapter, &av7110->video_dev, - &dvbdev_video, av7110, DVB_DEVICE_VIDEO); + &dvbdev_video, av7110, DVB_DEVICE_VIDEO, 0); dvb_register_device(&av7110->dvb_adapter, &av7110->audio_dev, - &dvbdev_audio, av7110, DVB_DEVICE_AUDIO); + &dvbdev_audio, av7110, DVB_DEVICE_AUDIO, 0); return 0; } diff --git a/drivers/media/pci/ttpci/av7110_ca.c b/drivers/media/pci/ttpci/av7110_ca.c index bc4c65ffd4b9..96a130fb4595 100644 --- a/drivers/media/pci/ttpci/av7110_ca.c +++ b/drivers/media/pci/ttpci/av7110_ca.c @@ -378,7 +378,7 @@ static struct dvb_device dvbdev_ca = { int av7110_ca_register(struct av7110 *av7110) { return dvb_register_device(&av7110->dvb_adapter, &av7110->ca_dev, - &dvbdev_ca, av7110, DVB_DEVICE_CA); + &dvbdev_ca, av7110, DVB_DEVICE_CA, 0); } void av7110_ca_unregister(struct av7110 *av7110) diff --git a/drivers/media/platform/exynos4-is/common.c b/drivers/media/platform/exynos4-is/common.c index b6716c57b5db..b90f5bb15517 100644 --- a/drivers/media/platform/exynos4-is/common.c +++ b/drivers/media/platform/exynos4-is/common.c @@ -22,8 +22,7 @@ struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity) while (pad->flags & MEDIA_PAD_FL_SINK) { /* source pad */ pad = media_entity_remote_pad(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; sd = media_entity_to_v4l2_subdev(pad->entity); diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index 0d549a6c8a13..bf47d3b9cbe7 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -1136,8 +1136,7 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc) } } - if (src_pad == NULL || - media_entity_type(src_pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!src_pad || !is_media_entity_v4l2_subdev(src_pad->entity)) break; /* Don't call FIMC subdev operation to avoid nested locking */ @@ -1392,7 +1391,7 @@ static int fimc_link_setup(struct media_entity *entity, struct fimc_vid_cap *vc = &fimc->vid_cap; struct v4l2_subdev *sensor; - if (media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!is_media_entity_v4l2_subdev(remote->entity)) return -EINVAL; if (WARN_ON(fimc == NULL)) @@ -1800,7 +1799,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, vid_cap->wb_fmt.code = fmt->mbus_code; vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0); + ret = media_entity_pads_init(&vfd->entity, 1, &vid_cap->vd_pad); if (ret) goto err_free_ctx; @@ -1892,8 +1891,8 @@ int fimc_initialize_capture_subdev(struct fimc_dev *fimc) fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_CAM].flags = MEDIA_PAD_FL_SINK; fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_FIFO].flags = MEDIA_PAD_FL_SINK; fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, - fimc->vid_cap.sd_pads, 0); + ret = media_entity_pads_init(&sd->entity, FIMC_SD_PADS_NUM, + fimc->vid_cap.sd_pads); if (ret) return ret; diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index 0dd22ec66694..bf9261eb57a1 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -287,7 +287,7 @@ static int isp_video_open(struct file *file) goto rel_fh; if (v4l2_fh_is_singular_file(file)) { - mutex_lock(&me->parent->graph_mutex); + mutex_lock(&me->graph_obj.mdev->graph_mutex); ret = fimc_pipeline_call(ve, open, me, true); @@ -295,7 +295,7 @@ static int isp_video_open(struct file *file) if (ret == 0) me->use_count++; - mutex_unlock(&me->parent->graph_mutex); + mutex_unlock(&me->graph_obj.mdev->graph_mutex); } if (!ret) goto unlock; @@ -311,7 +311,7 @@ static int isp_video_release(struct file *file) struct fimc_isp *isp = video_drvdata(file); struct fimc_is_video *ivc = &isp->video_capture; struct media_entity *entity = &ivc->ve.vdev.entity; - struct media_device *mdev = entity->parent; + struct media_device *mdev = entity->graph_obj.mdev; mutex_lock(&isp->video_lock); @@ -466,8 +466,7 @@ static int isp_video_pipeline_validate(struct fimc_isp *isp) /* Retrieve format at the source pad */ pad = media_entity_remote_pad(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; sd = media_entity_to_v4l2_subdev(pad->entity); @@ -617,7 +616,7 @@ int fimc_isp_video_device_register(struct fimc_isp *isp, vdev->lock = &isp->video_lock; iv->pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_init(&vdev->entity, 1, &iv->pad, 0); + ret = media_entity_pads_init(&vdev->entity, 1, &iv->pad); if (ret < 0) return ret; diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c index 5d78f5716f3b..293b807020c4 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/exynos4-is/fimc-isp.c @@ -708,8 +708,8 @@ int fimc_isp_subdev_create(struct fimc_isp *isp) isp->subdev_pads[FIMC_ISP_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_FIFO].flags = MEDIA_PAD_FL_SOURCE; isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_DMA].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, FIMC_ISP_SD_PADS_NUM, - isp->subdev_pads, 0); + ret = media_entity_pads_init(&sd->entity, FIMC_ISP_SD_PADS_NUM, + isp->subdev_pads); if (ret) return ret; diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 639ee710499e..e85649147dc8 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -494,7 +494,7 @@ static int fimc_lite_open(struct file *file) atomic_read(&fimc->out_path) != FIMC_IO_DMA) goto unlock; - mutex_lock(&me->parent->graph_mutex); + mutex_lock(&me->graph_obj.mdev->graph_mutex); ret = fimc_pipeline_call(&fimc->ve, open, me, true); @@ -502,7 +502,7 @@ static int fimc_lite_open(struct file *file) if (ret == 0) me->use_count++; - mutex_unlock(&me->parent->graph_mutex); + mutex_unlock(&me->graph_obj.mdev->graph_mutex); if (!ret) { fimc_lite_clear_event_counters(fimc); @@ -535,9 +535,9 @@ static int fimc_lite_release(struct file *file) fimc_pipeline_call(&fimc->ve, close); clear_bit(ST_FLITE_IN_USE, &fimc->state); - mutex_lock(&entity->parent->graph_mutex); + mutex_lock(&entity->graph_obj.mdev->graph_mutex); entity->use_count--; - mutex_unlock(&entity->parent->graph_mutex); + mutex_unlock(&entity->graph_obj.mdev->graph_mutex); } _vb2_fop_release(file, NULL); @@ -808,8 +808,7 @@ static int fimc_pipeline_validate(struct fimc_lite *fimc) } /* Retrieve format at the source pad */ pad = media_entity_remote_pad(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; sd = media_entity_to_v4l2_subdev(pad->entity); @@ -982,7 +981,6 @@ static int fimc_lite_link_setup(struct media_entity *entity, { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct fimc_lite *fimc = v4l2_get_subdevdata(sd); - unsigned int remote_ent_type = media_entity_type(remote->entity); int ret = 0; if (WARN_ON(fimc == NULL)) @@ -994,7 +992,7 @@ static int fimc_lite_link_setup(struct media_entity *entity, switch (local->index) { case FLITE_SD_PAD_SINK: - if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) { + if (!is_media_entity_v4l2_subdev(remote->entity)) { ret = -EINVAL; break; } @@ -1012,7 +1010,7 @@ static int fimc_lite_link_setup(struct media_entity *entity, case FLITE_SD_PAD_SOURCE_DMA: if (!(flags & MEDIA_LNK_FL_ENABLED)) atomic_set(&fimc->out_path, FIMC_IO_NONE); - else if (remote_ent_type == MEDIA_ENT_T_DEVNODE) + else if (is_media_entity_v4l2_io(remote->entity)) atomic_set(&fimc->out_path, FIMC_IO_DMA); else ret = -EINVAL; @@ -1021,7 +1019,7 @@ static int fimc_lite_link_setup(struct media_entity *entity, case FLITE_SD_PAD_SOURCE_ISP: if (!(flags & MEDIA_LNK_FL_ENABLED)) atomic_set(&fimc->out_path, FIMC_IO_NONE); - else if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) + else if (is_media_entity_v4l2_subdev(remote->entity)) atomic_set(&fimc->out_path, FIMC_IO_ISP); else ret = -EINVAL; @@ -1316,7 +1314,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) return ret; fimc->vd_pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_init(&vfd->entity, 1, &fimc->vd_pad, 0); + ret = media_entity_pads_init(&vfd->entity, 1, &fimc->vd_pad); if (ret < 0) return ret; @@ -1430,8 +1428,8 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) fimc->subdev_pads[FLITE_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; fimc->subdev_pads[FLITE_SD_PAD_SOURCE_DMA].flags = MEDIA_PAD_FL_SOURCE; fimc->subdev_pads[FLITE_SD_PAD_SOURCE_ISP].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, FLITE_SD_PADS_NUM, - fimc->subdev_pads, 0); + ret = media_entity_pads_init(&sd->entity, FLITE_SD_PADS_NUM, + fimc->subdev_pads); if (ret) return ret; diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index 5aa857c7b631..55ec4c99d484 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -739,7 +739,7 @@ int fimc_register_m2m_device(struct fimc_dev *fimc, return PTR_ERR(fimc->m2m.m2m_dev); } - ret = media_entity_init(&vfd->entity, 0, NULL, 0); + ret = media_entity_pads_init(&vfd->entity, 0, NULL); if (ret) goto err_me; diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 9481ce3201a2..f3b2dd30ec77 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -88,8 +88,7 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, break; } - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; sd = media_entity_to_v4l2_subdev(pad->entity); @@ -729,7 +728,7 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, flags = ((1 << i) & link_mask) ? MEDIA_LNK_FL_ENABLED : 0; sink = &fmd->fimc[i]->vid_cap.subdev.entity; - ret = media_entity_create_link(source, pad, sink, + ret = media_create_pad_link(source, pad, sink, FIMC_SD_PAD_SINK_CAM, flags); if (ret) return ret; @@ -749,7 +748,7 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, continue; sink = &fmd->fimc_lite[i]->subdev.entity; - ret = media_entity_create_link(source, pad, sink, + ret = media_create_pad_link(source, pad, sink, FLITE_SD_PAD_SINK, 0); if (ret) return ret; @@ -781,13 +780,13 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) source = &fimc->subdev.entity; sink = &fimc->ve.vdev.entity; /* FIMC-LITE's subdev and video node */ - ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_DMA, + ret = media_create_pad_link(source, FLITE_SD_PAD_SOURCE_DMA, sink, 0, 0); if (ret) break; /* Link from FIMC-LITE to IS-ISP subdev */ sink = &fmd->fimc_is->isp.subdev.entity; - ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_ISP, + ret = media_create_pad_link(source, FLITE_SD_PAD_SOURCE_ISP, sink, 0, 0); if (ret) break; @@ -811,7 +810,7 @@ static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd) /* Link from FIMC-IS-ISP subdev to FIMC */ sink = &fmd->fimc[i]->vid_cap.subdev.entity; - ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO, + ret = media_create_pad_link(source, FIMC_ISP_SD_PAD_SRC_FIFO, sink, FIMC_SD_PAD_SINK_FIFO, 0); if (ret) return ret; @@ -824,7 +823,7 @@ static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd) if (sink->num_pads == 0) return 0; - return media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_DMA, + return media_create_pad_link(source, FIMC_ISP_SD_PAD_SRC_DMA, sink, 0, 0); } @@ -873,7 +872,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) return -EINVAL; pad = sensor->entity.num_pads - 1; - ret = media_entity_create_link(&sensor->entity, pad, + ret = media_create_pad_link(&sensor->entity, pad, &csis->entity, CSIS_PAD_SINK, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); @@ -927,7 +926,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) source = &fmd->fimc[i]->vid_cap.subdev.entity; sink = &fmd->fimc[i]->vid_cap.ve.vdev.entity; - ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, + ret = media_create_pad_link(source, FIMC_SD_PAD_SOURCE, sink, 0, flags); if (ret) break; @@ -1046,11 +1045,11 @@ static int __fimc_md_modify_pipeline(struct media_entity *entity, bool enable) return ret; } -/* Locking: called with entity->parent->graph_mutex mutex held. */ -static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable) +/* Locking: called with entity->graph_obj.mdev->graph_mutex mutex held. */ +static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable, + struct media_entity_graph *graph) { struct media_entity *entity_err = entity; - struct media_entity_graph graph; int ret; /* @@ -1059,10 +1058,10 @@ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable) * through active links. This is needed as we cannot power on/off the * subdevs in random order. */ - media_entity_graph_walk_start(&graph, entity); + media_entity_graph_walk_start(graph, entity); - while ((entity = media_entity_graph_walk_next(&graph))) { - if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + while ((entity = media_entity_graph_walk_next(graph))) { + if (!is_media_entity_v4l2_io(entity)) continue; ret = __fimc_md_modify_pipeline(entity, enable); @@ -1072,11 +1071,12 @@ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable) } return 0; - err: - media_entity_graph_walk_start(&graph, entity_err); - while ((entity_err = media_entity_graph_walk_next(&graph))) { - if (media_entity_type(entity_err) != MEDIA_ENT_T_DEVNODE) +err: + media_entity_graph_walk_start(graph, entity_err); + + while ((entity_err = media_entity_graph_walk_next(graph))) { + if (!is_media_entity_v4l2_io(entity_err)) continue; __fimc_md_modify_pipeline(entity_err, !enable); @@ -1091,21 +1091,29 @@ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable) static int fimc_md_link_notify(struct media_link *link, unsigned int flags, unsigned int notification) { + struct media_entity_graph *graph = + &container_of(link->graph_obj.mdev, struct fimc_md, + media_dev)->link_setup_graph; struct media_entity *sink = link->sink->entity; int ret = 0; /* Before link disconnection */ if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) { + ret = media_entity_graph_walk_init(graph, + link->graph_obj.mdev); + if (ret) + return ret; if (!(flags & MEDIA_LNK_FL_ENABLED)) - ret = __fimc_md_modify_pipelines(sink, false); + ret = __fimc_md_modify_pipelines(sink, false, graph); #if 0 else /* TODO: Link state change validation */ #endif /* After link activation */ - } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && - (link->flags & MEDIA_LNK_FL_ENABLED)) { - ret = __fimc_md_modify_pipelines(sink, true); + } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH) { + if (link->flags & MEDIA_LNK_FL_ENABLED) + ret = __fimc_md_modify_pipelines(sink, true, graph); + media_entity_graph_walk_cleanup(graph); } return ret ? -EPIPE : 0; @@ -1314,7 +1322,10 @@ static int subdev_notifier_complete(struct v4l2_async_notifier *notifier) ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); unlock: mutex_unlock(&fmd->media_dev.graph_mutex); - return ret; + if (ret < 0) + return ret; + + return media_device_register(&fmd->media_dev); } static int fimc_md_probe(struct platform_device *pdev) @@ -1345,18 +1356,14 @@ static int fimc_md_probe(struct platform_device *pdev) fmd->use_isp = fimc_md_is_isp_available(dev->of_node); fmd->user_subdev_api = true; + media_device_init(&fmd->media_dev); + ret = v4l2_device_register(dev, &fmd->v4l2_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); return ret; } - ret = media_device_register(&fmd->media_dev); - if (ret < 0) { - v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret); - goto err_v4l2_dev; - } - ret = fimc_md_get_clocks(fmd); if (ret) goto err_md; @@ -1425,8 +1432,7 @@ err_clk: err_m_ent: fimc_md_unregister_entities(fmd); err_md: - media_device_unregister(&fmd->media_dev); -err_v4l2_dev: + media_device_cleanup(&fmd->media_dev); v4l2_device_unregister(&fmd->v4l2_dev); return ret; } @@ -1446,6 +1452,7 @@ static int fimc_md_remove(struct platform_device *pdev) fimc_md_unregister_entities(fmd); fimc_md_pipelines_free(fmd); media_device_unregister(&fmd->media_dev); + media_device_cleanup(&fmd->media_dev); fimc_md_put_clocks(fmd); return 0; diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h index 93a96126929b..ed122cb2dd74 100644 --- a/drivers/media/platform/exynos4-is/media-dev.h +++ b/drivers/media/platform/exynos4-is/media-dev.h @@ -154,6 +154,7 @@ struct fimc_md { bool user_subdev_api; spinlock_t slock; struct list_head pipelines; + struct media_entity_graph link_setup_graph; }; static inline @@ -164,8 +165,8 @@ struct fimc_sensor_info *source_to_sensor_info(struct fimc_source_info *si) static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me) { - return me->parent == NULL ? NULL : - container_of(me->parent, struct fimc_md, media_dev); + return me->graph_obj.mdev == NULL ? NULL : + container_of(me->graph_obj.mdev, struct fimc_md, media_dev); } static inline struct fimc_md *notifier_to_fimc_md(struct v4l2_async_notifier *n) @@ -175,12 +176,12 @@ static inline struct fimc_md *notifier_to_fimc_md(struct v4l2_async_notifier *n) static inline void fimc_md_graph_lock(struct exynos_video_entity *ve) { - mutex_lock(&ve->vdev.entity.parent->graph_mutex); + mutex_lock(&ve->vdev.entity.graph_obj.mdev->graph_mutex); } static inline void fimc_md_graph_unlock(struct exynos_video_entity *ve) { - mutex_unlock(&ve->vdev.entity.parent->graph_mutex); + mutex_unlock(&ve->vdev.entity.graph_obj.mdev->graph_mutex); } int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on); diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index ff5dabf24694..ac5e50e595be 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -866,8 +866,8 @@ static int s5pcsis_probe(struct platform_device *pdev) state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK; state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&state->sd.entity, - CSIS_PADS_NUM, state->pads, 0); + ret = media_entity_pads_init(&state->sd.entity, + CSIS_PADS_NUM, state->pads); if (ret < 0) goto e_clkdis; diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 56e683b19a73..0bcfa553c1aa 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -683,15 +683,15 @@ static irqreturn_t isp_isr(int irq, void *_isp) * * Return the total number of users of all video device nodes in the pipeline. */ -static int isp_pipeline_pm_use_count(struct media_entity *entity) +static int isp_pipeline_pm_use_count(struct media_entity *entity, + struct media_entity_graph *graph) { - struct media_entity_graph graph; int use = 0; - media_entity_graph_walk_start(&graph, entity); + media_entity_graph_walk_start(graph, entity); - while ((entity = media_entity_graph_walk_next(&graph))) { - if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + while ((entity = media_entity_graph_walk_next(graph))) { + if (is_media_entity_v4l2_io(entity)) use += entity->use_count; } @@ -714,7 +714,7 @@ static int isp_pipeline_pm_power_one(struct media_entity *entity, int change) struct v4l2_subdev *subdev; int ret; - subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV + subdev = is_media_entity_v4l2_subdev(entity) ? media_entity_to_v4l2_subdev(entity) : NULL; if (entity->use_count == 0 && change > 0 && subdev != NULL) { @@ -742,29 +742,29 @@ static int isp_pipeline_pm_power_one(struct media_entity *entity, int change) * * Return 0 on success or a negative error code on failure. */ -static int isp_pipeline_pm_power(struct media_entity *entity, int change) +static int isp_pipeline_pm_power(struct media_entity *entity, int change, + struct media_entity_graph *graph) { - struct media_entity_graph graph; struct media_entity *first = entity; int ret = 0; if (!change) return 0; - media_entity_graph_walk_start(&graph, entity); + media_entity_graph_walk_start(graph, entity); - while (!ret && (entity = media_entity_graph_walk_next(&graph))) - if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + while (!ret && (entity = media_entity_graph_walk_next(graph))) + if (is_media_entity_v4l2_subdev(entity)) ret = isp_pipeline_pm_power_one(entity, change); if (!ret) - return 0; + return ret; - media_entity_graph_walk_start(&graph, first); + media_entity_graph_walk_start(graph, first); - while ((first = media_entity_graph_walk_next(&graph)) + while ((first = media_entity_graph_walk_next(graph)) && first != entity) - if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE) + if (is_media_entity_v4l2_subdev(first)) isp_pipeline_pm_power_one(first, -change); return ret; @@ -782,23 +782,24 @@ static int isp_pipeline_pm_power(struct media_entity *entity, int change) * off is assumed to never fail. No failure can occur when the use parameter is * set to 0. */ -int omap3isp_pipeline_pm_use(struct media_entity *entity, int use) +int omap3isp_pipeline_pm_use(struct media_entity *entity, int use, + struct media_entity_graph *graph) { int change = use ? 1 : -1; int ret; - mutex_lock(&entity->parent->graph_mutex); + mutex_lock(&entity->graph_obj.mdev->graph_mutex); /* Apply use count to node. */ entity->use_count += change; WARN_ON(entity->use_count < 0); /* Apply power change to connected non-nodes. */ - ret = isp_pipeline_pm_power(entity, change); + ret = isp_pipeline_pm_power(entity, change, graph); if (ret < 0) entity->use_count -= change; - mutex_unlock(&entity->parent->graph_mutex); + mutex_unlock(&entity->graph_obj.mdev->graph_mutex); return ret; } @@ -820,35 +821,49 @@ int omap3isp_pipeline_pm_use(struct media_entity *entity, int use) static int isp_pipeline_link_notify(struct media_link *link, u32 flags, unsigned int notification) { + struct media_entity_graph *graph = + &container_of(link->graph_obj.mdev, struct isp_device, + media_dev)->pm_count_graph; struct media_entity *source = link->source->entity; struct media_entity *sink = link->sink->entity; - int source_use = isp_pipeline_pm_use_count(source); - int sink_use = isp_pipeline_pm_use_count(sink); - int ret; + int source_use; + int sink_use; + int ret = 0; + + if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) { + ret = media_entity_graph_walk_init(graph, + link->graph_obj.mdev); + if (ret) + return ret; + } + + source_use = isp_pipeline_pm_use_count(source, graph); + sink_use = isp_pipeline_pm_use_count(sink, graph); if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && !(flags & MEDIA_LNK_FL_ENABLED)) { /* Powering off entities is assumed to never fail. */ - isp_pipeline_pm_power(source, -sink_use); - isp_pipeline_pm_power(sink, -source_use); + isp_pipeline_pm_power(source, -sink_use, graph); + isp_pipeline_pm_power(sink, -source_use, graph); return 0; } if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && (flags & MEDIA_LNK_FL_ENABLED)) { - ret = isp_pipeline_pm_power(source, sink_use); + ret = isp_pipeline_pm_power(source, sink_use, graph); if (ret < 0) return ret; - ret = isp_pipeline_pm_power(sink, source_use); + ret = isp_pipeline_pm_power(sink, source_use, graph); if (ret < 0) - isp_pipeline_pm_power(source, -sink_use); - - return ret; + isp_pipeline_pm_power(source, -sink_use, graph); } - return 0; + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH) + media_entity_graph_walk_cleanup(graph); + + return ret; } /* ----------------------------------------------------------------------------- @@ -881,7 +896,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, * starting entities if the pipeline won't start anyway (those entities * would then likely fail to stop, making the problem worse). */ - if (pipe->entities & isp->crashed) + if (media_entity_enum_intersects(&pipe->ent_enum, &isp->crashed)) return -EIO; spin_lock_irqsave(&pipe->lock, flags); @@ -897,8 +912,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, break; pad = media_entity_remote_pad(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; entity = pad->entity; @@ -987,8 +1001,7 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe) break; pad = media_entity_remote_pad(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; entity = pad->entity; @@ -1028,7 +1041,8 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe) dev_info(isp->dev, "Unable to stop %s\n", subdev->name); isp->stop_failure = true; if (subdev == &isp->isp_prev.subdev) - isp->crashed |= 1U << subdev->entity.id; + media_entity_enum_set(&isp->crashed, + &subdev->entity); failure = -ETIMEDOUT; } } @@ -1234,7 +1248,7 @@ static int isp_reset(struct isp_device *isp) } isp->stop_failure = false; - isp->crashed = 0; + media_entity_enum_zero(&isp->crashed); return 0; } @@ -1645,7 +1659,8 @@ static void __omap3isp_put(struct isp_device *isp, bool save_ctx) /* Reset the ISP if an entity has failed to stop. This is the * only way to recover from such conditions. */ - if (isp->crashed || isp->stop_failure) + if (!media_entity_enum_empty(&isp->crashed) || + isp->stop_failure) isp_reset(isp); isp_disable_clocks(isp); } @@ -1792,6 +1807,7 @@ static void isp_unregister_entities(struct isp_device *isp) v4l2_device_unregister(&isp->v4l2_dev); media_device_unregister(&isp->media_dev); + media_device_cleanup(&isp->media_dev); } static int isp_link_entity( @@ -1862,7 +1878,7 @@ static int isp_link_entity( return -EINVAL; } - return media_entity_create_link(entity, i, input, pad, flags); + return media_create_pad_link(entity, i, input, pad, flags); } static int isp_register_entities(struct isp_device *isp) @@ -1874,12 +1890,7 @@ static int isp_register_entities(struct isp_device *isp) sizeof(isp->media_dev.model)); isp->media_dev.hw_revision = isp->revision; isp->media_dev.link_notify = isp_pipeline_link_notify; - ret = media_device_register(&isp->media_dev); - if (ret < 0) { - dev_err(isp->dev, "%s: Media device registration failed (%d)\n", - __func__, ret); - return ret; - } + media_device_init(&isp->media_dev); isp->v4l2_dev.mdev = &isp->media_dev; ret = v4l2_device_register(isp->dev, &isp->v4l2_dev); @@ -1930,6 +1941,118 @@ done: return ret; } +/* + * isp_create_links() - Create links for internal and external ISP entities + * @isp : Pointer to ISP device + * + * This function creates all links between ISP internal and external entities. + * + * Return: A negative error code on failure or zero on success. Possible error + * codes are those returned by media_create_pad_link(). + */ +static int isp_create_links(struct isp_device *isp) +{ + int ret; + + /* Create links between entities and video nodes. */ + ret = media_create_pad_link( + &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE, + &isp->isp_csi2a.video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccp2.video_in.video.entity, 0, + &isp->isp_ccp2.subdev.entity, CCP2_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF, + &isp->isp_ccdc.video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_prev.video_in.video.entity, 0, + &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE, + &isp->isp_prev.video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_res.video_in.video.entity, 0, + &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_res.subdev.entity, RESZ_PAD_SOURCE, + &isp->isp_res.video_out.video.entity, 0, 0); + + if (ret < 0) + return ret; + + /* Create links between entities. */ + ret = media_create_pad_link( + &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE, + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE, + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, + &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF, + &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE, + &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, + &isp->isp_aewb.subdev.entity, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, + &isp->isp_af.subdev.entity, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, + &isp->isp_hist.subdev.entity, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) + return ret; + + return 0; +} + static void isp_cleanup_modules(struct isp_device *isp) { omap3isp_h3a_aewb_cleanup(isp); @@ -2000,62 +2123,8 @@ static int isp_initialize_modules(struct isp_device *isp) goto error_h3a_af; } - /* Connect the submodules. */ - ret = media_entity_create_link( - &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE, - &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE, - &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, - &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF, - &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE, - &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, - &isp->isp_aewb.subdev.entity, 0, - MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, - &isp->isp_af.subdev.entity, 0, - MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, - &isp->isp_hist.subdev.entity, 0, - MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); - if (ret < 0) - goto error_link; - return 0; -error_link: - omap3isp_h3a_af_cleanup(isp); error_h3a_af: omap3isp_h3a_aewb_cleanup(isp); error_h3a_aewb: @@ -2149,6 +2218,8 @@ static int isp_remove(struct platform_device *pdev) isp_detach_iommu(isp); __omap3isp_put(isp, false); + media_entity_enum_cleanup(&isp->crashed); + return 0; } @@ -2278,28 +2349,43 @@ static int isp_subdev_notifier_bound(struct v4l2_async_notifier *async, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { - struct isp_device *isp = container_of(async, struct isp_device, - notifier); struct isp_async_subdev *isd = container_of(asd, struct isp_async_subdev, asd); - int ret; - - ret = isp_link_entity(isp, &subdev->entity, isd->bus.interface); - if (ret < 0) - return ret; isd->sd = subdev; isd->sd->host_priv = &isd->bus; - return ret; + return 0; } static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async) { struct isp_device *isp = container_of(async, struct isp_device, notifier); + struct v4l2_device *v4l2_dev = &isp->v4l2_dev; + struct v4l2_subdev *sd; + struct isp_bus_cfg *bus; + int ret; + + ret = media_entity_enum_init(&isp->crashed, &isp->media_dev); + if (ret) + return ret; - return v4l2_device_register_subdev_nodes(&isp->v4l2_dev); + list_for_each_entry(sd, &v4l2_dev->subdevs, list) { + /* Only try to link entities whose interface was set on bound */ + if (sd->host_priv) { + bus = (struct isp_bus_cfg *)sd->host_priv; + ret = isp_link_entity(isp, &sd->entity, bus->interface); + if (ret < 0) + return ret; + } + } + + ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); + if (ret < 0) + return ret; + + return media_device_register(&isp->media_dev); } /* @@ -2465,6 +2551,10 @@ static int isp_probe(struct platform_device *pdev) if (ret < 0) goto error_modules; + ret = isp_create_links(isp); + if (ret < 0) + goto error_register_entities; + isp->notifier.bound = isp_subdev_notifier_bound; isp->notifier.complete = isp_subdev_notifier_complete; diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index 5acc2e6511a5..49b7f71ac968 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -17,6 +17,7 @@ #ifndef OMAP3_ISP_CORE_H #define OMAP3_ISP_CORE_H +#include <media/media-entity.h> #include <media/v4l2-async.h> #include <media/v4l2-device.h> #include <linux/clk-provider.h> @@ -152,7 +153,7 @@ struct isp_xclk { * @stat_lock: Spinlock for handling statistics * @isp_mutex: Mutex for serializing requests to ISP. * @stop_failure: Indicates that an entity failed to stop. - * @crashed: Bitmask of crashed entities (indexed by entity ID) + * @crashed: Crashed ent_enum * @has_context: Context has been saved at least once and can be restored. * @ref_count: Reference count for handling multiple ISP requests. * @cam_ick: Pointer to camera interface clock structure. @@ -176,6 +177,7 @@ struct isp_device { struct v4l2_device v4l2_dev; struct v4l2_async_notifier notifier; struct media_device media_dev; + struct media_entity_graph pm_count_graph; struct device *dev; u32 revision; @@ -194,7 +196,7 @@ struct isp_device { spinlock_t stat_lock; /* common lock for statistic drivers */ struct mutex isp_mutex; /* For handling ref_count field */ bool stop_failure; - u32 crashed; + struct media_entity_enum crashed; int has_context; int ref_count; unsigned int autoidle; @@ -265,7 +267,8 @@ void omap3isp_subclk_enable(struct isp_device *isp, void omap3isp_subclk_disable(struct isp_device *isp, enum isp_subclk_resource res); -int omap3isp_pipeline_pm_use(struct media_entity *entity, int use); +int omap3isp_pipeline_pm_use(struct media_entity *entity, int use, + struct media_entity_graph *graph); int omap3isp_register_entities(struct platform_device *pdev, struct v4l2_device *v4l2_dev); diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index a6a61cce43dd..bb3974c98e37 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c @@ -1608,7 +1608,7 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc) /* Wait for the CCDC to become idle. */ if (ccdc_sbl_wait_idle(ccdc, 1000)) { dev_info(isp->dev, "CCDC won't become idle!\n"); - isp->crashed |= 1U << ccdc->subdev.entity.id; + media_entity_enum_set(&isp->crashed, &ccdc->subdev.entity); omap3isp_pipeline_cancel_stream(pipe); return 0; } @@ -2513,9 +2513,14 @@ static int ccdc_link_setup(struct media_entity *entity, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct isp_device *isp = to_isp_device(ccdc); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case CCDC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case CCDC_PAD_SINK | 2 << 16: /* Read from the sensor (parallel interface), CCP2, CSI2a or * CSI2c. */ @@ -2543,7 +2548,7 @@ static int ccdc_link_setup(struct media_entity *entity, * Revisit this when it will be implemented, and return -EBUSY for now. */ - case CCDC_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV: + case CCDC_PAD_SOURCE_VP | 2 << 16: /* Write to preview engine, histogram and H3A. When none of * those links are active, the video port can be disabled. */ @@ -2556,7 +2561,7 @@ static int ccdc_link_setup(struct media_entity *entity, } break; - case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_DEVNODE: + case CCDC_PAD_SOURCE_OF: /* Write to memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ccdc->output & ~CCDC_OUTPUT_MEMORY) @@ -2567,7 +2572,7 @@ static int ccdc_link_setup(struct media_entity *entity, } break; - case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_V4L2_SUBDEV: + case CCDC_PAD_SOURCE_OF | 2 << 16: /* Write to resizer */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ccdc->output & ~CCDC_OUTPUT_RESIZER) @@ -2650,7 +2655,7 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc) pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE; me->ops = &ccdc_media_ops; - ret = media_entity_init(me, CCDC_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, CCDC_PADS_NUM, pads); if (ret < 0) return ret; @@ -2664,19 +2669,11 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc) ret = omap3isp_video_init(&ccdc->video_out, "CCDC"); if (ret < 0) - goto error_video; - - /* Connect the CCDC subdev to the video node. */ - ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF, - &ccdc->video_out.video.entity, 0, 0); - if (ret < 0) - goto error_link; + goto error; return 0; -error_link: - omap3isp_video_cleanup(&ccdc->video_out); -error_video: +error: media_entity_cleanup(me); return ret; } diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c index 38e6a974c5b1..ca095238510d 100644 --- a/drivers/media/platform/omap3isp/ispccp2.c +++ b/drivers/media/platform/omap3isp/ispccp2.c @@ -956,9 +956,14 @@ static int ccp2_link_setup(struct media_entity *entity, { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case CCP2_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case CCP2_PAD_SINK: /* read from memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ccp2->input == CCP2_INPUT_SENSOR) @@ -970,7 +975,7 @@ static int ccp2_link_setup(struct media_entity *entity, } break; - case CCP2_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + case CCP2_PAD_SINK | 2 << 16: /* read from sensor/phy */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ccp2->input == CCP2_INPUT_MEMORY) @@ -981,7 +986,7 @@ static int ccp2_link_setup(struct media_entity *entity, ccp2->input = CCP2_INPUT_NONE; } break; - case CCP2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case CCP2_PAD_SOURCE | 2 << 16: /* write to video port/ccdc */ if (flags & MEDIA_LNK_FL_ENABLED) ccp2->output = CCP2_OUTPUT_CCDC; @@ -1071,7 +1076,7 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2) pads[CCP2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; me->ops = &ccp2_media_ops; - ret = media_entity_init(me, CCP2_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, CCP2_PADS_NUM, pads); if (ret < 0) return ret; @@ -1097,19 +1102,11 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2) ret = omap3isp_video_init(&ccp2->video_in, "CCP2"); if (ret < 0) - goto error_video; - - /* Connect the video node to the ccp2 subdev. */ - ret = media_entity_create_link(&ccp2->video_in.video.entity, 0, - &ccp2->subdev.entity, CCP2_PAD_SINK, 0); - if (ret < 0) - goto error_link; + goto error; return 0; -error_link: - omap3isp_video_cleanup(&ccp2->video_in); -error_video: +error: media_entity_cleanup(&ccp2->subdev.entity); return ret; } diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c index a78338d012b4..f75a1be29d84 100644 --- a/drivers/media/platform/omap3isp/ispcsi2.c +++ b/drivers/media/platform/omap3isp/ispcsi2.c @@ -1144,14 +1144,19 @@ static int csi2_link_setup(struct media_entity *entity, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct isp_csi2_ctrl_cfg *ctrl = &csi2->ctrl; + unsigned int index = local->index; /* * The ISP core doesn't support pipelines with multiple video outputs. * Revisit this when it will be implemented, and return -EBUSY for now. */ - switch (local->index | media_entity_type(remote->entity)) { - case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case CSI2_PAD_SOURCE: if (flags & MEDIA_LNK_FL_ENABLED) { if (csi2->output & ~CSI2_OUTPUT_MEMORY) return -EBUSY; @@ -1161,7 +1166,7 @@ static int csi2_link_setup(struct media_entity *entity, } break; - case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case CSI2_PAD_SOURCE | 2 << 16: if (flags & MEDIA_LNK_FL_ENABLED) { if (csi2->output & ~CSI2_OUTPUT_CCDC) return -EBUSY; @@ -1245,7 +1250,7 @@ static int csi2_init_entities(struct isp_csi2_device *csi2) | MEDIA_PAD_FL_MUST_CONNECT; me->ops = &csi2_media_ops; - ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, CSI2_PADS_NUM, pads); if (ret < 0) return ret; @@ -1264,16 +1269,8 @@ static int csi2_init_entities(struct isp_csi2_device *csi2) if (ret < 0) goto error_video; - /* Connect the CSI2 subdev to the video node. */ - ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE, - &csi2->video_out.video.entity, 0, 0); - if (ret < 0) - goto error_link; - return 0; -error_link: - omap3isp_video_cleanup(&csi2->video_out); error_video: media_entity_cleanup(&csi2->subdev.entity); return ret; diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index 13803270d104..84a96670e2e7 100644 --- a/drivers/media/platform/omap3isp/isppreview.c +++ b/drivers/media/platform/omap3isp/isppreview.c @@ -2144,9 +2144,14 @@ static int preview_link_setup(struct media_entity *entity, { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct isp_prev_device *prev = v4l2_get_subdevdata(sd); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case PREV_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case PREV_PAD_SINK: /* read from memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (prev->input == PREVIEW_INPUT_CCDC) @@ -2158,7 +2163,7 @@ static int preview_link_setup(struct media_entity *entity, } break; - case PREV_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + case PREV_PAD_SINK | 2 << 16: /* read from ccdc */ if (flags & MEDIA_LNK_FL_ENABLED) { if (prev->input == PREVIEW_INPUT_MEMORY) @@ -2175,7 +2180,7 @@ static int preview_link_setup(struct media_entity *entity, * Revisit this when it will be implemented, and return -EBUSY for now. */ - case PREV_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + case PREV_PAD_SOURCE: /* write to memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (prev->output & ~PREVIEW_OUTPUT_MEMORY) @@ -2186,7 +2191,7 @@ static int preview_link_setup(struct media_entity *entity, } break; - case PREV_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case PREV_PAD_SOURCE | 2 << 16: /* write to resizer */ if (flags & MEDIA_LNK_FL_ENABLED) { if (prev->output & ~PREVIEW_OUTPUT_RESIZER) @@ -2282,7 +2287,7 @@ static int preview_init_entities(struct isp_prev_device *prev) pads[PREV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; me->ops = &preview_media_ops; - ret = media_entity_init(me, PREV_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, PREV_PADS_NUM, pads); if (ret < 0) return ret; @@ -2311,21 +2316,8 @@ static int preview_init_entities(struct isp_prev_device *prev) if (ret < 0) goto error_video_out; - /* Connect the video nodes to the previewer subdev. */ - ret = media_entity_create_link(&prev->video_in.video.entity, 0, - &prev->subdev.entity, PREV_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link(&prev->subdev.entity, PREV_PAD_SOURCE, - &prev->video_out.video.entity, 0, 0); - if (ret < 0) - goto error_link; - return 0; -error_link: - omap3isp_video_cleanup(&prev->video_out); error_video_out: omap3isp_video_cleanup(&prev->video_in); error_video_in: diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c index 7cfb43dc0ffd..0b6a87508584 100644 --- a/drivers/media/platform/omap3isp/ispresizer.c +++ b/drivers/media/platform/omap3isp/ispresizer.c @@ -1623,9 +1623,14 @@ static int resizer_link_setup(struct media_entity *entity, { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct isp_res_device *res = v4l2_get_subdevdata(sd); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case RESZ_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case RESZ_PAD_SINK: /* read from memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (res->input == RESIZER_INPUT_VP) @@ -1637,7 +1642,7 @@ static int resizer_link_setup(struct media_entity *entity, } break; - case RESZ_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + case RESZ_PAD_SINK | 2 << 16: /* read from ccdc or previewer */ if (flags & MEDIA_LNK_FL_ENABLED) { if (res->input == RESIZER_INPUT_MEMORY) @@ -1649,7 +1654,7 @@ static int resizer_link_setup(struct media_entity *entity, } break; - case RESZ_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + case RESZ_PAD_SOURCE: /* resizer always write to memory */ break; @@ -1728,7 +1733,7 @@ static int resizer_init_entities(struct isp_res_device *res) pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; me->ops = &resizer_media_ops; - ret = media_entity_init(me, RESZ_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, RESZ_PADS_NUM, pads); if (ret < 0) return ret; @@ -1755,21 +1760,8 @@ static int resizer_init_entities(struct isp_res_device *res) res->video_out.video.entity.flags |= MEDIA_ENT_FL_DEFAULT; - /* Connect the video nodes to the resizer subdev. */ - ret = media_entity_create_link(&res->video_in.video.entity, 0, - &res->subdev.entity, RESZ_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE, - &res->video_out.video.entity, 0, 0); - if (ret < 0) - goto error_link; - return 0; -error_link: - omap3isp_video_cleanup(&res->video_out); error_video_out: omap3isp_video_cleanup(&res->video_in); error_video_in: diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c index 94d4c295d3d0..1b9217d3b1b6 100644 --- a/drivers/media/platform/omap3isp/ispstat.c +++ b/drivers/media/platform/omap3isp/ispstat.c @@ -1028,7 +1028,7 @@ static int isp_stat_init_entities(struct ispstat *stat, const char *name, stat->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; me->ops = NULL; - return media_entity_init(me, 1, &stat->pad, 0); + return media_entity_pads_init(me, 1, &stat->pad); } int omap3isp_stat_init(struct ispstat *stat, const char *name, diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index ecadca3e945b..994dfc0813f6 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -210,8 +210,7 @@ isp_video_remote_subdev(struct isp_video *video, u32 *pad) remote = media_entity_remote_pad(&video->pad); - if (remote == NULL || - media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; if (pad) @@ -226,16 +225,23 @@ static int isp_video_get_graph_data(struct isp_video *video, { struct media_entity_graph graph; struct media_entity *entity = &video->video.entity; - struct media_device *mdev = entity->parent; + struct media_device *mdev = entity->graph_obj.mdev; struct isp_video *far_end = NULL; + int ret; mutex_lock(&mdev->graph_mutex); + ret = media_entity_graph_walk_init(&graph, entity->graph_obj.mdev); + if (ret) { + mutex_unlock(&mdev->graph_mutex); + return ret; + } + media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { struct isp_video *__video; - pipe->entities |= 1 << entity->id; + media_entity_enum_set(&pipe->ent_enum, entity); if (far_end != NULL) continue; @@ -243,7 +249,7 @@ static int isp_video_get_graph_data(struct isp_video *video, if (entity == &video->video.entity) continue; - if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + if (!is_media_entity_v4l2_io(entity)) continue; __video = to_isp_video(media_entity_to_video_device(entity)); @@ -253,6 +259,8 @@ static int isp_video_get_graph_data(struct isp_video *video, mutex_unlock(&mdev->graph_mutex); + media_entity_graph_walk_cleanup(&graph); + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { pipe->input = far_end; pipe->output = video; @@ -900,7 +908,7 @@ static int isp_video_check_external_subdevs(struct isp_video *video, for (i = 0; i < ARRAY_SIZE(ents); i++) { /* Is the entity part of the pipeline? */ - if (!(pipe->entities & (1 << ents[i]->id))) + if (!media_entity_enum_test(&pipe->ent_enum, ents[i])) continue; /* ISP entities have always sink pad == 0. Find source. */ @@ -918,7 +926,7 @@ static int isp_video_check_external_subdevs(struct isp_video *video, return -EINVAL; } - if (media_entity_type(source) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!is_media_entity_v4l2_subdev(source)) return 0; pipe->external = media_entity_to_v4l2_subdev(source); @@ -952,7 +960,8 @@ static int isp_video_check_external_subdevs(struct isp_video *video, pipe->external_rate = ctrl.value64; - if (pipe->entities & (1 << isp->isp_ccdc.subdev.entity.id)) { + if (media_entity_enum_test(&pipe->ent_enum, + &isp->isp_ccdc.subdev.entity)) { unsigned int rate = UINT_MAX; /* * Check that maximum allowed CCDC pixel rate isn't @@ -1018,7 +1027,9 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) pipe = video->video.entity.pipe ? to_isp_pipeline(&video->video.entity) : &video->pipe; - pipe->entities = 0; + ret = media_entity_enum_init(&pipe->ent_enum, &video->isp->media_dev); + if (ret) + goto err_enum_init; /* TODO: Implement PM QoS */ pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); @@ -1092,6 +1103,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) } mutex_unlock(&video->stream_lock); + return 0; err_set_stream: @@ -1112,7 +1124,11 @@ err_pipeline_start: INIT_LIST_HEAD(&video->dmaqueue); video->queue = NULL; + media_entity_enum_cleanup(&pipe->ent_enum); + +err_enum_init: mutex_unlock(&video->stream_lock); + return ret; } @@ -1164,6 +1180,8 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) /* TODO: Implement PM QoS */ media_entity_pipeline_stop(&video->video.entity); + media_entity_enum_cleanup(&pipe->ent_enum); + done: mutex_unlock(&video->stream_lock); return 0; @@ -1243,7 +1261,12 @@ static int isp_video_open(struct file *file) goto done; } - ret = omap3isp_pipeline_pm_use(&video->video.entity, 1); + ret = media_entity_graph_walk_init(&handle->graph, + &video->isp->media_dev); + if (ret) + goto done; + + ret = omap3isp_pipeline_pm_use(&video->video.entity, 1, &handle->graph); if (ret < 0) { omap3isp_put(video->isp); goto done; @@ -1274,6 +1297,7 @@ static int isp_video_open(struct file *file) done: if (ret < 0) { v4l2_fh_del(&handle->vfh); + media_entity_graph_walk_cleanup(&handle->graph); kfree(handle); } @@ -1293,7 +1317,8 @@ static int isp_video_release(struct file *file) vb2_queue_release(&handle->queue); mutex_unlock(&video->queue_lock); - omap3isp_pipeline_pm_use(&video->video.entity, 0); + omap3isp_pipeline_pm_use(&video->video.entity, 0, &handle->graph); + media_entity_graph_walk_cleanup(&handle->graph); /* Release the file handle. */ v4l2_fh_del(vfh); @@ -1367,7 +1392,7 @@ int omap3isp_video_init(struct isp_video *video, const char *name) if (IS_ERR(video->alloc_ctx)) return PTR_ERR(video->alloc_ctx); - ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); + ret = media_entity_pads_init(&video->video.entity, 1, &video->pad); if (ret < 0) { vb2_dma_contig_cleanup_ctx(video->alloc_ctx); return ret; diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/omap3isp/ispvideo.h index bcf0e0acc8f3..156429878d64 100644 --- a/drivers/media/platform/omap3isp/ispvideo.h +++ b/drivers/media/platform/omap3isp/ispvideo.h @@ -80,7 +80,7 @@ enum isp_pipeline_state { * struct isp_pipeline - An ISP hardware pipeline * @field: The field being processed by the pipeline * @error: A hardware error occurred during capture - * @entities: Bitmask of entities in the pipeline (indexed by entity ID) + * @ent_enum: Entities in the pipeline */ struct isp_pipeline { struct media_pipeline pipe; @@ -89,7 +89,7 @@ struct isp_pipeline { enum isp_pipeline_stream_state stream_state; struct isp_video *input; struct isp_video *output; - u32 entities; + struct media_entity_enum ent_enum; unsigned long l3_ick; unsigned int max_rate; enum v4l2_field field; @@ -189,6 +189,7 @@ struct isp_video_fh { struct vb2_queue queue; struct v4l2_format format; struct v4l2_fract timeperframe; + struct media_entity_graph graph; }; #define to_isp_video_fh(fh) container_of(fh, struct isp_video_fh, vfh) diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index ec3abbed87d9..bd060ef5d1e1 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -822,7 +822,7 @@ static int camif_pipeline_validate(struct camif_dev *camif) /* Retrieve format at the sensor subdev source pad */ pad = media_entity_remote_pad(&camif->pads[0]); - if (!pad || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) return -EPIPE; src_fmt.pad = pad->index; @@ -1144,7 +1144,7 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx) goto err_vd_rel; vp->pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_init(&vfd->entity, 1, &vp->pad, 0); + ret = media_entity_pads_init(&vfd->entity, 1, &vp->pad); if (ret) goto err_vd_rel; @@ -1559,8 +1559,8 @@ int s3c_camif_create_subdev(struct camif_dev *camif) camif->pads[CAMIF_SD_PAD_SOURCE_C].flags = MEDIA_PAD_FL_SOURCE; camif->pads[CAMIF_SD_PAD_SOURCE_P].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, CAMIF_SD_PADS_NUM, - camif->pads, 0); + ret = media_entity_pads_init(&sd->entity, CAMIF_SD_PADS_NUM, + camif->pads); if (ret) return ret; diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c index 1ba9bb08f5da..0b44b9accf50 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -263,7 +263,7 @@ static int camif_create_media_links(struct camif_dev *camif) { int i, ret; - ret = media_entity_create_link(&camif->sensor.sd->entity, 0, + ret = media_create_pad_link(&camif->sensor.sd->entity, 0, &camif->subdev.entity, CAMIF_SD_PAD_SINK, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); @@ -271,7 +271,7 @@ static int camif_create_media_links(struct camif_dev *camif) return ret; for (i = 1; i < CAMIF_SD_PADS_NUM && !ret; i++) { - ret = media_entity_create_link(&camif->subdev.entity, i, + ret = media_create_pad_link(&camif->subdev.entity, i, &camif->vp[i - 1].vdev.entity, 0, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); @@ -305,7 +305,7 @@ static void camif_unregister_media_entities(struct camif_dev *camif) /* * Media device */ -static int camif_media_dev_register(struct camif_dev *camif) +static int camif_media_dev_init(struct camif_dev *camif) { struct media_device *md = &camif->media_dev; struct v4l2_device *v4l2_dev = &camif->v4l2_dev; @@ -324,14 +324,12 @@ static int camif_media_dev_register(struct camif_dev *camif) strlcpy(v4l2_dev->name, "s3c-camif", sizeof(v4l2_dev->name)); v4l2_dev->mdev = md; + media_device_init(md); + ret = v4l2_device_register(camif->dev, v4l2_dev); if (ret < 0) return ret; - ret = media_device_register(md); - if (ret < 0) - v4l2_device_unregister(v4l2_dev); - return ret; } @@ -483,7 +481,7 @@ static int s3c_camif_probe(struct platform_device *pdev) goto err_alloc; } - ret = camif_media_dev_register(camif); + ret = camif_media_dev_init(camif); if (ret < 0) goto err_mdev; @@ -510,6 +508,11 @@ static int s3c_camif_probe(struct platform_device *pdev) goto err_unlock; mutex_unlock(&camif->media_dev.graph_mutex); + + ret = media_device_register(&camif->media_dev); + if (ret < 0) + goto err_sens; + pm_runtime_put(dev); return 0; @@ -518,6 +521,7 @@ err_unlock: err_sens: v4l2_device_unregister(&camif->v4l2_dev); media_device_unregister(&camif->media_dev); + media_device_cleanup(&camif->media_dev); camif_unregister_media_entities(camif); err_mdev: vb2_dma_contig_cleanup_ctx(camif->alloc_ctx); @@ -539,6 +543,7 @@ static int s3c_camif_remove(struct platform_device *pdev) struct s3c_camif_plat_data *pdata = &camif->pdata; media_device_unregister(&camif->media_dev); + media_device_cleanup(&camif->media_dev); camif_unregister_media_entities(camif); v4l2_device_unregister(&camif->v4l2_dev); diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 4e61886384e3..42dff9d020af 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -101,7 +101,7 @@ static int vsp1_create_links(struct vsp1_device *vsp1, struct vsp1_entity *sink) if (!(entity->pads[pad].flags & MEDIA_PAD_FL_SINK)) continue; - ret = media_entity_create_link(&source->subdev.entity, + ret = media_create_pad_link(&source->subdev.entity, source->source_pad, entity, pad, flags); if (ret < 0) @@ -127,6 +127,7 @@ static void vsp1_destroy_entities(struct vsp1_device *vsp1) v4l2_device_unregister(&vsp1->v4l2_dev); media_device_unregister(&vsp1->media_dev); + media_device_cleanup(&vsp1->media_dev); } static int vsp1_create_entities(struct vsp1_device *vsp1) @@ -141,12 +142,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) strlcpy(mdev->model, "VSP1", sizeof(mdev->model)); snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", dev_name(mdev->dev)); - ret = media_device_register(mdev); - if (ret < 0) { - dev_err(vsp1->dev, "media device registration failed (%d)\n", - ret); - return ret; - } + media_device_init(mdev); vdev->mdev = mdev; ret = v4l2_device_register(vsp1->dev, vdev); @@ -250,34 +246,44 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&wpf->entity.list_dev, &vsp1->entities); } - /* Create links. */ + /* Register all subdevs. */ list_for_each_entry(entity, &vsp1->entities, list_dev) { - if (entity->type == VSP1_ENTITY_LIF || - entity->type == VSP1_ENTITY_RPF) - continue; - - ret = vsp1_create_links(vsp1, entity); + ret = v4l2_device_register_subdev(&vsp1->v4l2_dev, + &entity->subdev); if (ret < 0) goto done; } + /* Create links. */ + list_for_each_entry(entity, &vsp1->entities, list_dev) { + if (entity->type == VSP1_ENTITY_LIF) { + ret = vsp1_wpf_create_links(vsp1, entity); + if (ret < 0) + goto done; + } else if (entity->type == VSP1_ENTITY_RPF) { + ret = vsp1_rpf_create_links(vsp1, entity); + if (ret < 0) + goto done; + } else { + ret = vsp1_create_links(vsp1, entity); + if (ret < 0) + goto done; + } + } + if (vsp1->pdata.features & VSP1_HAS_LIF) { - ret = media_entity_create_link( + ret = media_create_pad_link( &vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE, &vsp1->lif->entity.subdev.entity, LIF_PAD_SINK, 0); if (ret < 0) return ret; } - /* Register all subdevs. */ - list_for_each_entry(entity, &vsp1->entities, list_dev) { - ret = v4l2_device_register_subdev(&vsp1->v4l2_dev, - &entity->subdev); - if (ret < 0) - goto done; - } - ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev); + if (ret < 0) + goto done; + + ret = media_device_register(mdev); done: if (ret < 0) diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index fd95a75b04f4..d7308530952f 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -219,8 +219,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, entity->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE; /* Initialize the media entity. */ - return media_entity_init(&entity->subdev.entity, num_pads, - entity->pads, 0); + return media_entity_pads_init(&entity->subdev.entity, num_pads, + entity->pads); } void vsp1_entity_destroy(struct vsp1_entity *entity) diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index cd5248a9a271..924538223d3e 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -277,18 +277,29 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) rpf->entity.video = video; - /* Connect the video device to the RPF. */ - ret = media_entity_create_link(&rpf->video.video.entity, 0, - &rpf->entity.subdev.entity, - RWPF_PAD_SINK, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); - if (ret < 0) - goto error; - return rpf; error: vsp1_entity_destroy(&rpf->entity); return ERR_PTR(ret); } + +/* + * vsp1_rpf_create_links() - RPF pads links creation + * @vsp1: Pointer to VSP1 device + * @entity: Pointer to VSP1 entity + * + * return negative error code or zero on success + */ +int vsp1_rpf_create_links(struct vsp1_device *vsp1, + struct vsp1_entity *entity) +{ + struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); + + /* Connect the video device to the RPF. */ + return media_create_pad_link(&rpf->video.video.entity, 0, + &rpf->entity.subdev.entity, + RWPF_PAD_SINK, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index f452dce1a931..731d36e5258d 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -50,6 +50,11 @@ static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev) struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index); struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index); +int vsp1_rpf_create_links(struct vsp1_device *vsp1, + struct vsp1_entity *entity); +int vsp1_wpf_create_links(struct vsp1_device *vsp1, + struct vsp1_entity *entity); + int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code); diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 45eb65fa23db..637d0d6f79fb 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -160,8 +160,7 @@ vsp1_video_remote_subdev(struct media_pad *local, u32 *pad) struct media_pad *remote; remote = media_entity_remote_pad(local); - if (remote == NULL || - media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; if (pad) @@ -283,24 +282,35 @@ static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe, struct vsp1_rwpf *output) { struct vsp1_entity *entity; - unsigned int entities = 0; + struct media_entity_enum ent_enum; struct media_pad *pad; + int rval; bool bru_found = false; input->location.left = 0; input->location.top = 0; + rval = media_entity_enum_init( + &ent_enum, input->entity.pads[RWPF_PAD_SOURCE].graph_obj.mdev); + if (rval) + return rval; + pad = media_entity_remote_pad(&input->entity.pads[RWPF_PAD_SOURCE]); while (1) { - if (pad == NULL) - return -EPIPE; + if (pad == NULL) { + rval = -EPIPE; + goto out; + } /* We've reached a video node, that shouldn't have happened. */ - if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) - return -EPIPE; + if (!is_media_entity_v4l2_subdev(pad->entity)) { + rval = -EPIPE; + goto out; + } - entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); + entity = to_vsp1_entity( + media_entity_to_v4l2_subdev(pad->entity)); /* A BRU is present in the pipeline, store the compose rectangle * location in the input RPF for use when configuring the RPF. @@ -323,15 +333,18 @@ static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe, break; /* Ensure the branch has no loop. */ - if (entities & (1 << entity->subdev.entity.id)) - return -EPIPE; - - entities |= 1 << entity->subdev.entity.id; + if (media_entity_enum_test_and_set(&ent_enum, + &entity->subdev.entity)) { + rval = -EPIPE; + goto out; + } /* UDS can't be chained. */ if (entity->type == VSP1_ENTITY_UDS) { - if (pipe->uds) - return -EPIPE; + if (pipe->uds) { + rval = -EPIPE; + goto out; + } pipe->uds = entity; pipe->uds_input = bru_found ? pipe->bru @@ -349,9 +362,12 @@ static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe, /* The last entity must be the output WPF. */ if (entity != &output->entity) - return -EPIPE; + rval = -EPIPE; - return 0; +out: + media_entity_enum_cleanup(&ent_enum); + + return rval; } static void __vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe) @@ -380,13 +396,19 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, { struct media_entity_graph graph; struct media_entity *entity = &video->video.entity; - struct media_device *mdev = entity->parent; + struct media_device *mdev = entity->graph_obj.mdev; unsigned int i; int ret; mutex_lock(&mdev->graph_mutex); /* Walk the graph to locate the entities and video nodes. */ + ret = media_entity_graph_walk_init(&graph, mdev); + if (ret) { + mutex_unlock(&mdev->graph_mutex); + return ret; + } + media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { @@ -394,7 +416,7 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, struct vsp1_rwpf *rwpf; struct vsp1_entity *e; - if (media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV) { + if (is_media_entity_v4l2_io(entity)) { pipe->num_video++; continue; } @@ -420,6 +442,8 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, mutex_unlock(&mdev->graph_mutex); + media_entity_graph_walk_cleanup(&graph); + /* We need one output and at least one input. */ if (pipe->num_inputs == 0 || !pipe->output) { ret = -EPIPE; @@ -663,7 +687,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]); while (pad) { - if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!is_media_entity_v4l2_subdev(pad->entity)) break; entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); @@ -1193,7 +1217,7 @@ int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf) video->pipe.state = VSP1_PIPELINE_STOPPED; /* Initialize the media entity... */ - ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); + ret = media_entity_pads_init(&video->video.entity, 1, &video->pad); if (ret < 0) return ret; diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 95b62f4f77e7..cbf514a6582d 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -220,7 +220,6 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) struct v4l2_subdev *subdev; struct vsp1_video *video; struct vsp1_rwpf *wpf; - unsigned int flags; int ret; wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL); @@ -276,20 +275,6 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) goto error; wpf->entity.video = video; - - /* Connect the video device to the WPF. All connections are immutable - * except for the WPF0 source link if a LIF is present. - */ - flags = MEDIA_LNK_FL_ENABLED; - if (!(vsp1->pdata.features & VSP1_HAS_LIF) || index != 0) - flags |= MEDIA_LNK_FL_IMMUTABLE; - - ret = media_entity_create_link(&wpf->entity.subdev.entity, - RWPF_PAD_SOURCE, - &wpf->video.video.entity, 0, flags); - if (ret < 0) - goto error; - wpf->entity.sink = &wpf->video.video.entity; return wpf; @@ -298,3 +283,28 @@ error: vsp1_entity_destroy(&wpf->entity); return ERR_PTR(ret); } + +/* + * vsp1_wpf_create_links() - RPF pads links creation + * @vsp1: Pointer to VSP1 device + * @entity: Pointer to VSP1 entity + * + * return negative error code or zero on success + */ +int vsp1_wpf_create_links(struct vsp1_device *vsp1, + struct vsp1_entity *entity) +{ + struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); + unsigned int flags; + + /* Connect the video device to the WPF. All connections are immutable + * except for the WPF0 source link if a LIF is present. + */ + flags = MEDIA_LNK_FL_ENABLED; + if (!(vsp1->pdata.features & VSP1_HAS_LIF) || entity->index != 0) + flags |= MEDIA_LNK_FL_IMMUTABLE; + + return media_create_pad_link(&wpf->entity.subdev.entity, + RWPF_PAD_SOURCE, + &wpf->video.video.entity, 0, flags); +} diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c index 722758f33924..7f6898b13cac 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -49,8 +49,7 @@ xvip_dma_remote_subdev(struct media_pad *local, u32 *pad) struct media_pad *remote; remote = media_entity_remote_pad(local); - if (remote == NULL || - media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; if (pad) @@ -113,8 +112,7 @@ static int xvip_pipeline_start_stop(struct xvip_pipeline *pipe, bool start) break; pad = media_entity_remote_pad(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; entity = pad->entity; @@ -181,19 +179,26 @@ static int xvip_pipeline_validate(struct xvip_pipeline *pipe, { struct media_entity_graph graph; struct media_entity *entity = &start->video.entity; - struct media_device *mdev = entity->parent; + struct media_device *mdev = entity->graph_obj.mdev; unsigned int num_inputs = 0; unsigned int num_outputs = 0; + int ret; mutex_lock(&mdev->graph_mutex); /* Walk the graph to locate the video nodes. */ + ret = media_entity_graph_walk_init(&graph, entity->graph_obj.mdev); + if (ret) { + mutex_unlock(&mdev->graph_mutex); + return ret; + } + media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { struct xvip_dma *dma; - if (entity->type != MEDIA_ENT_T_DEVNODE_V4L) + if (entity->function != MEDIA_ENT_F_IO_V4L) continue; dma = to_xvip_dma(media_entity_to_video_device(entity)); @@ -208,6 +213,8 @@ static int xvip_pipeline_validate(struct xvip_pipeline *pipe, mutex_unlock(&mdev->graph_mutex); + media_entity_graph_walk_cleanup(&graph); + /* We need exactly one output and zero or one input. */ if (num_outputs != 1 || num_inputs > 1) return -EPIPE; @@ -677,7 +684,7 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, dma->pad.flags = type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&dma->video.entity, 1, &dma->pad, 0); + ret = media_entity_pads_init(&dma->video.entity, 1, &dma->pad); if (ret < 0) goto error; diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c index 8bd7e3736019..2ec1f6c4b274 100644 --- a/drivers/media/platform/xilinx/xilinx-tpg.c +++ b/drivers/media/platform/xilinx/xilinx-tpg.c @@ -838,7 +838,7 @@ static int xtpg_probe(struct platform_device *pdev) subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; subdev->entity.ops = &xtpg_media_ops; - ret = media_entity_init(&subdev->entity, xtpg->npads, xtpg->pads, 0); + ret = media_entity_pads_init(&subdev->entity, xtpg->npads, xtpg->pads); if (ret < 0) goto error; diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c index b9bf24fefa5a..e795a4501e8b 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.c +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -156,7 +156,7 @@ static int xvip_graph_build_one(struct xvip_composite_device *xdev, local->name, local_pad->index, remote->name, remote_pad->index); - ret = media_entity_create_link(local, local_pad->index, + ret = media_create_pad_link(local, local_pad->index, remote, remote_pad->index, link_flags); if (ret < 0) { @@ -270,7 +270,7 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev) source->name, source_pad->index, sink->name, sink_pad->index); - ret = media_entity_create_link(source, source_pad->index, + ret = media_create_pad_link(source, source_pad->index, sink, sink_pad->index, link_flags); if (ret < 0) { @@ -311,7 +311,7 @@ static int xvip_graph_notify_complete(struct v4l2_async_notifier *notifier) if (ret < 0) dev_err(xdev->dev, "failed to register subdev nodes\n"); - return ret; + return media_device_register(&xdev->media_dev); } static int xvip_graph_notify_bound(struct v4l2_async_notifier *notifier, @@ -573,6 +573,7 @@ static void xvip_composite_v4l2_cleanup(struct xvip_composite_device *xdev) { v4l2_device_unregister(&xdev->v4l2_dev); media_device_unregister(&xdev->media_dev); + media_device_cleanup(&xdev->media_dev); } static int xvip_composite_v4l2_init(struct xvip_composite_device *xdev) @@ -584,19 +585,14 @@ static int xvip_composite_v4l2_init(struct xvip_composite_device *xdev) sizeof(xdev->media_dev.model)); xdev->media_dev.hw_revision = 0; - ret = media_device_register(&xdev->media_dev); - if (ret < 0) { - dev_err(xdev->dev, "media device registration failed (%d)\n", - ret); - return ret; - } + media_device_init(&xdev->media_dev); xdev->v4l2_dev.mdev = &xdev->media_dev; ret = v4l2_device_register(xdev->dev, &xdev->v4l2_dev); if (ret < 0) { dev_err(xdev->dev, "V4L2 device registration failed (%d)\n", ret); - media_device_unregister(&xdev->media_dev); + media_device_cleanup(&xdev->media_dev); return ret; } diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index 6b469e8c4c6e..ca861aea68a5 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -228,6 +228,10 @@ void au0828_card_analog_fe_setup(struct au0828_dev *dev) "au8522", 0x8e >> 1, NULL); if (sd == NULL) pr_err("analog subdev registration failed\n"); +#ifdef CONFIG_MEDIA_CONTROLLER + if (sd) + dev->decoder = &sd->entity; +#endif } /* Setup tuners */ diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index 0934024fb89d..9e29e70a78d7 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -27,6 +27,9 @@ #include <media/v4l2-common.h> #include <linux/mutex.h> +/* Due to enum tuner_pad_index */ +#include <media/tuner.h> + /* * 1 = General debug messages * 2 = USB handling @@ -127,8 +130,23 @@ static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, return status; } +static void au0828_unregister_media_device(struct au0828_dev *dev) +{ + +#ifdef CONFIG_MEDIA_CONTROLLER + if (dev->media_dev) { + media_device_unregister(dev->media_dev); + media_device_cleanup(dev->media_dev); + kfree(dev->media_dev); + dev->media_dev = NULL; + } +#endif +} + static void au0828_usb_release(struct au0828_dev *dev) { + au0828_unregister_media_device(dev); + /* I2C */ au0828_i2c_unregister(dev); @@ -136,6 +154,20 @@ static void au0828_usb_release(struct au0828_dev *dev) } #ifdef CONFIG_VIDEO_AU0828_V4L2 + +static void au0828_usb_v4l2_media_release(struct au0828_dev *dev) +{ +#ifdef CONFIG_MEDIA_CONTROLLER + int i; + + for (i = 0; i < AU0828_MAX_INPUT; i++) { + if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED) + return; + media_device_unregister_entity(&dev->input_ent[i]); + } +#endif +} + static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev) { struct au0828_dev *dev = @@ -143,6 +175,7 @@ static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev) v4l2_ctrl_handler_free(&dev->v4l2_ctrl_hdl); v4l2_device_unregister(&dev->v4l2_dev); + au0828_usb_v4l2_media_release(dev); au0828_usb_release(dev); } #endif @@ -174,12 +207,123 @@ static void au0828_usb_disconnect(struct usb_interface *interface) au0828_analog_unregister(dev); v4l2_device_disconnect(&dev->v4l2_dev); v4l2_device_put(&dev->v4l2_dev); + /* + * No need to call au0828_usb_release() if V4L2 is enabled, + * as this is already called via au0828_usb_v4l2_release() + */ return; } #endif au0828_usb_release(dev); } +static int au0828_media_device_init(struct au0828_dev *dev, + struct usb_device *udev) +{ +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device *mdev; + + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + if (!mdev) + return -ENOMEM; + + mdev->dev = &udev->dev; + + if (!dev->board.name) + strlcpy(mdev->model, "unknown au0828", sizeof(mdev->model)); + else + strlcpy(mdev->model, dev->board.name, sizeof(mdev->model)); + if (udev->serial) + strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial)); + strcpy(mdev->bus_info, udev->devpath); + mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); + mdev->driver_version = LINUX_VERSION_CODE; + + media_device_init(mdev); + + dev->media_dev = mdev; +#endif + return 0; +} + + +static int au0828_create_media_graph(struct au0828_dev *dev) +{ +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device *mdev = dev->media_dev; + struct media_entity *entity; + struct media_entity *tuner = NULL, *decoder = NULL; + int i, ret; + + if (!mdev) + return 0; + + media_device_for_each_entity(entity, mdev) { + switch (entity->function) { + case MEDIA_ENT_F_TUNER: + tuner = entity; + break; + case MEDIA_ENT_F_ATV_DECODER: + decoder = entity; + break; + } + } + + /* Analog setup, using tuner as a link */ + + /* Something bad happened! */ + if (!decoder) + return -EINVAL; + + if (tuner) { + ret = media_create_pad_link(tuner, TUNER_PAD_IF_OUTPUT, + decoder, 0, + MEDIA_LNK_FL_ENABLED); + if (ret) + return ret; + } + ret = media_create_pad_link(decoder, 1, &dev->vdev.entity, 0, + MEDIA_LNK_FL_ENABLED); + if (ret) + return ret; + ret = media_create_pad_link(decoder, 2, &dev->vbi_dev.entity, 0, + MEDIA_LNK_FL_ENABLED); + if (ret) + return ret; + + for (i = 0; i < AU0828_MAX_INPUT; i++) { + struct media_entity *ent = &dev->input_ent[i]; + + if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED) + break; + + switch (AUVI_INPUT(i).type) { + case AU0828_VMUX_CABLE: + case AU0828_VMUX_TELEVISION: + case AU0828_VMUX_DVB: + if (!tuner) + break; + + ret = media_create_pad_link(ent, 0, tuner, + TUNER_PAD_RF_INPUT, + MEDIA_LNK_FL_ENABLED); + if (ret) + return ret; + break; + case AU0828_VMUX_COMPOSITE: + case AU0828_VMUX_SVIDEO: + default: /* AU0828_VMUX_DEBUG */ + /* FIXME: fix the decoder PAD */ + ret = media_create_pad_link(ent, 0, decoder, 0, 0); + if (ret) + return ret; + break; + } + } +#endif + return 0; +} + static int au0828_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -224,11 +368,23 @@ static int au0828_usb_probe(struct usb_interface *interface, dev->boardnr = id->driver_info; dev->board = au0828_boards[dev->boardnr]; + /* Initialize the media controller */ + retval = au0828_media_device_init(dev, usbdev); + if (retval) { + pr_err("%s() au0828_media_device_init failed\n", + __func__); + mutex_unlock(&dev->lock); + kfree(dev); + return retval; + } #ifdef CONFIG_VIDEO_AU0828_V4L2 dev->v4l2_dev.release = au0828_usb_v4l2_release; /* Create the v4l2_device */ +#ifdef CONFIG_MEDIA_CONTROLLER + dev->v4l2_dev.mdev = dev->media_dev; +#endif retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); if (retval) { pr_err("%s() v4l2_device_register failed\n", @@ -287,6 +443,21 @@ static int au0828_usb_probe(struct usb_interface *interface, mutex_unlock(&dev->lock); + retval = au0828_create_media_graph(dev); + if (retval) { + pr_err("%s() au0282_dev_register failed to create graph\n", + __func__); + goto done; + } + +#ifdef CONFIG_MEDIA_CONTROLLER + retval = media_device_register(dev->media_dev); +#endif + +done: + if (retval < 0) + au0828_usb_disconnect(interface); + return retval; } diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c index c267d76f5b3c..94363a3ba400 100644 --- a/drivers/media/usb/au0828/au0828-dvb.c +++ b/drivers/media/usb/au0828/au0828-dvb.c @@ -415,6 +415,11 @@ static int dvb_register(struct au0828_dev *dev) result); goto fail_adapter; } + +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + dvb->adapter.mdev = dev->media_dev; +#endif + dvb->adapter.priv = dev; /* register frontend */ @@ -480,8 +485,15 @@ static int dvb_register(struct au0828_dev *dev) dvb->start_count = 0; dvb->stop_count = 0; + + result = dvb_create_media_graph(&dvb->adapter, false); + if (result < 0) + goto fail_create_graph; + return 0; +fail_create_graph: + dvb_net_release(&dvb->net); fail_fe_conn: dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); fail_fe_mem: diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 0a725a161dd6..8c54fd21022e 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -638,6 +638,64 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) return rc; } +static int au0828_enable_analog_tuner(struct au0828_dev *dev) +{ +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device *mdev = dev->media_dev; + struct media_entity *source; + struct media_link *link, *found_link = NULL; + int ret, active_links = 0; + + if (!mdev || !dev->decoder) + return 0; + + /* + * This will find the tuner that is connected into the decoder. + * Technically, this is not 100% correct, as the device may be + * using an analog input instead of the tuner. However, as we can't + * do DVB streaming while the DMA engine is being used for V4L2, + * this should be enough for the actual needs. + */ + list_for_each_entry(link, &dev->decoder->links, list) { + if (link->sink->entity == dev->decoder) { + found_link = link; + if (link->flags & MEDIA_LNK_FL_ENABLED) + active_links++; + break; + } + } + + if (active_links == 1 || !found_link) + return 0; + + source = found_link->source->entity; + list_for_each_entry(link, &source->links, list) { + struct media_entity *sink; + int flags = 0; + + sink = link->sink->entity; + + if (sink == dev->decoder) + flags = MEDIA_LNK_FL_ENABLED; + + ret = media_entity_setup_link(link, flags); + if (ret) { + pr_err( + "Couldn't change link %s->%s to %s. Error %d\n", + source->name, sink->name, + flags ? "enabled" : "disabled", + ret); + return ret; + } else + au0828_isocdbg( + "link %s->%s was %s\n", + source->name, sink->name, + flags ? "ENABLED" : "disabled"); + } +#endif + return 0; +} + static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) @@ -650,6 +708,8 @@ static int queue_setup(struct vb2_queue *vq, *nplanes = 1; sizes[0] = size; + au0828_enable_analog_tuner(dev); + return 0; } @@ -1735,6 +1795,69 @@ static int au0828_vb2_setup(struct au0828_dev *dev) return 0; } +static void au0828_analog_create_entities(struct au0828_dev *dev) +{ +#if defined(CONFIG_MEDIA_CONTROLLER) + static const char * const inames[] = { + [AU0828_VMUX_COMPOSITE] = "Composite", + [AU0828_VMUX_SVIDEO] = "S-Video", + [AU0828_VMUX_CABLE] = "Cable TV", + [AU0828_VMUX_TELEVISION] = "Television", + [AU0828_VMUX_DVB] = "DVB", + [AU0828_VMUX_DEBUG] = "tv debug" + }; + int ret, i; + + /* Initialize Video and VBI pads */ + dev->video_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&dev->vdev.entity, 1, &dev->video_pad); + if (ret < 0) + pr_err("failed to initialize video media entity!\n"); + + dev->vbi_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&dev->vbi_dev.entity, 1, &dev->vbi_pad); + if (ret < 0) + pr_err("failed to initialize vbi media entity!\n"); + + /* Create entities for each input connector */ + for (i = 0; i < AU0828_MAX_INPUT; i++) { + struct media_entity *ent = &dev->input_ent[i]; + + if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED) + break; + + ent->name = inames[AUVI_INPUT(i).type]; + ent->flags = MEDIA_ENT_FL_CONNECTOR; + dev->input_pad[i].flags = MEDIA_PAD_FL_SOURCE; + + switch (AUVI_INPUT(i).type) { + case AU0828_VMUX_COMPOSITE: + ent->function = MEDIA_ENT_F_CONN_COMPOSITE; + break; + case AU0828_VMUX_SVIDEO: + ent->function = MEDIA_ENT_F_CONN_SVIDEO; + break; + case AU0828_VMUX_CABLE: + case AU0828_VMUX_TELEVISION: + case AU0828_VMUX_DVB: + ent->function = MEDIA_ENT_F_CONN_RF; + break; + default: /* AU0828_VMUX_DEBUG */ + ent->function = MEDIA_ENT_F_CONN_TEST; + break; + } + + ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]); + if (ret < 0) + pr_err("failed to initialize input pad[%d]!\n", i); + + ret = media_device_register_entity(dev->media_dev, ent); + if (ret < 0) + pr_err("failed to register input entity %d!\n", i); + } +#endif +} + /**************************************************************************/ int au0828_analog_register(struct au0828_dev *dev, @@ -1823,6 +1946,9 @@ int au0828_analog_register(struct au0828_dev *dev, dev->vbi_dev.queue->lock = &dev->vb_vbi_queue_lock; strcpy(dev->vbi_dev.name, "au0828a vbi"); + /* Init entities at the Media Controller */ + au0828_analog_create_entities(dev); + /* initialize videobuf2 stuff */ retval = au0828_vb2_setup(dev); if (retval != 0) { diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index 60b59391ea2a..8276072bc55a 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -33,6 +33,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-fh.h> +#include <media/media-device.h> /* DVB */ #include "demux.h" @@ -93,7 +94,6 @@ struct au0828_board { unsigned char has_ir_i2c:1; unsigned char has_analog:1; struct au0828_input input[AU0828_MAX_INPUT]; - }; struct au0828_dvb { @@ -276,6 +276,14 @@ struct au0828_dev { /* Preallocated transfer digital transfer buffers */ char *dig_transfer_buffer[URB_COUNT]; + +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device *media_dev; + struct media_pad video_pad, vbi_pad; + struct media_entity *decoder; + struct media_entity input_ent[AU0828_MAX_INPUT]; + struct media_pad input_pad[AU0828_MAX_INPUT]; +#endif }; diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 89dc695c696e..620b83d03f75 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1172,6 +1172,7 @@ static void cx231xx_unregister_media_device(struct cx231xx *dev) #ifdef CONFIG_MEDIA_CONTROLLER if (dev->media_dev) { media_device_unregister(dev->media_dev); + media_device_cleanup(dev->media_dev); kfree(dev->media_dev); dev->media_dev = NULL; } @@ -1185,8 +1186,6 @@ static void cx231xx_unregister_media_device(struct cx231xx *dev) */ void cx231xx_release_resources(struct cx231xx *dev) { - cx231xx_unregister_media_device(dev); - cx231xx_release_analog_resources(dev); cx231xx_remove_from_devlist(dev); @@ -1199,22 +1198,23 @@ void cx231xx_release_resources(struct cx231xx *dev) /* delete v4l2 device */ v4l2_device_unregister(&dev->v4l2_dev); + cx231xx_unregister_media_device(dev); + usb_put_dev(dev->udev); /* Mark device as unused */ clear_bit(dev->devno, &cx231xx_devused); } -static void cx231xx_media_device_register(struct cx231xx *dev, - struct usb_device *udev) +static int cx231xx_media_device_init(struct cx231xx *dev, + struct usb_device *udev) { #ifdef CONFIG_MEDIA_CONTROLLER struct media_device *mdev; - int ret; mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); if (!mdev) - return; + return -ENOMEM; mdev->dev = dev->dev; strlcpy(mdev->model, dev->board.name, sizeof(mdev->model)); @@ -1224,35 +1224,30 @@ static void cx231xx_media_device_register(struct cx231xx *dev, mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); mdev->driver_version = LINUX_VERSION_CODE; - ret = media_device_register(mdev); - if (ret) { - dev_err(dev->dev, - "Couldn't create a media device. Error: %d\n", - ret); - kfree(mdev); - return; - } + media_device_init(mdev); dev->media_dev = mdev; #endif + return 0; } -static void cx231xx_create_media_graph(struct cx231xx *dev) +static int cx231xx_create_media_graph(struct cx231xx *dev) { #ifdef CONFIG_MEDIA_CONTROLLER struct media_device *mdev = dev->media_dev; struct media_entity *entity; struct media_entity *tuner = NULL, *decoder = NULL; + int ret; if (!mdev) - return; + return 0; media_device_for_each_entity(entity, mdev) { - switch (entity->type) { - case MEDIA_ENT_T_V4L2_SUBDEV_TUNER: + switch (entity->function) { + case MEDIA_ENT_F_TUNER: tuner = entity; break; - case MEDIA_ENT_T_V4L2_SUBDEV_DECODER: + case MEDIA_ENT_F_ATV_DECODER: decoder = entity; break; } @@ -1261,16 +1256,24 @@ static void cx231xx_create_media_graph(struct cx231xx *dev) /* Analog setup, using tuner as a link */ if (!decoder) - return; + return 0; - if (tuner) - media_entity_create_link(tuner, 0, decoder, 0, - MEDIA_LNK_FL_ENABLED); - media_entity_create_link(decoder, 1, &dev->vdev.entity, 0, - MEDIA_LNK_FL_ENABLED); - media_entity_create_link(decoder, 2, &dev->vbi_dev.entity, 0, - MEDIA_LNK_FL_ENABLED); + if (tuner) { + ret = media_create_pad_link(tuner, TUNER_PAD_IF_OUTPUT, decoder, 0, + MEDIA_LNK_FL_ENABLED); + if (ret < 0) + return ret; + } + ret = media_create_pad_link(decoder, 1, &dev->vdev.entity, 0, + MEDIA_LNK_FL_ENABLED); + if (ret < 0) + return ret; + ret = media_create_pad_link(decoder, 2, &dev->vbi_dev.entity, 0, + MEDIA_LNK_FL_ENABLED); + if (ret < 0) + return ret; #endif + return 0; } /* @@ -1660,8 +1663,12 @@ static int cx231xx_usb_probe(struct usb_interface *interface, /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); - /* Register the media controller */ - cx231xx_media_device_register(dev, udev); + /* Initialize the media controller */ + retval = cx231xx_media_device_init(dev, udev); + if (retval) { + dev_err(d, "cx231xx_media_device_init failed\n"); + goto err_media_init; + } /* Create v4l2 device */ #ifdef CONFIG_MEDIA_CONTROLLER @@ -1732,9 +1739,19 @@ static int cx231xx_usb_probe(struct usb_interface *interface, /* load other modules required */ request_modules(dev); - cx231xx_create_media_graph(dev); + retval = cx231xx_create_media_graph(dev); + if (retval < 0) + goto done; + +#ifdef CONFIG_MEDIA_CONTROLLER + retval = media_device_register(dev->media_dev); +#endif + +done: + if (retval < 0) + cx231xx_release_resources(dev); + return retval; - return 0; err_video_alt: /* cx231xx_uninit_dev: */ cx231xx_close_extension(dev); @@ -1746,6 +1763,8 @@ err_video_alt: err_init: v4l2_device_unregister(&dev->v4l2_dev); err_v4l2: + cx231xx_unregister_media_device(dev); +err_media_init: usb_set_intfdata(interface, NULL); err_if: usb_put_dev(udev); diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index e3594b9fab4a..b8d5b2be9293 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -551,10 +551,14 @@ static int register_dvb(struct cx231xx_dvb *dvb, /* register network adapter */ dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); - dvb_create_media_graph(&dvb->adapter); + result = dvb_create_media_graph(&dvb->adapter, false); + if (result < 0) + goto fail_create_graph; return 0; +fail_create_graph: + dvb_net_release(&dvb->net); fail_fe_conn: dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); fail_fe_mem: diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index a70850fe6235..9b88cd8127ac 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -106,7 +106,7 @@ static int cx231xx_enable_analog_tuner(struct cx231xx *dev) struct media_device *mdev = dev->media_dev; struct media_entity *entity, *decoder = NULL, *source; struct media_link *link, *found_link = NULL; - int i, ret, active_links = 0; + int ret, active_links = 0; if (!mdev) return 0; @@ -119,7 +119,7 @@ static int cx231xx_enable_analog_tuner(struct cx231xx *dev) * this should be enough for the actual needs. */ media_device_for_each_entity(entity, mdev) { - if (entity->type == MEDIA_ENT_T_V4L2_SUBDEV_DECODER) { + if (entity->function == MEDIA_ENT_F_ATV_DECODER) { decoder = entity; break; } @@ -127,8 +127,7 @@ static int cx231xx_enable_analog_tuner(struct cx231xx *dev) if (!decoder) return 0; - for (i = 0; i < decoder->num_links; i++) { - link = &decoder->links[i]; + list_for_each_entry(link, &decoder->links, list) { if (link->sink->entity == decoder) { found_link = link; if (link->flags & MEDIA_LNK_FL_ENABLED) @@ -141,11 +140,10 @@ static int cx231xx_enable_analog_tuner(struct cx231xx *dev) return 0; source = found_link->source->entity; - for (i = 0; i < source->num_links; i++) { + list_for_each_entry(link, &source->links, list) { struct media_entity *sink; int flags = 0; - link = &source->links[i]; sink = link->sink->entity; if (sink == entity) @@ -2177,7 +2175,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) cx231xx_vdev_init(dev, &dev->vdev, &cx231xx_video_template, "video"); #if defined(CONFIG_MEDIA_CONTROLLER) dev->video_pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_init(&dev->vdev.entity, 1, &dev->video_pad, 0); + ret = media_entity_pads_init(&dev->vdev.entity, 1, &dev->video_pad); if (ret < 0) dev_err(dev->dev, "failed to initialize video media entity!\n"); #endif @@ -2204,7 +2202,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) #if defined(CONFIG_MEDIA_CONTROLLER) dev->vbi_pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_init(&dev->vbi_dev.entity, 1, &dev->vbi_pad, 0); + ret = media_entity_pads_init(&dev->vbi_dev.entity, 1, &dev->vbi_pad); if (ret < 0) dev_err(dev->dev, "failed to initialize vbi media entity!\n"); #endif diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index f5df9eaba04f..f0565bf3673e 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -400,17 +400,16 @@ skip_feed_stop: return ret; } -static void dvb_usbv2_media_device_register(struct dvb_usb_adapter *adap) +static int dvb_usbv2_media_device_init(struct dvb_usb_adapter *adap) { #ifdef CONFIG_MEDIA_CONTROLLER_DVB struct media_device *mdev; struct dvb_usb_device *d = adap_to_d(adap); struct usb_device *udev = d->udev; - int ret; mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); if (!mdev) - return; + return -ENOMEM; mdev->dev = &udev->dev; strlcpy(mdev->model, d->name, sizeof(mdev->model)); @@ -420,19 +419,21 @@ static void dvb_usbv2_media_device_register(struct dvb_usb_adapter *adap) mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); mdev->driver_version = LINUX_VERSION_CODE; - ret = media_device_register(mdev); - if (ret) { - dev_err(&d->udev->dev, - "Couldn't create a media device. Error: %d\n", - ret); - kfree(mdev); - return; - } + media_device_init(mdev); dvb_register_media_controller(&adap->dvb_adap, mdev); dev_info(&d->udev->dev, "media controller created\n"); +#endif + return 0; +} +static int dvb_usbv2_media_device_register(struct dvb_usb_adapter *adap) +{ +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + return media_device_register(adap->dvb_adap.mdev); +#else + return 0; #endif } @@ -444,6 +445,7 @@ static void dvb_usbv2_media_device_unregister(struct dvb_usb_adapter *adap) return; media_device_unregister(adap->dvb_adap.mdev); + media_device_cleanup(adap->dvb_adap.mdev); kfree(adap->dvb_adap.mdev); adap->dvb_adap.mdev = NULL; @@ -467,7 +469,12 @@ static int dvb_usbv2_adapter_dvb_init(struct dvb_usb_adapter *adap) adap->dvb_adap.priv = adap; - dvb_usbv2_media_device_register(adap); + ret = dvb_usbv2_media_device_init(adap); + if (ret < 0) { + dev_dbg(&d->udev->dev, "%s: dvb_usbv2_media_device_init() failed=%d\n", + __func__, ret); + goto err_dvb_register_mc; + } if (d->props->read_mac_address) { ret = d->props->read_mac_address(adap, @@ -518,6 +525,7 @@ err_dvb_dmxdev_init: dvb_dmx_release(&adap->demux); err_dvb_dmx_init: dvb_usbv2_media_device_unregister(adap); +err_dvb_register_mc: dvb_unregister_adapter(&adap->dvb_adap); err_dvb_register_adapter: adap->dvb_adap.priv = NULL; @@ -534,7 +542,6 @@ static int dvb_usbv2_adapter_dvb_exit(struct dvb_usb_adapter *adap) adap->demux.dmx.close(&adap->demux.dmx); dvb_dmxdev_release(&adap->dmxdev); dvb_dmx_release(&adap->demux); - dvb_usbv2_media_device_unregister(adap); dvb_unregister_adapter(&adap->dvb_adap); } @@ -698,9 +705,13 @@ static int dvb_usbv2_adapter_frontend_init(struct dvb_usb_adapter *adap) } } - dvb_create_media_graph(&adap->dvb_adap); + ret = dvb_create_media_graph(&adap->dvb_adap, true); + if (ret < 0) + goto err_dvb_unregister_frontend; - return 0; + ret = dvb_usbv2_media_device_register(adap); + + return ret; err_dvb_unregister_frontend: for (i = count_registered - 1; i >= 0; i--) @@ -840,6 +851,7 @@ static int dvb_usbv2_adapter_exit(struct dvb_usb_device *d) dvb_usbv2_adapter_dvb_exit(&d->adapter[i]); dvb_usbv2_adapter_stream_exit(&d->adapter[i]); dvb_usbv2_adapter_frontend_exit(&d->adapter[i]); + dvb_usbv2_media_device_unregister(&d->adapter[i]); } } diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c index 1710f9038d75..b669deccc34c 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c @@ -10,6 +10,7 @@ #include <linux/vmalloc.h> #include <linux/i2c.h> +#include <media/tuner.h> #include "mxl111sf.h" #include "mxl111sf-reg.h" @@ -868,6 +869,10 @@ static struct mxl111sf_tuner_config mxl_tuner_config = { static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap) { struct mxl111sf_state *state = adap_to_priv(adap); +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + struct media_device *mdev = dvb_get_media_controller(&adap->dvb_adap); + int ret; +#endif int i; pr_debug("%s()\n", __func__); @@ -879,6 +884,21 @@ static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap) adap->fe[i]->ops.read_signal_strength = adap->fe[i]->ops.tuner_ops.get_rf_strength; } +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + state->tuner.function = MEDIA_ENT_F_TUNER; + state->tuner.name = "mxl111sf tuner"; + state->tuner_pads[TUNER_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK; + state->tuner_pads[TUNER_PAD_IF_OUTPUT].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&state->tuner, + TUNER_NUM_PADS, state->tuner_pads); + if (ret) + return ret; + + ret = media_device_register_entity(mdev, &state->tuner); + if (ret) + return ret; +#endif return 0; } diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.h b/drivers/media/usb/dvb-usb-v2/mxl111sf.h index ee70df1f1e94..846260e0eec0 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.h @@ -17,6 +17,7 @@ #define DVB_USB_LOG_PREFIX "mxl111sf" #include "dvb_usb.h" #include <media/tveeprom.h> +#include <media/media-entity.h> #define MXL_EP1_REG_READ 1 #define MXL_EP2_REG_WRITE 2 @@ -85,6 +86,10 @@ struct mxl111sf_state { struct mutex fe_lock; u8 num_frontends; struct mxl111sf_adap_state adap_state[3]; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + struct media_entity tuner; + struct media_pad tuner_pads[2]; +#endif }; int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data); diff --git a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c index 8a260c854653..9ddfcab268be 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c @@ -95,17 +95,16 @@ static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) return dvb_usb_ctrl_feed(dvbdmxfeed, 0); } -static void dvb_usb_media_device_register(struct dvb_usb_adapter *adap) +static int dvb_usb_media_device_init(struct dvb_usb_adapter *adap) { #ifdef CONFIG_MEDIA_CONTROLLER_DVB struct media_device *mdev; struct dvb_usb_device *d = adap->dev; struct usb_device *udev = d->udev; - int ret; mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); if (!mdev) - return; + return -ENOMEM; mdev->dev = &udev->dev; strlcpy(mdev->model, d->desc->name, sizeof(mdev->model)); @@ -115,18 +114,22 @@ static void dvb_usb_media_device_register(struct dvb_usb_adapter *adap) mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); mdev->driver_version = LINUX_VERSION_CODE; - ret = media_device_register(mdev); - if (ret) { - dev_err(&d->udev->dev, - "Couldn't create a media device. Error: %d\n", - ret); - kfree(mdev); - return; - } + media_device_init(mdev); + dvb_register_media_controller(&adap->dvb_adap, mdev); dev_info(&d->udev->dev, "media controller created\n"); #endif + return 0; +} + +static int dvb_usb_media_device_register(struct dvb_usb_adapter *adap) +{ +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + return media_device_register(adap->dvb_adap.mdev); +#else + return 0; +#endif } static void dvb_usb_media_device_unregister(struct dvb_usb_adapter *adap) @@ -136,6 +139,7 @@ static void dvb_usb_media_device_unregister(struct dvb_usb_adapter *adap) return; media_device_unregister(adap->dvb_adap.mdev); + media_device_cleanup(adap->dvb_adap.mdev); kfree(adap->dvb_adap.mdev); adap->dvb_adap.mdev = NULL; #endif @@ -154,7 +158,11 @@ int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums) } adap->dvb_adap.priv = adap; - dvb_usb_media_device_register(adap); + ret = dvb_usb_media_device_init(adap); + if (ret < 0) { + deb_info("dvb_usb_media_device_init failed: error %d", ret); + goto err_mc; + } if (adap->dev->props.read_mac_address) { if (adap->dev->props.read_mac_address(adap->dev, adap->dvb_adap.proposed_mac) == 0) @@ -204,6 +212,7 @@ err_dmx_dev: dvb_dmx_release(&adap->demux); err_dmx: dvb_usb_media_device_unregister(adap); +err_mc: dvb_unregister_adapter(&adap->dvb_adap); err: return ret; @@ -318,10 +327,16 @@ int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap) adap->num_frontends_initialized++; } + if (ret) + return ret; - dvb_create_media_graph(&adap->dvb_adap); + ret = dvb_create_media_graph(&adap->dvb_adap, true); + if (ret) + return ret; - return 0; + ret = dvb_usb_media_device_register(adap); + + return ret; } int dvb_usb_adapter_frontend_exit(struct dvb_usb_adapter *adap) diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index c945e4c2fbd4..8abbd3cc8eba 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -361,10 +361,11 @@ static void *siano_media_device_register(struct smsusb_device_t *dev, mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); mdev->driver_version = LINUX_VERSION_CODE; + media_device_init(mdev); + ret = media_device_register(mdev); if (ret) { - pr_err("Couldn't create a media device. Error: %d\n", - ret); + media_device_cleanup(mdev); kfree(mdev); return NULL; } diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 39abbafad796..4e7148815a78 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1656,6 +1656,7 @@ static void uvc_delete(struct uvc_device *dev) #ifdef CONFIG_MEDIA_CONTROLLER if (media_devnode_is_registered(&dev->mdev.devnode)) media_device_unregister(&dev->mdev); + media_device_cleanup(&dev->mdev); #endif list_for_each_safe(p, n, &dev->chains) { @@ -1906,7 +1907,7 @@ static int uvc_probe(struct usb_interface *intf, "linux-uvc-devel mailing list.\n"); } - /* Register the media and V4L2 devices. */ + /* Initialize the media device and register the V4L2 device. */ #ifdef CONFIG_MEDIA_CONTROLLER dev->mdev.dev = &intf->dev; strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); @@ -1916,8 +1917,7 @@ static int uvc_probe(struct usb_interface *intf, strcpy(dev->mdev.bus_info, udev->devpath); dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); dev->mdev.driver_version = LINUX_VERSION_CODE; - if (media_device_register(&dev->mdev) < 0) - goto error; + media_device_init(&dev->mdev); dev->vdev.mdev = &dev->mdev; #endif @@ -1936,6 +1936,11 @@ static int uvc_probe(struct usb_interface *intf, if (uvc_register_chains(dev) < 0) goto error; +#ifdef CONFIG_MEDIA_CONTROLLER + /* Register the media device node */ + if (media_device_register(&dev->mdev) < 0) + goto error; +#endif /* Save our data pointer in the interface data. */ usb_set_intfdata(intf, dev); diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c index dc56a59ecadc..ac386bb547e6 100644 --- a/drivers/media/usb/uvc/uvc_entity.c +++ b/drivers/media/usb/uvc/uvc_entity.c @@ -19,12 +19,8 @@ #include "uvcvideo.h" -/* ------------------------------------------------------------------------ - * Video subdevices registration and unregistration - */ - -static int uvc_mc_register_entity(struct uvc_video_chain *chain, - struct uvc_entity *entity) +static int uvc_mc_create_links(struct uvc_video_chain *chain, + struct uvc_entity *entity) { const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; struct media_entity *sink; @@ -56,16 +52,13 @@ static int uvc_mc_register_entity(struct uvc_video_chain *chain, continue; remote_pad = remote->num_pads - 1; - ret = media_entity_create_link(source, remote_pad, + ret = media_create_pad_link(source, remote_pad, sink, i, flags); if (ret < 0) return ret; } - if (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING) - return 0; - - return v4l2_device_register_subdev(&chain->dev->vdev, &entity->subdev); + return 0; } static struct v4l2_subdev_ops uvc_subdev_ops = { @@ -79,7 +72,8 @@ void uvc_mc_cleanup_entity(struct uvc_entity *entity) media_entity_cleanup(&entity->vdev->entity); } -static int uvc_mc_init_entity(struct uvc_entity *entity) +static int uvc_mc_init_entity(struct uvc_video_chain *chain, + struct uvc_entity *entity) { int ret; @@ -88,11 +82,17 @@ static int uvc_mc_init_entity(struct uvc_entity *entity) strlcpy(entity->subdev.name, entity->name, sizeof(entity->subdev.name)); - ret = media_entity_init(&entity->subdev.entity, - entity->num_pads, entity->pads, 0); + ret = media_entity_pads_init(&entity->subdev.entity, + entity->num_pads, entity->pads); + + if (ret < 0) + return ret; + + ret = v4l2_device_register_subdev(&chain->dev->vdev, + &entity->subdev); } else if (entity->vdev != NULL) { - ret = media_entity_init(&entity->vdev->entity, - entity->num_pads, entity->pads, 0); + ret = media_entity_pads_init(&entity->vdev->entity, + entity->num_pads, entity->pads); if (entity->flags & UVC_ENTITY_FLAG_DEFAULT) entity->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; } else @@ -107,7 +107,7 @@ int uvc_mc_register_entities(struct uvc_video_chain *chain) int ret; list_for_each_entry(entity, &chain->entities, chain) { - ret = uvc_mc_init_entity(entity); + ret = uvc_mc_init_entity(chain, entity); if (ret < 0) { uvc_printk(KERN_INFO, "Failed to initialize entity for " "entity %u\n", entity->id); @@ -116,9 +116,9 @@ int uvc_mc_register_entities(struct uvc_video_chain *chain) } list_for_each_entry(entity, &chain->entities, chain) { - ret = uvc_mc_register_entity(chain, entity); + ret = uvc_mc_create_links(chain, entity); if (ret < 0) { - uvc_printk(KERN_INFO, "Failed to register entity for " + uvc_printk(KERN_INFO, "Failed to create links for " "entity %u\n", entity->id); return ret; } diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index 581e21ad6801..76496fd282aa 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -134,8 +134,9 @@ struct tuner { unsigned int type; /* chip type id */ void *config; const char *name; + #if defined(CONFIG_MEDIA_CONTROLLER) - struct media_pad pad; + struct media_pad pad[TUNER_NUM_PADS]; #endif }; @@ -695,11 +696,12 @@ static int tuner_probe(struct i2c_client *client, /* Should be just before return */ register_client: #if defined(CONFIG_MEDIA_CONTROLLER) - t->pad.flags = MEDIA_PAD_FL_SOURCE; - t->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_TUNER; + t->pad[TUNER_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK; + t->pad[TUNER_PAD_IF_OUTPUT].flags = MEDIA_PAD_FL_SOURCE; + t->sd.entity.function = MEDIA_ENT_F_TUNER; t->sd.entity.name = t->name; - ret = media_entity_init(&t->sd.entity, 1, &t->pad, 0); + ret = media_entity_pads_init(&t->sd.entity, TUNER_NUM_PADS, &t->pad[0]); if (ret < 0) { tuner_err("failed to initialize media entity!\n"); kfree(t); diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 6b1eaeddbdb3..d8e5994cccf1 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -194,9 +194,12 @@ static void v4l2_device_release(struct device *cd) mutex_unlock(&videodev_lock); #if defined(CONFIG_MEDIA_CONTROLLER) - if (v4l2_dev->mdev && - vdev->vfl_type != VFL_TYPE_SUBDEV) - media_device_unregister_entity(&vdev->entity); + if (v4l2_dev->mdev) { + /* Remove interfaces and interface links */ + media_devnode_remove(vdev->intf_devnode); + if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN) + media_device_unregister_entity(&vdev->entity); + } #endif /* Do not call v4l2_device_put if there is no release callback set. @@ -723,6 +726,91 @@ static void determine_valid_ioctls(struct video_device *vdev) BASE_VIDIOC_PRIVATE); } +static int video_register_media_controller(struct video_device *vdev, int type) +{ +#if defined(CONFIG_MEDIA_CONTROLLER) + u32 intf_type; + int ret; + + if (!vdev->v4l2_dev->mdev) + return 0; + + vdev->entity.function = MEDIA_ENT_F_UNKNOWN; + + switch (type) { + case VFL_TYPE_GRABBER: + intf_type = MEDIA_INTF_T_V4L_VIDEO; + vdev->entity.function = MEDIA_ENT_F_IO_V4L; + break; + case VFL_TYPE_VBI: + intf_type = MEDIA_INTF_T_V4L_VBI; + vdev->entity.function = MEDIA_ENT_F_IO_VBI; + break; + case VFL_TYPE_SDR: + intf_type = MEDIA_INTF_T_V4L_SWRADIO; + vdev->entity.function = MEDIA_ENT_F_IO_SWRADIO; + break; + case VFL_TYPE_RADIO: + intf_type = MEDIA_INTF_T_V4L_RADIO; + /* + * Radio doesn't have an entity at the V4L2 side to represent + * radio input or output. Instead, the audio input/output goes + * via either physical wires or ALSA. + */ + break; + case VFL_TYPE_SUBDEV: + intf_type = MEDIA_INTF_T_V4L_SUBDEV; + /* Entity will be created via v4l2_device_register_subdev() */ + break; + default: + return 0; + } + + if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN) { + vdev->entity.name = vdev->name; + + /* Needed just for backward compatibility with legacy MC API */ + vdev->entity.info.dev.major = VIDEO_MAJOR; + vdev->entity.info.dev.minor = vdev->minor; + + ret = media_device_register_entity(vdev->v4l2_dev->mdev, + &vdev->entity); + if (ret < 0) { + printk(KERN_WARNING + "%s: media_device_register_entity failed\n", + __func__); + return ret; + } + } + + vdev->intf_devnode = media_devnode_create(vdev->v4l2_dev->mdev, + intf_type, + 0, VIDEO_MAJOR, + vdev->minor); + if (!vdev->intf_devnode) { + media_device_unregister_entity(&vdev->entity); + return -ENOMEM; + } + + if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN) { + struct media_link *link; + + link = media_create_intf_link(&vdev->entity, + &vdev->intf_devnode->intf, + MEDIA_LNK_FL_ENABLED); + if (!link) { + media_devnode_remove(vdev->intf_devnode); + media_device_unregister_entity(&vdev->entity); + return -ENOMEM; + } + } + + /* FIXME: how to create the other interface links? */ + +#endif + return 0; +} + /** * __video_register_device - register video4linux devices * @vdev: video device structure we want to register @@ -918,22 +1006,9 @@ int __video_register_device(struct video_device *vdev, int type, int nr, /* Increase v4l2_device refcount */ v4l2_device_get(vdev->v4l2_dev); -#if defined(CONFIG_MEDIA_CONTROLLER) /* Part 5: Register the entity. */ - if (vdev->v4l2_dev->mdev && - vdev->vfl_type != VFL_TYPE_SUBDEV) { - vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L; - vdev->entity.name = vdev->name; - vdev->entity.info.dev.major = VIDEO_MAJOR; - vdev->entity.info.dev.minor = vdev->minor; - ret = media_device_register_entity(vdev->v4l2_dev->mdev, - &vdev->entity); - if (ret < 0) - printk(KERN_WARNING - "%s: media_device_register_entity failed\n", - __func__); - } -#endif + ret = video_register_media_controller(vdev, type); + /* Part 6: Activate this minor. The char device can now be used. */ set_bit(V4L2_FL_REGISTERED, &vdev->flags); diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 7129e438f29e..06fa5f1b2cff 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -180,26 +180,26 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, return -ENODEV; sd->v4l2_dev = v4l2_dev; - if (sd->internal_ops && sd->internal_ops->registered) { - err = sd->internal_ops->registered(sd); - if (err) - goto error_module; - } - /* This just returns 0 if either of the two args is NULL */ err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL); if (err) - goto error_unregister; + goto error_module; #if defined(CONFIG_MEDIA_CONTROLLER) /* Register the entity. */ if (v4l2_dev->mdev) { err = media_device_register_entity(v4l2_dev->mdev, entity); if (err < 0) - goto error_unregister; + goto error_module; } #endif + if (sd->internal_ops && sd->internal_ops->registered) { + err = sd->internal_ops->registered(sd); + if (err) + goto error_unregister; + } + spin_lock(&v4l2_dev->lock); list_add_tail(&sd->list, &v4l2_dev->subdevs); spin_unlock(&v4l2_dev->lock); @@ -207,8 +207,9 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, return 0; error_unregister: - if (sd->internal_ops && sd->internal_ops->unregistered) - sd->internal_ops->unregistered(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_device_unregister_entity(entity); +#endif error_module: if (!sd->owner_v4l2_dev) module_put(sd->owner); @@ -258,6 +259,19 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) #if defined(CONFIG_MEDIA_CONTROLLER) sd->entity.info.dev.major = VIDEO_MAJOR; sd->entity.info.dev.minor = vdev->minor; + + /* Interface is created by __video_register_device() */ + if (vdev->v4l2_dev->mdev) { + struct media_link *link; + + link = media_create_intf_link(&sd->entity, + &vdev->intf_devnode->intf, + MEDIA_LNK_FL_ENABLED); + if (!link) { + err = -ENOMEM; + goto clean_up; + } + } #endif sd->devnode = vdev; } @@ -294,7 +308,10 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) #if defined(CONFIG_MEDIA_CONTROLLER) if (v4l2_dev->mdev) { - media_entity_remove_links(&sd->entity); + /* + * No need to explicitly remove links, as both pads and + * links are removed by the function below, in the right order + */ media_device_unregister_entity(&sd->entity); } #endif diff --git a/drivers/media/v4l2-core/v4l2-flash-led-class.c b/drivers/media/v4l2-core/v4l2-flash-led-class.c index 5d673357f75f..fc5ff8b215f9 100644 --- a/drivers/media/v4l2-core/v4l2-flash-led-class.c +++ b/drivers/media/v4l2-core/v4l2-flash-led-class.c @@ -651,11 +651,11 @@ struct v4l2_flash *v4l2_flash_init( sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; strlcpy(sd->name, config->dev_name, sizeof(sd->name)); - ret = media_entity_init(&sd->entity, 0, NULL, 0); + ret = media_entity_pads_init(&sd->entity, 0, NULL); if (ret < 0) return ERR_PTR(ret); - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + sd->entity.function = MEDIA_ENT_F_FLASH; ret = v4l2_flash_init_controls(v4l2_flash, config); if (ret < 0) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 83615b8fb46a..d63083803144 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -526,7 +526,7 @@ static int v4l2_subdev_link_validate_get_format(struct media_pad *pad, struct v4l2_subdev_format *fmt) { - if (media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV) { + if (is_media_entity_v4l2_subdev(pad->entity)) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity); @@ -535,9 +535,9 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); } - WARN(pad->entity->type != MEDIA_ENT_T_DEVNODE_V4L, + WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L, "Driver bug! Wrong media entity type 0x%08x, entity %s\n", - pad->entity->type, pad->entity->name); + pad->entity->function, pad->entity->name); return -EINVAL; } @@ -584,7 +584,7 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) sd->host_priv = NULL; #if defined(CONFIG_MEDIA_CONTROLLER) sd->entity.name = sd->name; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; #endif } EXPORT_SYMBOL(v4l2_subdev_init); diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c index c492914768ea..ac78ed2f8bcc 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c @@ -1712,8 +1712,11 @@ ipipe_link_setup(struct media_entity *entity, const struct media_pad *local, struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe); u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; - switch (local->index | media_entity_type(remote->entity)) { - case IPIPE_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!is_media_entity_v4l2_subdev(remote->entity)) + return -EINVAL; + + switch (local->index) { + case IPIPE_PAD_SINK: if (!(flags & MEDIA_LNK_FL_ENABLED)) { ipipe->input = IPIPE_INPUT_NONE; break; @@ -1726,7 +1729,7 @@ ipipe_link_setup(struct media_entity *entity, const struct media_pad *local, ipipe->input = IPIPE_INPUT_CCDC; break; - case IPIPE_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case IPIPE_PAD_SOURCE: /* out to RESIZER */ if (flags & MEDIA_LNK_FL_ENABLED) ipipe->output = IPIPE_OUTPUT_RESIZER; @@ -1840,7 +1843,7 @@ vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, struct platform_device *pdev) v4l2_ctrl_handler_setup(&ipipe->ctrls); sd->ctrl_handler = &ipipe->ctrls; - return media_entity_init(me, IPIPE_PADS_NUM, pads, 0); + return media_entity_pads_init(me, IPIPE_PADS_NUM, pads); } /* diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c index 8b230541b1d1..633d6456fdce 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c @@ -885,9 +885,14 @@ ipipeif_link_setup(struct media_entity *entity, const struct media_pad *local, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); struct vpfe_device *vpfe = to_vpfe_device(ipipeif); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case IPIPEIF_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case IPIPEIF_PAD_SINK: /* Single shot mode */ if (!(flags & MEDIA_LNK_FL_ENABLED)) { ipipeif->input = IPIPEIF_INPUT_NONE; @@ -896,7 +901,7 @@ ipipeif_link_setup(struct media_entity *entity, const struct media_pad *local, ipipeif->input = IPIPEIF_INPUT_MEMORY; break; - case IPIPEIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + case IPIPEIF_PAD_SINK | 2 << 16: /* read from isif */ if (!(flags & MEDIA_LNK_FL_ENABLED)) { ipipeif->input = IPIPEIF_INPUT_NONE; @@ -908,7 +913,7 @@ ipipeif_link_setup(struct media_entity *entity, const struct media_pad *local, ipipeif->input = IPIPEIF_INPUT_ISIF; break; - case IPIPEIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case IPIPEIF_PAD_SOURCE | 2 << 16: if (!(flags & MEDIA_LNK_FL_ENABLED)) { ipipeif->output = IPIPEIF_OUTPUT_NONE; break; @@ -971,7 +976,7 @@ vpfe_ipipeif_register_entities(struct vpfe_ipipeif_device *ipipeif, ipipeif->video_in.vpfe_dev = vpfe_dev; flags = 0; - ret = media_entity_create_link(&ipipeif->video_in.video_dev.entity, 0, + ret = media_create_pad_link(&ipipeif->video_in.video_dev.entity, 0, &ipipeif->subdev.entity, 0, flags); if (ret < 0) goto fail; @@ -1026,7 +1031,7 @@ int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif, ipipeif->output = IPIPEIF_OUTPUT_NONE; me->ops = &ipipeif_media_ops; - ret = media_entity_init(me, IPIPEIF_NUM_PADS, pads, 0); + ret = media_entity_pads_init(me, IPIPEIF_NUM_PADS, pads); if (ret) goto fail; diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c index 80907b464412..99057892d88d 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_isif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c @@ -1707,9 +1707,14 @@ isif_link_setup(struct media_entity *entity, const struct media_pad *local, { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case ISIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case ISIF_PAD_SINK | 2 << 16: /* read from decoder/sensor */ if (!(flags & MEDIA_LNK_FL_ENABLED)) { isif->input = ISIF_INPUT_NONE; @@ -1720,7 +1725,7 @@ isif_link_setup(struct media_entity *entity, const struct media_pad *local, isif->input = ISIF_INPUT_PARALLEL; break; - case ISIF_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + case ISIF_PAD_SOURCE: /* write to memory */ if (flags & MEDIA_LNK_FL_ENABLED) isif->output = ISIF_OUTPUT_MEMORY; @@ -1728,7 +1733,7 @@ isif_link_setup(struct media_entity *entity, const struct media_pad *local, isif->output = ISIF_OUTPUT_NONE; break; - case ISIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case ISIF_PAD_SOURCE | 2 << 16: if (flags & MEDIA_LNK_FL_ENABLED) isif->output = ISIF_OUTPUT_IPIPEIF; else @@ -1817,7 +1822,7 @@ int vpfe_isif_register_entities(struct vpfe_isif_device *isif, isif->video_out.vpfe_dev = vpfe_dev; flags = 0; /* connect isif to video node */ - ret = media_entity_create_link(&isif->subdev.entity, 1, + ret = media_create_pad_link(&isif->subdev.entity, 1, &isif->video_out.video_dev.entity, 0, flags); if (ret < 0) @@ -2052,7 +2057,7 @@ int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev) isif->input = ISIF_INPUT_NONE; isif->output = ISIF_OUTPUT_NONE; me->ops = &isif_media_ops; - status = media_entity_init(me, ISIF_PADS_NUM, pads, 0); + status = media_entity_pads_init(me, ISIF_PADS_NUM, pads); if (status) goto isif_fail; isif->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c index d892fee3f52f..a91395ce91e1 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_resizer.c +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c @@ -1648,10 +1648,15 @@ static int resizer_link_setup(struct media_entity *entity, struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output; u16 ipipe_source = vpfe_dev->vpfe_ipipe.output; + unsigned int index = local->index; + + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; if (&resizer->crop_resizer.subdev == sd) { - switch (local->index | media_entity_type(remote->entity)) { - case RESIZER_CROP_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + switch (index) { + case RESIZER_CROP_PAD_SINK | 2 << 16: if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->crop_resizer.input = RESIZER_CROP_INPUT_NONE; @@ -1671,7 +1676,7 @@ static int resizer_link_setup(struct media_entity *entity, return -EINVAL; break; - case RESIZER_CROP_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case RESIZER_CROP_PAD_SOURCE | 2 << 16: if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->crop_resizer.output = RESIZER_CROP_OUTPUT_NONE; @@ -1683,7 +1688,7 @@ static int resizer_link_setup(struct media_entity *entity, resizer->crop_resizer.output = RESIZER_A; break; - case RESIZER_CROP_PAD_SOURCE2 | MEDIA_ENT_T_V4L2_SUBDEV: + case RESIZER_CROP_PAD_SOURCE2 | 2 << 16: if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->crop_resizer.output2 = RESIZER_CROP_OUTPUT_NONE; @@ -1699,8 +1704,8 @@ static int resizer_link_setup(struct media_entity *entity, return -EINVAL; } } else if (&resizer->resizer_a.subdev == sd) { - switch (local->index | media_entity_type(remote->entity)) { - case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + switch (index) { + case RESIZER_PAD_SINK | 2 << 16: if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->resizer_a.input = RESIZER_INPUT_NONE; break; @@ -1710,7 +1715,7 @@ static int resizer_link_setup(struct media_entity *entity, resizer->resizer_a.input = RESIZER_INPUT_CROP_RESIZER; break; - case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + case RESIZER_PAD_SOURCE: if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->resizer_a.output = RESIZER_OUTPUT_NONE; break; @@ -1724,8 +1729,8 @@ static int resizer_link_setup(struct media_entity *entity, return -EINVAL; } } else if (&resizer->resizer_b.subdev == sd) { - switch (local->index | media_entity_type(remote->entity)) { - case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + switch (index) { + case RESIZER_PAD_SINK | 2 << 16: if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->resizer_b.input = RESIZER_INPUT_NONE; break; @@ -1735,7 +1740,7 @@ static int resizer_link_setup(struct media_entity *entity, resizer->resizer_b.input = RESIZER_INPUT_CROP_RESIZER; break; - case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + case RESIZER_PAD_SOURCE: if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->resizer_b.output = RESIZER_OUTPUT_NONE; break; @@ -1826,27 +1831,27 @@ int vpfe_resizer_register_entities(struct vpfe_resizer_device *resizer, resizer->resizer_b.video_out.vpfe_dev = vpfe_dev; /* create link between Resizer Crop----> Resizer A*/ - ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 1, + ret = media_create_pad_link(&resizer->crop_resizer.subdev.entity, 1, &resizer->resizer_a.subdev.entity, 0, flags); if (ret < 0) goto out_create_link; /* create link between Resizer Crop----> Resizer B*/ - ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 2, + ret = media_create_pad_link(&resizer->crop_resizer.subdev.entity, 2, &resizer->resizer_b.subdev.entity, 0, flags); if (ret < 0) goto out_create_link; /* create link between Resizer A ----> video out */ - ret = media_entity_create_link(&resizer->resizer_a.subdev.entity, 1, + ret = media_create_pad_link(&resizer->resizer_a.subdev.entity, 1, &resizer->resizer_a.video_out.video_dev.entity, 0, flags); if (ret < 0) goto out_create_link; /* create link between Resizer B ----> video out */ - ret = media_entity_create_link(&resizer->resizer_b.subdev.entity, 1, + ret = media_create_pad_link(&resizer->resizer_b.subdev.entity, 1, &resizer->resizer_b.video_out.video_dev.entity, 0, flags); if (ret < 0) goto out_create_link; @@ -1910,7 +1915,7 @@ int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, vpfe_rsz->crop_resizer.output2 = RESIZER_CROP_OUTPUT_NONE; vpfe_rsz->crop_resizer.rsz_device = vpfe_rsz; me->ops = &resizer_media_ops; - ret = media_entity_init(me, RESIZER_CROP_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, RESIZER_CROP_PADS_NUM, pads); if (ret) return ret; @@ -1932,7 +1937,7 @@ int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, vpfe_rsz->resizer_a.output = RESIZER_OUTPUT_NONE; vpfe_rsz->resizer_a.rsz_device = vpfe_rsz; me->ops = &resizer_media_ops; - ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, RESIZER_PADS_NUM, pads); if (ret) return ret; @@ -1954,7 +1959,7 @@ int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, vpfe_rsz->resizer_b.output = RESIZER_OUTPUT_NONE; vpfe_rsz->resizer_b.rsz_device = vpfe_rsz; me->ops = &resizer_media_ops; - ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, RESIZER_PADS_NUM, pads); if (ret) return ret; diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c index 69b678ca40c0..ec46f366dd17 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c @@ -445,32 +445,32 @@ static int vpfe_register_entities(struct vpfe_device *vpfe_dev) /* if entity has no pads (ex: amplifier), cant establish link */ if (vpfe_dev->sd[i]->entity.num_pads) { - ret = media_entity_create_link(&vpfe_dev->sd[i]->entity, + ret = media_create_pad_link(&vpfe_dev->sd[i]->entity, 0, &vpfe_dev->vpfe_isif.subdev.entity, 0, flags); if (ret < 0) goto out_resizer_register; } - ret = media_entity_create_link(&vpfe_dev->vpfe_isif.subdev.entity, 1, + ret = media_create_pad_link(&vpfe_dev->vpfe_isif.subdev.entity, 1, &vpfe_dev->vpfe_ipipeif.subdev.entity, 0, flags); if (ret < 0) goto out_resizer_register; - ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, + ret = media_create_pad_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, &vpfe_dev->vpfe_ipipe.subdev.entity, 0, flags); if (ret < 0) goto out_resizer_register; - ret = media_entity_create_link(&vpfe_dev->vpfe_ipipe.subdev.entity, + ret = media_create_pad_link(&vpfe_dev->vpfe_ipipe.subdev.entity, 1, &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity, 0, flags); if (ret < 0) goto out_resizer_register; - ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, + ret = media_create_pad_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity, 0, flags); if (ret < 0) diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c index adb2bc8811ab..3ec7e65a3ffa 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -88,7 +88,7 @@ vpfe_video_remote_subdev(struct vpfe_video_device *video, u32 *pad) { struct media_pad *remote = media_entity_remote_pad(&video->pad); - if (remote == NULL || remote->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; if (pad) *pad = remote->index; @@ -127,13 +127,14 @@ __vpfe_video_get_format(struct vpfe_video_device *video, } /* make a note of pipeline details */ -static void vpfe_prepare_pipeline(struct vpfe_video_device *video) +static int vpfe_prepare_pipeline(struct vpfe_video_device *video) { + struct media_entity_graph graph; struct media_entity *entity = &video->video_dev.entity; - struct media_device *mdev = entity->parent; + struct media_device *mdev = entity->graph_obj.mdev; struct vpfe_pipeline *pipe = &video->pipe; struct vpfe_video_device *far_end = NULL; - struct media_entity_graph graph; + int ret; pipe->input_num = 0; pipe->output_num = 0; @@ -144,11 +145,16 @@ static void vpfe_prepare_pipeline(struct vpfe_video_device *video) pipe->outputs[pipe->output_num++] = video; mutex_lock(&mdev->graph_mutex); + ret = media_entity_graph_walk_init(&graph, entity->graph_obj.mdev); + if (ret) { + mutex_unlock(&video->lock); + return -ENOMEM; + } media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { if (entity == &video->video_dev.entity) continue; - if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + if (!is_media_entity_v4l2_io(entity)) continue; far_end = to_vpfe_video(media_entity_to_video_device(entity)); if (far_end->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) @@ -156,7 +162,10 @@ static void vpfe_prepare_pipeline(struct vpfe_video_device *video) else pipe->outputs[pipe->output_num++] = far_end; } + media_entity_graph_walk_cleanup(&graph); mutex_unlock(&mdev->graph_mutex); + + return 0; } /* update pipe state selected by user */ @@ -165,7 +174,9 @@ static int vpfe_update_pipe_state(struct vpfe_video_device *video) struct vpfe_pipeline *pipe = &video->pipe; int ret; - vpfe_prepare_pipeline(video); + ret = vpfe_prepare_pipeline(video); + if (ret) + return ret; /* Find out if there is any input video if yes, it is single shot. @@ -243,8 +254,7 @@ static int vpfe_video_validate_pipeline(struct vpfe_pipeline *pipe) /* Retrieve the source format */ pad = media_entity_remote_pad(pad); - if (pad == NULL || - pad->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; subdev = media_entity_to_v4l2_subdev(pad->entity); @@ -277,29 +287,35 @@ static int vpfe_video_validate_pipeline(struct vpfe_pipeline *pipe) */ static int vpfe_pipeline_enable(struct vpfe_pipeline *pipe) { - struct media_entity_graph graph; struct media_entity *entity; struct v4l2_subdev *subdev; struct media_device *mdev; - int ret = 0; + int ret; if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) entity = vpfe_get_input_entity(pipe->outputs[0]); else entity = &pipe->inputs[0]->video_dev.entity; - mdev = entity->parent; + mdev = entity->graph_obj.mdev; mutex_lock(&mdev->graph_mutex); - media_entity_graph_walk_start(&graph, entity); - while ((entity = media_entity_graph_walk_next(&graph))) { + ret = media_entity_graph_walk_init(&pipe->graph, + entity->graph_obj.mdev); + if (ret) + goto out; + media_entity_graph_walk_start(&pipe->graph, entity); + while ((entity = media_entity_graph_walk_next(&pipe->graph))) { - if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + if (!is_media_entity_v4l2_subdev(entity)) continue; subdev = media_entity_to_v4l2_subdev(entity); ret = v4l2_subdev_call(subdev, video, s_stream, 1); if (ret < 0 && ret != -ENOIOCTLCMD) break; } +out: + if (ret) + media_entity_graph_walk_cleanup(&pipe->graph); mutex_unlock(&mdev->graph_mutex); return ret; } @@ -317,7 +333,6 @@ static int vpfe_pipeline_enable(struct vpfe_pipeline *pipe) */ static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe) { - struct media_entity_graph graph; struct media_entity *entity; struct v4l2_subdev *subdev; struct media_device *mdev; @@ -328,13 +343,13 @@ static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe) else entity = &pipe->inputs[0]->video_dev.entity; - mdev = entity->parent; + mdev = entity->graph_obj.mdev; mutex_lock(&mdev->graph_mutex); - media_entity_graph_walk_start(&graph, entity); + media_entity_graph_walk_start(&pipe->graph, entity); - while ((entity = media_entity_graph_walk_next(&graph))) { + while ((entity = media_entity_graph_walk_next(&pipe->graph))) { - if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + if (!is_media_entity_v4l2_subdev(entity)) continue; subdev = media_entity_to_v4l2_subdev(entity); ret = v4l2_subdev_call(subdev, video, s_stream, 0); @@ -343,6 +358,7 @@ static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe) } mutex_unlock(&mdev->graph_mutex); + media_entity_graph_walk_cleanup(&pipe->graph); return ret ? -ETIMEDOUT : 0; } @@ -1600,8 +1616,8 @@ int vpfe_video_init(struct vpfe_video_device *video, const char *name) spin_lock_init(&video->irqlock); spin_lock_init(&video->dma_queue_lock); mutex_init(&video->lock); - ret = media_entity_init(&video->video_dev.entity, - 1, &video->pad, 0); + ret = media_entity_pads_init(&video->video_dev.entity, + 1, &video->pad); if (ret < 0) return ret; diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.h b/drivers/staging/media/davinci_vpfe/vpfe_video.h index 673cefe3ef61..653334d537d3 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.h +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.h @@ -52,6 +52,7 @@ enum vpfe_video_state { struct vpfe_pipeline { /* media pipeline */ struct media_pipeline *pipe; + struct media_entity_graph graph; /* state of the pipeline, continuous, * single-shot or stopped */ diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index e27a988540a6..30b473cfb020 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -389,15 +389,15 @@ static irqreturn_t iss_isr(int irq, void *_iss) * * Return the total number of users of all video device nodes in the pipeline. */ -static int iss_pipeline_pm_use_count(struct media_entity *entity) +static int iss_pipeline_pm_use_count(struct media_entity *entity, + struct media_entity_graph *graph) { - struct media_entity_graph graph; int use = 0; - media_entity_graph_walk_start(&graph, entity); + media_entity_graph_walk_start(graph, entity); - while ((entity = media_entity_graph_walk_next(&graph))) { - if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + while ((entity = media_entity_graph_walk_next(graph))) { + if (is_media_entity_v4l2_io(entity)) use += entity->use_count; } @@ -419,7 +419,7 @@ static int iss_pipeline_pm_power_one(struct media_entity *entity, int change) { struct v4l2_subdev *subdev; - subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV + subdev = is_media_entity_v4l2_subdev(entity) ? media_entity_to_v4l2_subdev(entity) : NULL; if (entity->use_count == 0 && change > 0 && subdev) { @@ -449,29 +449,29 @@ static int iss_pipeline_pm_power_one(struct media_entity *entity, int change) * * Return 0 on success or a negative error code on failure. */ -static int iss_pipeline_pm_power(struct media_entity *entity, int change) +static int iss_pipeline_pm_power(struct media_entity *entity, int change, + struct media_entity_graph *graph) { - struct media_entity_graph graph; struct media_entity *first = entity; int ret = 0; if (!change) return 0; - media_entity_graph_walk_start(&graph, entity); + media_entity_graph_walk_start(graph, entity); - while (!ret && (entity = media_entity_graph_walk_next(&graph))) - if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + while (!ret && (entity = media_entity_graph_walk_next(graph))) + if (is_media_entity_v4l2_subdev(entity)) ret = iss_pipeline_pm_power_one(entity, change); if (!ret) return 0; - media_entity_graph_walk_start(&graph, first); + media_entity_graph_walk_start(graph, first); - while ((first = media_entity_graph_walk_next(&graph)) && + while ((first = media_entity_graph_walk_next(graph)) && first != entity) - if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE) + if (is_media_entity_v4l2_subdev(first)) iss_pipeline_pm_power_one(first, -change); return ret; @@ -489,23 +489,24 @@ static int iss_pipeline_pm_power(struct media_entity *entity, int change) * off is assumed to never fail. No failure can occur when the use parameter is * set to 0. */ -int omap4iss_pipeline_pm_use(struct media_entity *entity, int use) +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use, + struct media_entity_graph *graph) { int change = use ? 1 : -1; int ret; - mutex_lock(&entity->parent->graph_mutex); + mutex_lock(&entity->graph_obj.mdev->graph_mutex); /* Apply use count to node. */ entity->use_count += change; WARN_ON(entity->use_count < 0); /* Apply power change to connected non-nodes. */ - ret = iss_pipeline_pm_power(entity, change); + ret = iss_pipeline_pm_power(entity, change, graph); if (ret < 0) entity->use_count -= change; - mutex_unlock(&entity->parent->graph_mutex); + mutex_unlock(&entity->graph_obj.mdev->graph_mutex); return ret; } @@ -526,34 +527,48 @@ int omap4iss_pipeline_pm_use(struct media_entity *entity, int use) static int iss_pipeline_link_notify(struct media_link *link, u32 flags, unsigned int notification) { + struct media_entity_graph *graph = + &container_of(link->graph_obj.mdev, struct iss_device, + media_dev)->pm_count_graph; struct media_entity *source = link->source->entity; struct media_entity *sink = link->sink->entity; - int source_use = iss_pipeline_pm_use_count(source); - int sink_use = iss_pipeline_pm_use_count(sink); + int source_use; + int sink_use; int ret; + if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) { + ret = media_entity_graph_walk_init(graph, + link->graph_obj.mdev); + if (ret) + return ret; + } + + source_use = iss_pipeline_pm_use_count(source, graph); + sink_use = iss_pipeline_pm_use_count(sink, graph); + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && - !(link->flags & MEDIA_LNK_FL_ENABLED)) { + !(flags & MEDIA_LNK_FL_ENABLED)) { /* Powering off entities is assumed to never fail. */ - iss_pipeline_pm_power(source, -sink_use); - iss_pipeline_pm_power(sink, -source_use); + iss_pipeline_pm_power(source, -sink_use, graph); + iss_pipeline_pm_power(sink, -source_use, graph); return 0; } - if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && + if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && (flags & MEDIA_LNK_FL_ENABLED)) { - ret = iss_pipeline_pm_power(source, sink_use); + ret = iss_pipeline_pm_power(source, sink_use, graph); if (ret < 0) return ret; - ret = iss_pipeline_pm_power(sink, source_use); + ret = iss_pipeline_pm_power(sink, source_use, graph); if (ret < 0) - iss_pipeline_pm_power(source, -sink_use); - - return ret; + iss_pipeline_pm_power(source, -sink_use, graph); } - return 0; + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH) + media_entity_graph_walk_cleanup(graph); + + return ret; } /* ----------------------------------------------------------------------------- @@ -590,8 +605,7 @@ static int iss_pipeline_disable(struct iss_pipeline *pipe, break; pad = media_entity_remote_pad(pad); - if (!pad || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; entity = pad->entity; @@ -607,7 +621,7 @@ static int iss_pipeline_disable(struct iss_pipeline *pipe, * crashed. Mark it as such, the ISS will be reset when * applications will release it. */ - iss->crashed |= 1U << subdev->entity.id; + media_entity_enum_set(&iss->crashed, &subdev->entity); failure = -ETIMEDOUT; } } @@ -642,7 +656,7 @@ static int iss_pipeline_enable(struct iss_pipeline *pipe, * pipeline won't start anyway (those entities would then likely fail to * stop, making the problem worse). */ - if (pipe->entities & iss->crashed) + if (media_entity_enum_intersects(&pipe->ent_enum, &iss->crashed)) return -EIO; spin_lock_irqsave(&pipe->lock, flags); @@ -658,8 +672,7 @@ static int iss_pipeline_enable(struct iss_pipeline *pipe, break; pad = media_entity_remote_pad(pad); - if (!pad || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; entity = pad->entity; @@ -763,7 +776,8 @@ static int iss_reset(struct iss_device *iss) return -ETIMEDOUT; } - iss->crashed = 0; + media_entity_enum_zero(&iss->crashed); + return 0; } @@ -1092,7 +1106,7 @@ void omap4iss_put(struct iss_device *iss) * be worth investigating whether resetting the ISP only can't * fix the problem in some cases. */ - if (iss->crashed) + if (!media_entity_enum_empty(&iss->crashed)) iss_reset(iss); iss_disable_clocks(iss); } @@ -1259,7 +1273,7 @@ static int iss_register_entities(struct iss_device *iss) goto done; } - ret = media_entity_create_link(&sensor->entity, 0, input, pad, + ret = media_create_pad_link(&sensor->entity, 0, input, pad, flags); if (ret < 0) goto done; @@ -1274,6 +1288,68 @@ done: return ret; } +/* + * iss_create_links() - Pads links creation for the subdevices + * @iss : Pointer to ISS device + * + * return negative error code or zero on success + */ +static int iss_create_links(struct iss_device *iss) +{ + int ret; + + ret = omap4iss_csi2_create_links(iss); + if (ret < 0) { + dev_err(iss->dev, "CSI2 pads links creation failed\n"); + return ret; + } + + ret = omap4iss_ipipeif_create_links(iss); + if (ret < 0) { + dev_err(iss->dev, "ISP IPIPEIF pads links creation failed\n"); + return ret; + } + + ret = omap4iss_resizer_create_links(iss); + if (ret < 0) { + dev_err(iss->dev, "ISP RESIZER pads links creation failed\n"); + return ret; + } + + /* Connect the submodules. */ + ret = media_create_pad_link( + &iss->csi2a.subdev.entity, CSI2_PAD_SOURCE, + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &iss->csi2b.subdev.entity, CSI2_PAD_SOURCE, + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP, + &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP, + &iss->ipipe.subdev.entity, IPIPE_PAD_SINK, 0); + if (ret < 0) + return ret; + + ret = media_create_pad_link( + &iss->ipipe.subdev.entity, IPIPE_PAD_SOURCE_VP, + &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0); + if (ret < 0) + return ret; + + return 0; +}; + static void iss_cleanup_modules(struct iss_device *iss) { omap4iss_csi2_cleanup(iss); @@ -1316,41 +1392,8 @@ static int iss_initialize_modules(struct iss_device *iss) goto error_resizer; } - /* Connect the submodules. */ - ret = media_entity_create_link( - &iss->csi2a.subdev.entity, CSI2_PAD_SOURCE, - &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &iss->csi2b.subdev.entity, CSI2_PAD_SOURCE, - &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP, - &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP, - &iss->ipipe.subdev.entity, IPIPE_PAD_SINK, 0); - if (ret < 0) - goto error_link; - - ret = media_entity_create_link( - &iss->ipipe.subdev.entity, IPIPE_PAD_SOURCE_VP, - &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0); - if (ret < 0) - goto error_link; - return 0; -error_link: - omap4iss_resizer_cleanup(iss); error_resizer: omap4iss_ipipe_cleanup(iss); error_ipipe: @@ -1464,10 +1507,21 @@ static int iss_probe(struct platform_device *pdev) if (ret < 0) goto error_modules; + ret = media_entity_enum_init(&iss->crashed, &iss->media_dev); + if (ret) + goto error_entities; + + ret = iss_create_links(iss); + if (ret < 0) + goto error_entities; + omap4iss_put(iss); return 0; +error_entities: + iss_unregister_entities(iss); + media_entity_enum_cleanup(&iss->crashed); error_modules: iss_cleanup_modules(iss); error_iss: @@ -1485,6 +1539,7 @@ static int iss_remove(struct platform_device *pdev) struct iss_device *iss = platform_get_drvdata(pdev); iss_unregister_entities(iss); + media_entity_enum_cleanup(&iss->crashed); iss_cleanup_modules(iss); return 0; diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h index 5929357fe687..05f08a3caa19 100644 --- a/drivers/staging/media/omap4iss/iss.h +++ b/drivers/staging/media/omap4iss/iss.h @@ -82,11 +82,12 @@ struct iss_reg { /* * struct iss_device - ISS device structure. * @syscon: Regmap for the syscon register space - * @crashed: Bitmask of crashed entities (indexed by entity ID) + * @crashed: Crashed entities */ struct iss_device { struct v4l2_device v4l2_dev; struct media_device media_dev; + struct media_entity_graph pm_count_graph; struct device *dev; u32 revision; @@ -101,7 +102,7 @@ struct iss_device { u64 raw_dmamask; struct mutex iss_mutex; /* For handling ref_count field */ - unsigned int crashed; + struct media_entity_enum crashed; int has_context; int ref_count; @@ -151,7 +152,8 @@ void omap4iss_isp_subclk_enable(struct iss_device *iss, void omap4iss_isp_subclk_disable(struct iss_device *iss, enum iss_isp_subclk_resource res); -int omap4iss_pipeline_pm_use(struct media_entity *entity, int use); +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use, + struct media_entity_graph *graph); int omap4iss_register_entities(struct platform_device *pdev, struct v4l2_device *v4l2_dev); diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index b941035139ae..aaca39d751a5 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -1170,14 +1170,19 @@ static int csi2_link_setup(struct media_entity *entity, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct iss_csi2_ctrl_cfg *ctrl = &csi2->ctrl; + unsigned int index = local->index; + + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; /* * The ISS core doesn't support pipelines with multiple video outputs. * Revisit this when it will be implemented, and return -EBUSY for now. */ - switch (local->index | media_entity_type(remote->entity)) { - case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + switch (index) { + case CSI2_PAD_SOURCE: if (flags & MEDIA_LNK_FL_ENABLED) { if (csi2->output & ~CSI2_OUTPUT_MEMORY) return -EBUSY; @@ -1187,7 +1192,7 @@ static int csi2_link_setup(struct media_entity *entity, } break; - case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + case CSI2_PAD_SOURCE | 2 << 16: if (flags & MEDIA_LNK_FL_ENABLED) { if (csi2->output & ~CSI2_OUTPUT_IPIPEIF) return -EBUSY; @@ -1271,7 +1276,7 @@ static int csi2_init_entities(struct iss_csi2_device *csi2, const char *subname) pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; me->ops = &csi2_media_ops; - ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, CSI2_PADS_NUM, pads); if (ret < 0) return ret; @@ -1290,16 +1295,8 @@ static int csi2_init_entities(struct iss_csi2_device *csi2, const char *subname) if (ret < 0) goto error_video; - /* Connect the CSI2 subdev to the video node. */ - ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE, - &csi2->video_out.video.entity, 0, 0); - if (ret < 0) - goto error_link; - return 0; -error_link: - omap4iss_video_cleanup(&csi2->video_out); error_video: media_entity_cleanup(&csi2->subdev.entity); return ret; @@ -1342,6 +1339,33 @@ int omap4iss_csi2_init(struct iss_device *iss) } /* + * omap4iss_csi2_create_links() - CSI2 pads links creation + * @iss: Pointer to ISS device + * + * return negative error code or zero on success + */ +int omap4iss_csi2_create_links(struct iss_device *iss) +{ + struct iss_csi2_device *csi2a = &iss->csi2a; + struct iss_csi2_device *csi2b = &iss->csi2b; + int ret; + + /* Connect the CSI2a subdev to the video node. */ + ret = media_create_pad_link(&csi2a->subdev.entity, CSI2_PAD_SOURCE, + &csi2a->video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + /* Connect the CSI2b subdev to the video node. */ + ret = media_create_pad_link(&csi2b->subdev.entity, CSI2_PAD_SOURCE, + &csi2b->video_out.video.entity, 0, 0); + if (ret < 0) + return ret; + + return 0; +} + +/* * omap4iss_csi2_cleanup - Routine for module driver cleanup */ void omap4iss_csi2_cleanup(struct iss_device *iss) diff --git a/drivers/staging/media/omap4iss/iss_csi2.h b/drivers/staging/media/omap4iss/iss_csi2.h index f2f5343b4a80..24ab378d469f 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.h +++ b/drivers/staging/media/omap4iss/iss_csi2.h @@ -151,6 +151,7 @@ struct iss_csi2_device { void omap4iss_csi2_isr(struct iss_csi2_device *csi2); int omap4iss_csi2_reset(struct iss_csi2_device *csi2); int omap4iss_csi2_init(struct iss_device *iss); +int omap4iss_csi2_create_links(struct iss_device *iss); void omap4iss_csi2_cleanup(struct iss_device *iss); void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2); int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2, diff --git a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c index dd0abeffd893..d38782e8e84c 100644 --- a/drivers/staging/media/omap4iss/iss_ipipe.c +++ b/drivers/staging/media/omap4iss/iss_ipipe.c @@ -447,8 +447,11 @@ static int ipipe_link_setup(struct media_entity *entity, struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); struct iss_device *iss = to_iss_device(ipipe); - switch (local->index | media_entity_type(remote->entity)) { - case IPIPE_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!is_media_entity_v4l2_subdev(remote->entity)) + return -EINVAL; + + switch (local->index) { + case IPIPE_PAD_SINK: /* Read from IPIPEIF. */ if (!(flags & MEDIA_LNK_FL_ENABLED)) { ipipe->input = IPIPE_INPUT_NONE; @@ -463,7 +466,7 @@ static int ipipe_link_setup(struct media_entity *entity, break; - case IPIPE_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV: + case IPIPE_PAD_SOURCE_VP: /* Send to RESIZER */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ipipe->output & ~IPIPE_OUTPUT_VP) @@ -513,7 +516,7 @@ static int ipipe_init_entities(struct iss_ipipe_device *ipipe) pads[IPIPE_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE; me->ops = &ipipe_media_ops; - ret = media_entity_init(me, IPIPE_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, IPIPE_PADS_NUM, pads); if (ret < 0) return ret; diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index 5f9e449e7007..23de8330731d 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -662,9 +662,14 @@ static int ipipeif_link_setup(struct media_entity *entity, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); struct iss_device *iss = to_iss_device(ipipeif); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case IPIPEIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case IPIPEIF_PAD_SINK | 2 << 16: /* Read from the sensor CSI2a or CSI2b. */ if (!(flags & MEDIA_LNK_FL_ENABLED)) { ipipeif->input = IPIPEIF_INPUT_NONE; @@ -681,7 +686,7 @@ static int ipipeif_link_setup(struct media_entity *entity, break; - case IPIPEIF_PAD_SOURCE_ISIF_SF | MEDIA_ENT_T_DEVNODE: + case IPIPEIF_PAD_SOURCE_ISIF_SF: /* Write to memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ipipeif->output & ~IPIPEIF_OUTPUT_MEMORY) @@ -692,7 +697,7 @@ static int ipipeif_link_setup(struct media_entity *entity, } break; - case IPIPEIF_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV: + case IPIPEIF_PAD_SOURCE_VP | 2 << 16: /* Send to IPIPE/RESIZER */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ipipeif->output & ~IPIPEIF_OUTPUT_VP) @@ -743,7 +748,7 @@ static int ipipeif_init_entities(struct iss_ipipeif_device *ipipeif) pads[IPIPEIF_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE; me->ops = &ipipeif_media_ops; - ret = media_entity_init(me, IPIPEIF_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, IPIPEIF_PADS_NUM, pads); if (ret < 0) return ret; @@ -757,18 +762,7 @@ static int ipipeif_init_entities(struct iss_ipipeif_device *ipipeif) ipipeif->video_out.bpl_zero_padding = 1; ipipeif->video_out.bpl_max = 0x1ffe0; - ret = omap4iss_video_init(&ipipeif->video_out, "ISP IPIPEIF"); - if (ret < 0) - return ret; - - /* Connect the IPIPEIF subdev to the video node. */ - ret = media_entity_create_link(&ipipeif->subdev.entity, - IPIPEIF_PAD_SOURCE_ISIF_SF, - &ipipeif->video_out.video.entity, 0, 0); - if (ret < 0) - return ret; - - return 0; + return omap4iss_video_init(&ipipeif->video_out, "ISP IPIPEIF"); } void omap4iss_ipipeif_unregister_entities(struct iss_ipipeif_device *ipipeif) @@ -821,6 +815,22 @@ int omap4iss_ipipeif_init(struct iss_device *iss) } /* + * omap4iss_ipipeif_create_links() - IPIPEIF pads links creation + * @iss: Pointer to ISS device + * + * return negative error code or zero on success + */ +int omap4iss_ipipeif_create_links(struct iss_device *iss) +{ + struct iss_ipipeif_device *ipipeif = &iss->ipipeif; + + /* Connect the IPIPEIF subdev to the video node. */ + return media_create_pad_link(&ipipeif->subdev.entity, + IPIPEIF_PAD_SOURCE_ISIF_SF, + &ipipeif->video_out.video.entity, 0, 0); +} + +/* * omap4iss_ipipeif_cleanup - IPIPEIF module cleanup. * @iss: Device pointer specific to the OMAP4 ISS. */ diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.h b/drivers/staging/media/omap4iss/iss_ipipeif.h index c6bd96d9656c..bad32b1d6ad8 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.h +++ b/drivers/staging/media/omap4iss/iss_ipipeif.h @@ -78,6 +78,7 @@ struct iss_ipipeif_device { struct iss_device; int omap4iss_ipipeif_init(struct iss_device *iss); +int omap4iss_ipipeif_create_links(struct iss_device *iss); void omap4iss_ipipeif_cleanup(struct iss_device *iss); int omap4iss_ipipeif_register_entities(struct iss_ipipeif_device *ipipeif, struct v4l2_device *vdev); diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index 108961e05f53..f1d352c711d5 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -716,9 +716,14 @@ static int resizer_link_setup(struct media_entity *entity, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd); struct iss_device *iss = to_iss_device(resizer); + unsigned int index = local->index; - switch (local->index | media_entity_type(remote->entity)) { - case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* FIXME: this is actually a hack! */ + if (is_media_entity_v4l2_subdev(remote->entity)) + index |= 2 << 16; + + switch (index) { + case RESIZER_PAD_SINK | 2 << 16: /* Read from IPIPE or IPIPEIF. */ if (!(flags & MEDIA_LNK_FL_ENABLED)) { resizer->input = RESIZER_INPUT_NONE; @@ -735,7 +740,7 @@ static int resizer_link_setup(struct media_entity *entity, break; - case RESIZER_PAD_SOURCE_MEM | MEDIA_ENT_T_DEVNODE: + case RESIZER_PAD_SOURCE_MEM: /* Write to memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (resizer->output & ~RESIZER_OUTPUT_MEMORY) @@ -785,7 +790,7 @@ static int resizer_init_entities(struct iss_resizer_device *resizer) pads[RESIZER_PAD_SOURCE_MEM].flags = MEDIA_PAD_FL_SOURCE; me->ops = &resizer_media_ops; - ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + ret = media_entity_pads_init(me, RESIZER_PADS_NUM, pads); if (ret < 0) return ret; @@ -799,18 +804,7 @@ static int resizer_init_entities(struct iss_resizer_device *resizer) resizer->video_out.bpl_zero_padding = 1; resizer->video_out.bpl_max = 0x1ffe0; - ret = omap4iss_video_init(&resizer->video_out, "ISP resizer a"); - if (ret < 0) - return ret; - - /* Connect the RESIZER subdev to the video node. */ - ret = media_entity_create_link(&resizer->subdev.entity, - RESIZER_PAD_SOURCE_MEM, - &resizer->video_out.video.entity, 0, 0); - if (ret < 0) - return ret; - - return 0; + return omap4iss_video_init(&resizer->video_out, "ISP resizer a"); } void omap4iss_resizer_unregister_entities(struct iss_resizer_device *resizer) @@ -863,6 +857,22 @@ int omap4iss_resizer_init(struct iss_device *iss) } /* + * omap4iss_resizer_create_links() - RESIZER pads links creation + * @iss: Pointer to ISS device + * + * return negative error code or zero on success + */ +int omap4iss_resizer_create_links(struct iss_device *iss) +{ + struct iss_resizer_device *resizer = &iss->resizer; + + /* Connect the RESIZER subdev to the video node. */ + return media_create_pad_link(&resizer->subdev.entity, + RESIZER_PAD_SOURCE_MEM, + &resizer->video_out.video.entity, 0, 0); +} + +/* * omap4iss_resizer_cleanup - RESIZER module cleanup. * @iss: Device pointer specific to the OMAP4 ISS. */ diff --git a/drivers/staging/media/omap4iss/iss_resizer.h b/drivers/staging/media/omap4iss/iss_resizer.h index 1e145abafc65..8b7c5fe9ffed 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.h +++ b/drivers/staging/media/omap4iss/iss_resizer.h @@ -61,6 +61,7 @@ struct iss_resizer_device { struct iss_device; int omap4iss_resizer_init(struct iss_device *iss); +int omap4iss_resizer_create_links(struct iss_device *iss); void omap4iss_resizer_cleanup(struct iss_device *iss); int omap4iss_resizer_register_entities(struct iss_resizer_device *resizer, struct v4l2_device *vdev); diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index e9aeca08986f..058233a9de67 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -190,8 +190,7 @@ iss_video_remote_subdev(struct iss_video *video, u32 *pad) remote = media_entity_remote_pad(&video->pad); - if (!remote || - media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; if (pad) @@ -206,17 +205,23 @@ iss_video_far_end(struct iss_video *video) { struct media_entity_graph graph; struct media_entity *entity = &video->video.entity; - struct media_device *mdev = entity->parent; + struct media_device *mdev = entity->graph_obj.mdev; struct iss_video *far_end = NULL; mutex_lock(&mdev->graph_mutex); + + if (media_entity_graph_walk_init(&graph, mdev)) { + mutex_unlock(&mdev->graph_mutex); + return NULL; + } + media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { if (entity == &video->video.entity) continue; - if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + if (!is_media_entity_v4l2_io(entity)) continue; far_end = to_iss_video(media_entity_to_video_device(entity)); @@ -227,6 +232,9 @@ iss_video_far_end(struct iss_video *video) } mutex_unlock(&mdev->graph_mutex); + + media_entity_graph_walk_cleanup(&graph); + return far_end; } @@ -750,7 +758,7 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) struct iss_video_fh *vfh = to_iss_video_fh(fh); struct iss_video *video = video_drvdata(file); struct media_entity_graph graph; - struct media_entity *entity; + struct media_entity *entity = &video->video.entity; enum iss_pipeline_state state; struct iss_pipeline *pipe; struct iss_video *far_end; @@ -765,24 +773,30 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) /* Start streaming on the pipeline. No link touching an entity in the * pipeline can be activated or deactivated once streaming is started. */ - pipe = video->video.entity.pipe - ? to_iss_pipeline(&video->video.entity) : &video->pipe; + pipe = entity->pipe + ? to_iss_pipeline(entity) : &video->pipe; pipe->external = NULL; pipe->external_rate = 0; pipe->external_bpp = 0; - pipe->entities = 0; + + ret = media_entity_enum_init(&pipe->ent_enum, entity->graph_obj.mdev); + if (ret) + goto err_graph_walk_init; + + ret = media_entity_graph_walk_init(&graph, entity->graph_obj.mdev); + if (ret) + goto err_graph_walk_init; if (video->iss->pdata->set_constraints) video->iss->pdata->set_constraints(video->iss, true); - ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe); + ret = media_entity_pipeline_start(entity, &pipe->pipe); if (ret < 0) goto err_media_entity_pipeline_start; - entity = &video->video.entity; media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) - pipe->entities |= 1 << entity->id; + media_entity_enum_set(&pipe->ent_enum, entity); /* Verify that the currently configured format matches the output of * the connected subdev. @@ -852,7 +866,10 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) spin_unlock_irqrestore(&video->qlock, flags); } + media_entity_graph_walk_cleanup(&graph); + mutex_unlock(&video->stream_lock); + return 0; err_omap4iss_set_stream: @@ -864,7 +881,13 @@ err_media_entity_pipeline_start: video->iss->pdata->set_constraints(video->iss, false); video->queue = NULL; + media_entity_graph_walk_cleanup(&graph); + +err_graph_walk_init: + media_entity_enum_cleanup(&pipe->ent_enum); + mutex_unlock(&video->stream_lock); + return ret; } @@ -902,6 +925,8 @@ iss_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) vb2_streamoff(&vfh->queue, type); video->queue = NULL; + media_entity_enum_cleanup(&pipe->ent_enum); + if (video->iss->pdata->set_constraints) video->iss->pdata->set_constraints(video->iss, false); media_entity_pipeline_stop(&video->video.entity); @@ -984,7 +1009,13 @@ static int iss_video_open(struct file *file) goto done; } - ret = omap4iss_pipeline_pm_use(&video->video.entity, 1); + ret = media_entity_graph_walk_init(&handle->graph, + &video->iss->media_dev); + if (ret) + goto done; + + ret = omap4iss_pipeline_pm_use(&video->video.entity, 1, + &handle->graph); if (ret < 0) { omap4iss_put(video->iss); goto done; @@ -1023,6 +1054,7 @@ static int iss_video_open(struct file *file) done: if (ret < 0) { v4l2_fh_del(&handle->vfh); + media_entity_graph_walk_cleanup(&handle->graph); kfree(handle); } @@ -1038,12 +1070,13 @@ static int iss_video_release(struct file *file) /* Disable streaming and free the buffers queue resources. */ iss_video_streamoff(file, vfh, video->type); - omap4iss_pipeline_pm_use(&video->video.entity, 0); + omap4iss_pipeline_pm_use(&video->video.entity, 0, &handle->graph); /* Release the videobuf2 queue */ vb2_queue_release(&handle->queue); /* Release the file handle. */ + media_entity_graph_walk_cleanup(&handle->graph); v4l2_fh_del(vfh); kfree(handle); file->private_data = NULL; @@ -1102,7 +1135,7 @@ int omap4iss_video_init(struct iss_video *video, const char *name) return -EINVAL; } - ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); + ret = media_entity_pads_init(&video->video.entity, 1, &video->pad); if (ret < 0) return ret; diff --git a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h index 41532eda1277..34588b7176ca 100644 --- a/drivers/staging/media/omap4iss/iss_video.h +++ b/drivers/staging/media/omap4iss/iss_video.h @@ -77,7 +77,7 @@ enum iss_pipeline_state { /* * struct iss_pipeline - An OMAP4 ISS hardware pipeline - * @entities: Bitmask of entities in the pipeline (indexed by entity ID) + * @ent_enum: Entities in the pipeline * @error: A hardware error occurred during capture */ struct iss_pipeline { @@ -87,7 +87,7 @@ struct iss_pipeline { enum iss_pipeline_stream_state stream_state; struct iss_video *input; struct iss_video *output; - unsigned int entities; + struct media_entity_enum ent_enum; atomic_t frame_number; bool do_propagation; /* of frame number */ bool error; @@ -183,6 +183,7 @@ struct iss_video_fh { struct vb2_queue queue; struct v4l2_format format; struct v4l2_fract timeperframe; + struct media_entity_graph graph; }; #define to_iss_video_fh(fh) container_of(fh, struct iss_video_fh, vfh) |