sna: Allow connectors without an associated encoder

In the future, we may see a setup where connectors are created without
being attached to a specific encoder, instead associated with multiple
generic encoders - such as DP MST sink devices. To handle this, we need
to recheck their active encoder at runtime after branch configuration
events (or possibly hotplug).

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
Chris Wilson 2014-05-06 07:37:59 +01:00
parent ea602f7e10
commit f62dfd8f51
1 changed files with 124 additions and 59 deletions

View File

@ -129,9 +129,11 @@ struct sna_property {
struct sna_output {
int id;
int encoder_id, encoder_idx;
int serial;
unsigned possible_encoders;
unsigned attached_encoders;
unsigned int is_panel : 1;
uint32_t edid_idx;
@ -158,6 +160,11 @@ struct sna_output {
};
inline static unsigned count_to_mask(int x)
{
return (1 << x) - 1;
}
static inline struct sna_output *to_sna_output(xf86OutputPtr output)
{
return output->driver_private;
@ -2640,14 +2647,75 @@ output_ignored(ScrnInfoPtr scrn, const char *name)
return xf86CheckBoolOption(conf->mon_option_lst, "Ignore", 0);
}
static bool
gather_encoders(struct sna *sna,
drmModeResPtr res,
uint32_t id, int count,
struct drm_mode_get_encoder *out)
{
union compat_mode_get_connector compat_conn;
struct drm_mode_modeinfo dummy;
struct drm_mode_get_encoder enc;
uint32_t *ids = NULL;
VG_CLEAR(compat_conn);
memset(out, 0, sizeof(*out));
do {
free(ids);
ids = malloc(sizeof(*ids) * count);
if (ids == 0)
return false;
compat_conn.conn.connector_id = id;
compat_conn.conn.count_props = 0;
compat_conn.conn.count_modes = 1; /* skip detect */
compat_conn.conn.modes_ptr = (uintptr_t)&dummy;
compat_conn.conn.count_encoders = count;
compat_conn.conn.encoders_ptr = (uintptr_t)ids;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCONNECTOR, &compat_conn.conn)) {
DBG(("%s: GETCONNECTOR[%d] failed, ret=%d\n", __FUNCTION__, id, errno));
compat_conn.conn.count_encoders = count = 0;
}
if (count == compat_conn.conn.count_encoders)
break;
count = compat_conn.conn.count_encoders;
} while (1);
for (count = 0; count < compat_conn.conn.count_encoders; count++) {
enc.encoder_id = ids[count];
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETENCODER, &enc)) {
DBG(("%s: GETENCODER[%d] failed, ret=%d\n", __FUNCTION__, ids[count], errno));
count = 0;
break;
}
out->possible_crtcs |= enc.possible_crtcs;
out->possible_clones |= enc.possible_clones;
for (id = 0; id < res->count_encoders; id++) {
if (enc.encoder_id == res->encoders[id]) {
out->crtc_id |= 1 << id;
break;
}
}
}
free(ids);
return count > 0;
}
/* 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)
sna_mode_compute_possible_outputs(struct sna *sna)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
int encoder_mask[32];
int i, j;
assert(sna->mode.num_real_output < 32);
@ -2656,55 +2724,25 @@ sna_mode_compute_possible_outputs(struct sna *sna, drmModeResPtr res)
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));
assert(sna_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;
output->possible_clones = sna_output->possible_encoders;
encoder_mask[i] = sna_output->attached_encoders;
}
/* 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;
unsigned clones;
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 = 0;
for (j = 0; j < sna->mode.num_real_output; j++)
if (i != j && output->possible_clones & encoder_mask[j])
clones |= 1 << j;
}
output->possible_clones = clones;
DBG(("%s: updated output '%s' %d [%d] (possible crtc:%x, possible clones:%x)\n",
@ -2724,6 +2762,7 @@ sna_output_add(struct sna *sna, int id, drmModeResPtr res, int serial)
struct drm_mode_modeinfo dummy;
struct sna_output *sna_output;
xf86OutputPtr *outputs, output;
unsigned possible_encoders, attached_encoders;
const char *output_name;
char name[32];
int len, i;
@ -2733,7 +2772,7 @@ sna_output_add(struct sna *sna, int id, drmModeResPtr res, int serial)
COMPILE_TIME_ASSERT(sizeof(struct drm_mode_get_connector) <= sizeof(compat_conn.pad));
VG_CLEAR(compat_conn);
VG_CLEAR(enc);
memset(&enc, 0, sizeof(enc));
compat_conn.conn.connector_id = id;
compat_conn.conn.count_props = 0;
@ -2743,16 +2782,10 @@ sna_output_add(struct sna *sna, int id, drmModeResPtr res, int serial)
compat_conn.conn.encoders_ptr = (uintptr_t)&enc.encoder_id;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCONNECTOR, &compat_conn.conn)) {
DBG(("%s: GETCONNECTOR failed, ret=%d\n", __FUNCTION__, errno));
DBG(("%s: GETCONNECTOR[%d] failed, ret=%d\n", __FUNCTION__, id, errno));
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 0;
}
if (compat_conn.conn.connector_type < ARRAY_SIZE(output_names))
output_name = output_names[compat_conn.conn.connector_type];
else
@ -2761,9 +2794,38 @@ sna_output_add(struct sna *sna, int id, drmModeResPtr res, int serial)
if (output_ignored(scrn, name))
return 0;
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETENCODER, &enc)) {
DBG(("%s: GETENCODER failed, ret=%d\n", __FUNCTION__, errno));
return 0;
if (enc.encoder_id) {
if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETENCODER, &enc)) {
DBG(("%s: GETENCODER[%d] failed, ret=%d\n", __FUNCTION__, enc.encoder_id, errno));
return 0;
}
possible_encoders = enc.possible_clones;
attached_encoders = 0;
for (i = 0; i < res->count_encoders; i++) {
if (enc.encoder_id == res->encoders[i]) {
attached_encoders = 1 << i;
break;
}
}
if (attached_encoders == 0) {
DBG(("%s: failed to find attached encoder\n", __FUNCTION__));
return 0;
}
} else {
DBG(("%s: unexpected number [%d] of encoders attached\n",
__FUNCTION__, compat_conn.conn.count_encoders));
if (!gather_encoders(sna, res, id, compat_conn.conn.count_encoders, &enc)) {
DBG(("%s: gather encoders failed\n", __FUNCTION__));
return 0;
}
possible_encoders = enc.possible_clones;
attached_encoders = enc.crtc_id;
memset(&enc, 0, sizeof(enc));
enc.encoder_id = compat_conn.conn.encoder_id;
(void)drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETENCODER, &enc);
}
if (xf86IsEntityShared(scrn->entityList[0])) {
@ -2845,12 +2907,13 @@ sna_output_add(struct sna *sna, int id, drmModeResPtr res, int serial)
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");
sna_output->possible_encoders = possible_encoders;
sna_output->attached_encoders = attached_encoders;
output->mm_width = compat_conn.conn.mm_width;
output->mm_height = compat_conn.conn.mm_height;
@ -2862,7 +2925,7 @@ sna_output_add(struct sna *sna, int id, drmModeResPtr res, int serial)
if (sna_output->is_panel)
sna_output_backlight_init(output);
output->possible_crtcs = enc.possible_crtcs & ((1 << sna->mode.num_real_crtc) - 1);
output->possible_crtcs = enc.possible_crtcs & count_to_mask(sna->mode.num_real_crtc);
output->interlaceAllowed = TRUE;
if (serial) {
@ -2880,9 +2943,11 @@ sna_output_add(struct sna *sna, int id, drmModeResPtr res, int serial)
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,
DBG(("%s: created output '%s' %d, encoder=%d (possible crtc:%x, attached encoders:%x, possible clones:%x), serial=%d, edid=%d, dpms=%d, crtc=%lu\n",
__FUNCTION__, name, id, enc.encoder_id,
(uint32_t)output->possible_crtcs,
sna_output->attached_encoders,
sna_output->possible_encoders,
serial, sna_output->edid_idx, sna_output->dpms_id,
(unsigned long)(uintptr_t)output->crtc));
@ -2964,6 +3029,8 @@ void sna_mode_discover(struct sna *sna)
changed |= sna_output_add(sna, res->connectors[i], res, serial) > 0;
}
drmModeFreeResources(res);
for (i = 0; i < sna->mode.num_real_output; i++) {
xf86OutputPtr output = config->output[i];
if (to_sna_output(output)->serial != serial) {
@ -2973,14 +3040,12 @@ void sna_mode_discover(struct sna *sna)
}
if (changed) {
sna_mode_compute_possible_outputs(sna, res);
sna_mode_compute_possible_outputs(sna);
/* Reorder user visible listing */
sort_randr_outputs(sna, screen);
xf86RandR12TellChanged(screen);
}
drmModeFreeResources(res);
}
static void copy_front(struct sna *sna, PixmapPtr old, PixmapPtr new)
@ -4415,7 +4480,7 @@ bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna)
sna->mode.num_real_output = xf86_config->num_output;
sna_mode_compute_possible_outputs(sna, res);
sna_mode_compute_possible_outputs(sna);
sna->mode.max_crtc_width = res->max_width;
sna->mode.max_crtc_height = res->max_height;