Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/media/platform/renesas/vsp1/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 16 kB image not shown  

Quelle  vsp1_vspx.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/*
 * vsp1_vspx.c  --  R-Car Gen 4 VSPX
 *
 * Copyright (C) 2025 Ideas On Board Oy
 * Copyright (C) 2025 Renesas Electronics Corporation
 */


#include "vsp1_vspx.h"

#include <linux/cleanup.h>
#include <linux/container_of.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/export.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/spinlock.h>

#include <media/media-entity.h>
#include <media/v4l2-subdev.h>
#include <media/vsp1.h>

#include "vsp1_dl.h"
#include "vsp1_iif.h"
#include "vsp1_pipe.h"
#include "vsp1_rwpf.h"

/*
 * struct vsp1_vspx_pipeline - VSPX pipeline
 * @pipe: the VSP1 pipeline
 * @partition: the pre-calculated partition used by the pipeline
 * @mutex: protects the streaming start/stop sequences
 * @lock: protect access to the enabled flag
 * @enabled: the enable flag
 * @vspx_frame_end: frame end callback
 * @frame_end_data: data for the frame end callback
 */

struct vsp1_vspx_pipeline {
 struct vsp1_pipeline pipe;
 struct vsp1_partition partition;

 /*
 * Protects the streaming start/stop sequences.
 *
 * The start/stop sequences cannot be locked with the 'lock' spinlock
 * as they acquire mutexes when handling the pm runtime and the vsp1
 * pipe start/stop operations. Provide a dedicated mutex for this
 * reason.
 */

 struct mutex mutex;

 /*
 * Protects the enable flag.
 *
 * The enabled flag is contended between the start/stop streaming
 * routines and the job_run one, which cannot take a mutex as it is
 * called from the ISP irq context.
 */

 spinlock_t lock;
 bool enabled;

 void (*vspx_frame_end)(void *frame_end_data);
 void *frame_end_data;
};

static inline struct vsp1_vspx_pipeline *
to_vsp1_vspx_pipeline(struct vsp1_pipeline *pipe)
{
 return container_of(pipe, struct vsp1_vspx_pipeline, pipe);
}

/*
 * struct vsp1_vspx - VSPX device
 * @vsp1: the VSP1 device
 * @pipe: the VSPX pipeline
 */

struct vsp1_vspx {
 struct vsp1_device *vsp1;
 struct vsp1_vspx_pipeline pipe;
};

/* Apply the given width, height and fourcc to the RWPF's subdevice */
static int vsp1_vspx_rwpf_set_subdev_fmt(struct vsp1_device *vsp1,
      struct vsp1_rwpf *rwpf,
      u32 isp_fourcc,
      unsigned int width,
      unsigned int height)
{
 struct vsp1_entity *ent = &rwpf->entity;
 struct v4l2_subdev_format format = {};
 u32 vspx_fourcc;

 switch (isp_fourcc) {
 case V4L2_PIX_FMT_GREY:
  /* 8 bit RAW Bayer image. */
  vspx_fourcc = V4L2_PIX_FMT_RGB332;
  break;
 case V4L2_PIX_FMT_Y10:
 case V4L2_PIX_FMT_Y12:
 case V4L2_PIX_FMT_Y16:
  /* 10, 12 and 16 bit RAW Bayer image. */
  vspx_fourcc = V4L2_PIX_FMT_RGB565;
  break;
 case V4L2_META_FMT_GENERIC_8:
  /* ConfigDMA parameters buffer. */
  vspx_fourcc = V4L2_PIX_FMT_XBGR32;
  break;
 default:
  return -EINVAL;
 }

 rwpf->fmtinfo = vsp1_get_format_info(vsp1, vspx_fourcc);

 format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
 format.pad = RWPF_PAD_SINK;
 format.format.width = width;
 format.format.height = height;
 format.format.field = V4L2_FIELD_NONE;
 format.format.code = rwpf->fmtinfo->mbus;

 return v4l2_subdev_call(&ent->subdev, pad, set_fmt, NULL, &format);
}

/* Configure the RPF->IIF->WPF pipeline for ConfigDMA or RAW image transfer. */
static int vsp1_vspx_pipeline_configure(struct vsp1_device *vsp1,
     dma_addr_t addr, u32 isp_fourcc,
     unsigned int width, unsigned int height,
     unsigned int stride,
     unsigned int iif_sink_pad,
     struct vsp1_dl_list *dl,
     struct vsp1_dl_body *dlb)
{
 struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
 struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
 struct vsp1_rwpf *rpf0 = pipe->inputs[0];
 int ret;

 ret = vsp1_vspx_rwpf_set_subdev_fmt(vsp1, rpf0, isp_fourcc, width,
         height);
 if (ret)
  return ret;

 ret = vsp1_vspx_rwpf_set_subdev_fmt(vsp1, pipe->output, isp_fourcc,
         width, height);
 if (ret)
  return ret;

 vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0], width, 0);
 rpf0->format.plane_fmt[0].bytesperline = stride;
 rpf0->format.num_planes = 1;
 rpf0->mem.addr[0] = addr;

 /*
 * Connect RPF0 to the IIF sink pad corresponding to the config or image
 * path.
 */

 rpf0->entity.sink_pad = iif_sink_pad;

 vsp1_entity_route_setup(&rpf0->entity, pipe, dlb);
 vsp1_entity_configure_stream(&rpf0->entity, rpf0->entity.state, pipe,
         dl, dlb);
 vsp1_entity_configure_partition(&rpf0->entity, pipe,
     &pipe->part_table[0], dl, dlb);

 return 0;
}

/* -----------------------------------------------------------------------------
 * Interrupt handling
 */


static void vsp1_vspx_pipeline_frame_end(struct vsp1_pipeline *pipe,
      unsigned int completion)
{
 struct vsp1_vspx_pipeline *vspx_pipe = to_vsp1_vspx_pipeline(pipe);

 scoped_guard(spinlock_irqsave, &pipe->irqlock) {
  /*
 * Operating the vsp1_pipe in singleshot mode requires to
 * manually set the pipeline state to stopped when a transfer
 * is completed.
 */

  pipe->state = VSP1_PIPELINE_STOPPED;
 }

 if (vspx_pipe->vspx_frame_end)
  vspx_pipe->vspx_frame_end(vspx_pipe->frame_end_data);
}

/* -----------------------------------------------------------------------------
 * ISP Driver API (include/media/vsp1.h)
 */


/**
 * vsp1_isp_init() - Initialize the VSPX
 * @dev: The VSP1 struct device
 *
 * Return: %0 on success or a negative error code on failure
 */

int vsp1_isp_init(struct device *dev)
{
 struct vsp1_device *vsp1 = dev_get_drvdata(dev);

 if (!vsp1)
  return -EPROBE_DEFER;

 return 0;
}
EXPORT_SYMBOL_GPL(vsp1_isp_init);

/**
 * vsp1_isp_get_bus_master - Get VSPX bus master
 * @dev: The VSP1 struct device
 *
 * The VSPX accesses memory through an FCPX instance. When allocating memory
 * buffers that will have to be accessed by the VSPX the 'struct device' of
 * the FCPX should be used. Use this function to get a reference to it.
 *
 * Return: a pointer to the bus master's device
 */

struct device *vsp1_isp_get_bus_master(struct device *dev)
{
 struct vsp1_device *vsp1 = dev_get_drvdata(dev);

 if (!vsp1)
  return ERR_PTR(-ENODEV);

 return vsp1->bus_master;
}
EXPORT_SYMBOL_GPL(vsp1_isp_get_bus_master);

/**
 * vsp1_isp_alloc_buffer - Allocate a buffer in the VSPX address space
 * @dev: The VSP1 struct device
 * @size: The size of the buffer to be allocated by the VSPX
 * @buffer_desc: The buffer descriptor. Will be filled with the buffer
 *  CPU-mapped address, the bus address and the size of the
 *  allocated buffer
 *
 * Allocate a buffer that will be later accessed by the VSPX. Buffers allocated
 * using vsp1_isp_alloc_buffer() shall be released with a call to
 * vsp1_isp_free_buffer(). This function is used by the ISP driver to allocate
 * memory for the ConfigDMA parameters buffer.
 *
 * Return: %0 on success or a negative error code on failure
 */

int vsp1_isp_alloc_buffer(struct device *dev, size_t size,
     struct vsp1_isp_buffer_desc *buffer_desc)
{
 struct device *bus_master = vsp1_isp_get_bus_master(dev);

 if (IS_ERR_OR_NULL(bus_master))
  return -ENODEV;

 buffer_desc->cpu_addr = dma_alloc_coherent(bus_master, size,
         &buffer_desc->dma_addr,
         GFP_KERNEL);
 if (!buffer_desc->cpu_addr)
  return -ENOMEM;

 buffer_desc->size = size;

 return 0;
}
EXPORT_SYMBOL_GPL(vsp1_isp_alloc_buffer);

/**
 * vsp1_isp_free_buffer - Release a buffer allocated by vsp1_isp_alloc_buffer()
 * @dev: The VSP1 struct device
 * @buffer_desc: The descriptor of the buffer to release as returned by
 *  vsp1_isp_alloc_buffer()
 *
 * Release memory in the VSPX address space allocated by
 * vsp1_isp_alloc_buffer().
 */

void vsp1_isp_free_buffer(struct device *dev,
     struct vsp1_isp_buffer_desc *buffer_desc)
{
 struct device *bus_master = vsp1_isp_get_bus_master(dev);

 if (IS_ERR_OR_NULL(bus_master))
  return;

 dma_free_coherent(bus_master, buffer_desc->size, buffer_desc->cpu_addr,
     buffer_desc->dma_addr);
}
EXPORT_SYMBOL_GPL(vsp1_isp_free_buffer);

/**
 * vsp1_isp_start_streaming - Start processing VSPX jobs
 * @dev: The VSP1 struct device
 * @frame_end: The frame end callback description
 *
 * Start the VSPX and prepare for accepting buffer transfer job requests.
 * The caller is responsible for tracking the started state of the VSPX.
 * Attempting to start an already started VSPX instance is an error.
 *
 * Return: %0 on success or a negative error code on failure
 */

int vsp1_isp_start_streaming(struct device *dev,
        struct vsp1_vspx_frame_end *frame_end)
{
 struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
 struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
 u32 value;
 int ret;

 if (!frame_end)
  return -EINVAL;

 guard(mutex)(&vspx_pipe->mutex);

 scoped_guard(spinlock_irq, &vspx_pipe->lock) {
  if (vspx_pipe->enabled)
   return -EBUSY;
 }

 vspx_pipe->vspx_frame_end = frame_end->vspx_frame_end;
 vspx_pipe->frame_end_data = frame_end->frame_end_data;

 /* Enable the VSP1 and prepare for streaming. */
 vsp1_pipeline_dump(pipe, "VSPX job");

 ret = vsp1_device_get(vsp1);
 if (ret < 0)
  return ret;

 /*
 * Make sure VSPX is not active. This should never happen in normal
 * usage
 */

 value = vsp1_read(vsp1, VI6_CMD(0));
 if (value & VI6_CMD_STRCMD) {
  dev_err(vsp1->dev,
   "%s: Starting of WPF0 already reserved\n", __func__);
  ret = -EBUSY;
  goto error_put;
 }

 value = vsp1_read(vsp1, VI6_STATUS);
 if (value & VI6_STATUS_SYS_ACT(0)) {
  dev_err(vsp1->dev,
   "%s: WPF0 has not entered idle state\n", __func__);
  ret = -EBUSY;
  goto error_put;
 }

 scoped_guard(spinlock_irq, &vspx_pipe->lock) {
  vspx_pipe->enabled = true;
 }

 return 0;

error_put:
 vsp1_device_put(vsp1);
 return ret;
}
EXPORT_SYMBOL_GPL(vsp1_isp_start_streaming);

/**
 * vsp1_isp_stop_streaming - Stop the VSPX
 * @dev: The VSP1 struct device
 *
 * Stop the VSPX operation by stopping the vsp1 pipeline and waiting for the
 * last frame in transfer, if any, to complete.
 *
 * The caller is responsible for tracking the stopped state of the VSPX.
 * Attempting to stop an already stopped VSPX instance is a nop.
 */

void vsp1_isp_stop_streaming(struct device *dev)
{
 struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
 struct vsp1_pipeline *pipe = &vspx_pipe->pipe;

 guard(mutex)(&vspx_pipe->mutex);

 scoped_guard(spinlock_irq, &vspx_pipe->lock) {
  if (!vspx_pipe->enabled)
   return;

  vspx_pipe->enabled = false;
 }

 WARN_ON_ONCE(vsp1_pipeline_stop(pipe));

 vspx_pipe->vspx_frame_end = NULL;
 vsp1_dlm_reset(pipe->output->dlm);
 vsp1_device_put(vsp1);
}
EXPORT_SYMBOL_GPL(vsp1_isp_stop_streaming);

/**
 * vsp1_isp_job_prepare - Prepare a new buffer transfer job
 * @dev: The VSP1 struct device
 * @job: The job description
 *
 * Prepare a new buffer transfer job by populating a display list that will be
 * later executed by a call to vsp1_isp_job_run(). All pending jobs must be
 * released after stopping the streaming operations with a call to
 * vsp1_isp_job_release().
 *
 * In order for the VSPX to accept new jobs to prepare the VSPX must have been
 * started.
 *
 * Return: %0 on success or a negative error code on failure
 */

int vsp1_isp_job_prepare(struct device *dev, struct vsp1_isp_job_desc *job)
{
 struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
 struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
 const struct v4l2_pix_format_mplane *pix_mp;
 struct vsp1_dl_list *second_dl = NULL;
 struct vsp1_dl_body *dlb;
 struct vsp1_dl_list *dl;
 int ret;

 /*
 * Transfer the buffers described in the job: an optional ConfigDMA
 * parameters buffer and a RAW image.
 */


 job->dl = vsp1_dl_list_get(pipe->output->dlm);
 if (!job->dl)
  return -ENOMEM;

 dl = job->dl;
 dlb = vsp1_dl_list_get_body0(dl);

 /* Configure IIF routing and enable IIF function. */
 vsp1_entity_route_setup(pipe->iif, pipe, dlb);
 vsp1_entity_configure_stream(pipe->iif, pipe->iif->state, pipe,
         dl, dlb);

 /* Configure WPF0 to enable RPF0 as source. */
 vsp1_entity_route_setup(&pipe->output->entity, pipe, dlb);
 vsp1_entity_configure_stream(&pipe->output->entity,
         pipe->output->entity.state, pipe,
         dl, dlb);

 if (job->config.pairs) {
  /*
 * Writing less than 17 pairs corrupts the output images ( < 16
 * pairs) or freezes the VSPX operations (= 16 pairs). Only
 * allow more than 16 pairs to be written.
 */

  if (job->config.pairs <= 16) {
   ret = -EINVAL;
   goto error_put_dl;
  }

  /*
 * Configure RPF0 for ConfigDMA data. Transfer the number of
 * configuration pairs plus 2 words for the header.
 */

  ret = vsp1_vspx_pipeline_configure(vsp1, job->config.mem,
         V4L2_META_FMT_GENERIC_8,
         job->config.pairs * 2 + 2, 1,
         job->config.pairs * 2 + 2,
         VSPX_IIF_SINK_PAD_CONFIG,
         dl, dlb);
  if (ret)
   goto error_put_dl;

  second_dl = vsp1_dl_list_get(pipe->output->dlm);
  if (!second_dl) {
   ret = -ENOMEM;
   goto error_put_dl;
  }

  dl = second_dl;
  dlb = vsp1_dl_list_get_body0(dl);
 }

 /* Configure RPF0 for RAW image transfer. */
 pix_mp = &job->img.fmt;
 ret = vsp1_vspx_pipeline_configure(vsp1, job->img.mem,
        pix_mp->pixelformat,
        pix_mp->width, pix_mp->height,
        pix_mp->plane_fmt[0].bytesperline,
        VSPX_IIF_SINK_PAD_IMG, dl, dlb);
 if (ret)
  goto error_put_dl;

 if (second_dl)
  vsp1_dl_list_add_chain(job->dl, second_dl);

 return 0;

error_put_dl:
 if (second_dl)
  vsp1_dl_list_put(second_dl);
 vsp1_dl_list_put(job->dl);
 job->dl = NULL;
 return ret;
}
EXPORT_SYMBOL_GPL(vsp1_isp_job_prepare);

/**
 * vsp1_isp_job_run - Run a buffer transfer job
 * @dev: The VSP1 struct device
 * @job: The job to be run
 *
 * Run the display list contained in the job description provided by the caller.
 * The job must have been prepared with a call to vsp1_isp_job_prepare() and
 * the job's display list shall be valid.
 *
 * Jobs can be run only on VSPX instances which have been started. Requests
 * to run a job after the VSPX has been stopped return -EINVAL and the job
 * resources shall be released by the caller with vsp1_isp_job_release().
 * When a job is run successfully all the resources acquired by
 * vsp1_isp_job_prepare() are released by this function and no further action
 * is required to the caller.
 *
 * Return: %0 on success or a negative error code on failure
 */

int vsp1_isp_job_run(struct device *dev, struct vsp1_isp_job_desc *job)
{
 struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
 struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
 u32 value;

 /* Make sure VSPX is not busy processing a frame. */
 value = vsp1_read(vsp1, VI6_CMD(0));
 if (value) {
  dev_err(vsp1->dev,
   "%s: Starting of WPF0 already reserved\n", __func__);
  return -EBUSY;
 }

 scoped_guard(spinlock_irqsave, &vspx_pipe->lock) {
  /*
 * If a new job is scheduled when the VSPX is stopped, do not
 * run it.
 */

  if (!vspx_pipe->enabled)
   return -EINVAL;

  vsp1_dl_list_commit(job->dl, 0);

  /*
 * The display list is now under control of the display list
 * manager and will be released automatically when the job
 * completes.
 */

  job->dl = NULL;
 }

 scoped_guard(spinlock_irqsave, &pipe->irqlock) {
  vsp1_pipeline_run(pipe);
 }

 return 0;
}
EXPORT_SYMBOL_GPL(vsp1_isp_job_run);

/**
 * vsp1_isp_job_release - Release a non processed transfer job
 * @dev: The VSP1 struct device
 * @job: The job to release
 *
 * Release a job prepared by a call to vsp1_isp_job_prepare() and not yet
 * run. All pending jobs shall be released after streaming has been stopped.
 */

void vsp1_isp_job_release(struct device *dev,
     struct vsp1_isp_job_desc *job)
{
 vsp1_dl_list_put(job->dl);
}
EXPORT_SYMBOL_GPL(vsp1_isp_job_release);

/* -----------------------------------------------------------------------------
 * Initialization and cleanup
 */


int vsp1_vspx_init(struct vsp1_device *vsp1)
{
 struct vsp1_vspx_pipeline *vspx_pipe;
 struct vsp1_pipeline *pipe;

 vsp1->vspx = devm_kzalloc(vsp1->dev, sizeof(*vsp1->vspx), GFP_KERNEL);
 if (!vsp1->vspx)
  return -ENOMEM;

 vsp1->vspx->vsp1 = vsp1;

 vspx_pipe = &vsp1->vspx->pipe;
 vspx_pipe->enabled = false;

 pipe = &vspx_pipe->pipe;

 vsp1_pipeline_init(pipe);

 pipe->partitions = 1;
 pipe->part_table = &vspx_pipe->partition;
 pipe->interlaced = false;
 pipe->frame_end = vsp1_vspx_pipeline_frame_end;

 mutex_init(&vspx_pipe->mutex);
 spin_lock_init(&vspx_pipe->lock);

 /*
 * Initialize RPF0 as input for VSPX and use it unconditionally for
 * now.
 */

 pipe->inputs[0] = vsp1->rpf[0];
 pipe->inputs[0]->entity.pipe = pipe;
 pipe->inputs[0]->entity.sink = &vsp1->iif->entity;
 list_add_tail(&pipe->inputs[0]->entity.list_pipe, &pipe->entities);

 pipe->iif = &vsp1->iif->entity;
 pipe->iif->pipe = pipe;
 pipe->iif->sink = &vsp1->wpf[0]->entity;
 pipe->iif->sink_pad = RWPF_PAD_SINK;
 list_add_tail(&pipe->iif->list_pipe, &pipe->entities);

 pipe->output = vsp1->wpf[0];
 pipe->output->entity.pipe = pipe;
 list_add_tail(&pipe->output->entity.list_pipe, &pipe->entities);

 return 0;
}

void vsp1_vspx_cleanup(struct vsp1_device *vsp1)
{
 struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;

 mutex_destroy(&vspx_pipe->mutex);
}

Messung V0.5
C=95 H=94 G=94

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.