diff --git a/src/sna/sna.h b/src/sna/sna.h index cf9a4d57..cc086d75 100644 --- a/src/sna/sna.h +++ b/src/sna/sna.h @@ -242,6 +242,7 @@ struct sna { #define SNA_IS_HOSTED 0x80 #define SNA_PERFORMANCE 0x100 #define SNA_POWERSAVE 0x200 +#define SNA_REDISCOVER 0x40000000 #define SNA_REPROBE 0x80000000 unsigned cpu_features; @@ -281,6 +282,7 @@ struct sna { unsigned num_real_crtc; unsigned num_real_output; unsigned num_fake; + unsigned serial; } mode; struct { @@ -378,6 +380,7 @@ struct sna { bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna); bool sna_mode_fake_init(struct sna *sna, int num_fake); void sna_mode_adjust_frame(struct sna *sna, int x, int y); +extern void sna_mode_discover(struct sna *sna); extern void sna_mode_update(struct sna *sna); extern void sna_mode_reset(struct sna *sna); extern void sna_mode_wakeup(struct sna *sna); diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c index ae072524..a66ca09f 100644 --- a/src/sna/sna_display.c +++ b/src/sna/sna_display.c @@ -47,6 +47,7 @@ #include "backlight.h" #include +#include #include #if XF86_CRTC_VERSION >= 3 @@ -128,7 +129,8 @@ struct sna_property { struct sna_output { int id; - int encoder_idx; + int encoder_id, encoder_idx; + int serial; unsigned int is_panel : 1; @@ -743,14 +745,15 @@ sna_crtc_apply(xf86CrtcPtr crtc) if (output->crtc != crtc) continue; - assert(output->possible_crtcs & (1 << sna_crtc->pipe) || - xf86IsEntityShared(crtc->scrn->entityList[0])); - DBG(("%s: attaching output '%s' %d [%d] to crtc:%d (pipe %d) (possible crtc:%x, possible clones:%x)\n", __FUNCTION__, output->name, i, to_connector_id(output), sna_crtc->id, sna_crtc->pipe, (uint32_t)output->possible_crtcs, (uint32_t)output->possible_clones)); + + assert(output->possible_crtcs & (1 << sna_crtc->pipe) || + xf86IsEntityShared(crtc->scrn->entityList[0])); + output_ids[output_count] = to_connector_id(output); if (++output_count == ARRAY_SIZE(output_ids)) return false; @@ -1799,7 +1802,7 @@ sna_crtc_init__cursor(struct sna *sna, struct sna_crtc *crtc) } static bool -sna_crtc_init(ScrnInfoPtr scrn, int id) +sna_crtc_add(ScrnInfoPtr scrn, int id) { struct sna *sna = to_sna(scrn); xf86CrtcPtr crtc; @@ -2634,24 +2637,98 @@ output_ignored(ScrnInfoPtr scrn, const char *name) return xf86CheckBoolOption(conf->mon_option_lst, "Ignore", 0); } -static bool -sna_output_init(ScrnInfoPtr scrn, int id, drmModeResPtr res) +/* We need to map from kms encoder based possible_clones mask to X output based + * possible clones masking. Note that for SDVO and on Haswell with DP/HDMI we + * can have more than one output hanging off the same encoder. + */ +static void +sna_mode_compute_possible_outputs(struct sna *sna, drmModeResPtr res) { - struct sna *sna = to_sna(scrn); - xf86OutputPtr output; + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + int i, j; + + assert(sna->mode.num_real_output < 32); + assert(sna->mode.num_real_crtc < 32); + + for (i = 0; i < sna->mode.num_real_output; i++) { + xf86OutputPtr output = config->output[i]; + struct sna_output *sna_output = to_sna_output(output); + struct drm_mode_get_encoder enc; + + assert(to_sna_output(output)); + + sna_output->encoder_idx = -1; + output->possible_clones = 0; + + VG_CLEAR(enc); + enc.encoder_id = sna_output->encoder_id; + + if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETENCODER, &enc)) { + DBG(("%s: GETENCODER failed, ret=%d\n", __FUNCTION__, errno)); + continue; + } + + for (j = 0; j < res->count_encoders; j++) { + if (enc.encoder_id == res->encoders[j]) { + sna_output->encoder_idx = j; + break; + } + } + assert(sna_output->encoder_idx != -1); + + output->possible_clones = enc.possible_clones; + } + + /* Convert from encoder numbering to output numbering */ + for (i = 0; i < sna->mode.num_real_output; i++) { + xf86OutputPtr output = config->output[i]; + unsigned mask = output->possible_clones; + unsigned clones = 0; + + assert(to_sna_output(output)); + if (output->possible_clones == 0) + continue; + + for (j = 0; j < sna->mode.num_real_output; j++) { + struct sna_output *sna_output = to_sna_output(config->output[j]); + + if (i == j) + continue; + + if (sna_output->encoder_idx == -1) + continue; + + if (mask & (1 << sna_output->encoder_idx)) + clones |= 1 << j; + } + + output->possible_clones = clones; + + DBG(("%s: updated output '%s' %d [%d] (possible crtc:%x, possible clones:%x)\n", + __FUNCTION__, output->name, i, to_connector_id(output), + (uint32_t)output->possible_crtcs, + (uint32_t)output->possible_clones)); + } +} + +static int +sna_output_add(struct sna *sna, int id, drmModeResPtr res, int serial) +{ + ScrnInfoPtr scrn = sna->scrn; + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); union compat_mode_get_connector compat_conn; struct drm_mode_get_encoder enc; struct drm_mode_modeinfo dummy; struct sna_output *sna_output; + xf86OutputPtr *outputs, output; const char *output_name; char name[32]; - bool ret = false; - int i; - - COMPILE_TIME_ASSERT(sizeof(struct drm_mode_get_connector) <= sizeof(compat_conn.pad)); + int len, i, ret = -1; DBG(("%s(%d)\n", __FUNCTION__, id)); + COMPILE_TIME_ASSERT(sizeof(struct drm_mode_get_connector) <= sizeof(compat_conn.pad)); + VG_CLEAR(compat_conn); VG_CLEAR(enc); @@ -2664,23 +2741,23 @@ sna_output_init(ScrnInfoPtr scrn, int id, drmModeResPtr res) if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCONNECTOR, &compat_conn.conn)) { DBG(("%s: GETCONNECTOR failed, ret=%d\n", __FUNCTION__, errno)); - return false; + return -1; } if (compat_conn.conn.count_encoders != 1) { DBG(("%s: unexpected number [%d] of encoders attached\n", __FUNCTION__, compat_conn.conn.count_encoders)); - return false; + return 0; } if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETENCODER, &enc)) { DBG(("%s: GETENCODER failed, ret=%d\n", __FUNCTION__, errno)); - return false; + return 0; } sna_output = calloc(sizeof(struct sna_output), 1); if (!sna_output) - return false; + return -1; sna_output->num_props = compat_conn.conn.count_props; sna_output->prop_ids = malloc(sizeof(uint32_t)*compat_conn.conn.count_props); @@ -2710,7 +2787,9 @@ sna_output_init(ScrnInfoPtr scrn, int id, drmModeResPtr res) output_name = output_names[compat_conn.conn.connector_type]; else output_name = "UNKNOWN"; - snprintf(name, 32, "%s%d", output_name, compat_conn.conn.connector_type_id); + len = snprintf(name, 32, "%s%d", output_name, compat_conn.conn.connector_type_id); + if (output_ignored(scrn, name)) + return 0; if (xf86IsEntityShared(scrn->entityList[0])) { const char *str; @@ -2718,7 +2797,7 @@ sna_output_init(ScrnInfoPtr scrn, int id, drmModeResPtr res) str = xf86GetOptValString(sna->Options, OPTION_ZAPHOD); if (str && !sna_zaphod_match(str, name)) { DBG(("%s: zaphod mismatch, want %s, have %s\n", __FUNCTION__, str, name)); - ret = true; + ret = 0; goto cleanup; } @@ -2732,22 +2811,39 @@ sna_output_init(ScrnInfoPtr scrn, int id, drmModeResPtr res) } enc.possible_crtcs = 1; - enc.possible_clones = 0; } - output = xf86OutputCreate(scrn, &sna_output_funcs, name); - if (!output) { - /* xf86OutputCreate does not differentiate between - * a failure to allocate the output, and a user request - * to ignore the output. So reconstruct whether the user - * explicitly ignored the output. - */ - ret = output_ignored(scrn, name); - DBG(("%s: create failed, ignored? %d\n", __FUNCTION__, ret)); + output = calloc(1, sizeof(*output) + len + 1); + if (!output) + goto cleanup; + + outputs = realloc(config->output, (config->num_output + 1) * sizeof(output)); + if (outputs == NULL) { + free(output); goto cleanup; } + for (i = config->num_output; i > sna->mode.num_real_output; i--) { + outputs[i] = outputs[i-1]; + assert(outputs[i]->driver_private == NULL); + outputs[i]->possible_clones <<= 1; + } + outputs[i] = output; + sna->mode.num_real_output++; + config->num_output++; + config->output = outputs; + + output->scrn = scrn; + output->funcs = &sna_output_funcs; + output->name = (char *)(output + 1); + memcpy(output->name, name, len + 1); + + output->use_screen_monitor = config->num_output; + xf86OutputUseScreenMonitor(output, !config->num_output); + sna_output->id = compat_conn.conn.connector_id; + sna_output->encoder_id = enc.encoder_id; + sna_output->encoder_idx = -1; sna_output->is_panel = is_panel(compat_conn.conn.connector_type); sna_output->edid_idx = find_property(sna, sna_output, "EDID"); sna_output->dpms_id = find_property_id(sna, sna_output, "DPMS"); @@ -2760,33 +2856,34 @@ sna_output_init(ScrnInfoPtr scrn, int id, drmModeResPtr res) output->subpixel_order = subpixel_conv_table[compat_conn.conn.subpixel]; output->driver_private = sna_output; - for (i = 0; i < res->count_encoders; i++) { - if (enc.encoder_id == res->encoders[i]) { - sna_output->encoder_idx = i; - break; - } - } - if (sna_output->is_panel) sna_output_backlight_init(output); - output->possible_crtcs = enc.possible_crtcs; - output->possible_clones = enc.possible_clones; + output->possible_crtcs = enc.possible_crtcs & ((1 << sna->mode.num_real_crtc) - 1); output->interlaceAllowed = TRUE; - /* stash the active CRTC id for our probe function */ - output->crtc = NULL; - if (compat_conn.conn.connection != DRM_MODE_DISCONNECTED) - output->crtc = (void *)(uintptr_t)enc.crtc_id; + if (serial) { + output->randr_output = RROutputCreate(xf86ScrnToScreen(scrn), name, len, output); + if (output->randr_output == NULL) + goto cleanup; - DBG(("%s: created output '%s' %d (possible crtc:%x, possible clones:%x), edid=%d, dpms=%d, crtc=%lu\n", + sna_output_create_resources(output); + RRPostPendingProperties(output->randr_output); + + sna_output->serial = serial; + } else { + /* stash the active CRTC id for our probe function */ + if (compat_conn.conn.connection != DRM_MODE_DISCONNECTED) + output->crtc = (void *)(uintptr_t)enc.crtc_id; + } + + DBG(("%s: created output '%s' %d (possible crtc:%x), serial=%d, edid=%d, dpms=%d, crtc=%lu\n", __FUNCTION__, name, id, (uint32_t)output->possible_crtcs, - (uint32_t)output->possible_clones, - sna_output->edid_idx, sna_output->dpms_id, + serial, sna_output->edid_idx, sna_output->dpms_id, (unsigned long)(uintptr_t)output->crtc)); - return true; + return 1; cleanup: free(sna_output->prop_ids); @@ -2795,41 +2892,92 @@ cleanup: return ret; } -/* We need to map from kms encoder based possible_clones mask to X output based - * possible clones masking. Note that for SDVO and on Haswell with DP/HDMI we - * can have more than one output hanging off the same encoder. - */ -static void -sna_mode_compute_possible_outputs(ScrnInfoPtr scrn) +static void sna_output_del(xf86OutputPtr output) { + ScrnInfoPtr scrn = output->scrn; xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); - unsigned crtc_mask; - int i, j; + int i; - assert(config->num_output < 32); - assert(config->num_crtc < 32); + assert(to_sna_output(output)); - crtc_mask = (1 << config->num_crtc) - 1; + RROutputDestroy(output->randr_output); + sna_output_destroy(output); - /* Convert from encoder numbering to output numbering */ - for (i = 0; i < config->num_output; i++) { - xf86OutputPtr output = config->output[i]; - unsigned mask = output->possible_clones; - unsigned clones = 0; + while (output->probed_modes) + xf86DeleteMode(&output->probed_modes, output->probed_modes); - for (j = 0; j < config->num_output; j++) { - if (mask & (1 << to_sna_output(config->output[j])->encoder_idx)) - clones |= 1 << j; - } + free(output); - output->possible_clones = clones; - output->possible_crtcs &= crtc_mask; + for (i = 0; i < config->num_output; i++) + if (config->output[i] == output) + break; + assert(i < to_sna(scrn)->mode.num_real_output); - DBG(("%s: updated output '%s' %d [%d] (possible crtc:%x, possible clones:%x)\n", - __FUNCTION__, output->name, i, to_connector_id(output), - (uint32_t)output->possible_crtcs, - (uint32_t)output->possible_clones)); + for (; i < config->num_output; i++) { + config->output[i] = config->output[i+1]; + config->output[i]->possible_clones >>= 1; } + config->num_output--; + to_sna(scrn)->mode.num_real_output--; +} + +static void sort_randr_outputs(struct sna *sna, ScreenPtr screen) +{ + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + rrScrPriv(screen); + int i; + + assert(pScrPriv->numOutputs = config->num_output); + for (i = 0; i < config->num_output; i++) { + assert(config->output[i]->randr_output); + pScrPriv->outputs[i] = config->output[i]->randr_output; + } +} + +void sna_mode_discover(struct sna *sna) +{ + ScreenPtr screen = xf86ScrnToScreen(sna->scrn); + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + drmModeResPtr res; + int i, j, serial = ++sna->mode.serial; + bool changed = false; + + res = drmModeGetResources(sna->kgem.fd); + if (res == NULL) + return; + + assert(res->count_crtcs == sna->mode.num_real_crtc); + assert(sna->mode.max_crtc_width == res->max_width); + assert(sna->mode.max_crtc_height == res->max_height); + + for (i = 0; i < res->count_connectors; i++) { + for (j = 0; j < sna->mode.num_real_output; j++) { + if (to_sna_output(config->output[j])->id == res->connectors[i]) { + to_sna_output(config->output[j])->serial = serial; + break; + } + } + if (j == sna->mode.num_real_output) + changed |= sna_output_add(sna, res->connectors[i], res, serial) > 0; + } + + for (i = 0; i < sna->mode.num_real_output; i++) { + xf86OutputPtr output = config->output[i]; + if (to_sna_output(output)->serial != serial) { + sna_output_del(output); + changed = true; + } + } + + if (changed) { + sna_mode_compute_possible_outputs(sna, res); + + /* Reorder user visible listing */ + sort_randr_outputs(sna, screen); + xf86RandR12TellChanged(screen); + } + + drmModeFreeResources(res); } static void copy_front(struct sna *sna, PixmapPtr old, PixmapPtr new) @@ -4254,18 +4402,18 @@ bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna) xf86_config->xf86_crtc_notify = sna_crtc_config_notify; for (i = 0; i < res->count_crtcs; i++) - if (!sna_crtc_init(scrn, res->crtcs[i])) + if (!sna_crtc_add(scrn, res->crtcs[i])) return false; + sna->mode.num_real_crtc = xf86_config->num_crtc; + for (i = 0; i < res->count_connectors; i++) - if (!sna_output_init(scrn, res->connectors[i], res)) + if (sna_output_add(sna, res->connectors[i], res, 0) < 0) return false; - sna->mode.num_real_crtc = xf86_config->num_crtc; sna->mode.num_real_output = xf86_config->num_output; - if (!xf86IsEntityShared(scrn->entityList[0])) - sna_mode_compute_possible_outputs(scrn); + sna_mode_compute_possible_outputs(sna, res); sna->mode.max_crtc_width = res->max_width; sna->mode.max_crtc_height = res->max_height; diff --git a/src/sna/sna_driver.c b/src/sna/sna_driver.c index 673e2a5c..80ff33bc 100644 --- a/src/sna/sna_driver.c +++ b/src/sna/sna_driver.c @@ -689,7 +689,7 @@ sna_handle_uevents(int fd, void *closure) ScrnInfoPtr scrn = closure; struct sna *sna = to_sna(scrn); struct udev_device *dev; - const char *hotplug; + const char *str; struct stat s; dev_t udev_devnum; @@ -700,21 +700,13 @@ sna_handle_uevents(int fd, void *closure) return; udev_devnum = udev_device_get_devnum(dev); - if (fstat(sna->kgem.fd, &s)) { + if (fstat(sna->kgem.fd, &s) || memcmp(&s.st_rdev, &udev_devnum, sizeof (dev_t))) { udev_device_unref(dev); return; } - /* - * Check to make sure this event is directed at our - * device (by comparing dev_t values), then make - * sure it's a hotplug event (HOTPLUG=1) - */ - - hotplug = udev_device_get_property_value(dev, "HOTPLUG"); - - if (memcmp(&s.st_rdev, &udev_devnum, sizeof (dev_t)) == 0 && - hotplug && atoi(hotplug) == 1) { + str = udev_device_get_property_value(dev, "HOTPLUG"); + if (str && atoi(str) == 1) { DBG(("%s: hotplug event (vtSema?=%d)\n", __FUNCTION__, sna->scrn->vtSema)); if (sna->scrn->vtSema) { @@ -724,6 +716,16 @@ sna_handle_uevents(int fd, void *closure) sna->flags |= SNA_REPROBE; } + str = udev_device_get_property_value(dev, "DISCOVER"); + if (str && atoi(str) == 1) { + DBG(("%s: discover event (vtSema?=%d)\n", + __FUNCTION__, sna->scrn->vtSema)); + if (sna->scrn->vtSema) + sna_mode_discover(sna); + else + sna->flags |= SNA_REDISCOVER; + } + udev_device_unref(dev); } @@ -1094,6 +1096,13 @@ static Bool sna_enter_vt(VT_FUNC_ARGS_DECL) if (!sna_set_desired_mode(sna)) return FALSE; + if (sna->flags & SNA_REDISCOVER) { + DBG(("%s: reporting deferred discover event\n", + __FUNCTION__)); + sna_mode_discover(sna); + sna->flags &= ~SNA_REDISCOVER; + } + if (sna->flags & SNA_REPROBE) { DBG(("%s: reporting deferred hotplug event\n", __FUNCTION__));