162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Xilinx Video DMA 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013-2015 Ideas on Board 662306a36Sopenharmony_ci * Copyright (C) 2013-2015 Xilinx, Inc. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> 962306a36Sopenharmony_ci * Laurent Pinchart <laurent.pinchart@ideasonboard.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/dma/xilinx_dma.h> 1362306a36Sopenharmony_ci#include <linux/lcm.h> 1462306a36Sopenharmony_ci#include <linux/list.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <media/v4l2-dev.h> 2062306a36Sopenharmony_ci#include <media/v4l2-fh.h> 2162306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 2262306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h> 2362306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "xilinx-dma.h" 2662306a36Sopenharmony_ci#include "xilinx-vip.h" 2762306a36Sopenharmony_ci#include "xilinx-vipp.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define XVIP_DMA_DEF_WIDTH 1920 3062306a36Sopenharmony_ci#define XVIP_DMA_DEF_HEIGHT 1080 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Minimum and maximum widths are expressed in bytes */ 3362306a36Sopenharmony_ci#define XVIP_DMA_MIN_WIDTH 1U 3462306a36Sopenharmony_ci#define XVIP_DMA_MAX_WIDTH 65535U 3562306a36Sopenharmony_ci#define XVIP_DMA_MIN_HEIGHT 1U 3662306a36Sopenharmony_ci#define XVIP_DMA_MAX_HEIGHT 8191U 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 3962306a36Sopenharmony_ci * Helper functions 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic struct v4l2_subdev * 4362306a36Sopenharmony_cixvip_dma_remote_subdev(struct media_pad *local, u32 *pad) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct media_pad *remote; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci remote = media_pad_remote_pad_first(local); 4862306a36Sopenharmony_ci if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) 4962306a36Sopenharmony_ci return NULL; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (pad) 5262306a36Sopenharmony_ci *pad = remote->index; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return media_entity_to_v4l2_subdev(remote->entity); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int xvip_dma_verify_format(struct xvip_dma *dma) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct v4l2_subdev_format fmt = { 6062306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 6162306a36Sopenharmony_ci }; 6262306a36Sopenharmony_ci struct v4l2_subdev *subdev; 6362306a36Sopenharmony_ci int ret; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci subdev = xvip_dma_remote_subdev(&dma->pad, &fmt.pad); 6662306a36Sopenharmony_ci if (subdev == NULL) 6762306a36Sopenharmony_ci return -EPIPE; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); 7062306a36Sopenharmony_ci if (ret < 0) 7162306a36Sopenharmony_ci return ret == -ENOIOCTLCMD ? -EINVAL : ret; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (dma->fmtinfo->code != fmt.format.code || 7462306a36Sopenharmony_ci dma->format.height != fmt.format.height || 7562306a36Sopenharmony_ci dma->format.width != fmt.format.width || 7662306a36Sopenharmony_ci dma->format.colorspace != fmt.format.colorspace) 7762306a36Sopenharmony_ci return -EINVAL; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 8362306a36Sopenharmony_ci * Pipeline Stream Management 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/** 8762306a36Sopenharmony_ci * xvip_pipeline_start_stop - Start ot stop streaming on a pipeline 8862306a36Sopenharmony_ci * @pipe: The pipeline 8962306a36Sopenharmony_ci * @start: Start (when true) or stop (when false) the pipeline 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * Walk the entities chain starting at the pipeline output video node and start 9262306a36Sopenharmony_ci * or stop all of them. 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * Return: 0 if successful, or the return value of the failed video::s_stream 9562306a36Sopenharmony_ci * operation otherwise. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_cistatic int xvip_pipeline_start_stop(struct xvip_pipeline *pipe, bool start) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct xvip_dma *dma = pipe->output; 10062306a36Sopenharmony_ci struct media_entity *entity; 10162306a36Sopenharmony_ci struct media_pad *pad; 10262306a36Sopenharmony_ci struct v4l2_subdev *subdev; 10362306a36Sopenharmony_ci int ret; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci entity = &dma->video.entity; 10662306a36Sopenharmony_ci while (1) { 10762306a36Sopenharmony_ci pad = &entity->pads[0]; 10862306a36Sopenharmony_ci if (!(pad->flags & MEDIA_PAD_FL_SINK)) 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci pad = media_pad_remote_pad_first(pad); 11262306a36Sopenharmony_ci if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci entity = pad->entity; 11662306a36Sopenharmony_ci subdev = media_entity_to_v4l2_subdev(entity); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci ret = v4l2_subdev_call(subdev, video, s_stream, start); 11962306a36Sopenharmony_ci if (start && ret < 0 && ret != -ENOIOCTLCMD) 12062306a36Sopenharmony_ci return ret; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/** 12762306a36Sopenharmony_ci * xvip_pipeline_set_stream - Enable/disable streaming on a pipeline 12862306a36Sopenharmony_ci * @pipe: The pipeline 12962306a36Sopenharmony_ci * @on: Turn the stream on when true or off when false 13062306a36Sopenharmony_ci * 13162306a36Sopenharmony_ci * The pipeline is shared between all DMA engines connect at its input and 13262306a36Sopenharmony_ci * output. While the stream state of DMA engines can be controlled 13362306a36Sopenharmony_ci * independently, pipelines have a shared stream state that enable or disable 13462306a36Sopenharmony_ci * all entities in the pipeline. For this reason the pipeline uses a streaming 13562306a36Sopenharmony_ci * counter that tracks the number of DMA engines that have requested the stream 13662306a36Sopenharmony_ci * to be enabled. 13762306a36Sopenharmony_ci * 13862306a36Sopenharmony_ci * When called with the @on argument set to true, this function will increment 13962306a36Sopenharmony_ci * the pipeline streaming count. If the streaming count reaches the number of 14062306a36Sopenharmony_ci * DMA engines in the pipeline it will enable all entities that belong to the 14162306a36Sopenharmony_ci * pipeline. 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * Similarly, when called with the @on argument set to false, this function will 14462306a36Sopenharmony_ci * decrement the pipeline streaming count and disable all entities in the 14562306a36Sopenharmony_ci * pipeline when the streaming count reaches zero. 14662306a36Sopenharmony_ci * 14762306a36Sopenharmony_ci * Return: 0 if successful, or the return value of the failed video::s_stream 14862306a36Sopenharmony_ci * operation otherwise. Stopping the pipeline never fails. The pipeline state is 14962306a36Sopenharmony_ci * not updated when the operation fails. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_cistatic int xvip_pipeline_set_stream(struct xvip_pipeline *pipe, bool on) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci int ret = 0; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci mutex_lock(&pipe->lock); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (on) { 15862306a36Sopenharmony_ci if (pipe->stream_count == pipe->num_dmas - 1) { 15962306a36Sopenharmony_ci ret = xvip_pipeline_start_stop(pipe, true); 16062306a36Sopenharmony_ci if (ret < 0) 16162306a36Sopenharmony_ci goto done; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci pipe->stream_count++; 16462306a36Sopenharmony_ci } else { 16562306a36Sopenharmony_ci if (--pipe->stream_count == 0) 16662306a36Sopenharmony_ci xvip_pipeline_start_stop(pipe, false); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cidone: 17062306a36Sopenharmony_ci mutex_unlock(&pipe->lock); 17162306a36Sopenharmony_ci return ret; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int xvip_pipeline_validate(struct xvip_pipeline *pipe, 17562306a36Sopenharmony_ci struct xvip_dma *start) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct media_pipeline_pad_iter iter; 17862306a36Sopenharmony_ci unsigned int num_inputs = 0; 17962306a36Sopenharmony_ci unsigned int num_outputs = 0; 18062306a36Sopenharmony_ci struct media_pad *pad; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* Locate the video nodes in the pipeline. */ 18362306a36Sopenharmony_ci media_pipeline_for_each_pad(&pipe->pipe, &iter, pad) { 18462306a36Sopenharmony_ci struct xvip_dma *dma; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (pad->entity->function != MEDIA_ENT_F_IO_V4L) 18762306a36Sopenharmony_ci continue; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci dma = to_xvip_dma(media_entity_to_video_device(pad->entity)); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (dma->pad.flags & MEDIA_PAD_FL_SINK) { 19262306a36Sopenharmony_ci pipe->output = dma; 19362306a36Sopenharmony_ci num_outputs++; 19462306a36Sopenharmony_ci } else { 19562306a36Sopenharmony_ci num_inputs++; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* We need exactly one output and zero or one input. */ 20062306a36Sopenharmony_ci if (num_outputs != 1 || num_inputs > 1) 20162306a36Sopenharmony_ci return -EPIPE; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci pipe->num_dmas = num_inputs + num_outputs; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void __xvip_pipeline_cleanup(struct xvip_pipeline *pipe) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci pipe->num_dmas = 0; 21162306a36Sopenharmony_ci pipe->output = NULL; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/** 21562306a36Sopenharmony_ci * xvip_pipeline_cleanup - Cleanup the pipeline after streaming 21662306a36Sopenharmony_ci * @pipe: the pipeline 21762306a36Sopenharmony_ci * 21862306a36Sopenharmony_ci * Decrease the pipeline use count and clean it up if we were the last user. 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_cistatic void xvip_pipeline_cleanup(struct xvip_pipeline *pipe) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci mutex_lock(&pipe->lock); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* If we're the last user clean up the pipeline. */ 22562306a36Sopenharmony_ci if (--pipe->use_count == 0) 22662306a36Sopenharmony_ci __xvip_pipeline_cleanup(pipe); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci mutex_unlock(&pipe->lock); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/** 23262306a36Sopenharmony_ci * xvip_pipeline_prepare - Prepare the pipeline for streaming 23362306a36Sopenharmony_ci * @pipe: the pipeline 23462306a36Sopenharmony_ci * @dma: DMA engine at one end of the pipeline 23562306a36Sopenharmony_ci * 23662306a36Sopenharmony_ci * Validate the pipeline if no user exists yet, otherwise just increase the use 23762306a36Sopenharmony_ci * count. 23862306a36Sopenharmony_ci * 23962306a36Sopenharmony_ci * Return: 0 if successful or -EPIPE if the pipeline is not valid. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_cistatic int xvip_pipeline_prepare(struct xvip_pipeline *pipe, 24262306a36Sopenharmony_ci struct xvip_dma *dma) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci int ret; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci mutex_lock(&pipe->lock); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* If we're the first user validate and initialize the pipeline. */ 24962306a36Sopenharmony_ci if (pipe->use_count == 0) { 25062306a36Sopenharmony_ci ret = xvip_pipeline_validate(pipe, dma); 25162306a36Sopenharmony_ci if (ret < 0) { 25262306a36Sopenharmony_ci __xvip_pipeline_cleanup(pipe); 25362306a36Sopenharmony_ci goto done; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci pipe->use_count++; 25862306a36Sopenharmony_ci ret = 0; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cidone: 26162306a36Sopenharmony_ci mutex_unlock(&pipe->lock); 26262306a36Sopenharmony_ci return ret; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 26662306a36Sopenharmony_ci * videobuf2 queue operations 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/** 27062306a36Sopenharmony_ci * struct xvip_dma_buffer - Video DMA buffer 27162306a36Sopenharmony_ci * @buf: vb2 buffer base object 27262306a36Sopenharmony_ci * @queue: buffer list entry in the DMA engine queued buffers list 27362306a36Sopenharmony_ci * @dma: DMA channel that uses the buffer 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_cistruct xvip_dma_buffer { 27662306a36Sopenharmony_ci struct vb2_v4l2_buffer buf; 27762306a36Sopenharmony_ci struct list_head queue; 27862306a36Sopenharmony_ci struct xvip_dma *dma; 27962306a36Sopenharmony_ci}; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci#define to_xvip_dma_buffer(vb) container_of(vb, struct xvip_dma_buffer, buf) 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic void xvip_dma_complete(void *param) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct xvip_dma_buffer *buf = param; 28662306a36Sopenharmony_ci struct xvip_dma *dma = buf->dma; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci spin_lock(&dma->queued_lock); 28962306a36Sopenharmony_ci list_del(&buf->queue); 29062306a36Sopenharmony_ci spin_unlock(&dma->queued_lock); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci buf->buf.field = V4L2_FIELD_NONE; 29362306a36Sopenharmony_ci buf->buf.sequence = dma->sequence++; 29462306a36Sopenharmony_ci buf->buf.vb2_buf.timestamp = ktime_get_ns(); 29562306a36Sopenharmony_ci vb2_set_plane_payload(&buf->buf.vb2_buf, 0, dma->format.sizeimage); 29662306a36Sopenharmony_ci vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int 30062306a36Sopenharmony_cixvip_dma_queue_setup(struct vb2_queue *vq, 30162306a36Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 30262306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct xvip_dma *dma = vb2_get_drv_priv(vq); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* Make sure the image size is large enough. */ 30762306a36Sopenharmony_ci if (*nplanes) 30862306a36Sopenharmony_ci return sizes[0] < dma->format.sizeimage ? -EINVAL : 0; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci *nplanes = 1; 31162306a36Sopenharmony_ci sizes[0] = dma->format.sizeimage; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int xvip_dma_buffer_prepare(struct vb2_buffer *vb) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 31962306a36Sopenharmony_ci struct xvip_dma *dma = vb2_get_drv_priv(vb->vb2_queue); 32062306a36Sopenharmony_ci struct xvip_dma_buffer *buf = to_xvip_dma_buffer(vbuf); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci buf->dma = dma; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return 0; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic void xvip_dma_buffer_queue(struct vb2_buffer *vb) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 33062306a36Sopenharmony_ci struct xvip_dma *dma = vb2_get_drv_priv(vb->vb2_queue); 33162306a36Sopenharmony_ci struct xvip_dma_buffer *buf = to_xvip_dma_buffer(vbuf); 33262306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc; 33362306a36Sopenharmony_ci dma_addr_t addr = vb2_dma_contig_plane_dma_addr(vb, 0); 33462306a36Sopenharmony_ci u32 flags; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { 33762306a36Sopenharmony_ci flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; 33862306a36Sopenharmony_ci dma->xt.dir = DMA_DEV_TO_MEM; 33962306a36Sopenharmony_ci dma->xt.src_sgl = false; 34062306a36Sopenharmony_ci dma->xt.dst_sgl = true; 34162306a36Sopenharmony_ci dma->xt.dst_start = addr; 34262306a36Sopenharmony_ci } else { 34362306a36Sopenharmony_ci flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; 34462306a36Sopenharmony_ci dma->xt.dir = DMA_MEM_TO_DEV; 34562306a36Sopenharmony_ci dma->xt.src_sgl = true; 34662306a36Sopenharmony_ci dma->xt.dst_sgl = false; 34762306a36Sopenharmony_ci dma->xt.src_start = addr; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci dma->xt.frame_size = 1; 35162306a36Sopenharmony_ci dma->sgl[0].size = dma->format.width * dma->fmtinfo->bpp; 35262306a36Sopenharmony_ci dma->sgl[0].icg = dma->format.bytesperline - dma->sgl[0].size; 35362306a36Sopenharmony_ci dma->xt.numf = dma->format.height; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci desc = dmaengine_prep_interleaved_dma(dma->dma, &dma->xt, flags); 35662306a36Sopenharmony_ci if (!desc) { 35762306a36Sopenharmony_ci dev_err(dma->xdev->dev, "Failed to prepare DMA transfer\n"); 35862306a36Sopenharmony_ci vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_ERROR); 35962306a36Sopenharmony_ci return; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci desc->callback = xvip_dma_complete; 36262306a36Sopenharmony_ci desc->callback_param = buf; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci spin_lock_irq(&dma->queued_lock); 36562306a36Sopenharmony_ci list_add_tail(&buf->queue, &dma->queued_bufs); 36662306a36Sopenharmony_ci spin_unlock_irq(&dma->queued_lock); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci dmaengine_submit(desc); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (vb2_is_streaming(&dma->queue)) 37162306a36Sopenharmony_ci dma_async_issue_pending(dma->dma); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int xvip_dma_start_streaming(struct vb2_queue *vq, unsigned int count) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct xvip_dma *dma = vb2_get_drv_priv(vq); 37762306a36Sopenharmony_ci struct xvip_dma_buffer *buf, *nbuf; 37862306a36Sopenharmony_ci struct xvip_pipeline *pipe; 37962306a36Sopenharmony_ci int ret; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci dma->sequence = 0; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* 38462306a36Sopenharmony_ci * Start streaming on the pipeline. No link touching an entity in the 38562306a36Sopenharmony_ci * pipeline can be activated or deactivated once streaming is started. 38662306a36Sopenharmony_ci * 38762306a36Sopenharmony_ci * Use the pipeline object embedded in the first DMA object that starts 38862306a36Sopenharmony_ci * streaming. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci pipe = to_xvip_pipeline(&dma->video) ? : &dma->pipe; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ret = video_device_pipeline_start(&dma->video, &pipe->pipe); 39362306a36Sopenharmony_ci if (ret < 0) 39462306a36Sopenharmony_ci goto error; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* Verify that the configured format matches the output of the 39762306a36Sopenharmony_ci * connected subdev. 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_ci ret = xvip_dma_verify_format(dma); 40062306a36Sopenharmony_ci if (ret < 0) 40162306a36Sopenharmony_ci goto error_stop; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci ret = xvip_pipeline_prepare(pipe, dma); 40462306a36Sopenharmony_ci if (ret < 0) 40562306a36Sopenharmony_ci goto error_stop; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* Start the DMA engine. This must be done before starting the blocks 40862306a36Sopenharmony_ci * in the pipeline to avoid DMA synchronization issues. 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_ci dma_async_issue_pending(dma->dma); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Start the pipeline. */ 41362306a36Sopenharmony_ci xvip_pipeline_set_stream(pipe, true); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cierror_stop: 41862306a36Sopenharmony_ci video_device_pipeline_stop(&dma->video); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cierror: 42162306a36Sopenharmony_ci /* Give back all queued buffers to videobuf2. */ 42262306a36Sopenharmony_ci spin_lock_irq(&dma->queued_lock); 42362306a36Sopenharmony_ci list_for_each_entry_safe(buf, nbuf, &dma->queued_bufs, queue) { 42462306a36Sopenharmony_ci vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_QUEUED); 42562306a36Sopenharmony_ci list_del(&buf->queue); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci spin_unlock_irq(&dma->queued_lock); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return ret; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic void xvip_dma_stop_streaming(struct vb2_queue *vq) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct xvip_dma *dma = vb2_get_drv_priv(vq); 43562306a36Sopenharmony_ci struct xvip_pipeline *pipe = to_xvip_pipeline(&dma->video); 43662306a36Sopenharmony_ci struct xvip_dma_buffer *buf, *nbuf; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* Stop the pipeline. */ 43962306a36Sopenharmony_ci xvip_pipeline_set_stream(pipe, false); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* Stop and reset the DMA engine. */ 44262306a36Sopenharmony_ci dmaengine_terminate_all(dma->dma); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* Cleanup the pipeline and mark it as being stopped. */ 44562306a36Sopenharmony_ci xvip_pipeline_cleanup(pipe); 44662306a36Sopenharmony_ci video_device_pipeline_stop(&dma->video); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* Give back all queued buffers to videobuf2. */ 44962306a36Sopenharmony_ci spin_lock_irq(&dma->queued_lock); 45062306a36Sopenharmony_ci list_for_each_entry_safe(buf, nbuf, &dma->queued_bufs, queue) { 45162306a36Sopenharmony_ci vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_ERROR); 45262306a36Sopenharmony_ci list_del(&buf->queue); 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci spin_unlock_irq(&dma->queued_lock); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic const struct vb2_ops xvip_dma_queue_qops = { 45862306a36Sopenharmony_ci .queue_setup = xvip_dma_queue_setup, 45962306a36Sopenharmony_ci .buf_prepare = xvip_dma_buffer_prepare, 46062306a36Sopenharmony_ci .buf_queue = xvip_dma_buffer_queue, 46162306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 46262306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 46362306a36Sopenharmony_ci .start_streaming = xvip_dma_start_streaming, 46462306a36Sopenharmony_ci .stop_streaming = xvip_dma_stop_streaming, 46562306a36Sopenharmony_ci}; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 46862306a36Sopenharmony_ci * V4L2 ioctls 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic int 47262306a36Sopenharmony_cixvip_dma_querycap(struct file *file, void *fh, struct v4l2_capability *cap) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci struct v4l2_fh *vfh = file->private_data; 47562306a36Sopenharmony_ci struct xvip_dma *dma = to_xvip_dma(vfh->vdev); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci cap->capabilities = dma->xdev->v4l2_caps | V4L2_CAP_STREAMING | 47862306a36Sopenharmony_ci V4L2_CAP_DEVICE_CAPS; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci strscpy(cap->driver, "xilinx-vipp", sizeof(cap->driver)); 48162306a36Sopenharmony_ci strscpy(cap->card, dma->video.name, sizeof(cap->card)); 48262306a36Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%pOFn:%u", 48362306a36Sopenharmony_ci dma->xdev->dev->of_node, dma->port); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return 0; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci/* FIXME: without this callback function, some applications are not configured 48962306a36Sopenharmony_ci * with correct formats, and it results in frames in wrong format. Whether this 49062306a36Sopenharmony_ci * callback needs to be required is not clearly defined, so it should be 49162306a36Sopenharmony_ci * clarified through the mailing list. 49262306a36Sopenharmony_ci */ 49362306a36Sopenharmony_cistatic int 49462306a36Sopenharmony_cixvip_dma_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct v4l2_fh *vfh = file->private_data; 49762306a36Sopenharmony_ci struct xvip_dma *dma = to_xvip_dma(vfh->vdev); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (f->index > 0) 50062306a36Sopenharmony_ci return -EINVAL; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci f->pixelformat = dma->format.pixelformat; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci return 0; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic int 50862306a36Sopenharmony_cixvip_dma_get_format(struct file *file, void *fh, struct v4l2_format *format) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct v4l2_fh *vfh = file->private_data; 51162306a36Sopenharmony_ci struct xvip_dma *dma = to_xvip_dma(vfh->vdev); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci format->fmt.pix = dma->format; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic void 51962306a36Sopenharmony_ci__xvip_dma_try_format(struct xvip_dma *dma, struct v4l2_pix_format *pix, 52062306a36Sopenharmony_ci const struct xvip_video_format **fmtinfo) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci const struct xvip_video_format *info; 52362306a36Sopenharmony_ci unsigned int min_width; 52462306a36Sopenharmony_ci unsigned int max_width; 52562306a36Sopenharmony_ci unsigned int min_bpl; 52662306a36Sopenharmony_ci unsigned int max_bpl; 52762306a36Sopenharmony_ci unsigned int width; 52862306a36Sopenharmony_ci unsigned int align; 52962306a36Sopenharmony_ci unsigned int bpl; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* Retrieve format information and select the default format if the 53262306a36Sopenharmony_ci * requested format isn't supported. 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ci info = xvip_get_format_by_fourcc(pix->pixelformat); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci pix->pixelformat = info->fourcc; 53762306a36Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* The transfer alignment requirements are expressed in bytes. Compute 54062306a36Sopenharmony_ci * the minimum and maximum values, clamp the requested width and convert 54162306a36Sopenharmony_ci * it back to pixels. 54262306a36Sopenharmony_ci */ 54362306a36Sopenharmony_ci align = lcm(dma->align, info->bpp); 54462306a36Sopenharmony_ci min_width = roundup(XVIP_DMA_MIN_WIDTH, align); 54562306a36Sopenharmony_ci max_width = rounddown(XVIP_DMA_MAX_WIDTH, align); 54662306a36Sopenharmony_ci width = rounddown(pix->width * info->bpp, align); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci pix->width = clamp(width, min_width, max_width) / info->bpp; 54962306a36Sopenharmony_ci pix->height = clamp(pix->height, XVIP_DMA_MIN_HEIGHT, 55062306a36Sopenharmony_ci XVIP_DMA_MAX_HEIGHT); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* Clamp the requested bytes per line value. If the maximum bytes per 55362306a36Sopenharmony_ci * line value is zero, the module doesn't support user configurable line 55462306a36Sopenharmony_ci * sizes. Override the requested value with the minimum in that case. 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci min_bpl = pix->width * info->bpp; 55762306a36Sopenharmony_ci max_bpl = rounddown(XVIP_DMA_MAX_WIDTH, dma->align); 55862306a36Sopenharmony_ci bpl = rounddown(pix->bytesperline, dma->align); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci pix->bytesperline = clamp(bpl, min_bpl, max_bpl); 56162306a36Sopenharmony_ci pix->sizeimage = pix->bytesperline * pix->height; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (fmtinfo) 56462306a36Sopenharmony_ci *fmtinfo = info; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int 56862306a36Sopenharmony_cixvip_dma_try_format(struct file *file, void *fh, struct v4l2_format *format) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct v4l2_fh *vfh = file->private_data; 57162306a36Sopenharmony_ci struct xvip_dma *dma = to_xvip_dma(vfh->vdev); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci __xvip_dma_try_format(dma, &format->fmt.pix, NULL); 57462306a36Sopenharmony_ci return 0; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic int 57862306a36Sopenharmony_cixvip_dma_set_format(struct file *file, void *fh, struct v4l2_format *format) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct v4l2_fh *vfh = file->private_data; 58162306a36Sopenharmony_ci struct xvip_dma *dma = to_xvip_dma(vfh->vdev); 58262306a36Sopenharmony_ci const struct xvip_video_format *info; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci __xvip_dma_try_format(dma, &format->fmt.pix, &info); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (vb2_is_busy(&dma->queue)) 58762306a36Sopenharmony_ci return -EBUSY; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci dma->format = format->fmt.pix; 59062306a36Sopenharmony_ci dma->fmtinfo = info; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return 0; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops xvip_dma_ioctl_ops = { 59662306a36Sopenharmony_ci .vidioc_querycap = xvip_dma_querycap, 59762306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = xvip_dma_enum_format, 59862306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = xvip_dma_get_format, 59962306a36Sopenharmony_ci .vidioc_g_fmt_vid_out = xvip_dma_get_format, 60062306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = xvip_dma_set_format, 60162306a36Sopenharmony_ci .vidioc_s_fmt_vid_out = xvip_dma_set_format, 60262306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = xvip_dma_try_format, 60362306a36Sopenharmony_ci .vidioc_try_fmt_vid_out = xvip_dma_try_format, 60462306a36Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 60562306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 60662306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 60762306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 60862306a36Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 60962306a36Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 61062306a36Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 61162306a36Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 61262306a36Sopenharmony_ci}; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 61562306a36Sopenharmony_ci * V4L2 file operations 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic const struct v4l2_file_operations xvip_dma_fops = { 61962306a36Sopenharmony_ci .owner = THIS_MODULE, 62062306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 62162306a36Sopenharmony_ci .open = v4l2_fh_open, 62262306a36Sopenharmony_ci .release = vb2_fop_release, 62362306a36Sopenharmony_ci .poll = vb2_fop_poll, 62462306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 62562306a36Sopenharmony_ci}; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 62862306a36Sopenharmony_ci * Xilinx Video DMA Core 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ciint xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, 63262306a36Sopenharmony_ci enum v4l2_buf_type type, unsigned int port) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci char name[16]; 63562306a36Sopenharmony_ci int ret; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci dma->xdev = xdev; 63862306a36Sopenharmony_ci dma->port = port; 63962306a36Sopenharmony_ci mutex_init(&dma->lock); 64062306a36Sopenharmony_ci mutex_init(&dma->pipe.lock); 64162306a36Sopenharmony_ci INIT_LIST_HEAD(&dma->queued_bufs); 64262306a36Sopenharmony_ci spin_lock_init(&dma->queued_lock); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci dma->fmtinfo = xvip_get_format_by_fourcc(V4L2_PIX_FMT_YUYV); 64562306a36Sopenharmony_ci dma->format.pixelformat = dma->fmtinfo->fourcc; 64662306a36Sopenharmony_ci dma->format.colorspace = V4L2_COLORSPACE_SRGB; 64762306a36Sopenharmony_ci dma->format.field = V4L2_FIELD_NONE; 64862306a36Sopenharmony_ci dma->format.width = XVIP_DMA_DEF_WIDTH; 64962306a36Sopenharmony_ci dma->format.height = XVIP_DMA_DEF_HEIGHT; 65062306a36Sopenharmony_ci dma->format.bytesperline = dma->format.width * dma->fmtinfo->bpp; 65162306a36Sopenharmony_ci dma->format.sizeimage = dma->format.bytesperline * dma->format.height; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* Initialize the media entity... */ 65462306a36Sopenharmony_ci dma->pad.flags = type == V4L2_BUF_TYPE_VIDEO_CAPTURE 65562306a36Sopenharmony_ci ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci ret = media_entity_pads_init(&dma->video.entity, 1, &dma->pad); 65862306a36Sopenharmony_ci if (ret < 0) 65962306a36Sopenharmony_ci goto error; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci /* ... and the video node... */ 66262306a36Sopenharmony_ci dma->video.fops = &xvip_dma_fops; 66362306a36Sopenharmony_ci dma->video.v4l2_dev = &xdev->v4l2_dev; 66462306a36Sopenharmony_ci dma->video.queue = &dma->queue; 66562306a36Sopenharmony_ci snprintf(dma->video.name, sizeof(dma->video.name), "%pOFn %s %u", 66662306a36Sopenharmony_ci xdev->dev->of_node, 66762306a36Sopenharmony_ci type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? "output" : "input", 66862306a36Sopenharmony_ci port); 66962306a36Sopenharmony_ci dma->video.vfl_type = VFL_TYPE_VIDEO; 67062306a36Sopenharmony_ci dma->video.vfl_dir = type == V4L2_BUF_TYPE_VIDEO_CAPTURE 67162306a36Sopenharmony_ci ? VFL_DIR_RX : VFL_DIR_TX; 67262306a36Sopenharmony_ci dma->video.release = video_device_release_empty; 67362306a36Sopenharmony_ci dma->video.ioctl_ops = &xvip_dma_ioctl_ops; 67462306a36Sopenharmony_ci dma->video.lock = &dma->lock; 67562306a36Sopenharmony_ci dma->video.device_caps = V4L2_CAP_STREAMING; 67662306a36Sopenharmony_ci if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 67762306a36Sopenharmony_ci dma->video.device_caps |= V4L2_CAP_VIDEO_CAPTURE; 67862306a36Sopenharmony_ci else 67962306a36Sopenharmony_ci dma->video.device_caps |= V4L2_CAP_VIDEO_OUTPUT; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci video_set_drvdata(&dma->video, dma); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci /* ... and the buffers queue... */ 68462306a36Sopenharmony_ci /* Don't enable VB2_READ and VB2_WRITE, as using the read() and write() 68562306a36Sopenharmony_ci * V4L2 APIs would be inefficient. Testing on the command line with a 68662306a36Sopenharmony_ci * 'cat /dev/video?' thus won't be possible, but given that the driver 68762306a36Sopenharmony_ci * anyway requires a test tool to setup the pipeline before any video 68862306a36Sopenharmony_ci * stream can be started, requiring a specific V4L2 test tool as well 68962306a36Sopenharmony_ci * instead of 'cat' isn't really a drawback. 69062306a36Sopenharmony_ci */ 69162306a36Sopenharmony_ci dma->queue.type = type; 69262306a36Sopenharmony_ci dma->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 69362306a36Sopenharmony_ci dma->queue.lock = &dma->lock; 69462306a36Sopenharmony_ci dma->queue.drv_priv = dma; 69562306a36Sopenharmony_ci dma->queue.buf_struct_size = sizeof(struct xvip_dma_buffer); 69662306a36Sopenharmony_ci dma->queue.ops = &xvip_dma_queue_qops; 69762306a36Sopenharmony_ci dma->queue.mem_ops = &vb2_dma_contig_memops; 69862306a36Sopenharmony_ci dma->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC 69962306a36Sopenharmony_ci | V4L2_BUF_FLAG_TSTAMP_SRC_EOF; 70062306a36Sopenharmony_ci dma->queue.dev = dma->xdev->dev; 70162306a36Sopenharmony_ci ret = vb2_queue_init(&dma->queue); 70262306a36Sopenharmony_ci if (ret < 0) { 70362306a36Sopenharmony_ci dev_err(dma->xdev->dev, "failed to initialize VB2 queue\n"); 70462306a36Sopenharmony_ci goto error; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* ... and the DMA channel. */ 70862306a36Sopenharmony_ci snprintf(name, sizeof(name), "port%u", port); 70962306a36Sopenharmony_ci dma->dma = dma_request_chan(dma->xdev->dev, name); 71062306a36Sopenharmony_ci if (IS_ERR(dma->dma)) { 71162306a36Sopenharmony_ci ret = PTR_ERR(dma->dma); 71262306a36Sopenharmony_ci if (ret != -EPROBE_DEFER) 71362306a36Sopenharmony_ci dev_err(dma->xdev->dev, "no VDMA channel found\n"); 71462306a36Sopenharmony_ci goto error; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci dma->align = 1 << dma->dma->device->copy_align; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci ret = video_register_device(&dma->video, VFL_TYPE_VIDEO, -1); 72062306a36Sopenharmony_ci if (ret < 0) { 72162306a36Sopenharmony_ci dev_err(dma->xdev->dev, "failed to register video device\n"); 72262306a36Sopenharmony_ci goto error; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return 0; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cierror: 72862306a36Sopenharmony_ci xvip_dma_cleanup(dma); 72962306a36Sopenharmony_ci return ret; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_civoid xvip_dma_cleanup(struct xvip_dma *dma) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci if (video_is_registered(&dma->video)) 73562306a36Sopenharmony_ci video_unregister_device(&dma->video); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(dma->dma)) 73862306a36Sopenharmony_ci dma_release_channel(dma->dma); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci media_entity_cleanup(&dma->video.entity); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci mutex_destroy(&dma->lock); 74362306a36Sopenharmony_ci mutex_destroy(&dma->pipe.lock); 74462306a36Sopenharmony_ci} 745