linux/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
   4 * All rights reserved.
   5 * Author: Yong Deng <yong.deng@magewell.com>
   6 */
   7
   8#include <linux/of.h>
   9
  10#include <media/v4l2-device.h>
  11#include <media/v4l2-event.h>
  12#include <media/v4l2-ioctl.h>
  13#include <media/v4l2-mc.h>
  14#include <media/videobuf2-dma-contig.h>
  15#include <media/videobuf2-v4l2.h>
  16
  17#include "sun6i_csi.h"
  18#include "sun6i_video.h"
  19
  20/* This is got from BSP sources. */
  21#define MIN_WIDTH       (32)
  22#define MIN_HEIGHT      (32)
  23#define MAX_WIDTH       (4800)
  24#define MAX_HEIGHT      (4800)
  25
  26struct sun6i_csi_buffer {
  27        struct vb2_v4l2_buffer          vb;
  28        struct list_head                list;
  29
  30        dma_addr_t                      dma_addr;
  31        bool                            queued_to_csi;
  32};
  33
  34static const u32 supported_pixformats[] = {
  35        V4L2_PIX_FMT_SBGGR8,
  36        V4L2_PIX_FMT_SGBRG8,
  37        V4L2_PIX_FMT_SGRBG8,
  38        V4L2_PIX_FMT_SRGGB8,
  39        V4L2_PIX_FMT_SBGGR10,
  40        V4L2_PIX_FMT_SGBRG10,
  41        V4L2_PIX_FMT_SGRBG10,
  42        V4L2_PIX_FMT_SRGGB10,
  43        V4L2_PIX_FMT_SBGGR12,
  44        V4L2_PIX_FMT_SGBRG12,
  45        V4L2_PIX_FMT_SGRBG12,
  46        V4L2_PIX_FMT_SRGGB12,
  47        V4L2_PIX_FMT_YUYV,
  48        V4L2_PIX_FMT_YVYU,
  49        V4L2_PIX_FMT_UYVY,
  50        V4L2_PIX_FMT_VYUY,
  51        V4L2_PIX_FMT_HM12,
  52        V4L2_PIX_FMT_NV12,
  53        V4L2_PIX_FMT_NV21,
  54        V4L2_PIX_FMT_YUV420,
  55        V4L2_PIX_FMT_YVU420,
  56        V4L2_PIX_FMT_NV16,
  57        V4L2_PIX_FMT_NV61,
  58        V4L2_PIX_FMT_YUV422P,
  59        V4L2_PIX_FMT_RGB565,
  60        V4L2_PIX_FMT_RGB565X,
  61        V4L2_PIX_FMT_JPEG,
  62};
  63
  64static bool is_pixformat_valid(unsigned int pixformat)
  65{
  66        unsigned int i;
  67
  68        for (i = 0; i < ARRAY_SIZE(supported_pixformats); i++)
  69                if (supported_pixformats[i] == pixformat)
  70                        return true;
  71
  72        return false;
  73}
  74
  75static struct v4l2_subdev *
  76sun6i_video_remote_subdev(struct sun6i_video *video, u32 *pad)
  77{
  78        struct media_pad *remote;
  79
  80        remote = media_entity_remote_pad(&video->pad);
  81
  82        if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
  83                return NULL;
  84
  85        if (pad)
  86                *pad = remote->index;
  87
  88        return media_entity_to_v4l2_subdev(remote->entity);
  89}
  90
  91static int sun6i_video_queue_setup(struct vb2_queue *vq,
  92                                   unsigned int *nbuffers,
  93                                   unsigned int *nplanes,
  94                                   unsigned int sizes[],
  95                                   struct device *alloc_devs[])
  96{
  97        struct sun6i_video *video = vb2_get_drv_priv(vq);
  98        unsigned int size = video->fmt.fmt.pix.sizeimage;
  99
 100        if (*nplanes)
 101                return sizes[0] < size ? -EINVAL : 0;
 102
 103        *nplanes = 1;
 104        sizes[0] = size;
 105
 106        return 0;
 107}
 108
 109static int sun6i_video_buffer_prepare(struct vb2_buffer *vb)
 110{
 111        struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
 112        struct sun6i_csi_buffer *buf =
 113                        container_of(vbuf, struct sun6i_csi_buffer, vb);
 114        struct sun6i_video *video = vb2_get_drv_priv(vb->vb2_queue);
 115        unsigned long size = video->fmt.fmt.pix.sizeimage;
 116
 117        if (vb2_plane_size(vb, 0) < size) {
 118                v4l2_err(video->vdev.v4l2_dev, "buffer too small (%lu < %lu)\n",
 119                         vb2_plane_size(vb, 0), size);
 120                return -EINVAL;
 121        }
 122
 123        vb2_set_plane_payload(vb, 0, size);
 124
 125        buf->dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
 126
 127        vbuf->field = video->fmt.fmt.pix.field;
 128
 129        return 0;
 130}
 131
 132static int sun6i_video_start_streaming(struct vb2_queue *vq, unsigned int count)
 133{
 134        struct sun6i_video *video = vb2_get_drv_priv(vq);
 135        struct sun6i_csi_buffer *buf;
 136        struct sun6i_csi_buffer *next_buf;
 137        struct sun6i_csi_config config;
 138        struct v4l2_subdev *subdev;
 139        unsigned long flags;
 140        int ret;
 141
 142        video->sequence = 0;
 143
 144        ret = media_pipeline_start(&video->vdev.entity, &video->vdev.pipe);
 145        if (ret < 0)
 146                goto clear_dma_queue;
 147
 148        if (video->mbus_code == 0) {
 149                ret = -EINVAL;
 150                goto stop_media_pipeline;
 151        }
 152
 153        subdev = sun6i_video_remote_subdev(video, NULL);
 154        if (!subdev) {
 155                ret = -EINVAL;
 156                goto stop_media_pipeline;
 157        }
 158
 159        config.pixelformat = video->fmt.fmt.pix.pixelformat;
 160        config.code = video->mbus_code;
 161        config.field = video->fmt.fmt.pix.field;
 162        config.width = video->fmt.fmt.pix.width;
 163        config.height = video->fmt.fmt.pix.height;
 164
 165        ret = sun6i_csi_update_config(video->csi, &config);
 166        if (ret < 0)
 167                goto stop_media_pipeline;
 168
 169        spin_lock_irqsave(&video->dma_queue_lock, flags);
 170
 171        buf = list_first_entry(&video->dma_queue,
 172                               struct sun6i_csi_buffer, list);
 173        buf->queued_to_csi = true;
 174        sun6i_csi_update_buf_addr(video->csi, buf->dma_addr);
 175
 176        sun6i_csi_set_stream(video->csi, true);
 177
 178        /*
 179         * CSI will lookup the next dma buffer for next frame before the
 180         * the current frame done IRQ triggered. This is not documented
 181         * but reported by Ondřej Jirman.
 182         * The BSP code has workaround for this too. It skip to mark the
 183         * first buffer as frame done for VB2 and pass the second buffer
 184         * to CSI in the first frame done ISR call. Then in second frame
 185         * done ISR call, it mark the first buffer as frame done for VB2
 186         * and pass the third buffer to CSI. And so on. The bad thing is
 187         * that the first buffer will be written twice and the first frame
 188         * is dropped even the queued buffer is sufficient.
 189         * So, I make some improvement here. Pass the next buffer to CSI
 190         * just follow starting the CSI. In this case, the first frame
 191         * will be stored in first buffer, second frame in second buffer.
 192         * This method is used to avoid dropping the first frame, it
 193         * would also drop frame when lacking of queued buffer.
 194         */
 195        next_buf = list_next_entry(buf, list);
 196        next_buf->queued_to_csi = true;
 197        sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr);
 198
 199        spin_unlock_irqrestore(&video->dma_queue_lock, flags);
 200
 201        ret = v4l2_subdev_call(subdev, video, s_stream, 1);
 202        if (ret && ret != -ENOIOCTLCMD)
 203                goto stop_csi_stream;
 204
 205        return 0;
 206
 207stop_csi_stream:
 208        sun6i_csi_set_stream(video->csi, false);
 209stop_media_pipeline:
 210        media_pipeline_stop(&video->vdev.entity);
 211clear_dma_queue:
 212        spin_lock_irqsave(&video->dma_queue_lock, flags);
 213        list_for_each_entry(buf, &video->dma_queue, list)
 214                vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
 215        INIT_LIST_HEAD(&video->dma_queue);
 216        spin_unlock_irqrestore(&video->dma_queue_lock, flags);
 217
 218        return ret;
 219}
 220
 221static void sun6i_video_stop_streaming(struct vb2_queue *vq)
 222{
 223        struct sun6i_video *video = vb2_get_drv_priv(vq);
 224        struct v4l2_subdev *subdev;
 225        unsigned long flags;
 226        struct sun6i_csi_buffer *buf;
 227
 228        subdev = sun6i_video_remote_subdev(video, NULL);
 229        if (subdev)
 230                v4l2_subdev_call(subdev, video, s_stream, 0);
 231
 232        sun6i_csi_set_stream(video->csi, false);
 233
 234        media_pipeline_stop(&video->vdev.entity);
 235
 236        /* Release all active buffers */
 237        spin_lock_irqsave(&video->dma_queue_lock, flags);
 238        list_for_each_entry(buf, &video->dma_queue, list)
 239                vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
 240        INIT_LIST_HEAD(&video->dma_queue);
 241        spin_unlock_irqrestore(&video->dma_queue_lock, flags);
 242}
 243
 244static void sun6i_video_buffer_queue(struct vb2_buffer *vb)
 245{
 246        struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
 247        struct sun6i_csi_buffer *buf =
 248                        container_of(vbuf, struct sun6i_csi_buffer, vb);
 249        struct sun6i_video *video = vb2_get_drv_priv(vb->vb2_queue);
 250        unsigned long flags;
 251
 252        spin_lock_irqsave(&video->dma_queue_lock, flags);
 253        buf->queued_to_csi = false;
 254        list_add_tail(&buf->list, &video->dma_queue);
 255        spin_unlock_irqrestore(&video->dma_queue_lock, flags);
 256}
 257
 258void sun6i_video_frame_done(struct sun6i_video *video)
 259{
 260        struct sun6i_csi_buffer *buf;
 261        struct sun6i_csi_buffer *next_buf;
 262        struct vb2_v4l2_buffer *vbuf;
 263
 264        spin_lock(&video->dma_queue_lock);
 265
 266        buf = list_first_entry(&video->dma_queue,
 267                               struct sun6i_csi_buffer, list);
 268        if (list_is_last(&buf->list, &video->dma_queue)) {
 269                dev_dbg(video->csi->dev, "Frame dropped!\n");
 270                goto unlock;
 271        }
 272
 273        next_buf = list_next_entry(buf, list);
 274        /* If a new buffer (#next_buf) had not been queued to CSI, the old
 275         * buffer (#buf) is still holding by CSI for storing the next
 276         * frame. So, we queue a new buffer (#next_buf) to CSI then wait
 277         * for next ISR call.
 278         */
 279        if (!next_buf->queued_to_csi) {
 280                next_buf->queued_to_csi = true;
 281                sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr);
 282                dev_dbg(video->csi->dev, "Frame dropped!\n");
 283                goto unlock;
 284        }
 285
 286        list_del(&buf->list);
 287        vbuf = &buf->vb;
 288        vbuf->vb2_buf.timestamp = ktime_get_ns();
 289        vbuf->sequence = video->sequence;
 290        vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
 291
 292        /* Prepare buffer for next frame but one.  */
 293        if (!list_is_last(&next_buf->list, &video->dma_queue)) {
 294                next_buf = list_next_entry(next_buf, list);
 295                next_buf->queued_to_csi = true;
 296                sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr);
 297        } else {
 298                dev_dbg(video->csi->dev, "Next frame will be dropped!\n");
 299        }
 300
 301unlock:
 302        video->sequence++;
 303        spin_unlock(&video->dma_queue_lock);
 304}
 305
 306static const struct vb2_ops sun6i_csi_vb2_ops = {
 307        .queue_setup            = sun6i_video_queue_setup,
 308        .wait_prepare           = vb2_ops_wait_prepare,
 309        .wait_finish            = vb2_ops_wait_finish,
 310        .buf_prepare            = sun6i_video_buffer_prepare,
 311        .start_streaming        = sun6i_video_start_streaming,
 312        .stop_streaming         = sun6i_video_stop_streaming,
 313        .buf_queue              = sun6i_video_buffer_queue,
 314};
 315
 316static int vidioc_querycap(struct file *file, void *priv,
 317                           struct v4l2_capability *cap)
 318{
 319        struct sun6i_video *video = video_drvdata(file);
 320
 321        strscpy(cap->driver, "sun6i-video", sizeof(cap->driver));
 322        strscpy(cap->card, video->vdev.name, sizeof(cap->card));
 323        snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
 324                 video->csi->dev->of_node->name);
 325
 326        return 0;
 327}
 328
 329static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
 330                                   struct v4l2_fmtdesc *f)
 331{
 332        u32 index = f->index;
 333
 334        if (index >= ARRAY_SIZE(supported_pixformats))
 335                return -EINVAL;
 336
 337        f->pixelformat = supported_pixformats[index];
 338
 339        return 0;
 340}
 341
 342static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
 343                                struct v4l2_format *fmt)
 344{
 345        struct sun6i_video *video = video_drvdata(file);
 346
 347        *fmt = video->fmt;
 348
 349        return 0;
 350}
 351
 352static int sun6i_video_try_fmt(struct sun6i_video *video,
 353                               struct v4l2_format *f)
 354{
 355        struct v4l2_pix_format *pixfmt = &f->fmt.pix;
 356        int bpp;
 357
 358        if (!is_pixformat_valid(pixfmt->pixelformat))
 359                pixfmt->pixelformat = supported_pixformats[0];
 360
 361        v4l_bound_align_image(&pixfmt->width, MIN_WIDTH, MAX_WIDTH, 1,
 362                              &pixfmt->height, MIN_HEIGHT, MAX_WIDTH, 1, 1);
 363
 364        bpp = sun6i_csi_get_bpp(pixfmt->pixelformat);
 365        pixfmt->bytesperline = (pixfmt->width * bpp) >> 3;
 366        pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
 367
 368        if (pixfmt->field == V4L2_FIELD_ANY)
 369                pixfmt->field = V4L2_FIELD_NONE;
 370
 371        pixfmt->colorspace = V4L2_COLORSPACE_RAW;
 372        pixfmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
 373        pixfmt->quantization = V4L2_QUANTIZATION_DEFAULT;
 374        pixfmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
 375
 376        return 0;
 377}
 378
 379static int sun6i_video_set_fmt(struct sun6i_video *video, struct v4l2_format *f)
 380{
 381        int ret;
 382
 383        ret = sun6i_video_try_fmt(video, f);
 384        if (ret)
 385                return ret;
 386
 387        video->fmt = *f;
 388
 389        return 0;
 390}
 391
 392static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
 393                                struct v4l2_format *f)
 394{
 395        struct sun6i_video *video = video_drvdata(file);
 396
 397        if (vb2_is_busy(&video->vb2_vidq))
 398                return -EBUSY;
 399
 400        return sun6i_video_set_fmt(video, f);
 401}
 402
 403static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
 404                                  struct v4l2_format *f)
 405{
 406        struct sun6i_video *video = video_drvdata(file);
 407
 408        return sun6i_video_try_fmt(video, f);
 409}
 410
 411static int vidioc_enum_input(struct file *file, void *fh,
 412                             struct v4l2_input *inp)
 413{
 414        if (inp->index != 0)
 415                return -EINVAL;
 416
 417        strscpy(inp->name, "camera", sizeof(inp->name));
 418        inp->type = V4L2_INPUT_TYPE_CAMERA;
 419
 420        return 0;
 421}
 422
 423static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
 424{
 425        *i = 0;
 426
 427        return 0;
 428}
 429
 430static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
 431{
 432        if (i != 0)
 433                return -EINVAL;
 434
 435        return 0;
 436}
 437
 438static const struct v4l2_ioctl_ops sun6i_video_ioctl_ops = {
 439        .vidioc_querycap                = vidioc_querycap,
 440        .vidioc_enum_fmt_vid_cap        = vidioc_enum_fmt_vid_cap,
 441        .vidioc_g_fmt_vid_cap           = vidioc_g_fmt_vid_cap,
 442        .vidioc_s_fmt_vid_cap           = vidioc_s_fmt_vid_cap,
 443        .vidioc_try_fmt_vid_cap         = vidioc_try_fmt_vid_cap,
 444
 445        .vidioc_enum_input              = vidioc_enum_input,
 446        .vidioc_s_input                 = vidioc_s_input,
 447        .vidioc_g_input                 = vidioc_g_input,
 448
 449        .vidioc_reqbufs                 = vb2_ioctl_reqbufs,
 450        .vidioc_querybuf                = vb2_ioctl_querybuf,
 451        .vidioc_qbuf                    = vb2_ioctl_qbuf,
 452        .vidioc_expbuf                  = vb2_ioctl_expbuf,
 453        .vidioc_dqbuf                   = vb2_ioctl_dqbuf,
 454        .vidioc_create_bufs             = vb2_ioctl_create_bufs,
 455        .vidioc_prepare_buf             = vb2_ioctl_prepare_buf,
 456        .vidioc_streamon                = vb2_ioctl_streamon,
 457        .vidioc_streamoff               = vb2_ioctl_streamoff,
 458
 459        .vidioc_log_status              = v4l2_ctrl_log_status,
 460        .vidioc_subscribe_event         = v4l2_ctrl_subscribe_event,
 461        .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
 462};
 463
 464/* -----------------------------------------------------------------------------
 465 * V4L2 file operations
 466 */
 467static int sun6i_video_open(struct file *file)
 468{
 469        struct sun6i_video *video = video_drvdata(file);
 470        int ret;
 471
 472        if (mutex_lock_interruptible(&video->lock))
 473                return -ERESTARTSYS;
 474
 475        ret = v4l2_fh_open(file);
 476        if (ret < 0)
 477                goto unlock;
 478
 479        ret = v4l2_pipeline_pm_get(&video->vdev.entity);
 480        if (ret < 0)
 481                goto fh_release;
 482
 483        /* check if already powered */
 484        if (!v4l2_fh_is_singular_file(file)) {
 485                ret = -EBUSY;
 486                goto unlock;
 487        }
 488
 489        ret = sun6i_csi_set_power(video->csi, true);
 490        if (ret < 0)
 491                goto fh_release;
 492
 493        mutex_unlock(&video->lock);
 494        return 0;
 495
 496fh_release:
 497        v4l2_fh_release(file);
 498unlock:
 499        mutex_unlock(&video->lock);
 500        return ret;
 501}
 502
 503static int sun6i_video_close(struct file *file)
 504{
 505        struct sun6i_video *video = video_drvdata(file);
 506        bool last_fh;
 507
 508        mutex_lock(&video->lock);
 509
 510        last_fh = v4l2_fh_is_singular_file(file);
 511
 512        _vb2_fop_release(file, NULL);
 513
 514        v4l2_pipeline_pm_put(&video->vdev.entity);
 515
 516        if (last_fh)
 517                sun6i_csi_set_power(video->csi, false);
 518
 519        mutex_unlock(&video->lock);
 520
 521        return 0;
 522}
 523
 524static const struct v4l2_file_operations sun6i_video_fops = {
 525        .owner          = THIS_MODULE,
 526        .open           = sun6i_video_open,
 527        .release        = sun6i_video_close,
 528        .unlocked_ioctl = video_ioctl2,
 529        .mmap           = vb2_fop_mmap,
 530        .poll           = vb2_fop_poll
 531};
 532
 533/* -----------------------------------------------------------------------------
 534 * Media Operations
 535 */
 536static int sun6i_video_link_validate_get_format(struct media_pad *pad,
 537                                                struct v4l2_subdev_format *fmt)
 538{
 539        if (is_media_entity_v4l2_subdev(pad->entity)) {
 540                struct v4l2_subdev *sd =
 541                                media_entity_to_v4l2_subdev(pad->entity);
 542
 543                fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
 544                fmt->pad = pad->index;
 545                return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
 546        }
 547
 548        return -EINVAL;
 549}
 550
 551static int sun6i_video_link_validate(struct media_link *link)
 552{
 553        struct video_device *vdev = container_of(link->sink->entity,
 554                                                 struct video_device, entity);
 555        struct sun6i_video *video = video_get_drvdata(vdev);
 556        struct v4l2_subdev_format source_fmt;
 557        int ret;
 558
 559        video->mbus_code = 0;
 560
 561        if (!media_entity_remote_pad(link->sink->entity->pads)) {
 562                dev_info(video->csi->dev,
 563                         "video node %s pad not connected\n", vdev->name);
 564                return -ENOLINK;
 565        }
 566
 567        ret = sun6i_video_link_validate_get_format(link->source, &source_fmt);
 568        if (ret < 0)
 569                return ret;
 570
 571        if (!sun6i_csi_is_format_supported(video->csi,
 572                                           video->fmt.fmt.pix.pixelformat,
 573                                           source_fmt.format.code)) {
 574                dev_err(video->csi->dev,
 575                        "Unsupported pixformat: 0x%x with mbus code: 0x%x!\n",
 576                        video->fmt.fmt.pix.pixelformat,
 577                        source_fmt.format.code);
 578                return -EPIPE;
 579        }
 580
 581        if (source_fmt.format.width != video->fmt.fmt.pix.width ||
 582            source_fmt.format.height != video->fmt.fmt.pix.height) {
 583                dev_err(video->csi->dev,
 584                        "Wrong width or height %ux%u (%ux%u expected)\n",
 585                        video->fmt.fmt.pix.width, video->fmt.fmt.pix.height,
 586                        source_fmt.format.width, source_fmt.format.height);
 587                return -EPIPE;
 588        }
 589
 590        video->mbus_code = source_fmt.format.code;
 591
 592        return 0;
 593}
 594
 595static const struct media_entity_operations sun6i_video_media_ops = {
 596        .link_validate = sun6i_video_link_validate
 597};
 598
 599int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi,
 600                     const char *name)
 601{
 602        struct video_device *vdev = &video->vdev;
 603        struct vb2_queue *vidq = &video->vb2_vidq;
 604        struct v4l2_format fmt = { 0 };
 605        int ret;
 606
 607        video->csi = csi;
 608
 609        /* Initialize the media entity... */
 610        video->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
 611        vdev->entity.ops = &sun6i_video_media_ops;
 612        ret = media_entity_pads_init(&vdev->entity, 1, &video->pad);
 613        if (ret < 0)
 614                return ret;
 615
 616        mutex_init(&video->lock);
 617
 618        INIT_LIST_HEAD(&video->dma_queue);
 619        spin_lock_init(&video->dma_queue_lock);
 620
 621        video->sequence = 0;
 622
 623        /* Setup default format */
 624        fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 625        fmt.fmt.pix.pixelformat = supported_pixformats[0];
 626        fmt.fmt.pix.width = 1280;
 627        fmt.fmt.pix.height = 720;
 628        fmt.fmt.pix.field = V4L2_FIELD_NONE;
 629        sun6i_video_set_fmt(video, &fmt);
 630
 631        /* Initialize videobuf2 queue */
 632        vidq->type                      = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 633        vidq->io_modes                  = VB2_MMAP | VB2_DMABUF;
 634        vidq->drv_priv                  = video;
 635        vidq->buf_struct_size           = sizeof(struct sun6i_csi_buffer);
 636        vidq->ops                       = &sun6i_csi_vb2_ops;
 637        vidq->mem_ops                   = &vb2_dma_contig_memops;
 638        vidq->timestamp_flags           = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
 639        vidq->lock                      = &video->lock;
 640        /* Make sure non-dropped frame */
 641        vidq->min_buffers_needed        = 3;
 642        vidq->dev                       = csi->dev;
 643
 644        ret = vb2_queue_init(vidq);
 645        if (ret) {
 646                v4l2_err(&csi->v4l2_dev, "vb2_queue_init failed: %d\n", ret);
 647                goto clean_entity;
 648        }
 649
 650        /* Register video device */
 651        strscpy(vdev->name, name, sizeof(vdev->name));
 652        vdev->release           = video_device_release_empty;
 653        vdev->fops              = &sun6i_video_fops;
 654        vdev->ioctl_ops         = &sun6i_video_ioctl_ops;
 655        vdev->vfl_type          = VFL_TYPE_VIDEO;
 656        vdev->vfl_dir           = VFL_DIR_RX;
 657        vdev->v4l2_dev          = &csi->v4l2_dev;
 658        vdev->queue             = vidq;
 659        vdev->lock              = &video->lock;
 660        vdev->device_caps       = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
 661        video_set_drvdata(vdev, video);
 662
 663        ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
 664        if (ret < 0) {
 665                v4l2_err(&csi->v4l2_dev,
 666                         "video_register_device failed: %d\n", ret);
 667                goto clean_entity;
 668        }
 669
 670        return 0;
 671
 672clean_entity:
 673        media_entity_cleanup(&video->vdev.entity);
 674        mutex_destroy(&video->lock);
 675        return ret;
 676}
 677
 678void sun6i_video_cleanup(struct sun6i_video *video)
 679{
 680        vb2_video_unregister_device(&video->vdev);
 681        media_entity_cleanup(&video->vdev.entity);
 682        mutex_destroy(&video->lock);
 683}
 684