You can subscribe to this list here.
1999 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(115) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2000 |
Jan
(143) |
Feb
(177) |
Mar
(390) |
Apr
(285) |
May
(316) |
Jun
(241) |
Jul
(303) |
Aug
(504) |
Sep
(322) |
Oct
(368) |
Nov
(398) |
Dec
(474) |
2001 |
Jan
(734) |
Feb
(712) |
Mar
(736) |
Apr
(358) |
May
(403) |
Jun
(317) |
Jul
(286) |
Aug
(299) |
Sep
(304) |
Oct
(398) |
Nov
(169) |
Dec
(313) |
2002 |
Jan
(406) |
Feb
(506) |
Mar
(520) |
Apr
(629) |
May
(714) |
Jun
(711) |
Jul
(761) |
Aug
(665) |
Sep
(542) |
Oct
(713) |
Nov
(641) |
Dec
(639) |
2003 |
Jan
(468) |
Feb
(748) |
Mar
(781) |
Apr
(812) |
May
(789) |
Jun
(731) |
Jul
(567) |
Aug
(579) |
Sep
(624) |
Oct
(647) |
Nov
(387) |
Dec
(422) |
2004 |
Jan
(592) |
Feb
(630) |
Mar
(514) |
Apr
(457) |
May
(647) |
Jun
(388) |
Jul
(276) |
Aug
(528) |
Sep
(840) |
Oct
(831) |
Nov
(350) |
Dec
(458) |
2005 |
Jan
(584) |
Feb
(654) |
Mar
(706) |
Apr
(229) |
May
(411) |
Jun
(594) |
Jul
(341) |
Aug
(435) |
Sep
(251) |
Oct
(297) |
Nov
(196) |
Dec
(286) |
2006 |
Jan
(295) |
Feb
(378) |
Mar
(300) |
Apr
(204) |
May
(241) |
Jun
(316) |
Jul
(256) |
Aug
(346) |
Sep
(338) |
Oct
(352) |
Nov
(288) |
Dec
(272) |
2007 |
Jan
(194) |
Feb
(242) |
Mar
(329) |
Apr
(357) |
May
(254) |
Jun
(309) |
Jul
(291) |
Aug
(370) |
Sep
(279) |
Oct
(336) |
Nov
(357) |
Dec
(465) |
2008 |
Jan
(396) |
Feb
(370) |
Mar
(407) |
Apr
(350) |
May
(337) |
Jun
(339) |
Jul
(352) |
Aug
(314) |
Sep
(338) |
Oct
(299) |
Nov
(279) |
Dec
(365) |
2009 |
Jan
(596) |
Feb
(601) |
Mar
(588) |
Apr
(542) |
May
(731) |
Jun
(701) |
Jul
(673) |
Aug
(1050) |
Sep
(740) |
Oct
(750) |
Nov
(774) |
Dec
(1044) |
2010 |
Jan
(835) |
Feb
(1215) |
Mar
(1249) |
Apr
(485) |
May
(138) |
Jun
(164) |
Jul
(143) |
Aug
(148) |
Sep
(102) |
Oct
(121) |
Nov
(74) |
Dec
(83) |
2011 |
Jan
(131) |
Feb
(200) |
Mar
(122) |
Apr
(111) |
May
(125) |
Jun
(6) |
Jul
|
Aug
|
Sep
(1) |
Oct
(6) |
Nov
(1) |
Dec
(4) |
2012 |
Jan
|
Feb
(4) |
Mar
(2) |
Apr
(3) |
May
|
Jun
|
Jul
|
Aug
(6) |
Sep
(2) |
Oct
(2) |
Nov
|
Dec
|
2013 |
Jan
(4) |
Feb
(1) |
Mar
|
Apr
|
May
|
Jun
|
Jul
(1) |
Aug
(2) |
Sep
|
Oct
(6) |
Nov
(15) |
Dec
|
2014 |
Jan
|
Feb
(1) |
Mar
|
Apr
(1) |
May
(1) |
Jun
|
Jul
|
Aug
|
Sep
(1) |
Oct
|
Nov
|
Dec
|
2015 |
Jan
(6) |
Feb
(10) |
Mar
(1) |
Apr
|
May
|
Jun
|
Jul
|
Aug
(3) |
Sep
|
Oct
|
Nov
|
Dec
|
2017 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(2) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2022 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(7) |
Aug
(1) |
Sep
|
Oct
|
Nov
|
Dec
|
From: Dave A. <ai...@gm...> - 2015-01-30 00:14:44
|
On 30 January 2015 at 10:03, Linus Torvalds <tor...@li...> wrote: > On Thu, Jan 29, 2015 at 3:57 PM, Greg Kroah-Hartman > <gr...@li...> wrote: >> >> I can take this through the tty tree, but can I put it in linux-next and >> wait for the 3.20 merge window to give people who might notice a >> slow-down a chance to object? > > Yes. The problem only affects one (or a couple of) truly outrageously > bad graphics cards that are only used in servers (because they are > such crap that they wouldn't be acceptable anywhere else anyway), and > they have afaik never worked with 64-bit kernels, so it's not even a > regression. > > So it's worth fixing because it's a real - albeit very rare - problem > (especially since the enhanched rep instruction model of memcpy could > easily be *worse* than the 16-bit-at-a-time manual version), but I > wouldn't consider it anywhere near high priority. > Totally not a priority, it just finally got tested for RHEL so I wanted to make sure I posted it upstream before I forgot about it for months, I also filed: https://bugzilla.kernel.org/show_bug.cgi?id=92311 since the RH bug is private and full of crap, that bug contains a screenshot of the remote console to see what sort of crap it produces. Dave. |
From: Linus T. <tor...@li...> - 2015-01-30 00:03:35
|
On Thu, Jan 29, 2015 at 3:57 PM, Greg Kroah-Hartman <gr...@li...> wrote: > > I can take this through the tty tree, but can I put it in linux-next and > wait for the 3.20 merge window to give people who might notice a > slow-down a chance to object? Yes. The problem only affects one (or a couple of) truly outrageously bad graphics cards that are only used in servers (because they are such crap that they wouldn't be acceptable anywhere else anyway), and they have afaik never worked with 64-bit kernels, so it's not even a regression. So it's worth fixing because it's a real - albeit very rare - problem (especially since the enhanched rep instruction model of memcpy could easily be *worse* than the 16-bit-at-a-time manual version), but I wouldn't consider it anywhere near high priority. Linus |
From: Linus T. <tor...@li...> - 2015-01-29 23:40:39
|
On Wed, Jan 28, 2015 at 8:11 PM, Dave Airlie <ai...@re...> wrote: > > Linus, this came up a while back I finally got some confirmation > that it fixes those servers. I'm certainly ok with this. which way should it go in? The users are: - drivers/tty/vt/vt.c (Greg KH, "tty layer") - drivers/video/console/* (fbcon people: Tomi Valkeinen and friends) and it might make sense to have *some* indication of how much worse this makes fbcon performance in particular.. Greg/Tomi - the patch is removing this: #define scr_memcpyw(d, s, c) memcpy(d, s, c) #define scr_memmovew(d, s, c) memmove(d, s, c) #define VT_BUF_HAVE_MEMCPYW #define VT_BUF_HAVE_MEMMOVEW from <linux/vt_buffer.h>, because some stupid graphics cards apparently cannot handle 64-bit accesses of regular memcpy/memmove. And on other setups, this will be the reverse: 8-bit accesses due to using "rep movsb", which is the fast way to move/clear memory on modern Intel CPU's, but is really wrong for MMIO where it will be slow as hell. So just getting rid of the memcpy/memmove is likely the right thing in general, since the fallbacks go this the traditional 16-bit-at-a-time way. And getting rid of the memcpy _may_ speed things up. But if it slows things down, we might have to try something else. Like saying "all cards we've ever seen have been ok with aligned 32-bit accesses", and extend the open-coded scr_memcpy/memmove functions to do that. Hmm? Linus |
From: Peter H. <pe...@hu...> - 2015-01-29 12:34:34
|
On 01/28/2015 11:11 PM, Dave Airlie wrote: > These two copy to/from VGA memory, however on the Silicon > Motion SMI750 VGA card on a 64-bit system cause console corruption. > > This is due to the hw being buggy and not handling a 64-bit transaction > correctly. > > We could try and create a 32-bit version of these routines, > but I'm not sure the optimisation is worth much today. > > Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1132826 Restricted link. > Tested-by: Huawei engineering. > Signed-off-by: Dave Airlie <ai...@re...> > --- > > Linus, this came up a while back I finally got some confirmation > that it fixes those servers. > > include/linux/vt_buffer.h | 4 ---- > 1 file changed, 4 deletions(-) > > diff --git a/include/linux/vt_buffer.h b/include/linux/vt_buffer.h > index 057db7d..f38c10b 100644 > --- a/include/linux/vt_buffer.h > +++ b/include/linux/vt_buffer.h > @@ -21,10 +21,6 @@ > #ifndef VT_BUF_HAVE_RW > #define scr_writew(val, addr) (*(addr) = (val)) > #define scr_readw(addr) (*(addr)) > -#define scr_memcpyw(d, s, c) memcpy(d, s, c) > -#define scr_memmovew(d, s, c) memmove(d, s, c) > -#define VT_BUF_HAVE_MEMCPYW > -#define VT_BUF_HAVE_MEMMOVEW > #endif > > #ifndef VT_BUF_HAVE_MEMSETW > |
From: Dave A. <ai...@re...> - 2015-01-29 04:11:43
|
These two copy to/from VGA memory, however on the Silicon Motion SMI750 VGA card on a 64-bit system cause console corruption. This is due to the hw being buggy and not handling a 64-bit transaction correctly. We could try and create a 32-bit version of these routines, but I'm not sure the optimisation is worth much today. Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1132826 Tested-by: Huawei engineering. Signed-off-by: Dave Airlie <ai...@re...> --- Linus, this came up a while back I finally got some confirmation that it fixes those servers. include/linux/vt_buffer.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/linux/vt_buffer.h b/include/linux/vt_buffer.h index 057db7d..f38c10b 100644 --- a/include/linux/vt_buffer.h +++ b/include/linux/vt_buffer.h @@ -21,10 +21,6 @@ #ifndef VT_BUF_HAVE_RW #define scr_writew(val, addr) (*(addr) = (val)) #define scr_readw(addr) (*(addr)) -#define scr_memcpyw(d, s, c) memcpy(d, s, c) -#define scr_memmovew(d, s, c) memmove(d, s, c) -#define VT_BUF_HAVE_MEMCPYW -#define VT_BUF_HAVE_MEMMOVEW #endif #ifndef VT_BUF_HAVE_MEMSETW -- 1.9.3 |
From: Émeric M. <eme...@gm...> - 2014-05-22 18:27:28
|
Hi, Several years ago, I started a thread about stability issues with hp zx1 chipset and ATI FireGL X1 graphics adapter on ia64 platform [1]. As an attempt to minimize problems, a quirk was introduced to restrict AGP mode to 2x [2]. This never solve the stability issues, as the real cause was in fact incorrect settings of the selectable timer interrupt frequency [3]. The attached patch thus removes the useless restriction on AGP mode. Indeed, I'm running at full AGP 4x mode for several weeks now without a problem. The attached patch applies cleanly against 3.15-rc5. Thanks, Émeric [1] http://marc.info/?l=dri-devel&m=126494456828755&w=2 [2] http://cgit.freedesktop.org/xorg/driver/xf86-video-ati/commit/?id=a7f465f73363fce409870f62173d518b1bc02ae6 [3] http://marc.info/?l=linux-ia64&m=140077543819871&w=2 |
From: Martin P. <mar...@fr...> - 2014-02-25 10:43:53
|
Hey, open source graphics enthusiasts, Yesterday, Google announced that the X.Org foundation is an accepted organization for the GSoC 2014! If you are a potential mentor and would have a project you would like a student to work on, you can propose a project in the summer of code ideas wiki page[1]. Right now, we have 16 projects ideas, half of them being related to Xpra. If you are a student and are interested in a project covered by the X.Org Foundation (DRI, mesa, X-Server, Wayland and 2D/3D/video accel), you can send your project proposal to Google[2]. Bonus points if you find a mentor yourself. You can also have a look at the summer of code ideas wiki page[1] for interesting projects. Looking forward to seeing which projects will happen for this 2014 edition! Martin Peres [1] http://www.x.org/wiki/SummerOfCodeIdeas/ [2] https://www.google-melange.com/gsoc/homepage/google/gsoc2014 |
From: Ian R. <id...@fr...> - 2013-11-04 22:00:20
|
On 10/31/2013 04:13 PM, Keith Packard wrote: > Instead of assuming that the size will be height * pitch, have the caller pass > in the size explicitly. > > Signed-off-by: Keith Packard <ke...@ke...> One nit below. With that changed, Reviewed-by: Ian Romanick <ian...@in...> > --- > src/mesa/drivers/dri/i915/intel_regions.c | 4 ++-- > src/mesa/drivers/dri/i915/intel_regions.h | 2 +- > src/mesa/drivers/dri/i915/intel_screen.c | 2 +- > src/mesa/drivers/dri/i965/intel_regions.c | 4 ++-- > src/mesa/drivers/dri/i965/intel_regions.h | 1 + > src/mesa/drivers/dri/i965/intel_screen.c | 2 +- > 6 files changed, 8 insertions(+), 7 deletions(-) > > diff --git a/src/mesa/drivers/dri/i915/intel_regions.c b/src/mesa/drivers/dri/i915/intel_regions.c > index 44f7030..9f5b89e 100644 > --- a/src/mesa/drivers/dri/i915/intel_regions.c > +++ b/src/mesa/drivers/dri/i915/intel_regions.c > @@ -209,6 +209,7 @@ struct intel_region * > intel_region_alloc_for_fd(struct intel_screen *screen, > GLuint cpp, > GLuint width, GLuint height, GLuint pitch, > + GLuint size, > int fd, const char *name) > { > struct intel_region *region; > @@ -216,8 +217,7 @@ intel_region_alloc_for_fd(struct intel_screen *screen, > int ret; > uint32_t bit_6_swizzle, tiling; > > - buffer = drm_intel_bo_gem_create_from_prime(screen->bufmgr, > - fd, height * pitch); > + buffer = drm_intel_bo_gem_create_from_prime(screen->bufmgr, fd, size); > if (buffer == NULL) > return NULL; > ret = drm_intel_bo_get_tiling(buffer, &tiling, &bit_6_swizzle); > diff --git a/src/mesa/drivers/dri/i915/intel_regions.h b/src/mesa/drivers/dri/i915/intel_regions.h > index 5c612a9..6bc4a42 100644 > --- a/src/mesa/drivers/dri/i915/intel_regions.h > +++ b/src/mesa/drivers/dri/i915/intel_regions.h > @@ -91,7 +91,7 @@ struct intel_region * > intel_region_alloc_for_fd(struct intel_screen *screen, > GLuint cpp, > GLuint width, GLuint height, GLuint pitch, > - int fd, const char *name); > + GLuint size, int fd, const char *name); > > bool > intel_region_flink(struct intel_region *region, uint32_t *name); > diff --git a/src/mesa/drivers/dri/i915/intel_screen.c b/src/mesa/drivers/dri/i915/intel_screen.c > index 3f54752..085e894 100644 > --- a/src/mesa/drivers/dri/i915/intel_screen.c > +++ b/src/mesa/drivers/dri/i915/intel_screen.c > @@ -653,7 +653,7 @@ intel_create_image_from_fds(__DRIscreen *screen, > return NULL; > > image->region = intel_region_alloc_for_fd(intelScreen, > - 1, width, height, > + 1, width, height, height * strides[0], > strides[0], fds[0], "image"); > if (image->region == NULL) { > free(image); > diff --git a/src/mesa/drivers/dri/i965/intel_regions.c b/src/mesa/drivers/dri/i965/intel_regions.c > index a6b80fd..3920f4f 100644 > --- a/src/mesa/drivers/dri/i965/intel_regions.c > +++ b/src/mesa/drivers/dri/i965/intel_regions.c > @@ -209,6 +209,7 @@ struct intel_region * > intel_region_alloc_for_fd(struct intel_screen *screen, > GLuint cpp, > GLuint width, GLuint height, GLuint pitch, > + GLuint size, > int fd, const char *name) > { > struct intel_region *region; > @@ -216,8 +217,7 @@ intel_region_alloc_for_fd(struct intel_screen *screen, > int ret; > uint32_t bit_6_swizzle, tiling; > > - buffer = drm_intel_bo_gem_create_from_prime(screen->bufmgr, > - fd, height * pitch); > + buffer = drm_intel_bo_gem_create_from_prime(screen->bufmgr, fd, size); > if (buffer == NULL) > return NULL; > ret = drm_intel_bo_get_tiling(buffer, &tiling, &bit_6_swizzle); > diff --git a/src/mesa/drivers/dri/i965/intel_regions.h b/src/mesa/drivers/dri/i965/intel_regions.h > index f08a113..05dfef3 100644 > --- a/src/mesa/drivers/dri/i965/intel_regions.h > +++ b/src/mesa/drivers/dri/i965/intel_regions.h > @@ -92,6 +92,7 @@ struct intel_region * > intel_region_alloc_for_fd(struct intel_screen *screen, > GLuint cpp, > GLuint width, GLuint height, GLuint pitch, > + GLuint size, Since this isn't exposed to the GL API, use either unsigned or size_t. > int fd, const char *name); > > bool > diff --git a/src/mesa/drivers/dri/i965/intel_screen.c b/src/mesa/drivers/dri/i965/intel_screen.c > index eafafa2..0bd0789 100644 > --- a/src/mesa/drivers/dri/i965/intel_screen.c > +++ b/src/mesa/drivers/dri/i965/intel_screen.c > @@ -712,7 +712,7 @@ intel_create_image_from_fds(__DRIscreen *screen, > return NULL; > > image->region = intel_region_alloc_for_fd(intelScreen, > - 1, width, height, > + 1, width, height, height * strides[0], > strides[0], fds[0], "image"); > if (image->region == NULL) { > free(image); > |
From: Thomas H. <the...@vm...> - 2013-11-04 20:37:03
|
On 11/04/2013 05:40 PM, Konrad Rzeszutek Wilk wrote: > On Mon, Nov 04, 2013 at 05:57:39AM -0800, Thomas Hellstrom wrote: >> The code handles three different cases: >> 1) physical page addresses. The ttm page array is used. >> 2) DMA subsystem addresses. A scatter-gather list is used. >> 3) Coherent pages. The ttm dma pool is used, together with the dma_ttm >> array os dma_addr_t >> >> Signed-off-by: Thomas Hellstrom <the...@vm...> >> Reviewed-by: Jakob Bornecrantz <ja...@vm...> > I looked at it from the TTM DMA perspective and it looks OK for me. > Great. Thanks, Thomas |
From: Thomas H. <the...@vm...> - 2013-11-04 20:36:14
|
On 11/04/2013 05:34 PM, Konrad Rzeszutek Wilk wrote: > On Mon, Nov 04, 2013 at 05:57:38AM -0800, Thomas Hellstrom wrote: >> Used by the vmwgfx driver > That looks OK to me. And baremetal should not be > affected as the Intel VT-d driver turns of the SWIOTLB > driver - so it will still use the classic ttm pool code. > > Reviewed-by: Konrad Rzeszutek Wilk <kon...@or...> > > Thanks for reviewing, Konrad. /Thomas |
From: Thomas H. <the...@vm...> - 2013-11-04 20:35:34
|
On 11/04/2013 05:30 PM, Konrad Rzeszutek Wilk wrote: > On Mon, Nov 04, 2013 at 05:57:37AM -0800, Thomas Hellstrom wrote: >> These patches makes the vmwgfx driver use the DMA API to obtain valid >> device addresses rather than blindly using physical addresses. >> >> The main motivation is to be able to use a virtual IOMMU in the future. > Ooooh. Neat! Are there any RFC patches available? > Nope, not yet. Thanks, Thomas |
From: Thomas H. <the...@vm...> - 2013-11-04 17:26:25
|
On 11/04/2013 05:27 PM, Daniel Vetter wrote: > On Mon, Nov 04, 2013 at 05:57:39AM -0800, Thomas Hellstrom wrote: >> The code handles three different cases: >> 1) physical page addresses. The ttm page array is used. >> 2) DMA subsystem addresses. A scatter-gather list is used. >> 3) Coherent pages. The ttm dma pool is used, together with the dma_ttm >> array os dma_addr_t >> >> Signed-off-by: Thomas Hellstrom <the...@vm...> >> Reviewed-by: Jakob Bornecrantz <ja...@vm...> > For i915.ko use we've added page iterators which should walk the physical > backing storage. > > commit a321e91b6d73ed011ffceed384c40d2785cf723b > Author: Imre Deak <imr...@in...> > Date: Wed Feb 27 17:02:56 2013 -0800 > > lib/scatterlist: add simple page iterator > > Yes, I saw those iterators, (nice stuff!) and my patch are using them as a "base class", handling also TTM page - and dma address arrays basically with the same interface. In the long run we might want to move ttm over to sg_tables as well. One problem, though, the page iterators break in the mapped case where sg_dma_len(sg) != sg_len(sg). An iommu implementation is allowed to reduce the sg list to a single segment, in which case those page iterators will fall apart. I was planning to see if I could fix that up, but unfortunately there is no generic dma_to_phys. It all works now because intel_iommu, amd_iommu and swiotlb all keep the number of entries in an sg list across mapping.... /Thomas |
From: Daniel V. <da...@ff...> - 2013-11-04 16:57:42
|
On Mon, Nov 04, 2013 at 05:57:39AM -0800, Thomas Hellstrom wrote: > The code handles three different cases: > 1) physical page addresses. The ttm page array is used. > 2) DMA subsystem addresses. A scatter-gather list is used. > 3) Coherent pages. The ttm dma pool is used, together with the dma_ttm > array os dma_addr_t > > Signed-off-by: Thomas Hellstrom <the...@vm...> > Reviewed-by: Jakob Bornecrantz <ja...@vm...> For i915.ko use we've added page iterators which should walk the physical backing storage. commit a321e91b6d73ed011ffceed384c40d2785cf723b Author: Imre Deak <imr...@in...> Date: Wed Feb 27 17:02:56 2013 -0800 lib/scatterlist: add simple page iterator Now we've unified all our backing storage handling around sg tables (even for stolen memory), so maybe that's not useful for you guys. Just figured I'll drop this here, it imo made our code look fairly tidy. Cheers, Daniel > --- > drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c | 379 ++++++++++++++++++++++++++++++-- > drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 87 +++++++- > drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 98 ++++++++- > drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c | 150 ++++++------- > 4 files changed, 620 insertions(+), 94 deletions(-) > > diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c > index 96dc84d..7776e6f 100644 > --- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c > +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c > @@ -141,37 +141,374 @@ struct ttm_placement vmw_srf_placement = { > }; > > struct vmw_ttm_tt { > - struct ttm_tt ttm; > + struct ttm_dma_tt dma_ttm; > struct vmw_private *dev_priv; > int gmr_id; > + struct sg_table sgt; > + struct vmw_sg_table vsgt; > + uint64_t sg_alloc_size; > + bool mapped; > }; > > +/** > + * Helper functions to advance a struct vmw_piter iterator. > + * > + * @viter: Pointer to the iterator. > + * > + * These functions return false if past the end of the list, > + * true otherwise. Functions are selected depending on the current > + * DMA mapping mode. > + */ > +static bool __vmw_piter_non_sg_next(struct vmw_piter *viter) > +{ > + return ++(viter->i) < viter->num_pages; > +} > + > +static bool __vmw_piter_sg_next(struct vmw_piter *viter) > +{ > + return __sg_page_iter_next(&viter->iter); > +} > + > + > +/** > + * Helper functions to return a pointer to the current page. > + * > + * @viter: Pointer to the iterator > + * > + * These functions return a pointer to the page currently > + * pointed to by @viter. Functions are selected depending on the > + * current mapping mode. > + */ > +static struct page *__vmw_piter_non_sg_page(struct vmw_piter *viter) > +{ > + return viter->pages[viter->i]; > +} > + > +static struct page *__vmw_piter_sg_page(struct vmw_piter *viter) > +{ > + return sg_page_iter_page(&viter->iter); > +} > + > + > +/** > + * Helper functions to return the DMA address of the current page. > + * > + * @viter: Pointer to the iterator > + * > + * These functions return the DMA address of the page currently > + * pointed to by @viter. Functions are selected depending on the > + * current mapping mode. > + */ > +static dma_addr_t __vmw_piter_phys_addr(struct vmw_piter *viter) > +{ > + return page_to_phys(viter->pages[viter->i]); > +} > + > +static dma_addr_t __vmw_piter_dma_addr(struct vmw_piter *viter) > +{ > + return viter->addrs[viter->i]; > +} > + > +static dma_addr_t __vmw_piter_sg_addr(struct vmw_piter *viter) > +{ > + return sg_page_iter_dma_address(&viter->iter); > +} > + > + > +/** > + * vmw_piter_start - Initialize a struct vmw_piter. > + * > + * @viter: Pointer to the iterator to initialize > + * @vsgt: Pointer to a struct vmw_sg_table to initialize from > + * > + * Note that we're following the convention of __sg_page_iter_start, so that > + * the iterator doesn't point to a valid page after initialization; it has > + * to be advanced one step first. > + */ > +void vmw_piter_start(struct vmw_piter *viter, const struct vmw_sg_table *vsgt, > + unsigned long p_offset) > +{ > + viter->i = p_offset - 1; > + viter->num_pages = vsgt->num_pages; > + switch (vsgt->mode) { > + case vmw_dma_phys: > + viter->next = &__vmw_piter_non_sg_next; > + viter->dma_address = &__vmw_piter_phys_addr; > + viter->page = &__vmw_piter_non_sg_page; > + viter->pages = vsgt->pages; > + break; > + case vmw_dma_alloc_coherent: > + viter->next = &__vmw_piter_non_sg_next; > + viter->dma_address = &__vmw_piter_dma_addr; > + viter->page = &__vmw_piter_non_sg_page; > + viter->addrs = vsgt->addrs; > + break; > + case vmw_dma_map_populate: > + case vmw_dma_map_bind: > + viter->next = &__vmw_piter_sg_next; > + viter->dma_address = &__vmw_piter_sg_addr; > + viter->page = &__vmw_piter_sg_page; > + __sg_page_iter_start(&viter->iter, vsgt->sgt->sgl, > + vsgt->sgt->orig_nents, p_offset); > + break; > + default: > + BUG(); > + } > +} > + > +/** > + * vmw_ttm_unmap_from_dma - unmap device addresses previsouly mapped for > + * TTM pages > + * > + * @vmw_tt: Pointer to a struct vmw_ttm_backend > + * > + * Used to free dma mappings previously mapped by vmw_ttm_map_for_dma. > + */ > +static void vmw_ttm_unmap_from_dma(struct vmw_ttm_tt *vmw_tt) > +{ > + struct device *dev = vmw_tt->dev_priv->dev->dev; > + > + dma_unmap_sg(dev, vmw_tt->sgt.sgl, vmw_tt->sgt.nents, > + DMA_BIDIRECTIONAL); > + vmw_tt->sgt.nents = vmw_tt->sgt.orig_nents; > +} > + > +/** > + * vmw_ttm_map_for_dma - map TTM pages to get device addresses > + * > + * @vmw_tt: Pointer to a struct vmw_ttm_backend > + * > + * This function is used to get device addresses from the kernel DMA layer. > + * However, it's violating the DMA API in that when this operation has been > + * performed, it's illegal for the CPU to write to the pages without first > + * unmapping the DMA mappings, or calling dma_sync_sg_for_cpu(). It is > + * therefore only legal to call this function if we know that the function > + * dma_sync_sg_for_cpu() is a NOP, and dma_sync_sg_for_device() is at most > + * a CPU write buffer flush. > + */ > +static int vmw_ttm_map_for_dma(struct vmw_ttm_tt *vmw_tt) > +{ > + struct device *dev = vmw_tt->dev_priv->dev->dev; > + int ret; > + > + ret = dma_map_sg(dev, vmw_tt->sgt.sgl, vmw_tt->sgt.orig_nents, > + DMA_BIDIRECTIONAL); > + if (unlikely(ret == 0)) > + return -ENOMEM; > + > + vmw_tt->sgt.nents = ret; > + > + return 0; > +} > + > +/** > + * vmw_ttm_map_dma - Make sure TTM pages are visible to the device > + * > + * @vmw_tt: Pointer to a struct vmw_ttm_tt > + * > + * Select the correct function for and make sure the TTM pages are > + * visible to the device. Allocate storage for the device mappings. > + * If a mapping has already been performed, indicated by the storage > + * pointer being non NULL, the function returns success. > + */ > +static int vmw_ttm_map_dma(struct vmw_ttm_tt *vmw_tt) > +{ > + struct vmw_private *dev_priv = vmw_tt->dev_priv; > + struct ttm_mem_global *glob = vmw_mem_glob(dev_priv); > + struct vmw_sg_table *vsgt = &vmw_tt->vsgt; > + struct vmw_piter iter; > + dma_addr_t old; > + int ret = 0; > + static size_t sgl_size; > + static size_t sgt_size; > + > + if (vmw_tt->mapped) > + return 0; > + > + vsgt->mode = dev_priv->map_mode; > + vsgt->pages = vmw_tt->dma_ttm.ttm.pages; > + vsgt->num_pages = vmw_tt->dma_ttm.ttm.num_pages; > + vsgt->addrs = vmw_tt->dma_ttm.dma_address; > + vsgt->sgt = &vmw_tt->sgt; > + > + switch (dev_priv->map_mode) { > + case vmw_dma_map_bind: > + case vmw_dma_map_populate: > + if (unlikely(!sgl_size)) { > + sgl_size = ttm_round_pot(sizeof(struct scatterlist)); > + sgt_size = ttm_round_pot(sizeof(struct sg_table)); > + } > + vmw_tt->sg_alloc_size = sgt_size + sgl_size * vsgt->num_pages; > + ret = ttm_mem_global_alloc(glob, vmw_tt->sg_alloc_size, false, > + true); > + if (unlikely(ret != 0)) > + return ret; > + > + ret = sg_alloc_table_from_pages(&vmw_tt->sgt, vsgt->pages, > + vsgt->num_pages, 0, > + (unsigned long) > + vsgt->num_pages << PAGE_SHIFT, > + GFP_KERNEL); > + if (unlikely(ret != 0)) > + goto out_sg_alloc_fail; > + > + if (vsgt->num_pages > vmw_tt->sgt.nents) { > + uint64_t over_alloc = > + sgl_size * (vsgt->num_pages - > + vmw_tt->sgt.nents); > + > + ttm_mem_global_free(glob, over_alloc); > + vmw_tt->sg_alloc_size -= over_alloc; > + } > + > + ret = vmw_ttm_map_for_dma(vmw_tt); > + if (unlikely(ret != 0)) > + goto out_map_fail; > + > + break; > + default: > + break; > + } > + > + old = ~((dma_addr_t) 0); > + vmw_tt->vsgt.num_regions = 0; > + for (vmw_piter_start(&iter, vsgt, 0); vmw_piter_next(&iter);) { > + dma_addr_t cur = vmw_piter_dma_addr(&iter); > + > + if (cur != old + PAGE_SIZE) > + vmw_tt->vsgt.num_regions++; > + old = cur; > + } > + > + vmw_tt->mapped = true; > + return 0; > + > +out_map_fail: > + sg_free_table(vmw_tt->vsgt.sgt); > + vmw_tt->vsgt.sgt = NULL; > +out_sg_alloc_fail: > + ttm_mem_global_free(glob, vmw_tt->sg_alloc_size); > + return ret; > +} > + > +/** > + * vmw_ttm_unmap_dma - Tear down any TTM page device mappings > + * > + * @vmw_tt: Pointer to a struct vmw_ttm_tt > + * > + * Tear down any previously set up device DMA mappings and free > + * any storage space allocated for them. If there are no mappings set up, > + * this function is a NOP. > + */ > +static void vmw_ttm_unmap_dma(struct vmw_ttm_tt *vmw_tt) > +{ > + struct vmw_private *dev_priv = vmw_tt->dev_priv; > + > + if (!vmw_tt->vsgt.sgt) > + return; > + > + switch (dev_priv->map_mode) { > + case vmw_dma_map_bind: > + case vmw_dma_map_populate: > + vmw_ttm_unmap_from_dma(vmw_tt); > + sg_free_table(vmw_tt->vsgt.sgt); > + vmw_tt->vsgt.sgt = NULL; > + ttm_mem_global_free(vmw_mem_glob(dev_priv), > + vmw_tt->sg_alloc_size); > + break; > + default: > + break; > + } > + vmw_tt->mapped = false; > +} > + > static int vmw_ttm_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem) > { > - struct vmw_ttm_tt *vmw_be = container_of(ttm, struct vmw_ttm_tt, ttm); > + struct vmw_ttm_tt *vmw_be = > + container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); > + int ret; > + > + ret = vmw_ttm_map_dma(vmw_be); > + if (unlikely(ret != 0)) > + return ret; > > vmw_be->gmr_id = bo_mem->start; > > - return vmw_gmr_bind(vmw_be->dev_priv, ttm->pages, > + return vmw_gmr_bind(vmw_be->dev_priv, &vmw_be->vsgt, > ttm->num_pages, vmw_be->gmr_id); > } > > static int vmw_ttm_unbind(struct ttm_tt *ttm) > { > - struct vmw_ttm_tt *vmw_be = container_of(ttm, struct vmw_ttm_tt, ttm); > + struct vmw_ttm_tt *vmw_be = > + container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); > > vmw_gmr_unbind(vmw_be->dev_priv, vmw_be->gmr_id); > + > + if (vmw_be->dev_priv->map_mode == vmw_dma_map_bind) > + vmw_ttm_unmap_dma(vmw_be); > + > return 0; > } > > static void vmw_ttm_destroy(struct ttm_tt *ttm) > { > - struct vmw_ttm_tt *vmw_be = container_of(ttm, struct vmw_ttm_tt, ttm); > - > - ttm_tt_fini(ttm); > + struct vmw_ttm_tt *vmw_be = > + container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); > + > + vmw_ttm_unmap_dma(vmw_be); > + if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent) > + ttm_dma_tt_fini(&vmw_be->dma_ttm); > + else > + ttm_tt_fini(ttm); > kfree(vmw_be); > } > > +static int vmw_ttm_populate(struct ttm_tt *ttm) > +{ > + struct vmw_ttm_tt *vmw_tt = > + container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); > + struct vmw_private *dev_priv = vmw_tt->dev_priv; > + struct ttm_mem_global *glob = vmw_mem_glob(dev_priv); > + int ret; > + > + if (ttm->state != tt_unpopulated) > + return 0; > + > + if (dev_priv->map_mode == vmw_dma_alloc_coherent) { > + size_t size = > + ttm_round_pot(ttm->num_pages * sizeof(dma_addr_t)); > + ret = ttm_mem_global_alloc(glob, size, false, true); > + if (unlikely(ret != 0)) > + return ret; > + > + ret = ttm_dma_populate(&vmw_tt->dma_ttm, dev_priv->dev->dev); > + if (unlikely(ret != 0)) > + ttm_mem_global_free(glob, size); > + } else > + ret = ttm_pool_populate(ttm); > + > + return ret; > +} > + > +static void vmw_ttm_unpopulate(struct ttm_tt *ttm) > +{ > + struct vmw_ttm_tt *vmw_tt = container_of(ttm, struct vmw_ttm_tt, > + dma_ttm.ttm); > + struct vmw_private *dev_priv = vmw_tt->dev_priv; > + struct ttm_mem_global *glob = vmw_mem_glob(dev_priv); > + > + vmw_ttm_unmap_dma(vmw_tt); > + if (dev_priv->map_mode == vmw_dma_alloc_coherent) { > + size_t size = > + ttm_round_pot(ttm->num_pages * sizeof(dma_addr_t)); > + > + ttm_dma_unpopulate(&vmw_tt->dma_ttm, dev_priv->dev->dev); > + ttm_mem_global_free(glob, size); > + } else > + ttm_pool_unpopulate(ttm); > +} > + > static struct ttm_backend_func vmw_ttm_func = { > .bind = vmw_ttm_bind, > .unbind = vmw_ttm_unbind, > @@ -183,20 +520,28 @@ struct ttm_tt *vmw_ttm_tt_create(struct ttm_bo_device *bdev, > struct page *dummy_read_page) > { > struct vmw_ttm_tt *vmw_be; > + int ret; > > - vmw_be = kmalloc(sizeof(*vmw_be), GFP_KERNEL); > + vmw_be = kzalloc(sizeof(*vmw_be), GFP_KERNEL); > if (!vmw_be) > return NULL; > > - vmw_be->ttm.func = &vmw_ttm_func; > + vmw_be->dma_ttm.ttm.func = &vmw_ttm_func; > vmw_be->dev_priv = container_of(bdev, struct vmw_private, bdev); > > - if (ttm_tt_init(&vmw_be->ttm, bdev, size, page_flags, dummy_read_page)) { > - kfree(vmw_be); > - return NULL; > - } > - > - return &vmw_be->ttm; > + if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent) > + ret = ttm_dma_tt_init(&vmw_be->dma_ttm, bdev, size, page_flags, > + dummy_read_page); > + else > + ret = ttm_tt_init(&vmw_be->dma_ttm.ttm, bdev, size, page_flags, > + dummy_read_page); > + if (unlikely(ret != 0)) > + goto out_no_init; > + > + return &vmw_be->dma_ttm.ttm; > +out_no_init: > + kfree(vmw_be); > + return NULL; > } > > int vmw_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) > @@ -332,8 +677,8 @@ static int vmw_sync_obj_wait(void *sync_obj, bool lazy, bool interruptible) > > struct ttm_bo_driver vmw_bo_driver = { > .ttm_tt_create = &vmw_ttm_tt_create, > - .ttm_tt_populate = &ttm_pool_populate, > - .ttm_tt_unpopulate = &ttm_pool_unpopulate, > + .ttm_tt_populate = &vmw_ttm_populate, > + .ttm_tt_unpopulate = &vmw_ttm_unpopulate, > .invalidate_caches = vmw_invalidate_caches, > .init_mem_type = vmw_init_mem_type, > .evict_flags = vmw_evict_flags, > diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c > index 1a90f0a..0b5c781 100644 > --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c > +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c > @@ -32,6 +32,7 @@ > #include <drm/ttm/ttm_bo_driver.h> > #include <drm/ttm/ttm_object.h> > #include <drm/ttm/ttm_module.h> > +#include <linux/dma_remapping.h> > > #define VMWGFX_DRIVER_NAME "vmwgfx" > #define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices" > @@ -185,6 +186,9 @@ static struct pci_device_id vmw_pci_id_list[] = { > MODULE_DEVICE_TABLE(pci, vmw_pci_id_list); > > static int enable_fbdev = IS_ENABLED(CONFIG_DRM_VMWGFX_FBCON); > +static int vmw_force_iommu; > +static int vmw_restrict_iommu; > +static int vmw_force_coherent; > > static int vmw_probe(struct pci_dev *, const struct pci_device_id *); > static void vmw_master_init(struct vmw_master *); > @@ -193,6 +197,13 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, > > MODULE_PARM_DESC(enable_fbdev, "Enable vmwgfx fbdev"); > module_param_named(enable_fbdev, enable_fbdev, int, 0600); > +MODULE_PARM_DESC(force_dma_api, "Force using the DMA API for TTM pages"); > +module_param_named(force_dma_api, vmw_force_iommu, int, 0600); > +MODULE_PARM_DESC(restrict_iommu, "Try to limit IOMMU usage for TTM pages"); > +module_param_named(restrict_iommu, vmw_restrict_iommu, int, 0600); > +MODULE_PARM_DESC(force_coherent, "Force coherent TTM pages"); > +module_param_named(force_coherent, vmw_force_coherent, int, 0600); > + > > static void vmw_print_capabilities(uint32_t capabilities) > { > @@ -427,12 +438,78 @@ static void vmw_get_initial_size(struct vmw_private *dev_priv) > dev_priv->initial_height = height; > } > > +/** > + * vmw_dma_select_mode - Determine how DMA mappings should be set up for this > + * system. > + * > + * @dev_priv: Pointer to a struct vmw_private > + * > + * This functions tries to determine the IOMMU setup and what actions > + * need to be taken by the driver to make system pages visible to the > + * device. > + * If this function decides that DMA is not possible, it returns -EINVAL. > + * The driver may then try to disable features of the device that require > + * DMA. > + */ > +static int vmw_dma_select_mode(struct vmw_private *dev_priv) > +{ > + const struct dma_map_ops *dma_ops = get_dma_ops(dev_priv->dev->dev); > + static const char *names[vmw_dma_map_max] = { > + [vmw_dma_phys] = "Using physical TTM page addresses.", > + [vmw_dma_alloc_coherent] = "Using coherent TTM pages.", > + [vmw_dma_map_populate] = "Keeping DMA mappings.", > + [vmw_dma_map_bind] = "Giving up DMA mappings early."}; > + > +#ifdef CONFIG_INTEL_IOMMU > + if (intel_iommu_enabled) { > + dev_priv->map_mode = vmw_dma_map_populate; > + goto out_fixup; > + } > +#endif > + > + if (!(vmw_force_iommu || vmw_force_coherent)) { > + dev_priv->map_mode = vmw_dma_phys; > + DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]); > + return 0; > + } > + > + dev_priv->map_mode = vmw_dma_map_populate; > + > + if (dma_ops->sync_single_for_cpu) > + dev_priv->map_mode = vmw_dma_alloc_coherent; > +#ifdef CONFIG_SWIOTLB > + if (swiotlb_nr_tbl() == 0) > + dev_priv->map_mode = vmw_dma_map_populate; > +#endif > + > +out_fixup: > + if (dev_priv->map_mode == vmw_dma_map_populate && > + vmw_restrict_iommu) > + dev_priv->map_mode = vmw_dma_map_bind; > + > + if (vmw_force_coherent) > + dev_priv->map_mode = vmw_dma_alloc_coherent; > + > +#if !defined(CONFIG_SWIOTLB) && !defined(CONFIG_INTEL_IOMMU) > + /* > + * No coherent page pool > + */ > + if (dev_priv->map_mode == vmw_dma_alloc_coherent) > + return -EINVAL; > +#endif > + > + DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]); > + > + return 0; > +} > + > static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) > { > struct vmw_private *dev_priv; > int ret; > uint32_t svga_id; > enum vmw_res_type i; > + bool refuse_dma = false; > > dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); > if (unlikely(dev_priv == NULL)) { > @@ -481,6 +558,11 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) > } > > dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES); > + ret = vmw_dma_select_mode(dev_priv); > + if (unlikely(ret != 0)) { > + DRM_INFO("Restricting capabilities due to IOMMU setup.\n"); > + refuse_dma = true; > + } > > dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE); > dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE); > @@ -558,8 +640,9 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) > } > > dev_priv->has_gmr = true; > - if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, > - dev_priv->max_gmr_ids) != 0) { > + if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) || > + refuse_dma || ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, > + dev_priv->max_gmr_ids) != 0) { > DRM_INFO("No GMR memory available. " > "Graphics memory resources are very limited.\n"); > dev_priv->has_gmr = false; > diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h > index 150ec64..e401d5d 100644 > --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h > +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h > @@ -177,6 +177,58 @@ struct vmw_res_cache_entry { > struct vmw_resource_val_node *node; > }; > > +/** > + * enum vmw_dma_map_mode - indicate how to perform TTM page dma mappings. > + */ > +enum vmw_dma_map_mode { > + vmw_dma_phys, /* Use physical page addresses */ > + vmw_dma_alloc_coherent, /* Use TTM coherent pages */ > + vmw_dma_map_populate, /* Unmap from DMA just after unpopulate */ > + vmw_dma_map_bind, /* Unmap from DMA just before unbind */ > + vmw_dma_map_max > +}; > + > +/** > + * struct vmw_sg_table - Scatter/gather table for binding, with additional > + * device-specific information. > + * > + * @sgt: Pointer to a struct sg_table with binding information > + * @num_regions: Number of regions with device-address contigous pages > + */ > +struct vmw_sg_table { > + enum vmw_dma_map_mode mode; > + struct page **pages; > + const dma_addr_t *addrs; > + struct sg_table *sgt; > + unsigned long num_regions; > + unsigned long num_pages; > +}; > + > +/** > + * struct vmw_piter - Page iterator that iterates over a list of pages > + * and DMA addresses that could be either a scatter-gather list or > + * arrays > + * > + * @pages: Array of page pointers to the pages. > + * @addrs: DMA addresses to the pages if coherent pages are used. > + * @iter: Scatter-gather page iterator. Current position in SG list. > + * @i: Current position in arrays. > + * @num_pages: Number of pages total. > + * @next: Function to advance the iterator. Returns false if past the list > + * of pages, true otherwise. > + * @dma_address: Function to return the DMA address of the current page. > + */ > +struct vmw_piter { > + struct page **pages; > + const dma_addr_t *addrs; > + struct sg_page_iter iter; > + unsigned long i; > + unsigned long num_pages; > + bool (*next)(struct vmw_piter *); > + dma_addr_t (*dma_address)(struct vmw_piter *); > + struct page *(*page)(struct vmw_piter *); > +}; > + > struct vmw_sw_context{ > struct drm_open_hash res_ht; > bool res_ht_initialized; > @@ -358,6 +410,11 @@ struct vmw_private { > > struct list_head res_lru[vmw_res_max]; > uint32_t used_memory_size; > + > + /* > + * DMA mapping stuff. > + */ > + enum vmw_dma_map_mode map_mode; > }; > > static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res) > @@ -405,7 +462,7 @@ void vmw_3d_resource_dec(struct vmw_private *dev_priv, bool hide_svga); > */ > > extern int vmw_gmr_bind(struct vmw_private *dev_priv, > - struct page *pages[], > + const struct vmw_sg_table *vsgt, > unsigned long num_pages, > int gmr_id); > extern void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id); > @@ -568,6 +625,45 @@ extern struct ttm_placement vmw_evictable_placement; > extern struct ttm_placement vmw_srf_placement; > extern struct ttm_bo_driver vmw_bo_driver; > extern int vmw_dma_quiescent(struct drm_device *dev); > +extern void vmw_piter_start(struct vmw_piter *viter, > + const struct vmw_sg_table *vsgt, > + unsigned long p_offs); > + > +/** > + * vmw_piter_next - Advance the iterator one page. > + * > + * @viter: Pointer to the iterator to advance. > + * > + * Returns false if past the list of pages, true otherwise. > + */ > +static inline bool vmw_piter_next(struct vmw_piter *viter) > +{ > + return viter->next(viter); > +} > + > +/** > + * vmw_piter_dma_addr - Return the DMA address of the current page. > + * > + * @viter: Pointer to the iterator > + * > + * Returns the DMA address of the page pointed to by @viter. > + */ > +static inline dma_addr_t vmw_piter_dma_addr(struct vmw_piter *viter) > +{ > + return viter->dma_address(viter); > +} > + > +/** > + * vmw_piter_page - Return a pointer to the current page. > + * > + * @viter: Pointer to the iterator > + * > + * Returns the DMA address of the page pointed to by @viter. > + */ > +static inline struct page *vmw_piter_page(struct vmw_piter *viter) > +{ > + return viter->page(viter); > +} > > /** > * Command submission - vmwgfx_execbuf.c > diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c > index 1a0bf07..6d09523 100644 > --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c > +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c > @@ -32,9 +32,11 @@ > #define VMW_PPN_SIZE (sizeof(unsigned long)) > /* A future safe maximum remap size. */ > #define VMW_PPN_PER_REMAP ((31 * 1024) / VMW_PPN_SIZE) > +#define DMA_ADDR_INVALID ((dma_addr_t) 0) > +#define DMA_PAGE_INVALID 0UL > > static int vmw_gmr2_bind(struct vmw_private *dev_priv, > - struct page *pages[], > + struct vmw_piter *iter, > unsigned long num_pages, > int gmr_id) > { > @@ -81,11 +83,13 @@ static int vmw_gmr2_bind(struct vmw_private *dev_priv, > > for (i = 0; i < nr; ++i) { > if (VMW_PPN_SIZE <= 4) > - *cmd = page_to_pfn(*pages++); > + *cmd = vmw_piter_dma_addr(iter) >> PAGE_SHIFT; > else > - *((uint64_t *)cmd) = page_to_pfn(*pages++); > + *((uint64_t *)cmd) = vmw_piter_dma_addr(iter) >> > + PAGE_SHIFT; > > cmd += VMW_PPN_SIZE / sizeof(*cmd); > + vmw_piter_next(iter); > } > > num_pages -= nr; > @@ -120,22 +124,54 @@ static void vmw_gmr2_unbind(struct vmw_private *dev_priv, > vmw_fifo_commit(dev_priv, define_size); > } > > + > +static void vmw_gmr_free_descriptors(struct device *dev, dma_addr_t desc_dma, > + struct list_head *desc_pages) > +{ > + struct page *page, *next; > + struct svga_guest_mem_descriptor *page_virtual; > + unsigned int desc_per_page = PAGE_SIZE / > + sizeof(struct svga_guest_mem_descriptor) - 1; > + > + if (list_empty(desc_pages)) > + return; > + > + list_for_each_entry_safe(page, next, desc_pages, lru) { > + list_del_init(&page->lru); > + > + if (likely(desc_dma != DMA_ADDR_INVALID)) { > + dma_unmap_page(dev, desc_dma, PAGE_SIZE, > + DMA_TO_DEVICE); > + } > + > + page_virtual = kmap_atomic(page); > + desc_dma = page_virtual[desc_per_page].ppn << PAGE_SHIFT; > + kunmap_atomic(page_virtual); > + > + __free_page(page); > + } > +} > + > /** > * FIXME: Adjust to the ttm lowmem / highmem storage to minimize > * the number of used descriptors. > + * > */ > > -static int vmw_gmr_build_descriptors(struct list_head *desc_pages, > - struct page *pages[], > - unsigned long num_pages) > +static int vmw_gmr_build_descriptors(struct device *dev, > + struct list_head *desc_pages, > + struct vmw_piter *iter, > + unsigned long num_pages, > + dma_addr_t *first_dma) > { > - struct page *page, *next; > + struct page *page; > struct svga_guest_mem_descriptor *page_virtual = NULL; > struct svga_guest_mem_descriptor *desc_virtual = NULL; > unsigned int desc_per_page; > unsigned long prev_pfn; > unsigned long pfn; > int ret; > + dma_addr_t desc_dma; > > desc_per_page = PAGE_SIZE / > sizeof(struct svga_guest_mem_descriptor) - 1; > @@ -148,23 +184,12 @@ static int vmw_gmr_build_descriptors(struct list_head *desc_pages, > } > > list_add_tail(&page->lru, desc_pages); > - > - /* > - * Point previous page terminating descriptor to this > - * page before unmapping it. > - */ > - > - if (likely(page_virtual != NULL)) { > - desc_virtual->ppn = page_to_pfn(page); > - kunmap_atomic(page_virtual); > - } > - > page_virtual = kmap_atomic(page); > desc_virtual = page_virtual - 1; > prev_pfn = ~(0UL); > > while (likely(num_pages != 0)) { > - pfn = page_to_pfn(*pages); > + pfn = vmw_piter_dma_addr(iter) >> PAGE_SHIFT; > > if (pfn != prev_pfn + 1) { > > @@ -181,104 +206,81 @@ static int vmw_gmr_build_descriptors(struct list_head *desc_pages, > } > prev_pfn = pfn; > --num_pages; > - ++pages; > + vmw_piter_next(iter); > } > > - (++desc_virtual)->ppn = cpu_to_le32(0); > + (++desc_virtual)->ppn = DMA_PAGE_INVALID; > desc_virtual->num_pages = cpu_to_le32(0); > + kunmap_atomic(page_virtual); > } > > - if (likely(page_virtual != NULL)) > + desc_dma = 0; > + list_for_each_entry_reverse(page, desc_pages, lru) { > + page_virtual = kmap_atomic(page); > + page_virtual[desc_per_page].ppn = desc_dma >> PAGE_SHIFT; > kunmap_atomic(page_virtual); > + desc_dma = dma_map_page(dev, page, 0, PAGE_SIZE, > + DMA_TO_DEVICE); > + > + if (unlikely(dma_mapping_error(dev, desc_dma))) > + goto out_err; > + } > + *first_dma = desc_dma; > > return 0; > out_err: > - list_for_each_entry_safe(page, next, desc_pages, lru) { > - list_del_init(&page->lru); > - __free_page(page); > - } > + vmw_gmr_free_descriptors(dev, DMA_ADDR_INVALID, desc_pages); > return ret; > } > > -static inline void vmw_gmr_free_descriptors(struct list_head *desc_pages) > -{ > - struct page *page, *next; > - > - list_for_each_entry_safe(page, next, desc_pages, lru) { > - list_del_init(&page->lru); > - __free_page(page); > - } > -} > - > static void vmw_gmr_fire_descriptors(struct vmw_private *dev_priv, > - int gmr_id, struct list_head *desc_pages) > + int gmr_id, dma_addr_t desc_dma) > { > - struct page *page; > - > - if (unlikely(list_empty(desc_pages))) > - return; > - > - page = list_entry(desc_pages->next, struct page, lru); > - > mutex_lock(&dev_priv->hw_mutex); > > vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id); > wmb(); > - vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, page_to_pfn(page)); > + vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, desc_dma >> PAGE_SHIFT); > mb(); > > mutex_unlock(&dev_priv->hw_mutex); > > } > > -/** > - * FIXME: Adjust to the ttm lowmem / highmem storage to minimize > - * the number of used descriptors. > - */ > - > -static unsigned long vmw_gmr_count_descriptors(struct page *pages[], > - unsigned long num_pages) > -{ > - unsigned long prev_pfn = ~(0UL); > - unsigned long pfn; > - unsigned long descriptors = 0; > - > - while (num_pages--) { > - pfn = page_to_pfn(*pages++); > - if (prev_pfn + 1 != pfn) > - ++descriptors; > - prev_pfn = pfn; > - } > - > - return descriptors; > -} > - > int vmw_gmr_bind(struct vmw_private *dev_priv, > - struct page *pages[], > + const struct vmw_sg_table *vsgt, > unsigned long num_pages, > int gmr_id) > { > struct list_head desc_pages; > + dma_addr_t desc_dma = 0; > + struct device *dev = dev_priv->dev->dev; > + struct vmw_piter data_iter; > int ret; > > + vmw_piter_start(&data_iter, vsgt, 0); > + > + if (unlikely(!vmw_piter_next(&data_iter))) > + return 0; > + > if (likely(dev_priv->capabilities & SVGA_CAP_GMR2)) > - return vmw_gmr2_bind(dev_priv, pages, num_pages, gmr_id); > + return vmw_gmr2_bind(dev_priv, &data_iter, num_pages, gmr_id); > > if (unlikely(!(dev_priv->capabilities & SVGA_CAP_GMR))) > return -EINVAL; > > - if (vmw_gmr_count_descriptors(pages, num_pages) > > - dev_priv->max_gmr_descriptors) > + if (vsgt->num_regions > dev_priv->max_gmr_descriptors) > return -EINVAL; > > INIT_LIST_HEAD(&desc_pages); > > - ret = vmw_gmr_build_descriptors(&desc_pages, pages, num_pages); > + ret = vmw_gmr_build_descriptors(dev, &desc_pages, &data_iter, > + num_pages, &desc_dma); > if (unlikely(ret != 0)) > return ret; > > - vmw_gmr_fire_descriptors(dev_priv, gmr_id, &desc_pages); > - vmw_gmr_free_descriptors(&desc_pages); > + vmw_gmr_fire_descriptors(dev_priv, gmr_id, desc_dma); > + vmw_gmr_free_descriptors(dev, desc_dma, &desc_pages); > > return 0; > } > -- > 1.7.10.4 > > ------------------------------------------------------------------------------ > Android is increasing in popularity, but the open development platform that > developers love is also attractive to malware creators. Download this white > paper to learn more about secure code signing practices that can help keep > Android apps secure. > http://pubads.g.doubleclick.net/gampad/clk?id=65839951&iu=/4140/ostg.clktrk > -- > _______________________________________________ > Dri-devel mailing list > Dri...@li... > https://lists.sourceforge.net/lists/listinfo/dri-devel > _______________________________________________ > dri-devel mailing list > dri...@li... > http://lists.freedesktop.org/mailman/listinfo/dri-devel -- Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch |
From: Konrad R. W. <kon...@or...> - 2013-11-04 16:41:00
|
On Mon, Nov 04, 2013 at 05:57:39AM -0800, Thomas Hellstrom wrote: > The code handles three different cases: > 1) physical page addresses. The ttm page array is used. > 2) DMA subsystem addresses. A scatter-gather list is used. > 3) Coherent pages. The ttm dma pool is used, together with the dma_ttm > array os dma_addr_t > > Signed-off-by: Thomas Hellstrom <the...@vm...> > Reviewed-by: Jakob Bornecrantz <ja...@vm...> I looked at it from the TTM DMA perspective and it looks OK for me. > --- > drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c | 379 ++++++++++++++++++++++++++++++-- > drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 87 +++++++- > drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 98 ++++++++- > drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c | 150 ++++++------- > 4 files changed, 620 insertions(+), 94 deletions(-) > > diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c > index 96dc84d..7776e6f 100644 > --- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c > +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c > @@ -141,37 +141,374 @@ struct ttm_placement vmw_srf_placement = { > }; > > struct vmw_ttm_tt { > - struct ttm_tt ttm; > + struct ttm_dma_tt dma_ttm; > struct vmw_private *dev_priv; > int gmr_id; > + struct sg_table sgt; > + struct vmw_sg_table vsgt; > + uint64_t sg_alloc_size; > + bool mapped; > }; > > +/** > + * Helper functions to advance a struct vmw_piter iterator. > + * > + * @viter: Pointer to the iterator. > + * > + * These functions return false if past the end of the list, > + * true otherwise. Functions are selected depending on the current > + * DMA mapping mode. > + */ > +static bool __vmw_piter_non_sg_next(struct vmw_piter *viter) > +{ > + return ++(viter->i) < viter->num_pages; > +} > + > +static bool __vmw_piter_sg_next(struct vmw_piter *viter) > +{ > + return __sg_page_iter_next(&viter->iter); > +} > + > + > +/** > + * Helper functions to return a pointer to the current page. > + * > + * @viter: Pointer to the iterator > + * > + * These functions return a pointer to the page currently > + * pointed to by @viter. Functions are selected depending on the > + * current mapping mode. > + */ > +static struct page *__vmw_piter_non_sg_page(struct vmw_piter *viter) > +{ > + return viter->pages[viter->i]; > +} > + > +static struct page *__vmw_piter_sg_page(struct vmw_piter *viter) > +{ > + return sg_page_iter_page(&viter->iter); > +} > + > + > +/** > + * Helper functions to return the DMA address of the current page. > + * > + * @viter: Pointer to the iterator > + * > + * These functions return the DMA address of the page currently > + * pointed to by @viter. Functions are selected depending on the > + * current mapping mode. > + */ > +static dma_addr_t __vmw_piter_phys_addr(struct vmw_piter *viter) > +{ > + return page_to_phys(viter->pages[viter->i]); > +} > + > +static dma_addr_t __vmw_piter_dma_addr(struct vmw_piter *viter) > +{ > + return viter->addrs[viter->i]; > +} > + > +static dma_addr_t __vmw_piter_sg_addr(struct vmw_piter *viter) > +{ > + return sg_page_iter_dma_address(&viter->iter); > +} > + > + > +/** > + * vmw_piter_start - Initialize a struct vmw_piter. > + * > + * @viter: Pointer to the iterator to initialize > + * @vsgt: Pointer to a struct vmw_sg_table to initialize from > + * > + * Note that we're following the convention of __sg_page_iter_start, so that > + * the iterator doesn't point to a valid page after initialization; it has > + * to be advanced one step first. > + */ > +void vmw_piter_start(struct vmw_piter *viter, const struct vmw_sg_table *vsgt, > + unsigned long p_offset) > +{ > + viter->i = p_offset - 1; > + viter->num_pages = vsgt->num_pages; > + switch (vsgt->mode) { > + case vmw_dma_phys: > + viter->next = &__vmw_piter_non_sg_next; > + viter->dma_address = &__vmw_piter_phys_addr; > + viter->page = &__vmw_piter_non_sg_page; > + viter->pages = vsgt->pages; > + break; > + case vmw_dma_alloc_coherent: > + viter->next = &__vmw_piter_non_sg_next; > + viter->dma_address = &__vmw_piter_dma_addr; > + viter->page = &__vmw_piter_non_sg_page; > + viter->addrs = vsgt->addrs; > + break; > + case vmw_dma_map_populate: > + case vmw_dma_map_bind: > + viter->next = &__vmw_piter_sg_next; > + viter->dma_address = &__vmw_piter_sg_addr; > + viter->page = &__vmw_piter_sg_page; > + __sg_page_iter_start(&viter->iter, vsgt->sgt->sgl, > + vsgt->sgt->orig_nents, p_offset); > + break; > + default: > + BUG(); > + } > +} > + > +/** > + * vmw_ttm_unmap_from_dma - unmap device addresses previsouly mapped for > + * TTM pages > + * > + * @vmw_tt: Pointer to a struct vmw_ttm_backend > + * > + * Used to free dma mappings previously mapped by vmw_ttm_map_for_dma. > + */ > +static void vmw_ttm_unmap_from_dma(struct vmw_ttm_tt *vmw_tt) > +{ > + struct device *dev = vmw_tt->dev_priv->dev->dev; > + > + dma_unmap_sg(dev, vmw_tt->sgt.sgl, vmw_tt->sgt.nents, > + DMA_BIDIRECTIONAL); > + vmw_tt->sgt.nents = vmw_tt->sgt.orig_nents; > +} > + > +/** > + * vmw_ttm_map_for_dma - map TTM pages to get device addresses > + * > + * @vmw_tt: Pointer to a struct vmw_ttm_backend > + * > + * This function is used to get device addresses from the kernel DMA layer. > + * However, it's violating the DMA API in that when this operation has been > + * performed, it's illegal for the CPU to write to the pages without first > + * unmapping the DMA mappings, or calling dma_sync_sg_for_cpu(). It is > + * therefore only legal to call this function if we know that the function > + * dma_sync_sg_for_cpu() is a NOP, and dma_sync_sg_for_device() is at most > + * a CPU write buffer flush. > + */ > +static int vmw_ttm_map_for_dma(struct vmw_ttm_tt *vmw_tt) > +{ > + struct device *dev = vmw_tt->dev_priv->dev->dev; > + int ret; > + > + ret = dma_map_sg(dev, vmw_tt->sgt.sgl, vmw_tt->sgt.orig_nents, > + DMA_BIDIRECTIONAL); > + if (unlikely(ret == 0)) > + return -ENOMEM; > + > + vmw_tt->sgt.nents = ret; > + > + return 0; > +} > + > +/** > + * vmw_ttm_map_dma - Make sure TTM pages are visible to the device > + * > + * @vmw_tt: Pointer to a struct vmw_ttm_tt > + * > + * Select the correct function for and make sure the TTM pages are > + * visible to the device. Allocate storage for the device mappings. > + * If a mapping has already been performed, indicated by the storage > + * pointer being non NULL, the function returns success. > + */ > +static int vmw_ttm_map_dma(struct vmw_ttm_tt *vmw_tt) > +{ > + struct vmw_private *dev_priv = vmw_tt->dev_priv; > + struct ttm_mem_global *glob = vmw_mem_glob(dev_priv); > + struct vmw_sg_table *vsgt = &vmw_tt->vsgt; > + struct vmw_piter iter; > + dma_addr_t old; > + int ret = 0; > + static size_t sgl_size; > + static size_t sgt_size; > + > + if (vmw_tt->mapped) > + return 0; > + > + vsgt->mode = dev_priv->map_mode; > + vsgt->pages = vmw_tt->dma_ttm.ttm.pages; > + vsgt->num_pages = vmw_tt->dma_ttm.ttm.num_pages; > + vsgt->addrs = vmw_tt->dma_ttm.dma_address; > + vsgt->sgt = &vmw_tt->sgt; > + > + switch (dev_priv->map_mode) { > + case vmw_dma_map_bind: > + case vmw_dma_map_populate: > + if (unlikely(!sgl_size)) { > + sgl_size = ttm_round_pot(sizeof(struct scatterlist)); > + sgt_size = ttm_round_pot(sizeof(struct sg_table)); > + } > + vmw_tt->sg_alloc_size = sgt_size + sgl_size * vsgt->num_pages; > + ret = ttm_mem_global_alloc(glob, vmw_tt->sg_alloc_size, false, > + true); > + if (unlikely(ret != 0)) > + return ret; > + > + ret = sg_alloc_table_from_pages(&vmw_tt->sgt, vsgt->pages, > + vsgt->num_pages, 0, > + (unsigned long) > + vsgt->num_pages << PAGE_SHIFT, > + GFP_KERNEL); > + if (unlikely(ret != 0)) > + goto out_sg_alloc_fail; > + > + if (vsgt->num_pages > vmw_tt->sgt.nents) { > + uint64_t over_alloc = > + sgl_size * (vsgt->num_pages - > + vmw_tt->sgt.nents); > + > + ttm_mem_global_free(glob, over_alloc); > + vmw_tt->sg_alloc_size -= over_alloc; > + } > + > + ret = vmw_ttm_map_for_dma(vmw_tt); > + if (unlikely(ret != 0)) > + goto out_map_fail; > + > + break; > + default: > + break; > + } > + > + old = ~((dma_addr_t) 0); > + vmw_tt->vsgt.num_regions = 0; > + for (vmw_piter_start(&iter, vsgt, 0); vmw_piter_next(&iter);) { > + dma_addr_t cur = vmw_piter_dma_addr(&iter); > + > + if (cur != old + PAGE_SIZE) > + vmw_tt->vsgt.num_regions++; > + old = cur; > + } > + > + vmw_tt->mapped = true; > + return 0; > + > +out_map_fail: > + sg_free_table(vmw_tt->vsgt.sgt); > + vmw_tt->vsgt.sgt = NULL; > +out_sg_alloc_fail: > + ttm_mem_global_free(glob, vmw_tt->sg_alloc_size); > + return ret; > +} > + > +/** > + * vmw_ttm_unmap_dma - Tear down any TTM page device mappings > + * > + * @vmw_tt: Pointer to a struct vmw_ttm_tt > + * > + * Tear down any previously set up device DMA mappings and free > + * any storage space allocated for them. If there are no mappings set up, > + * this function is a NOP. > + */ > +static void vmw_ttm_unmap_dma(struct vmw_ttm_tt *vmw_tt) > +{ > + struct vmw_private *dev_priv = vmw_tt->dev_priv; > + > + if (!vmw_tt->vsgt.sgt) > + return; > + > + switch (dev_priv->map_mode) { > + case vmw_dma_map_bind: > + case vmw_dma_map_populate: > + vmw_ttm_unmap_from_dma(vmw_tt); > + sg_free_table(vmw_tt->vsgt.sgt); > + vmw_tt->vsgt.sgt = NULL; > + ttm_mem_global_free(vmw_mem_glob(dev_priv), > + vmw_tt->sg_alloc_size); > + break; > + default: > + break; > + } > + vmw_tt->mapped = false; > +} > + > static int vmw_ttm_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem) > { > - struct vmw_ttm_tt *vmw_be = container_of(ttm, struct vmw_ttm_tt, ttm); > + struct vmw_ttm_tt *vmw_be = > + container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); > + int ret; > + > + ret = vmw_ttm_map_dma(vmw_be); > + if (unlikely(ret != 0)) > + return ret; > > vmw_be->gmr_id = bo_mem->start; > > - return vmw_gmr_bind(vmw_be->dev_priv, ttm->pages, > + return vmw_gmr_bind(vmw_be->dev_priv, &vmw_be->vsgt, > ttm->num_pages, vmw_be->gmr_id); > } > > static int vmw_ttm_unbind(struct ttm_tt *ttm) > { > - struct vmw_ttm_tt *vmw_be = container_of(ttm, struct vmw_ttm_tt, ttm); > + struct vmw_ttm_tt *vmw_be = > + container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); > > vmw_gmr_unbind(vmw_be->dev_priv, vmw_be->gmr_id); > + > + if (vmw_be->dev_priv->map_mode == vmw_dma_map_bind) > + vmw_ttm_unmap_dma(vmw_be); > + > return 0; > } > > static void vmw_ttm_destroy(struct ttm_tt *ttm) > { > - struct vmw_ttm_tt *vmw_be = container_of(ttm, struct vmw_ttm_tt, ttm); > - > - ttm_tt_fini(ttm); > + struct vmw_ttm_tt *vmw_be = > + container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); > + > + vmw_ttm_unmap_dma(vmw_be); > + if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent) > + ttm_dma_tt_fini(&vmw_be->dma_ttm); > + else > + ttm_tt_fini(ttm); > kfree(vmw_be); > } > > +static int vmw_ttm_populate(struct ttm_tt *ttm) > +{ > + struct vmw_ttm_tt *vmw_tt = > + container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); > + struct vmw_private *dev_priv = vmw_tt->dev_priv; > + struct ttm_mem_global *glob = vmw_mem_glob(dev_priv); > + int ret; > + > + if (ttm->state != tt_unpopulated) > + return 0; > + > + if (dev_priv->map_mode == vmw_dma_alloc_coherent) { > + size_t size = > + ttm_round_pot(ttm->num_pages * sizeof(dma_addr_t)); > + ret = ttm_mem_global_alloc(glob, size, false, true); > + if (unlikely(ret != 0)) > + return ret; > + > + ret = ttm_dma_populate(&vmw_tt->dma_ttm, dev_priv->dev->dev); > + if (unlikely(ret != 0)) > + ttm_mem_global_free(glob, size); > + } else > + ret = ttm_pool_populate(ttm); > + > + return ret; > +} > + > +static void vmw_ttm_unpopulate(struct ttm_tt *ttm) > +{ > + struct vmw_ttm_tt *vmw_tt = container_of(ttm, struct vmw_ttm_tt, > + dma_ttm.ttm); > + struct vmw_private *dev_priv = vmw_tt->dev_priv; > + struct ttm_mem_global *glob = vmw_mem_glob(dev_priv); > + > + vmw_ttm_unmap_dma(vmw_tt); > + if (dev_priv->map_mode == vmw_dma_alloc_coherent) { > + size_t size = > + ttm_round_pot(ttm->num_pages * sizeof(dma_addr_t)); > + > + ttm_dma_unpopulate(&vmw_tt->dma_ttm, dev_priv->dev->dev); > + ttm_mem_global_free(glob, size); > + } else > + ttm_pool_unpopulate(ttm); > +} > + > static struct ttm_backend_func vmw_ttm_func = { > .bind = vmw_ttm_bind, > .unbind = vmw_ttm_unbind, > @@ -183,20 +520,28 @@ struct ttm_tt *vmw_ttm_tt_create(struct ttm_bo_device *bdev, > struct page *dummy_read_page) > { > struct vmw_ttm_tt *vmw_be; > + int ret; > > - vmw_be = kmalloc(sizeof(*vmw_be), GFP_KERNEL); > + vmw_be = kzalloc(sizeof(*vmw_be), GFP_KERNEL); > if (!vmw_be) > return NULL; > > - vmw_be->ttm.func = &vmw_ttm_func; > + vmw_be->dma_ttm.ttm.func = &vmw_ttm_func; > vmw_be->dev_priv = container_of(bdev, struct vmw_private, bdev); > > - if (ttm_tt_init(&vmw_be->ttm, bdev, size, page_flags, dummy_read_page)) { > - kfree(vmw_be); > - return NULL; > - } > - > - return &vmw_be->ttm; > + if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent) > + ret = ttm_dma_tt_init(&vmw_be->dma_ttm, bdev, size, page_flags, > + dummy_read_page); > + else > + ret = ttm_tt_init(&vmw_be->dma_ttm.ttm, bdev, size, page_flags, > + dummy_read_page); > + if (unlikely(ret != 0)) > + goto out_no_init; > + > + return &vmw_be->dma_ttm.ttm; > +out_no_init: > + kfree(vmw_be); > + return NULL; > } > > int vmw_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) > @@ -332,8 +677,8 @@ static int vmw_sync_obj_wait(void *sync_obj, bool lazy, bool interruptible) > > struct ttm_bo_driver vmw_bo_driver = { > .ttm_tt_create = &vmw_ttm_tt_create, > - .ttm_tt_populate = &ttm_pool_populate, > - .ttm_tt_unpopulate = &ttm_pool_unpopulate, > + .ttm_tt_populate = &vmw_ttm_populate, > + .ttm_tt_unpopulate = &vmw_ttm_unpopulate, > .invalidate_caches = vmw_invalidate_caches, > .init_mem_type = vmw_init_mem_type, > .evict_flags = vmw_evict_flags, > diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c > index 1a90f0a..0b5c781 100644 > --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c > +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c > @@ -32,6 +32,7 @@ > #include <drm/ttm/ttm_bo_driver.h> > #include <drm/ttm/ttm_object.h> > #include <drm/ttm/ttm_module.h> > +#include <linux/dma_remapping.h> > > #define VMWGFX_DRIVER_NAME "vmwgfx" > #define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices" > @@ -185,6 +186,9 @@ static struct pci_device_id vmw_pci_id_list[] = { > MODULE_DEVICE_TABLE(pci, vmw_pci_id_list); > > static int enable_fbdev = IS_ENABLED(CONFIG_DRM_VMWGFX_FBCON); > +static int vmw_force_iommu; > +static int vmw_restrict_iommu; > +static int vmw_force_coherent; > > static int vmw_probe(struct pci_dev *, const struct pci_device_id *); > static void vmw_master_init(struct vmw_master *); > @@ -193,6 +197,13 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, > > MODULE_PARM_DESC(enable_fbdev, "Enable vmwgfx fbdev"); > module_param_named(enable_fbdev, enable_fbdev, int, 0600); > +MODULE_PARM_DESC(force_dma_api, "Force using the DMA API for TTM pages"); > +module_param_named(force_dma_api, vmw_force_iommu, int, 0600); > +MODULE_PARM_DESC(restrict_iommu, "Try to limit IOMMU usage for TTM pages"); > +module_param_named(restrict_iommu, vmw_restrict_iommu, int, 0600); > +MODULE_PARM_DESC(force_coherent, "Force coherent TTM pages"); > +module_param_named(force_coherent, vmw_force_coherent, int, 0600); > + > > static void vmw_print_capabilities(uint32_t capabilities) > { > @@ -427,12 +438,78 @@ static void vmw_get_initial_size(struct vmw_private *dev_priv) > dev_priv->initial_height = height; > } > > +/** > + * vmw_dma_select_mode - Determine how DMA mappings should be set up for this > + * system. > + * > + * @dev_priv: Pointer to a struct vmw_private > + * > + * This functions tries to determine the IOMMU setup and what actions > + * need to be taken by the driver to make system pages visible to the > + * device. > + * If this function decides that DMA is not possible, it returns -EINVAL. > + * The driver may then try to disable features of the device that require > + * DMA. > + */ > +static int vmw_dma_select_mode(struct vmw_private *dev_priv) > +{ > + const struct dma_map_ops *dma_ops = get_dma_ops(dev_priv->dev->dev); > + static const char *names[vmw_dma_map_max] = { > + [vmw_dma_phys] = "Using physical TTM page addresses.", > + [vmw_dma_alloc_coherent] = "Using coherent TTM pages.", > + [vmw_dma_map_populate] = "Keeping DMA mappings.", > + [vmw_dma_map_bind] = "Giving up DMA mappings early."}; > + > +#ifdef CONFIG_INTEL_IOMMU > + if (intel_iommu_enabled) { > + dev_priv->map_mode = vmw_dma_map_populate; > + goto out_fixup; > + } > +#endif > + > + if (!(vmw_force_iommu || vmw_force_coherent)) { > + dev_priv->map_mode = vmw_dma_phys; > + DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]); > + return 0; > + } > + > + dev_priv->map_mode = vmw_dma_map_populate; > + > + if (dma_ops->sync_single_for_cpu) > + dev_priv->map_mode = vmw_dma_alloc_coherent; > +#ifdef CONFIG_SWIOTLB > + if (swiotlb_nr_tbl() == 0) > + dev_priv->map_mode = vmw_dma_map_populate; > +#endif > + > +out_fixup: > + if (dev_priv->map_mode == vmw_dma_map_populate && > + vmw_restrict_iommu) > + dev_priv->map_mode = vmw_dma_map_bind; > + > + if (vmw_force_coherent) > + dev_priv->map_mode = vmw_dma_alloc_coherent; > + > +#if !defined(CONFIG_SWIOTLB) && !defined(CONFIG_INTEL_IOMMU) > + /* > + * No coherent page pool > + */ > + if (dev_priv->map_mode == vmw_dma_alloc_coherent) > + return -EINVAL; > +#endif > + > + DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]); > + > + return 0; > +} > + > static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) > { > struct vmw_private *dev_priv; > int ret; > uint32_t svga_id; > enum vmw_res_type i; > + bool refuse_dma = false; > > dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); > if (unlikely(dev_priv == NULL)) { > @@ -481,6 +558,11 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) > } > > dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES); > + ret = vmw_dma_select_mode(dev_priv); > + if (unlikely(ret != 0)) { > + DRM_INFO("Restricting capabilities due to IOMMU setup.\n"); > + refuse_dma = true; > + } > > dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE); > dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE); > @@ -558,8 +640,9 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) > } > > dev_priv->has_gmr = true; > - if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, > - dev_priv->max_gmr_ids) != 0) { > + if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) || > + refuse_dma || ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, > + dev_priv->max_gmr_ids) != 0) { > DRM_INFO("No GMR memory available. " > "Graphics memory resources are very limited.\n"); > dev_priv->has_gmr = false; > diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h > index 150ec64..e401d5d 100644 > --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h > +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h > @@ -177,6 +177,58 @@ struct vmw_res_cache_entry { > struct vmw_resource_val_node *node; > }; > > +/** > + * enum vmw_dma_map_mode - indicate how to perform TTM page dma mappings. > + */ > +enum vmw_dma_map_mode { > + vmw_dma_phys, /* Use physical page addresses */ > + vmw_dma_alloc_coherent, /* Use TTM coherent pages */ > + vmw_dma_map_populate, /* Unmap from DMA just after unpopulate */ > + vmw_dma_map_bind, /* Unmap from DMA just before unbind */ > + vmw_dma_map_max > +}; > + > +/** > + * struct vmw_sg_table - Scatter/gather table for binding, with additional > + * device-specific information. > + * > + * @sgt: Pointer to a struct sg_table with binding information > + * @num_regions: Number of regions with device-address contigous pages > + */ > +struct vmw_sg_table { > + enum vmw_dma_map_mode mode; > + struct page **pages; > + const dma_addr_t *addrs; > + struct sg_table *sgt; > + unsigned long num_regions; > + unsigned long num_pages; > +}; > + > +/** > + * struct vmw_piter - Page iterator that iterates over a list of pages > + * and DMA addresses that could be either a scatter-gather list or > + * arrays > + * > + * @pages: Array of page pointers to the pages. > + * @addrs: DMA addresses to the pages if coherent pages are used. > + * @iter: Scatter-gather page iterator. Current position in SG list. > + * @i: Current position in arrays. > + * @num_pages: Number of pages total. > + * @next: Function to advance the iterator. Returns false if past the list > + * of pages, true otherwise. > + * @dma_address: Function to return the DMA address of the current page. > + */ > +struct vmw_piter { > + struct page **pages; > + const dma_addr_t *addrs; > + struct sg_page_iter iter; > + unsigned long i; > + unsigned long num_pages; > + bool (*next)(struct vmw_piter *); > + dma_addr_t (*dma_address)(struct vmw_piter *); > + struct page *(*page)(struct vmw_piter *); > +}; > + > struct vmw_sw_context{ > struct drm_open_hash res_ht; > bool res_ht_initialized; > @@ -358,6 +410,11 @@ struct vmw_private { > > struct list_head res_lru[vmw_res_max]; > uint32_t used_memory_size; > + > + /* > + * DMA mapping stuff. > + */ > + enum vmw_dma_map_mode map_mode; > }; > > static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res) > @@ -405,7 +462,7 @@ void vmw_3d_resource_dec(struct vmw_private *dev_priv, bool hide_svga); > */ > > extern int vmw_gmr_bind(struct vmw_private *dev_priv, > - struct page *pages[], > + const struct vmw_sg_table *vsgt, > unsigned long num_pages, > int gmr_id); > extern void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id); > @@ -568,6 +625,45 @@ extern struct ttm_placement vmw_evictable_placement; > extern struct ttm_placement vmw_srf_placement; > extern struct ttm_bo_driver vmw_bo_driver; > extern int vmw_dma_quiescent(struct drm_device *dev); > +extern void vmw_piter_start(struct vmw_piter *viter, > + const struct vmw_sg_table *vsgt, > + unsigned long p_offs); > + > +/** > + * vmw_piter_next - Advance the iterator one page. > + * > + * @viter: Pointer to the iterator to advance. > + * > + * Returns false if past the list of pages, true otherwise. > + */ > +static inline bool vmw_piter_next(struct vmw_piter *viter) > +{ > + return viter->next(viter); > +} > + > +/** > + * vmw_piter_dma_addr - Return the DMA address of the current page. > + * > + * @viter: Pointer to the iterator > + * > + * Returns the DMA address of the page pointed to by @viter. > + */ > +static inline dma_addr_t vmw_piter_dma_addr(struct vmw_piter *viter) > +{ > + return viter->dma_address(viter); > +} > + > +/** > + * vmw_piter_page - Return a pointer to the current page. > + * > + * @viter: Pointer to the iterator > + * > + * Returns the DMA address of the page pointed to by @viter. > + */ > +static inline struct page *vmw_piter_page(struct vmw_piter *viter) > +{ > + return viter->page(viter); > +} > > /** > * Command submission - vmwgfx_execbuf.c > diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c > index 1a0bf07..6d09523 100644 > --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c > +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c > @@ -32,9 +32,11 @@ > #define VMW_PPN_SIZE (sizeof(unsigned long)) > /* A future safe maximum remap size. */ > #define VMW_PPN_PER_REMAP ((31 * 1024) / VMW_PPN_SIZE) > +#define DMA_ADDR_INVALID ((dma_addr_t) 0) > +#define DMA_PAGE_INVALID 0UL > > static int vmw_gmr2_bind(struct vmw_private *dev_priv, > - struct page *pages[], > + struct vmw_piter *iter, > unsigned long num_pages, > int gmr_id) > { > @@ -81,11 +83,13 @@ static int vmw_gmr2_bind(struct vmw_private *dev_priv, > > for (i = 0; i < nr; ++i) { > if (VMW_PPN_SIZE <= 4) > - *cmd = page_to_pfn(*pages++); > + *cmd = vmw_piter_dma_addr(iter) >> PAGE_SHIFT; > else > - *((uint64_t *)cmd) = page_to_pfn(*pages++); > + *((uint64_t *)cmd) = vmw_piter_dma_addr(iter) >> > + PAGE_SHIFT; > > cmd += VMW_PPN_SIZE / sizeof(*cmd); > + vmw_piter_next(iter); > } > > num_pages -= nr; > @@ -120,22 +124,54 @@ static void vmw_gmr2_unbind(struct vmw_private *dev_priv, > vmw_fifo_commit(dev_priv, define_size); > } > > + > +static void vmw_gmr_free_descriptors(struct device *dev, dma_addr_t desc_dma, > + struct list_head *desc_pages) > +{ > + struct page *page, *next; > + struct svga_guest_mem_descriptor *page_virtual; > + unsigned int desc_per_page = PAGE_SIZE / > + sizeof(struct svga_guest_mem_descriptor) - 1; > + > + if (list_empty(desc_pages)) > + return; > + > + list_for_each_entry_safe(page, next, desc_pages, lru) { > + list_del_init(&page->lru); > + > + if (likely(desc_dma != DMA_ADDR_INVALID)) { > + dma_unmap_page(dev, desc_dma, PAGE_SIZE, > + DMA_TO_DEVICE); > + } > + > + page_virtual = kmap_atomic(page); > + desc_dma = page_virtual[desc_per_page].ppn << PAGE_SHIFT; > + kunmap_atomic(page_virtual); > + > + __free_page(page); > + } > +} > + > /** > * FIXME: Adjust to the ttm lowmem / highmem storage to minimize > * the number of used descriptors. > + * > */ > > -static int vmw_gmr_build_descriptors(struct list_head *desc_pages, > - struct page *pages[], > - unsigned long num_pages) > +static int vmw_gmr_build_descriptors(struct device *dev, > + struct list_head *desc_pages, > + struct vmw_piter *iter, > + unsigned long num_pages, > + dma_addr_t *first_dma) > { > - struct page *page, *next; > + struct page *page; > struct svga_guest_mem_descriptor *page_virtual = NULL; > struct svga_guest_mem_descriptor *desc_virtual = NULL; > unsigned int desc_per_page; > unsigned long prev_pfn; > unsigned long pfn; > int ret; > + dma_addr_t desc_dma; > > desc_per_page = PAGE_SIZE / > sizeof(struct svga_guest_mem_descriptor) - 1; > @@ -148,23 +184,12 @@ static int vmw_gmr_build_descriptors(struct list_head *desc_pages, > } > > list_add_tail(&page->lru, desc_pages); > - > - /* > - * Point previous page terminating descriptor to this > - * page before unmapping it. > - */ > - > - if (likely(page_virtual != NULL)) { > - desc_virtual->ppn = page_to_pfn(page); > - kunmap_atomic(page_virtual); > - } > - > page_virtual = kmap_atomic(page); > desc_virtual = page_virtual - 1; > prev_pfn = ~(0UL); > > while (likely(num_pages != 0)) { > - pfn = page_to_pfn(*pages); > + pfn = vmw_piter_dma_addr(iter) >> PAGE_SHIFT; > > if (pfn != prev_pfn + 1) { > > @@ -181,104 +206,81 @@ static int vmw_gmr_build_descriptors(struct list_head *desc_pages, > } > prev_pfn = pfn; > --num_pages; > - ++pages; > + vmw_piter_next(iter); > } > > - (++desc_virtual)->ppn = cpu_to_le32(0); > + (++desc_virtual)->ppn = DMA_PAGE_INVALID; > desc_virtual->num_pages = cpu_to_le32(0); > + kunmap_atomic(page_virtual); > } > > - if (likely(page_virtual != NULL)) > + desc_dma = 0; > + list_for_each_entry_reverse(page, desc_pages, lru) { > + page_virtual = kmap_atomic(page); > + page_virtual[desc_per_page].ppn = desc_dma >> PAGE_SHIFT; > kunmap_atomic(page_virtual); > + desc_dma = dma_map_page(dev, page, 0, PAGE_SIZE, > + DMA_TO_DEVICE); > + > + if (unlikely(dma_mapping_error(dev, desc_dma))) > + goto out_err; > + } > + *first_dma = desc_dma; > > return 0; > out_err: > - list_for_each_entry_safe(page, next, desc_pages, lru) { > - list_del_init(&page->lru); > - __free_page(page); > - } > + vmw_gmr_free_descriptors(dev, DMA_ADDR_INVALID, desc_pages); > return ret; > } > > -static inline void vmw_gmr_free_descriptors(struct list_head *desc_pages) > -{ > - struct page *page, *next; > - > - list_for_each_entry_safe(page, next, desc_pages, lru) { > - list_del_init(&page->lru); > - __free_page(page); > - } > -} > - > static void vmw_gmr_fire_descriptors(struct vmw_private *dev_priv, > - int gmr_id, struct list_head *desc_pages) > + int gmr_id, dma_addr_t desc_dma) > { > - struct page *page; > - > - if (unlikely(list_empty(desc_pages))) > - return; > - > - page = list_entry(desc_pages->next, struct page, lru); > - > mutex_lock(&dev_priv->hw_mutex); > > vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id); > wmb(); > - vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, page_to_pfn(page)); > + vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, desc_dma >> PAGE_SHIFT); > mb(); > > mutex_unlock(&dev_priv->hw_mutex); > > } > > -/** > - * FIXME: Adjust to the ttm lowmem / highmem storage to minimize > - * the number of used descriptors. > - */ > - > -static unsigned long vmw_gmr_count_descriptors(struct page *pages[], > - unsigned long num_pages) > -{ > - unsigned long prev_pfn = ~(0UL); > - unsigned long pfn; > - unsigned long descriptors = 0; > - > - while (num_pages--) { > - pfn = page_to_pfn(*pages++); > - if (prev_pfn + 1 != pfn) > - ++descriptors; > - prev_pfn = pfn; > - } > - > - return descriptors; > -} > - > int vmw_gmr_bind(struct vmw_private *dev_priv, > - struct page *pages[], > + const struct vmw_sg_table *vsgt, > unsigned long num_pages, > int gmr_id) > { > struct list_head desc_pages; > + dma_addr_t desc_dma = 0; > + struct device *dev = dev_priv->dev->dev; > + struct vmw_piter data_iter; > int ret; > > + vmw_piter_start(&data_iter, vsgt, 0); > + > + if (unlikely(!vmw_piter_next(&data_iter))) > + return 0; > + > if (likely(dev_priv->capabilities & SVGA_CAP_GMR2)) > - return vmw_gmr2_bind(dev_priv, pages, num_pages, gmr_id); > + return vmw_gmr2_bind(dev_priv, &data_iter, num_pages, gmr_id); > > if (unlikely(!(dev_priv->capabilities & SVGA_CAP_GMR))) > return -EINVAL; > > - if (vmw_gmr_count_descriptors(pages, num_pages) > > - dev_priv->max_gmr_descriptors) > + if (vsgt->num_regions > dev_priv->max_gmr_descriptors) > return -EINVAL; > > INIT_LIST_HEAD(&desc_pages); > > - ret = vmw_gmr_build_descriptors(&desc_pages, pages, num_pages); > + ret = vmw_gmr_build_descriptors(dev, &desc_pages, &data_iter, > + num_pages, &desc_dma); > if (unlikely(ret != 0)) > return ret; > > - vmw_gmr_fire_descriptors(dev_priv, gmr_id, &desc_pages); > - vmw_gmr_free_descriptors(&desc_pages); > + vmw_gmr_fire_descriptors(dev_priv, gmr_id, desc_dma); > + vmw_gmr_free_descriptors(dev, desc_dma, &desc_pages); > > return 0; > } > -- > 1.7.10.4 |
From: Konrad R. W. <kon...@or...> - 2013-11-04 16:34:57
|
On Mon, Nov 04, 2013 at 05:57:38AM -0800, Thomas Hellstrom wrote: > Used by the vmwgfx driver That looks OK to me. And baremetal should not be affected as the Intel VT-d driver turns of the SWIOTLB driver - so it will still use the classic ttm pool code. Reviewed-by: Konrad Rzeszutek Wilk <kon...@or...> > > Signed-off-by: Thomas Hellstrom <the...@vm...> > Reviewed-by: Jakob Bornecrantz <ja...@vm...> > --- > drivers/gpu/drm/ttm/Makefile | 6 +----- > drivers/gpu/drm/ttm/ttm_page_alloc_dma.c | 3 +++ > include/drm/ttm/ttm_page_alloc.h | 11 ++++++++++- > 3 files changed, 14 insertions(+), 6 deletions(-) > > diff --git a/drivers/gpu/drm/ttm/Makefile b/drivers/gpu/drm/ttm/Makefile > index b2b33dd..b433b9f 100644 > --- a/drivers/gpu/drm/ttm/Makefile > +++ b/drivers/gpu/drm/ttm/Makefile > @@ -5,10 +5,6 @@ ccflags-y := -Iinclude/drm > ttm-y := ttm_agp_backend.o ttm_memory.o ttm_tt.o ttm_bo.o \ > ttm_bo_util.o ttm_bo_vm.o ttm_module.o \ > ttm_object.o ttm_lock.o ttm_execbuf_util.o ttm_page_alloc.o \ > - ttm_bo_manager.o > - > -ifeq ($(CONFIG_SWIOTLB),y) > -ttm-y += ttm_page_alloc_dma.o > -endif > + ttm_bo_manager.o ttm_page_alloc_dma.o > > obj-$(CONFIG_DRM_TTM) += ttm.o > diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c > index 7957bee..fb8259f 100644 > --- a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c > +++ b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c > @@ -33,6 +33,7 @@ > * when freed). > */ > > +#if defined(CONFIG_SWIOTLB) || defined(CONFIG_INTEL_IOMMU) > #define pr_fmt(fmt) "[TTM] " fmt > > #include <linux/dma-mapping.h> > @@ -1142,3 +1143,5 @@ int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data) > return 0; > } > EXPORT_SYMBOL_GPL(ttm_dma_page_alloc_debugfs); > + > +#endif > diff --git a/include/drm/ttm/ttm_page_alloc.h b/include/drm/ttm/ttm_page_alloc.h > index 706b962..d1f61bf 100644 > --- a/include/drm/ttm/ttm_page_alloc.h > +++ b/include/drm/ttm/ttm_page_alloc.h > @@ -62,7 +62,7 @@ extern void ttm_pool_unpopulate(struct ttm_tt *ttm); > extern int ttm_page_alloc_debugfs(struct seq_file *m, void *data); > > > -#ifdef CONFIG_SWIOTLB > +#if defined(CONFIG_SWIOTLB) || defined(CONFIG_INTEL_IOMMU) > /** > * Initialize pool allocator. > */ > @@ -94,6 +94,15 @@ static inline int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data) > { > return 0; > } > +static inline int ttm_dma_populate(struct ttm_dma_tt *ttm_dma, > + struct device *dev) > +{ > + return -ENOMEM; > +} > +static inline void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, > + struct device *dev) > +{ > +} > #endif > > #endif > -- > 1.7.10.4 |
From: Konrad R. W. <kon...@or...> - 2013-11-04 16:31:02
|
On Mon, Nov 04, 2013 at 05:57:37AM -0800, Thomas Hellstrom wrote: > These patches makes the vmwgfx driver use the DMA API to obtain valid > device addresses rather than blindly using physical addresses. > > The main motivation is to be able to use a virtual IOMMU in the future. Ooooh. Neat! Are there any RFC patches available? > > Other TTM drivers typically map pages one by one rather than using a > scatter-gather list, but since we can benefit from having a single dma > address region set up by the IOMMU, we use a scatter-gather list instead. > > Finally to be able to handle all the dma mapping modes, we locally extend the > scatter-gather list page iterator to handle also the direct physical- and > coherent cases. > > Finally, the TTM DMA page pool is enabled also when the Intel IOMMU is active |
From: Thomas H. <the...@vm...> - 2013-11-04 13:58:03
|
The code handles three different cases: 1) physical page addresses. The ttm page array is used. 2) DMA subsystem addresses. A scatter-gather list is used. 3) Coherent pages. The ttm dma pool is used, together with the dma_ttm array os dma_addr_t Signed-off-by: Thomas Hellstrom <the...@vm...> Reviewed-by: Jakob Bornecrantz <ja...@vm...> --- drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c | 379 ++++++++++++++++++++++++++++++-- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 87 +++++++- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 98 ++++++++- drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c | 150 ++++++------- 4 files changed, 620 insertions(+), 94 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c index 96dc84d..7776e6f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c @@ -141,37 +141,374 @@ struct ttm_placement vmw_srf_placement = { }; struct vmw_ttm_tt { - struct ttm_tt ttm; + struct ttm_dma_tt dma_ttm; struct vmw_private *dev_priv; int gmr_id; + struct sg_table sgt; + struct vmw_sg_table vsgt; + uint64_t sg_alloc_size; + bool mapped; }; +/** + * Helper functions to advance a struct vmw_piter iterator. + * + * @viter: Pointer to the iterator. + * + * These functions return false if past the end of the list, + * true otherwise. Functions are selected depending on the current + * DMA mapping mode. + */ +static bool __vmw_piter_non_sg_next(struct vmw_piter *viter) +{ + return ++(viter->i) < viter->num_pages; +} + +static bool __vmw_piter_sg_next(struct vmw_piter *viter) +{ + return __sg_page_iter_next(&viter->iter); +} + + +/** + * Helper functions to return a pointer to the current page. + * + * @viter: Pointer to the iterator + * + * These functions return a pointer to the page currently + * pointed to by @viter. Functions are selected depending on the + * current mapping mode. + */ +static struct page *__vmw_piter_non_sg_page(struct vmw_piter *viter) +{ + return viter->pages[viter->i]; +} + +static struct page *__vmw_piter_sg_page(struct vmw_piter *viter) +{ + return sg_page_iter_page(&viter->iter); +} + + +/** + * Helper functions to return the DMA address of the current page. + * + * @viter: Pointer to the iterator + * + * These functions return the DMA address of the page currently + * pointed to by @viter. Functions are selected depending on the + * current mapping mode. + */ +static dma_addr_t __vmw_piter_phys_addr(struct vmw_piter *viter) +{ + return page_to_phys(viter->pages[viter->i]); +} + +static dma_addr_t __vmw_piter_dma_addr(struct vmw_piter *viter) +{ + return viter->addrs[viter->i]; +} + +static dma_addr_t __vmw_piter_sg_addr(struct vmw_piter *viter) +{ + return sg_page_iter_dma_address(&viter->iter); +} + + +/** + * vmw_piter_start - Initialize a struct vmw_piter. + * + * @viter: Pointer to the iterator to initialize + * @vsgt: Pointer to a struct vmw_sg_table to initialize from + * + * Note that we're following the convention of __sg_page_iter_start, so that + * the iterator doesn't point to a valid page after initialization; it has + * to be advanced one step first. + */ +void vmw_piter_start(struct vmw_piter *viter, const struct vmw_sg_table *vsgt, + unsigned long p_offset) +{ + viter->i = p_offset - 1; + viter->num_pages = vsgt->num_pages; + switch (vsgt->mode) { + case vmw_dma_phys: + viter->next = &__vmw_piter_non_sg_next; + viter->dma_address = &__vmw_piter_phys_addr; + viter->page = &__vmw_piter_non_sg_page; + viter->pages = vsgt->pages; + break; + case vmw_dma_alloc_coherent: + viter->next = &__vmw_piter_non_sg_next; + viter->dma_address = &__vmw_piter_dma_addr; + viter->page = &__vmw_piter_non_sg_page; + viter->addrs = vsgt->addrs; + break; + case vmw_dma_map_populate: + case vmw_dma_map_bind: + viter->next = &__vmw_piter_sg_next; + viter->dma_address = &__vmw_piter_sg_addr; + viter->page = &__vmw_piter_sg_page; + __sg_page_iter_start(&viter->iter, vsgt->sgt->sgl, + vsgt->sgt->orig_nents, p_offset); + break; + default: + BUG(); + } +} + +/** + * vmw_ttm_unmap_from_dma - unmap device addresses previsouly mapped for + * TTM pages + * + * @vmw_tt: Pointer to a struct vmw_ttm_backend + * + * Used to free dma mappings previously mapped by vmw_ttm_map_for_dma. + */ +static void vmw_ttm_unmap_from_dma(struct vmw_ttm_tt *vmw_tt) +{ + struct device *dev = vmw_tt->dev_priv->dev->dev; + + dma_unmap_sg(dev, vmw_tt->sgt.sgl, vmw_tt->sgt.nents, + DMA_BIDIRECTIONAL); + vmw_tt->sgt.nents = vmw_tt->sgt.orig_nents; +} + +/** + * vmw_ttm_map_for_dma - map TTM pages to get device addresses + * + * @vmw_tt: Pointer to a struct vmw_ttm_backend + * + * This function is used to get device addresses from the kernel DMA layer. + * However, it's violating the DMA API in that when this operation has been + * performed, it's illegal for the CPU to write to the pages without first + * unmapping the DMA mappings, or calling dma_sync_sg_for_cpu(). It is + * therefore only legal to call this function if we know that the function + * dma_sync_sg_for_cpu() is a NOP, and dma_sync_sg_for_device() is at most + * a CPU write buffer flush. + */ +static int vmw_ttm_map_for_dma(struct vmw_ttm_tt *vmw_tt) +{ + struct device *dev = vmw_tt->dev_priv->dev->dev; + int ret; + + ret = dma_map_sg(dev, vmw_tt->sgt.sgl, vmw_tt->sgt.orig_nents, + DMA_BIDIRECTIONAL); + if (unlikely(ret == 0)) + return -ENOMEM; + + vmw_tt->sgt.nents = ret; + + return 0; +} + +/** + * vmw_ttm_map_dma - Make sure TTM pages are visible to the device + * + * @vmw_tt: Pointer to a struct vmw_ttm_tt + * + * Select the correct function for and make sure the TTM pages are + * visible to the device. Allocate storage for the device mappings. + * If a mapping has already been performed, indicated by the storage + * pointer being non NULL, the function returns success. + */ +static int vmw_ttm_map_dma(struct vmw_ttm_tt *vmw_tt) +{ + struct vmw_private *dev_priv = vmw_tt->dev_priv; + struct ttm_mem_global *glob = vmw_mem_glob(dev_priv); + struct vmw_sg_table *vsgt = &vmw_tt->vsgt; + struct vmw_piter iter; + dma_addr_t old; + int ret = 0; + static size_t sgl_size; + static size_t sgt_size; + + if (vmw_tt->mapped) + return 0; + + vsgt->mode = dev_priv->map_mode; + vsgt->pages = vmw_tt->dma_ttm.ttm.pages; + vsgt->num_pages = vmw_tt->dma_ttm.ttm.num_pages; + vsgt->addrs = vmw_tt->dma_ttm.dma_address; + vsgt->sgt = &vmw_tt->sgt; + + switch (dev_priv->map_mode) { + case vmw_dma_map_bind: + case vmw_dma_map_populate: + if (unlikely(!sgl_size)) { + sgl_size = ttm_round_pot(sizeof(struct scatterlist)); + sgt_size = ttm_round_pot(sizeof(struct sg_table)); + } + vmw_tt->sg_alloc_size = sgt_size + sgl_size * vsgt->num_pages; + ret = ttm_mem_global_alloc(glob, vmw_tt->sg_alloc_size, false, + true); + if (unlikely(ret != 0)) + return ret; + + ret = sg_alloc_table_from_pages(&vmw_tt->sgt, vsgt->pages, + vsgt->num_pages, 0, + (unsigned long) + vsgt->num_pages << PAGE_SHIFT, + GFP_KERNEL); + if (unlikely(ret != 0)) + goto out_sg_alloc_fail; + + if (vsgt->num_pages > vmw_tt->sgt.nents) { + uint64_t over_alloc = + sgl_size * (vsgt->num_pages - + vmw_tt->sgt.nents); + + ttm_mem_global_free(glob, over_alloc); + vmw_tt->sg_alloc_size -= over_alloc; + } + + ret = vmw_ttm_map_for_dma(vmw_tt); + if (unlikely(ret != 0)) + goto out_map_fail; + + break; + default: + break; + } + + old = ~((dma_addr_t) 0); + vmw_tt->vsgt.num_regions = 0; + for (vmw_piter_start(&iter, vsgt, 0); vmw_piter_next(&iter);) { + dma_addr_t cur = vmw_piter_dma_addr(&iter); + + if (cur != old + PAGE_SIZE) + vmw_tt->vsgt.num_regions++; + old = cur; + } + + vmw_tt->mapped = true; + return 0; + +out_map_fail: + sg_free_table(vmw_tt->vsgt.sgt); + vmw_tt->vsgt.sgt = NULL; +out_sg_alloc_fail: + ttm_mem_global_free(glob, vmw_tt->sg_alloc_size); + return ret; +} + +/** + * vmw_ttm_unmap_dma - Tear down any TTM page device mappings + * + * @vmw_tt: Pointer to a struct vmw_ttm_tt + * + * Tear down any previously set up device DMA mappings and free + * any storage space allocated for them. If there are no mappings set up, + * this function is a NOP. + */ +static void vmw_ttm_unmap_dma(struct vmw_ttm_tt *vmw_tt) +{ + struct vmw_private *dev_priv = vmw_tt->dev_priv; + + if (!vmw_tt->vsgt.sgt) + return; + + switch (dev_priv->map_mode) { + case vmw_dma_map_bind: + case vmw_dma_map_populate: + vmw_ttm_unmap_from_dma(vmw_tt); + sg_free_table(vmw_tt->vsgt.sgt); + vmw_tt->vsgt.sgt = NULL; + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_tt->sg_alloc_size); + break; + default: + break; + } + vmw_tt->mapped = false; +} + static int vmw_ttm_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem) { - struct vmw_ttm_tt *vmw_be = container_of(ttm, struct vmw_ttm_tt, ttm); + struct vmw_ttm_tt *vmw_be = + container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); + int ret; + + ret = vmw_ttm_map_dma(vmw_be); + if (unlikely(ret != 0)) + return ret; vmw_be->gmr_id = bo_mem->start; - return vmw_gmr_bind(vmw_be->dev_priv, ttm->pages, + return vmw_gmr_bind(vmw_be->dev_priv, &vmw_be->vsgt, ttm->num_pages, vmw_be->gmr_id); } static int vmw_ttm_unbind(struct ttm_tt *ttm) { - struct vmw_ttm_tt *vmw_be = container_of(ttm, struct vmw_ttm_tt, ttm); + struct vmw_ttm_tt *vmw_be = + container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); vmw_gmr_unbind(vmw_be->dev_priv, vmw_be->gmr_id); + + if (vmw_be->dev_priv->map_mode == vmw_dma_map_bind) + vmw_ttm_unmap_dma(vmw_be); + return 0; } static void vmw_ttm_destroy(struct ttm_tt *ttm) { - struct vmw_ttm_tt *vmw_be = container_of(ttm, struct vmw_ttm_tt, ttm); - - ttm_tt_fini(ttm); + struct vmw_ttm_tt *vmw_be = + container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); + + vmw_ttm_unmap_dma(vmw_be); + if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent) + ttm_dma_tt_fini(&vmw_be->dma_ttm); + else + ttm_tt_fini(ttm); kfree(vmw_be); } +static int vmw_ttm_populate(struct ttm_tt *ttm) +{ + struct vmw_ttm_tt *vmw_tt = + container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); + struct vmw_private *dev_priv = vmw_tt->dev_priv; + struct ttm_mem_global *glob = vmw_mem_glob(dev_priv); + int ret; + + if (ttm->state != tt_unpopulated) + return 0; + + if (dev_priv->map_mode == vmw_dma_alloc_coherent) { + size_t size = + ttm_round_pot(ttm->num_pages * sizeof(dma_addr_t)); + ret = ttm_mem_global_alloc(glob, size, false, true); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_dma_populate(&vmw_tt->dma_ttm, dev_priv->dev->dev); + if (unlikely(ret != 0)) + ttm_mem_global_free(glob, size); + } else + ret = ttm_pool_populate(ttm); + + return ret; +} + +static void vmw_ttm_unpopulate(struct ttm_tt *ttm) +{ + struct vmw_ttm_tt *vmw_tt = container_of(ttm, struct vmw_ttm_tt, + dma_ttm.ttm); + struct vmw_private *dev_priv = vmw_tt->dev_priv; + struct ttm_mem_global *glob = vmw_mem_glob(dev_priv); + + vmw_ttm_unmap_dma(vmw_tt); + if (dev_priv->map_mode == vmw_dma_alloc_coherent) { + size_t size = + ttm_round_pot(ttm->num_pages * sizeof(dma_addr_t)); + + ttm_dma_unpopulate(&vmw_tt->dma_ttm, dev_priv->dev->dev); + ttm_mem_global_free(glob, size); + } else + ttm_pool_unpopulate(ttm); +} + static struct ttm_backend_func vmw_ttm_func = { .bind = vmw_ttm_bind, .unbind = vmw_ttm_unbind, @@ -183,20 +520,28 @@ struct ttm_tt *vmw_ttm_tt_create(struct ttm_bo_device *bdev, struct page *dummy_read_page) { struct vmw_ttm_tt *vmw_be; + int ret; - vmw_be = kmalloc(sizeof(*vmw_be), GFP_KERNEL); + vmw_be = kzalloc(sizeof(*vmw_be), GFP_KERNEL); if (!vmw_be) return NULL; - vmw_be->ttm.func = &vmw_ttm_func; + vmw_be->dma_ttm.ttm.func = &vmw_ttm_func; vmw_be->dev_priv = container_of(bdev, struct vmw_private, bdev); - if (ttm_tt_init(&vmw_be->ttm, bdev, size, page_flags, dummy_read_page)) { - kfree(vmw_be); - return NULL; - } - - return &vmw_be->ttm; + if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent) + ret = ttm_dma_tt_init(&vmw_be->dma_ttm, bdev, size, page_flags, + dummy_read_page); + else + ret = ttm_tt_init(&vmw_be->dma_ttm.ttm, bdev, size, page_flags, + dummy_read_page); + if (unlikely(ret != 0)) + goto out_no_init; + + return &vmw_be->dma_ttm.ttm; +out_no_init: + kfree(vmw_be); + return NULL; } int vmw_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) @@ -332,8 +677,8 @@ static int vmw_sync_obj_wait(void *sync_obj, bool lazy, bool interruptible) struct ttm_bo_driver vmw_bo_driver = { .ttm_tt_create = &vmw_ttm_tt_create, - .ttm_tt_populate = &ttm_pool_populate, - .ttm_tt_unpopulate = &ttm_pool_unpopulate, + .ttm_tt_populate = &vmw_ttm_populate, + .ttm_tt_unpopulate = &vmw_ttm_unpopulate, .invalidate_caches = vmw_invalidate_caches, .init_mem_type = vmw_init_mem_type, .evict_flags = vmw_evict_flags, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 1a90f0a..0b5c781 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -32,6 +32,7 @@ #include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_object.h> #include <drm/ttm/ttm_module.h> +#include <linux/dma_remapping.h> #define VMWGFX_DRIVER_NAME "vmwgfx" #define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices" @@ -185,6 +186,9 @@ static struct pci_device_id vmw_pci_id_list[] = { MODULE_DEVICE_TABLE(pci, vmw_pci_id_list); static int enable_fbdev = IS_ENABLED(CONFIG_DRM_VMWGFX_FBCON); +static int vmw_force_iommu; +static int vmw_restrict_iommu; +static int vmw_force_coherent; static int vmw_probe(struct pci_dev *, const struct pci_device_id *); static void vmw_master_init(struct vmw_master *); @@ -193,6 +197,13 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, MODULE_PARM_DESC(enable_fbdev, "Enable vmwgfx fbdev"); module_param_named(enable_fbdev, enable_fbdev, int, 0600); +MODULE_PARM_DESC(force_dma_api, "Force using the DMA API for TTM pages"); +module_param_named(force_dma_api, vmw_force_iommu, int, 0600); +MODULE_PARM_DESC(restrict_iommu, "Try to limit IOMMU usage for TTM pages"); +module_param_named(restrict_iommu, vmw_restrict_iommu, int, 0600); +MODULE_PARM_DESC(force_coherent, "Force coherent TTM pages"); +module_param_named(force_coherent, vmw_force_coherent, int, 0600); + static void vmw_print_capabilities(uint32_t capabilities) { @@ -427,12 +438,78 @@ static void vmw_get_initial_size(struct vmw_private *dev_priv) dev_priv->initial_height = height; } +/** + * vmw_dma_select_mode - Determine how DMA mappings should be set up for this + * system. + * + * @dev_priv: Pointer to a struct vmw_private + * + * This functions tries to determine the IOMMU setup and what actions + * need to be taken by the driver to make system pages visible to the + * device. + * If this function decides that DMA is not possible, it returns -EINVAL. + * The driver may then try to disable features of the device that require + * DMA. + */ +static int vmw_dma_select_mode(struct vmw_private *dev_priv) +{ + const struct dma_map_ops *dma_ops = get_dma_ops(dev_priv->dev->dev); + static const char *names[vmw_dma_map_max] = { + [vmw_dma_phys] = "Using physical TTM page addresses.", + [vmw_dma_alloc_coherent] = "Using coherent TTM pages.", + [vmw_dma_map_populate] = "Keeping DMA mappings.", + [vmw_dma_map_bind] = "Giving up DMA mappings early."}; + +#ifdef CONFIG_INTEL_IOMMU + if (intel_iommu_enabled) { + dev_priv->map_mode = vmw_dma_map_populate; + goto out_fixup; + } +#endif + + if (!(vmw_force_iommu || vmw_force_coherent)) { + dev_priv->map_mode = vmw_dma_phys; + DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]); + return 0; + } + + dev_priv->map_mode = vmw_dma_map_populate; + + if (dma_ops->sync_single_for_cpu) + dev_priv->map_mode = vmw_dma_alloc_coherent; +#ifdef CONFIG_SWIOTLB + if (swiotlb_nr_tbl() == 0) + dev_priv->map_mode = vmw_dma_map_populate; +#endif + +out_fixup: + if (dev_priv->map_mode == vmw_dma_map_populate && + vmw_restrict_iommu) + dev_priv->map_mode = vmw_dma_map_bind; + + if (vmw_force_coherent) + dev_priv->map_mode = vmw_dma_alloc_coherent; + +#if !defined(CONFIG_SWIOTLB) && !defined(CONFIG_INTEL_IOMMU) + /* + * No coherent page pool + */ + if (dev_priv->map_mode == vmw_dma_alloc_coherent) + return -EINVAL; +#endif + + DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]); + + return 0; +} + static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) { struct vmw_private *dev_priv; int ret; uint32_t svga_id; enum vmw_res_type i; + bool refuse_dma = false; dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); if (unlikely(dev_priv == NULL)) { @@ -481,6 +558,11 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) } dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES); + ret = vmw_dma_select_mode(dev_priv); + if (unlikely(ret != 0)) { + DRM_INFO("Restricting capabilities due to IOMMU setup.\n"); + refuse_dma = true; + } dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE); dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE); @@ -558,8 +640,9 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) } dev_priv->has_gmr = true; - if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, - dev_priv->max_gmr_ids) != 0) { + if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) || + refuse_dma || ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, + dev_priv->max_gmr_ids) != 0) { DRM_INFO("No GMR memory available. " "Graphics memory resources are very limited.\n"); dev_priv->has_gmr = false; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 150ec64..e401d5d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -177,6 +177,58 @@ struct vmw_res_cache_entry { struct vmw_resource_val_node *node; }; +/** + * enum vmw_dma_map_mode - indicate how to perform TTM page dma mappings. + */ +enum vmw_dma_map_mode { + vmw_dma_phys, /* Use physical page addresses */ + vmw_dma_alloc_coherent, /* Use TTM coherent pages */ + vmw_dma_map_populate, /* Unmap from DMA just after unpopulate */ + vmw_dma_map_bind, /* Unmap from DMA just before unbind */ + vmw_dma_map_max +}; + +/** + * struct vmw_sg_table - Scatter/gather table for binding, with additional + * device-specific information. + * + * @sgt: Pointer to a struct sg_table with binding information + * @num_regions: Number of regions with device-address contigous pages + */ +struct vmw_sg_table { + enum vmw_dma_map_mode mode; + struct page **pages; + const dma_addr_t *addrs; + struct sg_table *sgt; + unsigned long num_regions; + unsigned long num_pages; +}; + +/** + * struct vmw_piter - Page iterator that iterates over a list of pages + * and DMA addresses that could be either a scatter-gather list or + * arrays + * + * @pages: Array of page pointers to the pages. + * @addrs: DMA addresses to the pages if coherent pages are used. + * @iter: Scatter-gather page iterator. Current position in SG list. + * @i: Current position in arrays. + * @num_pages: Number of pages total. + * @next: Function to advance the iterator. Returns false if past the list + * of pages, true otherwise. + * @dma_address: Function to return the DMA address of the current page. + */ +struct vmw_piter { + struct page **pages; + const dma_addr_t *addrs; + struct sg_page_iter iter; + unsigned long i; + unsigned long num_pages; + bool (*next)(struct vmw_piter *); + dma_addr_t (*dma_address)(struct vmw_piter *); + struct page *(*page)(struct vmw_piter *); +}; + struct vmw_sw_context{ struct drm_open_hash res_ht; bool res_ht_initialized; @@ -358,6 +410,11 @@ struct vmw_private { struct list_head res_lru[vmw_res_max]; uint32_t used_memory_size; + + /* + * DMA mapping stuff. + */ + enum vmw_dma_map_mode map_mode; }; static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res) @@ -405,7 +462,7 @@ void vmw_3d_resource_dec(struct vmw_private *dev_priv, bool hide_svga); */ extern int vmw_gmr_bind(struct vmw_private *dev_priv, - struct page *pages[], + const struct vmw_sg_table *vsgt, unsigned long num_pages, int gmr_id); extern void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id); @@ -568,6 +625,45 @@ extern struct ttm_placement vmw_evictable_placement; extern struct ttm_placement vmw_srf_placement; extern struct ttm_bo_driver vmw_bo_driver; extern int vmw_dma_quiescent(struct drm_device *dev); +extern void vmw_piter_start(struct vmw_piter *viter, + const struct vmw_sg_table *vsgt, + unsigned long p_offs); + +/** + * vmw_piter_next - Advance the iterator one page. + * + * @viter: Pointer to the iterator to advance. + * + * Returns false if past the list of pages, true otherwise. + */ +static inline bool vmw_piter_next(struct vmw_piter *viter) +{ + return viter->next(viter); +} + +/** + * vmw_piter_dma_addr - Return the DMA address of the current page. + * + * @viter: Pointer to the iterator + * + * Returns the DMA address of the page pointed to by @viter. + */ +static inline dma_addr_t vmw_piter_dma_addr(struct vmw_piter *viter) +{ + return viter->dma_address(viter); +} + +/** + * vmw_piter_page - Return a pointer to the current page. + * + * @viter: Pointer to the iterator + * + * Returns the DMA address of the page pointed to by @viter. + */ +static inline struct page *vmw_piter_page(struct vmw_piter *viter) +{ + return viter->page(viter); +} /** * Command submission - vmwgfx_execbuf.c diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c index 1a0bf07..6d09523 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c @@ -32,9 +32,11 @@ #define VMW_PPN_SIZE (sizeof(unsigned long)) /* A future safe maximum remap size. */ #define VMW_PPN_PER_REMAP ((31 * 1024) / VMW_PPN_SIZE) +#define DMA_ADDR_INVALID ((dma_addr_t) 0) +#define DMA_PAGE_INVALID 0UL static int vmw_gmr2_bind(struct vmw_private *dev_priv, - struct page *pages[], + struct vmw_piter *iter, unsigned long num_pages, int gmr_id) { @@ -81,11 +83,13 @@ static int vmw_gmr2_bind(struct vmw_private *dev_priv, for (i = 0; i < nr; ++i) { if (VMW_PPN_SIZE <= 4) - *cmd = page_to_pfn(*pages++); + *cmd = vmw_piter_dma_addr(iter) >> PAGE_SHIFT; else - *((uint64_t *)cmd) = page_to_pfn(*pages++); + *((uint64_t *)cmd) = vmw_piter_dma_addr(iter) >> + PAGE_SHIFT; cmd += VMW_PPN_SIZE / sizeof(*cmd); + vmw_piter_next(iter); } num_pages -= nr; @@ -120,22 +124,54 @@ static void vmw_gmr2_unbind(struct vmw_private *dev_priv, vmw_fifo_commit(dev_priv, define_size); } + +static void vmw_gmr_free_descriptors(struct device *dev, dma_addr_t desc_dma, + struct list_head *desc_pages) +{ + struct page *page, *next; + struct svga_guest_mem_descriptor *page_virtual; + unsigned int desc_per_page = PAGE_SIZE / + sizeof(struct svga_guest_mem_descriptor) - 1; + + if (list_empty(desc_pages)) + return; + + list_for_each_entry_safe(page, next, desc_pages, lru) { + list_del_init(&page->lru); + + if (likely(desc_dma != DMA_ADDR_INVALID)) { + dma_unmap_page(dev, desc_dma, PAGE_SIZE, + DMA_TO_DEVICE); + } + + page_virtual = kmap_atomic(page); + desc_dma = page_virtual[desc_per_page].ppn << PAGE_SHIFT; + kunmap_atomic(page_virtual); + + __free_page(page); + } +} + /** * FIXME: Adjust to the ttm lowmem / highmem storage to minimize * the number of used descriptors. + * */ -static int vmw_gmr_build_descriptors(struct list_head *desc_pages, - struct page *pages[], - unsigned long num_pages) +static int vmw_gmr_build_descriptors(struct device *dev, + struct list_head *desc_pages, + struct vmw_piter *iter, + unsigned long num_pages, + dma_addr_t *first_dma) { - struct page *page, *next; + struct page *page; struct svga_guest_mem_descriptor *page_virtual = NULL; struct svga_guest_mem_descriptor *desc_virtual = NULL; unsigned int desc_per_page; unsigned long prev_pfn; unsigned long pfn; int ret; + dma_addr_t desc_dma; desc_per_page = PAGE_SIZE / sizeof(struct svga_guest_mem_descriptor) - 1; @@ -148,23 +184,12 @@ static int vmw_gmr_build_descriptors(struct list_head *desc_pages, } list_add_tail(&page->lru, desc_pages); - - /* - * Point previous page terminating descriptor to this - * page before unmapping it. - */ - - if (likely(page_virtual != NULL)) { - desc_virtual->ppn = page_to_pfn(page); - kunmap_atomic(page_virtual); - } - page_virtual = kmap_atomic(page); desc_virtual = page_virtual - 1; prev_pfn = ~(0UL); while (likely(num_pages != 0)) { - pfn = page_to_pfn(*pages); + pfn = vmw_piter_dma_addr(iter) >> PAGE_SHIFT; if (pfn != prev_pfn + 1) { @@ -181,104 +206,81 @@ static int vmw_gmr_build_descriptors(struct list_head *desc_pages, } prev_pfn = pfn; --num_pages; - ++pages; + vmw_piter_next(iter); } - (++desc_virtual)->ppn = cpu_to_le32(0); + (++desc_virtual)->ppn = DMA_PAGE_INVALID; desc_virtual->num_pages = cpu_to_le32(0); + kunmap_atomic(page_virtual); } - if (likely(page_virtual != NULL)) + desc_dma = 0; + list_for_each_entry_reverse(page, desc_pages, lru) { + page_virtual = kmap_atomic(page); + page_virtual[desc_per_page].ppn = desc_dma >> PAGE_SHIFT; kunmap_atomic(page_virtual); + desc_dma = dma_map_page(dev, page, 0, PAGE_SIZE, + DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(dev, desc_dma))) + goto out_err; + } + *first_dma = desc_dma; return 0; out_err: - list_for_each_entry_safe(page, next, desc_pages, lru) { - list_del_init(&page->lru); - __free_page(page); - } + vmw_gmr_free_descriptors(dev, DMA_ADDR_INVALID, desc_pages); return ret; } -static inline void vmw_gmr_free_descriptors(struct list_head *desc_pages) -{ - struct page *page, *next; - - list_for_each_entry_safe(page, next, desc_pages, lru) { - list_del_init(&page->lru); - __free_page(page); - } -} - static void vmw_gmr_fire_descriptors(struct vmw_private *dev_priv, - int gmr_id, struct list_head *desc_pages) + int gmr_id, dma_addr_t desc_dma) { - struct page *page; - - if (unlikely(list_empty(desc_pages))) - return; - - page = list_entry(desc_pages->next, struct page, lru); - mutex_lock(&dev_priv->hw_mutex); vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id); wmb(); - vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, page_to_pfn(page)); + vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, desc_dma >> PAGE_SHIFT); mb(); mutex_unlock(&dev_priv->hw_mutex); } -/** - * FIXME: Adjust to the ttm lowmem / highmem storage to minimize - * the number of used descriptors. - */ - -static unsigned long vmw_gmr_count_descriptors(struct page *pages[], - unsigned long num_pages) -{ - unsigned long prev_pfn = ~(0UL); - unsigned long pfn; - unsigned long descriptors = 0; - - while (num_pages--) { - pfn = page_to_pfn(*pages++); - if (prev_pfn + 1 != pfn) - ++descriptors; - prev_pfn = pfn; - } - - return descriptors; -} - int vmw_gmr_bind(struct vmw_private *dev_priv, - struct page *pages[], + const struct vmw_sg_table *vsgt, unsigned long num_pages, int gmr_id) { struct list_head desc_pages; + dma_addr_t desc_dma = 0; + struct device *dev = dev_priv->dev->dev; + struct vmw_piter data_iter; int ret; + vmw_piter_start(&data_iter, vsgt, 0); + + if (unlikely(!vmw_piter_next(&data_iter))) + return 0; + if (likely(dev_priv->capabilities & SVGA_CAP_GMR2)) - return vmw_gmr2_bind(dev_priv, pages, num_pages, gmr_id); + return vmw_gmr2_bind(dev_priv, &data_iter, num_pages, gmr_id); if (unlikely(!(dev_priv->capabilities & SVGA_CAP_GMR))) return -EINVAL; - if (vmw_gmr_count_descriptors(pages, num_pages) > - dev_priv->max_gmr_descriptors) + if (vsgt->num_regions > dev_priv->max_gmr_descriptors) return -EINVAL; INIT_LIST_HEAD(&desc_pages); - ret = vmw_gmr_build_descriptors(&desc_pages, pages, num_pages); + ret = vmw_gmr_build_descriptors(dev, &desc_pages, &data_iter, + num_pages, &desc_dma); if (unlikely(ret != 0)) return ret; - vmw_gmr_fire_descriptors(dev_priv, gmr_id, &desc_pages); - vmw_gmr_free_descriptors(&desc_pages); + vmw_gmr_fire_descriptors(dev_priv, gmr_id, desc_dma); + vmw_gmr_free_descriptors(dev, desc_dma, &desc_pages); return 0; } -- 1.7.10.4 |
From: Thomas H. <the...@vm...> - 2013-11-04 13:58:01
|
These patches makes the vmwgfx driver use the DMA API to obtain valid device addresses rather than blindly using physical addresses. The main motivation is to be able to use a virtual IOMMU in the future. Other TTM drivers typically map pages one by one rather than using a scatter-gather list, but since we can benefit from having a single dma address region set up by the IOMMU, we use a scatter-gather list instead. Finally to be able to handle all the dma mapping modes, we locally extend the scatter-gather list page iterator to handle also the direct physical- and coherent cases. Finally, the TTM DMA page pool is enabled also when the Intel IOMMU is active |
From: Thomas H. <the...@vm...> - 2013-11-04 13:58:00
|
Used by the vmwgfx driver Signed-off-by: Thomas Hellstrom <the...@vm...> Reviewed-by: Jakob Bornecrantz <ja...@vm...> --- drivers/gpu/drm/ttm/Makefile | 6 +----- drivers/gpu/drm/ttm/ttm_page_alloc_dma.c | 3 +++ include/drm/ttm/ttm_page_alloc.h | 11 ++++++++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/ttm/Makefile b/drivers/gpu/drm/ttm/Makefile index b2b33dd..b433b9f 100644 --- a/drivers/gpu/drm/ttm/Makefile +++ b/drivers/gpu/drm/ttm/Makefile @@ -5,10 +5,6 @@ ccflags-y := -Iinclude/drm ttm-y := ttm_agp_backend.o ttm_memory.o ttm_tt.o ttm_bo.o \ ttm_bo_util.o ttm_bo_vm.o ttm_module.o \ ttm_object.o ttm_lock.o ttm_execbuf_util.o ttm_page_alloc.o \ - ttm_bo_manager.o - -ifeq ($(CONFIG_SWIOTLB),y) -ttm-y += ttm_page_alloc_dma.o -endif + ttm_bo_manager.o ttm_page_alloc_dma.o obj-$(CONFIG_DRM_TTM) += ttm.o diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c index 7957bee..fb8259f 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c @@ -33,6 +33,7 @@ * when freed). */ +#if defined(CONFIG_SWIOTLB) || defined(CONFIG_INTEL_IOMMU) #define pr_fmt(fmt) "[TTM] " fmt #include <linux/dma-mapping.h> @@ -1142,3 +1143,5 @@ int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data) return 0; } EXPORT_SYMBOL_GPL(ttm_dma_page_alloc_debugfs); + +#endif diff --git a/include/drm/ttm/ttm_page_alloc.h b/include/drm/ttm/ttm_page_alloc.h index 706b962..d1f61bf 100644 --- a/include/drm/ttm/ttm_page_alloc.h +++ b/include/drm/ttm/ttm_page_alloc.h @@ -62,7 +62,7 @@ extern void ttm_pool_unpopulate(struct ttm_tt *ttm); extern int ttm_page_alloc_debugfs(struct seq_file *m, void *data); -#ifdef CONFIG_SWIOTLB +#if defined(CONFIG_SWIOTLB) || defined(CONFIG_INTEL_IOMMU) /** * Initialize pool allocator. */ @@ -94,6 +94,15 @@ static inline int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data) { return 0; } +static inline int ttm_dma_populate(struct ttm_dma_tt *ttm_dma, + struct device *dev) +{ + return -ENOMEM; +} +static inline void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, + struct device *dev) +{ +} #endif #endif -- 1.7.10.4 |
From: Kristian H. <hoe...@gm...> - 2013-11-01 23:48:58
|
On Thu, Oct 31, 2013 at 04:13:15PM -0700, Keith Packard wrote: > This hooks DRI3 support into the GLX layer, the DRI common layer and the Intel > driver. > > Signed-off-by: Keith Packard <ke...@ke...> > --- > configure.ac | 10 +- > include/GL/internal/dri_interface.h | 158 +++ > src/glx/Makefile.am | 2 + > src/glx/dri3_common.c | 146 +++ > src/glx/dri3_glx.c | 1539 +++++++++++++++++++++++++ > src/glx/dri3_priv.h | 128 ++ > src/glx/glxclient.h | 2 + > src/glx/glxext.c | 6 +- > src/mesa/drivers/dri/common/dri_util.c | 163 ++- > src/mesa/drivers/dri/common/dri_util.h | 23 + > src/mesa/drivers/dri/i915/intel_context.c | 109 +- > src/mesa/drivers/dri/i915/intel_mipmap_tree.c | 33 + > src/mesa/drivers/dri/i915/intel_mipmap_tree.h | 8 + > src/mesa/drivers/dri/i965/brw_context.c | 110 +- > src/mesa/drivers/dri/i965/intel_mipmap_tree.c | 61 + > src/mesa/drivers/dri/i965/intel_mipmap_tree.h | 8 + > src/mesa/drivers/dri/i965/intel_screen.c | 107 +- > 17 files changed, 2594 insertions(+), 19 deletions(-) > create mode 100644 src/glx/dri3_common.c > create mode 100644 src/glx/dri3_glx.c > create mode 100644 src/glx/dri3_priv.h > > diff --git a/configure.ac b/configure.ac > index f94c9b9..b6158d9 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -38,6 +38,8 @@ LIBDRM_NVVIEUX_REQUIRED=2.4.33 > LIBDRM_NOUVEAU_REQUIRED="2.4.33 libdrm >= 2.4.41" > LIBDRM_FREEDRENO_REQUIRED=2.4.39 > DRI2PROTO_REQUIRED=2.6 > +DRI3PROTO_REQUIRED=1.0 > +LIBUDEV_REQUIRED=151 > GLPROTO_REQUIRED=1.4.14 > LIBDRM_XORG_REQUIRED=2.4.24 > LIBKMS_XORG_REQUIRED=1.0.0 > @@ -820,10 +822,12 @@ xyesno) > fi > PKG_CHECK_MODULES([DRI2PROTO], [dri2proto >= $DRI2PROTO_REQUIRED]) > GL_PC_REQ_PRIV="$GL_PC_REQ_PRIV libdrm >= $LIBDRM_REQUIRED" > + PKG_CHECK_MODULES([DRI3PROTO], [dri3proto >= $DRI3PROTO_REQUIRED]) > + PKG_CHECK_MODULES([LIBUDEV], [libudev >= $LIBUDEV_REQUIRED]) > fi > > # find the DRI deps for libGL > - dri_modules="x11 xext xdamage xfixes x11-xcb xcb-glx >= 1.8.1 xcb-dri2 >= 1.8" > + dri_modules="x11 xext xdamage xfixes x11-xcb xcb-glx >= 1.8.1 xcb-dri2 >= 1.8 xcb-dri3 xcb-sync xshmfence" > > # add xf86vidmode if available > PKG_CHECK_MODULES([XF86VIDMODE], [xxf86vm], HAVE_XF86VIDMODE=yes, HAVE_XF86VIDMODE=no) > @@ -833,8 +837,8 @@ xyesno) > > PKG_CHECK_MODULES([DRIGL], [$dri_modules]) > GL_PC_REQ_PRIV="$GL_PC_REQ_PRIV $dri_modules" > - X11_INCLUDES="$X11_INCLUDES $DRIGL_CFLAGS" > - GL_LIB_DEPS="$DRIGL_LIBS" > + X11_INCLUDES="$X11_INCLUDES $DRIGL_CFLAGS $LIBUDEV_CFLAGS" > + GL_LIB_DEPS="$DRIGL_LIBS $LIBUDEV_LIBS" > > # need DRM libs, $PTHREAD_LIBS, etc. > GL_LIB_DEPS="$GL_LIB_DEPS $LIBDRM_LIBS -lm $PTHREAD_LIBS $DLOPEN_LIBS" > diff --git a/include/GL/internal/dri_interface.h b/include/GL/internal/dri_interface.h > index 48993b9..b06ad8d 100644 > --- a/include/GL/internal/dri_interface.h > +++ b/include/GL/internal/dri_interface.h > @@ -86,6 +86,11 @@ typedef struct __DRIdri2LoaderExtensionRec __DRIdri2LoaderExtension; > typedef struct __DRI2flushExtensionRec __DRI2flushExtension; > typedef struct __DRI2throttleExtensionRec __DRI2throttleExtension; > > + > +typedef struct __DRIdri3BufferRec __DRIdri3Buffer; > +typedef struct __DRIdri3ExtensionRec __DRIdri3Extension; > +typedef struct __DRIdri3LoaderExtensionRec __DRIdri3LoaderExtension; > + > /*@}*/ > > > @@ -966,6 +971,159 @@ struct __DRIdri2ExtensionRec { > > > /** > + * DRI3 Loader extension. > + */ > + > +#define __DRI3_DRIVER_EXTENSIONS "__dri3DriverExtensions" > + > +enum __DRI3bufferType { > + __DRI3_BUFFER_BACK = 0, > + __DRI3_BUFFER_FRONT = 1 > +}; > + > +struct __DRIdri3BufferRec { > + unsigned int size; > + unsigned int pitch; > + unsigned int cpp; > + unsigned int flags; > + unsigned int width, height; > + enum __DRI3bufferType buffer_type; > + uint32_t pixmap; > + uint32_t sync_fence; > + int32_t *shm_fence; > + void *driverPrivate; > +}; My main concern with this patch is that I'd like to use __DRIimage for the color buffers instead of this new __DRIdri3Buffer. __DRIimage is an opaque structure which we introduced for EGLImage support. It essentially wraps an intel_region, so it's similar to __DRI3buffer, except it's all opaque. __DRIimage is essentially *the* color buffer abstraction in the DRI driver interface now and we're already using it for color buffers in GBM and Wayland. Using __DRIimage will make those backends and this patch simpler. The intel version of __DRIimage is defined in intel_regions.h. It's more complicated than __DRIdri3Buffer since it supports planar buffers, but at the end of the day it's the same idea. The pixmap, sync_fence and shm_fence fields above should move to a new dri3_buffer struct in dri3_glx.c: struct dri3_buffer { __DRIimage *image; uint32_t pixmap; uint32_t sync_fence; int32_t *shm_fence; }; They are only used by the glx integration and the driver doesn't need to see them. We could also include widht and height height here for convenience, but there is a getter that will return the dimensions of a __DRIimage. > +#define __DRI_DRI3_LOADER "DRI_DRI3Loader" > +#define __DRI_DRI3_LOADER_VERSION 1 > + > +struct __DRIdri3LoaderExtensionRec { > + __DRIextension base; > + > + int (*getBuffers)(__DRIdrawable *driDrawable, > + int *width, int *height, > + unsigned int format, > + void *loaderPrivate, > + int need_front, > + int need_back, > + __DRIdri3Buffer **front, > + __DRIdri3Buffer **back); This function would then not have width and height, since they're part of __DRIimage and instead of the two __DRIdri3Buffer ** args, it would have two __DRIimage ** args. > + /** > + * Flush pending front-buffer rendering > + * > + * Any rendering that has been performed to the > + * \c __DRI3_BUFFER_FAKE_FRONT_LEFT will be flushed to the > + * \c __DRI3_BUFFER_FRONT_LEFT. > + * > + * \param driDrawable Drawable whose front-buffer is to be flushed > + * \param loaderPrivate Loader's private data that was previously passed > + * into __DRIdri2ExtensionRec::createNewDrawable > + */ > + void (*flushFrontBuffer)(__DRIdrawable *driDrawable, void *loaderPrivate); > +}; > + > +/** > + * DRI3 extension. > + */ > + > +struct gl_context; > +struct dd_function_table; (these two seem out of place) > +typedef __DRIscreen * > +(*__DRIcreateNewScreen2)(int screen, int fd, > + const __DRIextension **extensions, > + const __DRIextension **driver_extensions, > + const __DRIconfig ***driver_configs, > + void *loaderPrivate); > + > +typedef __DRIdrawable * > +(*__DRIcreateNewDrawable)(__DRIscreen *screen, > + const __DRIconfig *config, > + void *loaderPrivate); > + > +typedef __DRIcontext * > +(*__DRIcreateNewContext)(__DRIscreen *screen, > + const __DRIconfig *config, > + __DRIcontext *shared, > + void *loaderPrivate); > + > +typedef __DRIcontext * > +(*__DRIcreateContextAttribs)(__DRIscreen *screen, > + int api, > + const __DRIconfig *config, > + __DRIcontext *shared, > + unsigned num_attribs, > + const uint32_t *attribs, > + unsigned *error, > + void *loaderPrivate); > + > +typedef unsigned int > +(*__DRIgetAPIMask)(__DRIscreen *screen); > + > +typedef __DRIdri3Buffer * > +(*__DRIdri3AllocateBuffer)(__DRIscreen *screen, > + unsigned int bpp, > + int width, > + int height); Instead of this, we can call __DRIimage->createImage(screen, width, height, format, use, loader_priv); where format is one of the __DRI_IMAGE_FORMAT_* codes and use is one of the __DRI_IMAGE_USE_* flags. > +typedef void > +(*__DRIdri3ReleaseBuffer)(__DRIscreen *screen, > + __DRIdri3Buffer *buffer); to destroy a __DRIimage, call __DRIimage->destroyImage(image); > +typedef int > +(*__DRIdri3BufferToFd)(__DRIscreen *screen, > + __DRIdri3Buffer *buffer); to get an fd from a __DRIimage call __DRIimage->queryImage(image, __DRI_IMAGE_ATTRIB_FD, &fd) > +typedef __DRIdri3Buffer * > +(*__DRIdri3FdToBuffer)(__DRIscreen *screen, > + int fd, unsigned int bpp, > + int width, int height, int stride, > + unsigned int size); use __DRIimage->createImageFromFDs for this. This entry point can take multiple fds for the case of a planar image that's laid out over multiple BOs. > + > +typedef uint32_t * > +(*__DRIdri3Stamp)(__DRIdrawable *drawable); This looks OK, as long as it's not tied into the xcb special_event semantics. From the way it's used, it looks like a loader can just increment this uint32_t when it needs to invalidate the buffer. Still seems like an odd API - just an invalidate call would be simpler, but I'm guessing it's limited by what you can do if you receive the special event in a different thread. with those changes, we can reuse __DRIimage for DRI3 which make a lot of sense. The GBM and Wayland backends already use __DRIimage for color buffers, but convert them to __DRI2buffer to be able to pass them into the DRI driver. Kristian > +#define __DRI_DRI3 "DRI_DRI3" > +#define __DRI_DRI3_VERSION 1 > + > +struct __DRIdri3ExtensionRec { > + __DRIextension base; > + > + /* Common DRI functions, shared with DRI2 */ > + __DRIcreateNewScreen2 createNewScreen2; > + __DRIcreateNewDrawable createNewDrawable; > + __DRIcreateNewContext createNewContext; > + __DRIcreateContextAttribs createContextAttribs; > + __DRIgetAPIMask getAPIMask; > + > + /* DRI3-specific functions*/ > + > + /* Allocate color buffer for front/back of windows and pixmaps > + */ > + __DRIdri3AllocateBuffer allocateBuffer; > + > + /* Release color buffer > + */ > + __DRIdri3ReleaseBuffer releaseBuffer; > + > + /* Given a buffer, wrap it in a DMA-BUF file descriptor > + */ > + __DRIdri3BufferToFd bufferToFd; > + > + /* Given a DMA-BUF file descriptor, construct a suitable > + * color buffer > + */ > + __DRIdri3FdToBuffer fdToBuffer; > + > + /* Ask the driver for a pointer to an integer which > + * can be incremented when the drawable needs to be > + * revalidated > + */ > + __DRIdri3Stamp stamp; > +}; > + > +/** > * This extension provides functionality to enable various EGLImage > * extensions. > */ > diff --git a/src/glx/Makefile.am b/src/glx/Makefile.am > index f01709b..854025d 100644 > --- a/src/glx/Makefile.am > +++ b/src/glx/Makefile.am > @@ -92,6 +92,8 @@ libglx_la_SOURCES = \ > glxhash.c \ > dri2_glx.c \ > dri2.c \ > + dri3_glx.c \ > + dri3_common.c \ > applegl_glx.c > > GL_LIBS = \ > diff --git a/src/glx/dri3_common.c b/src/glx/dri3_common.c > new file mode 100644 > index 0000000..c758f96 > --- /dev/null > +++ b/src/glx/dri3_common.c > @@ -0,0 +1,146 @@ > +/* > + * Copyright © 2013 Keith Packard > + * > + * Permission to use, copy, modify, distribute, and sell this software and its > + * documentation for any purpose is hereby granted without fee, provided that > + * the above copyright notice appear in all copies and that both that copyright > + * notice and this permission notice appear in supporting documentation, and > + * that the name of the copyright holders not be used in advertising or > + * publicity pertaining to distribution of the software without specific, > + * written prior permission. The copyright holders make no representations > + * about the suitability of this software for any purpose. It is provided "as > + * is" without express or implied warranty. > + * > + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, > + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO > + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR > + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, > + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER > + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE > + * OF THIS SOFTWARE. > + */ > + > +/* > + * This code is derived from src/egl/drivers/dri2/common.c which > + * carries the following copyright: > + * > + * Copyright © 2011 Intel Corporation > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the "Software"), > + * to deal in the Software without restriction, including without limitation > + * the rights to use, copy, modify, merge, publish, distribute, sublicense, > + * and/or sell copies of the Software, and to permit persons to whom the > + * Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice (including the next > + * paragraph) shall be included in all copies or substantial portions of the > + * Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND > + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT > + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, > + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER > + * DEALINGS IN THE SOFTWARE. > + * > + * Authors: > + * Kristian Høgsberg <kr...@bi...> > + * Benjamin Franzke <ben...@go...> > + */ > + > +#include <stdio.h> > +#include <string.h> > + > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <unistd.h> > +#include <GL/gl.h> > +#include "glapi.h" > +#include "glxclient.h" > +#include "xf86dri.h" > +#include <dlfcn.h> > +#include <fcntl.h> > +#include <unistd.h> > +#include <sys/types.h> > +#include <sys/mman.h> > +#include <sys/time.h> > +#include "xf86drm.h" > +#include "dri_common.h" > +#include "dri3_priv.h" > + > +#define DRIVER_MAP_DRI3_ONLY > +#include "pci_ids/pci_id_driver_map.h" > + > +#include <libudev.h> > + > +static struct udev_device * > +dri3_udev_device_new_from_fd(struct udev *udev, int fd) > +{ > + struct udev_device *device; > + struct stat buf; > + > + if (fstat(fd, &buf) < 0) { > + ErrorMessageF("DRI3: failed to stat fd %d", fd); > + return NULL; > + } > + > + device = udev_device_new_from_devnum(udev, 'c', buf.st_rdev); > + if (device == NULL) { > + ErrorMessageF("DRI3: could not create udev device for fd %d", fd); > + return NULL; > + } > + > + return device; > +} > + > +char * > +dri3_get_driver_for_fd(int fd) > +{ > + struct udev *udev; > + struct udev_device *device, *parent; > + const char *pci_id; > + char *driver = NULL; > + int vendor_id, chip_id, i, j; > + > + udev = udev_new(); > + device = dri3_udev_device_new_from_fd(udev, fd); > + if (device == NULL) > + return NULL; > + > + parent = udev_device_get_parent(device); > + if (parent == NULL) { > + ErrorMessageF("DRI3: could not get parent device"); > + goto out; > + } > + > + pci_id = udev_device_get_property_value(parent, "PCI_ID"); > + if (pci_id == NULL || > + sscanf(pci_id, "%x:%x", &vendor_id, &chip_id) != 2) { > + ErrorMessageF("DRI3: malformed or no PCI ID"); > + goto out; > + } > + > + for (i = 0; driver_map[i].driver; i++) { > + if (vendor_id != driver_map[i].vendor_id) > + continue; > + if (driver_map[i].num_chips_ids == -1) { > + driver = strdup(driver_map[i].driver); > + goto out; > + } > + > + for (j = 0; j < driver_map[i].num_chips_ids; j++) > + if (driver_map[i].chip_ids[j] == chip_id) { > + driver = strdup(driver_map[i].driver); > + goto out; > + } > + } > + > +out: > + udev_device_unref(device); > + udev_unref(udev); > + > + return driver; > +} > diff --git a/src/glx/dri3_glx.c b/src/glx/dri3_glx.c > new file mode 100644 > index 0000000..4d275f2 > --- /dev/null > +++ b/src/glx/dri3_glx.c > @@ -0,0 +1,1539 @@ > +/* > + * Copyright © 2013 Keith Packard > + * > + * Permission to use, copy, modify, distribute, and sell this software and its > + * documentation for any purpose is hereby granted without fee, provided that > + * the above copyright notice appear in all copies and that both that copyright > + * notice and this permission notice appear in supporting documentation, and > + * that the name of the copyright holders not be used in advertising or > + * publicity pertaining to distribution of the software without specific, > + * written prior permission. The copyright holders make no representations > + * about the suitability of this software for any purpose. It is provided "as > + * is" without express or implied warranty. > + * > + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, > + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO > + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR > + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, > + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER > + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE > + * OF THIS SOFTWARE. > + */ > + > +/* > + * Portions of this code were adapted from dri2_glx.c which carries the > + * following copyright: > + * > + * Copyright © 2008 Red Hat, Inc. > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the "Soft- > + * ware"), to deal in the Software without restriction, including without > + * limitation the rights to use, copy, modify, merge, publish, distribute, > + * and/or sell copies of the Software, and to permit persons to whom the > + * Software is furnished to do so, provided that the above copyright > + * notice(s) and this permission notice appear in all copies of the Soft- > + * ware and that both the above copyright notice(s) and this permission > + * notice appear in supporting documentation. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS > + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- > + * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY > + * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN > + * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE- > + * QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, > + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER > + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR- > + * MANCE OF THIS SOFTWARE. > + * > + * Except as contained in this notice, the name of a copyright holder shall > + * not be used in advertising or otherwise to promote the sale, use or > + * other dealings in this Software without prior written authorization of > + * the copyright holder. > + * > + * Authors: > + * Kristian Høgsberg (kr...@re...) > + */ > + > +#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL) > + > +#include <X11/Xlib.h> > +#include <X11/extensions/Xfixes.h> > +#include <X11/Xlib-xcb.h> > +#include <X11/xshmfence.h> > +#include <xcb/xcb.h> > +#include <xcb/dri3.h> > +#include <xcb/present.h> > +#include <GL/gl.h> > +#include "glapi.h" > +#include "glxclient.h" > +#include "xf86dri.h" > +#include <dlfcn.h> > +#include <fcntl.h> > +#include <unistd.h> > +#include <sys/types.h> > +#include <sys/mman.h> > +#include <sys/time.h> > +#include "xf86drm.h" > +#include "dri_common.h" > +#include "dri3_priv.h" > + > +static const struct glx_context_vtable dri3_context_vtable; > + > +#define HAS_SBC 0 > + > +#if HAS_SBC > + > +/* For XCB's handling of ust/msc/sbc counters, we have to hand it the high and > + * low halves separately. This helps you split them. > + */ > +static void > +split_counter(uint64_t counter, uint32_t *hi, uint32_t *lo) > +{ > + *hi = (counter >> 32); > + *lo = counter & 0xffffffff; > +} > + > +static uint64_t > +merge_counter(uint32_t hi, uint32_t lo) > +{ > + return ((uint64_t)hi << 32) | lo; > +} > +#endif /* HAS_SBC */ > + > +static inline void > +dri3_fence_reset(xcb_connection_t *c, __DRIdri3Buffer *buffer) { > + xshmfence_reset(buffer->shm_fence); > +} > + > +static inline void > +dri3_fence_trigger(xcb_connection_t *c, __DRIdri3Buffer *buffer) { > + xcb_sync_trigger_fence(c, buffer->sync_fence); > +} > + > +static inline void > +dri3_fence_await(xcb_connection_t *c, __DRIdri3Buffer *buffer) { > + xcb_flush(c); > + xshmfence_await(buffer->shm_fence); > +} > + > +static void > +dri3_destroy_context(struct glx_context *context) > +{ > + struct dri3_context *pcp = (struct dri3_context *) context; > + struct dri3_screen *psc = (struct dri3_screen *) context->psc; > + > + driReleaseDrawables(&pcp->base); > + > + free((char *) context->extensions); > + > + (*psc->core->destroyContext) (pcp->driContext); > + > + free(pcp); > +} > + > +static Bool > +dri3_bind_context(struct glx_context *context, struct glx_context *old, > + GLXDrawable draw, GLXDrawable read) > +{ > + struct dri3_context *pcp = (struct dri3_context *) context; > + struct dri3_screen *psc = (struct dri3_screen *) pcp->base.psc; > + struct dri3_drawable *pdraw, *pread; > + > + pdraw = (struct dri3_drawable *) driFetchDrawable(context, draw); > + pread = (struct dri3_drawable *) driFetchDrawable(context, read); > + > + driReleaseDrawables(&pcp->base); > + > + if (pdraw == NULL || pread == NULL) > + return GLXBadDrawable; > + > + if (!(*psc->core->bindContext) (pcp->driContext, > + pdraw->driDrawable, pread->driDrawable)) > + return GLXBadContext; > + > + return Success; > +} > + > +static void > +dri3_unbind_context(struct glx_context *context, struct glx_context *new) > +{ > + struct dri3_context *pcp = (struct dri3_context *) context; > + struct dri3_screen *psc = (struct dri3_screen *) pcp->base.psc; > + > + (*psc->core->unbindContext) (pcp->driContext); > +} > + > +static struct glx_context * > +dri3_create_context(struct glx_screen *base, > + struct glx_config *config_base, > + struct glx_context *shareList, int renderType) > +{ > + struct dri3_context *pcp, *pcp_shared; > + struct dri3_screen *psc = (struct dri3_screen *) base; > + __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base; > + __DRIcontext *shared = NULL; > + > + if (shareList) { > + /* If the shareList context is not a DRI3 context, we cannot possibly > + * create a DRI3 context that shares it. > + */ > + if (shareList->vtable->destroy != dri3_destroy_context) { > + return NULL; > + } > + > + pcp_shared = (struct dri3_context *) shareList; > + shared = pcp_shared->driContext; > + } > + > + pcp = calloc(1, sizeof *pcp); > + if (pcp == NULL) > + return NULL; > + > + if (!glx_context_init(&pcp->base, &psc->base, &config->base)) { > + free(pcp); > + return NULL; > + } > + > + pcp->driContext = > + (*psc->dri3->createNewContext) (psc->driScreen, > + config->driConfig, shared, pcp); > + > + if (pcp->driContext == NULL) { > + free(pcp); > + return NULL; > + } > + > + pcp->base.vtable = &dri3_context_vtable; > + > + return &pcp->base; > +} > + > +static struct glx_context * > +dri3_create_context_attribs(struct glx_screen *base, > + struct glx_config *config_base, > + struct glx_context *shareList, > + unsigned num_attribs, > + const uint32_t *attribs, > + unsigned *error) > +{ > + struct dri3_context *pcp = NULL; > + struct dri3_context *pcp_shared = NULL; > + struct dri3_screen *psc = (struct dri3_screen *) base; > + __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base; > + __DRIcontext *shared = NULL; > + > + uint32_t minor_ver = 1; > + uint32_t major_ver = 2; > + uint32_t flags = 0; > + unsigned api; > + int reset = __DRI_CTX_RESET_NO_NOTIFICATION; > + uint32_t ctx_attribs[2 * 5]; > + unsigned num_ctx_attribs = 0; > + uint32_t render_type; > + > + /* Remap the GLX tokens to DRI2 tokens. > + */ > + if (!dri2_convert_glx_attribs(num_attribs, attribs, > + &major_ver, &minor_ver, > + &render_type, &flags, &api, > + &reset, error)) > + goto error_exit; > + > + /* Check the renderType value */ > + if (!validate_renderType_against_config(config_base, render_type)) > + goto error_exit; > + > + if (shareList) { > + pcp_shared = (struct dri3_context *) shareList; > + shared = pcp_shared->driContext; > + } > + > + pcp = calloc(1, sizeof *pcp); > + if (pcp == NULL) { > + *error = __DRI_CTX_ERROR_NO_MEMORY; > + goto error_exit; > + } > + > + if (!glx_context_init(&pcp->base, &psc->base, &config->base)) > + goto error_exit; > + > + ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_MAJOR_VERSION; > + ctx_attribs[num_ctx_attribs++] = major_ver; > + ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_MINOR_VERSION; > + ctx_attribs[num_ctx_attribs++] = minor_ver; > + > + /* Only send a value when the non-default value is requested. By doing > + * this we don't have to check the driver's DRI3 version before sending the > + * default value. > + */ > + if (reset != __DRI_CTX_RESET_NO_NOTIFICATION) { > + ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_RESET_STRATEGY; > + ctx_attribs[num_ctx_attribs++] = reset; > + } > + > + if (flags != 0) { > + ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_FLAGS; > + > + /* The current __DRI_CTX_FLAG_* values are identical to the > + * GLX_CONTEXT_*_BIT values. > + */ > + ctx_attribs[num_ctx_attribs++] = flags; > + } > + > + pcp->driContext = > + (*psc->dri3->createContextAttribs) (psc->driScreen, > + api, > + config->driConfig, > + shared, > + num_ctx_attribs / 2, > + ctx_attribs, > + error, > + pcp); > + > + if (pcp->driContext == NULL) > + goto error_exit; > + > + pcp->base.vtable = &dri3_context_vtable; > + > + return &pcp->base; > + > +error_exit: > + free(pcp); > + > + return NULL; > +} > + > +static void > +dri3_destroy_drawable(__GLXDRIdrawable *base) > +{ > + struct dri3_screen *psc = (struct dri3_screen *) base->psc; > + struct dri3_drawable *pdraw = (struct dri3_drawable *) base; > + > + (*psc->core->destroyDrawable) (pdraw->driDrawable); > + > + free(pdraw); > +} > + > +static __GLXDRIdrawable * > +dri3_create_drawable(struct glx_screen *base, XID xDrawable, > + GLXDrawable drawable, struct glx_config *config_base) > +{ > + struct dri3_drawable *pdraw; > + struct dri3_screen *psc = (struct dri3_screen *) base; > + __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base; > + GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1; > + > + pdraw = calloc(1, sizeof(*pdraw)); > + if (!pdraw) > + return NULL; > + > + pdraw->base.destroyDrawable = dri3_destroy_drawable; > + pdraw->base.xDrawable = xDrawable; > + pdraw->base.drawable = drawable; > + pdraw->base.psc = &psc->base; > +// pdraw->bufferCount = 0; > + pdraw->swap_interval = 1; /* default may be overridden below */ > + pdraw->have_back = 0; > + pdraw->have_fake_front = 0; > + > + if (psc->config) > + psc->config->configQueryi(psc->driScreen, > + "vblank_mode", &vblank_mode); > + > + switch (vblank_mode) { > + case DRI_CONF_VBLANK_NEVER: > + case DRI_CONF_VBLANK_DEF_INTERVAL_0: > + pdraw->swap_interval = 0; > + break; > + case DRI_CONF_VBLANK_DEF_INTERVAL_1: > + case DRI_CONF_VBLANK_ALWAYS_SYNC: > + default: > + pdraw->swap_interval = 1; > + break; > + } > + > + (void) __glXInitialize(psc->base.dpy); > + > + /* Create a new drawable */ > + pdraw->driDrawable = > + (*psc->dri3->createNewDrawable) (psc->driScreen, > + config->driConfig, pdraw); > + > + if (!pdraw->driDrawable) { > + free(pdraw); > + return NULL; > + } > + > + /* > + * Make sure server has the same swap interval we do for the new > + * drawable. > + */ > + if (psc->vtable.setSwapInterval) > + psc->vtable.setSwapInterval(&pdraw->base, pdraw->swap_interval); > + > + return &pdraw->base; > +} > + > +#if HAS_SBC > +static int > +dri3_drawable_get_msc(struct glx_screen *psc, __GLXDRIdrawable *pdraw, > + int64_t *ust, int64_t *msc, int64_t *sbc) > +{ > + xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy); > + xcb_dri2_get_msc_cookie_t get_msc_cookie; > + xcb_dri2_get_msc_reply_t *get_msc_reply; > + > + get_msc_cookie = xcb_dri2_get_msc_unchecked(c, pdraw->xDrawable); > + get_msc_reply = xcb_dri2_get_msc_reply(c, get_msc_cookie, NULL); > + > + if (!get_msc_reply) > + return 0; > + > + *ust = merge_counter(get_msc_reply->ust_hi, get_msc_reply->ust_lo); > + *msc = merge_counter(get_msc_reply->msc_hi, get_msc_reply->msc_lo); > + *sbc = merge_counter(get_msc_reply->sbc_hi, get_msc_reply->sbc_lo); > + free(get_msc_reply); > + > + return 1; > +} > + > +static int > +dri3_wait_for_msc(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor, > + int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc) > +{ > + xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy); > + xcb_dri2_wait_msc_cookie_t wait_msc_cookie; > + xcb_dri2_wait_msc_reply_t *wait_msc_reply; > + uint32_t target_msc_hi, target_msc_lo; > + uint32_t divisor_hi, divisor_lo; > + uint32_t remainder_hi, remainder_lo; > + > + split_counter(target_msc, &target_msc_hi, &target_msc_lo); > + split_counter(divisor, &divisor_hi, &divisor_lo); > + split_counter(remainder, &remainder_hi, &remainder_lo); > + > + wait_msc_cookie = xcb_dri2_wait_msc_unchecked(c, pdraw->xDrawable, > + target_msc_hi, target_msc_lo, > + divisor_hi, divisor_lo, > + remainder_hi, remainder_lo); > + wait_msc_reply = xcb_dri2_wait_msc_reply(c, wait_msc_cookie, NULL); > + > + if (!wait_msc_reply) > + return 0; > + > + *ust = merge_counter(wait_msc_reply->ust_hi, wait_msc_reply->ust_lo); > + *msc = merge_counter(wait_msc_reply->msc_hi, wait_msc_reply->msc_lo); > + *sbc = merge_counter(wait_msc_reply->sbc_hi, wait_msc_reply->sbc_lo); > + free(wait_msc_reply); > + > + return 1; > +} > + > +static int > +dri3_wait_for_sbc(__GLXDRIdrawable *pdraw, int64_t target_sbc, int64_t *ust, > + int64_t *msc, int64_t *sbc) > +{ > + xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy); > + xcb_dri2_wait_sbc_cookie_t wait_sbc_cookie; > + xcb_dri2_wait_sbc_reply_t *wait_sbc_reply; > + uint32_t target_sbc_hi, target_sbc_lo; > + > + split_counter(target_sbc, &target_sbc_hi, &target_sbc_lo); > + > + wait_sbc_cookie = xcb_dri2_wait_sbc_unchecked(c, pdraw->xDrawable, > + target_sbc_hi, target_sbc_lo); > + wait_sbc_reply = xcb_dri2_wait_sbc_reply(c, wait_sbc_cookie, NULL); > + > + if (!wait_sbc_reply) > + return 0; > + > + *ust = merge_counter(wait_sbc_reply->ust_hi, wait_sbc_reply->ust_lo); > + *msc = merge_counter(wait_sbc_reply->msc_hi, wait_sbc_reply->msc_lo); > + *sbc = merge_counter(wait_sbc_reply->sbc_hi, wait_sbc_reply->sbc_lo); > + free(wait_sbc_reply); > + > + return 1; > +} > +#endif /* HAS_SBC */ > + > +static __DRIcontext * > +dri3_get_current_context(void) > +{ > + struct glx_context *gc = __glXGetCurrentContext(); > + struct dri3_context *dri3Ctx = (struct dri3_context *)gc; > + > + return dri3Ctx ? dri3Ctx->driContext : NULL; > +} > + > +/** > + * dri3Throttle - Request driver throttling > + * > + * This function uses the DRI2 throttle extension to give the > + * driver the opportunity to throttle on flush front, copysubbuffer > + * and swapbuffers. > + */ > +static void > +dri3_throttle(struct dri3_screen *psc, > + struct dri3_drawable *draw, > + enum __DRI2throttleReason reason) > +{ > + if (psc->throttle) { > + __DRIcontext *ctx = dri3_get_current_context(); > + > + psc->throttle->throttle(ctx, draw->driDrawable, reason); > + } > +} > + > +/** > + * Asks the driver to flush any queued work necessary for serializing with the > + * X command stream, and optionally the slightly more strict requirement of > + * glFlush() equivalence (which would require flushing even if nothing had > + * been drawn to a window system framebuffer, for example). > + */ > +static void > +dri3_flush(struct dri3_screen *psc, > + __DRIcontext *ctx, > + struct dri3_drawable *draw, > + unsigned flags, > + enum __DRI2throttleReason throttle_reason) > +{ > + if (ctx && psc->f && psc->f->base.version >= 4) { > + psc->f->flush_with_flags(ctx, draw->driDrawable, flags, throttle_reason); > + } else { > + if (flags & __DRI2_FLUSH_CONTEXT) > + glFlush(); > + > + if (psc->f) > + psc->f->flush(draw->driDrawable); > + > + dri3_throttle(psc, draw, throttle_reason); > + } > +} > + > +static xcb_gcontext_t > +dri3_drawable_gc(struct dri3_drawable *priv) > +{ > + if (!priv->gc) { > + uint32_t v; > + xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy); > + > + v = 0; > + xcb_create_gc(c, > + (priv->gc = xcb_generate_id(c)), > + priv->base.xDrawable, > + XCB_GC_GRAPHICS_EXPOSURES, > + &v); > + } > + return priv->gc; > +} > + > +static __DRIdri3Buffer * > +dri3_back_buffer(struct dri3_drawable *priv) > +{ > + return priv->buffers[__DRI3_BUFFER_BACK]; > +} > + > +static __DRIdri3Buffer * > +dri3_fake_front_buffer(struct dri3_drawable *priv) > +{ > + return priv->buffers[__DRI3_BUFFER_FRONT]; > +} > + > +static void > +dri3_copy_area (xcb_connection_t *c /**< */, > + xcb_drawable_t src_drawable /**< */, > + xcb_drawable_t dst_drawable /**< */, > + xcb_gcontext_t gc /**< */, > + int16_t src_x /**< */, > + int16_t src_y /**< */, > + int16_t dst_x /**< */, > + int16_t dst_y /**< */, > + uint16_t width /**< */, > + uint16_t height /**< */) > +{ > + xcb_void_cookie_t cookie; > + > + cookie = xcb_copy_area_checked(c, > + src_drawable, > + dst_drawable, > + gc, > + src_x, > + src_y, > + dst_x, > + dst_y, > + width, > + height); > + xcb_discard_reply(c, cookie.sequence); > +} > + > +static void > +_dri3_copy_sub_buffer(__GLXDRIdrawable *pdraw, int x, int y, > + int width, int height, > + enum __DRI2throttleReason reason, Bool flush) > +{ > + struct dri3_drawable *priv = (struct dri3_drawable *) pdraw; > + struct dri3_screen *psc = (struct dri3_screen *) pdraw->psc; > + xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy); > + __DRIcontext *ctx = dri3_get_current_context(); > + __DRIdri3Buffer *back = dri3_back_buffer(priv); > + > + unsigned flags; > + > + /* Check we have the right attachments */ > + if (!priv->have_back || priv->is_pixmap) > + return; > + > + flags = __DRI2_FLUSH_DRAWABLE; > + if (flush) > + flags |= __DRI2_FLUSH_CONTEXT; > + dri3_flush(psc, ctx, priv, flags, __DRI2_THROTTLE_SWAPBUFFER); > + > + y = priv->height - y - height; > + > + dri3_fence_reset(c, back); > + dri3_copy_area(c, > + dri3_back_buffer(priv)->pixmap, > + priv->base.xDrawable, > + dri3_drawable_gc(priv), > + x, y, x, y, width, height); > + dri3_fence_trigger(c, back); > + /* Refresh the fake front (if present) after we just damaged the real > + * front. > + */ > + if (priv->have_fake_front) { > + dri3_fence_reset(c, dri3_fake_front_buffer(priv)); > + dri3_copy_area(c, > + dri3_back_buffer(priv)->pixmap, > + dri3_fake_front_buffer(priv)->pixmap, > + dri3_drawable_gc(priv), > + x, y, x, y, width, height); > + dri3_fence_trigger(c, dri3_fake_front_buffer(priv)); > + dri3_fence_await(c, dri3_fake_front_buffer(priv)); > + } > + dri3_fence_await(c, back); > +} > + > +static void > +dri3_copy_sub_buffer(__GLXDRIdrawable *pdraw, int x, int y, > + int width, int height, Bool flush) > +{ > + _dri3_copy_sub_buffer(pdraw, x, y, width, height, > + __DRI2_THROTTLE_COPYSUBBUFFER, flush); > +} > + > + > +static void > +dri3_copy_drawable(struct dri3_drawable *priv, Drawable dest, Drawable src) > +{ > + struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc; > + xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy); > + > + if (psc->f) > + (*psc->f->flush) (priv->driDrawable); > + > + dri3_copy_area(c, > + src, dest, > + dri3_drawable_gc(priv), > + 0, 0, 0, 0, priv->width, priv->height); > +} > + > +static void > +dri3_wait_x(struct glx_context *gc) > +{ > + struct dri3_drawable *priv = (struct dri3_drawable *) > + GetGLXDRIDrawable(gc->currentDpy, gc->currentDrawable); > + > + if (priv == NULL || !priv->have_fake_front) > + return; > + > + dri3_copy_drawable(priv, dri3_fake_front_buffer(priv)->pixmap, priv->base.xDrawable); > +} > + > +static void > +dri3_wait_gl(struct glx_context *gc) > +{ > + struct dri3_drawable *priv = (struct dri3_drawable *) > + GetGLXDRIDrawable(gc->currentDpy, gc->currentDrawable); > + > + if (priv == NULL || !priv->have_fake_front) > + return; > + > + dri3_copy_drawable(priv, priv->base.xDrawable, dri3_fake_front_buffer(priv)->pixmap); > +} > + > +/** > + * Called by the driver when it needs to update the real front buffer with the > + * contents of its fake front buffer. > + */ > +static void > +dri3_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate) > +{ > + struct glx_context *gc; > + struct dri3_drawable *pdraw = loaderPrivate; > + struct dri3_screen *psc; > + > + if (!pdraw) > + return; > + > + if (!pdraw->base.psc) > + return; > + > + psc = (struct dri3_screen *) pdraw->base.psc; > + > + (void) __glXInitialize(psc->base.dpy); > + > + gc = __glXGetCurrentContext(); > + > + dri3_throttle(psc, pdraw, __DRI2_THROTTLE_FLUSHFRONT); > + > + dri3_wait_gl(gc); > +} > + > +static __DRIdri3Buffer * > +dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw, unsigned int format, int width, int height, int depth) > +{ > + struct dri3_screen *psc = (struct dri3_screen *) glx_screen; > + Display *dpy = glx_screen->dpy; > + __DRIdri3Buffer *buffer; > + xcb_connection_t *c = XGetXCBConnection(dpy); > + xcb_pixmap_t pixmap; > + xcb_sync_fence_t sync_fence; > + int32_t *shm_fence; > + int buffer_fd, fence_fd; > + > + fence_fd = xshmfence_alloc_shm(); > + if (fence_fd < 0) > + return NULL; > + shm_fence = xshmfence_map_shm(fence_fd); > + if (shm_fence == NULL) > + goto no_shm_fence; > + > + buffer = (*psc->dri3->allocateBuffer) (psc->driScreen, > + format, > + width, > + height); > + if (buffer == NULL) > + goto no_buffer; > + > + buffer_fd = (*psc->dri3->bufferToFd)(psc->driScreen, buffer); > + if (buffer_fd < 0) > + goto no_buffer_fd; > + > + xcb_dri3_pixmap_from_buffer(c, > + (pixmap = xcb_generate_id(c)), > + draw, > + buffer->size, > + width, height, buffer->pitch, > + depth, buffer->cpp * 8, > + buffer_fd); > + > + xcb_dri3_fence_from_fd(c, > + pixmap, > + (sync_fence = xcb_generate_id(c)), > + false, > + fence_fd); > + > + buffer->pixmap = pixmap; > + buffer->sync_fence = sync_fence; > + buffer->shm_fence = shm_fence; > + buffer->width = width; > + buffer->height = height; > + return buffer; > + > +no_buffer_fd: > + (*psc->dri3->releaseBuffer)(psc->driScreen, buffer); > +no_buffer: > + xshmfence_unmap_shm(shm_fence); > +no_shm_fence: > + close(fence_fd); > + return NULL; > +} > + > +static void > +dri3_free_render_buffer(struct dri3_drawable *pdraw, __DRIdri3Buffer *buffer) > +{ > + struct dri3_screen *psc = (struct dri3_screen *) pdraw->base.psc; > + xcb_connection_t *c = XGetXCBConnection(pdraw->base.psc->dpy); > + > + xcb_free_pixmap(c, buffer->pixmap); > + xcb_sync_destroy_fence(c, buffer->sync_fence); > + xshmfence_unmap_shm(buffer->shm_fence); > + (*psc->dri3->releaseBuffer)(psc->driScreen, buffer); > +} > + > +static int > +dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate) > +{ > + struct dri3_drawable *priv = loaderPrivate; > + xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy); > + > + /* First time through, go get the current drawable geometry > + */ > + if (priv->width == 0 || priv->height == 0 || priv->depth == 0) { > + xcb_get_geometry_cookie_t geom_cookie; > + xcb_get_geometry_reply_t *geom_reply; > + struct dri3_screen *psc; > + xcb_void_cookie_t cookie; > + xcb_generic_error_t *error; > + > + cookie = xcb_present_select_input_checked(c, > + (priv->eid = xcb_generate_id(c)), > + priv->base.xDrawable, > + XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY| > + XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY); > + > + if (!priv->present_extension) { > + priv->present_extension = xcb_get_extension_data(c, &xcb_present_id); > + if (!priv->present_extension) > + return false; > + } > + > + psc = (struct dri3_screen *) priv->base.psc; > + priv->special_event = xcb_register_for_special_event(c, > + priv->present_extension->major_opcode, > + priv->eid, > + psc->dri3->stamp(driDrawable)); > + > + geom_cookie = xcb_get_geometry(c, priv->base.xDrawable); > + > + geom_reply = xcb_get_geometry_reply(c, geom_cookie, NULL); > + > + if (!geom_reply) > + return false; > + > + priv->width = geom_reply->width; > + priv->height = geom_reply->height; > + priv->depth = geom_reply->depth; > + priv->is_pixmap = false; > + > + free(geom_reply); > + > + error = xcb_request_check(c, cookie); > + > + if (error) { > + if (error->error_code != BadWindow) { > + free(error); > + return false; > + } > + priv->is_pixmap = true; > + xcb_unregister_for_special_event(c, priv->special_event); > + priv->special_event = NULL; > + } > + } > + > + /* Check to see if any configuration changes have occurred > + * since we were last invoked > + */ > + if (priv->special_event) { > + xcb_generic_event_t *ev; > + > + while ((ev = xcb_check_for_special_event(c, priv->special_event)) != NULL) { > + xcb_present_generic_event_t *pe = (void *) ev; > + > + switch (pe->evtype) { > + case XCB_PRESENT_EVENT_CONFIGURE_NOTIFY: { > + xcb_configure_notify_event_t *ce = (void *) ev; > + > + priv->width = ce->width; > + priv->height = ce->height; > + break; > + } > + case XCB_PRESENT_EVENT_IDLE_NOTIFY: > + break; > + } > + free(ev); > + } > + } > + return true; > +} > + > +static __DRIdri3Buffer * > +dri3_get_pixmap_buffer(__DRIdrawable *driDrawable, > + unsigned int format, > + enum __DRI3bufferType buffer_type, > + void *loaderPrivate) > +{ > + struct dri3_drawable *pdraw = loaderPrivate; > + __DRIdri3Buffer *buffer = pdraw->buffers[buffer_type]; > + Pixmap pixmap; > + xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie; > + xcb_dri3_buffer_from_pixmap_reply_t *bp_reply; > + int *fds; > + int buffer_fd; > + Display *dpy; > + struct dri3_screen *psc; > + xcb_connection_t *c; > + xcb_sync_fence_t sync_fence; > + int32_t *shm_fence; > + int fence_fd; > + > + if (buffer) > + return buffer; > + > + pixmap = pdraw->base.xDrawable; > + psc = (struct dri3_screen *) pdraw->base.psc; > + dpy = psc->base.dpy; > + c = XGetXCBConnection(dpy); > + > + fence_fd = xshmfence_alloc_shm(); > + if (fence_fd < 0) > + return NULL; > + shm_fence = xshmfence_map_shm(fence_fd); > + if (shm_fence == NULL) { > + close (fence_fd); > + return NULL; > + } > + > + xcb_dri3_fence_from_fd(c, > + pixmap, > + (sync_fence = xcb_generate_id(c)), > + false, > + fence_fd); > + > + bp_cookie = xcb_dri3_buffer_from_pixmap(c, pixmap); > + bp_reply = xcb_dri3_buffer_from_pixmap_reply(c, bp_cookie, NULL); > + if (!bp_reply) > + goto no_pixmap; > + fds = xcb_dri3_buffer_from_pixmap_reply_fds(c, bp_reply); > + buffer_fd = fds[0]; > + buffer = (*psc->dri3->fdToBuffer)(psc->driScreen, > + buffer_fd, > + format, > + bp_reply->width, > + bp_reply->height, > + bp_reply->stride, > + bp_reply->size); > + close(buffer_fd); > + if (!buffer) > + goto no_pixmap; > + > + buffer->pixmap = pixmap; > + buffer->width = bp_reply->width; > + buffer->height = bp_reply->height; > + buffer->buffer_type = buffer_type; > + buffer->shm_fence = shm_fence; > + buffer->sync_fence = sync_fence; > + > + pdraw->buffers[buffer_type] = buffer; > + return buffer; > + > +no_pixmap: > + xcb_sync_destroy_fence(c, sync_fence); > + xshmfence_unmap_shm(shm_fence); > + return NULL; > +} > + > +static __DRIdri3Buffer * > +dri3_get_buffer(__DRIdrawable *driDrawable, > + unsigned int format, > + enum __DRI3bufferType buffer_type, > + void *loaderPrivate) > +{ > + struct dri3_drawable *priv = loaderPrivate; > + __DRIdri3Buffer *buffer = priv->buffers[buffer_type]; > + > + if (!buffer || buffer->width != priv->width || buffer->height != priv->height) { > + xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy); > + __DRIdri3Buffer *new_buffer; > + > + /* Allocate the new buffers > + */ > + new_buffer = dri3_alloc_render_buffer(priv->base.psc, > + priv->base.xDrawable, > + format, priv->width, priv->height, priv->depth); > + if (!new_buffer) > + return NULL; > + switch (buffer_type) { > + case __DRI3_BUFFER_BACK: > + if (buffer) { > + dri3_fence_reset(c, new_buffer); > + dri3_fence_await(c, buffer); > + dri3_copy_area(c, > + buffer->pixmap, > + new_buffer->pixmap, > + dri3_drawable_gc(priv), > + 0, 0, 0, 0, priv->width, priv->height); > + dri3_fence_trigger(c, new_buffer); > + dri3_free_render_buffer(priv, buffer); > + dri3_fence_await(c, new_buffer); > + } > + break; > + case __DRI3_BUFFER_FRONT: > + dri3_fence_reset(c, new_buffer); > + dri3_copy_area(c, > + priv->base.xDrawable, > + new_buffer->pixmap, > + dri3_drawable_gc(priv), > + 0, 0, 0, 0, priv->width, priv->height); > + dri3_fence_trigger(c, new_buffer); > + dri3_fence_await(c, new_buffer); > + break; > + } > + buffer = new_buffer; > + buffer->buffer_type = buffer_type; > + priv->buffers[buffer_type] = buffer; > + } > + > + /* Return the requested buffer */ > + return buffer; > +} > + > +static void > +dri3_free_buffer(__DRIdrawable *driDrawable, > + enum __DRI3bufferType buffer_type, > + void *loaderPrivate) > +{ > + struct dri3_drawable *priv = loaderPrivate; > + __DRIdri3Buffer *buffer = priv->buffers[buffer_type]; > + > + if (buffer) { > + dri3_free_render_buffer(priv, buffer); > + priv->buffers[buffer_type] = NULL; > + } > +} > + > +static int > +dri3_get_buffers(__DRIdrawable *driDrawable, > + int *width, int *height, > + unsigned int format, > + void *loaderPrivate, > + int need_front, > + int need_back, > + __DRIdri3Buffer **front, > + __DRIdri3Buffer **back) > +{ > + struct dri3_drawable *priv = loaderPrivate; > + > + *front = NULL; > + *back = NULL; > + > + if (!dri3_update_drawable(driDrawable, loaderPrivate)) > + return false; > + > + if (priv->is_pixmap) > + need_front = 1; > + > + if (need_front) { > + if (priv->is_pixmap) > + *front = dri3_get_pixmap_buffer(driDrawable, > + format, > + __DRI3_BUFFER_FRONT, > + loaderPrivate); > + else > + *front = dri3_get_buffer(driDrawable, > + format, > + __DRI3_BUFFER_FRONT, > + loaderPrivate); > + > + if (!*front) > + return false; > + priv->have_fake_front = !priv->is_pixmap; > + } else { > + dri3_free_buffer(driDrawable, __DRI3_BUFFER_FRONT, loaderPrivate); > + priv->have_fake_front = 0; > + } > + > + if (need_back) { > + *back = dri3_get_buffer(driDrawable, > + format, > + __DRI3_BUFFER_BACK, > + loaderPrivate); > + if (!*back) > + return false; > + priv->have_back = 1; > + } else { > + dri3_free_buffer(driDrawable, __DRI3_BUFFER_BACK, loaderPrivate); > + priv->have_back = 0; > + } > + > + /* Report back current geometry */ > + *width = priv->width; > + *height = priv->height; > + return true; > +} > + > +static int64_t > +dri3_swap_buffers(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor, > + int64_t remainder, Bool flush) > +{ > + struct dri3_drawable *priv = (struct dri3_drawable *) pdraw; > + struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc; > + xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy); > + int64_t ret = 0; > + > + __DRIcontext *ctx = dri3_get_current_context(); > + unsigned flags = __DRI2_FLUSH_DRAWABLE; > + if (flush) > + flags |= __DRI2_FLUSH_CONTEXT; > + dri3_flush(psc, ctx, priv, flags, __DRI2_THROTTLE_SWAPBUFFER); > + > + if (priv->buffers[0] && !priv->is_pixmap) { > + dri3_fence_reset(c, priv->buffers[0]); > + dri3_copy_area(c, > + priv->buffers[0]->pixmap, > + priv->base.xDrawable, > + dri3_drawable_gc(priv), > + 0, 0, 0, 0, priv->width, priv->height); > + dri3_fence_trigger(c, priv->buffers[0]); > + if (priv->have_fake_front) { > + dri3_fence_reset(c, priv->buffers[1]); > + dri3_copy_area(c, > + priv->buffers[0]->pixmap, > + priv->buffers[1]->pixmap, > + dri3_drawable_gc(priv), > + 0, 0, 0, 0, priv->width, priv->height); > + dri3_fence_trigger(c, priv->buffers[1]); > + } > + dri3_fence_await(c, priv->buffers[0]); > + if (priv->have_fake_front) > + dri3_fence_await(c, priv->buffers[1]); > + } > + > + return ret; > +} > + > +static int > +dri3_query_version(Display *dpy, int *major, int *minor) > +{ > + xcb_dri3_query_version_cookie_t cookie; > + xcb_dri3_query_version_reply_t *reply; > + xcb_connection_t *c = XGetXCBConnection(dpy); > + xcb_generic_error_t *error; > + > + cookie = xcb_dri3_query_version(c, > + XCB_DRI3_MAJOR_VERSION, > + XCB_DRI3_MINOR_VERSION); > + reply = xcb_dri3_query_version_reply(c, cookie, &error); > + if (!reply) { > + if (error) { > + free(error); > + } > + return 0; > + } > + *major = reply->major_version; > + *minor = reply->minor_version; > + free(reply); > + return 1; > +} > + > +static int > +dri3_open(Display *dpy, > + Window root, > + CARD32 provider) > +{ > + xcb_dri3_open_cookie_t cookie; > + xcb_dri3_open_reply_t *reply; > + xcb_connection_t *c = XGetXCBConnection(dpy); > + xcb_generic_error_t *error; > + int fd; > + > + cookie = xcb_dri3_open(c, > + root, > + provider); > + > + reply = xcb_dri3_open_reply(c, cookie, &error); > + if (!reply) > + return -1; > + > + if (reply->nfd != 1) { > + free(reply); > + return -1; > + } > + > + fd = xcb_dri3_open_reply_fds(c, reply)[0]; > + fcntl(fd, F_SETFD, FD_CLOEXEC); > + > + return fd; > +} > + > + > +static void > +dri3_destroy_screen(struct glx_screen *base) > +{ > + struct dri3_screen *psc = (struct dri3_screen *) base; > + > + /* Free the direct rendering per screen data */ > + (*psc->core->destroyScreen) (psc->driScreen); > + driDestroyConfigs(psc->driver_configs); > + close(psc->fd); > + free(psc); > +} > + > +#if HAS_SBC > +static int > +dri3_set_swap_interval(__GLXDRIdrawable *pdraw, int interval) > +{ > + xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy); > + struct dri3_drawable *priv = (struct dri3_drawable *) pdraw; > + GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1; > + struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc; > + > + if (psc->config) > + psc->config->configQueryi(psc->driScreen, > + "vblank_mode", &vblank_mode); > + > + switch (vblank_mode) { > + case DRI_CONF_VBLANK_NEVER: > + if (interval != 0) > + return GLX_BAD_VALUE; > + break; > + case DRI_CONF_VBLANK_ALWAYS_SYNC: > + if (interval <= 0) > + return GLX_BAD_VALUE; > + break; > + default: > + break; > + } > + > + xcb_dri2_swap_interval(c, priv->base.xDrawable, interval); > + priv->swap_interval = interval; > + > + return 0; > +} > + > +static int > +dri3_get_swap_interval(__GLXDRIdrawable *pdraw) > +{ > + struct dri3_drawable *priv = (struct dri3_drawable *) pdraw; > + > + return priv->swap_interval; > +} > +#endif > + > +static const __DRIdri3LoaderExtension dri3LoaderExtension = { > + {__DRI_DRI3_LOADER, __DRI_DRI3_LOADER_VERSION}, > + .getBuffers = dri3_get_buffers, > + .flushFrontBuffer = dri3_flush_front_buffer, > +}; > + > +static void > +dri3_bind_tex_image(Display * dpy, > + GLXDrawable drawable, > + int buffer, const int *attrib_list) > +{ > + struct glx_context *gc = __glXGetCurrentContext(); > + struct dri3_context *pcp = (struct dri3_context *) gc; > + __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable); > + struct dri3_drawable *pdraw = (struct dri3_drawable *) base; > + struct dri3_screen *psc; > + > + if (pdraw != NULL) { > + psc = (struct dri3_screen *) base->psc; > + > + if (psc->f && > + psc->f->base.version >= 3 && psc->f->invalidate) > + psc->f->invalidate(pdraw->driDrawable); > + > + XSync(dpy, false); > + if (psc->texBuffer->base.version >= 2 && > + psc->texBuffer->setTexBuffer2 != NULL) { > + (*psc->texBuffer->setTexBuffer2) (pcp->driContext, > + pdraw->base.textureTarget, > + pdraw->base.textureFormat, > + pdraw->driDrawable); > + } > + else { > + (*psc->texBuffer->setTexBuffer) (pcp->driContext, > + pdraw->base.textureTarget, > + pdraw->driDrawable); > + } > + } > +} > + > +static void > +dri3_release_tex_image(Display * dpy, GLXDrawable drawable, int buffer) > +{ > +#if __DRI_TEX_BUFFER_VERSION >= 3 > + struct glx_context *gc = __glXGetCurrentContext(); > + struct dri3_context *pcp = (struct dri3_context *) gc; > + __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable); > + struct glx_display *dpyPriv = __glXInitialize(dpy); > + struct dri3_drawable *pdraw = (struct dri3_drawable *) base; > + struct dri3_display *pdp = > + (struct dri3_display *) dpyPriv->dri3Display; > + struct dri3_screen *psc; > + > + if (pdraw != NULL) { > + psc = (struct dri3_screen *) base->psc; > + > + if (psc->texBuffer->base.version >= 3 && > + psc->texBuffer->releaseTexBuffer != NULL) { > + (*psc->texBuffer->releaseTexBuffer) (pcp->driContext, > + pdraw->base.textureTarget, > + pdraw->driDrawable); > + } > + } > +#endif > +} > + > +static const struct glx_context_vtable dri3_context_vtable = { > + dri3_destroy_context, > + dri3_bind_context, > + dri3_unbind_context, > + dri3_wait_gl, > + dri3_wait_x, > + DRI_glXUseXFont, > + dri3_bind_tex_image, > + dri3_release_tex_image, > + NULL, /* get_proc_address */ > +}; > + > +static void > +dri3_bind_extensions(struct dri3_screen *psc, struct glx_display * priv, > + const char *driverName) > +{ > +// const struct dri3_display *const pdp = (struct dri3_display *) priv->dri3Display; > + const __... [truncated message content] |
From: Kristian H. <hoe...@gm...> - 2013-11-01 17:51:15
|
On Thu, Oct 31, 2013 at 04:13:12PM -0700, Keith Packard wrote: > Make an easy place to splice in a DRI3 version of this function > > Signed-off-by: Keith Packard <ke...@ke...> > --- > src/mesa/drivers/dri/i915/intel_context.c | 90 +++++++++++++++++-------------- > src/mesa/drivers/dri/i965/brw_context.c | 22 ++++++-- > 2 files changed, 68 insertions(+), 44 deletions(-) Reviewed-by: Kristian Høgsberg <kr...@bi...> > diff --git a/src/mesa/drivers/dri/i915/intel_context.c b/src/mesa/drivers/dri/i915/intel_context.c > index 2748514..1798bc7 100644 > --- a/src/mesa/drivers/dri/i915/intel_context.c > +++ b/src/mesa/drivers/dri/i915/intel_context.c > @@ -133,15 +133,58 @@ intel_process_dri2_buffer(struct intel_context *intel, > struct intel_renderbuffer *rb, > const char *buffer_name); > > -void > -intel_update_renderbuffers(__DRIcontext *context, __DRIdrawable *drawable) > +static void > +intel_update_dri2_buffers(struct intel_context *intel, __DRIdrawable *drawable) > { > - struct gl_framebuffer *fb = drawable->driverPrivate; > - struct intel_renderbuffer *rb; > - struct intel_context *intel = context->driverPrivate; > __DRIbuffer *buffers = NULL; > int i, count; > const char *region_name; > + struct intel_renderbuffer *rb; > + struct gl_framebuffer *fb = drawable->driverPrivate; > + > + intel_query_dri2_buffers(intel, drawable, &buffers, &count); > + > + if (buffers == NULL) > + return; > + > + for (i = 0; i < count; i++) { > + switch (buffers[i].attachment) { > + case __DRI_BUFFER_FRONT_LEFT: > + rb = intel_get_renderbuffer(fb, BUFFER_FRONT_LEFT); > + region_name = "dri2 front buffer"; > + break; > + > + case __DRI_BUFFER_FAKE_FRONT_LEFT: > + rb = intel_get_renderbuffer(fb, BUFFER_FRONT_LEFT); > + region_name = "dri2 fake front buffer"; > + break; > + > + case __DRI_BUFFER_BACK_LEFT: > + rb = intel_get_renderbuffer(fb, BUFFER_BACK_LEFT); > + region_name = "dri2 back buffer"; > + break; > + > + case __DRI_BUFFER_DEPTH: > + case __DRI_BUFFER_HIZ: > + case __DRI_BUFFER_DEPTH_STENCIL: > + case __DRI_BUFFER_STENCIL: > + case __DRI_BUFFER_ACCUM: > + default: > + fprintf(stderr, > + "unhandled buffer attach event, attachment type %d\n", > + buffers[i].attachment); > + return; > + } > + > + intel_process_dri2_buffer(intel, drawable, &buffers[i], rb, region_name); > + } > +} > + > +void > +intel_update_renderbuffers(__DRIcontext *context, __DRIdrawable *drawable) > +{ > + struct intel_context *intel = context->driverPrivate; > + __DRIscreen *screen = intel->intelScreen->driScrnPriv; > > /* Set this up front, so that in case our buffers get invalidated > * while we're getting new buffers, we don't clobber the stamp and > @@ -151,42 +194,7 @@ intel_update_renderbuffers(__DRIcontext *context, __DRIdrawable *drawable) > if (unlikely(INTEL_DEBUG & DEBUG_DRI)) > fprintf(stderr, "enter %s, drawable %p\n", __func__, drawable); > > - intel_query_dri2_buffers(intel, drawable, &buffers, &count); > - > - if (buffers == NULL) > - return; > - > - for (i = 0; i < count; i++) { > - switch (buffers[i].attachment) { > - case __DRI_BUFFER_FRONT_LEFT: > - rb = intel_get_renderbuffer(fb, BUFFER_FRONT_LEFT); > - region_name = "dri2 front buffer"; > - break; > - > - case __DRI_BUFFER_FAKE_FRONT_LEFT: > - rb = intel_get_renderbuffer(fb, BUFFER_FRONT_LEFT); > - region_name = "dri2 fake front buffer"; > - break; > - > - case __DRI_BUFFER_BACK_LEFT: > - rb = intel_get_renderbuffer(fb, BUFFER_BACK_LEFT); > - region_name = "dri2 back buffer"; > - break; > - > - case __DRI_BUFFER_DEPTH: > - case __DRI_BUFFER_HIZ: > - case __DRI_BUFFER_DEPTH_STENCIL: > - case __DRI_BUFFER_STENCIL: > - case __DRI_BUFFER_ACCUM: > - default: > - fprintf(stderr, > - "unhandled buffer attach event, attachment type %d\n", > - buffers[i].attachment); > - return; > - } > - > - intel_process_dri2_buffer(intel, drawable, &buffers[i], rb, region_name); > - } > + intel_update_dri2_buffers(intel, drawable); > > driUpdateFramebufferSize(&intel->ctx, drawable); > } > diff --git a/src/mesa/drivers/dri/i965/brw_context.c b/src/mesa/drivers/dri/i965/brw_context.c > index 38147e9..c213b31 100644 > --- a/src/mesa/drivers/dri/i965/brw_context.c > +++ b/src/mesa/drivers/dri/i965/brw_context.c > @@ -929,12 +929,11 @@ intel_process_dri2_buffer(struct brw_context *brw, > struct intel_renderbuffer *rb, > const char *buffer_name); > > -void > -intel_update_renderbuffers(__DRIcontext *context, __DRIdrawable *drawable) > +static void > +intel_update_dri2_buffers(struct brw_context *brw, __DRIdrawable *drawable) > { > struct gl_framebuffer *fb = drawable->driverPrivate; > struct intel_renderbuffer *rb; > - struct brw_context *brw = context->driverPrivate; > __DRIbuffer *buffers = NULL; > int i, count; > const char *region_name; > @@ -984,6 +983,23 @@ intel_update_renderbuffers(__DRIcontext *context, __DRIdrawable *drawable) > intel_process_dri2_buffer(brw, drawable, &buffers[i], rb, region_name); > } > > +} > + > +void > +intel_update_renderbuffers(__DRIcontext *context, __DRIdrawable *drawable) > +{ > + struct brw_context *brw = context->driverPrivate; > + > + /* Set this up front, so that in case our buffers get invalidated > + * while we're getting new buffers, we don't clobber the stamp and > + * thus ignore the invalidate. */ > + drawable->lastStamp = drawable->dri2.stamp; > + > + if (unlikely(INTEL_DEBUG & DEBUG_DRI)) > + fprintf(stderr, "enter %s, drawable %p\n", __func__, drawable); > + > + intel_update_dri2_buffers(brw, drawable); > + > driUpdateFramebufferSize(&brw->ctx, drawable); > } > > -- > 1.8.4.2 > > > ------------------------------------------------------------------------------ > Android is increasing in popularity, but the open development platform that > developers love is also attractive to malware creators. Download this white > paper to learn more about secure code signing practices that can help keep > Android apps secure. > http://pubads.g.doubleclick.net/gampad/clk?id=65839951&iu=/4140/ostg.clktrk > -- > _______________________________________________ > Dri-devel mailing list > Dri...@li... > https://lists.sourceforge.net/lists/listinfo/dri-devel > _______________________________________________ > dri-devel mailing list > dri...@li... > http://lists.freedesktop.org/mailman/listinfo/dri-devel |
From: Kristian H. <hoe...@gm...> - 2013-11-01 17:48:25
|
On Thu, Oct 31, 2013 at 04:13:11PM -0700, Keith Packard wrote: > This just renames them so that they can be used with the DRI3 extension > without causing too much confusion. > > Signed-off-by: Keith Packard <ke...@ke...> > --- > src/mesa/drivers/dri/common/dri_util.c | 50 +++++++++++++++++----------------- > 1 file changed, 25 insertions(+), 25 deletions(-) Reviewed-by: Kristian Høgsberg <kr...@bi...> > diff --git a/src/mesa/drivers/dri/common/dri_util.c b/src/mesa/drivers/dri/common/dri_util.c > index c28b0fc..539fb4b 100644 > --- a/src/mesa/drivers/dri/common/dri_util.c > +++ b/src/mesa/drivers/dri/common/dri_util.c > @@ -291,13 +291,13 @@ validate_context_version(__DRIscreen *screen, > /*@{*/ > > static __DRIcontext * > -dri2CreateContextAttribs(__DRIscreen *screen, int api, > - const __DRIconfig *config, > - __DRIcontext *shared, > - unsigned num_attribs, > - const uint32_t *attribs, > - unsigned *error, > - void *data) > +driCreateContextAttribs(__DRIscreen *screen, int api, > + const __DRIconfig *config, > + __DRIcontext *shared, > + unsigned num_attribs, > + const uint32_t *attribs, > + unsigned *error, > + void *data) > { > __DRIcontext *context; > const struct gl_config *modes = (config != NULL) ? &config->modes : NULL; > @@ -442,22 +442,22 @@ dri2CreateContextAttribs(__DRIscreen *screen, int api, > } > > static __DRIcontext * > -dri2CreateNewContextForAPI(__DRIscreen *screen, int api, > - const __DRIconfig *config, > - __DRIcontext *shared, void *data) > +driCreateNewContextForAPI(__DRIscreen *screen, int api, > + const __DRIconfig *config, > + __DRIcontext *shared, void *data) > { > unsigned error; > > - return dri2CreateContextAttribs(screen, api, config, shared, 0, NULL, > - &error, data); > + return driCreateContextAttribs(screen, api, config, shared, 0, NULL, > + &error, data); > } > > static __DRIcontext * > -dri2CreateNewContext(__DRIscreen *screen, const __DRIconfig *config, > - __DRIcontext *shared, void *data) > +driCreateNewContext(__DRIscreen *screen, const __DRIconfig *config, > + __DRIcontext *shared, void *data) > { > - return dri2CreateNewContextForAPI(screen, __DRI_API_OPENGL, > - config, shared, data); > + return driCreateNewContextForAPI(screen, __DRI_API_OPENGL, > + config, shared, data); > } > > /** > @@ -609,9 +609,9 @@ static void dri_put_drawable(__DRIdrawable *pdp) > } > > static __DRIdrawable * > -dri2CreateNewDrawable(__DRIscreen *screen, > - const __DRIconfig *config, > - void *data) > +driCreateNewDrawable(__DRIscreen *screen, > + const __DRIconfig *config, > + void *data) > { > __DRIdrawable *pdraw; > > @@ -698,7 +698,7 @@ dri2ConfigQueryf(__DRIscreen *screen, const char *var, GLfloat *val) > } > > static unsigned int > -dri2GetAPIMask(__DRIscreen *screen) > +driGetAPIMask(__DRIscreen *screen) > { > return screen->api_mask; > } > @@ -741,13 +741,13 @@ const __DRIdri2Extension driDRI2Extension = { > .base = { __DRI_DRI2, 4 }, > > .createNewScreen = dri2CreateNewScreen, > - .createNewDrawable = dri2CreateNewDrawable, > - .createNewContext = dri2CreateNewContext, > - .getAPIMask = dri2GetAPIMask, > - .createNewContextForAPI = dri2CreateNewContextForAPI, > + .createNewDrawable = driCreateNewDrawable, > + .createNewContext = driCreateNewContext, > + .getAPIMask = driGetAPIMask, > + .createNewContextForAPI = driCreateNewContextForAPI, > .allocateBuffer = dri2AllocateBuffer, > .releaseBuffer = dri2ReleaseBuffer, > - .createContextAttribs = dri2CreateContextAttribs, > + .createContextAttribs = driCreateContextAttribs > .createNewScreen2 = dri2CreateNewScreen2, > }; > > -- > 1.8.4.2 > > > ------------------------------------------------------------------------------ > Android is increasing in popularity, but the open development platform that > developers love is also attractive to malware creators. Download this white > paper to learn more about secure code signing practices that can help keep > Android apps secure. > http://pubads.g.doubleclick.net/gampad/clk?id=65839951&iu=/4140/ostg.clktrk > -- > _______________________________________________ > Dri-devel mailing list > Dri...@li... > https://lists.sourceforge.net/lists/listinfo/dri-devel > _______________________________________________ > dri-devel mailing list > dri...@li... > http://lists.freedesktop.org/mailman/listinfo/dri-devel |
From: Keith P. <ke...@ke...> - 2013-10-31 23:31:19
|
This hooks DRI3 support into the GLX layer, the DRI common layer and the Intel driver. Signed-off-by: Keith Packard <ke...@ke...> --- configure.ac | 10 +- include/GL/internal/dri_interface.h | 158 +++ src/glx/Makefile.am | 2 + src/glx/dri3_common.c | 146 +++ src/glx/dri3_glx.c | 1539 +++++++++++++++++++++++++ src/glx/dri3_priv.h | 128 ++ src/glx/glxclient.h | 2 + src/glx/glxext.c | 6 +- src/mesa/drivers/dri/common/dri_util.c | 163 ++- src/mesa/drivers/dri/common/dri_util.h | 23 + src/mesa/drivers/dri/i915/intel_context.c | 109 +- src/mesa/drivers/dri/i915/intel_mipmap_tree.c | 33 + src/mesa/drivers/dri/i915/intel_mipmap_tree.h | 8 + src/mesa/drivers/dri/i965/brw_context.c | 110 +- src/mesa/drivers/dri/i965/intel_mipmap_tree.c | 61 + src/mesa/drivers/dri/i965/intel_mipmap_tree.h | 8 + src/mesa/drivers/dri/i965/intel_screen.c | 107 +- 17 files changed, 2594 insertions(+), 19 deletions(-) create mode 100644 src/glx/dri3_common.c create mode 100644 src/glx/dri3_glx.c create mode 100644 src/glx/dri3_priv.h diff --git a/configure.ac b/configure.ac index f94c9b9..b6158d9 100644 --- a/configure.ac +++ b/configure.ac @@ -38,6 +38,8 @@ LIBDRM_NVVIEUX_REQUIRED=2.4.33 LIBDRM_NOUVEAU_REQUIRED="2.4.33 libdrm >= 2.4.41" LIBDRM_FREEDRENO_REQUIRED=2.4.39 DRI2PROTO_REQUIRED=2.6 +DRI3PROTO_REQUIRED=1.0 +LIBUDEV_REQUIRED=151 GLPROTO_REQUIRED=1.4.14 LIBDRM_XORG_REQUIRED=2.4.24 LIBKMS_XORG_REQUIRED=1.0.0 @@ -820,10 +822,12 @@ xyesno) fi PKG_CHECK_MODULES([DRI2PROTO], [dri2proto >= $DRI2PROTO_REQUIRED]) GL_PC_REQ_PRIV="$GL_PC_REQ_PRIV libdrm >= $LIBDRM_REQUIRED" + PKG_CHECK_MODULES([DRI3PROTO], [dri3proto >= $DRI3PROTO_REQUIRED]) + PKG_CHECK_MODULES([LIBUDEV], [libudev >= $LIBUDEV_REQUIRED]) fi # find the DRI deps for libGL - dri_modules="x11 xext xdamage xfixes x11-xcb xcb-glx >= 1.8.1 xcb-dri2 >= 1.8" + dri_modules="x11 xext xdamage xfixes x11-xcb xcb-glx >= 1.8.1 xcb-dri2 >= 1.8 xcb-dri3 xcb-sync xshmfence" # add xf86vidmode if available PKG_CHECK_MODULES([XF86VIDMODE], [xxf86vm], HAVE_XF86VIDMODE=yes, HAVE_XF86VIDMODE=no) @@ -833,8 +837,8 @@ xyesno) PKG_CHECK_MODULES([DRIGL], [$dri_modules]) GL_PC_REQ_PRIV="$GL_PC_REQ_PRIV $dri_modules" - X11_INCLUDES="$X11_INCLUDES $DRIGL_CFLAGS" - GL_LIB_DEPS="$DRIGL_LIBS" + X11_INCLUDES="$X11_INCLUDES $DRIGL_CFLAGS $LIBUDEV_CFLAGS" + GL_LIB_DEPS="$DRIGL_LIBS $LIBUDEV_LIBS" # need DRM libs, $PTHREAD_LIBS, etc. GL_LIB_DEPS="$GL_LIB_DEPS $LIBDRM_LIBS -lm $PTHREAD_LIBS $DLOPEN_LIBS" diff --git a/include/GL/internal/dri_interface.h b/include/GL/internal/dri_interface.h index 48993b9..b06ad8d 100644 --- a/include/GL/internal/dri_interface.h +++ b/include/GL/internal/dri_interface.h @@ -86,6 +86,11 @@ typedef struct __DRIdri2LoaderExtensionRec __DRIdri2LoaderExtension; typedef struct __DRI2flushExtensionRec __DRI2flushExtension; typedef struct __DRI2throttleExtensionRec __DRI2throttleExtension; + +typedef struct __DRIdri3BufferRec __DRIdri3Buffer; +typedef struct __DRIdri3ExtensionRec __DRIdri3Extension; +typedef struct __DRIdri3LoaderExtensionRec __DRIdri3LoaderExtension; + /*@}*/ @@ -966,6 +971,159 @@ struct __DRIdri2ExtensionRec { /** + * DRI3 Loader extension. + */ + +#define __DRI3_DRIVER_EXTENSIONS "__dri3DriverExtensions" + +enum __DRI3bufferType { + __DRI3_BUFFER_BACK = 0, + __DRI3_BUFFER_FRONT = 1 +}; + +struct __DRIdri3BufferRec { + unsigned int size; + unsigned int pitch; + unsigned int cpp; + unsigned int flags; + unsigned int width, height; + enum __DRI3bufferType buffer_type; + uint32_t pixmap; + uint32_t sync_fence; + int32_t *shm_fence; + void *driverPrivate; +}; + +#define __DRI_DRI3_LOADER "DRI_DRI3Loader" +#define __DRI_DRI3_LOADER_VERSION 1 + +struct __DRIdri3LoaderExtensionRec { + __DRIextension base; + + int (*getBuffers)(__DRIdrawable *driDrawable, + int *width, int *height, + unsigned int format, + void *loaderPrivate, + int need_front, + int need_back, + __DRIdri3Buffer **front, + __DRIdri3Buffer **back); + + /** + * Flush pending front-buffer rendering + * + * Any rendering that has been performed to the + * \c __DRI3_BUFFER_FAKE_FRONT_LEFT will be flushed to the + * \c __DRI3_BUFFER_FRONT_LEFT. + * + * \param driDrawable Drawable whose front-buffer is to be flushed + * \param loaderPrivate Loader's private data that was previously passed + * into __DRIdri2ExtensionRec::createNewDrawable + */ + void (*flushFrontBuffer)(__DRIdrawable *driDrawable, void *loaderPrivate); +}; + +/** + * DRI3 extension. + */ + +struct gl_context; +struct dd_function_table; + +typedef __DRIscreen * +(*__DRIcreateNewScreen2)(int screen, int fd, + const __DRIextension **extensions, + const __DRIextension **driver_extensions, + const __DRIconfig ***driver_configs, + void *loaderPrivate); + +typedef __DRIdrawable * +(*__DRIcreateNewDrawable)(__DRIscreen *screen, + const __DRIconfig *config, + void *loaderPrivate); + +typedef __DRIcontext * +(*__DRIcreateNewContext)(__DRIscreen *screen, + const __DRIconfig *config, + __DRIcontext *shared, + void *loaderPrivate); + +typedef __DRIcontext * +(*__DRIcreateContextAttribs)(__DRIscreen *screen, + int api, + const __DRIconfig *config, + __DRIcontext *shared, + unsigned num_attribs, + const uint32_t *attribs, + unsigned *error, + void *loaderPrivate); + +typedef unsigned int +(*__DRIgetAPIMask)(__DRIscreen *screen); + +typedef __DRIdri3Buffer * +(*__DRIdri3AllocateBuffer)(__DRIscreen *screen, + unsigned int bpp, + int width, + int height); + +typedef void +(*__DRIdri3ReleaseBuffer)(__DRIscreen *screen, + __DRIdri3Buffer *buffer); + +typedef int +(*__DRIdri3BufferToFd)(__DRIscreen *screen, + __DRIdri3Buffer *buffer); + +typedef __DRIdri3Buffer * +(*__DRIdri3FdToBuffer)(__DRIscreen *screen, + int fd, unsigned int bpp, + int width, int height, int stride, + unsigned int size); + +typedef uint32_t * +(*__DRIdri3Stamp)(__DRIdrawable *drawable); + +#define __DRI_DRI3 "DRI_DRI3" +#define __DRI_DRI3_VERSION 1 + +struct __DRIdri3ExtensionRec { + __DRIextension base; + + /* Common DRI functions, shared with DRI2 */ + __DRIcreateNewScreen2 createNewScreen2; + __DRIcreateNewDrawable createNewDrawable; + __DRIcreateNewContext createNewContext; + __DRIcreateContextAttribs createContextAttribs; + __DRIgetAPIMask getAPIMask; + + /* DRI3-specific functions*/ + + /* Allocate color buffer for front/back of windows and pixmaps + */ + __DRIdri3AllocateBuffer allocateBuffer; + + /* Release color buffer + */ + __DRIdri3ReleaseBuffer releaseBuffer; + + /* Given a buffer, wrap it in a DMA-BUF file descriptor + */ + __DRIdri3BufferToFd bufferToFd; + + /* Given a DMA-BUF file descriptor, construct a suitable + * color buffer + */ + __DRIdri3FdToBuffer fdToBuffer; + + /* Ask the driver for a pointer to an integer which + * can be incremented when the drawable needs to be + * revalidated + */ + __DRIdri3Stamp stamp; +}; + +/** * This extension provides functionality to enable various EGLImage * extensions. */ diff --git a/src/glx/Makefile.am b/src/glx/Makefile.am index f01709b..854025d 100644 --- a/src/glx/Makefile.am +++ b/src/glx/Makefile.am @@ -92,6 +92,8 @@ libglx_la_SOURCES = \ glxhash.c \ dri2_glx.c \ dri2.c \ + dri3_glx.c \ + dri3_common.c \ applegl_glx.c GL_LIBS = \ diff --git a/src/glx/dri3_common.c b/src/glx/dri3_common.c new file mode 100644 index 0000000..c758f96 --- /dev/null +++ b/src/glx/dri3_common.c @@ -0,0 +1,146 @@ +/* + * Copyright © 2013 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * This code is derived from src/egl/drivers/dri2/common.c which + * carries the following copyright: + * + * Copyright © 2011 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Kristian Høgsberg <kr...@bi...> + * Benjamin Franzke <ben...@go...> + */ + +#include <stdio.h> +#include <string.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <GL/gl.h> +#include "glapi.h" +#include "glxclient.h" +#include "xf86dri.h" +#include <dlfcn.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/time.h> +#include "xf86drm.h" +#include "dri_common.h" +#include "dri3_priv.h" + +#define DRIVER_MAP_DRI3_ONLY +#include "pci_ids/pci_id_driver_map.h" + +#include <libudev.h> + +static struct udev_device * +dri3_udev_device_new_from_fd(struct udev *udev, int fd) +{ + struct udev_device *device; + struct stat buf; + + if (fstat(fd, &buf) < 0) { + ErrorMessageF("DRI3: failed to stat fd %d", fd); + return NULL; + } + + device = udev_device_new_from_devnum(udev, 'c', buf.st_rdev); + if (device == NULL) { + ErrorMessageF("DRI3: could not create udev device for fd %d", fd); + return NULL; + } + + return device; +} + +char * +dri3_get_driver_for_fd(int fd) +{ + struct udev *udev; + struct udev_device *device, *parent; + const char *pci_id; + char *driver = NULL; + int vendor_id, chip_id, i, j; + + udev = udev_new(); + device = dri3_udev_device_new_from_fd(udev, fd); + if (device == NULL) + return NULL; + + parent = udev_device_get_parent(device); + if (parent == NULL) { + ErrorMessageF("DRI3: could not get parent device"); + goto out; + } + + pci_id = udev_device_get_property_value(parent, "PCI_ID"); + if (pci_id == NULL || + sscanf(pci_id, "%x:%x", &vendor_id, &chip_id) != 2) { + ErrorMessageF("DRI3: malformed or no PCI ID"); + goto out; + } + + for (i = 0; driver_map[i].driver; i++) { + if (vendor_id != driver_map[i].vendor_id) + continue; + if (driver_map[i].num_chips_ids == -1) { + driver = strdup(driver_map[i].driver); + goto out; + } + + for (j = 0; j < driver_map[i].num_chips_ids; j++) + if (driver_map[i].chip_ids[j] == chip_id) { + driver = strdup(driver_map[i].driver); + goto out; + } + } + +out: + udev_device_unref(device); + udev_unref(udev); + + return driver; +} diff --git a/src/glx/dri3_glx.c b/src/glx/dri3_glx.c new file mode 100644 index 0000000..4d275f2 --- /dev/null +++ b/src/glx/dri3_glx.c @@ -0,0 +1,1539 @@ +/* + * Copyright © 2013 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * Portions of this code were adapted from dri2_glx.c which carries the + * following copyright: + * + * Copyright © 2008 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Soft- + * ware"), to deal in the Software without restriction, including without + * limitation the rights to use, copy, modify, merge, publish, distribute, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, provided that the above copyright + * notice(s) and this permission notice appear in all copies of the Soft- + * ware and that both the above copyright notice(s) and this permission + * notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- + * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY + * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN + * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE- + * QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR- + * MANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder shall + * not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization of + * the copyright holder. + * + * Authors: + * Kristian Høgsberg (kr...@re...) + */ + +#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL) + +#include <X11/Xlib.h> +#include <X11/extensions/Xfixes.h> +#include <X11/Xlib-xcb.h> +#include <X11/xshmfence.h> +#include <xcb/xcb.h> +#include <xcb/dri3.h> +#include <xcb/present.h> +#include <GL/gl.h> +#include "glapi.h" +#include "glxclient.h" +#include "xf86dri.h" +#include <dlfcn.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/time.h> +#include "xf86drm.h" +#include "dri_common.h" +#include "dri3_priv.h" + +static const struct glx_context_vtable dri3_context_vtable; + +#define HAS_SBC 0 + +#if HAS_SBC + +/* For XCB's handling of ust/msc/sbc counters, we have to hand it the high and + * low halves separately. This helps you split them. + */ +static void +split_counter(uint64_t counter, uint32_t *hi, uint32_t *lo) +{ + *hi = (counter >> 32); + *lo = counter & 0xffffffff; +} + +static uint64_t +merge_counter(uint32_t hi, uint32_t lo) +{ + return ((uint64_t)hi << 32) | lo; +} +#endif /* HAS_SBC */ + +static inline void +dri3_fence_reset(xcb_connection_t *c, __DRIdri3Buffer *buffer) { + xshmfence_reset(buffer->shm_fence); +} + +static inline void +dri3_fence_trigger(xcb_connection_t *c, __DRIdri3Buffer *buffer) { + xcb_sync_trigger_fence(c, buffer->sync_fence); +} + +static inline void +dri3_fence_await(xcb_connection_t *c, __DRIdri3Buffer *buffer) { + xcb_flush(c); + xshmfence_await(buffer->shm_fence); +} + +static void +dri3_destroy_context(struct glx_context *context) +{ + struct dri3_context *pcp = (struct dri3_context *) context; + struct dri3_screen *psc = (struct dri3_screen *) context->psc; + + driReleaseDrawables(&pcp->base); + + free((char *) context->extensions); + + (*psc->core->destroyContext) (pcp->driContext); + + free(pcp); +} + +static Bool +dri3_bind_context(struct glx_context *context, struct glx_context *old, + GLXDrawable draw, GLXDrawable read) +{ + struct dri3_context *pcp = (struct dri3_context *) context; + struct dri3_screen *psc = (struct dri3_screen *) pcp->base.psc; + struct dri3_drawable *pdraw, *pread; + + pdraw = (struct dri3_drawable *) driFetchDrawable(context, draw); + pread = (struct dri3_drawable *) driFetchDrawable(context, read); + + driReleaseDrawables(&pcp->base); + + if (pdraw == NULL || pread == NULL) + return GLXBadDrawable; + + if (!(*psc->core->bindContext) (pcp->driContext, + pdraw->driDrawable, pread->driDrawable)) + return GLXBadContext; + + return Success; +} + +static void +dri3_unbind_context(struct glx_context *context, struct glx_context *new) +{ + struct dri3_context *pcp = (struct dri3_context *) context; + struct dri3_screen *psc = (struct dri3_screen *) pcp->base.psc; + + (*psc->core->unbindContext) (pcp->driContext); +} + +static struct glx_context * +dri3_create_context(struct glx_screen *base, + struct glx_config *config_base, + struct glx_context *shareList, int renderType) +{ + struct dri3_context *pcp, *pcp_shared; + struct dri3_screen *psc = (struct dri3_screen *) base; + __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base; + __DRIcontext *shared = NULL; + + if (shareList) { + /* If the shareList context is not a DRI3 context, we cannot possibly + * create a DRI3 context that shares it. + */ + if (shareList->vtable->destroy != dri3_destroy_context) { + return NULL; + } + + pcp_shared = (struct dri3_context *) shareList; + shared = pcp_shared->driContext; + } + + pcp = calloc(1, sizeof *pcp); + if (pcp == NULL) + return NULL; + + if (!glx_context_init(&pcp->base, &psc->base, &config->base)) { + free(pcp); + return NULL; + } + + pcp->driContext = + (*psc->dri3->createNewContext) (psc->driScreen, + config->driConfig, shared, pcp); + + if (pcp->driContext == NULL) { + free(pcp); + return NULL; + } + + pcp->base.vtable = &dri3_context_vtable; + + return &pcp->base; +} + +static struct glx_context * +dri3_create_context_attribs(struct glx_screen *base, + struct glx_config *config_base, + struct glx_context *shareList, + unsigned num_attribs, + const uint32_t *attribs, + unsigned *error) +{ + struct dri3_context *pcp = NULL; + struct dri3_context *pcp_shared = NULL; + struct dri3_screen *psc = (struct dri3_screen *) base; + __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base; + __DRIcontext *shared = NULL; + + uint32_t minor_ver = 1; + uint32_t major_ver = 2; + uint32_t flags = 0; + unsigned api; + int reset = __DRI_CTX_RESET_NO_NOTIFICATION; + uint32_t ctx_attribs[2 * 5]; + unsigned num_ctx_attribs = 0; + uint32_t render_type; + + /* Remap the GLX tokens to DRI2 tokens. + */ + if (!dri2_convert_glx_attribs(num_attribs, attribs, + &major_ver, &minor_ver, + &render_type, &flags, &api, + &reset, error)) + goto error_exit; + + /* Check the renderType value */ + if (!validate_renderType_against_config(config_base, render_type)) + goto error_exit; + + if (shareList) { + pcp_shared = (struct dri3_context *) shareList; + shared = pcp_shared->driContext; + } + + pcp = calloc(1, sizeof *pcp); + if (pcp == NULL) { + *error = __DRI_CTX_ERROR_NO_MEMORY; + goto error_exit; + } + + if (!glx_context_init(&pcp->base, &psc->base, &config->base)) + goto error_exit; + + ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_MAJOR_VERSION; + ctx_attribs[num_ctx_attribs++] = major_ver; + ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_MINOR_VERSION; + ctx_attribs[num_ctx_attribs++] = minor_ver; + + /* Only send a value when the non-default value is requested. By doing + * this we don't have to check the driver's DRI3 version before sending the + * default value. + */ + if (reset != __DRI_CTX_RESET_NO_NOTIFICATION) { + ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_RESET_STRATEGY; + ctx_attribs[num_ctx_attribs++] = reset; + } + + if (flags != 0) { + ctx_attribs[num_ctx_attribs++] = __DRI_CTX_ATTRIB_FLAGS; + + /* The current __DRI_CTX_FLAG_* values are identical to the + * GLX_CONTEXT_*_BIT values. + */ + ctx_attribs[num_ctx_attribs++] = flags; + } + + pcp->driContext = + (*psc->dri3->createContextAttribs) (psc->driScreen, + api, + config->driConfig, + shared, + num_ctx_attribs / 2, + ctx_attribs, + error, + pcp); + + if (pcp->driContext == NULL) + goto error_exit; + + pcp->base.vtable = &dri3_context_vtable; + + return &pcp->base; + +error_exit: + free(pcp); + + return NULL; +} + +static void +dri3_destroy_drawable(__GLXDRIdrawable *base) +{ + struct dri3_screen *psc = (struct dri3_screen *) base->psc; + struct dri3_drawable *pdraw = (struct dri3_drawable *) base; + + (*psc->core->destroyDrawable) (pdraw->driDrawable); + + free(pdraw); +} + +static __GLXDRIdrawable * +dri3_create_drawable(struct glx_screen *base, XID xDrawable, + GLXDrawable drawable, struct glx_config *config_base) +{ + struct dri3_drawable *pdraw; + struct dri3_screen *psc = (struct dri3_screen *) base; + __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base; + GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1; + + pdraw = calloc(1, sizeof(*pdraw)); + if (!pdraw) + return NULL; + + pdraw->base.destroyDrawable = dri3_destroy_drawable; + pdraw->base.xDrawable = xDrawable; + pdraw->base.drawable = drawable; + pdraw->base.psc = &psc->base; +// pdraw->bufferCount = 0; + pdraw->swap_interval = 1; /* default may be overridden below */ + pdraw->have_back = 0; + pdraw->have_fake_front = 0; + + if (psc->config) + psc->config->configQueryi(psc->driScreen, + "vblank_mode", &vblank_mode); + + switch (vblank_mode) { + case DRI_CONF_VBLANK_NEVER: + case DRI_CONF_VBLANK_DEF_INTERVAL_0: + pdraw->swap_interval = 0; + break; + case DRI_CONF_VBLANK_DEF_INTERVAL_1: + case DRI_CONF_VBLANK_ALWAYS_SYNC: + default: + pdraw->swap_interval = 1; + break; + } + + (void) __glXInitialize(psc->base.dpy); + + /* Create a new drawable */ + pdraw->driDrawable = + (*psc->dri3->createNewDrawable) (psc->driScreen, + config->driConfig, pdraw); + + if (!pdraw->driDrawable) { + free(pdraw); + return NULL; + } + + /* + * Make sure server has the same swap interval we do for the new + * drawable. + */ + if (psc->vtable.setSwapInterval) + psc->vtable.setSwapInterval(&pdraw->base, pdraw->swap_interval); + + return &pdraw->base; +} + +#if HAS_SBC +static int +dri3_drawable_get_msc(struct glx_screen *psc, __GLXDRIdrawable *pdraw, + int64_t *ust, int64_t *msc, int64_t *sbc) +{ + xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy); + xcb_dri2_get_msc_cookie_t get_msc_cookie; + xcb_dri2_get_msc_reply_t *get_msc_reply; + + get_msc_cookie = xcb_dri2_get_msc_unchecked(c, pdraw->xDrawable); + get_msc_reply = xcb_dri2_get_msc_reply(c, get_msc_cookie, NULL); + + if (!get_msc_reply) + return 0; + + *ust = merge_counter(get_msc_reply->ust_hi, get_msc_reply->ust_lo); + *msc = merge_counter(get_msc_reply->msc_hi, get_msc_reply->msc_lo); + *sbc = merge_counter(get_msc_reply->sbc_hi, get_msc_reply->sbc_lo); + free(get_msc_reply); + + return 1; +} + +static int +dri3_wait_for_msc(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor, + int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc) +{ + xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy); + xcb_dri2_wait_msc_cookie_t wait_msc_cookie; + xcb_dri2_wait_msc_reply_t *wait_msc_reply; + uint32_t target_msc_hi, target_msc_lo; + uint32_t divisor_hi, divisor_lo; + uint32_t remainder_hi, remainder_lo; + + split_counter(target_msc, &target_msc_hi, &target_msc_lo); + split_counter(divisor, &divisor_hi, &divisor_lo); + split_counter(remainder, &remainder_hi, &remainder_lo); + + wait_msc_cookie = xcb_dri2_wait_msc_unchecked(c, pdraw->xDrawable, + target_msc_hi, target_msc_lo, + divisor_hi, divisor_lo, + remainder_hi, remainder_lo); + wait_msc_reply = xcb_dri2_wait_msc_reply(c, wait_msc_cookie, NULL); + + if (!wait_msc_reply) + return 0; + + *ust = merge_counter(wait_msc_reply->ust_hi, wait_msc_reply->ust_lo); + *msc = merge_counter(wait_msc_reply->msc_hi, wait_msc_reply->msc_lo); + *sbc = merge_counter(wait_msc_reply->sbc_hi, wait_msc_reply->sbc_lo); + free(wait_msc_reply); + + return 1; +} + +static int +dri3_wait_for_sbc(__GLXDRIdrawable *pdraw, int64_t target_sbc, int64_t *ust, + int64_t *msc, int64_t *sbc) +{ + xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy); + xcb_dri2_wait_sbc_cookie_t wait_sbc_cookie; + xcb_dri2_wait_sbc_reply_t *wait_sbc_reply; + uint32_t target_sbc_hi, target_sbc_lo; + + split_counter(target_sbc, &target_sbc_hi, &target_sbc_lo); + + wait_sbc_cookie = xcb_dri2_wait_sbc_unchecked(c, pdraw->xDrawable, + target_sbc_hi, target_sbc_lo); + wait_sbc_reply = xcb_dri2_wait_sbc_reply(c, wait_sbc_cookie, NULL); + + if (!wait_sbc_reply) + return 0; + + *ust = merge_counter(wait_sbc_reply->ust_hi, wait_sbc_reply->ust_lo); + *msc = merge_counter(wait_sbc_reply->msc_hi, wait_sbc_reply->msc_lo); + *sbc = merge_counter(wait_sbc_reply->sbc_hi, wait_sbc_reply->sbc_lo); + free(wait_sbc_reply); + + return 1; +} +#endif /* HAS_SBC */ + +static __DRIcontext * +dri3_get_current_context(void) +{ + struct glx_context *gc = __glXGetCurrentContext(); + struct dri3_context *dri3Ctx = (struct dri3_context *)gc; + + return dri3Ctx ? dri3Ctx->driContext : NULL; +} + +/** + * dri3Throttle - Request driver throttling + * + * This function uses the DRI2 throttle extension to give the + * driver the opportunity to throttle on flush front, copysubbuffer + * and swapbuffers. + */ +static void +dri3_throttle(struct dri3_screen *psc, + struct dri3_drawable *draw, + enum __DRI2throttleReason reason) +{ + if (psc->throttle) { + __DRIcontext *ctx = dri3_get_current_context(); + + psc->throttle->throttle(ctx, draw->driDrawable, reason); + } +} + +/** + * Asks the driver to flush any queued work necessary for serializing with the + * X command stream, and optionally the slightly more strict requirement of + * glFlush() equivalence (which would require flushing even if nothing had + * been drawn to a window system framebuffer, for example). + */ +static void +dri3_flush(struct dri3_screen *psc, + __DRIcontext *ctx, + struct dri3_drawable *draw, + unsigned flags, + enum __DRI2throttleReason throttle_reason) +{ + if (ctx && psc->f && psc->f->base.version >= 4) { + psc->f->flush_with_flags(ctx, draw->driDrawable, flags, throttle_reason); + } else { + if (flags & __DRI2_FLUSH_CONTEXT) + glFlush(); + + if (psc->f) + psc->f->flush(draw->driDrawable); + + dri3_throttle(psc, draw, throttle_reason); + } +} + +static xcb_gcontext_t +dri3_drawable_gc(struct dri3_drawable *priv) +{ + if (!priv->gc) { + uint32_t v; + xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy); + + v = 0; + xcb_create_gc(c, + (priv->gc = xcb_generate_id(c)), + priv->base.xDrawable, + XCB_GC_GRAPHICS_EXPOSURES, + &v); + } + return priv->gc; +} + +static __DRIdri3Buffer * +dri3_back_buffer(struct dri3_drawable *priv) +{ + return priv->buffers[__DRI3_BUFFER_BACK]; +} + +static __DRIdri3Buffer * +dri3_fake_front_buffer(struct dri3_drawable *priv) +{ + return priv->buffers[__DRI3_BUFFER_FRONT]; +} + +static void +dri3_copy_area (xcb_connection_t *c /**< */, + xcb_drawable_t src_drawable /**< */, + xcb_drawable_t dst_drawable /**< */, + xcb_gcontext_t gc /**< */, + int16_t src_x /**< */, + int16_t src_y /**< */, + int16_t dst_x /**< */, + int16_t dst_y /**< */, + uint16_t width /**< */, + uint16_t height /**< */) +{ + xcb_void_cookie_t cookie; + + cookie = xcb_copy_area_checked(c, + src_drawable, + dst_drawable, + gc, + src_x, + src_y, + dst_x, + dst_y, + width, + height); + xcb_discard_reply(c, cookie.sequence); +} + +static void +_dri3_copy_sub_buffer(__GLXDRIdrawable *pdraw, int x, int y, + int width, int height, + enum __DRI2throttleReason reason, Bool flush) +{ + struct dri3_drawable *priv = (struct dri3_drawable *) pdraw; + struct dri3_screen *psc = (struct dri3_screen *) pdraw->psc; + xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy); + __DRIcontext *ctx = dri3_get_current_context(); + __DRIdri3Buffer *back = dri3_back_buffer(priv); + + unsigned flags; + + /* Check we have the right attachments */ + if (!priv->have_back || priv->is_pixmap) + return; + + flags = __DRI2_FLUSH_DRAWABLE; + if (flush) + flags |= __DRI2_FLUSH_CONTEXT; + dri3_flush(psc, ctx, priv, flags, __DRI2_THROTTLE_SWAPBUFFER); + + y = priv->height - y - height; + + dri3_fence_reset(c, back); + dri3_copy_area(c, + dri3_back_buffer(priv)->pixmap, + priv->base.xDrawable, + dri3_drawable_gc(priv), + x, y, x, y, width, height); + dri3_fence_trigger(c, back); + /* Refresh the fake front (if present) after we just damaged the real + * front. + */ + if (priv->have_fake_front) { + dri3_fence_reset(c, dri3_fake_front_buffer(priv)); + dri3_copy_area(c, + dri3_back_buffer(priv)->pixmap, + dri3_fake_front_buffer(priv)->pixmap, + dri3_drawable_gc(priv), + x, y, x, y, width, height); + dri3_fence_trigger(c, dri3_fake_front_buffer(priv)); + dri3_fence_await(c, dri3_fake_front_buffer(priv)); + } + dri3_fence_await(c, back); +} + +static void +dri3_copy_sub_buffer(__GLXDRIdrawable *pdraw, int x, int y, + int width, int height, Bool flush) +{ + _dri3_copy_sub_buffer(pdraw, x, y, width, height, + __DRI2_THROTTLE_COPYSUBBUFFER, flush); +} + + +static void +dri3_copy_drawable(struct dri3_drawable *priv, Drawable dest, Drawable src) +{ + struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc; + xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy); + + if (psc->f) + (*psc->f->flush) (priv->driDrawable); + + dri3_copy_area(c, + src, dest, + dri3_drawable_gc(priv), + 0, 0, 0, 0, priv->width, priv->height); +} + +static void +dri3_wait_x(struct glx_context *gc) +{ + struct dri3_drawable *priv = (struct dri3_drawable *) + GetGLXDRIDrawable(gc->currentDpy, gc->currentDrawable); + + if (priv == NULL || !priv->have_fake_front) + return; + + dri3_copy_drawable(priv, dri3_fake_front_buffer(priv)->pixmap, priv->base.xDrawable); +} + +static void +dri3_wait_gl(struct glx_context *gc) +{ + struct dri3_drawable *priv = (struct dri3_drawable *) + GetGLXDRIDrawable(gc->currentDpy, gc->currentDrawable); + + if (priv == NULL || !priv->have_fake_front) + return; + + dri3_copy_drawable(priv, priv->base.xDrawable, dri3_fake_front_buffer(priv)->pixmap); +} + +/** + * Called by the driver when it needs to update the real front buffer with the + * contents of its fake front buffer. + */ +static void +dri3_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate) +{ + struct glx_context *gc; + struct dri3_drawable *pdraw = loaderPrivate; + struct dri3_screen *psc; + + if (!pdraw) + return; + + if (!pdraw->base.psc) + return; + + psc = (struct dri3_screen *) pdraw->base.psc; + + (void) __glXInitialize(psc->base.dpy); + + gc = __glXGetCurrentContext(); + + dri3_throttle(psc, pdraw, __DRI2_THROTTLE_FLUSHFRONT); + + dri3_wait_gl(gc); +} + +static __DRIdri3Buffer * +dri3_alloc_render_buffer(struct glx_screen *glx_screen, Drawable draw, unsigned int format, int width, int height, int depth) +{ + struct dri3_screen *psc = (struct dri3_screen *) glx_screen; + Display *dpy = glx_screen->dpy; + __DRIdri3Buffer *buffer; + xcb_connection_t *c = XGetXCBConnection(dpy); + xcb_pixmap_t pixmap; + xcb_sync_fence_t sync_fence; + int32_t *shm_fence; + int buffer_fd, fence_fd; + + fence_fd = xshmfence_alloc_shm(); + if (fence_fd < 0) + return NULL; + shm_fence = xshmfence_map_shm(fence_fd); + if (shm_fence == NULL) + goto no_shm_fence; + + buffer = (*psc->dri3->allocateBuffer) (psc->driScreen, + format, + width, + height); + if (buffer == NULL) + goto no_buffer; + + buffer_fd = (*psc->dri3->bufferToFd)(psc->driScreen, buffer); + if (buffer_fd < 0) + goto no_buffer_fd; + + xcb_dri3_pixmap_from_buffer(c, + (pixmap = xcb_generate_id(c)), + draw, + buffer->size, + width, height, buffer->pitch, + depth, buffer->cpp * 8, + buffer_fd); + + xcb_dri3_fence_from_fd(c, + pixmap, + (sync_fence = xcb_generate_id(c)), + false, + fence_fd); + + buffer->pixmap = pixmap; + buffer->sync_fence = sync_fence; + buffer->shm_fence = shm_fence; + buffer->width = width; + buffer->height = height; + return buffer; + +no_buffer_fd: + (*psc->dri3->releaseBuffer)(psc->driScreen, buffer); +no_buffer: + xshmfence_unmap_shm(shm_fence); +no_shm_fence: + close(fence_fd); + return NULL; +} + +static void +dri3_free_render_buffer(struct dri3_drawable *pdraw, __DRIdri3Buffer *buffer) +{ + struct dri3_screen *psc = (struct dri3_screen *) pdraw->base.psc; + xcb_connection_t *c = XGetXCBConnection(pdraw->base.psc->dpy); + + xcb_free_pixmap(c, buffer->pixmap); + xcb_sync_destroy_fence(c, buffer->sync_fence); + xshmfence_unmap_shm(buffer->shm_fence); + (*psc->dri3->releaseBuffer)(psc->driScreen, buffer); +} + +static int +dri3_update_drawable(__DRIdrawable *driDrawable, void *loaderPrivate) +{ + struct dri3_drawable *priv = loaderPrivate; + xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy); + + /* First time through, go get the current drawable geometry + */ + if (priv->width == 0 || priv->height == 0 || priv->depth == 0) { + xcb_get_geometry_cookie_t geom_cookie; + xcb_get_geometry_reply_t *geom_reply; + struct dri3_screen *psc; + xcb_void_cookie_t cookie; + xcb_generic_error_t *error; + + cookie = xcb_present_select_input_checked(c, + (priv->eid = xcb_generate_id(c)), + priv->base.xDrawable, + XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY| + XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY); + + if (!priv->present_extension) { + priv->present_extension = xcb_get_extension_data(c, &xcb_present_id); + if (!priv->present_extension) + return false; + } + + psc = (struct dri3_screen *) priv->base.psc; + priv->special_event = xcb_register_for_special_event(c, + priv->present_extension->major_opcode, + priv->eid, + psc->dri3->stamp(driDrawable)); + + geom_cookie = xcb_get_geometry(c, priv->base.xDrawable); + + geom_reply = xcb_get_geometry_reply(c, geom_cookie, NULL); + + if (!geom_reply) + return false; + + priv->width = geom_reply->width; + priv->height = geom_reply->height; + priv->depth = geom_reply->depth; + priv->is_pixmap = false; + + free(geom_reply); + + error = xcb_request_check(c, cookie); + + if (error) { + if (error->error_code != BadWindow) { + free(error); + return false; + } + priv->is_pixmap = true; + xcb_unregister_for_special_event(c, priv->special_event); + priv->special_event = NULL; + } + } + + /* Check to see if any configuration changes have occurred + * since we were last invoked + */ + if (priv->special_event) { + xcb_generic_event_t *ev; + + while ((ev = xcb_check_for_special_event(c, priv->special_event)) != NULL) { + xcb_present_generic_event_t *pe = (void *) ev; + + switch (pe->evtype) { + case XCB_PRESENT_EVENT_CONFIGURE_NOTIFY: { + xcb_configure_notify_event_t *ce = (void *) ev; + + priv->width = ce->width; + priv->height = ce->height; + break; + } + case XCB_PRESENT_EVENT_IDLE_NOTIFY: + break; + } + free(ev); + } + } + return true; +} + +static __DRIdri3Buffer * +dri3_get_pixmap_buffer(__DRIdrawable *driDrawable, + unsigned int format, + enum __DRI3bufferType buffer_type, + void *loaderPrivate) +{ + struct dri3_drawable *pdraw = loaderPrivate; + __DRIdri3Buffer *buffer = pdraw->buffers[buffer_type]; + Pixmap pixmap; + xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie; + xcb_dri3_buffer_from_pixmap_reply_t *bp_reply; + int *fds; + int buffer_fd; + Display *dpy; + struct dri3_screen *psc; + xcb_connection_t *c; + xcb_sync_fence_t sync_fence; + int32_t *shm_fence; + int fence_fd; + + if (buffer) + return buffer; + + pixmap = pdraw->base.xDrawable; + psc = (struct dri3_screen *) pdraw->base.psc; + dpy = psc->base.dpy; + c = XGetXCBConnection(dpy); + + fence_fd = xshmfence_alloc_shm(); + if (fence_fd < 0) + return NULL; + shm_fence = xshmfence_map_shm(fence_fd); + if (shm_fence == NULL) { + close (fence_fd); + return NULL; + } + + xcb_dri3_fence_from_fd(c, + pixmap, + (sync_fence = xcb_generate_id(c)), + false, + fence_fd); + + bp_cookie = xcb_dri3_buffer_from_pixmap(c, pixmap); + bp_reply = xcb_dri3_buffer_from_pixmap_reply(c, bp_cookie, NULL); + if (!bp_reply) + goto no_pixmap; + fds = xcb_dri3_buffer_from_pixmap_reply_fds(c, bp_reply); + buffer_fd = fds[0]; + buffer = (*psc->dri3->fdToBuffer)(psc->driScreen, + buffer_fd, + format, + bp_reply->width, + bp_reply->height, + bp_reply->stride, + bp_reply->size); + close(buffer_fd); + if (!buffer) + goto no_pixmap; + + buffer->pixmap = pixmap; + buffer->width = bp_reply->width; + buffer->height = bp_reply->height; + buffer->buffer_type = buffer_type; + buffer->shm_fence = shm_fence; + buffer->sync_fence = sync_fence; + + pdraw->buffers[buffer_type] = buffer; + return buffer; + +no_pixmap: + xcb_sync_destroy_fence(c, sync_fence); + xshmfence_unmap_shm(shm_fence); + return NULL; +} + +static __DRIdri3Buffer * +dri3_get_buffer(__DRIdrawable *driDrawable, + unsigned int format, + enum __DRI3bufferType buffer_type, + void *loaderPrivate) +{ + struct dri3_drawable *priv = loaderPrivate; + __DRIdri3Buffer *buffer = priv->buffers[buffer_type]; + + if (!buffer || buffer->width != priv->width || buffer->height != priv->height) { + xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy); + __DRIdri3Buffer *new_buffer; + + /* Allocate the new buffers + */ + new_buffer = dri3_alloc_render_buffer(priv->base.psc, + priv->base.xDrawable, + format, priv->width, priv->height, priv->depth); + if (!new_buffer) + return NULL; + switch (buffer_type) { + case __DRI3_BUFFER_BACK: + if (buffer) { + dri3_fence_reset(c, new_buffer); + dri3_fence_await(c, buffer); + dri3_copy_area(c, + buffer->pixmap, + new_buffer->pixmap, + dri3_drawable_gc(priv), + 0, 0, 0, 0, priv->width, priv->height); + dri3_fence_trigger(c, new_buffer); + dri3_free_render_buffer(priv, buffer); + dri3_fence_await(c, new_buffer); + } + break; + case __DRI3_BUFFER_FRONT: + dri3_fence_reset(c, new_buffer); + dri3_copy_area(c, + priv->base.xDrawable, + new_buffer->pixmap, + dri3_drawable_gc(priv), + 0, 0, 0, 0, priv->width, priv->height); + dri3_fence_trigger(c, new_buffer); + dri3_fence_await(c, new_buffer); + break; + } + buffer = new_buffer; + buffer->buffer_type = buffer_type; + priv->buffers[buffer_type] = buffer; + } + + /* Return the requested buffer */ + return buffer; +} + +static void +dri3_free_buffer(__DRIdrawable *driDrawable, + enum __DRI3bufferType buffer_type, + void *loaderPrivate) +{ + struct dri3_drawable *priv = loaderPrivate; + __DRIdri3Buffer *buffer = priv->buffers[buffer_type]; + + if (buffer) { + dri3_free_render_buffer(priv, buffer); + priv->buffers[buffer_type] = NULL; + } +} + +static int +dri3_get_buffers(__DRIdrawable *driDrawable, + int *width, int *height, + unsigned int format, + void *loaderPrivate, + int need_front, + int need_back, + __DRIdri3Buffer **front, + __DRIdri3Buffer **back) +{ + struct dri3_drawable *priv = loaderPrivate; + + *front = NULL; + *back = NULL; + + if (!dri3_update_drawable(driDrawable, loaderPrivate)) + return false; + + if (priv->is_pixmap) + need_front = 1; + + if (need_front) { + if (priv->is_pixmap) + *front = dri3_get_pixmap_buffer(driDrawable, + format, + __DRI3_BUFFER_FRONT, + loaderPrivate); + else + *front = dri3_get_buffer(driDrawable, + format, + __DRI3_BUFFER_FRONT, + loaderPrivate); + + if (!*front) + return false; + priv->have_fake_front = !priv->is_pixmap; + } else { + dri3_free_buffer(driDrawable, __DRI3_BUFFER_FRONT, loaderPrivate); + priv->have_fake_front = 0; + } + + if (need_back) { + *back = dri3_get_buffer(driDrawable, + format, + __DRI3_BUFFER_BACK, + loaderPrivate); + if (!*back) + return false; + priv->have_back = 1; + } else { + dri3_free_buffer(driDrawable, __DRI3_BUFFER_BACK, loaderPrivate); + priv->have_back = 0; + } + + /* Report back current geometry */ + *width = priv->width; + *height = priv->height; + return true; +} + +static int64_t +dri3_swap_buffers(__GLXDRIdrawable *pdraw, int64_t target_msc, int64_t divisor, + int64_t remainder, Bool flush) +{ + struct dri3_drawable *priv = (struct dri3_drawable *) pdraw; + struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc; + xcb_connection_t *c = XGetXCBConnection(priv->base.psc->dpy); + int64_t ret = 0; + + __DRIcontext *ctx = dri3_get_current_context(); + unsigned flags = __DRI2_FLUSH_DRAWABLE; + if (flush) + flags |= __DRI2_FLUSH_CONTEXT; + dri3_flush(psc, ctx, priv, flags, __DRI2_THROTTLE_SWAPBUFFER); + + if (priv->buffers[0] && !priv->is_pixmap) { + dri3_fence_reset(c, priv->buffers[0]); + dri3_copy_area(c, + priv->buffers[0]->pixmap, + priv->base.xDrawable, + dri3_drawable_gc(priv), + 0, 0, 0, 0, priv->width, priv->height); + dri3_fence_trigger(c, priv->buffers[0]); + if (priv->have_fake_front) { + dri3_fence_reset(c, priv->buffers[1]); + dri3_copy_area(c, + priv->buffers[0]->pixmap, + priv->buffers[1]->pixmap, + dri3_drawable_gc(priv), + 0, 0, 0, 0, priv->width, priv->height); + dri3_fence_trigger(c, priv->buffers[1]); + } + dri3_fence_await(c, priv->buffers[0]); + if (priv->have_fake_front) + dri3_fence_await(c, priv->buffers[1]); + } + + return ret; +} + +static int +dri3_query_version(Display *dpy, int *major, int *minor) +{ + xcb_dri3_query_version_cookie_t cookie; + xcb_dri3_query_version_reply_t *reply; + xcb_connection_t *c = XGetXCBConnection(dpy); + xcb_generic_error_t *error; + + cookie = xcb_dri3_query_version(c, + XCB_DRI3_MAJOR_VERSION, + XCB_DRI3_MINOR_VERSION); + reply = xcb_dri3_query_version_reply(c, cookie, &error); + if (!reply) { + if (error) { + free(error); + } + return 0; + } + *major = reply->major_version; + *minor = reply->minor_version; + free(reply); + return 1; +} + +static int +dri3_open(Display *dpy, + Window root, + CARD32 provider) +{ + xcb_dri3_open_cookie_t cookie; + xcb_dri3_open_reply_t *reply; + xcb_connection_t *c = XGetXCBConnection(dpy); + xcb_generic_error_t *error; + int fd; + + cookie = xcb_dri3_open(c, + root, + provider); + + reply = xcb_dri3_open_reply(c, cookie, &error); + if (!reply) + return -1; + + if (reply->nfd != 1) { + free(reply); + return -1; + } + + fd = xcb_dri3_open_reply_fds(c, reply)[0]; + fcntl(fd, F_SETFD, FD_CLOEXEC); + + return fd; +} + + +static void +dri3_destroy_screen(struct glx_screen *base) +{ + struct dri3_screen *psc = (struct dri3_screen *) base; + + /* Free the direct rendering per screen data */ + (*psc->core->destroyScreen) (psc->driScreen); + driDestroyConfigs(psc->driver_configs); + close(psc->fd); + free(psc); +} + +#if HAS_SBC +static int +dri3_set_swap_interval(__GLXDRIdrawable *pdraw, int interval) +{ + xcb_connection_t *c = XGetXCBConnection(pdraw->psc->dpy); + struct dri3_drawable *priv = (struct dri3_drawable *) pdraw; + GLint vblank_mode = DRI_CONF_VBLANK_DEF_INTERVAL_1; + struct dri3_screen *psc = (struct dri3_screen *) priv->base.psc; + + if (psc->config) + psc->config->configQueryi(psc->driScreen, + "vblank_mode", &vblank_mode); + + switch (vblank_mode) { + case DRI_CONF_VBLANK_NEVER: + if (interval != 0) + return GLX_BAD_VALUE; + break; + case DRI_CONF_VBLANK_ALWAYS_SYNC: + if (interval <= 0) + return GLX_BAD_VALUE; + break; + default: + break; + } + + xcb_dri2_swap_interval(c, priv->base.xDrawable, interval); + priv->swap_interval = interval; + + return 0; +} + +static int +dri3_get_swap_interval(__GLXDRIdrawable *pdraw) +{ + struct dri3_drawable *priv = (struct dri3_drawable *) pdraw; + + return priv->swap_interval; +} +#endif + +static const __DRIdri3LoaderExtension dri3LoaderExtension = { + {__DRI_DRI3_LOADER, __DRI_DRI3_LOADER_VERSION}, + .getBuffers = dri3_get_buffers, + .flushFrontBuffer = dri3_flush_front_buffer, +}; + +static void +dri3_bind_tex_image(Display * dpy, + GLXDrawable drawable, + int buffer, const int *attrib_list) +{ + struct glx_context *gc = __glXGetCurrentContext(); + struct dri3_context *pcp = (struct dri3_context *) gc; + __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable); + struct dri3_drawable *pdraw = (struct dri3_drawable *) base; + struct dri3_screen *psc; + + if (pdraw != NULL) { + psc = (struct dri3_screen *) base->psc; + + if (psc->f && + psc->f->base.version >= 3 && psc->f->invalidate) + psc->f->invalidate(pdraw->driDrawable); + + XSync(dpy, false); + if (psc->texBuffer->base.version >= 2 && + psc->texBuffer->setTexBuffer2 != NULL) { + (*psc->texBuffer->setTexBuffer2) (pcp->driContext, + pdraw->base.textureTarget, + pdraw->base.textureFormat, + pdraw->driDrawable); + } + else { + (*psc->texBuffer->setTexBuffer) (pcp->driContext, + pdraw->base.textureTarget, + pdraw->driDrawable); + } + } +} + +static void +dri3_release_tex_image(Display * dpy, GLXDrawable drawable, int buffer) +{ +#if __DRI_TEX_BUFFER_VERSION >= 3 + struct glx_context *gc = __glXGetCurrentContext(); + struct dri3_context *pcp = (struct dri3_context *) gc; + __GLXDRIdrawable *base = GetGLXDRIDrawable(dpy, drawable); + struct glx_display *dpyPriv = __glXInitialize(dpy); + struct dri3_drawable *pdraw = (struct dri3_drawable *) base; + struct dri3_display *pdp = + (struct dri3_display *) dpyPriv->dri3Display; + struct dri3_screen *psc; + + if (pdraw != NULL) { + psc = (struct dri3_screen *) base->psc; + + if (psc->texBuffer->base.version >= 3 && + psc->texBuffer->releaseTexBuffer != NULL) { + (*psc->texBuffer->releaseTexBuffer) (pcp->driContext, + pdraw->base.textureTarget, + pdraw->driDrawable); + } + } +#endif +} + +static const struct glx_context_vtable dri3_context_vtable = { + dri3_destroy_context, + dri3_bind_context, + dri3_unbind_context, + dri3_wait_gl, + dri3_wait_x, + DRI_glXUseXFont, + dri3_bind_tex_image, + dri3_release_tex_image, + NULL, /* get_proc_address */ +}; + +static void +dri3_bind_extensions(struct dri3_screen *psc, struct glx_display * priv, + const char *driverName) +{ +// const struct dri3_display *const pdp = (struct dri3_display *) priv->dri3Display; + const __DRIextension **extensions; + unsigned mask; + int i; + + extensions = psc->core->getExtensions(psc->driScreen); + + __glXEnableDirectExtension(&psc->base, "GLX_SGI_video_sync"); + __glXEnableDirectExtension(&psc->base, "GLX_SGI_swap_control"); + __glXEnableDirectExtension(&psc->base, "GLX_MESA_swap_control"); + __glXEnableDirectExtension(&psc->base, "GLX_SGI_make_current_read"); + + /* + * GLX_INTEL_swap_event is broken on the server side, where it's + * currently unconditionally enabled. This completely breaks + * systems running on drivers which don't support that extension. + * There's no way to test for its presence on this side, so instead + * of disabling it unconditionally, just disable it for drivers + * which are known to not support it, or for DDX drivers supporting + * only an older (pre-ScheduleSwap) version of DRI2. + * + * This is a hack which is required until: + * http://lists.x.org/archives/xorg-devel/2013-February/035449.html + * is merged and updated xserver makes it's way into distros: + */ +// if (pdp->swapAvailable && strcmp(driverName, "vmwgfx") != 0) { +// __glXEnableDirectExtension(&psc->base, "GLX_INTEL_swap_event"); +// } + + mask = psc->dri3->getAPIMask(psc->driScreen); + + __glXEnableDirectExtension(&psc->base, "GLX_ARB_create_context"); + __glXEnableDirectExtension(&psc->base, "GLX_ARB_create_context_profile"); + + if ((mask & (1 << __DRI_API_GLES2)) != 0) + __glXEnableDirectExtension(&psc->base, + "GLX_EXT_create_context_es2_profile"); + + for (i = 0; extensions[i]; i++) { + if ((strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0)) { + psc->texBuffer = (__DRItexBufferExtension *) extensions[i]; + __glXEnableDirectExtension(&psc->base, "GLX_EXT_texture_from_pixmap"); + } + + if ((strcmp(extensions[i]->name, __DRI2_FLUSH) == 0)) { + psc->f = (__DRI2flushExtension *) extensions[i]; + /* internal driver extension, no GL extension exposed */ + } + + if ((strcmp(extensions[i]->name, __DRI2_CONFIG_QUERY) == 0)) + psc->config = (__DRI2configQueryExtension *) extensions[i]; + + if (((strcmp(extensions[i]->name, __DRI2_THROTTLE) == 0))) + psc->throttle = (__DRI2throttleExtension *) extensions[i]; + + if (strcmp(extensions[i]->name, __DRI2_ROBUSTNESS) == 0) + __glXEnableDirectExtension(&psc->base, + "GLX_ARB_create_context_robustness"); + } +} + +static const struct glx_screen_vtable dri3_screen_vtable = { + dri3_create_context, + dri3_create_context_attribs +}; + +static struct glx_screen * +dri3_create_screen(int screen, struct glx_display * priv) +{ + const __DRIconfig **driver_configs; + const __DRIextension **extensions; + const struct dri3_display *const pdp = (struct dri3_display *) + priv->dri3Display; + struct dri3_screen *psc; + __GLXDRIscreen *psp; + struct glx_config *configs = NULL, *visuals = NULL; + char *driverName, *deviceName, *tmp; + int i; + + psc = calloc(1, sizeof *psc); + if (psc == NULL) + return NULL; + + psc->fd = -1; + + if (!glx_screen_init(&psc->base, screen, priv)) { + free(psc); + return NULL; + } + + psc->fd = dri3_open(priv->dpy, RootWindow(priv->dpy, screen), None); + if (psc->fd < 0) { + glx_screen_cleanup(&psc->base); + free(psc); + InfoMessageF("screen %d does not appear to be DRI3 capable\n", screen); + return NULL; + } + deviceName = NULL; + + driverName = dri3_get_driver_for_fd(psc->fd); + if (!driverName) { + ErrorMessageF("No driver found\n"); + goto handle_error; + } + + psc->driver = driOpenDriver(driverName); + if (psc->driver == NULL) { + ErrorMessageF("driver pointer missing\n"); + goto handle_error; + } + + extensions = driGetDriverExtensions(psc->driver, driverName); + if (extensions == NULL) + goto handle_error; + + for (i = 0; extensions[i]; i++) { + if (strcmp(extensions[i]->name, __DRI_CORE) == 0) + psc->core = (__DRIcoreExtension *) extensions[i]; + if (strcmp(extensions[i]->name, __DRI_DRI3) == 0) + psc->dri3 = (__DRIdri3Extension *) extensions[i]; + } + + if (psc->core == NULL || psc->dri3 == NULL) { + ErrorMessageF("core dri or dri3 extension not found\n"); + goto handle_error; + } + + psc->driScreen = + psc->dri3->createNewScreen2(screen, psc->fd, + (const __DRIextension **) + &pdp->loader_extensions[0], + extensions, + &driver_configs, psc); + + if (psc->driScreen == NULL) { + ErrorMessageF("failed to create dri screen\n"); + goto handle_error; + } + + dri3_bind_extensions(psc, priv, driverName); + + configs = driConvertConfigs(psc->core, psc->base.configs, driver_configs); + visuals = driConvertConfigs(psc->core, psc->base.visuals, driver_configs); + + if (!configs || !visuals) + goto handle_error; + + glx_config_destroy_list(psc->base.configs); + psc->base.configs = configs; + glx_config_destroy_list(psc->base.visuals); + psc->base.visuals = visuals; + + psc->driver_configs = driver_configs; + + psc->base.vtable = &dri3_screen_vtable; + psp = &psc->vtable; + psc->base.driScreen = psp; + psp->destroyScreen = dri3_destroy_screen; + psp->createDrawable = dri3_create_drawable; + psp->swapBuffers = dri3_swap_buffers; + psp->getDrawableMSC = NULL; + psp->waitForMSC = NULL; + psp->waitForSBC = NULL; + psp->setSwapInterval = NULL; + psp->getSwapInterval = NULL; + +#if HAS_SBC + if (pdp->driMinor >= 2) { + psp->getDrawableMSC = dri3DrawableGetMSC; + psp->waitForMSC = dri3WaitForMSC; + psp->waitForSBC = dri3WaitForSBC; + psp->setSwapInterval = dri3SetSwapInterval; + psp->getSwapInterval = dri3GetSwapInterval; + __glXEnableDirectExten... [truncated message content] |
From: Keith P. <ke...@ke...> - 2013-10-31 23:31:14
|
This just renames them so that they can be used with the DRI3 extension without causing too much confusion. Signed-off-by: Keith Packard <ke...@ke...> --- src/mesa/drivers/dri/common/dri_util.c | 50 +++++++++++++++++----------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/mesa/drivers/dri/common/dri_util.c b/src/mesa/drivers/dri/common/dri_util.c index c28b0fc..539fb4b 100644 --- a/src/mesa/drivers/dri/common/dri_util.c +++ b/src/mesa/drivers/dri/common/dri_util.c @@ -291,13 +291,13 @@ validate_context_version(__DRIscreen *screen, /*@{*/ static __DRIcontext * -dri2CreateContextAttribs(__DRIscreen *screen, int api, - const __DRIconfig *config, - __DRIcontext *shared, - unsigned num_attribs, - const uint32_t *attribs, - unsigned *error, - void *data) +driCreateContextAttribs(__DRIscreen *screen, int api, + const __DRIconfig *config, + __DRIcontext *shared, + unsigned num_attribs, + const uint32_t *attribs, + unsigned *error, + void *data) { __DRIcontext *context; const struct gl_config *modes = (config != NULL) ? &config->modes : NULL; @@ -442,22 +442,22 @@ dri2CreateContextAttribs(__DRIscreen *screen, int api, } static __DRIcontext * -dri2CreateNewContextForAPI(__DRIscreen *screen, int api, - const __DRIconfig *config, - __DRIcontext *shared, void *data) +driCreateNewContextForAPI(__DRIscreen *screen, int api, + const __DRIconfig *config, + __DRIcontext *shared, void *data) { unsigned error; - return dri2CreateContextAttribs(screen, api, config, shared, 0, NULL, - &error, data); + return driCreateContextAttribs(screen, api, config, shared, 0, NULL, + &error, data); } static __DRIcontext * -dri2CreateNewContext(__DRIscreen *screen, const __DRIconfig *config, - __DRIcontext *shared, void *data) +driCreateNewContext(__DRIscreen *screen, const __DRIconfig *config, + __DRIcontext *shared, void *data) { - return dri2CreateNewContextForAPI(screen, __DRI_API_OPENGL, - config, shared, data); + return driCreateNewContextForAPI(screen, __DRI_API_OPENGL, + config, shared, data); } /** @@ -609,9 +609,9 @@ static void dri_put_drawable(__DRIdrawable *pdp) } static __DRIdrawable * -dri2CreateNewDrawable(__DRIscreen *screen, - const __DRIconfig *config, - void *data) +driCreateNewDrawable(__DRIscreen *screen, + const __DRIconfig *config, + void *data) { __DRIdrawable *pdraw; @@ -698,7 +698,7 @@ dri2ConfigQueryf(__DRIscreen *screen, const char *var, GLfloat *val) } static unsigned int -dri2GetAPIMask(__DRIscreen *screen) +driGetAPIMask(__DRIscreen *screen) { return screen->api_mask; } @@ -741,13 +741,13 @@ const __DRIdri2Extension driDRI2Extension = { .base = { __DRI_DRI2, 4 }, .createNewScreen = dri2CreateNewScreen, - .createNewDrawable = dri2CreateNewDrawable, - .createNewContext = dri2CreateNewContext, - .getAPIMask = dri2GetAPIMask, - .createNewContextForAPI = dri2CreateNewContextForAPI, + .createNewDrawable = driCreateNewDrawable, + .createNewContext = driCreateNewContext, + .getAPIMask = driGetAPIMask, + .createNewContextForAPI = driCreateNewContextForAPI, .allocateBuffer = dri2AllocateBuffer, .releaseBuffer = dri2ReleaseBuffer, - .createContextAttribs = dri2CreateContextAttribs, + .createContextAttribs = driCreateContextAttribs .createNewScreen2 = dri2CreateNewScreen2, }; -- 1.8.4.2 |
From: Keith P. <ke...@ke...> - 2013-10-31 23:31:07
|
The first four patches prepare the library for DRI3, then the final two patches add DRI3 and Present support. Minor changes: [PATCH 1/6] drivers/dri/common: A few dri2 functions are not actually [PATCH 2/6] dri/intel: Split out DRI2 buffer update code to separate [PATCH 3/6] dri/intel: Add explicit size parameter to [PATCH 4/6] dri/intel: Add intel_fd_for_region New code: [PATCH 5/6] dri3: Add DRI3 support to GLX, DRI common and Intel [PATCH 6/6] Make GLX/dri3 use the Present extension when available |