On Thu, Mar 19, 2015 at 01:46:40PM +0100, Benjamin Gaignard wrote: > v1: This patch does the minimum to make sti driver use atomic helpers. > No big bang, only adapt some functions to new call order. > > v2: Use dpms and page flip atomic helpers > > Signed-off-by: Benjamin Gaignard <benjamin.gaignard@xxxxxxxxxx> Yeah atomic conversion looks complete now. Acked-by: Daniel Vetter <daniel.vetter@xxxxxxxx> > --- > drivers/gpu/drm/sti/sti_drm_crtc.c | 175 ++++++++---------------------------- > drivers/gpu/drm/sti/sti_drm_drv.c | 86 +++++++++++++++++- > drivers/gpu/drm/sti/sti_drm_drv.h | 6 ++ > drivers/gpu/drm/sti/sti_drm_plane.c | 66 ++++++++++++-- > drivers/gpu/drm/sti/sti_dvo.c | 6 +- > drivers/gpu/drm/sti/sti_hda.c | 6 +- > drivers/gpu/drm/sti/sti_hdmi.c | 6 +- > 7 files changed, 205 insertions(+), 146 deletions(-) > > diff --git a/drivers/gpu/drm/sti/sti_drm_crtc.c b/drivers/gpu/drm/sti/sti_drm_crtc.c > index e6f6ef7..6b641c5 100644 > --- a/drivers/gpu/drm/sti/sti_drm_crtc.c > +++ b/drivers/gpu/drm/sti/sti_drm_crtc.c > @@ -9,6 +9,8 @@ > #include <linux/clk.h> > > #include <drm/drmP.h> > +#include <drm/drm_atomic.h> > +#include <drm/drm_atomic_helper.h> > #include <drm/drm_crtc_helper.h> > #include <drm/drm_plane_helper.h> > > @@ -77,22 +79,18 @@ static bool sti_drm_crtc_mode_fixup(struct drm_crtc *crtc, > } > > static int > -sti_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, > - struct drm_display_mode *adjusted_mode, int x, int y, > - struct drm_framebuffer *old_fb) > +sti_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode) > { > struct sti_mixer *mixer = to_sti_mixer(crtc); > struct device *dev = mixer->dev; > struct sti_compositor *compo = dev_get_drvdata(dev); > - struct sti_layer *layer; > struct clk *clk; > int rate = mode->clock * 1000; > int res; > - unsigned int w, h; > > - DRM_DEBUG_KMS("CRTC:%d (%s) fb:%d mode:%d (%s)\n", > + DRM_DEBUG_KMS("CRTC:%d (%s) mode:%d (%s)\n", > crtc->base.id, sti_mixer_to_str(mixer), > - crtc->primary->fb->base.id, mode->base.id, mode->name); > + mode->base.id, mode->name); > > DRM_DEBUG_KMS("%d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n", > mode->vrefresh, mode->clock, > @@ -122,72 +120,13 @@ sti_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, > sti_vtg_set_config(mixer->id == STI_MIXER_MAIN ? > compo->vtg_main : compo->vtg_aux, &crtc->mode); > > - /* a GDP is reserved to the CRTC FB */ > - layer = to_sti_layer(crtc->primary); > - if (!layer) { > - DRM_ERROR("Can not find GDP0)\n"); > - return -EINVAL; > - } > - > - /* copy the mode data adjusted by mode_fixup() into crtc->mode > - * so that hardware can be set to proper mode > - */ > - memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode)); > - > - res = sti_mixer_set_layer_depth(mixer, layer); > - if (res) { > - DRM_ERROR("Can not set layer depth\n"); > - return -EINVAL; > - } > res = sti_mixer_active_video_area(mixer, &crtc->mode); > if (res) { > DRM_ERROR("Can not set active video area\n"); > return -EINVAL; > } > > - w = crtc->primary->fb->width - x; > - h = crtc->primary->fb->height - y; > - > - return sti_layer_prepare(layer, crtc, > - crtc->primary->fb, &crtc->mode, > - mixer->id, 0, 0, w, h, x, y, w, h); > -} > - > -static int sti_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, > - struct drm_framebuffer *old_fb) > -{ > - struct sti_mixer *mixer = to_sti_mixer(crtc); > - struct sti_layer *layer; > - unsigned int w, h; > - int ret; > - > - DRM_DEBUG_KMS("CRTC:%d (%s) fb:%d (%d,%d)\n", > - crtc->base.id, sti_mixer_to_str(mixer), > - crtc->primary->fb->base.id, x, y); > - > - /* GDP is reserved to the CRTC FB */ > - layer = to_sti_layer(crtc->primary); > - if (!layer) { > - DRM_ERROR("Can not find GDP0)\n"); > - ret = -EINVAL; > - goto out; > - } > - > - w = crtc->primary->fb->width - crtc->x; > - h = crtc->primary->fb->height - crtc->y; > - > - ret = sti_layer_prepare(layer, crtc, > - crtc->primary->fb, &crtc->mode, > - mixer->id, 0, 0, w, h, > - crtc->x, crtc->y, w, h); > - if (ret) { > - DRM_ERROR("Can not prepare layer\n"); > - goto out; > - } > - > - sti_drm_crtc_commit(crtc); > -out: > - return ret; > + return res; > } > > static void sti_drm_crtc_disable(struct drm_crtc *crtc) > @@ -195,7 +134,6 @@ static void sti_drm_crtc_disable(struct drm_crtc *crtc) > struct sti_mixer *mixer = to_sti_mixer(crtc); > struct device *dev = mixer->dev; > struct sti_compositor *compo = dev_get_drvdata(dev); > - struct sti_layer *layer; > > if (!mixer->enabled) > return; > @@ -205,24 +143,6 @@ static void sti_drm_crtc_disable(struct drm_crtc *crtc) > /* Disable Background */ > sti_mixer_set_background_status(mixer, false); > > - /* Disable GDP */ > - layer = to_sti_layer(crtc->primary); > - if (!layer) { > - DRM_ERROR("Cannot find GDP0\n"); > - return; > - } > - > - /* Disable layer at mixer level */ > - if (sti_mixer_set_layer_status(mixer, layer, false)) > - DRM_ERROR("Can not disable %s layer at mixer\n", > - sti_layer_to_str(layer)); > - > - /* Wait a while to be sure that a Vsync event is received */ > - msleep(WAIT_NEXT_VSYNC_MS); > - > - /* Then disable layer itself */ > - sti_layer_disable(layer); > - > drm_crtc_vblank_off(crtc); > > /* Disable pixel clock and compo IP clocks */ > @@ -237,64 +157,44 @@ static void sti_drm_crtc_disable(struct drm_crtc *crtc) > mixer->enabled = false; > } > > -static struct drm_crtc_helper_funcs sti_crtc_helper_funcs = { > - .dpms = sti_drm_crtc_dpms, > - .prepare = sti_drm_crtc_prepare, > - .commit = sti_drm_crtc_commit, > - .mode_fixup = sti_drm_crtc_mode_fixup, > - .mode_set = sti_drm_crtc_mode_set, > - .mode_set_base = sti_drm_crtc_mode_set_base, > - .disable = sti_drm_crtc_disable, > -}; > +static void > +sti_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) > +{ > + sti_drm_crtc_prepare(crtc); > + sti_drm_crtc_mode_set(crtc, &crtc->state->adjusted_mode); > +} > > -static int sti_drm_crtc_page_flip(struct drm_crtc *crtc, > - struct drm_framebuffer *fb, > - struct drm_pending_vblank_event *event, > - uint32_t page_flip_flags) > +static void sti_drm_atomic_begin(struct drm_crtc *crtc) > { > - struct drm_device *drm_dev = crtc->dev; > - struct drm_framebuffer *old_fb; > struct sti_mixer *mixer = to_sti_mixer(crtc); > - unsigned long flags; > - int ret; > > - DRM_DEBUG_KMS("fb %d --> fb %d\n", > - crtc->primary->fb->base.id, fb->base.id); > + if (crtc->state->event) { > + crtc->state->event->pipe = drm_crtc_index(crtc); > > - mutex_lock(&drm_dev->struct_mutex); > + WARN_ON(drm_crtc_vblank_get(crtc) != 0); > > - old_fb = crtc->primary->fb; > - crtc->primary->fb = fb; > - ret = sti_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb); > - if (ret) { > - DRM_ERROR("failed\n"); > - crtc->primary->fb = old_fb; > - goto out; > + mixer->pending_event = crtc->state->event; > + crtc->state->event = NULL; > } > +} > > - if (event) { > - event->pipe = mixer->id; > - > - ret = drm_vblank_get(drm_dev, event->pipe); > - if (ret) { > - DRM_ERROR("Cannot get vblank\n"); > - goto out; > - } > - > - spin_lock_irqsave(&drm_dev->event_lock, flags); > - if (mixer->pending_event) { > - drm_vblank_put(drm_dev, event->pipe); > - ret = -EBUSY; > - } else { > - mixer->pending_event = event; > - } > - spin_unlock_irqrestore(&drm_dev->event_lock, flags); > - } > -out: > - mutex_unlock(&drm_dev->struct_mutex); > - return ret; > +static void sti_drm_atomic_flush(struct drm_crtc *crtc) > +{ > } > > +static struct drm_crtc_helper_funcs sti_crtc_helper_funcs = { > + .dpms = sti_drm_crtc_dpms, > + .prepare = sti_drm_crtc_prepare, > + .commit = sti_drm_crtc_commit, > + .mode_fixup = sti_drm_crtc_mode_fixup, > + .mode_set = drm_helper_crtc_mode_set, > + .mode_set_nofb = sti_drm_crtc_mode_set_nofb, > + .mode_set_base = drm_helper_crtc_mode_set_base, > + .disable = sti_drm_crtc_disable, > + .atomic_begin = sti_drm_atomic_begin, > + .atomic_flush = sti_drm_atomic_flush, > +}; > + > static void sti_drm_crtc_destroy(struct drm_crtc *crtc) > { > DRM_DEBUG_KMS("\n"); > @@ -380,10 +280,13 @@ void sti_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) > EXPORT_SYMBOL(sti_drm_crtc_disable_vblank); > > static struct drm_crtc_funcs sti_crtc_funcs = { > - .set_config = drm_crtc_helper_set_config, > - .page_flip = sti_drm_crtc_page_flip, > + .set_config = drm_atomic_helper_set_config, > + .page_flip = drm_atomic_helper_page_flip, > .destroy = sti_drm_crtc_destroy, > .set_property = sti_drm_crtc_set_property, > + .reset = drm_atomic_helper_crtc_reset, > + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, > }; > > bool sti_drm_crtc_is_main(struct drm_crtc *crtc) > diff --git a/drivers/gpu/drm/sti/sti_drm_drv.c b/drivers/gpu/drm/sti/sti_drm_drv.c > index 5239fa1..59d558b 100644 > --- a/drivers/gpu/drm/sti/sti_drm_drv.c > +++ b/drivers/gpu/drm/sti/sti_drm_drv.c > @@ -12,6 +12,8 @@ > #include <linux/module.h> > #include <linux/of_platform.h> > > +#include <drm/drm_atomic.h> > +#include <drm/drm_atomic_helper.h> > #include <drm/drm_crtc_helper.h> > #include <drm/drm_gem_cma_helper.h> > #include <drm/drm_fb_cma_helper.h> > @@ -28,8 +30,87 @@ > #define STI_MAX_FB_HEIGHT 4096 > #define STI_MAX_FB_WIDTH 4096 > > +static void sti_drm_atomic_schedule(struct sti_drm_private *private, > + struct drm_atomic_state *state) > +{ > + private->commit.state = state; > + schedule_work(&private->commit.work); > +} > + > +static void sti_drm_atomic_complete(struct sti_drm_private *private, > + struct drm_atomic_state *state) > +{ > + struct drm_device *drm = private->drm_dev; > + > + /* > + * Everything below can be run asynchronously without the need to grab > + * any modeset locks at all under one condition: It must be guaranteed > + * that the asynchronous work has either been cancelled (if the driver > + * supports it, which at least requires that the framebuffers get > + * cleaned up with drm_atomic_helper_cleanup_planes()) or completed > + * before the new state gets committed on the software side with > + * drm_atomic_helper_swap_state(). > + * > + * This scheme allows new atomic state updates to be prepared and > + * checked in parallel to the asynchronous completion of the previous > + * update. Which is important since compositors need to figure out the > + * composition of the next frame right after having submitted the > + * current layout. > + */ > + > + drm_atomic_helper_commit_modeset_disables(drm, state); > + drm_atomic_helper_commit_planes(drm, state); > + drm_atomic_helper_commit_modeset_enables(drm, state); > + > + drm_atomic_helper_wait_for_vblanks(drm, state); > + > + drm_atomic_helper_cleanup_planes(drm, state); > + drm_atomic_state_free(state); > +} > + > +static void sti_drm_atomic_work(struct work_struct *work) > +{ > + struct sti_drm_private *private = container_of(work, > + struct sti_drm_private, commit.work); > + > + sti_drm_atomic_complete(private, private->commit.state); > +} > + > +static int sti_drm_atomic_commit(struct drm_device *drm, > + struct drm_atomic_state *state, bool async) > +{ > + struct sti_drm_private *private = drm->dev_private; > + int err; > + > + err = drm_atomic_helper_prepare_planes(drm, state); > + if (err) > + return err; > + > + /* serialize outstanding asynchronous commits */ > + mutex_lock(&private->commit.lock); > + flush_work(&private->commit.work); > + > + /* > + * This is the point of no return - everything below never fails except > + * when the hw goes bonghits. Which means we can commit the new state on > + * the software side now. > + */ > + > + drm_atomic_helper_swap_state(drm, state); > + > + if (async) > + sti_drm_atomic_schedule(private, state); > + else > + sti_drm_atomic_complete(private, state); > + > + mutex_unlock(&private->commit.lock); > + return 0; > +} > + > static struct drm_mode_config_funcs sti_drm_mode_config_funcs = { > .fb_create = drm_fb_cma_create, > + .atomic_check = drm_atomic_helper_check, > + .atomic_commit = sti_drm_atomic_commit, > }; > > static void sti_drm_mode_config_init(struct drm_device *dev) > @@ -61,6 +142,9 @@ static int sti_drm_load(struct drm_device *dev, unsigned long flags) > dev->dev_private = (void *)private; > private->drm_dev = dev; > > + mutex_init(&private->commit.lock); > + INIT_WORK(&private->commit.work, sti_drm_atomic_work); > + > drm_mode_config_init(dev); > drm_kms_helper_poll_init(dev); > > @@ -74,7 +158,7 @@ static int sti_drm_load(struct drm_device *dev, unsigned long flags) > return ret; > } > > - drm_helper_disable_unused_functions(dev); > + drm_mode_config_reset(dev); > > #ifdef CONFIG_DRM_STI_FBDEV > drm_fbdev_cma_init(dev, 32, > diff --git a/drivers/gpu/drm/sti/sti_drm_drv.h b/drivers/gpu/drm/sti/sti_drm_drv.h > index ec5e2eb..c413aa3 100644 > --- a/drivers/gpu/drm/sti/sti_drm_drv.h > +++ b/drivers/gpu/drm/sti/sti_drm_drv.h > @@ -24,6 +24,12 @@ struct sti_drm_private { > struct sti_compositor *compo; > struct drm_property *plane_zorder_property; > struct drm_device *drm_dev; > + > + struct { > + struct drm_atomic_state *state; > + struct work_struct work; > + struct mutex lock; > + } commit; > }; > > #endif > diff --git a/drivers/gpu/drm/sti/sti_drm_plane.c b/drivers/gpu/drm/sti/sti_drm_plane.c > index bb6a293..64d4ed4 100644 > --- a/drivers/gpu/drm/sti/sti_drm_plane.c > +++ b/drivers/gpu/drm/sti/sti_drm_plane.c > @@ -6,6 +6,10 @@ > * License terms: GNU General Public License (GPL), version 2 > */ > > +#include <drm/drmP.h> > +#include <drm/drm_atomic_helper.h> > +#include <drm/drm_plane_helper.h> > + > #include "sti_compositor.h" > #include "sti_drm_drv.h" > #include "sti_drm_plane.h" > @@ -33,9 +37,9 @@ sti_drm_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, > struct sti_mixer *mixer = to_sti_mixer(crtc); > int res; > > - DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s) drm fb:%d\n", > + DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", > crtc->base.id, sti_mixer_to_str(mixer), > - plane->base.id, sti_layer_to_str(layer), fb->base.id); > + plane->base.id, sti_layer_to_str(layer)); > DRM_DEBUG_KMS("(%dx%d)@(%d,%d)\n", crtc_w, crtc_h, crtc_x, crtc_y); > > res = sti_mixer_set_layer_depth(mixer, layer); > @@ -110,7 +114,7 @@ static void sti_drm_plane_destroy(struct drm_plane *plane) > { > DRM_DEBUG_DRIVER("\n"); > > - sti_drm_disable_plane(plane); > + drm_plane_helper_disable(plane); > drm_plane_cleanup(plane); > } > > @@ -133,10 +137,58 @@ static int sti_drm_plane_set_property(struct drm_plane *plane, > } > > static struct drm_plane_funcs sti_drm_plane_funcs = { > - .update_plane = sti_drm_update_plane, > - .disable_plane = sti_drm_disable_plane, > + .update_plane = drm_atomic_helper_update_plane, > + .disable_plane = drm_atomic_helper_disable_plane, > .destroy = sti_drm_plane_destroy, > .set_property = sti_drm_plane_set_property, > + .reset = drm_atomic_helper_plane_reset, > + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, > +}; > + > +static int sti_drm_plane_prepare_fb(struct drm_plane *plane, > + struct drm_framebuffer *fb, > + const struct drm_plane_state *new_state) > +{ > + return 0; > +} > + > +static void sti_drm_plane_cleanup_fb(struct drm_plane *plane, > + struct drm_framebuffer *fb, > + const struct drm_plane_state *old_fb) > +{ > +} > + > +static int sti_drm_plane_atomic_check(struct drm_plane *plane, > + struct drm_plane_state *state) > +{ > + return 0; > +} > + > +static void sti_drm_plane_atomic_update(struct drm_plane *plane, > + struct drm_plane_state *oldstate) > +{ > + struct drm_plane_state *state = plane->state; > + > + sti_drm_update_plane(plane, state->crtc, state->fb, > + state->crtc_x, state->crtc_y, > + state->crtc_w, state->crtc_h, > + state->src_x, state->src_y, > + state->src_w, state->src_h); > +} > + > +static void sti_drm_plane_atomic_disable(struct drm_plane *plane, > + struct drm_plane_state *oldstate) > +{ > + sti_drm_disable_plane(plane); > +} > + > +static const struct drm_plane_helper_funcs sti_drm_plane_helpers_funcs = { > + .prepare_fb = sti_drm_plane_prepare_fb, > + .cleanup_fb = sti_drm_plane_cleanup_fb, > + .atomic_check = sti_drm_plane_atomic_check, > + .atomic_update = sti_drm_plane_atomic_update, > + .atomic_disable = sti_drm_plane_atomic_disable, > }; > > static void sti_drm_plane_attach_zorder_property(struct drm_plane *plane, > @@ -178,11 +230,13 @@ struct drm_plane *sti_drm_plane_init(struct drm_device *dev, > return NULL; > } > > + drm_plane_helper_add(&layer->plane, &sti_drm_plane_helpers_funcs); > + > for (i = 0; i < ARRAY_SIZE(sti_layer_default_zorder); i++) > if (sti_layer_default_zorder[i] == layer->desc) > break; > > - default_zorder = i; > + default_zorder = i + 1; > > if (type == DRM_PLANE_TYPE_OVERLAY) > sti_drm_plane_attach_zorder_property(&layer->plane, > diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c > index aeb5070..a9b678a 100644 > --- a/drivers/gpu/drm/sti/sti_dvo.c > +++ b/drivers/gpu/drm/sti/sti_dvo.c > @@ -11,6 +11,7 @@ > #include <linux/platform_device.h> > > #include <drm/drmP.h> > +#include <drm/drm_atomic_helper.h> > #include <drm/drm_crtc_helper.h> > #include <drm/drm_panel.h> > > @@ -364,10 +365,13 @@ static void sti_dvo_connector_destroy(struct drm_connector *connector) > } > > static struct drm_connector_funcs sti_dvo_connector_funcs = { > - .dpms = drm_helper_connector_dpms, > + .dpms = drm_atomic_helper_connector_dpms, > .fill_modes = drm_helper_probe_single_connector_modes, > .detect = sti_dvo_connector_detect, > .destroy = sti_dvo_connector_destroy, > + .reset = drm_atomic_helper_connector_reset, > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > }; > > static struct drm_encoder *sti_dvo_find_encoder(struct drm_device *dev) > diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c > index a9bbb08..598cd78 100644 > --- a/drivers/gpu/drm/sti/sti_hda.c > +++ b/drivers/gpu/drm/sti/sti_hda.c > @@ -10,6 +10,7 @@ > #include <linux/platform_device.h> > > #include <drm/drmP.h> > +#include <drm/drm_atomic_helper.h> > #include <drm/drm_crtc_helper.h> > > /* HDformatter registers */ > @@ -611,10 +612,13 @@ static void sti_hda_connector_destroy(struct drm_connector *connector) > } > > static struct drm_connector_funcs sti_hda_connector_funcs = { > - .dpms = drm_helper_connector_dpms, > + .dpms = drm_atomic_helper_connector_dpms, > .fill_modes = drm_helper_probe_single_connector_modes, > .detect = sti_hda_connector_detect, > .destroy = sti_hda_connector_destroy, > + .reset = drm_atomic_helper_connector_reset, > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > }; > > static struct drm_encoder *sti_hda_find_encoder(struct drm_device *dev) > diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c > index 1485ade..ae5424b 100644 > --- a/drivers/gpu/drm/sti/sti_hdmi.c > +++ b/drivers/gpu/drm/sti/sti_hdmi.c > @@ -13,6 +13,7 @@ > #include <linux/reset.h> > > #include <drm/drmP.h> > +#include <drm/drm_atomic_helper.h> > #include <drm/drm_crtc_helper.h> > #include <drm/drm_edid.h> > > @@ -663,10 +664,13 @@ static void sti_hdmi_connector_destroy(struct drm_connector *connector) > } > > static struct drm_connector_funcs sti_hdmi_connector_funcs = { > - .dpms = drm_helper_connector_dpms, > + .dpms = drm_atomic_helper_connector_dpms, > .fill_modes = drm_helper_probe_single_connector_modes, > .detect = sti_hdmi_connector_detect, > .destroy = sti_hdmi_connector_destroy, > + .reset = drm_atomic_helper_connector_reset, > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > }; > > static struct drm_encoder *sti_hdmi_find_encoder(struct drm_device *dev) > -- > 1.9.1 > -- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel