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