/* Create FIMC-IS links */ static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd) { struct fimc_isp *isp = &fmd->fimc_is->isp; struct media_entity *source, *sink; int i, ret; source = &isp->subdev.entity; for (i = 0; i < FIMC_MAX_DEVS; i++) { if (fmd->fimc[i] == NULL) continue; /* Link from FIMC-IS-ISP subdev to FIMC */ sink = &fmd->fimc[i]->vid_cap.subdev.entity; ret = media_create_pad_link(source, FIMC_ISP_SD_PAD_SRC_FIFO, sink, FIMC_SD_PAD_SINK_FIFO, 0); if (ret) return ret; } /* Link from FIMC-IS-ISP subdev to fimc-is-isp.capture video node */ sink = &isp->video_capture.ve.vdev.entity; /* Skip this link if the fimc-is-isp video node driver isn't built-in */ if (sink->num_pads == 0) return 0; return media_create_pad_link(source, FIMC_ISP_SD_PAD_SRC_DMA, sink, 0, 0); }
/* Create links from FIMC-LITE source pads to other entities */ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) { struct media_entity *source, *sink; int i, ret = 0; for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { struct fimc_lite *fimc = fmd->fimc_lite[i]; if (fimc == NULL) continue; source = &fimc->subdev.entity; sink = &fimc->ve.vdev.entity; /* FIMC-LITE's subdev and video node */ 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_create_pad_link(source, FLITE_SD_PAD_SOURCE_ISP, sink, 0, 0); if (ret) break; } 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 int camif_create_media_links(struct camif_dev *camif) { int i, ret; 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); if (ret) return ret; for (i = 1; i < CAMIF_SD_PADS_NUM && !ret; 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); } return ret; }
/** * fimc_md_create_links - create default links between registered entities * * Parallel interface sensor entities are connected directly to FIMC capture * entities. The sensors using MIPI CSIS bus are connected through immutable * link with CSI receiver entity specified by mux_id. Any registered CSIS * entity has a link to each registered FIMC capture entity. Enabled links * are created by default between each subsequent registered sensor and * subsequent FIMC capture entity. The number of default active links is * determined by the number of available sensors or FIMC entities, * whichever is less. */ static int fimc_md_create_links(struct fimc_md *fmd) { struct v4l2_subdev *csi_sensors[CSIS_MAX_ENTITIES] = { NULL }; struct v4l2_subdev *sensor, *csis; struct fimc_source_info *pdata; struct media_entity *source, *sink; int i, pad, fimc_id = 0, ret = 0; u32 flags, link_mask = 0; for (i = 0; i < fmd->num_sensors; i++) { if (fmd->sensor[i].subdev == NULL) continue; sensor = fmd->sensor[i].subdev; pdata = v4l2_get_subdev_hostdata(sensor); if (!pdata) continue; source = NULL; switch (pdata->sensor_bus_type) { case FIMC_BUS_TYPE_MIPI_CSI2: if (WARN(pdata->mux_id >= CSIS_MAX_ENTITIES, "Wrong CSI channel id: %d\n", pdata->mux_id)) return -EINVAL; csis = fmd->csis[pdata->mux_id].sd; if (WARN(csis == NULL, "MIPI-CSI interface specified " "but s5p-csis module is not loaded!\n")) return -EINVAL; pad = sensor->entity.num_pads - 1; ret = media_create_pad_link(&sensor->entity, pad, &csis->entity, CSIS_PAD_SINK, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); if (ret) return ret; v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]\n", sensor->entity.name, csis->entity.name); source = NULL; csi_sensors[pdata->mux_id] = sensor; break; case FIMC_BUS_TYPE_ITU_601...FIMC_BUS_TYPE_ITU_656: source = &sensor->entity; pad = 0; break; default: v4l2_err(&fmd->v4l2_dev, "Wrong bus_type: %x\n", pdata->sensor_bus_type); return -EINVAL; } if (source == NULL) continue; link_mask = 1 << fimc_id++; ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor, pad, link_mask); } for (i = 0; i < CSIS_MAX_ENTITIES; i++) { if (fmd->csis[i].sd == NULL) continue; source = &fmd->csis[i].sd->entity; pad = CSIS_PAD_SOURCE; sensor = csi_sensors[i]; link_mask = 1 << fimc_id++; ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor, pad, link_mask); } /* Create immutable links between each FIMC's subdev and video node */ flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; for (i = 0; i < FIMC_MAX_DEVS; i++) { if (!fmd->fimc[i]) continue; source = &fmd->fimc[i]->vid_cap.subdev.entity; sink = &fmd->fimc[i]->vid_cap.ve.vdev.entity; ret = media_create_pad_link(source, FIMC_SD_PAD_SOURCE, sink, 0, flags); if (ret) break; } ret = __fimc_md_create_flite_source_links(fmd); if (ret < 0) return ret; if (fmd->use_isp) ret = __fimc_md_create_fimc_is_links(fmd); return ret; }
/** * __fimc_md_create_fimc_links - create links to all FIMC entities * @fmd: fimc media device * @source: the source entity to create links to all fimc entities from * @sensor: sensor subdev linked to FIMC[fimc_id] entity, may be null * @pad: the source entity pad index * @link_mask: bitmask of the fimc devices for which link should be enabled */ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, struct media_entity *source, struct v4l2_subdev *sensor, int pad, int link_mask) { struct fimc_source_info *si = NULL; struct media_entity *sink; unsigned int flags = 0; int i, ret = 0; if (sensor) { si = v4l2_get_subdev_hostdata(sensor); /* Skip direct FIMC links in the logical FIMC-IS sensor path */ if (si && si->fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) ret = 1; } for (i = 0; !ret && i < FIMC_MAX_DEVS; i++) { if (!fmd->fimc[i]) continue; /* * Some FIMC variants are not fitted with camera capture * interface. Skip creating a link from sensor for those. */ if (!fmd->fimc[i]->variant->has_cam_if) continue; flags = ((1 << i) & link_mask) ? MEDIA_LNK_FL_ENABLED : 0; sink = &fmd->fimc[i]->vid_cap.subdev.entity; ret = media_create_pad_link(source, pad, sink, FIMC_SD_PAD_SINK_CAM, flags); if (ret) return ret; /* Notify FIMC capture subdev entity */ ret = media_entity_call(sink, link_setup, &sink->pads[0], &source->pads[pad], flags); if (ret) break; v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]\n", source->name, flags ? '=' : '-', sink->name); } for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { if (!fmd->fimc_lite[i]) continue; sink = &fmd->fimc_lite[i]->subdev.entity; ret = media_create_pad_link(source, pad, sink, FLITE_SD_PAD_SINK, 0); if (ret) return ret; /* Notify FIMC-LITE subdev entity */ ret = media_entity_call(sink, link_setup, &sink->pads[0], &source->pads[pad], 0); if (ret) break; v4l2_info(&fmd->v4l2_dev, "created link [%s] -> [%s]\n", source->name, sink->name); } return 0; }
int v4l2_mc_create_media_graph(struct media_device *mdev) { struct media_entity *entity; struct media_entity *if_vid = NULL, *if_aud = NULL; struct media_entity *tuner = NULL, *decoder = NULL; struct media_entity *io_v4l = NULL, *io_vbi = NULL, *io_swradio = NULL; bool is_webcam = false; u32 flags; int ret; if (!mdev) return 0; media_device_for_each_entity(entity, mdev) { switch (entity->function) { case MEDIA_ENT_F_IF_VID_DECODER: if_vid = entity; break; case MEDIA_ENT_F_IF_AUD_DECODER: if_aud = entity; break; case MEDIA_ENT_F_TUNER: tuner = entity; break; case MEDIA_ENT_F_ATV_DECODER: decoder = entity; break; case MEDIA_ENT_F_IO_V4L: io_v4l = entity; break; case MEDIA_ENT_F_IO_VBI: io_vbi = entity; break; case MEDIA_ENT_F_IO_SWRADIO: io_swradio = entity; break; case MEDIA_ENT_F_CAM_SENSOR: is_webcam = true; break; } } /* It should have at least one I/O entity */ if (!io_v4l && !io_vbi && !io_swradio) return -EINVAL; /* * Here, webcams are modelled on a very simple way: the sensor is * connected directly to the I/O entity. All dirty details, like * scaler and crop HW are hidden. While such mapping is not enough * for mc-centric hardware, it is enough for v4l2 interface centric * PC-consumer's hardware. */ if (is_webcam) { if (!io_v4l) return -EINVAL; media_device_for_each_entity(entity, mdev) { if (entity->function != MEDIA_ENT_F_CAM_SENSOR) continue; ret = media_create_pad_link(entity, 0, io_v4l, 0, MEDIA_LNK_FL_ENABLED); if (ret) return ret; } if (!decoder) return 0; } /* The device isn't a webcam. So, it should have a decoder */ if (!decoder) return -EINVAL; /* Link the tuner and IF video output pads */ if (tuner) { if (if_vid) { ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT, if_vid, IF_VID_DEC_PAD_IF_INPUT, MEDIA_LNK_FL_ENABLED); if (ret) return ret; ret = media_create_pad_link(if_vid, IF_VID_DEC_PAD_OUT, decoder, DEMOD_PAD_IF_INPUT, MEDIA_LNK_FL_ENABLED); if (ret) return ret; } else { ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT, decoder, DEMOD_PAD_IF_INPUT, MEDIA_LNK_FL_ENABLED); if (ret) return ret; } if (if_aud) { ret = media_create_pad_link(tuner, TUNER_PAD_AUD_OUT, if_aud, IF_AUD_DEC_PAD_IF_INPUT, MEDIA_LNK_FL_ENABLED); if (ret) return ret; } else { if_aud = tuner; } } /* Create demod to V4L, VBI and SDR radio links */ if (io_v4l) { ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT, io_v4l, 0, MEDIA_LNK_FL_ENABLED); if (ret) return ret; } if (io_swradio) { ret = media_create_pad_link(decoder, DEMOD_PAD_VID_OUT, io_swradio, 0, MEDIA_LNK_FL_ENABLED); if (ret) return ret; } if (io_vbi) { ret = media_create_pad_link(decoder, DEMOD_PAD_VBI_OUT, io_vbi, 0, MEDIA_LNK_FL_ENABLED); if (ret) return ret; } /* Create links for the media connectors */ flags = MEDIA_LNK_FL_ENABLED; media_device_for_each_entity(entity, mdev) { switch (entity->function) { case MEDIA_ENT_F_CONN_RF: if (!tuner) continue; ret = media_create_pad_link(entity, 0, tuner, TUNER_PAD_RF_INPUT, flags); break; case MEDIA_ENT_F_CONN_SVIDEO: case MEDIA_ENT_F_CONN_COMPOSITE: ret = media_create_pad_link(entity, 0, decoder, DEMOD_PAD_IF_INPUT, flags); break; default: continue; } if (ret) return ret; flags = 0; } return 0; }
static int iss_register_entities(struct iss_device *iss) { struct iss_platform_data *pdata = iss->pdata; struct iss_v4l2_subdevs_group *subdevs; int ret; iss->media_dev.dev = iss->dev; strlcpy(iss->media_dev.model, "TI OMAP4 ISS", sizeof(iss->media_dev.model)); iss->media_dev.hw_revision = iss->revision; iss->media_dev.link_notify = iss_pipeline_link_notify; ret = media_device_register(&iss->media_dev); if (ret < 0) { dev_err(iss->dev, "Media device registration failed (%d)\n", ret); return ret; } iss->v4l2_dev.mdev = &iss->media_dev; ret = v4l2_device_register(iss->dev, &iss->v4l2_dev); if (ret < 0) { dev_err(iss->dev, "V4L2 device registration failed (%d)\n", ret); goto done; } /* Register internal entities */ ret = omap4iss_csi2_register_entities(&iss->csi2a, &iss->v4l2_dev); if (ret < 0) goto done; ret = omap4iss_csi2_register_entities(&iss->csi2b, &iss->v4l2_dev); if (ret < 0) goto done; ret = omap4iss_ipipeif_register_entities(&iss->ipipeif, &iss->v4l2_dev); if (ret < 0) goto done; ret = omap4iss_ipipe_register_entities(&iss->ipipe, &iss->v4l2_dev); if (ret < 0) goto done; ret = omap4iss_resizer_register_entities(&iss->resizer, &iss->v4l2_dev); if (ret < 0) goto done; /* Register external entities */ for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) { struct v4l2_subdev *sensor; struct media_entity *input; unsigned int flags; unsigned int pad; sensor = iss_register_subdev_group(iss, subdevs->subdevs); if (!sensor) continue; sensor->host_priv = subdevs; /* Connect the sensor to the correct interface module. * CSI2a receiver through CSIPHY1, or * CSI2b receiver through CSIPHY2 */ switch (subdevs->interface) { case ISS_INTERFACE_CSI2A_PHY1: input = &iss->csi2a.subdev.entity; pad = CSI2_PAD_SINK; flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; break; case ISS_INTERFACE_CSI2B_PHY2: input = &iss->csi2b.subdev.entity; pad = CSI2_PAD_SINK; flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; break; default: dev_err(iss->dev, "invalid interface type %u\n", subdevs->interface); ret = -EINVAL; goto done; } ret = media_create_pad_link(&sensor->entity, 0, input, pad, flags); if (ret < 0) goto done; } ret = v4l2_device_register_subdev_nodes(&iss->v4l2_dev); done: if (ret < 0) iss_unregister_entities(iss); return ret; }