On Mon, Aug 05, 2013 at 02:45:26PM +0300, Imre Deak wrote: > Iterate through all valid/invalid crtc/connector combinations. At the > moment only clone configurations are tested as the single output cases > are tested already by testdisplay. Also from combinations where all > connectors are on the same crtc (clone-single-crtc) only those are > tested that are invalid, as I haven't found any machine that supports > these (have to be GT2 with dvo and vga output). > > For configurations with one crtc per connector the FBs are per-crtc atm. > > Signed-off-by: Imre Deak <imre.deak@xxxxxxxxx> lgtm. Please push as soon as we've figured out what to do with patch 3. I wonder whether we shouldn't change our DP code a bit and force the port into normal mode (with some conservative link training values) even when link training fails. Contrary to fdi links I think the pixels would still flow and so would allow us to exercise more codepaths. But that's for another time I guess. Depending upon how the kms_ tests shape up we might want to extract a bit more code from here, but again we can do that later. Thanks, Daniel > --- > tests/.gitignore | 1 + > tests/Makefile.am | 1 + > tests/kms_setmode.c | 739 ++++++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 741 insertions(+) > create mode 100644 tests/kms_setmode.c > > diff --git a/tests/.gitignore b/tests/.gitignore > index 451ab2d..8303526 100644 > --- a/tests/.gitignore > +++ b/tests/.gitignore > @@ -90,6 +90,7 @@ getstats > getversion > kms_flip > kms_render > +kms_setmode > prime_nv_api > prime_nv_pcopy > prime_nv_test > diff --git a/tests/Makefile.am b/tests/Makefile.am > index a59c25f..36caa2a 100644 > --- a/tests/Makefile.am > +++ b/tests/Makefile.am > @@ -39,6 +39,7 @@ TESTS_progs_M = \ > gem_write_read_ring_switch \ > kms_flip \ > kms_render \ > + kms_setmode \ > $(NOUVEAU_TESTS_M) \ > prime_self_import \ > $(NULL) > diff --git a/tests/kms_setmode.c b/tests/kms_setmode.c > new file mode 100644 > index 0000000..8a1973d > --- /dev/null > +++ b/tests/kms_setmode.c > @@ -0,0 +1,739 @@ > +/* > + * 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 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: > + * Imre Deak <imre.deak@xxxxxxxxx> > + */ > +#ifdef HAVE_CONFIG_H > +#include "config.h" > +#endif > + > +#include <assert.h> > +#include <cairo.h> > +#include <errno.h> > +#include <stdint.h> > +#include <unistd.h> > +#include <string.h> > +#include <getopt.h> > +#include <sys/time.h> > + > +#include "drm_fourcc.h" > +#include "drmtest.h" > +#include "intel_bufmgr.h" > +#include "intel_batchbuffer.h" > +#include "intel_gpu_tools.h" > + > +#define MAX_CONNECTORS 10 > +#define MAX_CRTCS 3 > + > +/* max combinations with repetitions */ > +#define MAX_COMBINATION_COUNT \ > + (MAX_CONNECTORS * MAX_CONNECTORS * MAX_CONNECTORS) > +#define MAX_COMBINATION_ELEMS MAX_CRTCS > + > +static int drm_fd; > +static int filter_test_id; > +static bool dry_run; > + > +const drmModeModeInfo mode_640_480 = { > + .name = "640x480", > + .vrefresh = 60, > + .clock = 25200, > + > + .hdisplay = 640, > + .hsync_start = 656, > + .hsync_end = 752, > + .htotal = 800, > + > + .vdisplay = 480, > + .vsync_start = 490, > + .vsync_end = 492, > + .vtotal = 525, > + > + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, > +}; > + > +enum test_flags { > + TEST_INVALID = 0x01, > + TEST_CLONE = 0x02, > + TEST_SINGLE_CRTC_CLONE = 0x04, > + TEST_EXCLUSIVE_CRTC_CLONE = 0x08, > +}; > + > +struct test_config { > + const char *name; > + enum test_flags flags; > + drmModeRes *resources; > +}; > + > +struct connector_config { > + drmModeConnector *connector; > + int crtc_idx; > + bool connected; > + drmModeModeInfo default_mode; > +}; > + > +struct crtc_config { > + int crtc_idx; > + int crtc_id; > + int pipe_id; > + int connector_count; > + struct connector_config *cconfs; > + struct kmstest_fb fb_info; > + drmModeModeInfo mode; > +}; > + > +static bool drm_mode_equal(drmModeModeInfo *m1, drmModeModeInfo *m2) > +{ > +#define COMP(x) do { if (m1->x != m2->x) return false; } while (0) > + COMP(vrefresh); > + COMP(clock); > + COMP(hdisplay); > + COMP(hsync_start); > + COMP(hsync_end); > + COMP(htotal); > + COMP(vdisplay); > + COMP(vsync_start); > + COMP(vsync_end); > + COMP(vtotal); > + COMP(flags); > + > + return true; > +} > + > +static bool connector_supports_mode(drmModeConnector *connector, > + drmModeModeInfo *mode) > +{ > + int i; > + > + for (i = 0; i < connector->count_modes; i++) > + if (drm_mode_equal(&connector->modes[i], mode)) > + return true; > + > + return false; > +} > + > +static bool crtc_supports_mode(struct crtc_config *crtc, drmModeModeInfo *mode) > +{ > + int i; > + > + for (i = 0; i < crtc->connector_count; i++) { > + if (!connector_supports_mode(crtc->cconfs[i].connector, mode)) > + return false; > + } > + > + return true; > +} > + > +static int paint_fb(struct kmstest_fb *fb, const char *test_name, > + const char **crtc_str, int crtc_count, int current_crtc_idx) > +{ > + double x, y; > + cairo_t *cr; > + int i; > + > + cr = kmstest_get_cairo_ctx(drm_fd, fb); > + if (!cr) > + return -1; > + > + kmstest_paint_test_pattern(cr, fb->width, fb->height); > + > + cairo_select_font_face(cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL, > + CAIRO_FONT_WEIGHT_NORMAL); > + cairo_move_to(cr, fb->width / 2, fb->height / 2); > + cairo_set_font_size(cr, 24); > + kmstest_cairo_printf_line(cr, align_hcenter, 40, "%s", test_name); > + > + cairo_get_current_point(cr, &x, &y); > + cairo_move_to(cr, 60, y); > + > + for (i = 0; i < crtc_count; i++) { > + if (i == current_crtc_idx) { > + cairo_get_current_point(cr, &x, &y); > + cairo_move_to(cr, x - 20, y); > + kmstest_cairo_printf_line(cr, align_right, 20, "X"); > + cairo_move_to(cr, x, y); > + } > + kmstest_cairo_printf_line(cr, align_left, 20, "%s", > + crtc_str[i]); > + } > + > + return 0; > +} > + > +static void create_fb_for_crtc(struct crtc_config *crtc, > + struct kmstest_fb *fb_info) > +{ > + int width; > + int height; > + int bpp; > + int depth; > + bool enable_tiling; > + int fb_id; > + > + bpp = 32; > + depth = 24; > + width = crtc->mode.hdisplay; > + height = crtc->mode.vdisplay; > + enable_tiling = false; > + fb_id = kmstest_create_fb(drm_fd, width, height, bpp, depth, > + enable_tiling, fb_info); > + assert(fb_id > 0); > +} > + > +static void get_mode_for_crtc(struct crtc_config *crtc, > + drmModeModeInfo *mode_ret) > +{ > + drmModeModeInfo mode; > + int i; > + > + /* > + * First try to select a default mode that is supported by all > + * connectors. > + */ > + for (i = 0; i < crtc->connector_count; i++) { > + mode = crtc->cconfs[i].default_mode; > + if (crtc_supports_mode(crtc, &mode)) > + goto found; > + } > + > + /* > + * Then just fall back to find any that is supported by all > + * connectors. > + */ > + for (i = 0; i < crtc->cconfs[0].connector->count_modes; i++) { > + mode = crtc->cconfs[0].connector->modes[i]; > + if (crtc_supports_mode(crtc, &mode)) > + goto found; > + } > + > + /* > + * If none is found then just pick the default mode of the first > + * connector and hope the other connectors can support it by scaling > + * etc. > + */ > + mode = crtc->cconfs[0].default_mode; > +found: > + *mode_ret = mode; > +} > + > +static int get_encoder_idx(drmModeRes *resources, drmModeEncoder *encoder) > +{ > + int i; > + > + for (i = 0; i < resources->count_encoders; i++) > + if (resources->encoders[i] == encoder->encoder_id) > + return i; > + assert(0); > +} > + > +static void get_crtc_config_str(struct crtc_config *crtc, char *buf, > + size_t buf_size) > +{ > + int pos; > + int i; > + > + pos = snprintf(buf, buf_size, > + "CRTC[%d] [Pipe %s] Mode: %s@%dHz Connectors: ", > + crtc->crtc_id, kmstest_pipe_str(crtc->pipe_id), > + crtc->mode.name, crtc->mode.vrefresh); > + if (pos > buf_size) > + return; > + for (i = 0; i < crtc->connector_count; i++) { > + drmModeConnector *connector = crtc->cconfs[i].connector; > + > + pos += snprintf(&buf[pos], buf_size - pos, > + "%s%s-%d[%d]%s", i ? ", " : "", > + kmstest_connector_type_str(connector->connector_type), > + connector->connector_type_id, connector->connector_id, > + crtc->cconfs[i].connected ? "" : " (NC)"); > + if (pos > buf_size) > + return; > + } > +} > + > +static void setup_crtcs(drmModeRes *resources, struct connector_config *cconf, > + int connector_count, struct crtc_config *crtcs, > + int *crtc_count_ret, bool *config_valid_ret) > +{ > + struct crtc_config *crtc; > + int crtc_count; > + bool config_valid; > + int i; > + > + i = 0; > + crtc_count = 0; > + crtc = crtcs; > + config_valid = true; > + > + while (i < connector_count) { > + drmModeCrtc *drm_crtc; > + unsigned long encoder_mask; > + int j; > + > + assert(crtc_count < MAX_CRTCS); > + > + crtc->crtc_idx = cconf[i].crtc_idx; > + drm_crtc = drmModeGetCrtc(drm_fd, > + resources->crtcs[crtc->crtc_idx]); > + crtc->crtc_id = drm_crtc->crtc_id; > + drmModeFreeCrtc(drm_crtc); > + crtc->pipe_id = kmstest_get_pipe_from_crtc_id(drm_fd, > + crtc->crtc_id); > + > + crtc->connector_count = 1; > + for (j = i + 1; j < connector_count; j++) > + if (cconf[j].crtc_idx == crtc->crtc_idx) > + crtc->connector_count++; > + > + crtc->cconfs = malloc(sizeof(*crtc->cconfs) * > + crtc->connector_count); > + assert(crtc->cconfs); > + > + encoder_mask = 0; > + for (j = 0; j < crtc->connector_count; j++) { > + drmModeConnector *connector; > + drmModeEncoder *encoder; > + > + crtc->cconfs[j] = cconf[i + j]; > + connector = cconf[i + j].connector; > + > + /* Intel connectors have only a single encoder */ > + assert(connector->count_encoders == 1); > + encoder = drmModeGetEncoder(drm_fd, > + connector->encoders[0]); > + assert(encoder); > + > + config_valid &= !!(encoder->possible_crtcs & > + (1 << crtc->crtc_idx)); > + > + encoder_mask |= 1 << get_encoder_idx(resources, > + encoder); > + config_valid &= !(encoder_mask & > + ~encoder->possible_clones); > + > + drmModeFreeEncoder(encoder); > + } > + get_mode_for_crtc(crtc, &crtc->mode); > + create_fb_for_crtc(crtc, &crtc->fb_info); > + > + i += crtc->connector_count; > + crtc_count++; > + crtc++; > + } > + > + *crtc_count_ret = crtc_count; > + *config_valid_ret = config_valid; > +} > + > +static void cleanup_crtcs(struct crtc_config *crtcs, int crtc_count) > +{ > + int i; > + > + for (i = 0; i < crtc_count; i++) { > + free(crtcs[i].cconfs); > + } > +} > + > +static uint32_t *get_connector_ids(struct crtc_config *crtc) > +{ > + uint32_t *ids; > + int i; > + > + ids = malloc(sizeof(*ids) * crtc->connector_count); > + assert(ids); > + for (i = 0; i < crtc->connector_count; i++) > + ids[i] = crtc->cconfs[i].connector->connector_id; > + > + return ids; > +} > + > +static void test_crtc_config(const struct test_config *tconf, > + struct crtc_config *crtcs, int crtc_count) > +{ > + char str_buf[MAX_CRTCS][1024]; > + const char *crtc_strs[MAX_CRTCS]; > + struct crtc_config *crtc; > + static int test_id; > + bool config_failed = false; > + bool connector_connected = false; > + int ret = 0; > + int i; > + > + test_id++; > + > + if (filter_test_id && filter_test_id != test_id) > + return; > + > + printf(" Test id#%d CRTC count %d\n", test_id, crtc_count); > + > + for (i = 0; i < crtc_count; i++) { > + get_crtc_config_str(&crtcs[i], str_buf[i], sizeof(str_buf[i])); > + crtc_strs[i] = &str_buf[i][0]; > + } > + > + if (dry_run) { > + for (i = 0; i < crtc_count; i++) > + printf(" %s\n", crtc_strs[i]); > + return; > + } > + > + for (i = 0; i < crtc_count; i++) { > + uint32_t *ids; > + int j; > + > + crtc = &crtcs[i]; > + > + printf(" %s\n", crtc_strs[i]); > + > + create_fb_for_crtc(crtc, &crtc->fb_info); > + paint_fb(&crtc->fb_info, tconf->name, crtc_strs, crtc_count, i); > + > + ids = get_connector_ids(crtc); > + ret = drmModeSetCrtc(drm_fd, crtc->crtc_id, > + crtc->fb_info.fb_id, 0, 0, ids, > + crtc->connector_count, &crtc->mode); > + free(ids); > + > + if (ret < 0) { > + assert(errno == EINVAL); > + config_failed = true; > + } > + > + for (j = 0; j < crtc->connector_count; j++) > + connector_connected |= crtc->cconfs[j].connected; > + } > + > + assert(config_failed == !!(tconf->flags & TEST_INVALID)); > + > + if (ret == 0 && connector_connected && !(tconf->flags & TEST_INVALID)) > + sleep(5); > + > + for (i = 0; i < crtc_count; i++) { > + if (crtcs[i].fb_info.fb_id) { > + drmModeSetCrtc(drm_fd, crtcs[i].crtc_id, 0, 0, 0, NULL, > + 0, NULL); > + drmModeRmFB(drm_fd, crtcs[i].fb_info.fb_id); > + crtcs[i].fb_info.fb_id = 0; > + } > + } > + > + return; > +} > + > +static void test_one_combination(const struct test_config *tconf, > + struct connector_config *cconfs, > + int connector_count) > +{ > + struct crtc_config crtcs[MAX_CRTCS]; > + int crtc_count; > + bool config_valid; > + > + setup_crtcs(tconf->resources, cconfs, connector_count, crtcs, > + &crtc_count, &config_valid); > + > + if (config_valid == !(tconf->flags & TEST_INVALID)) > + test_crtc_config(tconf, crtcs, crtc_count); > + > + cleanup_crtcs(crtcs, crtc_count); > +} > + > +static int assign_crtc_to_connectors(const struct test_config *tconf, > + int *crtc_idxs, int connector_count, > + struct connector_config *cconfs) > +{ > + unsigned long crtc_idx_mask; > + int i; > + > + crtc_idx_mask = 0; > + for (i = 0; i < connector_count; i++) { > + int crtc_idx = crtc_idxs[i]; > + > + if ((tconf->flags & TEST_SINGLE_CRTC_CLONE) && > + crtc_idx_mask & ~(1 << crtc_idx)) > + return -1; > + > + if ((tconf->flags & TEST_EXCLUSIVE_CRTC_CLONE) && > + crtc_idx_mask & (1 << crtc_idx)) > + return -1; > + > + crtc_idx_mask |= 1 << crtc_idx; > + > + cconfs[i].crtc_idx = crtc_idx; > + } > + > + return 0; > +} > + > +static int get_one_connector(drmModeRes *resources, int connector_id, > + struct connector_config *cconf) > +{ > + drmModeConnector *connector; > + drmModeModeInfo mode; > + > + connector = drmModeGetConnector(drm_fd, connector_id); > + assert(connector); > + cconf->connector = connector; > + > + cconf->connected = connector->connection == DRM_MODE_CONNECTED; > + > + /* > + * For DP/eDP we need a connected sink, since mode setting depends > + * on successful link training and retrieved DPCD parameters. > + */ > + switch (connector->connector_type) { > + case DRM_MODE_CONNECTOR_DisplayPort: > + case DRM_MODE_CONNECTOR_eDP: > + if (!cconf->connected) { > + drmModeFreeConnector(connector); > + return -1; > + } > + } > + > + if (cconf->connected) { > + if (kmstest_get_connector_default_mode(drm_fd, connector, > + &mode) < 0) > + mode = mode_640_480; > + } else { > + mode = mode_640_480; > + } > + > + cconf->default_mode = mode; > + > + return 0; > +} > + > +static int get_connectors(drmModeRes *resources, int *connector_idxs, > + int connector_count, struct connector_config *cconfs) > +{ > + int i; > + > + for (i = 0; i < connector_count; i++) { > + int connector_idx; > + int connector_id; > + > + connector_idx = connector_idxs[i]; > + assert(connector_idx < resources->count_connectors); > + connector_id = resources->connectors[connector_idx]; > + > + if (get_one_connector(resources, connector_id, &cconfs[i]) < 0) > + goto err; > + > + } > + > + return 0; > + > +err: > + while (i--) > + drmModeFreeConnector(cconfs[i].connector); > + > + return -1; > +} > + > +static void free_connectors(struct connector_config *cconfs, > + int connector_count) > +{ > + int i; > + > + for (i = 0; i < connector_count; i++) > + drmModeFreeConnector(cconfs[i].connector); > +} > + > +struct combination { > + int elems[MAX_COMBINATION_ELEMS]; > +}; > + > +struct combination_set { > + int count; > + struct combination items[MAX_COMBINATION_COUNT]; > +}; > + > +/* > + * Get all possible selection of k elements from n elements with or without > + * repetitions. > + */ > +static void iterate_combinations(int n, int k, bool allow_repetitions, > + int depth, int base, struct combination *comb, > + struct combination_set *set) > +{ > + int v; > + > + if (!k) { > + assert(set->count < ARRAY_SIZE(set->items)); > + set->items[set->count++] = *comb; > + return; > + } > + > + for (v = base; v < n; v++) { > + comb->elems[depth] = v; > + iterate_combinations(n, k - 1, allow_repetitions, > + depth + 1, allow_repetitions ? 0 : v + 1, > + comb, set); > + } > + > +} > + > +static void get_combinations(int n, int k, bool allow_repetitions, > + struct combination_set *set) > +{ > + struct combination comb; > + > + assert(k <= ARRAY_SIZE(set->items[0].elems)); > + set->count = 0; > + iterate_combinations(n, k, allow_repetitions, 0, 0, &comb, set); > +} > + > +static void test_combinations(const struct test_config *tconf, > + int connector_count) > +{ > + struct combination_set connector_combs; > + struct combination_set crtc_combs; > + struct connector_config *cconfs; > + int i; > + > + get_combinations(tconf->resources->count_connectors, connector_count, > + false, &connector_combs); > + get_combinations(tconf->resources->count_crtcs, connector_count, > + true, &crtc_combs); > + > + printf("Testing: %s %d connector combinations\n", tconf->name, > + connector_count); > + for (i = 0; i < connector_combs.count; i++) { > + int *connector_idxs; > + int ret; > + int j; > + > + cconfs = malloc(sizeof(*cconfs) * connector_count); > + assert(cconfs); > + > + connector_idxs = &connector_combs.items[i].elems[0]; > + ret = get_connectors(tconf->resources, connector_idxs, > + connector_count, cconfs); > + if (ret < 0) > + goto free_cconfs; > + > + for (j = 0; j < crtc_combs.count; j++) { > + int *crtc_idxs = &crtc_combs.items[j].elems[0]; > + ret = assign_crtc_to_connectors(tconf, crtc_idxs, > + connector_count, > + cconfs); > + if (ret < 0) > + continue; > + > + test_one_combination(tconf, cconfs, connector_count); > + } > + > + free_connectors(cconfs, connector_count); > +free_cconfs: > + free(cconfs); > + } > +} > + > +static void run_test(const struct test_config *tconf) > +{ > + int connector_num; > + > + connector_num = tconf->flags & TEST_CLONE ? 2 : 1; > + for (; connector_num <= tconf->resources->count_crtcs; connector_num++) > + test_combinations(tconf, connector_num); > +} > + > +static int opt_handler(int opt, int opt_index) > +{ > + switch (opt) { > + case 'd': > + dry_run = true; > + break; > + case 't': > + filter_test_id = atoi(optarg); > + break; > + default: > + assert(0); > + } > + > + return 0; > +} > + > +int main(int argc, char **argv) > +{ > + const struct { > + enum test_flags flags; > + const char *name; > + } tests[] = { > + { TEST_INVALID | TEST_CLONE | TEST_SINGLE_CRTC_CLONE, > + "invalid-clone-single-crtc" }, > + { TEST_INVALID | TEST_CLONE | TEST_EXCLUSIVE_CRTC_CLONE, > + "invalid-clone-exclusive-crtc" }, > + { TEST_CLONE | TEST_EXCLUSIVE_CRTC_CLONE, > + "clone-exclusive-crtc" }, > + }; > + const char *help_str = > + " -d\t\tDon't run any test, only print what would be done.\n" > + " -t <test id>\tRun only the test with this id."; > + drmModeRes *resources; > + int i; > + int ret; > + > + ret = drmtest_subtest_init_parse_opts(argc, argv, "dt:", NULL, > + help_str, opt_handler); > + if (ret < 0) > + return ret == -2 ? 0 : ret; > + > + drmtest_skip_on_simulation(); > + > + if (dry_run && filter_test_id) { > + fprintf(stderr, "only one of -d and -t is accepted\n"); > + exit(1); > + } > + > + if (dry_run && drmtest_only_list_subtests()) { > + fprintf(stderr, > + "only one of -d and --list-subtests is accepted\n"); > + exit(1); > + } > + > + if (drmtest_only_list_subtests()) { > + for (i = 0; i < ARRAY_SIZE(tests); i++) > + drmtest_run_subtest(tests[i].name); > + return 0; > + } > + > + drm_fd = drm_open_any(); > + do_or_die(drmtest_set_vt_graphics_mode()); > + > + resources = drmModeGetResources(drm_fd); > + assert(resources); > + > + for (i = 0; i < ARRAY_SIZE(tests); i++) { > + if (drmtest_run_subtest(tests[i].name)) { > + struct test_config tconf = { > + .flags = tests[i].flags, > + .name = tests[i].name, > + .resources = resources, > + }; > + run_test(&tconf); > + } > + } > + > + drmModeFreeResources(resources); > + > + close(drm_fd); > + > + return 0; > +} > -- > 1.8.3.2 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/intel-gfx -- Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx