162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * vimc-streamer.c Virtual Media Controller Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2018 Lucas A. M. Magalhães <lucmaga@gmail.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/freezer.h>
1162306a36Sopenharmony_ci#include <linux/kthread.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "vimc-streamer.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/**
1662306a36Sopenharmony_ci * vimc_get_source_entity - get the entity connected with the first sink pad
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * @ent:	reference media_entity
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * Helper function that returns the media entity containing the source pad
2162306a36Sopenharmony_ci * linked with the first sink pad from the given media entity pad list.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Return: The source pad or NULL, if it wasn't found.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_cistatic struct media_entity *vimc_get_source_entity(struct media_entity *ent)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	struct media_pad *pad;
2862306a36Sopenharmony_ci	int i;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	for (i = 0; i < ent->num_pads; i++) {
3162306a36Sopenharmony_ci		if (ent->pads[i].flags & MEDIA_PAD_FL_SOURCE)
3262306a36Sopenharmony_ci			continue;
3362306a36Sopenharmony_ci		pad = media_pad_remote_pad_first(&ent->pads[i]);
3462306a36Sopenharmony_ci		return pad ? pad->entity : NULL;
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci	return NULL;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/**
4062306a36Sopenharmony_ci * vimc_streamer_pipeline_terminate - Disable stream in all ved in stream
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * @stream: the pointer to the stream structure with the pipeline to be
4362306a36Sopenharmony_ci *	    disabled.
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * Calls s_stream to disable the stream in each entity of the pipeline
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistatic void vimc_streamer_pipeline_terminate(struct vimc_stream *stream)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct vimc_ent_device *ved;
5162306a36Sopenharmony_ci	struct v4l2_subdev *sd;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	while (stream->pipe_size) {
5462306a36Sopenharmony_ci		stream->pipe_size--;
5562306a36Sopenharmony_ci		ved = stream->ved_pipeline[stream->pipe_size];
5662306a36Sopenharmony_ci		stream->ved_pipeline[stream->pipe_size] = NULL;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci		if (!is_media_entity_v4l2_subdev(ved->ent))
5962306a36Sopenharmony_ci			continue;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		sd = media_entity_to_v4l2_subdev(ved->ent);
6262306a36Sopenharmony_ci		v4l2_subdev_call(sd, video, s_stream, 0);
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/**
6762306a36Sopenharmony_ci * vimc_streamer_pipeline_init - Initializes the stream structure
6862306a36Sopenharmony_ci *
6962306a36Sopenharmony_ci * @stream: the pointer to the stream structure to be initialized
7062306a36Sopenharmony_ci * @ved:    the pointer to the vimc entity initializing the stream
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * Initializes the stream structure. Walks through the entity graph to
7362306a36Sopenharmony_ci * construct the pipeline used later on the streamer thread.
7462306a36Sopenharmony_ci * Calls vimc_streamer_s_stream() to enable stream in all entities of
7562306a36Sopenharmony_ci * the pipeline.
7662306a36Sopenharmony_ci *
7762306a36Sopenharmony_ci * Return: 0 if success, error code otherwise.
7862306a36Sopenharmony_ci */
7962306a36Sopenharmony_cistatic int vimc_streamer_pipeline_init(struct vimc_stream *stream,
8062306a36Sopenharmony_ci				       struct vimc_ent_device *ved)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct media_entity *entity;
8362306a36Sopenharmony_ci	struct video_device *vdev;
8462306a36Sopenharmony_ci	struct v4l2_subdev *sd;
8562306a36Sopenharmony_ci	int ret = 0;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	stream->pipe_size = 0;
8862306a36Sopenharmony_ci	while (stream->pipe_size < VIMC_STREAMER_PIPELINE_MAX_SIZE) {
8962306a36Sopenharmony_ci		if (!ved) {
9062306a36Sopenharmony_ci			vimc_streamer_pipeline_terminate(stream);
9162306a36Sopenharmony_ci			return -EINVAL;
9262306a36Sopenharmony_ci		}
9362306a36Sopenharmony_ci		stream->ved_pipeline[stream->pipe_size++] = ved;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		if (is_media_entity_v4l2_subdev(ved->ent)) {
9662306a36Sopenharmony_ci			sd = media_entity_to_v4l2_subdev(ved->ent);
9762306a36Sopenharmony_ci			ret = v4l2_subdev_call(sd, video, s_stream, 1);
9862306a36Sopenharmony_ci			if (ret && ret != -ENOIOCTLCMD) {
9962306a36Sopenharmony_ci				dev_err(ved->dev, "subdev_call error %s\n",
10062306a36Sopenharmony_ci					ved->ent->name);
10162306a36Sopenharmony_ci				vimc_streamer_pipeline_terminate(stream);
10262306a36Sopenharmony_ci				return ret;
10362306a36Sopenharmony_ci			}
10462306a36Sopenharmony_ci		}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		entity = vimc_get_source_entity(ved->ent);
10762306a36Sopenharmony_ci		/* Check if the end of the pipeline was reached */
10862306a36Sopenharmony_ci		if (!entity) {
10962306a36Sopenharmony_ci			/* the first entity of the pipe should be source only */
11062306a36Sopenharmony_ci			if (!vimc_is_source(ved->ent)) {
11162306a36Sopenharmony_ci				dev_err(ved->dev,
11262306a36Sopenharmony_ci					"first entity in the pipe '%s' is not a source\n",
11362306a36Sopenharmony_ci					ved->ent->name);
11462306a36Sopenharmony_ci				vimc_streamer_pipeline_terminate(stream);
11562306a36Sopenharmony_ci				return -EPIPE;
11662306a36Sopenharmony_ci			}
11762306a36Sopenharmony_ci			return 0;
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		/* Get the next device in the pipeline */
12162306a36Sopenharmony_ci		if (is_media_entity_v4l2_subdev(entity)) {
12262306a36Sopenharmony_ci			sd = media_entity_to_v4l2_subdev(entity);
12362306a36Sopenharmony_ci			ved = v4l2_get_subdevdata(sd);
12462306a36Sopenharmony_ci		} else {
12562306a36Sopenharmony_ci			vdev = container_of(entity,
12662306a36Sopenharmony_ci					    struct video_device,
12762306a36Sopenharmony_ci					    entity);
12862306a36Sopenharmony_ci			ved = video_get_drvdata(vdev);
12962306a36Sopenharmony_ci		}
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	vimc_streamer_pipeline_terminate(stream);
13362306a36Sopenharmony_ci	return -EINVAL;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/**
13762306a36Sopenharmony_ci * vimc_streamer_thread - Process frames through the pipeline
13862306a36Sopenharmony_ci *
13962306a36Sopenharmony_ci * @data:	vimc_stream struct of the current stream
14062306a36Sopenharmony_ci *
14162306a36Sopenharmony_ci * From the source to the sink, gets a frame from each subdevice and send to
14262306a36Sopenharmony_ci * the next one of the pipeline at a fixed framerate.
14362306a36Sopenharmony_ci *
14462306a36Sopenharmony_ci * Return:
14562306a36Sopenharmony_ci * Always zero (created as ``int`` instead of ``void`` to comply with
14662306a36Sopenharmony_ci * kthread API).
14762306a36Sopenharmony_ci */
14862306a36Sopenharmony_cistatic int vimc_streamer_thread(void *data)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct vimc_stream *stream = data;
15162306a36Sopenharmony_ci	u8 *frame = NULL;
15262306a36Sopenharmony_ci	int i;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	set_freezable();
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	for (;;) {
15762306a36Sopenharmony_ci		try_to_freeze();
15862306a36Sopenharmony_ci		if (kthread_should_stop())
15962306a36Sopenharmony_ci			break;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		for (i = stream->pipe_size - 1; i >= 0; i--) {
16262306a36Sopenharmony_ci			frame = stream->ved_pipeline[i]->process_frame(
16362306a36Sopenharmony_ci					stream->ved_pipeline[i], frame);
16462306a36Sopenharmony_ci			if (!frame || IS_ERR(frame))
16562306a36Sopenharmony_ci				break;
16662306a36Sopenharmony_ci		}
16762306a36Sopenharmony_ci		//wait for 60hz
16862306a36Sopenharmony_ci		set_current_state(TASK_UNINTERRUPTIBLE);
16962306a36Sopenharmony_ci		schedule_timeout(HZ / 60);
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	return 0;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/**
17662306a36Sopenharmony_ci * vimc_streamer_s_stream - Start/stop the streaming on the media pipeline
17762306a36Sopenharmony_ci *
17862306a36Sopenharmony_ci * @stream:	the pointer to the stream structure of the current stream
17962306a36Sopenharmony_ci * @ved:	pointer to the vimc entity of the entity of the stream
18062306a36Sopenharmony_ci * @enable:	flag to determine if stream should start/stop
18162306a36Sopenharmony_ci *
18262306a36Sopenharmony_ci * When starting, check if there is no ``stream->kthread`` allocated. This
18362306a36Sopenharmony_ci * should indicate that a stream is already running. Then, it initializes the
18462306a36Sopenharmony_ci * pipeline, creates and runs a kthread to consume buffers through the pipeline.
18562306a36Sopenharmony_ci * When stopping, analogously check if there is a stream running, stop the
18662306a36Sopenharmony_ci * thread and terminates the pipeline.
18762306a36Sopenharmony_ci *
18862306a36Sopenharmony_ci * Return: 0 if success, error code otherwise.
18962306a36Sopenharmony_ci */
19062306a36Sopenharmony_ciint vimc_streamer_s_stream(struct vimc_stream *stream,
19162306a36Sopenharmony_ci			   struct vimc_ent_device *ved,
19262306a36Sopenharmony_ci			   int enable)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	int ret;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (!stream || !ved)
19762306a36Sopenharmony_ci		return -EINVAL;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (enable) {
20062306a36Sopenharmony_ci		if (stream->kthread)
20162306a36Sopenharmony_ci			return 0;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci		ret = vimc_streamer_pipeline_init(stream, ved);
20462306a36Sopenharmony_ci		if (ret)
20562306a36Sopenharmony_ci			return ret;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		stream->kthread = kthread_run(vimc_streamer_thread, stream,
20862306a36Sopenharmony_ci					      "vimc-streamer thread");
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci		if (IS_ERR(stream->kthread)) {
21162306a36Sopenharmony_ci			ret = PTR_ERR(stream->kthread);
21262306a36Sopenharmony_ci			dev_err(ved->dev, "kthread_run failed with %d\n", ret);
21362306a36Sopenharmony_ci			vimc_streamer_pipeline_terminate(stream);
21462306a36Sopenharmony_ci			stream->kthread = NULL;
21562306a36Sopenharmony_ci			return ret;
21662306a36Sopenharmony_ci		}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	} else {
21962306a36Sopenharmony_ci		if (!stream->kthread)
22062306a36Sopenharmony_ci			return 0;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		ret = kthread_stop(stream->kthread);
22362306a36Sopenharmony_ci		/*
22462306a36Sopenharmony_ci		 * kthread_stop returns -EINTR in cases when streamon was
22562306a36Sopenharmony_ci		 * immediately followed by streamoff, and the thread didn't had
22662306a36Sopenharmony_ci		 * a chance to run. Ignore errors to stop the stream in the
22762306a36Sopenharmony_ci		 * pipeline.
22862306a36Sopenharmony_ci		 */
22962306a36Sopenharmony_ci		if (ret)
23062306a36Sopenharmony_ci			dev_dbg(ved->dev, "kthread_stop returned '%d'\n", ret);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		stream->kthread = NULL;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		vimc_streamer_pipeline_terminate(stream);
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return 0;
23862306a36Sopenharmony_ci}
239