mirror of
https://gitlab.freedesktop.org/plymouth/plymouth.git
synced 2026-05-08 10:08:06 +02:00
Merge branch 'drm-hotplug' into 'master'
Drm hotplug See merge request plymouth/plymouth!23
This commit is contained in:
commit
24a7ee5c5d
7 changed files with 373 additions and 163 deletions
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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: */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue