From patchwork Wed Apr 18 13:54:20 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rob Clark X-Patchwork-Id: 10746 Received: from mail.tu-berlin.de ([130.149.7.33]) by www.linuxtv.org with esmtp (Exim 4.72) (envelope-from ) id 1SKVLS-0007gg-HH for patchwork@linuxtv.org; Wed, 18 Apr 2012 15:54:54 +0200 X-tubIT-Incoming-IP: 209.132.180.67 Received: from vger.kernel.org ([209.132.180.67]) by mail.tu-berlin.de (exim-4.75/mailfrontend-4) with esmtp for id 1SKVLR-0006zz-Aw; Wed, 18 Apr 2012 15:54:54 +0200 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751245Ab2DRNy2 (ORCPT ); Wed, 18 Apr 2012 09:54:28 -0400 Received: from mail-ob0-f174.google.com ([209.85.214.174]:34148 "EHLO mail-ob0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750996Ab2DRNy1 (ORCPT ); Wed, 18 Apr 2012 09:54:27 -0400 Received: by obbta14 with SMTP id ta14so6628502obb.19 for ; Wed, 18 Apr 2012 06:54:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:x-mailer; bh=JnL3DKcxaEcW3L6T/UIGtcmVfX1NKWRPBNyC0Z98iEk=; b=exa+E24gYaePa0xbpGIDQ1pWBJ4pdOKVgDTHKKBZm4DI79s5koP5Sps9xkbsoS/v2J 92eRlsu4TGnlGAV20rdhWhmOHKFgCVCqMmpTAesjNBwwtn3PRq0G4tUkmhkHdKQZBb2o iO3+bMf9Tdf7SBtJIPU3s9g6A40JxmsACoBbT9PU/lhTNl7X4bIbk6RR+DBdS10sQZG+ 3fxpQwYI0B9jzmqqBk2k7o/V3M/vn1MhRSodBtHLWLHcV2mquq241YLvgcCf6cgb07Ob rIG36i2Abhp2/R9JMzQk2tZWBCyIPiOe3i88zkggEg1me8I8cI+P5t77Qp9Igo3ktz/5 XuPQ== Received: by 10.182.12.98 with SMTP id x2mr3166748obb.46.1334757266737; Wed, 18 Apr 2012 06:54:26 -0700 (PDT) Received: from localhost (ppp-70-129-143-41.dsl.rcsntx.swbell.net. [70.129.143.41]) by mx.google.com with ESMTPS id t5sm21621344oef.10.2012.04.18.06.54.25 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 18 Apr 2012 06:54:26 -0700 (PDT) From: Rob Clark To: linaro-mm-sig@lists.linaro.org Cc: patches@linaro.org, linux-kernel@vger.kernel.org, daniel.vetter@ffwll.ch, dri-devel@lists.freedesktop.org, linux-media@vger.kernel.org, rebecca@android.com, Rob Clark Subject: [PATCH] WIP: staging: drm/omap: dmabuf/prime mmap support Date: Wed, 18 Apr 2012 08:54:20 -0500 Message-Id: <1334757260-16351-1-git-send-email-rob.clark@linaro.org> X-Mailer: git-send-email 1.7.9.1 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-PMX-Version: 5.6.1.2065439, Antispam-Engine: 2.7.2.376379, Antispam-Data: 2012.4.18.134220 X-PMX-Spam: Gauge=IIIIIIII, Probability=8%, Report=' MULTIPLE_RCPTS 0.1, HTML_00_01 0.05, HTML_00_10 0.05, BODY_SIZE_10000_PLUS 0, DATE_TZ_NA 0, DATE_TZ_NEG_0500 0, __ANY_URI 0, __HAS_MSGID 0, __HAS_X_MAILER 0, __HAS_X_MAILING_LIST 0, __LINES_OF_YELLING 0, __MIME_TEXT_ONLY 0, __MULTIPLE_RCPTS_CC_X2 0, __SANE_MSGID 0, __SUBJ_ALPHA_END 0, __TO_MALFORMED_2 0, __TO_NO_NAME 0, __URI_NO_PATH 0, __URI_NO_WWW 0, __URI_NS ' From: Rob Clark There are still a few places to cleanup, and possibly things can be made a bit easier for individual drm drivers by some helpers in drm core. But this is enough for a proof of concept. With this, I can allocate cached buffers, fill them w/ software from userspace, and on kernel side with fault handling and PTE shootdown track which pages must be flushed to simulate coherency. Fyi, you can find my kernel branch, with this patch on top of Daniel's dmabuf mmap patch, here: https://github.com/robclark/kernel-omap4/commits/dmabuf/mmap --- drivers/staging/omapdrm/omap_drv.h | 4 + drivers/staging/omapdrm/omap_fb.c | 6 +- drivers/staging/omapdrm/omap_gem.c | 99 ++++++++++++++++++++++++++--- drivers/staging/omapdrm/omap_gem_dmabuf.c | 45 +++++++++++++ 4 files changed, 142 insertions(+), 12 deletions(-) diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h index 2b41152..5812e20 100644 --- a/drivers/staging/omapdrm/omap_drv.h +++ b/drivers/staging/omapdrm/omap_drv.h @@ -138,6 +138,8 @@ int omap_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args); int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma); +int omap_gem_mmap_obj(struct drm_gem_object *obj, + struct vm_area_struct *vma); int omap_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); int omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op); int omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op op); @@ -145,6 +147,8 @@ int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op); int omap_gem_op_async(struct drm_gem_object *obj, enum omap_gem_op op, void (*fxn)(void *arg), void *arg); int omap_gem_roll(struct drm_gem_object *obj, uint32_t roll); +int omap_gem_dma_sync(struct drm_gem_object *obj, + enum dma_data_direction dir); int omap_gem_get_paddr(struct drm_gem_object *obj, dma_addr_t *paddr, bool remap); int omap_gem_put_paddr(struct drm_gem_object *obj); diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/staging/omapdrm/omap_fb.c index 04b235b..ac32b3d 100644 --- a/drivers/staging/omapdrm/omap_fb.c +++ b/drivers/staging/omapdrm/omap_fb.c @@ -197,8 +197,10 @@ int omap_framebuffer_replace(struct drm_framebuffer *a, pa->paddr = 0; } - if (pb && !ret) - ret = omap_gem_get_paddr(pb->bo, &pb->paddr, true); + if (pb && !ret) { + ret = omap_gem_get_paddr(pb->bo, &pb->paddr, true) || + omap_gem_dma_sync(pb->bo, DMA_TO_DEVICE); + } } if (ret) { diff --git a/drivers/staging/omapdrm/omap_gem.c b/drivers/staging/omapdrm/omap_gem.c index c5ba334..be7f6c0 100644 --- a/drivers/staging/omapdrm/omap_gem.c +++ b/drivers/staging/omapdrm/omap_gem.c @@ -212,8 +212,11 @@ static DEFINE_SPINLOCK(sync_lock); /** ensure backing pages are allocated */ static int omap_gem_attach_pages(struct drm_gem_object *obj) { + struct drm_device *dev = obj->dev; struct omap_gem_object *omap_obj = to_omap_bo(obj); struct page **pages; + int i, npages = obj->size >> PAGE_SHIFT; + dma_addr_t *addrs; WARN_ON(omap_obj->pages); @@ -231,16 +234,18 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj) * DSS, GPU, etc. are not cache coherent: */ if (omap_obj->flags & (OMAP_BO_WC|OMAP_BO_UNCACHED)) { - int i, npages = obj->size >> PAGE_SHIFT; - dma_addr_t *addrs = kmalloc(npages * sizeof(addrs), GFP_KERNEL); + addrs = kmalloc(npages * sizeof(addrs), GFP_KERNEL); for (i = 0; i < npages; i++) { - addrs[i] = dma_map_page(obj->dev->dev, pages[i], + addrs[i] = dma_map_page(dev->dev, pages[i], 0, PAGE_SIZE, DMA_BIDIRECTIONAL); } - omap_obj->addrs = addrs; + } else { + addrs = kzalloc(npages * sizeof(addrs), GFP_KERNEL); } + omap_obj->addrs = addrs; omap_obj->pages = pages; + return 0; } @@ -258,10 +263,11 @@ static void omap_gem_detach_pages(struct drm_gem_object *obj) dma_unmap_page(obj->dev->dev, omap_obj->addrs[i], PAGE_SIZE, DMA_BIDIRECTIONAL); } - kfree(omap_obj->addrs); - omap_obj->addrs = NULL; } + kfree(omap_obj->addrs); + omap_obj->addrs = NULL; + _drm_gem_put_pages(obj, omap_obj->pages, true, false); omap_obj->pages = NULL; } @@ -336,6 +342,13 @@ static int fault_1d(struct drm_gem_object *obj, vma->vm_start) >> PAGE_SHIFT; if (omap_obj->pages) { + if ((omap_obj->flags & OMAP_BO_CACHE_MASK) == OMAP_BO_CACHED) { + if (omap_obj->addrs[pgoff]) { + dma_unmap_page(obj->dev->dev, omap_obj->addrs[pgoff], + PAGE_SIZE, DMA_BIDIRECTIONAL); + omap_obj->addrs[pgoff] = 0; + } + } pfn = page_to_pfn(omap_obj->pages[pgoff]); } else { BUG_ON(!(omap_obj->flags & OMAP_BO_DMA)); @@ -510,7 +523,6 @@ fail: /** We override mainly to fix up some of the vm mapping flags.. */ int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma) { - struct omap_gem_object *omap_obj; int ret; ret = drm_gem_mmap(filp, vma); @@ -519,8 +531,14 @@ int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma) return ret; } - /* after drm_gem_mmap(), it is safe to access the obj */ - omap_obj = to_omap_bo(vma->vm_private_data); + return omap_gem_mmap_obj(vma->vm_private_data, vma); +} + + +int omap_gem_mmap_obj(struct drm_gem_object *obj, + struct vm_area_struct *vma) +{ + struct omap_gem_object *omap_obj = to_omap_bo(obj); vma->vm_flags &= ~VM_PFNMAP; vma->vm_flags |= VM_MIXEDMAP; @@ -530,12 +548,34 @@ int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma) } else if (omap_obj->flags & OMAP_BO_UNCACHED) { vma->vm_page_prot = pgprot_noncached(vm_get_page_prot(vma->vm_flags)); } else { + /* + * We do have some private objects, at least for scanout buffers + * on hardware without DMM/TILER. But these are allocated write- + * combine + */ + if (WARN_ON(!obj->filp)) + return -EINVAL; + + // TODO quick hack to avoid needing to re-invent my own vmops: + WARN_ON(obj->filp->private_data); + obj->filp->private_data = vma->vm_file->private_data; + fput(vma->vm_file); + get_file(obj->filp); + + /* + * Shunt off cached objs to shmem file so they have their own + * address_space (so unmap_mapping_range does what we want) + */ + vma->vm_pgoff = 0; + vma->vm_file = obj->filp; + vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); } - return ret; + return 0; } + /** * omap_gem_dumb_create - create a dumb buffer * @drm_file: our client file @@ -645,6 +685,44 @@ fail: return ret; } +int omap_gem_dma_sync(struct drm_gem_object *obj, + enum dma_data_direction dir) +{ + struct drm_device *dev = obj->dev; + struct omap_gem_object *omap_obj = to_omap_bo(obj); + int ret = 0; + + // TODO not sure if I want this to be automagic or new flag set + // when allocating buffer (or maybe instead a flag to explictly + // disable? + + if ((omap_obj->flags & OMAP_BO_CACHE_MASK) == OMAP_BO_CACHED) { + int i, npages = obj->size >> PAGE_SHIFT; + struct page **pages = omap_obj->pages; + bool dirty = false; + + if (WARN_ON(!obj->filp)) + return -EINVAL; + + for (i = 0; i < npages; i++) { + if (!omap_obj->addrs[i]) { + // TODO using BIDIRECTIONAL for now because in the + // fault handler we don't really know the direction.. + omap_obj->addrs[i] = dma_map_page(dev->dev, pages[i], 0, + PAGE_SIZE, DMA_BIDIRECTIONAL); + dirty = true; + } + } + + if (dirty) { + unmap_mapping_range(obj->filp->f_mapping, 0, + omap_gem_mmap_size(obj), 1); + } + } + + return ret; +} + /* Get physical address for DMA.. if 'remap' is true, and the buffer is not * already contiguous, remap it to pin in physically contiguous memory.. (ie. * map in TILER) @@ -709,6 +787,7 @@ int omap_gem_get_paddr(struct drm_gem_object *obj, *paddr = omap_obj->paddr; } else { ret = -EINVAL; + goto fail; } fail: diff --git a/drivers/staging/omapdrm/omap_gem_dmabuf.c b/drivers/staging/omapdrm/omap_gem_dmabuf.c index 2fa39e8..85a0b1c 100644 --- a/drivers/staging/omapdrm/omap_gem_dmabuf.c +++ b/drivers/staging/omapdrm/omap_gem_dmabuf.c @@ -34,6 +34,8 @@ static struct sg_table *omap_gem_map_dma_buf( if (!sg) return ERR_PTR(-ENOMEM); + omap_gem_dma_sync(obj, dir); + /* camera, etc, need physically contiguous.. but we need a * better way to know this.. */ @@ -131,6 +133,48 @@ static void omap_gem_dmabuf_kunmap(struct dma_buf *buffer, kunmap(pages[page_num]); } +/* TODO: perhaps this could be a helper: */ +static int omap_gem_dmabuf_mmap(struct dma_buf *buffer, + struct vm_area_struct *vma) +{ + struct drm_gem_object *obj = buffer->priv; + int ret = 0; + + if (WARN_ON(!obj->filp)) + return -EINVAL; + + // TODO maybe we can split up drm_gem_mmap to avoid duplicating + // some here.. or at least have a drm_dmabuf helper.. + + /* Check for valid size. */ + if (omap_gem_mmap_size(obj) < vma->vm_end - vma->vm_start) { + ret = -EINVAL; + goto out_unlock; + } + + if (!obj->dev->driver->gem_vm_ops) { + ret = -EINVAL; + goto out_unlock; + } + + vma->vm_flags |= VM_RESERVED | VM_IO | VM_PFNMAP | VM_DONTEXPAND; + vma->vm_ops = obj->dev->driver->gem_vm_ops; + vma->vm_private_data = obj; + vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); + + /* Take a ref for this mapping of the object, so that the fault + * handler can dereference the mmap offset's pointer to the object. + * This reference is cleaned up by the corresponding vm_close + * (which should happen whether the vma was created by this call, or + * by a vm_open due to mremap or partial unmap or whatever). + */ + vma->vm_ops->open(vma); + +out_unlock: + + return omap_gem_mmap_obj(obj, vma); +} + struct dma_buf_ops omap_dmabuf_ops = { .map_dma_buf = omap_gem_map_dma_buf, .unmap_dma_buf = omap_gem_unmap_dma_buf, @@ -141,6 +185,7 @@ struct dma_buf_ops omap_dmabuf_ops = { .kunmap_atomic = omap_gem_dmabuf_kunmap_atomic, .kmap = omap_gem_dmabuf_kmap, .kunmap = omap_gem_dmabuf_kunmap, + .mmap = omap_gem_dmabuf_mmap, }; struct dma_buf * omap_gem_prime_export(struct drm_device *dev,