mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-01-20 03:36:36 +07:00
f13d5f3619
vimc uses Component API to split the driver into functional components. The real hardware resembles a monolith structure than component and component structure added a level of complexity making it hard to maintain without adding any real benefit. The sensor is one vimc component that would makes sense to be a separate module to closely align with the real hardware. It would be easier to collapse vimc into single monolithic driver first and then split the sensor off as a separate module. Collapse it into a single monolithic driver removing the Component API. This patch removes the component API and makes minimal changes to the code base preserving the functional division of the code structure. Preserving the functional structure allows us to split the sensor off as a separate module in the future. Major design elements in this change are: - Use existing struct vimc_ent_config and struct vimc_pipeline_config to drive the initialization of the functional components. - Make vimc_device and vimc_ent_config global by moving them to vimc-common.h - Add two new hooks add and rm to initialize and register, unregister and free subdevs. - All component API is now gone and bind and unbind hooks are modified to do "add" and "rm" with minimal changes to just add and rm subdevs. - vimc-core's bind and unbind are now register and unregister. - Add a new field to vimc_device structure for saving the pointers to struct vimc_ent_device(s) subdevs create in their "add" hooks. These get used to create links and removing the subdevs. vimc-core allocates this array which sized to number of entries in the topology defined in the vimc_pipeline_config structure. - vimc-core invokes "add" hooks from its vimc_register_devices(). The "add" hooks remain the same and register subdevs. They don't create platform devices of their own and use vimc's pdev.dev as their reference device. Each "add" hook returns pointer to its struct vimc_ent_device. This is saved in the vimc_device ent_devs array. - vimc-core invokes "rm" hooks from its unregister to unregister subdevs and cleanup. - vimc-core invokes "add" and "rm" hooks with pointer to struct vimc_device and the corresponding vimc_ent_device saved in the ent_devs. The following configure and stream test works on all devices. media-ctl -d platform:vimc -V '"Sensor A":0[fmt:SBGGR8_1X8/640x480]' media-ctl -d platform:vimc -V '"Debayer A":0[fmt:SBGGR8_1X8/640x480]' media-ctl -d platform:vimc -V '"Sensor B":0[fmt:SBGGR8_1X8/640x480]' media-ctl -d platform:vimc -V '"Debayer B":0[fmt:SBGGR8_1X8/640x480]' v4l2-ctl -z platform:vimc -d "RGB/YUV Capture" -v width=1920,height=1440 v4l2-ctl -z platform:vimc -d "Raw Capture 0" -v pixelformat=BA81 v4l2-ctl -z platform:vimc -d "Raw Capture 1" -v pixelformat=BA81 v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video1 v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video2 v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video3 Signed-off-by: Shuah Khan <skhan@linuxfoundation.org> Acked-by: Helen Koike <helen.koike@collabora.com> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
220 lines
5.4 KiB
C
220 lines
5.4 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* vimc-streamer.c Virtual Media Controller Driver
|
|
*
|
|
* Copyright (C) 2018 Lucas A. M. Magalhães <lucmaga@gmail.com>
|
|
*
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/freezer.h>
|
|
#include <linux/kthread.h>
|
|
|
|
#include "vimc-streamer.h"
|
|
|
|
/**
|
|
* vimc_get_source_entity - get the entity connected with the first sink pad
|
|
*
|
|
* @ent: reference media_entity
|
|
*
|
|
* Helper function that returns the media entity containing the source pad
|
|
* linked with the first sink pad from the given media entity pad list.
|
|
*
|
|
* Return: The source pad or NULL, if it wasn't found.
|
|
*/
|
|
static struct media_entity *vimc_get_source_entity(struct media_entity *ent)
|
|
{
|
|
struct media_pad *pad;
|
|
int i;
|
|
|
|
for (i = 0; i < ent->num_pads; i++) {
|
|
if (ent->pads[i].flags & MEDIA_PAD_FL_SOURCE)
|
|
continue;
|
|
pad = media_entity_remote_pad(&ent->pads[i]);
|
|
return pad ? pad->entity : NULL;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* vimc_streamer_pipeline_terminate - Disable stream in all ved in stream
|
|
*
|
|
* @stream: the pointer to the stream structure with the pipeline to be
|
|
* disabled.
|
|
*
|
|
* Calls s_stream to disable the stream in each entity of the pipeline
|
|
*
|
|
*/
|
|
static void vimc_streamer_pipeline_terminate(struct vimc_stream *stream)
|
|
{
|
|
struct vimc_ent_device *ved;
|
|
struct v4l2_subdev *sd;
|
|
|
|
while (stream->pipe_size) {
|
|
stream->pipe_size--;
|
|
ved = stream->ved_pipeline[stream->pipe_size];
|
|
stream->ved_pipeline[stream->pipe_size] = NULL;
|
|
|
|
if (!is_media_entity_v4l2_subdev(ved->ent))
|
|
continue;
|
|
|
|
sd = media_entity_to_v4l2_subdev(ved->ent);
|
|
v4l2_subdev_call(sd, video, s_stream, 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* vimc_streamer_pipeline_init - Initializes the stream structure
|
|
*
|
|
* @stream: the pointer to the stream structure to be initialized
|
|
* @ved: the pointer to the vimc entity initializing the stream
|
|
*
|
|
* Initializes the stream structure. Walks through the entity graph to
|
|
* construct the pipeline used later on the streamer thread.
|
|
* Calls vimc_streamer_s_stream() to enable stream in all entities of
|
|
* the pipeline.
|
|
*
|
|
* Return: 0 if success, error code otherwise.
|
|
*/
|
|
static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
|
|
struct vimc_ent_device *ved)
|
|
{
|
|
struct media_entity *entity;
|
|
struct video_device *vdev;
|
|
struct v4l2_subdev *sd;
|
|
int ret = 0;
|
|
|
|
stream->pipe_size = 0;
|
|
while (stream->pipe_size < VIMC_STREAMER_PIPELINE_MAX_SIZE) {
|
|
if (!ved) {
|
|
vimc_streamer_pipeline_terminate(stream);
|
|
return -EINVAL;
|
|
}
|
|
stream->ved_pipeline[stream->pipe_size++] = ved;
|
|
|
|
if (is_media_entity_v4l2_subdev(ved->ent)) {
|
|
sd = media_entity_to_v4l2_subdev(ved->ent);
|
|
ret = v4l2_subdev_call(sd, video, s_stream, 1);
|
|
if (ret && ret != -ENOIOCTLCMD) {
|
|
pr_err("subdev_call error %s\n",
|
|
ved->ent->name);
|
|
vimc_streamer_pipeline_terminate(stream);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
entity = vimc_get_source_entity(ved->ent);
|
|
/* Check if the end of the pipeline was reached*/
|
|
if (!entity)
|
|
return 0;
|
|
|
|
/* Get the next device in the pipeline */
|
|
if (is_media_entity_v4l2_subdev(entity)) {
|
|
sd = media_entity_to_v4l2_subdev(entity);
|
|
ved = v4l2_get_subdevdata(sd);
|
|
} else {
|
|
vdev = container_of(entity,
|
|
struct video_device,
|
|
entity);
|
|
ved = video_get_drvdata(vdev);
|
|
}
|
|
}
|
|
|
|
vimc_streamer_pipeline_terminate(stream);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/**
|
|
* vimc_streamer_thread - Process frames through the pipeline
|
|
*
|
|
* @data: vimc_stream struct of the current stream
|
|
*
|
|
* From the source to the sink, gets a frame from each subdevice and send to
|
|
* the next one of the pipeline at a fixed framerate.
|
|
*
|
|
* Return:
|
|
* Always zero (created as ``int`` instead of ``void`` to comply with
|
|
* kthread API).
|
|
*/
|
|
static int vimc_streamer_thread(void *data)
|
|
{
|
|
struct vimc_stream *stream = data;
|
|
u8 *frame = NULL;
|
|
int i;
|
|
|
|
set_freezable();
|
|
|
|
for (;;) {
|
|
try_to_freeze();
|
|
if (kthread_should_stop())
|
|
break;
|
|
|
|
for (i = stream->pipe_size - 1; i >= 0; i--) {
|
|
frame = stream->ved_pipeline[i]->process_frame(
|
|
stream->ved_pipeline[i], frame);
|
|
if (!frame || IS_ERR(frame))
|
|
break;
|
|
}
|
|
//wait for 60hz
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
schedule_timeout(HZ / 60);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* vimc_streamer_s_stream - Start/stop the streaming on the media pipeline
|
|
*
|
|
* @stream: the pointer to the stream structure of the current stream
|
|
* @ved: pointer to the vimc entity of the entity of the stream
|
|
* @enable: flag to determine if stream should start/stop
|
|
*
|
|
* When starting, check if there is no ``stream->kthread`` allocated. This
|
|
* should indicate that a stream is already running. Then, it initializes the
|
|
* pipeline, creates and runs a kthread to consume buffers through the pipeline.
|
|
* When stopping, analogously check if there is a stream running, stop the
|
|
* thread and terminates the pipeline.
|
|
*
|
|
* Return: 0 if success, error code otherwise.
|
|
*/
|
|
int vimc_streamer_s_stream(struct vimc_stream *stream,
|
|
struct vimc_ent_device *ved,
|
|
int enable)
|
|
{
|
|
int ret;
|
|
|
|
if (!stream || !ved)
|
|
return -EINVAL;
|
|
|
|
if (enable) {
|
|
if (stream->kthread)
|
|
return 0;
|
|
|
|
ret = vimc_streamer_pipeline_init(stream, ved);
|
|
if (ret)
|
|
return ret;
|
|
|
|
stream->kthread = kthread_run(vimc_streamer_thread, stream,
|
|
"vimc-streamer thread");
|
|
|
|
if (IS_ERR(stream->kthread))
|
|
return PTR_ERR(stream->kthread);
|
|
|
|
} else {
|
|
if (!stream->kthread)
|
|
return 0;
|
|
|
|
ret = kthread_stop(stream->kthread);
|
|
if (ret)
|
|
return ret;
|
|
|
|
stream->kthread = NULL;
|
|
|
|
vimc_streamer_pipeline_terminate(stream);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(vimc_streamer_s_stream);
|