From mboxrd@z Thu Jan 1 00:00:00 1970 From: Inki Dae Subject: RE: [PATCH 3/4] drm/exynos: added userptr feature. Date: Wed, 16 May 2012 19:30:33 +0900 Message-ID: <003601cd334e$ecde26e0$c69a74a0$%dae@samsung.com> References: <1335188594-17454-1-git-send-email-inki.dae@samsung.com> <1335188594-17454-4-git-send-email-inki.dae@samsung.com> <000d01cd3273$2ea426b0$8bec7410$%dae@samsung.com> <002b01cd3329$c2a659d0$47f30d70$%dae@samsung.com> Mime-Version: 1.0 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable Return-path: Received: from mailout3.samsung.com (mailout3.samsung.com [203.254.224.33]) by gabe.freedesktop.org (Postfix) with ESMTP id B80999E732 for ; Wed, 16 May 2012 03:30:41 -0700 (PDT) Received: from epcpsbgm1.samsung.com (mailout3.samsung.com [203.254.224.33]) by mailout3.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0M440050A2INYXC0@mailout3.samsung.com> for dri-devel@lists.freedesktop.org; Wed, 16 May 2012 19:30:40 +0900 (KST) Received: from NOINKIDAE02 ([165.213.219.102]) by mmp1.samsung.com (Oracle Communications Messaging Server 7u4-24.01 (7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTPA id <0M440023V2J3S550@mmp1.samsung.com> for dri-devel@lists.freedesktop.org; Wed, 16 May 2012 19:30:39 +0900 (KST) In-reply-to: Content-language: ko List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: dri-devel-bounces+sf-dri-devel=m.gmane.org@lists.freedesktop.org Errors-To: dri-devel-bounces+sf-dri-devel=m.gmane.org@lists.freedesktop.org To: 'Rob Clark' Cc: kyungmin.park@samsung.com, sw0312.kim@samsung.com, dri-devel@lists.freedesktop.org List-Id: dri-devel@lists.freedesktop.org > -----Original Message----- > From: robdclark@gmail.com [mailto:robdclark@gmail.com] On Behalf Of Rob > Clark > Sent: Wednesday, May 16, 2012 5:43 PM > To: Inki Dae > Cc: InKi Dae; kyungmin.park@samsung.com; sw0312.kim@samsung.com; dri- > devel@lists.freedesktop.org > Subject: Re: [PATCH 3/4] drm/exynos: added userptr feature. > = > On Wed, May 16, 2012 at 12:04 AM, Inki Dae wrote: > > > > > >> -----Original Message----- > >> From: robdclark@gmail.com [mailto:robdclark@gmail.com] On Behalf Of Rob > >> Clark > >> Sent: Tuesday, May 15, 2012 11:29 PM > >> To: InKi Dae > >> Cc: Inki Dae; kyungmin.park@samsung.com; sw0312.kim@samsung.com; dri- > >> devel@lists.freedesktop.org > >> Subject: Re: [PATCH 3/4] drm/exynos: added userptr feature. > >> > >> On Tue, May 15, 2012 at 7:40 AM, InKi Dae wrote: > >> > 2012/5/15 Rob Clark : > >> >> On Tue, May 15, 2012 at 2:17 AM, Inki Dae > wrote: > >> >>> Hi Rob, > >> >>> > >> >>>> -----Original Message----- > >> >>>> From: robdclark@gmail.com [mailto:robdclark@gmail.com] On Behalf > Of > >> Rob > >> >>>> Clark > >> >>>> Sent: Tuesday, May 15, 2012 4:35 PM > >> >>>> To: Inki Dae > >> >>>> Cc: airlied@linux.ie; dri-devel@lists.freedesktop.org; > >> >>>> kyungmin.park@samsung.com; sw0312.kim@samsung.com > >> >>>> Subject: Re: [PATCH 3/4] drm/exynos: added userptr feature. > >> >>>> > >> >>>> On Mon, Apr 23, 2012 at 7:43 AM, Inki Dae > >> wrote: > >> >>>> > this feature could be used to use memory region allocated by > >> malloc() in > >> >>>> user > >> >>>> > mode and mmaped memory region allocated by other memory > allocators. > >> >>>> userptr > >> >>>> > interface can identify memory type through vm_flags value and > would > >> get > >> >>>> > pages or page frame numbers to user space appropriately. > >> >>>> > >> >>>> I apologize for being a little late to jump in on this thread, > but... > >> >>>> > >> >>>> I must confess to not being a huge fan of userptr. =A0It really is > >> >>>> opening a can of worms, and seems best avoided if at all possible. > >> >>>> I'm not entirely sure the use-case for which you require this, but > I > >> >>>> wonder if there isn't an alternative way? =A0 I mean, the main ca= se > I > >> >>>> could think of for mapping userspace memory would be something > like > >> >>>> texture upload. =A0But could that be handled in an alternative wa= y, > >> >>>> something like a pwrite or texture_upload API, which could > >> temporarily > >> >>>> pin the userspace memory, kick off a dma from that user buffer to > a > >> >>>> proper GEM buffer, and then unpin the user buffer when the DMA > >> >>>> completes? > >> >>>> > >> >>> > >> >>> This feature have being discussed with drm and linux-mm guys and I > >> have > >> >>> posted this patch four times. > >> >>> So please, see below e-mail threads. > >> >>> http://www.spinics.net/lists/dri-devel/msg22729.html > >> >>> > >> >>> and then please, give me your opinions. > >> >> > >> >> > >> >> Yeah, I read that and understand that the purpose is to support > >> >> malloc()'d memory (and btw, all the other changes like limits to the > >> >> amount of userptr buffers are good if we do decide to support > userptr > >> >> buffers). =A0But my question was more about why do you need to supp= ort > >> >> malloc'd buffers... other than "just because v4l2 does" ;-) > >> >> > >> >> I don't really like the userptr feature in v4l2 either, and view it > as > >> >> a necessary evil because at the time there was nothing like dmabuf. > >> >> But now that we have dmabuf to share buffers with zero copy between > >> >> two devices, to we *really* still need userptr? > >> >> > >> > > >> > Definitely no, as I mentioned on this email thread, we are using the > >> > userptr feature for pixman and evas > >> > backend to use gpu acceleration directly(zero-copy). as you may know, > >> > Evas is part of the Enlightenment Foundation Libraries(EFL) and > >> > Elementary for pixman. all the applicaions based on them uses user > >> > address to draw something so the backends(such as gpu accelerators) > >> > can refer to only the user address to the buffer rendered. do you > >> > think we can avoid memory copy to use those GPUs without such userptr > >> > feature? I think there is no way and only the way that the gpu uses > >> > the user address to render without memory copy. and as mentioned on > >> > this thread, this feature had been tried by i915 in the desktop world. > >> > for this, you can refer to Daniel's comments. > >> > >> oh, yeah, for something like pixman it is hard to accelerate. =A0But I > >> wonder if there is still some benefit. =A0I don't know as much about > >> evas, but pixman is assumed to be a synchronous API, so you must > >> always block until the operation completes which looses a lot of the > >> potential benefit of a blitter. =A0And also I think you have no control > >> over when the client free()'s a buffer. =A0So you end up having to > >> map/unmap the buffer to the gpu/blitter/whatever every single > >> operation. =A0Not to mention possible need for cache operations, etc. = =A0I > >> wonder if trying to accel it in hw really brings any benefit vs just > >> using NEON? > >> > > > > Yes, actually, we are using NEON backend of PIXMAN for best performance > > also. like this, PIXMAN's NEON backend for small image and 2D GPU > backend of > > EXA for large image because NEON is faster than 2d gpu for small image > but > > not for large image. > > > >> I thought EFL has a GLES backend.. =A0I could be wrong about that, but > >> if it does probably you'd get better performance by using the GLES > >> backend vs trying to accelerate what is intended as a sw path.. > >> > > Right, EFL has a GLES backend and also PIXMAN for software path. > application > > using Elementary can use PIXMAN for software path and also GLES for > hardware > > acceleration. so the purpose of using userptr feature is for buffer > drawn by > > any user using Evas to be rendered by gpu hardware. > = > = > hmm, I don't suppose it is primarily a one-way transfer (ie. like one > time blit from sw rendered surface to texture)? I'd be more > comfortable w/ an API that somehow just created a temporary mapping > that unpins the pages before the syscall returns, ie. like a > EXYNOS_IOCTL_BLIT_FROM_USERPTR type interface, which does some blit > from a user ptr to a GEM bo. > = You mean use such interface to do BitBLIT instead of using userptr? via driver also uses this way. > BR, > -R > = > > > > Thanks, > > Inki Dae > > > >> > and as you know, note that we are already using dmabuf to share a > >> > buffer between device drivers or v4l2 and drm framework. for this, we > >> > also tested drm prime feature with v4l2 world and we applied umm > >> > concept to ump for 3d gpu(known as mali) also. > >> > > >> > Anyway, this userptr feature is different from your thought. and > maybe > >> > you also need such feature for performance enhancement. Please give > me > >> > any idea if you have any good way instead of this userptr fearure. > >> > there may be any good idea I don't know. > >> > >> well, I just try and point people to APIs that are actually designed > >> with hw acceleration in mind in the first place (x11/EXA, GLES, etc) > >> ;-) > >> > >> There missing really an API for client side 2d accel. =A0Well, I guess > >> there is OpenVG, although I'm not sure if anyone actually ever used > >> it.. maybe we do need something for better supporting 2d blitters in > >> weston compositor, for example. > >> > >> BR, > >> -R > >> > >> > >> > Thanks, > >> > Inki Dae > >> > > >> >> I guess if I understood better the use-case, maybe I could try to > >> >> think of some alternatives. > >> >> > >> >> BR, > >> >> -R > >> >> > >> >>> Thanks, > >> >>> Inki Dae > >> >>> > >> >>>> BR, > >> >>>> -R > >> >>>> > >> >>>> > Signed-off-by: Inki Dae > >> >>>> > Signed-off-by: Kyungmin Park > >> >>>> > --- > >> >>>> > =A0drivers/gpu/drm/exynos/exynos_drm_drv.c | =A0 =A02 + > >> >>>> > =A0drivers/gpu/drm/exynos/exynos_drm_gem.c | =A0258 > >> >>>> +++++++++++++++++++++++++++++++ > >> >>>> > =A0drivers/gpu/drm/exynos/exynos_drm_gem.h | =A0 13 ++- > >> >>>> > =A0include/drm/exynos_drm.h =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0| = =A0 25 +++- > >> >>>> > =A04 files changed, 296 insertions(+), 2 deletions(-) > >> >>>> > > >> >>>> > diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c > >> >>>> b/drivers/gpu/drm/exynos/exynos_drm_drv.c > >> >>>> > index f58a487..5bb0361 100644 > >> >>>> > --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c > >> >>>> > +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c > >> >>>> > @@ -211,6 +211,8 @@ static struct drm_ioctl_desc exynos_ioctls[] > =3D > >> { > >> >>>> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0DRM_AUTH), > >> >>>> > =A0 =A0 =A0 =A0DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MMAP, > >> >>>> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0exynos_drm_gem_m= map_ioctl, DRM_UNLOCKED | > >> >>> DRM_AUTH), > >> >>>> > + =A0 =A0 =A0 DRM_IOCTL_DEF_DRV(EXYNOS_GEM_USERPTR, > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 exynos_drm_gem_us= erptr_ioctl, > > DRM_UNLOCKED), > >> >>>> > =A0 =A0 =A0 =A0DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, > >> >>>> exynos_plane_set_zpos_ioctl, > >> >>>> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0DRM_UNLOCKED | D= RM_AUTH), > >> >>>> > =A0 =A0 =A0 =A0DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, > >> >>>> > diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c > >> >>>> b/drivers/gpu/drm/exynos/exynos_drm_gem.c > >> >>>> > index afd0cd4..b68d4ea 100644 > >> >>>> > --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c > >> >>>> > +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c > >> >>>> > @@ -66,6 +66,43 @@ static int check_gem_flags(unsigned int flags) > >> >>>> > =A0 =A0 =A0 =A0return 0; > >> >>>> > =A0} > >> >>>> > > >> >>>> > +static struct vm_area_struct *get_vma(struct vm_area_struct > *vma) > >> >>>> > +{ > >> >>>> > + =A0 =A0 =A0 struct vm_area_struct *vma_copy; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 vma_copy =3D kmalloc(sizeof(*vma_copy), GFP_KERNE= L); > >> >>>> > + =A0 =A0 =A0 if (!vma_copy) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return NULL; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 if (vma->vm_ops && vma->vm_ops->open) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 vma->vm_ops->open(vma); > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 if (vma->vm_file) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 get_file(vma->vm_file); > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 memcpy(vma_copy, vma, sizeof(*vma)); > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 vma_copy->vm_mm =3D NULL; > >> >>>> > + =A0 =A0 =A0 vma_copy->vm_next =3D NULL; > >> >>>> > + =A0 =A0 =A0 vma_copy->vm_prev =3D NULL; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 return vma_copy; > >> >>>> > +} > >> >>>> > + > >> >>>> > +static void put_vma(struct vm_area_struct *vma) > >> >>>> > +{ > >> >>>> > + =A0 =A0 =A0 if (!vma) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 if (vma->vm_ops && vma->vm_ops->close) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 vma->vm_ops->close(vma); > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 if (vma->vm_file) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 fput(vma->vm_file); > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 kfree(vma); > >> >>>> > +} > >> >>>> > + > >> >>>> > =A0static void update_vm_cache_attr(struct exynos_drm_gem_obj *obj, > >> >>>> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0struct vm_area_struct *vma) > >> >>>> > =A0{ > >> >>>> > @@ -254,6 +291,41 @@ static void exynos_drm_gem_put_pages(struct > >> >>>> drm_gem_object *obj) > >> >>>> > =A0 =A0 =A0 =A0/* add some codes for UNCACHED type here. TODO */ > >> >>>> > =A0} > >> >>>> > > >> >>>> > +static void exynos_drm_put_userptr(struct drm_gem_object *obj) > >> >>>> > +{ > >> >>>> > + =A0 =A0 =A0 struct exynos_drm_gem_obj *exynos_gem_obj; > >> >>>> > + =A0 =A0 =A0 struct exynos_drm_gem_buf *buf; > >> >>>> > + =A0 =A0 =A0 struct vm_area_struct *vma; > >> >>>> > + =A0 =A0 =A0 int npages; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 exynos_gem_obj =3D to_exynos_gem_obj(obj); > >> >>>> > + =A0 =A0 =A0 buf =3D exynos_gem_obj->buffer; > >> >>>> > + =A0 =A0 =A0 vma =3D exynos_gem_obj->vma; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) > > { > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 put_vma(exynos_gem_obj->vma); > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out; > >> >>>> > + =A0 =A0 =A0 } > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 npages =3D buf->size >> PAGE_SHIFT; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 npages--; > >> >>>> > + =A0 =A0 =A0 while (npages >=3D 0) { > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (buf->write) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 set_page_dirty_lo= ck(buf->pages[npages]); > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 put_page(buf->pages[npages]); > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 npages--; > >> >>>> > + =A0 =A0 =A0 } > >> >>>> > + > >> >>>> > +out: > >> >>>> > + =A0 =A0 =A0 kfree(buf->pages); > >> >>>> > + =A0 =A0 =A0 buf->pages =3D NULL; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 kfree(buf->sgt); > >> >>>> > + =A0 =A0 =A0 buf->sgt =3D NULL; > >> >>>> > +} > >> >>>> > + > >> >>>> > =A0static int exynos_drm_gem_handle_create(struct drm_gem_object > > *obj, > >> >>>> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0struct drm_file *file_priv, > >> >>>> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0unsigned int *handle) > >> >>>> > @@ -293,6 +365,8 @@ void exynos_drm_gem_destroy(struct > >> >>>> exynos_drm_gem_obj *exynos_gem_obj) > >> >>>> > > >> >>>> > =A0 =A0 =A0 =A0if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) > >> >>>> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0exynos_drm_gem_put_pages(obj); > >> >>>> > + =A0 =A0 =A0 else if (exynos_gem_obj->flags & EXYNOS_BO_USERPT= R) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 exynos_drm_put_userptr(obj); > >> >>>> > =A0 =A0 =A0 =A0else > >> >>>> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, > >> >>> buf); > >> >>>> > > >> >>>> > @@ -606,6 +680,190 @@ int exynos_drm_gem_mmap_ioctl(struct > >> drm_device > >> >>>> *dev, void *data, > >> >>>> > =A0 =A0 =A0 =A0return 0; > >> >>>> > =A0} > >> >>>> > > >> >>>> > +static int exynos_drm_get_userptr(struct drm_device *dev, > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 s= truct exynos_drm_gem_obj *obj, > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 u= nsigned long userptr, > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 u= nsigned int write) > >> >>>> > +{ > >> >>>> > + =A0 =A0 =A0 unsigned int get_npages; > >> >>>> > + =A0 =A0 =A0 unsigned long npages =3D 0; > >> >>>> > + =A0 =A0 =A0 struct vm_area_struct *vma; > >> >>>> > + =A0 =A0 =A0 struct exynos_drm_gem_buf *buf =3D obj->buffer; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 vma =3D find_vma(current->mm, userptr); > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 /* the memory region mmaped with VM_PFNMAP. */ > >> >>>> > + =A0 =A0 =A0 if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) > > { > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 unsigned long this_pfn, prev_pfn,= pa; > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 unsigned long start, end, offset; > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct scatterlist *sgl; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 start =3D userptr; > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 offset =3D userptr & ~PAGE_MASK; > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 end =3D start + buf->size; > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 sgl =3D buf->sgt->sgl; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 for (prev_pfn =3D 0; start < end;= start +=3D PAGE_SIZE) > > { > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int ret =3D follo= w_pfn(vma, start, > > &this_pfn); > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (ret) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 r= eturn ret; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (prev_pfn =3D= =3D 0) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 p= a =3D this_pfn << PAGE_SHIFT; > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 else if (this_pfn= !=3D prev_pfn + 1) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 r= eturn -EFAULT; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 sg_dma_address(sg= l) =3D (pa + offset); > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 sg_dma_len(sgl) = =3D PAGE_SIZE; > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 prev_pfn =3D this= _pfn; > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pa +=3D PAGE_SIZE; > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 npages++; > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 sgl =3D sg_next(s= gl); > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 buf->dma_addr =3D pa + offset; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 obj->vma =3D get_vma(vma); > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!obj->vma) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENOMEM; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 buf->pfnmap =3D true; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return npages; > >> >>>> > + =A0 =A0 =A0 } > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 buf->write =3D write; > >> >>>> > + =A0 =A0 =A0 npages =3D buf->size >> PAGE_SHIFT; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 down_read(¤t->mm->mmap_sem); > >> >>>> > + =A0 =A0 =A0 get_npages =3D get_user_pages(current, current->m= m, userptr, > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 npages, write, 1, > > buf->pages, > >> >>> NULL); > >> >>>> > + =A0 =A0 =A0 up_read(¤t->mm->mmap_sem); > >> >>>> > + =A0 =A0 =A0 if (get_npages !=3D npages) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 DRM_ERROR("failed to get user_pag= es.\n"); > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 buf->pfnmap =3D false; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 return get_npages; > >> >>>> > +} > >> >>>> > + > >> >>>> > +int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void > >> *data, > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 struct drm_file *file_priv) > >> >>>> > +{ > >> >>>> > + =A0 =A0 =A0 struct exynos_drm_gem_obj *exynos_gem_obj; > >> >>>> > + =A0 =A0 =A0 struct drm_exynos_gem_userptr *args =3D data; > >> >>>> > + =A0 =A0 =A0 struct exynos_drm_gem_buf *buf; > >> >>>> > + =A0 =A0 =A0 struct scatterlist *sgl; > >> >>>> > + =A0 =A0 =A0 unsigned long size, userptr; > >> >>>> > + =A0 =A0 =A0 unsigned int npages; > >> >>>> > + =A0 =A0 =A0 int ret, get_npages; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 DRM_DEBUG_KMS("%s\n", __FILE__); > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 if (!args->size) { > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 DRM_ERROR("invalid size.\n"); > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL; > >> >>>> > + =A0 =A0 =A0 } > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 ret =3D check_gem_flags(args->flags); > >> >>>> > + =A0 =A0 =A0 if (ret) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 size =3D roundup_gem_size(args->size, EXYNOS_BO_U= SERPTR); > >> >>>> > + =A0 =A0 =A0 userptr =3D args->userptr; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 buf =3D exynos_drm_init_buf(dev, size); > >> >>>> > + =A0 =A0 =A0 if (!buf) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENOMEM; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 exynos_gem_obj =3D exynos_drm_gem_init(dev, size); > >> >>>> > + =A0 =A0 =A0 if (!exynos_gem_obj) { > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D -ENOMEM; > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err_free_buffer; > >> >>>> > + =A0 =A0 =A0 } > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 buf->sgt =3D kzalloc(sizeof(struct sg_table), GFP= _KERNEL); > >> >>>> > + =A0 =A0 =A0 if (!buf->sgt) { > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 DRM_ERROR("failed to allocate buf= ->sgt.\n"); > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D -ENOMEM; > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err_release_gem; > >> >>>> > + =A0 =A0 =A0 } > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 npages =3D size >> PAGE_SHIFT; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 ret =3D sg_alloc_table(buf->sgt, npages, GFP_KERN= EL); > >> >>>> > + =A0 =A0 =A0 if (ret < 0) { > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 DRM_ERROR("failed to initailize s= g table.\n"); > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err_free_sgt; > >> >>>> > + =A0 =A0 =A0 } > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 buf->pages =3D kzalloc(npages * sizeof(struct pag= e *), > >> >>> GFP_KERNEL); > >> >>>> > + =A0 =A0 =A0 if (!buf->pages) { > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 DRM_ERROR("failed to allocate buf= ->pages\n"); > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D -ENOMEM; > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err_free_table; > >> >>>> > + =A0 =A0 =A0 } > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 exynos_gem_obj->buffer =3D buf; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 get_npages =3D exynos_drm_get_userptr(dev, exynos= _gem_obj, > >> >>> userptr, > >> >>>> 1); > >> >>>> > + =A0 =A0 =A0 if (get_npages !=3D npages) { > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 DRM_ERROR("failed to get user_pag= es.\n"); > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D get_npages; > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err_release_userptr; > >> >>>> > + =A0 =A0 =A0 } > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 ret =3D exynos_drm_gem_handle_create(&exynos_gem_obj->base, > >> >>>> file_priv, > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &args->handle); > >> >>>> > + =A0 =A0 =A0 if (ret < 0) { > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 DRM_ERROR("failed to create gem h= andle.\n"); > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err_release_userptr; > >> >>>> > + =A0 =A0 =A0 } > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 sgl =3D buf->sgt->sgl; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 /* > >> >>>> > + =A0 =A0 =A0 =A0* if buf->pfnmap is true then update sgl of sg= t with > pages > >> but > >> >>>> > + =A0 =A0 =A0 =A0* if buf->pfnmap is false then it means the sg= l was > > updated > >> >>>> already > >> >>>> > + =A0 =A0 =A0 =A0* so it doesn't need to update the sgl. > >> >>>> > + =A0 =A0 =A0 =A0*/ > >> >>>> > + =A0 =A0 =A0 if (!buf->pfnmap) { > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 unsigned int i =3D 0; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* set all pages to sg list. */ > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 while (i < npages) { > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 sg_set_page(sgl, = buf->pages[i], PAGE_SIZE, > > 0); > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 sg_dma_address(sg= l) =3D > >> >>> page_to_phys(buf->pages[i]); > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 i++; > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 sgl =3D sg_next(s= gl); > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } > >> >>>> > + =A0 =A0 =A0 } > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 /* always use EXYNOS_BO_USERPTR as memory type for > userptr. > >> */ > >> >>>> > + =A0 =A0 =A0 exynos_gem_obj->flags |=3D EXYNOS_BO_USERPTR; > >> >>>> > + > >> >>>> > + =A0 =A0 =A0 return 0; > >> >>>> > + > >> >>>> > +err_release_userptr: > >> >>>> > + =A0 =A0 =A0 get_npages--; > >> >>>> > + =A0 =A0 =A0 while (get_npages >=3D 0) > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 put_page(buf->pages[get_npages--]= ); > >> >>>> > + =A0 =A0 =A0 kfree(buf->pages); > >> >>>> > + =A0 =A0 =A0 buf->pages =3D NULL; > >> >>>> > +err_free_table: > >> >>>> > + =A0 =A0 =A0 sg_free_table(buf->sgt); > >> >>>> > +err_free_sgt: > >> >>>> > + =A0 =A0 =A0 kfree(buf->sgt); > >> >>>> > + =A0 =A0 =A0 buf->sgt =3D NULL; > >> >>>> > +err_release_gem: > >> >>>> > + =A0 =A0 =A0 drm_gem_object_release(&exynos_gem_obj->base); > >> >>>> > + =A0 =A0 =A0 kfree(exynos_gem_obj); > >> >>>> > + =A0 =A0 =A0 exynos_gem_obj =3D NULL; > >> >>>> > +err_free_buffer: > >> >>>> > + =A0 =A0 =A0 exynos_drm_free_buf(dev, 0, buf); > >> >>>> > + =A0 =A0 =A0 return ret; > >> >>>> > +} > >> >>>> > + > >> >>>> > =A0int exynos_drm_gem_init_object(struct drm_gem_object *obj) > >> >>>> > =A0{ > >> >>>> > =A0 =A0 =A0 =A0DRM_DEBUG_KMS("%s\n", __FILE__); > >> >>>> > diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h > >> >>>> b/drivers/gpu/drm/exynos/exynos_drm_gem.h > >> >>>> > index efc8252..1de2241 100644 > >> >>>> > --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h > >> >>>> > +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h > >> >>>> > @@ -29,7 +29,8 @@ > >> >>>> > =A0#define to_exynos_gem_obj(x) =A0 container_of(x,\ > >> >>>> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct exynos_dr= m_gem_obj, base) > >> >>>> > > >> >>>> > -#define IS_NONCONTIG_BUFFER(f) =A0 =A0 =A0 =A0 (f & EXYNOS_BO_NONCONTIG) > >> >>>> > +#define IS_NONCONTIG_BUFFER(f) =A0 =A0 =A0 =A0 ((f & EXYNOS_BO_NONCONTIG) > >> ||\ > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 (f & EXYNOS_BO_USERPTR)) > >> >>>> > > >> >>>> > =A0/* > >> >>>> > =A0* exynos drm gem buffer structure. > >> >>>> > @@ -38,18 +39,23 @@ > >> >>>> > =A0* @dma_addr: bus address(accessed by dma) to allocated memory > >> region. > >> >>>> > =A0* =A0 =A0 - this address could be physical address without I= OMMU and > >> >>>> > =A0* =A0 =A0 device address with IOMMU. > >> >>>> > + * @write: whether pages will be written to by the caller. > >> >>>> > =A0* @sgt: sg table to transfer page data. > >> >>>> > =A0* @pages: contain all pages to allocated memory region. > >> >>>> > =A0* @page_size: could be 4K, 64K or 1MB. > >> >>>> > =A0* @size: size of allocated memory region. > >> >>>> > + * @pfnmap: indicate whether memory region from userptr is > mmaped > >> with > >> >>>> > + * =A0 =A0 VM_PFNMAP or not. > >> >>>> > =A0*/ > >> >>>> > =A0struct exynos_drm_gem_buf { > >> >>>> > =A0 =A0 =A0 =A0void __iomem =A0 =A0 =A0 =A0 =A0 =A0*kvaddr; > >> >>>> > =A0 =A0 =A0 =A0dma_addr_t =A0 =A0 =A0 =A0 =A0 =A0 =A0dma_addr; > >> >>>> > + =A0 =A0 =A0 unsigned int =A0 =A0 =A0 =A0 =A0 =A0write; > >> >>>> > =A0 =A0 =A0 =A0struct sg_table =A0 =A0 =A0 =A0 *sgt; > >> >>>> > =A0 =A0 =A0 =A0struct page =A0 =A0 =A0 =A0 =A0 =A0 **pages; > >> >>>> > =A0 =A0 =A0 =A0unsigned long =A0 =A0 =A0 =A0 =A0 page_size; > >> >>>> > =A0 =A0 =A0 =A0unsigned long =A0 =A0 =A0 =A0 =A0 size; > >> >>>> > + =A0 =A0 =A0 bool =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0pfnma= p; > >> >>>> > =A0}; > >> >>>> > > >> >>>> > =A0/* > >> >>>> > @@ -73,6 +79,7 @@ struct exynos_drm_gem_obj { > >> >>>> > =A0 =A0 =A0 =A0struct drm_gem_object =A0 =A0 =A0 =A0 =A0 base; > >> >>>> > =A0 =A0 =A0 =A0struct exynos_drm_gem_buf =A0 =A0 =A0 *buffer; > >> >>>> > =A0 =A0 =A0 =A0unsigned long =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 size; > >> >>>> > + =A0 =A0 =A0 struct vm_area_struct =A0 =A0 =A0 =A0 =A0 *vma; > >> >>>> > =A0 =A0 =A0 =A0unsigned int =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0flags; > >> >>>> > =A0}; > >> >>>> > > >> >>>> > @@ -127,6 +134,10 @@ int exynos_drm_gem_map_offset_ioctl(struct > >> >>>> drm_device *dev, void *data, > >> >>>> > =A0int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void > *data, > >> >>>> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0stru= ct drm_file *file_priv); > >> >>>> > > >> >>>> > +/* map user space allocated by malloc to pages. */ > >> >>>> > +int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void > >> *data, > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 struct drm_file *file_priv); > >> >>>> > + > >> >>>> > =A0/* initialize gem object. */ > >> >>>> > =A0int exynos_drm_gem_init_object(struct drm_gem_object *obj); > >> >>>> > > >> >>>> > diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h > >> >>>> > index 2d6eb06..48eda6e 100644 > >> >>>> > --- a/include/drm/exynos_drm.h > >> >>>> > +++ b/include/drm/exynos_drm.h > >> >>>> > @@ -75,6 +75,23 @@ struct drm_exynos_gem_mmap { > >> >>>> > =A0}; > >> >>>> > > >> >>>> > =A0/** > >> >>>> > + * User-requested user space importing structure > >> >>>> > + * > >> >>>> > + * @userptr: user space address allocated by malloc. > >> >>>> > + * @size: size to the buffer allocated by malloc. > >> >>>> > + * @flags: indicate user-desired cache attribute to map the > >> allocated > >> >>>> buffer > >> >>>> > + * =A0 =A0 to kernel space. > >> >>>> > + * @handle: a returned handle to created gem object. > >> >>>> > + * =A0 =A0 - this handle will be set by gem module of kernel s= ide. > >> >>>> > + */ > >> >>>> > +struct drm_exynos_gem_userptr { > >> >>>> > + =A0 =A0 =A0 uint64_t userptr; > >> >>>> > + =A0 =A0 =A0 uint64_t size; > >> >>>> > + =A0 =A0 =A0 unsigned int flags; > >> >>>> > + =A0 =A0 =A0 unsigned int handle; > >> >>>> > +}; > >> >>>> > + > >> >>>> > +/** > >> >>>> > =A0* A structure for user connection request of virtual display. > >> >>>> > =A0* > >> >>>> > =A0* @connection: indicate whether doing connetion or not by us= er. > >> >>>> > @@ -105,13 +122,16 @@ enum e_drm_exynos_gem_mem_type { > >> >>>> > =A0 =A0 =A0 =A0EXYNOS_BO_CACHABLE =A0 =A0 =A0=3D 1 << 1, > >> >>>> > =A0 =A0 =A0 =A0/* write-combine mapping. */ > >> >>>> > =A0 =A0 =A0 =A0EXYNOS_BO_WC =A0 =A0 =A0 =A0 =A0 =A0=3D 1 << 2, > >> >>>> > + =A0 =A0 =A0 /* user space memory allocated by malloc. */ > >> >>>> > + =A0 =A0 =A0 EXYNOS_BO_USERPTR =A0 =A0 =A0 =3D 1 << 3, > >> >>>> > =A0 =A0 =A0 =A0EXYNOS_BO_MASK =A0 =A0 =A0 =A0 =A0=3D EXYNOS_BO_= NONCONTIG | > >> >>> EXYNOS_BO_CACHABLE > >> >>>> | > >> >>>> > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 EXYNOS_BO_WC > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 EXYNOS_BO_WC | > > EXYNOS_BO_USERPTR > >> >>>> > =A0}; > >> >>>> > > >> >>>> > =A0#define DRM_EXYNOS_GEM_CREATE =A0 =A0 =A0 =A0 =A00x00 > >> >>>> > =A0#define DRM_EXYNOS_GEM_MAP_OFFSET =A0 =A0 =A00x01 > >> >>>> > =A0#define DRM_EXYNOS_GEM_MMAP =A0 =A0 =A0 =A0 =A0 =A00x02 > >> >>>> > +#define DRM_EXYNOS_GEM_USERPTR =A0 =A0 =A0 =A0 0x03 > >> >>>> > =A0/* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */ > >> >>>> > =A0#define DRM_EXYNOS_PLANE_SET_ZPOS =A0 =A0 =A00x06 > >> >>>> > =A0#define DRM_EXYNOS_VIDI_CONNECTION =A0 =A0 0x07 > >> >>>> > @@ -125,6 +145,9 @@ enum e_drm_exynos_gem_mem_type { > >> >>>> > =A0#define DRM_IOCTL_EXYNOS_GEM_MMAP =A0 =A0 =A0DRM_IOWR(DRM_COMMAND_BASE > + > >> \ > >> >>>> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0DRM_EXYNOS_GEM_MMAP, struct drm_= exynos_gem_mmap) > >> >>>> > > >> >>>> > +#define > DRM_IOCTL_EXYNOS_GEM_USERPTR =A0 DRM_IOWR(DRM_COMMAND_BASE + > >> \ > >> >>>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 DRM_EXYNOS_GEM_USERPTR, struct > > drm_exynos_gem_userptr) > >> >>>> > + > >> >>>> > =A0#define DRM_IOCTL_EXYNOS_PLANE_SET_ZPOS > >> >>> =A0DRM_IOWR(DRM_COMMAND_BASE > >> >>>> + \ > >> >>>> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0DRM_EXYNOS_PLANE_SET_ZPOS, struct > >> >>> drm_exynos_plane_set_zpos) > >> >>>> > > >> >>>> > -- > >> >>>> > 1.7.4.1 > >> >>>> > > >> >>>> > _______________________________________________ > >> >>>> > dri-devel mailing list > >> >>>> > dri-devel@lists.freedesktop.org > >> >>>> > http://lists.freedesktop.org/mailman/listinfo/dri-devel > >> >>> > >> >>> _______________________________________________ > >> >>> dri-devel mailing list > >> >>> dri-devel@lists.freedesktop.org > >> >>> http://lists.freedesktop.org/mailman/listinfo/dri-devel > >> >> _______________________________________________ > >> >> dri-devel mailing list > >> >> dri-devel@lists.freedesktop.org > >> >> http://lists.freedesktop.org/mailman/listinfo/dri-devel > >> > _______________________________________________ > >> > dri-devel mailing list > >> > dri-devel@lists.freedesktop.org > >> > http://lists.freedesktop.org/mailman/listinfo/dri-devel > > > > _______________________________________________ > > dri-devel mailing list > > dri-devel@lists.freedesktop.org > > http://lists.freedesktop.org/mailman/listinfo/dri-devel