Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6319d6a0 authored by Sylwester Nawrocki's avatar Sylwester Nawrocki Committed by Mauro Carvalho Chehab
Browse files

[media] fimc-lite: Add ISP FIFO output support



Add second source media pad for the FIFO data output to FIMC-IS
and implement subdev s_stream op for configurations where FIMC-LITE
is used as a glue logic between FIMC-IS and MIPI-CSIS or an image
sensor. The second source media pad will be linked to the FIMC-LITE
video node.
For proper configuration the attached image sensor/video encoder
properties are needed, like video bus type, signal polarities, etc.
For this purpose there is a small routine added that walks the
pipeline and returns the sensor subdev.

Signed-off-by: default avatarSylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 588c87be
Loading
Loading
Loading
Loading
+114 −32
Original line number Diff line number Diff line
@@ -120,25 +120,29 @@ static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat,
	return def_fmt;
}

static int fimc_lite_hw_init(struct fimc_lite *fimc)
static int fimc_lite_hw_init(struct fimc_lite *fimc, bool isp_output)
{
	struct fimc_pipeline *pipeline = &fimc->pipeline;
	struct fimc_sensor_info *sensor;
	struct v4l2_subdev *sensor;
	struct fimc_sensor_info *si;
	unsigned long flags;

	if (pipeline->subdevs[IDX_SENSOR] == NULL)
	sensor = isp_output ? fimc->sensor : pipeline->subdevs[IDX_SENSOR];

	if (sensor == NULL)
		return -ENXIO;

	if (fimc->fmt == NULL)
		return -EINVAL;

	sensor = v4l2_get_subdev_hostdata(pipeline->subdevs[IDX_SENSOR]);
	/* Get sensor configuration data from the sensor subdev */
	si = v4l2_get_subdev_hostdata(sensor);
	spin_lock_irqsave(&fimc->slock, flags);

	flite_hw_set_camera_bus(fimc, &sensor->pdata);
	flite_hw_set_camera_bus(fimc, &si->pdata);
	flite_hw_set_source_format(fimc, &fimc->inp_frame);
	flite_hw_set_window_offset(fimc, &fimc->inp_frame);
	flite_hw_set_output_dma(fimc, &fimc->out_frame, true);
	flite_hw_set_output_dma(fimc, &fimc->out_frame, !isp_output);
	flite_hw_set_interrupt_mask(fimc);
	flite_hw_set_test_pattern(fimc, fimc->test_pattern->val);

@@ -296,7 +300,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)

	fimc->frame_count = 0;

	ret = fimc_lite_hw_init(fimc);
	ret = fimc_lite_hw_init(fimc, false);
	if (ret) {
		fimc_lite_reinit(fimc, false);
		return ret;
@@ -460,6 +464,11 @@ static int fimc_lite_open(struct file *file)
	if (mutex_lock_interruptible(&fimc->lock))
		return -ERESTARTSYS;

	if (fimc->out_path != FIMC_IO_DMA) {
		ret = -EBUSY;
		goto done;
	}

	set_bit(ST_FLITE_IN_USE, &fimc->state);
	ret = pm_runtime_get_sync(&fimc->pdev->dev);
	if (ret < 0)
@@ -962,6 +971,29 @@ static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = {
	.vidioc_streamoff		= fimc_lite_streamoff,
};

/* Called with the media graph mutex held */
static struct v4l2_subdev *__find_remote_sensor(struct media_entity *me)
{
	struct media_pad *pad = &me->pads[0];
	struct v4l2_subdev *sd;

	while (pad->flags & MEDIA_PAD_FL_SINK) {
		/* 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);

		if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR)
			return sd;
		/* sink pad */
		pad = &sd->entity.pads[0];
	}
	return NULL;
}

/* Capture subdev media entity operations */
static int fimc_lite_link_setup(struct media_entity *entity,
				const struct media_pad *local,
@@ -970,46 +1002,59 @@ 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))
		return 0;

	v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x",
		 __func__, local->entity->name, remote->entity->name,
		 __func__, remote->entity->name, local->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;
	mutex_lock(&fimc->lock);

	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)
				return -EBUSY;
			if (fimc->source_subdev_grp_id == 0)
				fimc->source_subdev_grp_id = sd->grp_id;
			return 0;
			else
				ret = -EBUSY;
		} else {
			fimc->source_subdev_grp_id = 0;
			fimc->sensor = NULL;
		}
		break;

		fimc->source_subdev_grp_id = 0;
	case FLITE_SD_PAD_SOURCE_DMA:
		if (!(flags & MEDIA_LNK_FL_ENABLED))
			fimc->out_path = FIMC_IO_NONE;
		else if (remote_ent_type == MEDIA_ENT_T_DEVNODE)
			fimc->out_path = FIMC_IO_DMA;
		else
			ret = -EINVAL;
		break;

	case FIMC_SD_PAD_SOURCE:
		if (!(flags & MEDIA_LNK_FL_ENABLED)) {
	case FLITE_SD_PAD_SOURCE_ISP:
		if (!(flags & MEDIA_LNK_FL_ENABLED))
			fimc->out_path = FIMC_IO_NONE;
			return 0;
		}
		if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV)
		else if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV)
			fimc->out_path = FIMC_IO_ISP;
		else
			fimc->out_path = FIMC_IO_DMA;
			ret = -EINVAL;
		break;

	default:
		v4l2_err(sd, "Invalid pad index\n");
		return -EINVAL;
		ret = -EINVAL;
	}

	return 0;
	mutex_unlock(&fimc->lock);
	return ret;
}

static const struct media_entity_operations fimc_lite_subdev_media_ops = {
@@ -1188,13 +1233,49 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd,
static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on)
{
	struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
	unsigned long flags;
	int ret;

	if (fimc->out_path == FIMC_IO_DMA)
	/*
	 * Find sensor subdev linked to FIMC-LITE directly or through
	 * MIPI-CSIS. This is required for configuration where FIMC-LITE
	 * is used as a subdev only and feeds data internally to FIMC-IS.
	 * The pipeline links are protected through entity.stream_count
	 * so there is no need to take the media graph mutex here.
	 */
	fimc->sensor = __find_remote_sensor(&sd->entity);

	mutex_lock(&fimc->lock);
	if (fimc->out_path != FIMC_IO_ISP) {
		mutex_unlock(&fimc->lock);
		return -ENOIOCTLCMD;
	}

	/* TODO: */
	if (on) {
		flite_hw_reset(fimc);
		ret = fimc_lite_hw_init(fimc, true);
		if (!ret) {
			spin_lock_irqsave(&fimc->slock, flags);
			flite_hw_capture_start(fimc);
			spin_unlock_irqrestore(&fimc->slock, flags);
		}
	} else {
		set_bit(ST_FLITE_OFF, &fimc->state);

	return 0;
		spin_lock_irqsave(&fimc->slock, flags);
		flite_hw_capture_stop(fimc);
		spin_unlock_irqrestore(&fimc->slock, flags);

		ret = wait_event_timeout(fimc->irq_queue,
				!test_bit(ST_FLITE_OFF, &fimc->state),
				msecs_to_jiffies(200));
		if (ret == 0)
			v4l2_err(sd, "s_stream(0) timeout\n");
		clear_bit(ST_FLITE_RUN, &fimc->state);
	}

	mutex_unlock(&fimc->lock);
	return ret;
}

static int fimc_lite_subdev_s_power(struct v4l2_subdev *sd, int on)
@@ -1347,9 +1428,10 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc)
	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
	snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index);

	fimc->subdev_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
	fimc->subdev_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
	ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
	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);
	if (ret)
		return ret;
@@ -1518,7 +1600,7 @@ static int fimc_lite_resume(struct device *dev)
	INIT_LIST_HEAD(&fimc->active_buf_q);
	fimc_pipeline_call(fimc, open, &fimc->pipeline,
			   &fimc->vfd.entity, false);
	fimc_lite_hw_init(fimc);
	fimc_lite_hw_init(fimc, fimc->out_path == FIMC_IO_ISP);
	clear_bit(ST_FLITE_SUSPENDED, &fimc->state);

	for (i = 0; i < fimc->reqbufs_count; i++) {
+5 −2
Original line number Diff line number Diff line
@@ -45,8 +45,9 @@ enum {
};

#define FLITE_SD_PAD_SINK	0
#define FLITE_SD_PAD_SOURCE	1
#define FLITE_SD_PADS_NUM	2
#define FLITE_SD_PAD_SOURCE_DMA	1
#define FLITE_SD_PAD_SOURCE_ISP	2
#define FLITE_SD_PADS_NUM	3

struct flite_variant {
	unsigned short max_width;
@@ -104,6 +105,7 @@ struct flite_buffer {
 * @subdev: FIMC-LITE subdev
 * @vd_pad: media (sink) pad for the capture video node
 * @subdev_pads: the subdev media pads
 * @sensor: sensor subdev attached to FIMC-LITE directly or through MIPI-CSIS
 * @ctrl_handler: v4l2 control handler
 * @test_pattern: test pattern controls
 * @index: FIMC-LITE platform device index
@@ -139,6 +141,7 @@ struct fimc_lite {
	struct v4l2_subdev	subdev;
	struct media_pad	vd_pad;
	struct media_pad	subdev_pads[FLITE_SD_PADS_NUM];
	struct v4l2_subdev	*sensor;
	struct v4l2_ctrl_handler ctrl_handler;
	struct v4l2_ctrl	*test_pattern;
	u32			index;
+1 −1
Original line number Diff line number Diff line
@@ -603,7 +603,7 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd)
		source = &fimc->subdev.entity;
		sink = &fimc->vfd.entity;
		/* FIMC-LITE's subdev and video node */
		ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
		ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_DMA,
					       sink, 0, flags);
		if (ret)
			break;