/** * fimc_md_set_camclk - peripheral sensor clock setup * @sd: sensor subdev to configure sclk_cam clock for * @on: 1 to enable or 0 to disable the clock * * There are 2 separate clock outputs available in the SoC for external * image processors. These clocks are shared between all registered FIMC * devices to which sensors can be attached, either directly or through * the MIPI CSI receiver. The clock is allowed here to be used by * multiple sensors concurrently if they use same frequency. * The per sensor subdev clk_on attribute helps to synchronize accesses * to the sclk_cam clocks from the video and media device nodes. * This function should only be called when the graph mutex is held. */ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) { struct fimc_sensor_info *s_info = v4l2_get_subdev_hostdata(sd); struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity); return __fimc_md_set_camclk(fmd, s_info, on); }
/** * __fimc_pipeline_open - update the pipeline information, enable power * of all pipeline subdevs and the sensor clock * @me: media entity to start graph walk with * @prepare: true to walk the current pipeline and acquire all subdevs * * Called with the graph mutex held. */ static int __fimc_pipeline_open(struct exynos_media_pipeline *ep, struct media_entity *me, bool prepare) { struct fimc_md *fmd = entity_to_fimc_mdev(me); struct fimc_pipeline *p = to_fimc_pipeline(ep); struct v4l2_subdev *sd; if (WARN_ON(p == NULL || me == NULL)) return -EINVAL; if (prepare) fimc_pipeline_prepare(p, me); sd = p->subdevs[IDX_SENSOR]; if (sd == NULL) { pr_warn("%s(): No sensor subdev\n", __func__); /* * Pipeline open cannot fail so as to make it possible * for the user space to configure the pipeline. */ return 0; } return __fimc_pipeline_enable(ep, fmd); }
/** * fimc_sensor_notify - v4l2_device notification from a sensor subdev * @sd: pointer to a subdev generating the notification * @notification: the notification type, must be S5P_FIMC_TX_END_NOTIFY * @arg: pointer to an u32 type integer that stores the frame payload value * * The End Of Frame notification sent by sensor subdev in its still capture * mode. If there is only a single VSYNC generated by the sensor at the * beginning of a frame transmission, FIMC does not issue the LastIrq * (end of frame) interrupt. And this notification is used to complete the * frame capture and returning a buffer to user-space. Subdev drivers should * call this notification from their last 'End of frame capture' interrupt. */ void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, void *arg) { struct fimc_sensor_info *sensor; struct fimc_vid_buffer *buf; struct fimc_md *fmd; struct fimc_dev *fimc; unsigned long flags; if (sd == NULL) return; sensor = v4l2_get_subdev_hostdata(sd); fmd = entity_to_fimc_mdev(&sd->entity); spin_lock_irqsave(&fmd->slock, flags); fimc = sensor ? sensor->host : NULL; if (fimc && arg && notification == S5P_FIMC_TX_END_NOTIFY && test_bit(ST_CAPT_PEND, &fimc->state)) { unsigned long irq_flags; spin_lock_irqsave(&fimc->slock, irq_flags); if (!list_empty(&fimc->vid_cap.active_buf_q)) { buf = list_entry(fimc->vid_cap.active_buf_q.next, struct fimc_vid_buffer, list); vb2_set_plane_payload(&buf->vb, 0, *((u32 *)arg)); }
/** * __fimc_pipeline_open - update the pipeline information, enable power * of all pipeline subdevs and the sensor clock * @me: media entity to start graph walk with * @prepare: true to walk the current pipeline and acquire all subdevs * * Called with the graph mutex held. */ static int __fimc_pipeline_open(struct exynos_media_pipeline *ep, struct media_entity *me, bool prepare) { struct fimc_md *fmd = entity_to_fimc_mdev(me); struct fimc_pipeline *p = to_fimc_pipeline(ep); struct v4l2_subdev *sd; int ret; if (WARN_ON(p == NULL || me == NULL)) return -EINVAL; if (prepare) fimc_pipeline_prepare(p, me); sd = p->subdevs[IDX_SENSOR]; if (sd == NULL) return -EINVAL; /* Disable PXLASYNC clock if this pipeline includes FIMC-IS */ if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) { ret = clk_prepare_enable(fmd->wbclk[CLK_IDX_WB_B]); if (ret < 0) return ret; } ret = fimc_pipeline_s_power(p, 1); if (!ret) return 0; if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]); 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]); }
/** * __fimc_pipeline_close - disable the sensor clock and pipeline power * @fimc: fimc device terminating the pipeline * * Disable power of all subdevs and turn the external sensor clock off. */ static int __fimc_pipeline_close(struct exynos_media_pipeline *ep) { struct fimc_pipeline *p = to_fimc_pipeline(ep); struct v4l2_subdev *sd = p ? p->subdevs[IDX_SENSOR] : NULL; struct fimc_md *fmd; int ret; if (sd == NULL) { pr_warn("%s(): No sensor subdev\n", __func__); return 0; } ret = fimc_pipeline_s_power(p, 0); fmd = entity_to_fimc_mdev(&sd->entity); /* Disable PXLASYNC clock if this pipeline includes FIMC-IS */ if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]); return ret == -ENXIO ? 0 : ret; }
/** * __fimc_pipeline_close - disable the sensor clock and pipeline power * @fimc: fimc device terminating the pipeline * * Disable power of all subdevs and turn the external sensor clock off. */ static int __fimc_pipeline_close(struct fimc_pipeline *p) { struct v4l2_subdev *sd = p ? p->subdevs[IDX_SENSOR] : NULL; struct fimc_md *fmd; int ret = 0; if (WARN_ON(sd == NULL)) return -EINVAL; if (p->subdevs[IDX_SENSOR]) { ret = fimc_pipeline_s_power(p, 0); fimc_md_set_camclk(sd, false); } fmd = entity_to_fimc_mdev(&sd->entity); /* Disable PXLASYNC clock if this pipeline includes FIMC-IS */ if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]); return ret == -ENXIO ? 0 : ret; }
/** * __fimc_pipeline_s_stream - call s_stream() on pipeline subdevs * @pipeline: video pipeline structure * @on: passed as the s_stream() callback argument */ static int __fimc_pipeline_s_stream(struct exynos_media_pipeline *ep, bool on) { static const u8 seq[2][IDX_MAX] = { { IDX_FIMC, IDX_SENSOR, IDX_IS_ISP, IDX_CSIS, IDX_FLITE }, { IDX_CSIS, IDX_FLITE, IDX_FIMC, IDX_SENSOR, IDX_IS_ISP }, }; struct fimc_pipeline *p = to_fimc_pipeline(ep); struct fimc_md *fmd = entity_to_fimc_mdev(&p->subdevs[IDX_CSIS]->entity); enum fimc_subdev_index sd_id; int i, ret = 0; if (p->subdevs[IDX_SENSOR] == NULL) { if (!fmd->user_subdev_api) { /* * Sensor must be already discovered if we * aren't in the user_subdev_api mode */ return -ENODEV; } /* Get pipeline sink entity */ if (p->subdevs[IDX_FIMC]) sd_id = IDX_FIMC; else if (p->subdevs[IDX_IS_ISP]) sd_id = IDX_IS_ISP; else if (p->subdevs[IDX_FLITE]) sd_id = IDX_FLITE; else return -ENODEV; /* * Sensor could have been linked between open and STREAMON - * check if this is the case. */ fimc_pipeline_prepare(p, &p->subdevs[sd_id]->entity); if (p->subdevs[IDX_SENSOR] == NULL) return -ENODEV; ret = __fimc_pipeline_enable(ep, fmd); if (ret < 0) return ret; } for (i = 0; i < IDX_MAX; i++) { unsigned int idx = seq[on][i]; ret = v4l2_subdev_call(p->subdevs[idx], video, s_stream, on); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) goto error; } return 0; error: fimc_pipeline_s_power(p, !on); for (; i >= 0; i--) { unsigned int idx = seq[on][i]; v4l2_subdev_call(p->subdevs[idx], video, s_stream, !on); } return ret; }