1df330515SLaurent Pinchart /* 2df330515SLaurent Pinchart * Xilinx Video DMA 3df330515SLaurent Pinchart * 4df330515SLaurent Pinchart * Copyright (C) 2013-2015 Ideas on Board 5df330515SLaurent Pinchart * Copyright (C) 2013-2015 Xilinx, Inc. 6df330515SLaurent Pinchart * 7df330515SLaurent Pinchart * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> 8df330515SLaurent Pinchart * Laurent Pinchart <laurent.pinchart@ideasonboard.com> 9df330515SLaurent Pinchart * 10df330515SLaurent Pinchart * This program is free software; you can redistribute it and/or modify 11df330515SLaurent Pinchart * it under the terms of the GNU General Public License version 2 as 12df330515SLaurent Pinchart * published by the Free Software Foundation. 13df330515SLaurent Pinchart */ 14df330515SLaurent Pinchart 154a655466SStephen Rothwell #include <linux/dma/xilinx_dma.h> 16df330515SLaurent Pinchart #include <linux/lcm.h> 17df330515SLaurent Pinchart #include <linux/list.h> 18df330515SLaurent Pinchart #include <linux/module.h> 19df330515SLaurent Pinchart #include <linux/of.h> 20df330515SLaurent Pinchart #include <linux/slab.h> 21df330515SLaurent Pinchart 22df330515SLaurent Pinchart #include <media/v4l2-dev.h> 23df330515SLaurent Pinchart #include <media/v4l2-fh.h> 24df330515SLaurent Pinchart #include <media/v4l2-ioctl.h> 25c139990eSJunghak Sung #include <media/videobuf2-v4l2.h> 26df330515SLaurent Pinchart #include <media/videobuf2-dma-contig.h> 27df330515SLaurent Pinchart 28df330515SLaurent Pinchart #include "xilinx-dma.h" 29df330515SLaurent Pinchart #include "xilinx-vip.h" 30df330515SLaurent Pinchart #include "xilinx-vipp.h" 31df330515SLaurent Pinchart 32df330515SLaurent Pinchart #define XVIP_DMA_DEF_FORMAT V4L2_PIX_FMT_YUYV 33df330515SLaurent Pinchart #define XVIP_DMA_DEF_WIDTH 1920 34df330515SLaurent Pinchart #define XVIP_DMA_DEF_HEIGHT 1080 35df330515SLaurent Pinchart 36df330515SLaurent Pinchart /* Minimum and maximum widths are expressed in bytes */ 37df330515SLaurent Pinchart #define XVIP_DMA_MIN_WIDTH 1U 38df330515SLaurent Pinchart #define XVIP_DMA_MAX_WIDTH 65535U 39df330515SLaurent Pinchart #define XVIP_DMA_MIN_HEIGHT 1U 40df330515SLaurent Pinchart #define XVIP_DMA_MAX_HEIGHT 8191U 41df330515SLaurent Pinchart 42df330515SLaurent Pinchart /* ----------------------------------------------------------------------------- 43df330515SLaurent Pinchart * Helper functions 44df330515SLaurent Pinchart */ 45df330515SLaurent Pinchart 46df330515SLaurent Pinchart static struct v4l2_subdev * 47df330515SLaurent Pinchart xvip_dma_remote_subdev(struct media_pad *local, u32 *pad) 48df330515SLaurent Pinchart { 49df330515SLaurent Pinchart struct media_pad *remote; 50df330515SLaurent Pinchart 51df330515SLaurent Pinchart remote = media_entity_remote_pad(local); 523efdf62cSMauro Carvalho Chehab if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) 53df330515SLaurent Pinchart return NULL; 54df330515SLaurent Pinchart 55df330515SLaurent Pinchart if (pad) 56df330515SLaurent Pinchart *pad = remote->index; 57df330515SLaurent Pinchart 58df330515SLaurent Pinchart return media_entity_to_v4l2_subdev(remote->entity); 59df330515SLaurent Pinchart } 60df330515SLaurent Pinchart 61df330515SLaurent Pinchart static int xvip_dma_verify_format(struct xvip_dma *dma) 62df330515SLaurent Pinchart { 63df330515SLaurent Pinchart struct v4l2_subdev_format fmt; 64df330515SLaurent Pinchart struct v4l2_subdev *subdev; 65df330515SLaurent Pinchart int ret; 66df330515SLaurent Pinchart 67df330515SLaurent Pinchart subdev = xvip_dma_remote_subdev(&dma->pad, &fmt.pad); 68df330515SLaurent Pinchart if (subdev == NULL) 69df330515SLaurent Pinchart return -EPIPE; 70df330515SLaurent Pinchart 71df330515SLaurent Pinchart fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; 72df330515SLaurent Pinchart ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); 73df330515SLaurent Pinchart if (ret < 0) 74df330515SLaurent Pinchart return ret == -ENOIOCTLCMD ? -EINVAL : ret; 75df330515SLaurent Pinchart 76df330515SLaurent Pinchart if (dma->fmtinfo->code != fmt.format.code || 77df330515SLaurent Pinchart dma->format.height != fmt.format.height || 78df330515SLaurent Pinchart dma->format.width != fmt.format.width || 79df330515SLaurent Pinchart dma->format.colorspace != fmt.format.colorspace) 80df330515SLaurent Pinchart return -EINVAL; 81df330515SLaurent Pinchart 82df330515SLaurent Pinchart return 0; 83df330515SLaurent Pinchart } 84df330515SLaurent Pinchart 85df330515SLaurent Pinchart /* ----------------------------------------------------------------------------- 86df330515SLaurent Pinchart * Pipeline Stream Management 87df330515SLaurent Pinchart */ 88df330515SLaurent Pinchart 89df330515SLaurent Pinchart /** 90df330515SLaurent Pinchart * xvip_pipeline_start_stop - Start ot stop streaming on a pipeline 91df330515SLaurent Pinchart * @pipe: The pipeline 92df330515SLaurent Pinchart * @start: Start (when true) or stop (when false) the pipeline 93df330515SLaurent Pinchart * 94df330515SLaurent Pinchart * Walk the entities chain starting at the pipeline output video node and start 95df330515SLaurent Pinchart * or stop all of them. 96df330515SLaurent Pinchart * 97df330515SLaurent Pinchart * Return: 0 if successful, or the return value of the failed video::s_stream 98df330515SLaurent Pinchart * operation otherwise. 99df330515SLaurent Pinchart */ 100df330515SLaurent Pinchart static int xvip_pipeline_start_stop(struct xvip_pipeline *pipe, bool start) 101df330515SLaurent Pinchart { 102df330515SLaurent Pinchart struct xvip_dma *dma = pipe->output; 103df330515SLaurent Pinchart struct media_entity *entity; 104df330515SLaurent Pinchart struct media_pad *pad; 105df330515SLaurent Pinchart struct v4l2_subdev *subdev; 106df330515SLaurent Pinchart int ret; 107df330515SLaurent Pinchart 108df330515SLaurent Pinchart entity = &dma->video.entity; 109df330515SLaurent Pinchart while (1) { 110df330515SLaurent Pinchart pad = &entity->pads[0]; 111df330515SLaurent Pinchart if (!(pad->flags & MEDIA_PAD_FL_SINK)) 112df330515SLaurent Pinchart break; 113df330515SLaurent Pinchart 114df330515SLaurent Pinchart pad = media_entity_remote_pad(pad); 1153efdf62cSMauro Carvalho Chehab if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 116df330515SLaurent Pinchart break; 117df330515SLaurent Pinchart 118df330515SLaurent Pinchart entity = pad->entity; 119df330515SLaurent Pinchart subdev = media_entity_to_v4l2_subdev(entity); 120df330515SLaurent Pinchart 121df330515SLaurent Pinchart ret = v4l2_subdev_call(subdev, video, s_stream, start); 122df330515SLaurent Pinchart if (start && ret < 0 && ret != -ENOIOCTLCMD) 123df330515SLaurent Pinchart return ret; 124df330515SLaurent Pinchart } 125df330515SLaurent Pinchart 126df330515SLaurent Pinchart return 0; 127df330515SLaurent Pinchart } 128df330515SLaurent Pinchart 129df330515SLaurent Pinchart /** 130df330515SLaurent Pinchart * xvip_pipeline_set_stream - Enable/disable streaming on a pipeline 131df330515SLaurent Pinchart * @pipe: The pipeline 132df330515SLaurent Pinchart * @on: Turn the stream on when true or off when false 133df330515SLaurent Pinchart * 134df330515SLaurent Pinchart * The pipeline is shared between all DMA engines connect at its input and 135df330515SLaurent Pinchart * output. While the stream state of DMA engines can be controlled 136df330515SLaurent Pinchart * independently, pipelines have a shared stream state that enable or disable 137df330515SLaurent Pinchart * all entities in the pipeline. For this reason the pipeline uses a streaming 138df330515SLaurent Pinchart * counter that tracks the number of DMA engines that have requested the stream 139df330515SLaurent Pinchart * to be enabled. 140df330515SLaurent Pinchart * 141df330515SLaurent Pinchart * When called with the @on argument set to true, this function will increment 142df330515SLaurent Pinchart * the pipeline streaming count. If the streaming count reaches the number of 143df330515SLaurent Pinchart * DMA engines in the pipeline it will enable all entities that belong to the 144df330515SLaurent Pinchart * pipeline. 145df330515SLaurent Pinchart * 146df330515SLaurent Pinchart * Similarly, when called with the @on argument set to false, this function will 147df330515SLaurent Pinchart * decrement the pipeline streaming count and disable all entities in the 148df330515SLaurent Pinchart * pipeline when the streaming count reaches zero. 149df330515SLaurent Pinchart * 150df330515SLaurent Pinchart * Return: 0 if successful, or the return value of the failed video::s_stream 151df330515SLaurent Pinchart * operation otherwise. Stopping the pipeline never fails. The pipeline state is 152df330515SLaurent Pinchart * not updated when the operation fails. 153df330515SLaurent Pinchart */ 154df330515SLaurent Pinchart static int xvip_pipeline_set_stream(struct xvip_pipeline *pipe, bool on) 155df330515SLaurent Pinchart { 156df330515SLaurent Pinchart int ret = 0; 157df330515SLaurent Pinchart 158df330515SLaurent Pinchart mutex_lock(&pipe->lock); 159df330515SLaurent Pinchart 160df330515SLaurent Pinchart if (on) { 161df330515SLaurent Pinchart if (pipe->stream_count == pipe->num_dmas - 1) { 162df330515SLaurent Pinchart ret = xvip_pipeline_start_stop(pipe, true); 163df330515SLaurent Pinchart if (ret < 0) 164df330515SLaurent Pinchart goto done; 165df330515SLaurent Pinchart } 166df330515SLaurent Pinchart pipe->stream_count++; 167df330515SLaurent Pinchart } else { 168df330515SLaurent Pinchart if (--pipe->stream_count == 0) 169df330515SLaurent Pinchart xvip_pipeline_start_stop(pipe, false); 170df330515SLaurent Pinchart } 171df330515SLaurent Pinchart 172df330515SLaurent Pinchart done: 173df330515SLaurent Pinchart mutex_unlock(&pipe->lock); 174df330515SLaurent Pinchart return ret; 175df330515SLaurent Pinchart } 176df330515SLaurent Pinchart 177df330515SLaurent Pinchart static int xvip_pipeline_validate(struct xvip_pipeline *pipe, 178df330515SLaurent Pinchart struct xvip_dma *start) 179df330515SLaurent Pinchart { 180df330515SLaurent Pinchart struct media_entity_graph graph; 181df330515SLaurent Pinchart struct media_entity *entity = &start->video.entity; 182d10c9894SJavier Martinez Canillas struct media_device *mdev = entity->graph_obj.mdev; 183df330515SLaurent Pinchart unsigned int num_inputs = 0; 184df330515SLaurent Pinchart unsigned int num_outputs = 0; 18508613c54SSakari Ailus int ret; 186df330515SLaurent Pinchart 187df330515SLaurent Pinchart mutex_lock(&mdev->graph_mutex); 188df330515SLaurent Pinchart 189df330515SLaurent Pinchart /* Walk the graph to locate the video nodes. */ 19008613c54SSakari Ailus ret = media_entity_graph_walk_init(&graph, entity->graph_obj.mdev); 19108613c54SSakari Ailus if (ret) { 19208613c54SSakari Ailus mutex_unlock(&mdev->graph_mutex); 19308613c54SSakari Ailus return ret; 19408613c54SSakari Ailus } 19508613c54SSakari Ailus 196df330515SLaurent Pinchart media_entity_graph_walk_start(&graph, entity); 197df330515SLaurent Pinchart 198df330515SLaurent Pinchart while ((entity = media_entity_graph_walk_next(&graph))) { 199df330515SLaurent Pinchart struct xvip_dma *dma; 200df330515SLaurent Pinchart 2014ca72efaSMauro Carvalho Chehab if (entity->function != MEDIA_ENT_F_IO_V4L) 202df330515SLaurent Pinchart continue; 203df330515SLaurent Pinchart 204df330515SLaurent Pinchart dma = to_xvip_dma(media_entity_to_video_device(entity)); 205df330515SLaurent Pinchart 206df330515SLaurent Pinchart if (dma->pad.flags & MEDIA_PAD_FL_SINK) { 207df330515SLaurent Pinchart pipe->output = dma; 208df330515SLaurent Pinchart num_outputs++; 209df330515SLaurent Pinchart } else { 210df330515SLaurent Pinchart num_inputs++; 211df330515SLaurent Pinchart } 212df330515SLaurent Pinchart } 213df330515SLaurent Pinchart 214df330515SLaurent Pinchart mutex_unlock(&mdev->graph_mutex); 215df330515SLaurent Pinchart 21608613c54SSakari Ailus media_entity_graph_walk_cleanup(&graph); 21708613c54SSakari Ailus 218df330515SLaurent Pinchart /* We need exactly one output and zero or one input. */ 219df330515SLaurent Pinchart if (num_outputs != 1 || num_inputs > 1) 220df330515SLaurent Pinchart return -EPIPE; 221df330515SLaurent Pinchart 222df330515SLaurent Pinchart pipe->num_dmas = num_inputs + num_outputs; 223df330515SLaurent Pinchart 224df330515SLaurent Pinchart return 0; 225df330515SLaurent Pinchart } 226df330515SLaurent Pinchart 227df330515SLaurent Pinchart static void __xvip_pipeline_cleanup(struct xvip_pipeline *pipe) 228df330515SLaurent Pinchart { 229df330515SLaurent Pinchart pipe->num_dmas = 0; 230df330515SLaurent Pinchart pipe->output = NULL; 231df330515SLaurent Pinchart } 232df330515SLaurent Pinchart 233df330515SLaurent Pinchart /** 234df330515SLaurent Pinchart * xvip_pipeline_cleanup - Cleanup the pipeline after streaming 235df330515SLaurent Pinchart * @pipe: the pipeline 236df330515SLaurent Pinchart * 237df330515SLaurent Pinchart * Decrease the pipeline use count and clean it up if we were the last user. 238df330515SLaurent Pinchart */ 239df330515SLaurent Pinchart static void xvip_pipeline_cleanup(struct xvip_pipeline *pipe) 240df330515SLaurent Pinchart { 241df330515SLaurent Pinchart mutex_lock(&pipe->lock); 242df330515SLaurent Pinchart 243df330515SLaurent Pinchart /* If we're the last user clean up the pipeline. */ 244df330515SLaurent Pinchart if (--pipe->use_count == 0) 245df330515SLaurent Pinchart __xvip_pipeline_cleanup(pipe); 246df330515SLaurent Pinchart 247df330515SLaurent Pinchart mutex_unlock(&pipe->lock); 248df330515SLaurent Pinchart } 249df330515SLaurent Pinchart 250df330515SLaurent Pinchart /** 251df330515SLaurent Pinchart * xvip_pipeline_prepare - Prepare the pipeline for streaming 252df330515SLaurent Pinchart * @pipe: the pipeline 253df330515SLaurent Pinchart * @dma: DMA engine at one end of the pipeline 254df330515SLaurent Pinchart * 255df330515SLaurent Pinchart * Validate the pipeline if no user exists yet, otherwise just increase the use 256df330515SLaurent Pinchart * count. 257df330515SLaurent Pinchart * 258df330515SLaurent Pinchart * Return: 0 if successful or -EPIPE if the pipeline is not valid. 259df330515SLaurent Pinchart */ 260df330515SLaurent Pinchart static int xvip_pipeline_prepare(struct xvip_pipeline *pipe, 261df330515SLaurent Pinchart struct xvip_dma *dma) 262df330515SLaurent Pinchart { 263df330515SLaurent Pinchart int ret; 264df330515SLaurent Pinchart 265df330515SLaurent Pinchart mutex_lock(&pipe->lock); 266df330515SLaurent Pinchart 267df330515SLaurent Pinchart /* If we're the first user validate and initialize the pipeline. */ 268df330515SLaurent Pinchart if (pipe->use_count == 0) { 269df330515SLaurent Pinchart ret = xvip_pipeline_validate(pipe, dma); 270df330515SLaurent Pinchart if (ret < 0) { 271df330515SLaurent Pinchart __xvip_pipeline_cleanup(pipe); 272df330515SLaurent Pinchart goto done; 273df330515SLaurent Pinchart } 274df330515SLaurent Pinchart } 275df330515SLaurent Pinchart 276df330515SLaurent Pinchart pipe->use_count++; 277df330515SLaurent Pinchart ret = 0; 278df330515SLaurent Pinchart 279df330515SLaurent Pinchart done: 280df330515SLaurent Pinchart mutex_unlock(&pipe->lock); 281df330515SLaurent Pinchart return ret; 282df330515SLaurent Pinchart } 283df330515SLaurent Pinchart 284df330515SLaurent Pinchart /* ----------------------------------------------------------------------------- 285df330515SLaurent Pinchart * videobuf2 queue operations 286df330515SLaurent Pinchart */ 287df330515SLaurent Pinchart 288df330515SLaurent Pinchart /** 289df330515SLaurent Pinchart * struct xvip_dma_buffer - Video DMA buffer 290df330515SLaurent Pinchart * @buf: vb2 buffer base object 291df330515SLaurent Pinchart * @queue: buffer list entry in the DMA engine queued buffers list 292df330515SLaurent Pinchart * @dma: DMA channel that uses the buffer 293df330515SLaurent Pinchart */ 294df330515SLaurent Pinchart struct xvip_dma_buffer { 2952d700715SJunghak Sung struct vb2_v4l2_buffer buf; 296df330515SLaurent Pinchart struct list_head queue; 297df330515SLaurent Pinchart struct xvip_dma *dma; 298df330515SLaurent Pinchart }; 299df330515SLaurent Pinchart 300df330515SLaurent Pinchart #define to_xvip_dma_buffer(vb) container_of(vb, struct xvip_dma_buffer, buf) 301df330515SLaurent Pinchart 302df330515SLaurent Pinchart static void xvip_dma_complete(void *param) 303df330515SLaurent Pinchart { 304df330515SLaurent Pinchart struct xvip_dma_buffer *buf = param; 305df330515SLaurent Pinchart struct xvip_dma *dma = buf->dma; 306df330515SLaurent Pinchart 307df330515SLaurent Pinchart spin_lock(&dma->queued_lock); 308df330515SLaurent Pinchart list_del(&buf->queue); 309df330515SLaurent Pinchart spin_unlock(&dma->queued_lock); 310df330515SLaurent Pinchart 3112d700715SJunghak Sung buf->buf.field = V4L2_FIELD_NONE; 3122d700715SJunghak Sung buf->buf.sequence = dma->sequence++; 313d6dd645eSJunghak Sung buf->buf.vb2_buf.timestamp = ktime_get_ns(); 3142d700715SJunghak Sung vb2_set_plane_payload(&buf->buf.vb2_buf, 0, dma->format.sizeimage); 3152d700715SJunghak Sung vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); 316df330515SLaurent Pinchart } 317df330515SLaurent Pinchart 318df330515SLaurent Pinchart static int 319df9ecb0cSHans Verkuil xvip_dma_queue_setup(struct vb2_queue *vq, 320df330515SLaurent Pinchart unsigned int *nbuffers, unsigned int *nplanes, 32136c0f8b3SHans Verkuil unsigned int sizes[], struct device *alloc_devs[]) 322df330515SLaurent Pinchart { 323df330515SLaurent Pinchart struct xvip_dma *dma = vb2_get_drv_priv(vq); 324df330515SLaurent Pinchart 325df330515SLaurent Pinchart /* Make sure the image size is large enough. */ 326df9ecb0cSHans Verkuil if (*nplanes) 327df9ecb0cSHans Verkuil return sizes[0] < dma->format.sizeimage ? -EINVAL : 0; 328df330515SLaurent Pinchart 329df330515SLaurent Pinchart *nplanes = 1; 330df9ecb0cSHans Verkuil sizes[0] = dma->format.sizeimage; 331df330515SLaurent Pinchart 332df330515SLaurent Pinchart return 0; 333df330515SLaurent Pinchart } 334df330515SLaurent Pinchart 335df330515SLaurent Pinchart static int xvip_dma_buffer_prepare(struct vb2_buffer *vb) 336df330515SLaurent Pinchart { 3372d700715SJunghak Sung struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 338df330515SLaurent Pinchart struct xvip_dma *dma = vb2_get_drv_priv(vb->vb2_queue); 3392d700715SJunghak Sung struct xvip_dma_buffer *buf = to_xvip_dma_buffer(vbuf); 340df330515SLaurent Pinchart 341df330515SLaurent Pinchart buf->dma = dma; 342df330515SLaurent Pinchart 343df330515SLaurent Pinchart return 0; 344df330515SLaurent Pinchart } 345df330515SLaurent Pinchart 346df330515SLaurent Pinchart static void xvip_dma_buffer_queue(struct vb2_buffer *vb) 347df330515SLaurent Pinchart { 3482d700715SJunghak Sung struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 349df330515SLaurent Pinchart struct xvip_dma *dma = vb2_get_drv_priv(vb->vb2_queue); 3502d700715SJunghak Sung struct xvip_dma_buffer *buf = to_xvip_dma_buffer(vbuf); 351df330515SLaurent Pinchart struct dma_async_tx_descriptor *desc; 352df330515SLaurent Pinchart dma_addr_t addr = vb2_dma_contig_plane_dma_addr(vb, 0); 353df330515SLaurent Pinchart u32 flags; 354df330515SLaurent Pinchart 355df330515SLaurent Pinchart if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { 356df330515SLaurent Pinchart flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; 357df330515SLaurent Pinchart dma->xt.dir = DMA_DEV_TO_MEM; 358df330515SLaurent Pinchart dma->xt.src_sgl = false; 359df330515SLaurent Pinchart dma->xt.dst_sgl = true; 360df330515SLaurent Pinchart dma->xt.dst_start = addr; 361df330515SLaurent Pinchart } else { 362df330515SLaurent Pinchart flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; 363df330515SLaurent Pinchart dma->xt.dir = DMA_MEM_TO_DEV; 364df330515SLaurent Pinchart dma->xt.src_sgl = true; 365df330515SLaurent Pinchart dma->xt.dst_sgl = false; 366df330515SLaurent Pinchart dma->xt.src_start = addr; 367df330515SLaurent Pinchart } 368df330515SLaurent Pinchart 369df330515SLaurent Pinchart dma->xt.frame_size = 1; 370df330515SLaurent Pinchart dma->sgl[0].size = dma->format.width * dma->fmtinfo->bpp; 371df330515SLaurent Pinchart dma->sgl[0].icg = dma->format.bytesperline - dma->sgl[0].size; 372df330515SLaurent Pinchart dma->xt.numf = dma->format.height; 373df330515SLaurent Pinchart 374df330515SLaurent Pinchart desc = dmaengine_prep_interleaved_dma(dma->dma, &dma->xt, flags); 375df330515SLaurent Pinchart if (!desc) { 376df330515SLaurent Pinchart dev_err(dma->xdev->dev, "Failed to prepare DMA transfer\n"); 3772d700715SJunghak Sung vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_ERROR); 378df330515SLaurent Pinchart return; 379df330515SLaurent Pinchart } 380df330515SLaurent Pinchart desc->callback = xvip_dma_complete; 381df330515SLaurent Pinchart desc->callback_param = buf; 382df330515SLaurent Pinchart 383df330515SLaurent Pinchart spin_lock_irq(&dma->queued_lock); 384df330515SLaurent Pinchart list_add_tail(&buf->queue, &dma->queued_bufs); 385df330515SLaurent Pinchart spin_unlock_irq(&dma->queued_lock); 386df330515SLaurent Pinchart 387df330515SLaurent Pinchart dmaengine_submit(desc); 388df330515SLaurent Pinchart 389df330515SLaurent Pinchart if (vb2_is_streaming(&dma->queue)) 390df330515SLaurent Pinchart dma_async_issue_pending(dma->dma); 391df330515SLaurent Pinchart } 392df330515SLaurent Pinchart 393df330515SLaurent Pinchart static int xvip_dma_start_streaming(struct vb2_queue *vq, unsigned int count) 394df330515SLaurent Pinchart { 395df330515SLaurent Pinchart struct xvip_dma *dma = vb2_get_drv_priv(vq); 396df330515SLaurent Pinchart struct xvip_dma_buffer *buf, *nbuf; 397df330515SLaurent Pinchart struct xvip_pipeline *pipe; 398df330515SLaurent Pinchart int ret; 399df330515SLaurent Pinchart 400df330515SLaurent Pinchart dma->sequence = 0; 401df330515SLaurent Pinchart 402df330515SLaurent Pinchart /* 403df330515SLaurent Pinchart * Start streaming on the pipeline. No link touching an entity in the 404df330515SLaurent Pinchart * pipeline can be activated or deactivated once streaming is started. 405df330515SLaurent Pinchart * 406df330515SLaurent Pinchart * Use the pipeline object embedded in the first DMA object that starts 407df330515SLaurent Pinchart * streaming. 408df330515SLaurent Pinchart */ 409df330515SLaurent Pinchart pipe = dma->video.entity.pipe 410df330515SLaurent Pinchart ? to_xvip_pipeline(&dma->video.entity) : &dma->pipe; 411df330515SLaurent Pinchart 412df330515SLaurent Pinchart ret = media_entity_pipeline_start(&dma->video.entity, &pipe->pipe); 413df330515SLaurent Pinchart if (ret < 0) 414df330515SLaurent Pinchart goto error; 415df330515SLaurent Pinchart 416df330515SLaurent Pinchart /* Verify that the configured format matches the output of the 417df330515SLaurent Pinchart * connected subdev. 418df330515SLaurent Pinchart */ 419df330515SLaurent Pinchart ret = xvip_dma_verify_format(dma); 420df330515SLaurent Pinchart if (ret < 0) 421df330515SLaurent Pinchart goto error_stop; 422df330515SLaurent Pinchart 423df330515SLaurent Pinchart ret = xvip_pipeline_prepare(pipe, dma); 424df330515SLaurent Pinchart if (ret < 0) 425df330515SLaurent Pinchart goto error_stop; 426df330515SLaurent Pinchart 427df330515SLaurent Pinchart /* Start the DMA engine. This must be done before starting the blocks 428df330515SLaurent Pinchart * in the pipeline to avoid DMA synchronization issues. 429df330515SLaurent Pinchart */ 430df330515SLaurent Pinchart dma_async_issue_pending(dma->dma); 431df330515SLaurent Pinchart 432df330515SLaurent Pinchart /* Start the pipeline. */ 433df330515SLaurent Pinchart xvip_pipeline_set_stream(pipe, true); 434df330515SLaurent Pinchart 435df330515SLaurent Pinchart return 0; 436df330515SLaurent Pinchart 437df330515SLaurent Pinchart error_stop: 438df330515SLaurent Pinchart media_entity_pipeline_stop(&dma->video.entity); 439df330515SLaurent Pinchart 440df330515SLaurent Pinchart error: 441df330515SLaurent Pinchart /* Give back all queued buffers to videobuf2. */ 442df330515SLaurent Pinchart spin_lock_irq(&dma->queued_lock); 443df330515SLaurent Pinchart list_for_each_entry_safe(buf, nbuf, &dma->queued_bufs, queue) { 4442d700715SJunghak Sung vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_QUEUED); 445df330515SLaurent Pinchart list_del(&buf->queue); 446df330515SLaurent Pinchart } 447df330515SLaurent Pinchart spin_unlock_irq(&dma->queued_lock); 448df330515SLaurent Pinchart 449df330515SLaurent Pinchart return ret; 450df330515SLaurent Pinchart } 451df330515SLaurent Pinchart 452df330515SLaurent Pinchart static void xvip_dma_stop_streaming(struct vb2_queue *vq) 453df330515SLaurent Pinchart { 454df330515SLaurent Pinchart struct xvip_dma *dma = vb2_get_drv_priv(vq); 455df330515SLaurent Pinchart struct xvip_pipeline *pipe = to_xvip_pipeline(&dma->video.entity); 456df330515SLaurent Pinchart struct xvip_dma_buffer *buf, *nbuf; 457df330515SLaurent Pinchart 458df330515SLaurent Pinchart /* Stop the pipeline. */ 459df330515SLaurent Pinchart xvip_pipeline_set_stream(pipe, false); 460df330515SLaurent Pinchart 461df330515SLaurent Pinchart /* Stop and reset the DMA engine. */ 462df330515SLaurent Pinchart dmaengine_terminate_all(dma->dma); 463df330515SLaurent Pinchart 464df330515SLaurent Pinchart /* Cleanup the pipeline and mark it as being stopped. */ 465df330515SLaurent Pinchart xvip_pipeline_cleanup(pipe); 466df330515SLaurent Pinchart media_entity_pipeline_stop(&dma->video.entity); 467df330515SLaurent Pinchart 468df330515SLaurent Pinchart /* Give back all queued buffers to videobuf2. */ 469df330515SLaurent Pinchart spin_lock_irq(&dma->queued_lock); 470df330515SLaurent Pinchart list_for_each_entry_safe(buf, nbuf, &dma->queued_bufs, queue) { 4712d700715SJunghak Sung vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_ERROR); 472df330515SLaurent Pinchart list_del(&buf->queue); 473df330515SLaurent Pinchart } 474df330515SLaurent Pinchart spin_unlock_irq(&dma->queued_lock); 475df330515SLaurent Pinchart } 476df330515SLaurent Pinchart 477b7b361f0SJulia Lawall static const struct vb2_ops xvip_dma_queue_qops = { 478df330515SLaurent Pinchart .queue_setup = xvip_dma_queue_setup, 479df330515SLaurent Pinchart .buf_prepare = xvip_dma_buffer_prepare, 480df330515SLaurent Pinchart .buf_queue = xvip_dma_buffer_queue, 481df330515SLaurent Pinchart .wait_prepare = vb2_ops_wait_prepare, 482df330515SLaurent Pinchart .wait_finish = vb2_ops_wait_finish, 483df330515SLaurent Pinchart .start_streaming = xvip_dma_start_streaming, 484df330515SLaurent Pinchart .stop_streaming = xvip_dma_stop_streaming, 485df330515SLaurent Pinchart }; 486df330515SLaurent Pinchart 487df330515SLaurent Pinchart /* ----------------------------------------------------------------------------- 488df330515SLaurent Pinchart * V4L2 ioctls 489df330515SLaurent Pinchart */ 490df330515SLaurent Pinchart 491df330515SLaurent Pinchart static int 492df330515SLaurent Pinchart xvip_dma_querycap(struct file *file, void *fh, struct v4l2_capability *cap) 493df330515SLaurent Pinchart { 494df330515SLaurent Pinchart struct v4l2_fh *vfh = file->private_data; 495df330515SLaurent Pinchart struct xvip_dma *dma = to_xvip_dma(vfh->vdev); 496df330515SLaurent Pinchart 497df330515SLaurent Pinchart cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING 498df330515SLaurent Pinchart | dma->xdev->v4l2_caps; 499df330515SLaurent Pinchart 500df330515SLaurent Pinchart if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 501df330515SLaurent Pinchart cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; 502df330515SLaurent Pinchart else 503df330515SLaurent Pinchart cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; 504df330515SLaurent Pinchart 505df330515SLaurent Pinchart strlcpy(cap->driver, "xilinx-vipp", sizeof(cap->driver)); 506df330515SLaurent Pinchart strlcpy(cap->card, dma->video.name, sizeof(cap->card)); 507df330515SLaurent Pinchart snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s:%u", 508df330515SLaurent Pinchart dma->xdev->dev->of_node->name, dma->port); 509df330515SLaurent Pinchart 510df330515SLaurent Pinchart return 0; 511df330515SLaurent Pinchart } 512df330515SLaurent Pinchart 513df330515SLaurent Pinchart /* FIXME: without this callback function, some applications are not configured 514df330515SLaurent Pinchart * with correct formats, and it results in frames in wrong format. Whether this 515df330515SLaurent Pinchart * callback needs to be required is not clearly defined, so it should be 516df330515SLaurent Pinchart * clarified through the mailing list. 517df330515SLaurent Pinchart */ 518df330515SLaurent Pinchart static int 519df330515SLaurent Pinchart xvip_dma_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) 520df330515SLaurent Pinchart { 521df330515SLaurent Pinchart struct v4l2_fh *vfh = file->private_data; 522df330515SLaurent Pinchart struct xvip_dma *dma = to_xvip_dma(vfh->vdev); 523df330515SLaurent Pinchart 524df330515SLaurent Pinchart if (f->index > 0) 525df330515SLaurent Pinchart return -EINVAL; 526df330515SLaurent Pinchart 527df330515SLaurent Pinchart f->pixelformat = dma->format.pixelformat; 528df330515SLaurent Pinchart strlcpy(f->description, dma->fmtinfo->description, 529df330515SLaurent Pinchart sizeof(f->description)); 530df330515SLaurent Pinchart 531df330515SLaurent Pinchart return 0; 532df330515SLaurent Pinchart } 533df330515SLaurent Pinchart 534df330515SLaurent Pinchart static int 535df330515SLaurent Pinchart xvip_dma_get_format(struct file *file, void *fh, struct v4l2_format *format) 536df330515SLaurent Pinchart { 537df330515SLaurent Pinchart struct v4l2_fh *vfh = file->private_data; 538df330515SLaurent Pinchart struct xvip_dma *dma = to_xvip_dma(vfh->vdev); 539df330515SLaurent Pinchart 540df330515SLaurent Pinchart format->fmt.pix = dma->format; 541df330515SLaurent Pinchart 542df330515SLaurent Pinchart return 0; 543df330515SLaurent Pinchart } 544df330515SLaurent Pinchart 545df330515SLaurent Pinchart static void 546df330515SLaurent Pinchart __xvip_dma_try_format(struct xvip_dma *dma, struct v4l2_pix_format *pix, 547df330515SLaurent Pinchart const struct xvip_video_format **fmtinfo) 548df330515SLaurent Pinchart { 549df330515SLaurent Pinchart const struct xvip_video_format *info; 550df330515SLaurent Pinchart unsigned int min_width; 551df330515SLaurent Pinchart unsigned int max_width; 552df330515SLaurent Pinchart unsigned int min_bpl; 553df330515SLaurent Pinchart unsigned int max_bpl; 554df330515SLaurent Pinchart unsigned int width; 555df330515SLaurent Pinchart unsigned int align; 556df330515SLaurent Pinchart unsigned int bpl; 557df330515SLaurent Pinchart 558df330515SLaurent Pinchart /* Retrieve format information and select the default format if the 559df330515SLaurent Pinchart * requested format isn't supported. 560df330515SLaurent Pinchart */ 561df330515SLaurent Pinchart info = xvip_get_format_by_fourcc(pix->pixelformat); 562df330515SLaurent Pinchart if (IS_ERR(info)) 563df330515SLaurent Pinchart info = xvip_get_format_by_fourcc(XVIP_DMA_DEF_FORMAT); 564df330515SLaurent Pinchart 565df330515SLaurent Pinchart pix->pixelformat = info->fourcc; 566df330515SLaurent Pinchart pix->field = V4L2_FIELD_NONE; 567df330515SLaurent Pinchart 568df330515SLaurent Pinchart /* The transfer alignment requirements are expressed in bytes. Compute 569df330515SLaurent Pinchart * the minimum and maximum values, clamp the requested width and convert 570df330515SLaurent Pinchart * it back to pixels. 571df330515SLaurent Pinchart */ 572df330515SLaurent Pinchart align = lcm(dma->align, info->bpp); 573df330515SLaurent Pinchart min_width = roundup(XVIP_DMA_MIN_WIDTH, align); 574df330515SLaurent Pinchart max_width = rounddown(XVIP_DMA_MAX_WIDTH, align); 575df330515SLaurent Pinchart width = rounddown(pix->width * info->bpp, align); 576df330515SLaurent Pinchart 577df330515SLaurent Pinchart pix->width = clamp(width, min_width, max_width) / info->bpp; 578df330515SLaurent Pinchart pix->height = clamp(pix->height, XVIP_DMA_MIN_HEIGHT, 579df330515SLaurent Pinchart XVIP_DMA_MAX_HEIGHT); 580df330515SLaurent Pinchart 581df330515SLaurent Pinchart /* Clamp the requested bytes per line value. If the maximum bytes per 582df330515SLaurent Pinchart * line value is zero, the module doesn't support user configurable line 583df330515SLaurent Pinchart * sizes. Override the requested value with the minimum in that case. 584df330515SLaurent Pinchart */ 585df330515SLaurent Pinchart min_bpl = pix->width * info->bpp; 586df330515SLaurent Pinchart max_bpl = rounddown(XVIP_DMA_MAX_WIDTH, dma->align); 587df330515SLaurent Pinchart bpl = rounddown(pix->bytesperline, dma->align); 588df330515SLaurent Pinchart 589df330515SLaurent Pinchart pix->bytesperline = clamp(bpl, min_bpl, max_bpl); 590df330515SLaurent Pinchart pix->sizeimage = pix->bytesperline * pix->height; 591df330515SLaurent Pinchart 592df330515SLaurent Pinchart if (fmtinfo) 593df330515SLaurent Pinchart *fmtinfo = info; 594df330515SLaurent Pinchart } 595df330515SLaurent Pinchart 596df330515SLaurent Pinchart static int 597df330515SLaurent Pinchart xvip_dma_try_format(struct file *file, void *fh, struct v4l2_format *format) 598df330515SLaurent Pinchart { 599df330515SLaurent Pinchart struct v4l2_fh *vfh = file->private_data; 600df330515SLaurent Pinchart struct xvip_dma *dma = to_xvip_dma(vfh->vdev); 601df330515SLaurent Pinchart 602df330515SLaurent Pinchart __xvip_dma_try_format(dma, &format->fmt.pix, NULL); 603df330515SLaurent Pinchart return 0; 604df330515SLaurent Pinchart } 605df330515SLaurent Pinchart 606df330515SLaurent Pinchart static int 607df330515SLaurent Pinchart xvip_dma_set_format(struct file *file, void *fh, struct v4l2_format *format) 608df330515SLaurent Pinchart { 609df330515SLaurent Pinchart struct v4l2_fh *vfh = file->private_data; 610df330515SLaurent Pinchart struct xvip_dma *dma = to_xvip_dma(vfh->vdev); 611df330515SLaurent Pinchart const struct xvip_video_format *info; 612df330515SLaurent Pinchart 613df330515SLaurent Pinchart __xvip_dma_try_format(dma, &format->fmt.pix, &info); 614df330515SLaurent Pinchart 615df330515SLaurent Pinchart if (vb2_is_busy(&dma->queue)) 616df330515SLaurent Pinchart return -EBUSY; 617df330515SLaurent Pinchart 618df330515SLaurent Pinchart dma->format = format->fmt.pix; 619df330515SLaurent Pinchart dma->fmtinfo = info; 620df330515SLaurent Pinchart 621df330515SLaurent Pinchart return 0; 622df330515SLaurent Pinchart } 623df330515SLaurent Pinchart 624df330515SLaurent Pinchart static const struct v4l2_ioctl_ops xvip_dma_ioctl_ops = { 625df330515SLaurent Pinchart .vidioc_querycap = xvip_dma_querycap, 626df330515SLaurent Pinchart .vidioc_enum_fmt_vid_cap = xvip_dma_enum_format, 627df330515SLaurent Pinchart .vidioc_g_fmt_vid_cap = xvip_dma_get_format, 628df330515SLaurent Pinchart .vidioc_g_fmt_vid_out = xvip_dma_get_format, 629df330515SLaurent Pinchart .vidioc_s_fmt_vid_cap = xvip_dma_set_format, 630df330515SLaurent Pinchart .vidioc_s_fmt_vid_out = xvip_dma_set_format, 631df330515SLaurent Pinchart .vidioc_try_fmt_vid_cap = xvip_dma_try_format, 632df330515SLaurent Pinchart .vidioc_try_fmt_vid_out = xvip_dma_try_format, 633df330515SLaurent Pinchart .vidioc_reqbufs = vb2_ioctl_reqbufs, 634df330515SLaurent Pinchart .vidioc_querybuf = vb2_ioctl_querybuf, 635df330515SLaurent Pinchart .vidioc_qbuf = vb2_ioctl_qbuf, 636df330515SLaurent Pinchart .vidioc_dqbuf = vb2_ioctl_dqbuf, 637df330515SLaurent Pinchart .vidioc_create_bufs = vb2_ioctl_create_bufs, 638df330515SLaurent Pinchart .vidioc_expbuf = vb2_ioctl_expbuf, 639df330515SLaurent Pinchart .vidioc_streamon = vb2_ioctl_streamon, 640df330515SLaurent Pinchart .vidioc_streamoff = vb2_ioctl_streamoff, 641df330515SLaurent Pinchart }; 642df330515SLaurent Pinchart 643df330515SLaurent Pinchart /* ----------------------------------------------------------------------------- 644df330515SLaurent Pinchart * V4L2 file operations 645df330515SLaurent Pinchart */ 646df330515SLaurent Pinchart 647df330515SLaurent Pinchart static const struct v4l2_file_operations xvip_dma_fops = { 648df330515SLaurent Pinchart .owner = THIS_MODULE, 649df330515SLaurent Pinchart .unlocked_ioctl = video_ioctl2, 650df330515SLaurent Pinchart .open = v4l2_fh_open, 651df330515SLaurent Pinchart .release = vb2_fop_release, 652df330515SLaurent Pinchart .poll = vb2_fop_poll, 653df330515SLaurent Pinchart .mmap = vb2_fop_mmap, 654df330515SLaurent Pinchart }; 655df330515SLaurent Pinchart 656df330515SLaurent Pinchart /* ----------------------------------------------------------------------------- 657df330515SLaurent Pinchart * Xilinx Video DMA Core 658df330515SLaurent Pinchart */ 659df330515SLaurent Pinchart 660df330515SLaurent Pinchart int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, 661df330515SLaurent Pinchart enum v4l2_buf_type type, unsigned int port) 662df330515SLaurent Pinchart { 663550f45bcSDan Carpenter char name[16]; 664df330515SLaurent Pinchart int ret; 665df330515SLaurent Pinchart 666df330515SLaurent Pinchart dma->xdev = xdev; 667df330515SLaurent Pinchart dma->port = port; 668df330515SLaurent Pinchart mutex_init(&dma->lock); 669df330515SLaurent Pinchart mutex_init(&dma->pipe.lock); 670df330515SLaurent Pinchart INIT_LIST_HEAD(&dma->queued_bufs); 671df330515SLaurent Pinchart spin_lock_init(&dma->queued_lock); 672df330515SLaurent Pinchart 673df330515SLaurent Pinchart dma->fmtinfo = xvip_get_format_by_fourcc(XVIP_DMA_DEF_FORMAT); 674df330515SLaurent Pinchart dma->format.pixelformat = dma->fmtinfo->fourcc; 675df330515SLaurent Pinchart dma->format.colorspace = V4L2_COLORSPACE_SRGB; 676df330515SLaurent Pinchart dma->format.field = V4L2_FIELD_NONE; 677df330515SLaurent Pinchart dma->format.width = XVIP_DMA_DEF_WIDTH; 678df330515SLaurent Pinchart dma->format.height = XVIP_DMA_DEF_HEIGHT; 679df330515SLaurent Pinchart dma->format.bytesperline = dma->format.width * dma->fmtinfo->bpp; 680df330515SLaurent Pinchart dma->format.sizeimage = dma->format.bytesperline * dma->format.height; 681df330515SLaurent Pinchart 682df330515SLaurent Pinchart /* Initialize the media entity... */ 683df330515SLaurent Pinchart dma->pad.flags = type == V4L2_BUF_TYPE_VIDEO_CAPTURE 684df330515SLaurent Pinchart ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; 685df330515SLaurent Pinchart 686ab22e77cSMauro Carvalho Chehab ret = media_entity_pads_init(&dma->video.entity, 1, &dma->pad); 687df330515SLaurent Pinchart if (ret < 0) 688df330515SLaurent Pinchart goto error; 689df330515SLaurent Pinchart 690df330515SLaurent Pinchart /* ... and the video node... */ 691df330515SLaurent Pinchart dma->video.fops = &xvip_dma_fops; 692df330515SLaurent Pinchart dma->video.v4l2_dev = &xdev->v4l2_dev; 693df330515SLaurent Pinchart dma->video.queue = &dma->queue; 694df330515SLaurent Pinchart snprintf(dma->video.name, sizeof(dma->video.name), "%s %s %u", 695df330515SLaurent Pinchart xdev->dev->of_node->name, 696df330515SLaurent Pinchart type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? "output" : "input", 697df330515SLaurent Pinchart port); 698df330515SLaurent Pinchart dma->video.vfl_type = VFL_TYPE_GRABBER; 699df330515SLaurent Pinchart dma->video.vfl_dir = type == V4L2_BUF_TYPE_VIDEO_CAPTURE 700df330515SLaurent Pinchart ? VFL_DIR_RX : VFL_DIR_TX; 701df330515SLaurent Pinchart dma->video.release = video_device_release_empty; 702df330515SLaurent Pinchart dma->video.ioctl_ops = &xvip_dma_ioctl_ops; 703df330515SLaurent Pinchart dma->video.lock = &dma->lock; 704df330515SLaurent Pinchart 705df330515SLaurent Pinchart video_set_drvdata(&dma->video, dma); 706df330515SLaurent Pinchart 707df330515SLaurent Pinchart /* ... and the buffers queue... */ 708df330515SLaurent Pinchart /* Don't enable VB2_READ and VB2_WRITE, as using the read() and write() 709df330515SLaurent Pinchart * V4L2 APIs would be inefficient. Testing on the command line with a 710df330515SLaurent Pinchart * 'cat /dev/video?' thus won't be possible, but given that the driver 711df330515SLaurent Pinchart * anyway requires a test tool to setup the pipeline before any video 712df330515SLaurent Pinchart * stream can be started, requiring a specific V4L2 test tool as well 713df330515SLaurent Pinchart * instead of 'cat' isn't really a drawback. 714df330515SLaurent Pinchart */ 715df330515SLaurent Pinchart dma->queue.type = type; 716df330515SLaurent Pinchart dma->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; 717df330515SLaurent Pinchart dma->queue.lock = &dma->lock; 718df330515SLaurent Pinchart dma->queue.drv_priv = dma; 719df330515SLaurent Pinchart dma->queue.buf_struct_size = sizeof(struct xvip_dma_buffer); 720df330515SLaurent Pinchart dma->queue.ops = &xvip_dma_queue_qops; 721df330515SLaurent Pinchart dma->queue.mem_ops = &vb2_dma_contig_memops; 722df330515SLaurent Pinchart dma->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC 723df330515SLaurent Pinchart | V4L2_BUF_FLAG_TSTAMP_SRC_EOF; 724dce57314SHans Verkuil dma->queue.dev = dma->xdev->dev; 725df330515SLaurent Pinchart ret = vb2_queue_init(&dma->queue); 726df330515SLaurent Pinchart if (ret < 0) { 727df330515SLaurent Pinchart dev_err(dma->xdev->dev, "failed to initialize VB2 queue\n"); 728df330515SLaurent Pinchart goto error; 729df330515SLaurent Pinchart } 730df330515SLaurent Pinchart 731df330515SLaurent Pinchart /* ... and the DMA channel. */ 732550f45bcSDan Carpenter snprintf(name, sizeof(name), "port%u", port); 733df330515SLaurent Pinchart dma->dma = dma_request_slave_channel(dma->xdev->dev, name); 734df330515SLaurent Pinchart if (dma->dma == NULL) { 735df330515SLaurent Pinchart dev_err(dma->xdev->dev, "no VDMA channel found\n"); 736df330515SLaurent Pinchart ret = -ENODEV; 737df330515SLaurent Pinchart goto error; 738df330515SLaurent Pinchart } 739df330515SLaurent Pinchart 740df330515SLaurent Pinchart dma->align = 1 << dma->dma->device->copy_align; 741df330515SLaurent Pinchart 742df330515SLaurent Pinchart ret = video_register_device(&dma->video, VFL_TYPE_GRABBER, -1); 743df330515SLaurent Pinchart if (ret < 0) { 744df330515SLaurent Pinchart dev_err(dma->xdev->dev, "failed to register video device\n"); 745df330515SLaurent Pinchart goto error; 746df330515SLaurent Pinchart } 747df330515SLaurent Pinchart 748df330515SLaurent Pinchart return 0; 749df330515SLaurent Pinchart 750df330515SLaurent Pinchart error: 751df330515SLaurent Pinchart xvip_dma_cleanup(dma); 752df330515SLaurent Pinchart return ret; 753df330515SLaurent Pinchart } 754df330515SLaurent Pinchart 755df330515SLaurent Pinchart void xvip_dma_cleanup(struct xvip_dma *dma) 756df330515SLaurent Pinchart { 757df330515SLaurent Pinchart if (video_is_registered(&dma->video)) 758df330515SLaurent Pinchart video_unregister_device(&dma->video); 759df330515SLaurent Pinchart 760df330515SLaurent Pinchart if (dma->dma) 761df330515SLaurent Pinchart dma_release_channel(dma->dma); 762df330515SLaurent Pinchart 763df330515SLaurent Pinchart media_entity_cleanup(&dma->video.entity); 764df330515SLaurent Pinchart 765df330515SLaurent Pinchart mutex_destroy(&dma->lock); 766df330515SLaurent Pinchart mutex_destroy(&dma->pipe.lock); 767df330515SLaurent Pinchart } 768