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