/* * pipeline_pm_power - Apply power change to all entities in a pipeline * @entity: The entity * @change: Use count change * * Walk the pipeline to update the use count and the power state of all non-node * entities. * * Return 0 on success or a negative error code on failure. */ static int pipeline_pm_power(struct media_entity *entity, int change, struct media_entity_graph *graph) { struct media_entity *first = entity; int ret = 0; if (!change) return 0; media_entity_graph_walk_start(graph, entity); while (!ret && (entity = media_entity_graph_walk_next(graph))) if (is_media_entity_v4l2_subdev(entity)) ret = pipeline_pm_power_one(entity, change); if (!ret) return ret; media_entity_graph_walk_start(graph, first); while ((first = media_entity_graph_walk_next(graph)) && first != entity) if (is_media_entity_v4l2_subdev(first)) pipeline_pm_power_one(first, -change); return ret; }
static void video_stop_streaming(struct vb2_queue *q) { struct camss_video *video = vb2_get_drv_priv(q); struct video_device *vdev = &video->vdev; struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; entity = &vdev->entity; while (1) { pad = &entity->pads[0]; if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; pad = media_entity_remote_pad(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; entity = pad->entity; subdev = media_entity_to_v4l2_subdev(entity); v4l2_subdev_call(subdev, video, s_stream, 0); } media_pipeline_stop(&vdev->entity); video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); }
/* * vpfe_pipeline_disable() - Disable streaming on a pipeline * @vpfe_dev: vpfe device * @pipe: VPFE pipeline * * Walk the entities chain starting at the pipeline output video node and stop * all modules in the chain. * * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module * can't be stopped. */ static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe) { struct media_entity *entity; struct v4l2_subdev *subdev; struct media_device *mdev; int ret = 0; 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->graph_obj.mdev; mutex_lock(&mdev->graph_mutex); media_graph_walk_start(&pipe->graph, entity); while ((entity = media_graph_walk_next(&pipe->graph))) { if (!is_media_entity_v4l2_subdev(entity)) continue; subdev = media_entity_to_v4l2_subdev(entity); ret = v4l2_subdev_call(subdev, video, s_stream, 0); if (ret < 0 && ret != -ENOIOCTLCMD) break; } mutex_unlock(&mdev->graph_mutex); media_graph_walk_cleanup(&pipe->graph); return ret ? -ETIMEDOUT : 0; }
static int imx7_csi_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct imx7_csi *csi = v4l2_get_subdevdata(sd); struct v4l2_subdev *remote_sd; int ret = 0; dev_dbg(csi->dev, "link setup %s -> %s\n", remote->entity->name, local->entity->name); mutex_lock(&csi->lock); if (local->flags & MEDIA_PAD_FL_SINK) { if (!is_media_entity_v4l2_subdev(remote->entity)) { ret = -EINVAL; goto unlock; } remote_sd = media_entity_to_v4l2_subdev(remote->entity); if (flags & MEDIA_LNK_FL_ENABLED) { if (csi->src_sd) { ret = -EBUSY; goto unlock; } csi->src_sd = remote_sd; } else { csi->src_sd = NULL; } goto init; } /* source pad */ if (flags & MEDIA_LNK_FL_ENABLED) { if (csi->sink) { ret = -EBUSY; goto unlock; } csi->sink = remote->entity; } else { v4l2_ctrl_handler_free(&csi->ctrl_hdlr); v4l2_ctrl_handler_init(&csi->ctrl_hdlr, 0); csi->sink = NULL; } init: if (csi->sink || csi->src_sd) ret = imx7_csi_init(csi); else imx7_csi_deinit(csi); unlock: mutex_unlock(&csi->lock); return ret; }
/** * fimc_pipeline_prepare - update pipeline information with subdevice pointers * @me: media entity terminating the pipeline * * Caller holds the graph mutex. */ static void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_entity *me) { struct fimc_md *fmd = entity_to_fimc_mdev(me); struct v4l2_subdev *sd; struct v4l2_subdev *sensor = NULL; int i; for (i = 0; i < IDX_MAX; i++) p->subdevs[i] = NULL; while (1) { struct media_pad *pad = NULL; /* Find remote source pad */ for (i = 0; i < me->num_pads; i++) { struct media_pad *spad = &me->pads[i]; if (!(spad->flags & MEDIA_PAD_FL_SINK)) continue; pad = media_entity_remote_pad(spad); if (pad) break; } if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; sd = media_entity_to_v4l2_subdev(pad->entity); switch (sd->grp_id) { case GRP_ID_SENSOR: sensor = sd; /* fall through */ case GRP_ID_FIMC_IS_SENSOR: p->subdevs[IDX_SENSOR] = sd; break; case GRP_ID_CSIS: p->subdevs[IDX_CSIS] = sd; break; case GRP_ID_FLITE: p->subdevs[IDX_FLITE] = sd; break; case GRP_ID_FIMC: p->subdevs[IDX_FIMC] = sd; break; case GRP_ID_FIMC_IS: p->subdevs[IDX_IS_ISP] = sd; break; default: break; } me = &sd->entity; if (me->num_pads == 1) break; } if (sensor && p->subdevs[IDX_FIMC]) __setup_sensor_notification(fmd, sensor, p->subdevs[IDX_FIMC]); }
/* get the subdev which is connected to the output video node */ static struct v4l2_subdev * vpfe_video_remote_subdev(struct vpfe_video_device *video, u32 *pad) { struct media_pad *remote = media_entity_remote_pad(&video->pad); if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) return NULL; if (pad) *pad = remote->index; return media_entity_to_v4l2_subdev(remote->entity); }
/** * Validate a pipeline by checking both ends of all links for format * discrepancies. * * Return 0 if all formats match, or -EPIPE if at least one link is found with * different formats on its two ends. */ static int vpfe_video_validate_pipeline(struct vpfe_pipeline *pipe) { struct v4l2_subdev_format fmt_source; struct v4l2_subdev_format fmt_sink; struct v4l2_subdev *subdev; struct media_pad *pad; int ret; /* * Should not matter if it is output[0] or 1 as * the general ideas is to traverse backwards and * the fact that the out video node always has the * format of the connected pad. */ subdev = vpfe_video_remote_subdev(pipe->outputs[0], NULL); if (!subdev) return -EPIPE; while (1) { /* Retrieve the sink format */ pad = &subdev->entity.pads[0]; if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt_sink.pad = pad->index; ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink); if (ret < 0 && ret != -ENOIOCTLCMD) return -EPIPE; /* Retrieve the source format */ pad = media_entity_remote_pad(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; subdev = media_entity_to_v4l2_subdev(pad->entity); fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt_source.pad = pad->index; ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source); if (ret < 0 && ret != -ENOIOCTLCMD) return -EPIPE; /* Check if the two ends match */ if (fmt_source.format.code != fmt_sink.format.code || fmt_source.format.width != fmt_sink.format.width || fmt_source.format.height != fmt_sink.format.height) return -EPIPE; } return 0; }
/* * iss_pipeline_enable - Enable streaming on a pipeline * @pipe: ISS pipeline * @mode: Stream mode (single shot or continuous) * * Walk the entities chain starting at the pipeline output video node and start * all modules in the chain in the given mode. * * Return 0 if successful, or the return value of the failed video::s_stream * operation otherwise. */ static int iss_pipeline_enable(struct iss_pipeline *pipe, enum iss_pipeline_stream_state mode) { struct iss_device *iss = pipe->output->iss; struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; unsigned long flags; int ret; /* If one of the entities in the pipeline has crashed it will not work * properly. Refuse to start streaming in that case. This check must be * performed before the loop below to avoid starting entities if the * pipeline won't start anyway (those entities would then likely fail to * stop, making the problem worse). */ if (media_entity_enum_intersects(&pipe->ent_enum, &iss->crashed)) return -EIO; spin_lock_irqsave(&pipe->lock, flags); pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT); spin_unlock_irqrestore(&pipe->lock, flags); pipe->do_propagation = false; entity = &pipe->output->video.entity; while (1) { pad = &entity->pads[0]; if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; pad = media_entity_remote_pad(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; entity = pad->entity; subdev = media_entity_to_v4l2_subdev(entity); ret = v4l2_subdev_call(subdev, video, s_stream, mode); if (ret < 0 && ret != -ENOIOCTLCMD) { iss_pipeline_disable(pipe, entity); return ret; } if (subdev == &iss->csi2a.subdev || subdev == &iss->csi2b.subdev) pipe->do_propagation = true; } iss_print_status(pipe->output->iss); return 0; }
/* * csi2_link_setup - Setup CSI2 connections. * @entity : Pointer to media entity structure * @local : Pointer to local pad array * @remote : Pointer to remote pad array * @flags : Link flags * return -EINVAL or zero on success */ static int csi2_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags) { 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. */ /* 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; csi2->output |= CSI2_OUTPUT_MEMORY; } else { csi2->output &= ~CSI2_OUTPUT_MEMORY; } break; case CSI2_PAD_SOURCE | 2 << 16: if (flags & MEDIA_LNK_FL_ENABLED) { if (csi2->output & ~CSI2_OUTPUT_CCDC) return -EBUSY; csi2->output |= CSI2_OUTPUT_CCDC; } else { csi2->output &= ~CSI2_OUTPUT_CCDC; } break; default: /* Link from camera to CSI2 is fixed... */ return -EINVAL; } ctrl->vp_only_enable = (csi2->output & CSI2_OUTPUT_MEMORY) ? false : true; ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_CCDC); return 0; }
static int fimc_pipeline_validate(struct fimc_lite *fimc) { struct v4l2_subdev *sd = &fimc->subdev; struct v4l2_subdev_format sink_fmt, src_fmt; struct media_pad *pad; int ret; while (1) { /* Retrieve format at the sink pad */ pad = &sd->entity.pads[0]; if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; /* Don't call FIMC subdev operation to avoid nested locking */ if (sd == &fimc->subdev) { struct flite_frame *ff = &fimc->out_frame; sink_fmt.format.width = ff->f_width; sink_fmt.format.height = ff->f_height; sink_fmt.format.code = fimc->inp_frame.fmt->mbus_code; } else { sink_fmt.pad = pad->index; sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt); if (ret < 0 && ret != -ENOIOCTLCMD) return -EPIPE; } /* Retrieve format at the source pad */ pad = media_entity_remote_pad(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; sd = media_entity_to_v4l2_subdev(pad->entity); src_fmt.pad = pad->index; src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); if (ret < 0 && ret != -ENOIOCTLCMD) return -EPIPE; if (src_fmt.format.width != sink_fmt.format.width || src_fmt.format.height != sink_fmt.format.height || src_fmt.format.code != sink_fmt.format.code) return -EPIPE; } return 0; }
static int video_start_streaming(struct vb2_queue *q, unsigned int count) { struct camss_video *video = vb2_get_drv_priv(q); struct video_device *vdev = &video->vdev; struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; int ret; ret = media_pipeline_start(&vdev->entity, &video->pipe); if (ret < 0) return ret; ret = video_check_format(video); if (ret < 0) goto error; entity = &vdev->entity; while (1) { pad = &entity->pads[0]; if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; pad = media_entity_remote_pad(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; entity = pad->entity; subdev = media_entity_to_v4l2_subdev(entity); ret = v4l2_subdev_call(subdev, video, s_stream, 1); if (ret < 0 && ret != -ENOIOCTLCMD) goto error; } return 0; error: media_pipeline_stop(&vdev->entity); video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED); return ret; }
/* * ipipe_link_setup - Setup IPIPE connections * @entity: IPIPE media entity * @local: Pad at the local end of the link * @remote: Pad at the remote end of the link * @flags: Link flags * * return -EINVAL or zero on success */ static int ipipe_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd); struct iss_device *iss = to_iss_device(ipipe); 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; break; } if (ipipe->input != IPIPE_INPUT_NONE) return -EBUSY; if (remote->entity == &iss->ipipeif.subdev.entity) ipipe->input = IPIPE_INPUT_IPIPEIF; break; case IPIPE_PAD_SOURCE_VP: /* Send to RESIZER */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ipipe->output & ~IPIPE_OUTPUT_VP) return -EBUSY; ipipe->output |= IPIPE_OUTPUT_VP; } else { ipipe->output &= ~IPIPE_OUTPUT_VP; } break; default: return -EINVAL; } return 0; }
static int v4l2_subdev_link_validate_get_format(struct media_pad *pad, struct v4l2_subdev_format *fmt) { if (is_media_entity_v4l2_subdev(pad->entity)) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity); fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt->pad = pad->index; return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); } WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L, "Driver bug! Wrong media entity type 0x%08x, entity %s\n", pad->entity->function, pad->entity->name); return -EINVAL; }
/* * iss_pipeline_disable - Disable streaming on a pipeline * @pipe: ISS pipeline * @until: entity at which to stop pipeline walk * * Walk the entities chain starting at the pipeline output video node and stop * all modules in the chain. Wait synchronously for the modules to be stopped if * necessary. * * If the until argument isn't NULL, stop the pipeline walk when reaching the * until entity. This is used to disable a partially started pipeline due to a * subdev start error. */ static int iss_pipeline_disable(struct iss_pipeline *pipe, struct media_entity *until) { struct iss_device *iss = pipe->output->iss; struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; int failure = 0; int ret; entity = &pipe->output->video.entity; while (1) { pad = &entity->pads[0]; if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; pad = media_entity_remote_pad(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; entity = pad->entity; if (entity == until) break; subdev = media_entity_to_v4l2_subdev(entity); ret = v4l2_subdev_call(subdev, video, s_stream, 0); if (ret < 0) { dev_warn(iss->dev, "%s: module stop timeout.\n", subdev->name); /* If the entity failed to stopped, assume it has * crashed. Mark it as such, the ISS will be reset when * applications will release it. */ media_entity_enum_set(&iss->crashed, &subdev->entity); failure = -ETIMEDOUT; } } return failure; }
/* * pipeline_pm_power_one - Apply power change to an entity * @entity: The entity * @change: Use count change * * Change the entity use count by @change. If the entity is a subdev update its * power state by calling the core::s_power operation when the use count goes * from 0 to != 0 or from != 0 to 0. * * Return 0 on success or a negative error code on failure. */ static int pipeline_pm_power_one(struct media_entity *entity, int change) { struct v4l2_subdev *subdev; int ret; subdev = is_media_entity_v4l2_subdev(entity) ? media_entity_to_v4l2_subdev(entity) : NULL; if (entity->use_count == 0 && change > 0 && subdev != NULL) { ret = v4l2_subdev_call(subdev, core, s_power, 1); if (ret < 0 && ret != -ENOIOCTLCMD) return ret; } entity->use_count += change; WARN_ON(entity->use_count < 0); if (entity->use_count == 0 && change < 0 && subdev != NULL) v4l2_subdev_call(subdev, core, s_power, 0); return 0; }
/* * Check for source/sink format differences at each link. * Return 0 if the formats match or -EPIPE otherwise. */ static int isp_video_pipeline_validate(struct fimc_isp *isp) { struct v4l2_subdev *sd = &isp->subdev; struct v4l2_subdev_format sink_fmt, src_fmt; struct media_pad *pad; int ret; while (1) { /* Retrieve format at the sink pad */ pad = &sd->entity.pads[0]; if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; sink_fmt.pad = pad->index; sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt); if (ret < 0 && ret != -ENOIOCTLCMD) return -EPIPE; /* Retrieve format at the source pad */ pad = media_entity_remote_pad(pad); if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) break; sd = media_entity_to_v4l2_subdev(pad->entity); src_fmt.pad = pad->index; src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); if (ret < 0 && ret != -ENOIOCTLCMD) return -EPIPE; if (src_fmt.format.width != sink_fmt.format.width || src_fmt.format.height != sink_fmt.format.height || src_fmt.format.code != sink_fmt.format.code) return -EPIPE; } return 0; }
static int vdic_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct vdic_priv *priv = v4l2_get_subdevdata(sd); struct v4l2_subdev *remote_sd; int ret = 0; dev_dbg(priv->dev, "link setup %s -> %s", remote->entity->name, local->entity->name); mutex_lock(&priv->lock); if (local->flags & MEDIA_PAD_FL_SOURCE) { if (!is_media_entity_v4l2_subdev(remote->entity)) { ret = -EINVAL; goto out; } remote_sd = media_entity_to_v4l2_subdev(remote->entity); if (flags & MEDIA_LNK_FL_ENABLED) { if (priv->sink_sd) { ret = -EBUSY; goto out; } priv->sink_sd = remote_sd; } else { priv->sink_sd = NULL; } goto out; } /* this is a sink pad */ if (flags & MEDIA_LNK_FL_ENABLED) { if (priv->src) { ret = -EBUSY; goto out; } } else { priv->src = NULL; goto out; } if (local->index == VDIC_SINK_PAD_IDMAC) { struct imx_media_video_dev *vdev = priv->vdev; if (!is_media_entity_v4l2_video_device(remote->entity)) { ret = -EINVAL; goto out; } if (!vdev) { ret = -ENODEV; goto out; } priv->csi_direct = false; } else { if (!is_media_entity_v4l2_subdev(remote->entity)) { ret = -EINVAL; goto out; } remote_sd = media_entity_to_v4l2_subdev(remote->entity); /* direct pad must connect to a CSI */ if (!(remote_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI) || remote->index != CSI_SRC_PAD_DIRECT) { ret = -EINVAL; goto out; } priv->csi_direct = true; } priv->src = remote->entity; /* record which input pad is now active */ priv->active_input_pad = local->index; out: mutex_unlock(&priv->lock); return ret; }