Split out the hardware pipe specifics from mdp5_plane. To start, the hw pipes are statically assigned to planes, but next step is to assign the hw pipes during plane->atomic_check() based on requested caps (scaling, YUV, etc). And then hw pipe re-assignment if required if required SMP blocks changes. Signed-off-by: Rob Clark <robdclark@xxxxxxxxx> --- drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 126 +++++++++++++++++++----------- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 7 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c | 43 ++++++++++ drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h | 39 +++++++++ drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 66 +++++++--------- 6 files changed, 197 insertions(+), 85 deletions(-) create mode 100644 drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c create mode 100644 drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index fb5be3e..90f66c4 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -37,6 +37,7 @@ msm-y := \ mdp/mdp5/mdp5_irq.o \ mdp/mdp5/mdp5_mdss.o \ mdp/mdp5/mdp5_kms.o \ + mdp/mdp5/mdp5_pipe.o \ mdp/mdp5/mdp5_plane.o \ mdp/mdp5/mdp5_smp.o \ msm_atomic.o \ diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index f1288c7..d3d45ed 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -119,6 +119,10 @@ static void mdp5_kms_destroy(struct msm_kms *kms) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); struct msm_gem_address_space *aspace = mdp5_kms->aspace; + int i; + + for (i = 0; i < mdp5_kms->num_hwpipes; i++) + mdp5_pipe_destroy(mdp5_kms->hwpipes[i]); if (aspace) { aspace->mmu->funcs->detach(aspace->mmu, @@ -323,15 +327,6 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) static int modeset_init(struct mdp5_kms *mdp5_kms) { - static const enum mdp5_pipe rgb_planes[] = { - SSPP_RGB0, SSPP_RGB1, SSPP_RGB2, SSPP_RGB3, - }; - static const enum mdp5_pipe vig_planes[] = { - SSPP_VIG0, SSPP_VIG1, SSPP_VIG2, SSPP_VIG3, - }; - static const enum mdp5_pipe dma_planes[] = { - SSPP_DMA0, SSPP_DMA1, - }; struct drm_device *dev = mdp5_kms->dev; struct msm_drm_private *priv = dev->dev_private; const struct mdp5_cfg_hw *hw_cfg; @@ -339,58 +334,34 @@ static int modeset_init(struct mdp5_kms *mdp5_kms) hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); - /* construct CRTCs and their private planes: */ - for (i = 0; i < hw_cfg->pipe_rgb.count; i++) { + /* Construct planes equaling the number of hw pipes, and CRTCs + * for the N layer-mixers (LM). The first N planes become primary + * planes for the CRTCs, with the remainder as overlay planes: + */ + for (i = 0; i < mdp5_kms->num_hwpipes; i++) { + bool primary = i < mdp5_cfg->lm.count; struct drm_plane *plane; struct drm_crtc *crtc; - plane = mdp5_plane_init(dev, rgb_planes[i], true, - hw_cfg->pipe_rgb.base[i], hw_cfg->pipe_rgb.caps); + plane = mdp5_plane_init(dev, mdp5_kms->hwpipes[i], primary); if (IS_ERR(plane)) { ret = PTR_ERR(plane); - dev_err(dev->dev, "failed to construct plane for %s (%d)\n", - pipe2name(rgb_planes[i]), ret); + dev_err(dev->dev, "failed to construct plane %d (%d)\n", i, ret); goto fail; } + if (!primary) + continue; + crtc = mdp5_crtc_init(dev, plane, i); if (IS_ERR(crtc)) { ret = PTR_ERR(crtc); - dev_err(dev->dev, "failed to construct crtc for %s (%d)\n", - pipe2name(rgb_planes[i]), ret); + dev_err(dev->dev, "failed to construct crtc %d (%d)\n", i, ret); goto fail; } priv->crtcs[priv->num_crtcs++] = crtc; } - /* Construct video planes: */ - for (i = 0; i < hw_cfg->pipe_vig.count; i++) { - struct drm_plane *plane; - - plane = mdp5_plane_init(dev, vig_planes[i], false, - hw_cfg->pipe_vig.base[i], hw_cfg->pipe_vig.caps); - if (IS_ERR(plane)) { - ret = PTR_ERR(plane); - dev_err(dev->dev, "failed to construct %s plane: %d\n", - pipe2name(vig_planes[i]), ret); - goto fail; - } - } - - /* DMA planes */ - for (i = 0; i < hw_cfg->pipe_dma.count; i++) { - struct drm_plane *plane; - - plane = mdp5_plane_init(dev, dma_planes[i], false, - hw_cfg->pipe_dma.base[i], hw_cfg->pipe_dma.caps); - if (IS_ERR(plane)) { - ret = PTR_ERR(plane); - dev_err(dev->dev, "failed to construct %s plane: %d\n", - pipe2name(dma_planes[i]), ret); - goto fail; - } - } - /* Construct encoders and modeset initialize connector devices * for each external display interface. */ @@ -676,6 +647,67 @@ static void mdp5_destroy(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } +static int construct_pipes(struct mdp5_kms *mdp5_kms, int cnt, + const enum mdp5_pipe *pipes, const uint32_t *offsets, + uint32_t caps) +{ + struct drm_device *dev = mdp5_kms->dev; + int i, ret; + + for (i = 0; i < cnt; i++) { + struct mdp5_hw_pipe *hwpipe; + + hwpipe = mdp5_pipe_init(pipes[i], offsets[i], caps); + if (IS_ERR(hwpipe)) { + ret = PTR_ERR(hwpipe); + dev_err(dev->dev, "failed to construct pipe for %s (%d)\n", + pipe2name(pipes[i]), ret); + return ret; + } + hwpipe->idx = mdp5_kms->num_hwpipes; + mdp5_kms->hwpipes[mdp5_kms->num_hwpipes++] = hwpipe; + } + + return 0; +} + +static int hwpipe_init(struct mdp5_kms *mdp5_kms) +{ + static const enum mdp5_pipe rgb_planes[] = { + SSPP_RGB0, SSPP_RGB1, SSPP_RGB2, SSPP_RGB3, + }; + static const enum mdp5_pipe vig_planes[] = { + SSPP_VIG0, SSPP_VIG1, SSPP_VIG2, SSPP_VIG3, + }; + static const enum mdp5_pipe dma_planes[] = { + SSPP_DMA0, SSPP_DMA1, + }; + const struct mdp5_cfg_hw *hw_cfg; + int ret; + + hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); + + /* Construct RGB pipes: */ + ret = construct_pipes(mdp5_kms, hw_cfg->pipe_rgb.count, rgb_planes, + hw_cfg->pipe_rgb.base, hw_cfg->pipe_rgb.caps); + if (ret) + return ret; + + /* Construct video (VIG) pipes: */ + ret = construct_pipes(mdp5_kms, hw_cfg->pipe_vig.count, vig_planes, + hw_cfg->pipe_vig.base, hw_cfg->pipe_vig.caps); + if (ret) + return ret; + + /* Construct DMA pipes: */ + ret = construct_pipes(mdp5_kms, hw_cfg->pipe_dma.count, dma_planes, + hw_cfg->pipe_dma.base, hw_cfg->pipe_dma.caps); + if (ret) + return ret; + + return 0; +} + static int mdp5_init(struct platform_device *pdev, struct drm_device *dev) { struct msm_drm_private *priv = dev->dev_private; @@ -765,6 +797,10 @@ static int mdp5_init(struct platform_device *pdev, struct drm_device *dev) goto fail; } + ret = hwpipe_init(mdp5_kms); + if (ret) + goto fail; + /* set uninit-ed kms */ priv->kms = &mdp5_kms->base.base; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index d5e40af..88120c5 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -24,6 +24,7 @@ #include "mdp5_cfg.h" /* must be included before mdp5.xml.h */ #include "mdp5.xml.h" #include "mdp5_ctl.h" +#include "mdp5_pipe.h" #include "mdp5_smp.h" struct mdp5_kms { @@ -33,6 +34,9 @@ struct mdp5_kms { struct platform_device *pdev; + unsigned num_hwpipes; + struct mdp5_hw_pipe *hwpipes[16]; + struct mdp5_cfg_handler *cfg; uint32_t caps; /* MDP capabilities (MDP_CAP_XXX bits) */ @@ -207,8 +211,7 @@ void mdp5_plane_complete_commit(struct drm_plane *plane, struct drm_plane_state *state); enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane); struct drm_plane *mdp5_plane_init(struct drm_device *dev, - enum mdp5_pipe pipe, bool private_plane, - uint32_t reg_offset, uint32_t caps); + struct mdp5_hw_pipe *hwpipe, bool primary); uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c new file mode 100644 index 0000000..7f3e8e50 --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 Red Hat + * Author: Rob Clark <robdclark@xxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "mdp5_kms.h" + +void mdp5_pipe_destroy(struct mdp5_hw_pipe *hwpipe) +{ + kfree(hwpipe); +} + +struct mdp5_hw_pipe *mdp5_pipe_init(enum mdp5_pipe pipe, + uint32_t reg_offset, uint32_t caps) +{ + struct mdp5_hw_pipe *hwpipe; + + hwpipe = kzalloc(sizeof(*hwpipe), GFP_KERNEL); + if (!hwpipe) + return ERR_PTR(-ENOMEM); + + hwpipe->name = pipe2name(pipe); + hwpipe->pipe = pipe; + hwpipe->reg_offset = reg_offset; + hwpipe->caps = caps; + hwpipe->flush_mask = mdp_ctl_flush_mask_pipe(pipe); + + spin_lock_init(&hwpipe->pipe_lock); + + return hwpipe; +} diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h new file mode 100644 index 0000000..dcfa0e0 --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Red Hat + * Author: Rob Clark <robdclark@xxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __MDP5_PIPE_H__ +#define __MDP5_PIPE_H__ + +/* represents a hw pipe, which is dynamically assigned to a plane */ +struct mdp5_hw_pipe { + int idx; + + const char *name; + enum mdp5_pipe pipe; + + spinlock_t pipe_lock; /* protect REG_MDP5_PIPE_* registers */ + uint32_t reg_offset; + uint32_t caps; + + uint32_t flush_mask; /* used to commit pipe registers */ +}; + +struct mdp5_hw_pipe *mdp5_pipe_init(enum mdp5_pipe pipe, + uint32_t reg_offset, uint32_t caps); +void mdp5_pipe_destroy(struct mdp5_hw_pipe *hwpipe); + +#endif /* __MDP5_PIPE_H__ */ diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 7b46cfb..e4ecfeb 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -22,13 +22,7 @@ struct mdp5_plane { struct drm_plane base; - enum mdp5_pipe pipe; - - spinlock_t pipe_lock; /* protect REG_MDP5_PIPE_* registers */ - uint32_t reg_offset; - uint32_t caps; - - uint32_t flush_mask; /* used to commit pipe registers */ + struct mdp5_hw_pipe *hwpipe; uint32_t nformats; uint32_t formats[32]; @@ -71,8 +65,8 @@ static void mdp5_plane_install_rotation_property(struct drm_device *dev, { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); - if (!(mdp5_plane->caps & MDP_PIPE_CAP_HFLIP) && - !(mdp5_plane->caps & MDP_PIPE_CAP_VFLIP)) + if (!(mdp5_plane->hwpipe->caps & MDP_PIPE_CAP_HFLIP) && + !(mdp5_plane->hwpipe->caps & MDP_PIPE_CAP_VFLIP)) return; drm_plane_create_rotation_property(plane, @@ -304,13 +298,13 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane, format = to_mdp_format(msm_framebuffer_format(state->fb)); if (MDP_FORMAT_IS_YUV(format) && - !pipe_supports_yuv(mdp5_plane->caps)) { + !pipe_supports_yuv(mdp5_plane->hwpipe->caps)) { DBG("Pipe doesn't support YUV\n"); return -EINVAL; } - if (!(mdp5_plane->caps & MDP_PIPE_CAP_SCALE) && + if (!(mdp5_plane->hwpipe->caps & MDP_PIPE_CAP_SCALE) && (((state->src_w >> 16) != state->crtc_w) || ((state->src_h >> 16) != state->crtc_h))) { DBG("Pipe doesn't support scaling (%dx%d -> %dx%d)\n", @@ -324,11 +318,12 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane, DRM_ROTATE_0 | DRM_REFLECT_X | DRM_REFLECT_Y); + hflip = !!(rotation & DRM_REFLECT_X); vflip = !!(rotation & DRM_REFLECT_Y); - if ((vflip && !(mdp5_plane->caps & MDP_PIPE_CAP_VFLIP)) || - (hflip && !(mdp5_plane->caps & MDP_PIPE_CAP_HFLIP))) { + if ((vflip && !(mdp5_plane->hwpipe->caps & MDP_PIPE_CAP_VFLIP)) || + (hflip && !(mdp5_plane->hwpipe->caps & MDP_PIPE_CAP_HFLIP))) { DBG("Pipe doesn't support flip\n"); return -EINVAL; @@ -396,7 +391,7 @@ static void set_scanout_locked(struct drm_plane *plane, { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct mdp5_kms *mdp5_kms = get_kms(plane); - enum mdp5_pipe pipe = mdp5_plane->pipe; + enum mdp5_pipe pipe = mdp5_plane->hwpipe->pipe; mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe), MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) | @@ -678,12 +673,13 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct drm_plane_state *pstate = plane->state; + struct mdp5_hw_pipe *hwpipe = mdp5_plane->hwpipe; struct mdp5_kms *mdp5_kms = get_kms(plane); - enum mdp5_pipe pipe = mdp5_plane->pipe; + enum mdp5_pipe pipe = hwpipe->pipe; const struct mdp_format *format; uint32_t nplanes, config = 0; uint32_t phasex_step[COMP_MAX] = {0,}, phasey_step[COMP_MAX] = {0,}; - bool pe = mdp5_plane->caps & MDP_PIPE_CAP_SW_PIX_EXT; + bool pe = hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT; int pe_left[COMP_MAX], pe_right[COMP_MAX]; int pe_top[COMP_MAX], pe_bottom[COMP_MAX]; uint32_t hdecm = 0, vdecm = 0; @@ -714,8 +710,8 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, /* Request some memory from the SMP: */ if (mdp5_kms->smp) { - ret = mdp5_smp_request(mdp5_kms->smp, - mdp5_plane->pipe, format, src_w, false); + ret = mdp5_smp_request(mdp5_kms->smp, pipe, + format, src_w, false); if (ret) return ret; } @@ -737,7 +733,7 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, if (ret) return ret; - if (mdp5_plane->caps & MDP_PIPE_CAP_SW_PIX_EXT) { + if (hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT) { calc_pixel_ext(format, src_w, crtc_w, phasex_step, pe_left, pe_right, true); calc_pixel_ext(format, src_h, crtc_h, phasey_step, @@ -758,7 +754,7 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, hflip = !!(rotation & DRM_REFLECT_X); vflip = !!(rotation & DRM_REFLECT_Y); - spin_lock_irqsave(&mdp5_plane->pipe_lock, flags); + spin_lock_irqsave(&hwpipe->pipe_lock, flags); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe), MDP5_PIPE_SRC_IMG_SIZE_WIDTH(min(fb->width, src_w)) | @@ -807,12 +803,12 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, /* not using secure mode: */ mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(pipe), 0); - if (mdp5_plane->caps & MDP_PIPE_CAP_SW_PIX_EXT) + if (hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT) mdp5_write_pixel_ext(mdp5_kms, pipe, format, src_w, pe_left, pe_right, src_h, pe_top, pe_bottom); - if (mdp5_plane->caps & MDP_PIPE_CAP_SCALE) { + if (hwpipe->caps & MDP_PIPE_CAP_SCALE) { mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe), phasex_step[COMP_0]); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe), @@ -827,7 +823,7 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe), config); } - if (mdp5_plane->caps & MDP_PIPE_CAP_CSC) { + if (hwpipe->caps & MDP_PIPE_CAP_CSC) { if (MDP_FORMAT_IS_YUV(format)) csc_enable(mdp5_kms, pipe, mdp_get_default_csc_cfg(CSC_YUV2RGB)); @@ -837,7 +833,7 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, set_scanout_locked(plane, fb); - spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags); + spin_unlock_irqrestore(&hwpipe->pipe_lock, flags); return ret; } @@ -845,14 +841,14 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); - return mdp5_plane->pipe; + return mdp5_plane->hwpipe->pipe; } uint32_t mdp5_plane_get_flush(struct drm_plane *plane) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); - return mdp5_plane->flush_mask; + return mdp5_plane->hwpipe->flush_mask; } /* called after vsync in thread context */ @@ -861,7 +857,7 @@ void mdp5_plane_complete_commit(struct drm_plane *plane, { struct mdp5_kms *mdp5_kms = get_kms(plane); struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); - enum mdp5_pipe pipe = mdp5_plane->pipe; + enum mdp5_pipe pipe = mdp5_plane->hwpipe->pipe; if (mdp5_kms->smp) { if (plane_enabled(plane->state)) { @@ -878,8 +874,7 @@ void mdp5_plane_complete_commit(struct drm_plane *plane, /* initialize plane */ struct drm_plane *mdp5_plane_init(struct drm_device *dev, - enum mdp5_pipe pipe, bool private_plane, uint32_t reg_offset, - uint32_t caps) + struct mdp5_hw_pipe *hwpipe, bool primary) { struct drm_plane *plane = NULL; struct mdp5_plane *mdp5_plane; @@ -894,21 +889,16 @@ struct drm_plane *mdp5_plane_init(struct drm_device *dev, plane = &mdp5_plane->base; - mdp5_plane->pipe = pipe; - mdp5_plane->caps = caps; + mdp5_plane->hwpipe = hwpipe; mdp5_plane->nformats = mdp_get_formats(mdp5_plane->formats, ARRAY_SIZE(mdp5_plane->formats), - !pipe_supports_yuv(mdp5_plane->caps)); - - mdp5_plane->flush_mask = mdp_ctl_flush_mask_pipe(pipe); - mdp5_plane->reg_offset = reg_offset; - spin_lock_init(&mdp5_plane->pipe_lock); + !pipe_supports_yuv(hwpipe->caps)); - type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; + type = primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; ret = drm_universal_plane_init(dev, plane, 0xff, &mdp5_plane_funcs, mdp5_plane->formats, mdp5_plane->nformats, - type, "%s", pipe2name(pipe)); + type, "%s", hwpipe->name); if (ret) goto fail; -- 2.7.4 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel