/** * xvip_pipeline_start_stop - Start ot stop streaming on a pipeline * @pipe: The pipeline * @start: Start (when true) or stop (when false) the pipeline * * Walk the entities chain starting at the pipeline output video node and start * or stop all of them. * * Return: 0 if successful, or the return value of the failed video::s_stream * operation otherwise. */ static int xvip_pipeline_start_stop(struct xvip_pipeline *pipe, bool start) { struct xvip_dma *dma = pipe->output; struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; int ret; entity = &dma->video.entity; pad = NULL; while (1) { pad = xvip_get_entity_sink(entity, pad); if (pad == NULL) break; if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; pad = media_entity_remote_pad(pad); if (pad == NULL || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) break; entity = pad->entity; subdev = media_entity_to_v4l2_subdev(entity); ret = v4l2_subdev_call(subdev, video, s_stream, start); if (start && ret < 0 && ret != -ENOIOCTLCMD) return ret; } return 0; }
/** * media_entity_ops */ static int nxp_resc_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags) { switch (local->index | media_entity_type(remote->entity)) { case NXP_RESC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: if (flags & MEDIA_LNK_FL_ENABLED) printk("%s: mlc connect to me\n", __func__); else printk("%s: mlc disconnect to me\n", __func__); break; case NXP_RESC_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: if (flags & MEDIA_LNK_FL_ENABLED) printk("%s: I connect to HDMI\n", __func__); else printk("%s: I disconnect to HDMI\n", __func__); break; default: pr_err("%s: invalid link\n", __func__); return -EINVAL; } return 0; }
/* * 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_graph graph; 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->parent; mutex_lock(&mdev->graph_mutex); media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) 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); return ret ? -ETIMEDOUT : 0; }
/* * unicam_pipeline_enable - enable streaming on a pipeline * @pipe: unicam pipeline * @mode: stream mode (single shot or continuous) * * Walk the entities chain starting at the pipeline output video node and start * all module in the chain in the given node. * * Return 0 if successful, or the return value of the failed video::s_stream * operation otherwise. */ static int unicam_pipeline_enable(struct unicam_pipeline *pipe, enum unicam_pipeline_stream_state mode) { struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; 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_source(pad); if (pad == NULL || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) 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) return ret; } return 0; }
static int tv_graph_pipeline_stream(struct mxr_pipeline *pipe, int on) { struct mxr_device *mdev = pipe->layer->mdev; struct media_entity *me = &pipe->layer->vfd.entity; /* source pad of graphic layer entity */ struct media_pad *pad = &me->pads[0]; struct v4l2_subdev *sd; struct exynos_entity_data md_data; mxr_dbg(mdev, "%s TV graphic layer pipeline\n", on ? "start" : "stop"); /* find remote pad through enabled link */ pad = media_entity_remote_source(pad); if (pad == NULL) return -EPIPE; if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) mxr_warn(mdev, "cannot find remote pad\n"); sd = media_entity_to_v4l2_subdev(pad->entity); mxr_dbg(mdev, "s_stream of %s sub-device is called\n", sd->name); md_data.mxr_data_from = FROM_MXR_VD; v4l2_set_subdevdata(sd, &md_data); v4l2_subdev_call(sd, video, s_stream, on); return 0; }
/* Capture subdev media entity operations */ static int fimc_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 fimc_dev *fimc = v4l2_get_subdevdata(sd); if (media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) return -EINVAL; if (WARN_ON(fimc == NULL)) return 0; dbg("%s --> %s, flags: 0x%x. input: 0x%x", local->entity->name, remote->entity->name, flags, fimc->vid_cap.input); if (flags & MEDIA_LNK_FL_ENABLED) { if (fimc->vid_cap.input != 0) return -EBUSY; fimc->vid_cap.input = sd->grp_id; return 0; } fimc->vid_cap.input = 0; return 0; }
static int fimc_md_link_notify(struct media_pad *source, struct media_pad *sink, u32 flags) { struct fimc_lite *fimc_lite = NULL; struct fimc_dev *fimc = NULL; struct fimc_pipeline *pipeline; struct v4l2_subdev *sd; int ret = 0; if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) return 0; sd = media_entity_to_v4l2_subdev(sink->entity); switch (sd->grp_id) { case FLITE_GROUP_ID: fimc_lite = v4l2_get_subdevdata(sd); pipeline = &fimc_lite->pipeline; break; case FIMC_GROUP_ID: fimc = v4l2_get_subdevdata(sd); pipeline = &fimc->pipeline; break; default: return 0; } if (!(flags & MEDIA_LNK_FL_ENABLED)) { ret = __fimc_pipeline_close(pipeline); pipeline->subdevs[IDX_SENSOR] = NULL; pipeline->subdevs[IDX_CSIS] = NULL; if (fimc) { mutex_lock(&fimc->lock); fimc_ctrls_delete(fimc->vid_cap.ctx); mutex_unlock(&fimc->lock); } return ret; } /* * Link activation. Enable power of pipeline elements only if the * pipeline is already in use, i.e. its video node is opened. * Recreate the controls destroyed during the link deactivation. */ if (fimc) { mutex_lock(&fimc->lock); if (fimc->vid_cap.refcnt > 0) { ret = __fimc_pipeline_open(pipeline, source->entity, true); if (!ret) ret = fimc_capture_ctrls_create(fimc); } mutex_unlock(&fimc->lock); } else { mutex_lock(&fimc_lite->lock); ret = __fimc_pipeline_open(pipeline, source->entity, true); mutex_unlock(&fimc_lite->lock); } return ret ? -EPIPE : ret; }
/* Return a pointer to the ISP video instance at the far end of the pipeline. */ static struct isp_video * isp_video_far_end(struct isp_video *video) { struct media_entity_graph graph; struct media_entity *entity = &video->video.entity; struct media_device *mdev = entity->parent; struct isp_video *far_end = NULL; mutex_lock(&mdev->graph_mutex); 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_ENTITY_TYPE_DEVNODE) continue; far_end = to_isp_video(media_entity_to_video_device(entity)); if (far_end->type != video->type) break; far_end = NULL; } mutex_unlock(&mdev->graph_mutex); return far_end; }
static int fimc_md_link_notify(struct media_pad *source, struct media_pad *sink, u32 flags) { struct v4l2_subdev *sd; struct fimc_dev *fimc; int ret = 0; if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) return 0; sd = media_entity_to_v4l2_subdev(sink->entity); fimc = v4l2_get_subdevdata(sd); if (!(flags & MEDIA_LNK_FL_ENABLED)) { ret = __fimc_pipeline_shutdown(fimc); fimc->pipeline.sensor = NULL; fimc->pipeline.csis = NULL; mutex_lock(&fimc->lock); fimc_ctrls_delete(fimc->vid_cap.ctx); mutex_unlock(&fimc->lock); return ret; } mutex_lock(&fimc->lock); if (fimc->vid_cap.refcnt > 0) { ret = __fimc_pipeline_initialize(fimc, source->entity, true); if (!ret) ret = fimc_capture_ctrls_create(fimc); } mutex_unlock(&fimc->lock); return ret ? -EPIPE : ret; }
static int unicam_pipeline_disable(struct unicam_pipeline *pipe) { struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; int failure = 0; entity = &pipe->output->video.entity; while (1) { pad = &entity->pads[0]; if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; pad = media_entity_remote_source(pad); if ((pad == NULL) || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) break; entity = pad->entity; subdev = media_entity_to_v4l2_subdev(entity); failure = v4l2_subdev_call(subdev, video, s_stream, 0); } return 0; }
/* make a note of pipeline details */ static void vpfe_prepare_pipeline(struct vpfe_video_device *video) { struct media_entity *entity = &video->video_dev.entity; struct media_device *mdev = entity->parent; struct vpfe_pipeline *pipe = &video->pipe; struct vpfe_video_device *far_end = NULL; struct media_entity_graph graph; pipe->input_num = 0; pipe->output_num = 0; if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) pipe->inputs[pipe->input_num++] = video; else pipe->outputs[pipe->output_num++] = video; mutex_lock(&mdev->graph_mutex); 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) continue; far_end = to_vpfe_video(media_entity_to_video_device(entity)); if (far_end->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) pipe->inputs[pipe->input_num++] = far_end; else pipe->outputs[pipe->output_num++] = far_end; } mutex_unlock(&mdev->graph_mutex); }
/* Capture subdev media entity operations */ static int fimc_lite_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 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)) return 0; v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x\n", __func__, remote->entity->name, local->entity->name, flags, fimc->source_subdev_grp_id); switch (local->index) { case FLITE_SD_PAD_SINK: if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) { ret = -EINVAL; break; } if (flags & MEDIA_LNK_FL_ENABLED) { if (fimc->source_subdev_grp_id == 0) fimc->source_subdev_grp_id = sd->grp_id; else ret = -EBUSY; } else { fimc->source_subdev_grp_id = 0; fimc->sensor = NULL; } break; 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) atomic_set(&fimc->out_path, FIMC_IO_DMA); else ret = -EINVAL; break; 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) atomic_set(&fimc->out_path, FIMC_IO_ISP); else ret = -EINVAL; break; default: v4l2_err(sd, "Invalid pad index\n"); ret = -EINVAL; } mb(); 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_source(spad); if (pad) break; } if (pad == NULL || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) 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]); }
int gsc_out_link_validate(const struct media_pad *source, const struct media_pad *sink) { struct v4l2_subdev_format src_fmt; struct v4l2_subdev_crop dst_crop; struct v4l2_subdev *sd; struct gsc_dev *gsc; struct gsc_frame *f; int ret; if (media_entity_type(source->entity) != MEDIA_ENT_T_V4L2_SUBDEV || media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) { gsc_err("media entity type isn't subdev\n"); return 0; } sd = media_entity_to_v4l2_subdev(source->entity); gsc = entity_data_to_gsc(v4l2_get_subdevdata(sd)); f = &gsc->out.ctx->d_frame; src_fmt.format.width = f->crop.width; src_fmt.format.height = f->crop.height; src_fmt.format.code = f->fmt->mbus_code; sd = media_entity_to_v4l2_subdev(sink->entity); /* To check if G-Scaler destination size and Mixer destinatin size are the same */ dst_crop.pad = sink->index; dst_crop.which = V4L2_SUBDEV_FORMAT_ACTIVE; ret = v4l2_subdev_call(sd, pad, get_crop, NULL, &dst_crop); if (ret < 0 && ret != -ENOIOCTLCMD) { gsc_err("subdev get_fmt is failed\n"); return -EPIPE; } if (src_fmt.format.width != dst_crop.rect.width || src_fmt.format.height != dst_crop.rect.height) { gsc_err("sink and source format is different\ src_fmt.w = %d, src_fmt.h = %d,\ dst_crop.w = %d, dst_crop.h = %d, rotation = %d", src_fmt.format.width, src_fmt.format.height, dst_crop.rect.width, dst_crop.rect.height, gsc->out.ctx->gsc_ctrls.rotate->val); return -EINVAL; }
static int v4l2_subdev_link_validate_get_format(struct media_pad *pad, struct v4l2_subdev_format *fmt) { switch (media_entity_type(pad->entity)) { case MEDIA_ENT_T_V4L2_SUBDEV: fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt->pad = pad->index; return v4l2_subdev_call(media_entity_to_v4l2_subdev( pad->entity), pad, get_fmt, NULL, fmt); default: WARN(1, "Driver bug! Wrong media entity type %d, entity %s\n", media_entity_type(pad->entity), pad->entity->name); /* Fall through */ case MEDIA_ENT_T_DEVNODE_V4L: return -EINVAL; } }
/* * isp_pipeline_enable - Enable streaming on a pipeline * @pipe: ISP 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 isp_pipeline_enable(struct isp_pipeline *pipe, enum isp_pipeline_stream_state mode) { struct isp_device *isp = pipe->output->isp; struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; unsigned long flags; int ret; spin_lock_irqsave(&pipe->lock, flags); pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_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_source(pad); if (pad == NULL || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) 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) return ret; if (subdev == &isp->isp_ccdc.subdev) { v4l2_subdev_call(&isp->isp_aewb.subdev, video, s_stream, mode); v4l2_subdev_call(&isp->isp_af.subdev, video, s_stream, mode); v4l2_subdev_call(&isp->isp_hist.subdev, video, s_stream, mode); pipe->do_propagation = true; } } /* Frame number propagation. In continuous streaming mode the number * is incremented in the frame start ISR. In mem-to-mem mode * singleshot is used and frame start IRQs are not available. * Thus we have to increment the number here. */ if (pipe->do_propagation && mode == ISP_PIPELINE_STREAM_SINGLESHOT) atomic_inc(&pipe->frame_number); return 0; }
/* * isp_subdev_link_setup - Setup isp subdev connections * @entity: ispsubdev 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 isp_subdev_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 atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); struct atomisp_device *isp = isp_sd->isp; switch (local->index | media_entity_type(remote->entity)) { case ATOMISP_SUBDEV_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: /* Read from the sensor CSI2-4p or CSI2-1p. */ if (!(flags & MEDIA_LNK_FL_ENABLED)) { isp_sd->input = ATOMISP_SUBDEV_INPUT_NONE; break; } if (isp_sd->input != ATOMISP_SUBDEV_INPUT_NONE) return -EBUSY; if (remote->entity == &isp->csi2_4p.subdev.entity) isp_sd->input = ATOMISP_SUBDEV_INPUT_CSI2_4P; else if (remote->entity == &isp->csi2_1p.subdev.entity) isp_sd->input = ATOMISP_SUBDEV_INPUT_CSI2_1P; else isp_sd->input = ATOMISP_SUBDEV_INPUT_CSI2_4P; break; case ATOMISP_SUBDEV_PAD_SINK | MEDIA_ENT_T_DEVNODE: /* read from memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (isp_sd->input == ATOMISP_SUBDEV_INPUT_CSI2_4P || isp_sd->input == ATOMISP_SUBDEV_INPUT_CSI2_1P) return -EBUSY; isp_sd->input = ATOMISP_SUBDEV_INPUT_MEMORY; } else { if (isp_sd->input == ATOMISP_SUBDEV_INPUT_MEMORY) isp_sd->input = ATOMISP_SUBDEV_INPUT_NONE; } break; case ATOMISP_SUBDEV_PAD_SOURCE_VF | MEDIA_ENT_T_DEVNODE: /* always write to memory */ break; case ATOMISP_SUBDEV_PAD_SOURCE_MO | MEDIA_ENT_T_DEVNODE: /* always write to memory */ break; default: return -EINVAL; } return 0; }
/* * ipipeif_link_setup - Setup IPIPEIF connections * @entity: IPIPEIF 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 ipipeif_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_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); struct iss_device *iss = to_iss_device(ipipeif); switch (local->index | media_entity_type(remote->entity)) { case IPIPEIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: /* Read from the sensor CSI2a or CSI2b. */ if (!(flags & MEDIA_LNK_FL_ENABLED)) { ipipeif->input = IPIPEIF_INPUT_NONE; break; } if (ipipeif->input != IPIPEIF_INPUT_NONE) return -EBUSY; if (remote->entity == &iss->csi2a.subdev.entity) ipipeif->input = IPIPEIF_INPUT_CSI2A; else if (remote->entity == &iss->csi2b.subdev.entity) ipipeif->input = IPIPEIF_INPUT_CSI2B; break; case IPIPEIF_PAD_SOURCE_ISIF_SF | MEDIA_ENT_T_DEVNODE: /* Write to memory */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ipipeif->output & ~IPIPEIF_OUTPUT_MEMORY) return -EBUSY; ipipeif->output |= IPIPEIF_OUTPUT_MEMORY; } else { ipipeif->output &= ~IPIPEIF_OUTPUT_MEMORY; } break; case IPIPEIF_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV: /* Send to IPIPE/RESIZER */ if (flags & MEDIA_LNK_FL_ENABLED) { if (ipipeif->output & ~IPIPEIF_OUTPUT_VP) return -EBUSY; ipipeif->output |= IPIPEIF_OUTPUT_VP; } else { ipipeif->output &= ~IPIPEIF_OUTPUT_VP; } break; default: return -EINVAL; } return 0; }
/* * ccdc_link_setup - Setup CCDC connections * @entity: CCDC 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 resizer_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 vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); switch (local->index | media_entity_type(remote->entity)) { case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: /* Read from previewer - continous mode */ if (!(flags & MEDIA_LNK_FL_ENABLED)) { reset_resizer_channel_mode(resizer); resizer->input = RESIZER_INPUT_NONE; break; } if (resizer->input != RESIZER_INPUT_NONE) return -EBUSY; resizer->input = RESIZER_INPUT_PREVIEWER; /* TODO: need 2 handle this */ set_resizer_channel_cont_mode(resizer); break; case RESIZER_PAD_SINK | MEDIA_ENT_T_DEVNODE: /* Read from memory - single shot mode*/ if (!(flags & MEDIA_LNK_FL_ENABLED)) { reset_resizer_channel_mode(resizer); resizer->input = RESIZER_INPUT_NONE; break; } if (set_resizer_channel_ss_mode(resizer)) return -EINVAL; resizer->input = RESIZER_INPUT_MEMORY; break; case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: /* Write to memory */ if (flags & MEDIA_LNK_FL_ENABLED) resizer->output = RESIZER_OUPUT_MEMORY; else resizer->output = RESIZER_OUTPUT_NONE; break; default: return -EINVAL; } return 0; }
/** * fimc_pipeline_validate - check for formats inconsistencies * between source and sink pad of each link * * Return 0 if all formats match or -EPIPE otherwise. */ static int fimc_pipeline_validate(struct fimc_dev *fimc) { struct v4l2_subdev_format sink_fmt, src_fmt; struct fimc_vid_cap *vid_cap = &fimc->vid_cap; struct v4l2_subdev *sd; struct media_pad *pad; int ret; /* Start with the video capture node pad */ pad = media_entity_remote_source(&vid_cap->vd_pad); if (pad == NULL) return -EPIPE; /* FIMC.{N} subdevice */ sd = media_entity_to_v4l2_subdev(pad->entity); 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->vid_cap.subdev) { struct fimc_frame *ff = &vid_cap->ctx->s_frame; sink_fmt.format.width = ff->f_width; sink_fmt.format.height = ff->f_height; sink_fmt.format.code = ff->fmt ? ff->fmt->mbus_code : 0; } 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_source(pad); if (pad == NULL || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) 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; }
/* Locking: called with entity->parent->graph_mutex mutex held. */ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable) { struct media_entity *entity_err = entity; struct media_entity_graph graph; int ret; /* * Walk current graph and call the pipeline open/close routine for each * opened video node that belongs to the graph of entities connected * through active links. This is needed as we cannot power on/off the * subdevs in random order. */ media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) continue; ret = __fimc_md_modify_pipeline(entity, enable); if (ret < 0) goto err; } 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) continue; __fimc_md_modify_pipeline(entity_err, !enable); if (entity_err == entity) break; } 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 v4l2_subdev *sd; 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_source(spad); if (pad) break; } if (pad == NULL || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) break; sd = media_entity_to_v4l2_subdev(pad->entity); switch (sd->grp_id) { case GRP_ID_FIMC_IS_SENSOR: case GRP_ID_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: /* No need to control FIMC subdev through subdev ops */ break; case GRP_ID_FIMC_IS: p->subdevs[IDX_IS_ISP] = sd; break; default: break; } me = &sd->entity; if (me->num_pads == 1) break; } }
/** * fimc_pipeline_prepare - update pipeline information with subdevice pointers * @fimc: fimc device terminating the pipeline * * Caller holds the graph mutex. */ void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_entity *me) { struct media_pad *pad = &me->pads[0]; struct v4l2_subdev *sd; int i; for (i = 0; i < IDX_MAX; i++) p->subdevs[i] = NULL; if (gfmd->fimc[0]->vid_cap.use_isp) { p->subdevs[IDX_SENSOR] = gfmd->fimc[0]->vid_cap.isp_subdev; p->subdevs[IDX_CSIS] = gfmd->fimc[0]->vid_cap.mipi_subdev; p->subdevs[IDX_FLITE] = gfmd->fimc[0]->vid_cap.flite_subdev; gfmd->fimc[0]->vid_cap.is.sd = \ gfmd->fimc[0]->vid_cap.isp_subdev; return; } while (1) { if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; /* source pad */ pad = media_entity_remote_source(pad); if (pad == NULL || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) break; sd = media_entity_to_v4l2_subdev(pad->entity); switch (sd->grp_id) { case SENSOR_GROUP_ID: p->subdevs[IDX_SENSOR] = sd; break; case CSIS_GROUP_ID: p->subdevs[IDX_CSIS] = sd; break; case FLITE_GROUP_ID: p->subdevs[IDX_FLITE] = sd; break; case FIMC_GROUP_ID: /* No need to control FIMC subdev through subdev ops */ break; default: pr_warn("%s: Unknown subdev grp_id: %#x\n", __func__, sd->grp_id); } /* sink pad */ pad = &sd->entity.pads[0]; } }
/* * isp_pipeline_pm_use_count - Count the number of users of a pipeline * @entity: The entity * * 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) { struct media_entity_graph graph; int use = 0; media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) use += entity->use_count; } return use; }
static struct v4l2_subdev *gsc_cap_remote_subdev(struct gsc_dev *gsc, u32 *pad) { struct media_pad *remote; remote = media_entity_remote_source(&gsc->cap.vd_pad); if (remote == NULL || media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) return NULL; if (pad) *pad = remote->index; return media_entity_to_v4l2_subdev(remote->entity); }
static struct v4l2_subdev * 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) return NULL; if (pad) *pad = remote->index; return media_entity_to_v4l2_subdev(remote->entity); }
static int gsc_capture_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 gsc_dev *gsc = v4l2_get_subdevdata(sd); struct gsc_capture_device *cap = &gsc->cap; switch (local->index | media_entity_type(remote->entity)) { case GSC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: if (flags & MEDIA_LNK_FL_ENABLED) { gsc_dbg("local to gsc-subdev link enable"); if (cap->input != 0) return -EBUSY; /* Write-Back link enabled */ if (!strcmp(remote->entity->name, FIMD_MODULE_NAME)) { gsc->cap.sd_disp = media_entity_to_v4l2_subdev(remote->entity); gsc->cap.sd_disp->grp_id = FIMD_GRP_ID; cap->ctx->in_path = GSC_WRITEBACK; cap->input = GSC_IN_FIMD_WRITEBACK; } else if (remote->index == FLITE_PAD_SOURCE_PREV) { cap->ctx->in_path = GSC_CAMERA; cap->input = GSC_IN_FLITE_PREVIEW; } else { cap->ctx->in_path = GSC_CAMERA; cap->input = GSC_IN_FLITE_CAMCORDING; } } else { if (cap->input == GSC_IN_FIMD_WRITEBACK) gsc->pipeline.disp = NULL; else if ((cap->input == GSC_IN_FLITE_PREVIEW) || (cap->input == GSC_IN_FLITE_CAMCORDING)) gsc->pipeline.flite = NULL; gsc_dbg("local to gsc-subdev link disable"); cap->input = GSC_IN_NONE; } break; case GSC_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: if (flags & MEDIA_LNK_FL_ENABLED) gsc_dbg("gsc-subdev to gsc-video link enable"); else gsc_dbg("gsc-subdev to gsc-video link disable"); break; } return 0; }
/* Capture subdev media entity operations */ static int fimc_lite_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 fimc_lite *fimc = v4l2_get_subdevdata(sd); unsigned int remote_ent_type = media_entity_type(remote->entity); if (WARN_ON(fimc == NULL)) return 0; v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x", __func__, local->entity->name, remote->entity->name, flags, fimc->source_subdev_grp_id); switch (local->index) { case FIMC_SD_PAD_SINK: if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) return -EINVAL; if (flags & MEDIA_LNK_FL_ENABLED) { if (fimc->source_subdev_grp_id != 0) return -EBUSY; fimc->source_subdev_grp_id = sd->grp_id; return 0; } fimc->source_subdev_grp_id = 0; break; case FIMC_SD_PAD_SOURCE: if (!(flags & MEDIA_LNK_FL_ENABLED)) { fimc->out_path = FIMC_IO_NONE; return 0; } if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) fimc->out_path = FIMC_IO_ISP; else fimc->out_path = FIMC_IO_DMA; break; default: v4l2_err(sd, "Invalid pad index\n"); return -EINVAL; } return 0; }
static struct v4l2_subdev * isp_video_remote_subdev(struct isp_video *video, u32 *pad) { struct media_pad *remote; remote = media_entity_remote_source(&video->pad); if (remote == NULL || media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) return NULL; if (pad) *pad = remote->index; return media_entity_to_v4l2_subdev(remote->entity); }
/* * 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 (pipe->entities & 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 == NULL || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) 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) return ret; } iss_print_status(pipe->output->iss); return 0; }