linux/drivers/media/common/videobuf2/videobuf2-dma-sg.c
<<
>>
Prefs
   1/*
   2 * videobuf2-dma-sg.c - dma scatter/gather memory allocator for videobuf2
   3 *
   4 * Copyright (C) 2010 Samsung Electronics
   5 *
   6 * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/mm.h>
  15#include <linux/refcount.h>
  16#include <linux/scatterlist.h>
  17#include <linux/sched.h>
  18#include <linux/slab.h>
  19#include <linux/vmalloc.h>
  20
  21#include <media/videobuf2-v4l2.h>
  22#include <media/videobuf2-memops.h>
  23#include <media/videobuf2-dma-sg.h>
  24
  25static int debug;
  26module_param(debug, int, 0644);
  27
  28#define dprintk(level, fmt, arg...)                                     \
  29        do {                                                            \
  30                if (debug >= level)                                     \
  31                        printk(KERN_DEBUG "vb2-dma-sg: " fmt, ## arg);  \
  32        } while (0)
  33
  34struct vb2_dma_sg_buf {
  35        struct device                   *dev;
  36        void                            *vaddr;
  37        struct page                     **pages;
  38        struct frame_vector             *vec;
  39        int                             offset;
  40        enum dma_data_direction         dma_dir;
  41        struct sg_table                 sg_table;
  42        /*
  43         * This will point to sg_table when used with the MMAP or USERPTR
  44         * memory model, and to the dma_buf sglist when used with the
  45         * DMABUF memory model.
  46         */
  47        struct sg_table                 *dma_sgt;
  48        size_t                          size;
  49        unsigned int                    num_pages;
  50        refcount_t                      refcount;
  51        struct vb2_vmarea_handler       handler;
  52
  53        struct dma_buf_attachment       *db_attach;
  54};
  55
  56static void vb2_dma_sg_put(void *buf_priv);
  57
  58static int vb2_dma_sg_alloc_compacted(struct vb2_dma_sg_buf *buf,
  59                gfp_t gfp_flags)
  60{
  61        unsigned int last_page = 0;
  62        unsigned long size = buf->size;
  63
  64        while (size > 0) {
  65                struct page *pages;
  66                int order;
  67                int i;
  68
  69                order = get_order(size);
  70                /* Don't over allocate*/
  71                if ((PAGE_SIZE << order) > size)
  72                        order--;
  73
  74                pages = NULL;
  75                while (!pages) {
  76                        pages = alloc_pages(GFP_KERNEL | __GFP_ZERO |
  77                                        __GFP_NOWARN | gfp_flags, order);
  78                        if (pages)
  79                                break;
  80
  81                        if (order == 0) {
  82                                while (last_page--)
  83                                        __free_page(buf->pages[last_page]);
  84                                return -ENOMEM;
  85                        }
  86                        order--;
  87                }
  88
  89                split_page(pages, order);
  90                for (i = 0; i < (1 << order); i++)
  91                        buf->pages[last_page++] = &pages[i];
  92
  93                size -= PAGE_SIZE << order;
  94        }
  95
  96        return 0;
  97}
  98
  99static void *vb2_dma_sg_alloc(struct device *dev, unsigned long dma_attrs,
 100                              unsigned long size, enum dma_data_direction dma_dir,
 101                              gfp_t gfp_flags)
 102{
 103        struct vb2_dma_sg_buf *buf;
 104        struct sg_table *sgt;
 105        int ret;
 106        int num_pages;
 107
 108        if (WARN_ON(!dev))
 109                return ERR_PTR(-EINVAL);
 110
 111        buf = kzalloc(sizeof *buf, GFP_KERNEL);
 112        if (!buf)
 113                return ERR_PTR(-ENOMEM);
 114
 115        buf->vaddr = NULL;
 116        buf->dma_dir = dma_dir;
 117        buf->offset = 0;
 118        buf->size = size;
 119        /* size is already page aligned */
 120        buf->num_pages = size >> PAGE_SHIFT;
 121        buf->dma_sgt = &buf->sg_table;
 122
 123        buf->pages = kvmalloc_array(buf->num_pages, sizeof(struct page *),
 124                                    GFP_KERNEL | __GFP_ZERO);
 125        if (!buf->pages)
 126                goto fail_pages_array_alloc;
 127
 128        ret = vb2_dma_sg_alloc_compacted(buf, gfp_flags);
 129        if (ret)
 130                goto fail_pages_alloc;
 131
 132        ret = sg_alloc_table_from_pages(buf->dma_sgt, buf->pages,
 133                        buf->num_pages, 0, size, GFP_KERNEL);
 134        if (ret)
 135                goto fail_table_alloc;
 136
 137        /* Prevent the device from being released while the buffer is used */
 138        buf->dev = get_device(dev);
 139
 140        sgt = &buf->sg_table;
 141        /*
 142         * No need to sync to the device, this will happen later when the
 143         * prepare() memop is called.
 144         */
 145        sgt->nents = dma_map_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents,
 146                                      buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC);
 147        if (!sgt->nents)
 148                goto fail_map;
 149
 150        buf->handler.refcount = &buf->refcount;
 151        buf->handler.put = vb2_dma_sg_put;
 152        buf->handler.arg = buf;
 153
 154        refcount_set(&buf->refcount, 1);
 155
 156        dprintk(1, "%s: Allocated buffer of %d pages\n",
 157                __func__, buf->num_pages);
 158        return buf;
 159
 160fail_map:
 161        put_device(buf->dev);
 162        sg_free_table(buf->dma_sgt);
 163fail_table_alloc:
 164        num_pages = buf->num_pages;
 165        while (num_pages--)
 166                __free_page(buf->pages[num_pages]);
 167fail_pages_alloc:
 168        kvfree(buf->pages);
 169fail_pages_array_alloc:
 170        kfree(buf);
 171        return ERR_PTR(-ENOMEM);
 172}
 173
 174static void vb2_dma_sg_put(void *buf_priv)
 175{
 176        struct vb2_dma_sg_buf *buf = buf_priv;
 177        struct sg_table *sgt = &buf->sg_table;
 178        int i = buf->num_pages;
 179
 180        if (refcount_dec_and_test(&buf->refcount)) {
 181                dprintk(1, "%s: Freeing buffer of %d pages\n", __func__,
 182                        buf->num_pages);
 183                dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents,
 184                                   buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC);
 185                if (buf->vaddr)
 186                        vm_unmap_ram(buf->vaddr, buf->num_pages);
 187                sg_free_table(buf->dma_sgt);
 188                while (--i >= 0)
 189                        __free_page(buf->pages[i]);
 190                kvfree(buf->pages);
 191                put_device(buf->dev);
 192                kfree(buf);
 193        }
 194}
 195
 196static void vb2_dma_sg_prepare(void *buf_priv)
 197{
 198        struct vb2_dma_sg_buf *buf = buf_priv;
 199        struct sg_table *sgt = buf->dma_sgt;
 200
 201        /* DMABUF exporter will flush the cache for us */
 202        if (buf->db_attach)
 203                return;
 204
 205        dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->orig_nents,
 206                               buf->dma_dir);
 207}
 208
 209static void vb2_dma_sg_finish(void *buf_priv)
 210{
 211        struct vb2_dma_sg_buf *buf = buf_priv;
 212        struct sg_table *sgt = buf->dma_sgt;
 213
 214        /* DMABUF exporter will flush the cache for us */
 215        if (buf->db_attach)
 216                return;
 217
 218        dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
 219}
 220
 221static void *vb2_dma_sg_get_userptr(struct device *dev, unsigned long vaddr,
 222                                    unsigned long size,
 223                                    enum dma_data_direction dma_dir)
 224{
 225        struct vb2_dma_sg_buf *buf;
 226        struct sg_table *sgt;
 227        struct frame_vector *vec;
 228
 229        if (WARN_ON(!dev))
 230                return ERR_PTR(-EINVAL);
 231
 232        buf = kzalloc(sizeof *buf, GFP_KERNEL);
 233        if (!buf)
 234                return ERR_PTR(-ENOMEM);
 235
 236        buf->vaddr = NULL;
 237        buf->dev = dev;
 238        buf->dma_dir = dma_dir;
 239        buf->offset = vaddr & ~PAGE_MASK;
 240        buf->size = size;
 241        buf->dma_sgt = &buf->sg_table;
 242        vec = vb2_create_framevec(vaddr, size);
 243        if (IS_ERR(vec))
 244                goto userptr_fail_pfnvec;
 245        buf->vec = vec;
 246
 247        buf->pages = frame_vector_pages(vec);
 248        if (IS_ERR(buf->pages))
 249                goto userptr_fail_sgtable;
 250        buf->num_pages = frame_vector_count(vec);
 251
 252        if (sg_alloc_table_from_pages(buf->dma_sgt, buf->pages,
 253                        buf->num_pages, buf->offset, size, 0))
 254                goto userptr_fail_sgtable;
 255
 256        sgt = &buf->sg_table;
 257        /*
 258         * No need to sync to the device, this will happen later when the
 259         * prepare() memop is called.
 260         */
 261        sgt->nents = dma_map_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents,
 262                                      buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC);
 263        if (!sgt->nents)
 264                goto userptr_fail_map;
 265
 266        return buf;
 267
 268userptr_fail_map:
 269        sg_free_table(&buf->sg_table);
 270userptr_fail_sgtable:
 271        vb2_destroy_framevec(vec);
 272userptr_fail_pfnvec:
 273        kfree(buf);
 274        return ERR_PTR(-ENOMEM);
 275}
 276
 277/*
 278 * @put_userptr: inform the allocator that a USERPTR buffer will no longer
 279 *               be used
 280 */
 281static void vb2_dma_sg_put_userptr(void *buf_priv)
 282{
 283        struct vb2_dma_sg_buf *buf = buf_priv;
 284        struct sg_table *sgt = &buf->sg_table;
 285        int i = buf->num_pages;
 286
 287        dprintk(1, "%s: Releasing userspace buffer of %d pages\n",
 288               __func__, buf->num_pages);
 289        dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir,
 290                           DMA_ATTR_SKIP_CPU_SYNC);
 291        if (buf->vaddr)
 292                vm_unmap_ram(buf->vaddr, buf->num_pages);
 293        sg_free_table(buf->dma_sgt);
 294        if (buf->dma_dir == DMA_FROM_DEVICE ||
 295            buf->dma_dir == DMA_BIDIRECTIONAL)
 296                while (--i >= 0)
 297                        set_page_dirty_lock(buf->pages[i]);
 298        vb2_destroy_framevec(buf->vec);
 299        kfree(buf);
 300}
 301
 302static void *vb2_dma_sg_vaddr(void *buf_priv)
 303{
 304        struct vb2_dma_sg_buf *buf = buf_priv;
 305
 306        BUG_ON(!buf);
 307
 308        if (!buf->vaddr) {
 309                if (buf->db_attach)
 310                        buf->vaddr = dma_buf_vmap(buf->db_attach->dmabuf);
 311                else
 312                        buf->vaddr = vm_map_ram(buf->pages,
 313                                        buf->num_pages, -1, PAGE_KERNEL);
 314        }
 315
 316        /* add offset in case userptr is not page-aligned */
 317        return buf->vaddr ? buf->vaddr + buf->offset : NULL;
 318}
 319
 320static unsigned int vb2_dma_sg_num_users(void *buf_priv)
 321{
 322        struct vb2_dma_sg_buf *buf = buf_priv;
 323
 324        return refcount_read(&buf->refcount);
 325}
 326
 327static int vb2_dma_sg_mmap(void *buf_priv, struct vm_area_struct *vma)
 328{
 329        struct vb2_dma_sg_buf *buf = buf_priv;
 330        int err;
 331
 332        if (!buf) {
 333                printk(KERN_ERR "No memory to map\n");
 334                return -EINVAL;
 335        }
 336
 337        err = vm_map_pages(vma, buf->pages, buf->num_pages);
 338        if (err) {
 339                printk(KERN_ERR "Remapping memory, error: %d\n", err);
 340                return err;
 341        }
 342
 343        /*
 344         * Use common vm_area operations to track buffer refcount.
 345         */
 346        vma->vm_private_data    = &buf->handler;
 347        vma->vm_ops             = &vb2_common_vm_ops;
 348
 349        vma->vm_ops->open(vma);
 350
 351        return 0;
 352}
 353
 354/*********************************************/
 355/*         DMABUF ops for exporters          */
 356/*********************************************/
 357
 358struct vb2_dma_sg_attachment {
 359        struct sg_table sgt;
 360        enum dma_data_direction dma_dir;
 361};
 362
 363static int vb2_dma_sg_dmabuf_ops_attach(struct dma_buf *dbuf,
 364        struct dma_buf_attachment *dbuf_attach)
 365{
 366        struct vb2_dma_sg_attachment *attach;
 367        unsigned int i;
 368        struct scatterlist *rd, *wr;
 369        struct sg_table *sgt;
 370        struct vb2_dma_sg_buf *buf = dbuf->priv;
 371        int ret;
 372
 373        attach = kzalloc(sizeof(*attach), GFP_KERNEL);
 374        if (!attach)
 375                return -ENOMEM;
 376
 377        sgt = &attach->sgt;
 378        /* Copy the buf->base_sgt scatter list to the attachment, as we can't
 379         * map the same scatter list to multiple attachments at the same time.
 380         */
 381        ret = sg_alloc_table(sgt, buf->dma_sgt->orig_nents, GFP_KERNEL);
 382        if (ret) {
 383                kfree(attach);
 384                return -ENOMEM;
 385        }
 386
 387        rd = buf->dma_sgt->sgl;
 388        wr = sgt->sgl;
 389        for (i = 0; i < sgt->orig_nents; ++i) {
 390                sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
 391                rd = sg_next(rd);
 392                wr = sg_next(wr);
 393        }
 394
 395        attach->dma_dir = DMA_NONE;
 396        dbuf_attach->priv = attach;
 397
 398        return 0;
 399}
 400
 401static void vb2_dma_sg_dmabuf_ops_detach(struct dma_buf *dbuf,
 402        struct dma_buf_attachment *db_attach)
 403{
 404        struct vb2_dma_sg_attachment *attach = db_attach->priv;
 405        struct sg_table *sgt;
 406
 407        if (!attach)
 408                return;
 409
 410        sgt = &attach->sgt;
 411
 412        /* release the scatterlist cache */
 413        if (attach->dma_dir != DMA_NONE)
 414                dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents,
 415                        attach->dma_dir);
 416        sg_free_table(sgt);
 417        kfree(attach);
 418        db_attach->priv = NULL;
 419}
 420
 421static struct sg_table *vb2_dma_sg_dmabuf_ops_map(
 422        struct dma_buf_attachment *db_attach, enum dma_data_direction dma_dir)
 423{
 424        struct vb2_dma_sg_attachment *attach = db_attach->priv;
 425        /* stealing dmabuf mutex to serialize map/unmap operations */
 426        struct mutex *lock = &db_attach->dmabuf->lock;
 427        struct sg_table *sgt;
 428
 429        mutex_lock(lock);
 430
 431        sgt = &attach->sgt;
 432        /* return previously mapped sg table */
 433        if (attach->dma_dir == dma_dir) {
 434                mutex_unlock(lock);
 435                return sgt;
 436        }
 437
 438        /* release any previous cache */
 439        if (attach->dma_dir != DMA_NONE) {
 440                dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents,
 441                        attach->dma_dir);
 442                attach->dma_dir = DMA_NONE;
 443        }
 444
 445        /* mapping to the client with new direction */
 446        sgt->nents = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents,
 447                                dma_dir);
 448        if (!sgt->nents) {
 449                pr_err("failed to map scatterlist\n");
 450                mutex_unlock(lock);
 451                return ERR_PTR(-EIO);
 452        }
 453
 454        attach->dma_dir = dma_dir;
 455
 456        mutex_unlock(lock);
 457
 458        return sgt;
 459}
 460
 461static void vb2_dma_sg_dmabuf_ops_unmap(struct dma_buf_attachment *db_attach,
 462        struct sg_table *sgt, enum dma_data_direction dma_dir)
 463{
 464        /* nothing to be done here */
 465}
 466
 467static void vb2_dma_sg_dmabuf_ops_release(struct dma_buf *dbuf)
 468{
 469        /* drop reference obtained in vb2_dma_sg_get_dmabuf */
 470        vb2_dma_sg_put(dbuf->priv);
 471}
 472
 473static void *vb2_dma_sg_dmabuf_ops_kmap(struct dma_buf *dbuf, unsigned long pgnum)
 474{
 475        struct vb2_dma_sg_buf *buf = dbuf->priv;
 476
 477        return buf->vaddr ? buf->vaddr + pgnum * PAGE_SIZE : NULL;
 478}
 479
 480static void *vb2_dma_sg_dmabuf_ops_vmap(struct dma_buf *dbuf)
 481{
 482        struct vb2_dma_sg_buf *buf = dbuf->priv;
 483
 484        return vb2_dma_sg_vaddr(buf);
 485}
 486
 487static int vb2_dma_sg_dmabuf_ops_mmap(struct dma_buf *dbuf,
 488        struct vm_area_struct *vma)
 489{
 490        return vb2_dma_sg_mmap(dbuf->priv, vma);
 491}
 492
 493static const struct dma_buf_ops vb2_dma_sg_dmabuf_ops = {
 494        .attach = vb2_dma_sg_dmabuf_ops_attach,
 495        .detach = vb2_dma_sg_dmabuf_ops_detach,
 496        .map_dma_buf = vb2_dma_sg_dmabuf_ops_map,
 497        .unmap_dma_buf = vb2_dma_sg_dmabuf_ops_unmap,
 498        .map = vb2_dma_sg_dmabuf_ops_kmap,
 499        .vmap = vb2_dma_sg_dmabuf_ops_vmap,
 500        .mmap = vb2_dma_sg_dmabuf_ops_mmap,
 501        .release = vb2_dma_sg_dmabuf_ops_release,
 502};
 503
 504static struct dma_buf *vb2_dma_sg_get_dmabuf(void *buf_priv, unsigned long flags)
 505{
 506        struct vb2_dma_sg_buf *buf = buf_priv;
 507        struct dma_buf *dbuf;
 508        DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
 509
 510        exp_info.ops = &vb2_dma_sg_dmabuf_ops;
 511        exp_info.size = buf->size;
 512        exp_info.flags = flags;
 513        exp_info.priv = buf;
 514
 515        if (WARN_ON(!buf->dma_sgt))
 516                return NULL;
 517
 518        dbuf = dma_buf_export(&exp_info);
 519        if (IS_ERR(dbuf))
 520                return NULL;
 521
 522        /* dmabuf keeps reference to vb2 buffer */
 523        refcount_inc(&buf->refcount);
 524
 525        return dbuf;
 526}
 527
 528/*********************************************/
 529/*       callbacks for DMABUF buffers        */
 530/*********************************************/
 531
 532static int vb2_dma_sg_map_dmabuf(void *mem_priv)
 533{
 534        struct vb2_dma_sg_buf *buf = mem_priv;
 535        struct sg_table *sgt;
 536
 537        if (WARN_ON(!buf->db_attach)) {
 538                pr_err("trying to pin a non attached buffer\n");
 539                return -EINVAL;
 540        }
 541
 542        if (WARN_ON(buf->dma_sgt)) {
 543                pr_err("dmabuf buffer is already pinned\n");
 544                return 0;
 545        }
 546
 547        /* get the associated scatterlist for this buffer */
 548        sgt = dma_buf_map_attachment(buf->db_attach, buf->dma_dir);
 549        if (IS_ERR(sgt)) {
 550                pr_err("Error getting dmabuf scatterlist\n");
 551                return -EINVAL;
 552        }
 553
 554        buf->dma_sgt = sgt;
 555        buf->vaddr = NULL;
 556
 557        return 0;
 558}
 559
 560static void vb2_dma_sg_unmap_dmabuf(void *mem_priv)
 561{
 562        struct vb2_dma_sg_buf *buf = mem_priv;
 563        struct sg_table *sgt = buf->dma_sgt;
 564
 565        if (WARN_ON(!buf->db_attach)) {
 566                pr_err("trying to unpin a not attached buffer\n");
 567                return;
 568        }
 569
 570        if (WARN_ON(!sgt)) {
 571                pr_err("dmabuf buffer is already unpinned\n");
 572                return;
 573        }
 574
 575        if (buf->vaddr) {
 576                dma_buf_vunmap(buf->db_attach->dmabuf, buf->vaddr);
 577                buf->vaddr = NULL;
 578        }
 579        dma_buf_unmap_attachment(buf->db_attach, sgt, buf->dma_dir);
 580
 581        buf->dma_sgt = NULL;
 582}
 583
 584static void vb2_dma_sg_detach_dmabuf(void *mem_priv)
 585{
 586        struct vb2_dma_sg_buf *buf = mem_priv;
 587
 588        /* if vb2 works correctly you should never detach mapped buffer */
 589        if (WARN_ON(buf->dma_sgt))
 590                vb2_dma_sg_unmap_dmabuf(buf);
 591
 592        /* detach this attachment */
 593        dma_buf_detach(buf->db_attach->dmabuf, buf->db_attach);
 594        kfree(buf);
 595}
 596
 597static void *vb2_dma_sg_attach_dmabuf(struct device *dev, struct dma_buf *dbuf,
 598        unsigned long size, enum dma_data_direction dma_dir)
 599{
 600        struct vb2_dma_sg_buf *buf;
 601        struct dma_buf_attachment *dba;
 602
 603        if (WARN_ON(!dev))
 604                return ERR_PTR(-EINVAL);
 605
 606        if (dbuf->size < size)
 607                return ERR_PTR(-EFAULT);
 608
 609        buf = kzalloc(sizeof(*buf), GFP_KERNEL);
 610        if (!buf)
 611                return ERR_PTR(-ENOMEM);
 612
 613        buf->dev = dev;
 614        /* create attachment for the dmabuf with the user device */
 615        dba = dma_buf_attach(dbuf, buf->dev);
 616        if (IS_ERR(dba)) {
 617                pr_err("failed to attach dmabuf\n");
 618                kfree(buf);
 619                return dba;
 620        }
 621
 622        buf->dma_dir = dma_dir;
 623        buf->size = size;
 624        buf->db_attach = dba;
 625
 626        return buf;
 627}
 628
 629static void *vb2_dma_sg_cookie(void *buf_priv)
 630{
 631        struct vb2_dma_sg_buf *buf = buf_priv;
 632
 633        return buf->dma_sgt;
 634}
 635
 636const struct vb2_mem_ops vb2_dma_sg_memops = {
 637        .alloc          = vb2_dma_sg_alloc,
 638        .put            = vb2_dma_sg_put,
 639        .get_userptr    = vb2_dma_sg_get_userptr,
 640        .put_userptr    = vb2_dma_sg_put_userptr,
 641        .prepare        = vb2_dma_sg_prepare,
 642        .finish         = vb2_dma_sg_finish,
 643        .vaddr          = vb2_dma_sg_vaddr,
 644        .mmap           = vb2_dma_sg_mmap,
 645        .num_users      = vb2_dma_sg_num_users,
 646        .get_dmabuf     = vb2_dma_sg_get_dmabuf,
 647        .map_dmabuf     = vb2_dma_sg_map_dmabuf,
 648        .unmap_dmabuf   = vb2_dma_sg_unmap_dmabuf,
 649        .attach_dmabuf  = vb2_dma_sg_attach_dmabuf,
 650        .detach_dmabuf  = vb2_dma_sg_detach_dmabuf,
 651        .cookie         = vb2_dma_sg_cookie,
 652};
 653EXPORT_SYMBOL_GPL(vb2_dma_sg_memops);
 654
 655MODULE_DESCRIPTION("dma scatter/gather memory handling routines for videobuf2");
 656MODULE_AUTHOR("Andrzej Pietrasiewicz");
 657MODULE_LICENSE("GPL");
 658