Merge branch 'drm-hotplug' into 'master'

Drm hotplug

See merge request plymouth/plymouth!23
This commit is contained in:
Hans de Goede 2019-01-25 09:06:31 +00:00
commit 24a7ee5c5d
7 changed files with 373 additions and 163 deletions

View file

@ -51,6 +51,9 @@ static bool create_devices_for_terminal_and_renderer_type (ply_device_manager_t
const char *device_path,
ply_terminal_t *terminal,
ply_renderer_type_t renderer_type);
static void create_pixel_displays_for_renderer (ply_device_manager_t *manager,
ply_renderer_t *renderer);
struct _ply_device_manager
{
ply_device_manager_flags_t flags;
@ -372,6 +375,39 @@ create_devices_for_subsystem (ply_device_manager_t *manager,
}
static void
on_drm_udev_add_or_change (ply_device_manager_t *manager,
const char *action,
struct udev_device *device)
{
const char *device_path = udev_device_get_devnode (device);
ply_renderer_t *renderer;
bool changed;
if (device_path == NULL)
return;
renderer = ply_hashtable_lookup (manager->renderers, (void *) device_path);
if (renderer == NULL) {
/* We also try to create the renderer again on change events,
* renderer creation fails when no outputs are connected and
* this may have changed.
*/
create_devices_for_udev_device (manager, device);
return;
}
/* Renderer exists, bail if this is not a change event */
if (strcmp (action, "change"))
return;
changed = ply_renderer_handle_change_event (renderer);
if (changed) {
free_displays_for_renderer (manager, renderer);
create_pixel_displays_for_renderer (manager, renderer);
}
}
static bool
on_udev_event (ply_device_manager_t *manager)
{
struct udev_device *device;
@ -379,16 +415,16 @@ on_udev_event (ply_device_manager_t *manager)
device = udev_monitor_receive_device (manager->udev_monitor);
if (device == NULL)
return;
return false;
action = udev_device_get_action (device);
ply_trace ("got %s event for device %s", action, udev_device_get_sysname (device));
if (action == NULL)
return;
return false;
if (strcmp (action, "add") == 0) {
if (strcmp (action, "add") == 0 || strcmp (action, "change") == 0) {
const char *subsystem;
subsystem = udev_device_get_subsystem (device);
@ -397,7 +433,7 @@ on_udev_event (ply_device_manager_t *manager)
if (manager->local_console_managed && manager->local_console_is_text)
ply_trace ("ignoring since we're already using text splash for local console");
else
create_devices_for_udev_device (manager, device);
on_drm_udev_add_or_change (manager, action, device);
} else {
ply_trace ("ignoring since we only handle subsystem %s devices after timeout", subsystem);
}
@ -406,6 +442,14 @@ on_udev_event (ply_device_manager_t *manager)
}
udev_device_unref (device);
return true;
}
static void
on_udev_event_loop (ply_device_manager_t *manager)
{
/* Call on_udev_event until all events are consumed */
while (on_udev_event (manager)) {}
}
static void
@ -435,7 +479,7 @@ watch_for_udev_events (ply_device_manager_t *manager)
fd,
PLY_EVENT_LOOP_FD_STATUS_HAS_DATA,
(ply_event_handler_t)
on_udev_event,
on_udev_event_loop,
NULL,
manager);
}

View file

@ -43,6 +43,7 @@ typedef struct
bool (*open_device)(ply_renderer_backend_t *backend);
void (*close_device)(ply_renderer_backend_t *backend);
bool (*query_device)(ply_renderer_backend_t *backend);
bool (*handle_change_event)(ply_renderer_backend_t *backend);
bool (*map_to_device)(ply_renderer_backend_t *backend);
void (*unmap_from_device)(ply_renderer_backend_t *backend);
void (*activate)(ply_renderer_backend_t *backend);

View file

@ -297,6 +297,15 @@ ply_renderer_close (ply_renderer_t *renderer)
renderer->is_active = false;
}
bool
ply_renderer_handle_change_event (ply_renderer_t *renderer)
{
if (renderer->plugin_interface->handle_change_event)
return renderer->plugin_interface->handle_change_event (renderer->backend);
return false;
}
void
ply_renderer_activate (ply_renderer_t *renderer)
{

View file

@ -55,6 +55,8 @@ ply_renderer_t *ply_renderer_new (ply_renderer_type_t renderer_type,
void ply_renderer_free (ply_renderer_t *renderer);
bool ply_renderer_open (ply_renderer_t *renderer);
void ply_renderer_close (ply_renderer_t *renderer);
/* Returns true when the heads have changed as a result of the change event */
bool ply_renderer_handle_change_event (ply_renderer_t *renderer);
void ply_renderer_activate (ply_renderer_t *renderer);
void ply_renderer_deactivate (ply_renderer_t *renderer);
bool ply_renderer_is_active (ply_renderer_t *renderer);

View file

@ -169,4 +169,22 @@ ply_array_steal_uint32_elements (ply_array_t *array)
return data;
}
bool
ply_array_contains_uint32_element (ply_array_t *array, const uint32_t element)
{
uint32_t const *elements;
int i, size;
assert (array->element_type == PLY_ARRAY_ELEMENT_TYPE_UINT32);
elements = (uint32_t const *) ply_buffer_get_bytes (array->buffer);
size = (ply_buffer_get_size (array->buffer) / sizeof(const uint32_t)) - 1;
for (i = 0; i < size; i++)
if (elements[i] == element)
return true;
return false;
}
/* vim: set ts=4 sw=4 expandtab autoindent cindent cino={.5s,(0: */

View file

@ -23,6 +23,7 @@
#define PLY_ARRAY_H
#include <stdint.h>
#include <stdbool.h>
typedef struct _ply_array ply_array_t;
typedef enum _ply_array_element_type ply_array_element_type_t;
@ -46,6 +47,8 @@ uint32_t const *ply_array_get_uint32_elements (ply_array_t *array);
void **ply_array_steal_pointer_elements (ply_array_t *array);
uint32_t *ply_array_steal_uint32_elements (ply_array_t *array);
bool ply_array_contains_uint32_element (ply_array_t *array,
const uint32_t element);
#endif
#endif /* PLY_ARRAY_H */

View file

@ -77,12 +77,12 @@ struct _ply_renderer_head
unsigned long row_stride;
ply_array_t *connector_ids;
drmModeConnector *connector0;
drmModeModeInfo *connector0_mode;
drmModeModeInfo connector0_mode;
uint32_t controller_id;
uint32_t console_buffer_id;
uint32_t scan_out_buffer_id;
bool scan_out_buffer_needs_reset;
int gamma_size;
uint16_t *gamma;
@ -117,12 +117,16 @@ typedef struct
typedef struct
{
drmModeConnector *connector;
drmModeModeInfo *mode;
drmModeModeInfo mode;
uint32_t connector_id;
uint32_t connector_type;
uint32_t controller_id;
uint32_t possible_controllers;
int device_scale;
int link_status;
ply_pixel_buffer_rotation_t rotation;
bool tiled;
bool connected;
} ply_output_t;
struct _ply_renderer_backend
@ -136,10 +140,14 @@ struct _ply_renderer_backend
ply_renderer_input_source_t input_source;
ply_list_t *heads;
ply_hashtable_t *heads_by_connector_id;
ply_hashtable_t *heads_by_controller_id;
ply_hashtable_t *output_buffers;
ply_output_t *outputs;
int outputs_len;
int connected_count;
int32_t dither_red;
int32_t dither_green;
int32_t dither_blue;
@ -164,19 +172,9 @@ static bool reset_scan_out_buffer_if_needed (ply_renderer_backend_t *backend,
static void flush_head (ply_renderer_backend_t *backend,
ply_renderer_head_t *head);
static bool efi_enabled (void)
{
return ply_directory_exists ("/sys/firmware/efi/efivars");
}
/* A small helper to determine if we should try to keep the current mode
* or pick the best mode ourselves, we keep the current mode if:
* 1. The user specified a specific mode using video= on the commandline
* 2. The code to pick the best mode was added because with flicker-free boot
* we can no longer rely on the kernel's fbcon code setting things up.
* We should be able to do a better job then fbcon regardless, but for
* now lets only use the new code on flicker-free systems until it is
* more mature, this means only using it on UEFI systems.
* or pick the best mode ourselves, we keep the current mode only if the
* user specified a specific mode using video= on the commandline.
*/
static bool
should_use_preferred_mode (void)
@ -186,9 +184,6 @@ should_use_preferred_mode (void)
if (ply_kernel_command_line_get_string_after_prefix ("video="))
use_preferred_mode = false;
if (!efi_enabled ())
use_preferred_mode = false;
ply_trace ("should_use_preferred_mode: %d", use_preferred_mode);
return use_preferred_mode;
@ -445,6 +440,7 @@ connector_orientation_prop_to_rotation (drmModePropertyPtr prop,
static void
ply_renderer_connector_get_rotation_and_tiled (ply_renderer_backend_t *backend,
drmModeConnector *connector,
ply_output_t *output)
{
drmModePropertyPtr prop;
@ -453,52 +449,60 @@ ply_renderer_connector_get_rotation_and_tiled (ply_renderer_backend_t *back
output->rotation = PLY_PIXEL_BUFFER_ROTATE_UPRIGHT;
output->tiled = false;
for (i = 0; i < output->connector->count_props; i++) {
prop = drmModeGetProperty (backend->device_fd, output->connector->props[i]);
for (i = 0; i < connector->count_props; i++) {
prop = drmModeGetProperty (backend->device_fd, connector->props[i]);
if (!prop)
continue;
if ((prop->flags & DRM_MODE_PROP_ENUM) &&
strcmp (prop->name, "panel orientation") == 0)
output->rotation = connector_orientation_prop_to_rotation (prop, output->connector->prop_values[i]);
output->rotation = connector_orientation_prop_to_rotation (prop, connector->prop_values[i]);
if ((prop->flags & DRM_MODE_PROP_BLOB) &&
strcmp (prop->name, "TILE") == 0)
output->tiled = true;
if ((prop->flags & DRM_MODE_PROP_ENUM) &&
strcmp (prop->name, "link-status") == 0) {
output->link_status = connector->prop_values[i];
ply_trace ("link-status %d", output->link_status);
}
drmModeFreeProperty (prop);
}
}
static bool
ply_renderer_head_add_connector (ply_renderer_head_t *head,
drmModeConnector *connector,
drmModeModeInfo *mode)
ply_output_t *output)
{
if (mode->hdisplay != head->area.width || mode->vdisplay != head->area.height) {
if (output->link_status == DRM_MODE_LINK_STATUS_BAD)
head->scan_out_buffer_needs_reset = true;
if (output->mode.hdisplay != head->area.width || output->mode.vdisplay != head->area.height) {
ply_trace ("Tried to add connector with resolution %dx%d to %dx%d head",
(int) mode->hdisplay, (int) mode->vdisplay,
(int) output->mode.hdisplay, (int) output->mode.vdisplay,
(int) head->area.width, (int) head->area.height);
return false;
} else {
ply_trace ("Adding connector with id %d to %dx%d head",
(int) connector->connector_id,
(int) head->area.width, (int) head->area.height);
}
ply_array_add_uint32_element (head->connector_ids, connector->connector_id);
if (ply_array_contains_uint32_element (head->connector_ids, output->connector_id)) {
ply_trace ("Head already contains connector with id %d", output->connector_id);
return false;
}
ply_trace ("Adding connector with id %d to %dx%d head",
(int) output->connector_id, (int) head->area.width, (int) head->area.height);
ply_array_add_uint32_element (head->connector_ids, output->connector_id);
return true;
}
static ply_renderer_head_t *
ply_renderer_head_new (ply_renderer_backend_t *backend,
drmModeConnector *connector,
drmModeModeInfo *mode,
uint32_t controller_id,
ply_output_t *output,
uint32_t console_buffer_id,
int gamma_size,
ply_pixel_buffer_rotation_t rotation)
int gamma_size)
{
ply_renderer_head_t *head;
int i, step;
@ -507,16 +511,14 @@ ply_renderer_head_new (ply_renderer_backend_t *backend,
head->backend = backend;
head->connector_ids = ply_array_new (PLY_ARRAY_ELEMENT_TYPE_UINT32);
head->controller_id = controller_id;
head->controller_id = output->controller_id;
head->console_buffer_id = console_buffer_id;
head->connector0 = connector;
head->connector0_mode = mode;
head->connector0_mode = output->mode;
head->area.x = 0;
head->area.y = 0;
head->area.width = mode->hdisplay;
head->area.height = mode->vdisplay;
head->area.width = output->mode.hdisplay;
head->area.height = output->mode.vdisplay;
if (gamma_size) {
head->gamma_size = gamma_size;
@ -530,29 +532,29 @@ ply_renderer_head_new (ply_renderer_backend_t *backend,
}
}
ply_renderer_head_add_connector (head, connector, mode);
ply_renderer_head_add_connector (head, output);
assert (ply_array_get_size (head->connector_ids) > 0);
head->pixel_buffer = ply_pixel_buffer_new_with_device_rotation (head->area.width, head->area.height, rotation);
ply_pixel_buffer_set_device_scale (head->pixel_buffer,
ply_get_device_scale (head->area.width,
head->area.height,
connector->mmWidth,
connector->mmHeight));
head->pixel_buffer = ply_pixel_buffer_new_with_device_rotation (head->area.width, head->area.height, output->rotation);
ply_pixel_buffer_set_device_scale (head->pixel_buffer, output->device_scale);
ply_trace ("Creating %ldx%ld renderer head", head->area.width, head->area.height);
ply_pixel_buffer_fill_with_color (head->pixel_buffer, NULL,
0.0, 0.0, 0.0, 1.0);
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
connector->connector_type == DRM_MODE_CONNECTOR_DSI) {
backend->panel_width = mode->hdisplay;
backend->panel_height = mode->vdisplay;
backend->panel_rotation = rotation;
backend->panel_scale = ply_pixel_buffer_get_device_scale (head->pixel_buffer);
if (output->connector_type == DRM_MODE_CONNECTOR_LVDS ||
output->connector_type == DRM_MODE_CONNECTOR_eDP ||
output->connector_type == DRM_MODE_CONNECTOR_DSI) {
backend->panel_width = output->mode.hdisplay;
backend->panel_height = output->mode.vdisplay;
backend->panel_rotation = output->rotation;
backend->panel_scale = output->device_scale;
}
ply_list_append_data (backend->heads, head);
ply_hashtable_insert (backend->heads_by_controller_id,
(void *) (intptr_t) output->controller_id,
head);
return head;
}
@ -562,7 +564,6 @@ ply_renderer_head_free (ply_renderer_head_t *head)
ply_trace ("freeing %ldx%ld renderer head", head->area.width, head->area.height);
ply_pixel_buffer_free (head->pixel_buffer);
drmModeFreeConnector (head->connector0);
ply_array_free (head->connector_ids);
free (head->gamma);
free (head);
@ -652,7 +653,7 @@ ply_renderer_head_set_scan_out_buffer (ply_renderer_backend_t *backend,
ply_renderer_head_t *head,
uint32_t buffer_id)
{
drmModeModeInfo *mode = head->connector0_mode;
drmModeModeInfo *mode = &head->connector0_mode;
uint32_t *connector_ids;
int number_of_connectors;
@ -712,11 +713,7 @@ ply_renderer_head_map (ply_renderer_backend_t *backend,
return false;
}
/* FIXME: Maybe we should blit the fbcon contents instead of the (blank)
* shadow buffer?
*/
ply_renderer_head_redraw (backend, head);
head->scan_out_buffer_needs_reset = true;
return true;
}
@ -731,6 +728,47 @@ ply_renderer_head_unmap (ply_renderer_backend_t *backend,
head->scan_out_buffer_id = 0;
}
static void
ply_renderer_head_remove (ply_renderer_backend_t *backend,
ply_renderer_head_t *head)
{
if (head->scan_out_buffer_id)
ply_renderer_head_unmap (backend, head);
ply_hashtable_remove (backend->heads_by_controller_id,
(void *) (intptr_t) head->controller_id);
ply_list_remove_data (backend->heads, head);
ply_renderer_head_free (head);
}
static void
ply_renderer_head_remove_connector (ply_renderer_backend_t *backend,
ply_renderer_head_t *head,
uint32_t connector_id)
{
int i, size = ply_array_get_size (head->connector_ids);
uint32_t *connector_ids;
if (!ply_array_contains_uint32_element (head->connector_ids, connector_id)) {
ply_trace ("Head does not contain connector %u, cannot remove", connector_id);
return;
}
if (size == 1) {
ply_renderer_head_remove (backend, head);
return;
}
/* Empty the array and re-add all connectors except the one being removed */
connector_ids = ply_array_steal_uint32_elements (head->connector_ids);
for (i = 0; i < size; i++) {
if (connector_ids[i] != connector_id)
ply_array_add_uint32_element (head->connector_ids,
connector_ids[i]);
}
free (connector_ids);
}
static void
flush_area (const char *src,
unsigned long src_row_stride,
@ -816,6 +854,7 @@ create_backend (const char *device_name,
backend->requires_explicit_flushing = true;
backend->output_buffers = ply_hashtable_new (ply_hashtable_direct_hash,
ply_hashtable_direct_compare);
backend->heads_by_controller_id = ply_hashtable_new (NULL, NULL);
backend->use_preferred_mode = should_use_preferred_mode ();
return backend;
@ -835,9 +874,9 @@ destroy_backend (ply_renderer_backend_t *backend)
free (backend->device_name);
ply_hashtable_free (backend->output_buffers);
ply_hashtable_free (backend->heads_by_controller_id);
drmModeFreeResources (backend->resources);
free (backend->outputs);
free (backend);
}
@ -981,6 +1020,7 @@ close_device (ply_renderer_backend_t *backend)
static void
output_get_controller_info (ply_renderer_backend_t *backend,
drmModeConnector *connector,
ply_output_t *output)
{
int i;
@ -990,16 +1030,16 @@ output_get_controller_info (ply_renderer_backend_t *backend,
output->possible_controllers = 0xffffffff;
for (i = 0; i < output->connector->count_encoders; i++) {
for (i = 0; i < connector->count_encoders; i++) {
encoder = drmModeGetEncoder (backend->device_fd,
output->connector->encoders[i]);
connector->encoders[i]);
if (encoder == NULL)
continue;
if (encoder->encoder_id == output->connector->encoder_id && encoder->crtc_id) {
if (encoder->encoder_id == connector->encoder_id && encoder->crtc_id) {
ply_trace ("Found already lit monitor on connector %u using controller %u",
output->connector->connector_id, encoder->crtc_id);
connector->connector_id, encoder->crtc_id);
output->controller_id = encoder->crtc_id;
}
@ -1008,7 +1048,7 @@ output_get_controller_info (ply_renderer_backend_t *backend,
*/
output->possible_controllers &= encoder->possible_crtcs;
ply_trace ("connector %u encoder %u possible controllers 0x%08x/0x%08x",
output->connector->connector_id, encoder->encoder_id,
connector->connector_id, encoder->encoder_id,
encoder->possible_crtcs, output->possible_controllers);
drmModeFreeEncoder (encoder);
}
@ -1071,6 +1111,7 @@ get_preferred_mode (drmModeConnector *connector)
static drmModeModeInfo *
get_active_mode (ply_renderer_backend_t *backend,
drmModeConnector *connector,
ply_output_t *output)
{
drmModeCrtc *controller;
@ -1085,13 +1126,55 @@ get_active_mode (ply_renderer_backend_t *backend,
ply_trace ("Looking for connector mode index of active mode %dx%d",
controller->mode.hdisplay, controller->mode.vdisplay);
mode = find_matching_connector_mode (backend, output->connector, &controller->mode);
mode = find_matching_connector_mode (backend, connector, &controller->mode);
drmModeFreeCrtc (controller);
return mode;
}
static void
get_output_info (ply_renderer_backend_t *backend,
uint32_t connector_id,
ply_output_t *output)
{
drmModeModeInfo *mode = NULL;
drmModeConnector *connector;
memset (output, 0, sizeof(*output));
output->connector_id = connector_id;
connector = drmModeGetConnector (backend->device_fd, connector_id);
if (connector == NULL)
return;
if (connector->connection != DRM_MODE_CONNECTED ||
connector->count_modes <= 0)
goto out;
output_get_controller_info (backend, connector, output);
ply_renderer_connector_get_rotation_and_tiled (backend, connector, output);
if (!output->tiled && backend->use_preferred_mode)
mode = get_preferred_mode (connector);
if (!mode && output->controller_id)
mode = get_active_mode (backend, connector, output);
/* If we couldn't find the current active mode, fall back to the first available. */
if (!mode) {
ply_trace ("falling back to first available mode");
mode = &connector->modes[0];
}
output->mode = *mode;
output->device_scale = ply_get_device_scale (mode->hdisplay, mode->vdisplay,
connector->mmWidth, connector->mmHeight);
output->connector_type = connector->connector_type;
output->connected = true;
out:
drmModeFreeConnector (connector);
}
/* Some controllers can only drive some outputs, we want to find a combination
* where all (connected) outputs get a controller. To do this setup_outputs
* picks which output to assign a controller for first (trying all outputs), so
@ -1158,9 +1241,9 @@ setup_outputs (ply_renderer_backend_t *backend,
best_count = count_setup_controllers (outputs, outputs_len);
best_outputs = outputs;
for (i = 0; i < outputs_len && best_count < outputs_len; i++) {
/* Already assigned? */
if (outputs[i].controller_id)
for (i = 0; i < outputs_len && best_count < backend->connected_count; i++) {
/* Not connected or already assigned? */
if (!outputs[i].connected || outputs[i].controller_id)
continue;
/* Assign controller for connector i */
@ -1199,62 +1282,73 @@ setup_outputs (ply_renderer_backend_t *backend,
return (ply_output_t *)best_outputs;
}
static bool
create_heads_for_active_connectors (ply_renderer_backend_t *backend)
static void
remove_output (ply_renderer_backend_t *backend, ply_output_t *output)
{
ply_hashtable_t *heads_by_controller_id;
ply_output_t *outputs;
int i, j, found, number_of_setup_outputs, outputs_len;
ply_renderer_head_t *head;
heads_by_controller_id = ply_hashtable_new (NULL, NULL);
head = ply_hashtable_lookup (backend->heads_by_controller_id,
(void *) (intptr_t) output->controller_id);
if (head == NULL) {
ply_trace ("Could not find head for connector %u, controller %u, cannot remove",
output->connector_id, output->controller_id);
return;
}
outputs = calloc (backend->resources->count_connectors, sizeof(*outputs));
ply_renderer_head_remove_connector (backend, head, output->connector_id);
}
/* Update our outputs array to match the hardware state and
* create and/or remove heads as necessary.
* Returns true if any heads were modified.
*/
static bool
create_heads_for_active_connectors (ply_renderer_backend_t *backend, bool change)
{
int i, j, number_of_setup_outputs, outputs_len;
ply_output_t output, *outputs;
bool changed = false;
/* Step 1:
* Build a list of connected outputs and get pre-configured controllers.
* Remove existing outputs from heads if they have changed.
*/
found = 0;
for (i = 0; i < backend->resources->count_connectors; i++) {
drmModeConnector *connector;
connector = drmModeGetConnector (backend->device_fd,
backend->resources->connectors[i]);
if (connector == NULL)
ply_trace ("Checking currently connected outputs for changes");
for (i = 0; i < backend->outputs_len; i++) {
if (!backend->outputs[i].controller_id)
continue;
if (connector->connection != DRM_MODE_CONNECTED) {
drmModeFreeConnector (connector);
continue;
get_output_info (backend, backend->outputs[i].connector_id, &output);
if (memcmp(&backend->outputs[i], &output, sizeof(ply_output_t))) {
ply_trace ("Output for connector %u changed, removing",
backend->outputs[i].connector_id);
remove_output (backend, &backend->outputs[i]);
changed = true;
}
if (connector->count_modes <= 0) {
drmModeFreeConnector (connector);
continue;
}
outputs[found].connector = connector;
output_get_controller_info (backend, &outputs[found]);
ply_renderer_connector_get_rotation_and_tiled (backend, &outputs[found]);
if (!outputs[found].tiled && backend->use_preferred_mode)
outputs[found].mode = get_preferred_mode (connector);
if (!outputs[found].mode && outputs[found].controller_id)
outputs[found].mode = get_active_mode (backend, &outputs[found]);
/* If we couldn't find the current active mode, fall back to the first available.
*/
if (!outputs[found].mode) {
ply_trace ("falling back to first available mode");
outputs[found].mode = &connector->modes[0];
}
found++;
}
outputs_len = found; /* outputs now contains found valid entries */
/* Step 2:
* Now that we've removed changed connectors from the heads, we can
* simply rebuild the outputs array from scratch. For any unchanged
* outputs for which we already have a head, we will end up in
* ply_renderer_head_add_connector which will ignore the already
* added connector.
*/
ply_trace ("(Re)enumerating all outputs");
free (backend->outputs);
backend->outputs = NULL;
outputs = calloc (backend->resources->count_connectors, sizeof(*outputs));
outputs_len = backend->resources->count_connectors;
backend->connected_count = 0;
for (i = 0; i < outputs_len; i++) {
get_output_info (backend, backend->resources->connectors[i], &outputs[i]);
if (outputs[i].connected)
backend->connected_count++;
}
/* Step 3:
* Drop controllers for clones for which we've picked different modes.
*/
for (i = 0; i < outputs_len; i++) {
@ -1266,28 +1360,31 @@ create_heads_for_active_connectors (ply_renderer_backend_t *backend)
continue;
if (outputs[i].controller_id == outputs[j].controller_id &&
(outputs[i].mode->hdisplay != outputs[j].mode->hdisplay ||
outputs[i].mode->vdisplay != outputs[j].mode->vdisplay)) {
(outputs[i].mode.hdisplay != outputs[j].mode.hdisplay ||
outputs[i].mode.vdisplay != outputs[j].mode.vdisplay)) {
ply_trace ("connector %u uses same controller as %u and modes differ, unlinking controller",
outputs[j].connector->connector_id,
outputs[i].connector->connector_id);
outputs[j].connector_id, outputs[i].connector_id);
outputs[j].controller_id = 0;
}
}
}
/* Step 3:
/* Step 4:
* Assign controllers to outputs without a controller
*/
number_of_setup_outputs = count_setup_controllers (outputs, outputs_len);
if (number_of_setup_outputs != outputs_len) {
if (number_of_setup_outputs != backend->connected_count) {
/* First try, try to assign controllers to outputs without one */
ply_trace ("Some outputs don't have controllers, picking controllers");
outputs = setup_outputs (backend, outputs, outputs_len);
number_of_setup_outputs = count_setup_controllers (outputs, outputs_len);
}
if (number_of_setup_outputs != outputs_len) {
/* Second try, re-assing controller for all outputs */
/* Try again if necessary, re-assing controllers for all outputs.
* Note this is skipped when processing change events, as we don't
* want to mess with the controller assignment of already lit monitors
* in that case.
*/
if (!change && number_of_setup_outputs != backend->connected_count) {
ply_trace ("Some outputs still don't have controllers, re-assigning controllers for all outputs");
for (i = 0; i < outputs_len; i++)
outputs[i].controller_id = 0;
@ -1295,55 +1392,50 @@ create_heads_for_active_connectors (ply_renderer_backend_t *backend)
}
for (i = 0; i < outputs_len; i++)
ply_trace ("Using controller %u for connector %u",
outputs[i].controller_id, outputs[i].connector->connector_id);
outputs[i].controller_id, outputs[i].connector_id);
/* Step 4:
/* Step 5:
* Create heads for all valid outputs
*/
for (i = 0; i < outputs_len; i++) {
drmModeConnector *connector = outputs[i].connector;
drmModeCrtc *controller;
ply_renderer_head_t *head;
uint32_t controller_id;
uint32_t console_buffer_id;
int gamma_size;
controller = drmModeGetCrtc (backend->device_fd, outputs[i].controller_id);
if (!controller) {
drmModeFreeConnector (connector);
if (!outputs[i].controller_id)
continue;
controller = drmModeGetCrtc (backend->device_fd, outputs[i].controller_id);
if (!controller)
continue;
}
controller_id = controller->crtc_id;
console_buffer_id = controller->buffer_id;
gamma_size = controller->gamma_size;
drmModeFreeCrtc (controller);
head = ply_hashtable_lookup (heads_by_controller_id,
head = ply_hashtable_lookup (backend->heads_by_controller_id,
(void *) (intptr_t) controller_id);
if (head == NULL) {
head = ply_renderer_head_new (backend, connector, outputs[i].mode,
controller_id, console_buffer_id,
gamma_size, outputs[i].rotation);
ply_list_append_data (backend->heads, head);
ply_hashtable_insert (heads_by_controller_id,
(void *) (intptr_t) controller_id,
head);
head = ply_renderer_head_new (backend, &outputs[i],
console_buffer_id,
gamma_size);
changed = true;
} else {
if (!ply_renderer_head_add_connector (head, connector, outputs[i].mode))
ply_trace ("couldn't connect monitor to existing head");
drmModeFreeConnector (connector);
if (ply_renderer_head_add_connector (head, &outputs[i]))
changed = true;
}
}
ply_hashtable_free (heads_by_controller_id);
free (outputs);
backend->outputs_len = outputs_len;
backend->outputs = outputs;
return ply_list_get_length (backend->heads) > 0;
ply_trace ("outputs %schanged\n", changed ? "" : "un");
return changed;
}
static bool
@ -1383,6 +1475,8 @@ has_32bpp_support (ply_renderer_backend_t *backend)
static bool
query_device (ply_renderer_backend_t *backend)
{
bool ret = true;
assert (backend != NULL);
assert (backend->device_fd >= 0);
@ -1393,17 +1487,37 @@ query_device (ply_renderer_backend_t *backend)
return false;
}
if (!create_heads_for_active_connectors (backend)) {
if (!create_heads_for_active_connectors (backend, false)) {
ply_trace ("Could not initialize heads");
return false;
}
if (!has_32bpp_support (backend)) {
ret = false;
} else if (!has_32bpp_support (backend)) {
ply_trace ("Device doesn't support 32bpp framebuffer");
ret = false;
}
drmModeFreeResources (backend->resources);
backend->resources = NULL;
return ret;
}
static bool
handle_change_event (ply_renderer_backend_t *backend)
{
bool ret = true;
backend->resources = drmModeGetResources (backend->device_fd);
if (backend->resources == NULL) {
ply_trace ("Could not get card resources for change event");
return false;
}
return true;
ret = create_heads_for_active_connectors (backend, true);
drmModeFreeResources (backend->resources);
backend->resources = NULL;
return ret;
}
static bool
@ -1421,8 +1535,13 @@ map_to_device (ply_renderer_backend_t *backend)
head = (ply_renderer_head_t *) ply_list_node_get_data (node);
next_node = ply_list_get_next_node (backend->heads, node);
if (ply_renderer_head_map (backend, head))
if (ply_renderer_head_map (backend, head)) {
/* FIXME: Maybe we should blit the fbcon contents instead of the (blank)
* shadow buffer?
*/
ply_renderer_head_redraw (backend, head);
head_mapped = true;
}
node = next_node;
}
@ -1469,6 +1588,13 @@ reset_scan_out_buffer_if_needed (ply_renderer_backend_t *backend,
if (!ply_terminal_is_active (backend->terminal))
return false;
if (head->scan_out_buffer_needs_reset) {
ply_renderer_head_set_scan_out_buffer (backend, head,
head->scan_out_buffer_id);
head->scan_out_buffer_needs_reset = false;
return true;
}
controller = drmModeGetCrtc (backend->device_fd, head->controller_id);
if (controller == NULL)
@ -1508,6 +1634,12 @@ flush_head (ply_renderer_backend_t *backend,
updated_region = ply_pixel_buffer_get_updated_areas (pixel_buffer);
areas_to_flush = ply_region_get_sorted_rectangle_list (updated_region);
/* A hotplugged head may not be mapped yet, map it now. */
if (!head->scan_out_buffer_id) {
if (!ply_renderer_head_map (backend, head))
return;
}
map_address = begin_flush (backend, head->scan_out_buffer_id);
node = ply_list_get_first_node (areas_to_flush);
@ -1673,6 +1805,7 @@ ply_renderer_backend_get_interface (void)
.open_device = open_device,
.close_device = close_device,
.query_device = query_device,
.handle_change_event = handle_change_event,
.map_to_device = map_to_device,
.unmap_from_device = unmap_from_device,
.activate = activate,