]> git.ipfire.org Git - thirdparty/plymouth.git/commitdiff
drm: Pick a controller for unconfigured connectors
authorHans de Goede <hdegoede@redhat.com>
Tue, 27 Nov 2018 23:53:46 +0000 (00:53 +0100)
committerHans de Goede <hdegoede@redhat.com>
Tue, 4 Dec 2018 08:45:52 +0000 (09:45 +0100)
So far we've been relying on the kernel fbcon code to set up all outputs,
now that distros have started using deferred fbcon takeover for flickerfree
booting, we can no longer rely on this and in some cases we must pick
our own controllers.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
src/plugins/renderers/drm/plugin.c

index 87ecbfdc9ec2a87200796638d7aa6d52ff24f94a..20543eaa3b162b897fa20585608114e8dffdde7b 100644 (file)
@@ -980,7 +980,7 @@ close_device (ply_renderer_backend_t *backend)
 }
 
 static void
-find_controller_for_output (ply_renderer_backend_t *backend,
+output_get_controller_info (ply_renderer_backend_t *backend,
                             ply_output_t           *output)
 {
         int i;
@@ -1092,12 +1092,119 @@ get_active_mode (ply_renderer_backend_t *backend,
         return mode;
 }
 
+/* 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
+ * that that one will get the first (free) controller and then recurses into
+ * itself to assign the remaining outputs. This tries assigning all remainig
+ * unassigned outputs first and returns the best result of all possible
+ * assignment orders for the remaining unassigned outputs.
+ * This repeats until we find an assignment order which results in a controller
+ * for all outputs, or we've tried all possible assignment orders.
+ */
+
+static uint32_t
+find_controller_for_output (ply_renderer_backend_t *backend,
+                            const ply_output_t     *outputs,
+                            int                     outputs_len,
+                            int                     output_idx)
+{
+        uint32_t possible_controllers = outputs[output_idx].possible_controllers;
+        int i, j;
+
+        for (i = 0; i < backend->resources->count_crtcs; i++) {
+                uint32_t controller_id = backend->resources->crtcs[i];
+
+                if (!(possible_controllers & (1 << i)))
+                        continue; /* controller not usable for this connector */
+
+                for (j = 0; j < outputs_len; j++) {
+                        if (outputs[j].controller_id == controller_id)
+                                break;
+                }
+                if (j < outputs_len)
+                        continue; /* controller already in use */
+
+                return controller_id;
+        }
+
+        return 0;
+}
+
+static int
+count_setup_controllers (const ply_output_t *outputs,
+                         int                 outputs_len)
+{
+        int i, count = 0;
+
+        for (i = 0; i < outputs_len; i++) {
+                if (outputs[i].controller_id)
+                        count++;
+        }
+
+        return count;
+}
+
+static ply_output_t *
+setup_outputs (ply_renderer_backend_t *backend,
+               const ply_output_t     *outputs,
+               int                     outputs_len)
+{
+        const ply_output_t *best_outputs;
+        ply_output_t *new_outputs;
+        int i, count, best_count;
+        uint32_t controller_id;
+
+        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)
+                        continue;
+
+                /* Assign controller for connector i */
+                controller_id = find_controller_for_output (backend, outputs, outputs_len, i);
+                if (!controller_id)
+                        continue;
+
+                /* Add the new controller to a copy of the passed in connector
+                 * template, we want to try all possible permutations of
+                 * unassigned outputs without modifying the template.
+                 */
+                new_outputs = calloc (outputs_len, sizeof(*new_outputs));
+                memcpy (new_outputs, outputs, outputs_len * sizeof(ply_output_t));
+                new_outputs[i].controller_id = controller_id;
+
+                /* Recurse into ourselves to assign remaining controllers,
+                 * trying all possible assignment orders.
+                 */
+                new_outputs = setup_outputs (backend, new_outputs, outputs_len);
+
+                count = count_setup_controllers (new_outputs, outputs_len);
+                if (count > best_count) {
+                        if (best_outputs != outputs)
+                                free ((void *)best_outputs);
+                        best_outputs = new_outputs;
+                        best_count = count;
+                } else {
+                        free (new_outputs);
+                }
+        }
+
+        if (best_outputs != outputs)
+                free ((void *)outputs);
+
+        /* Our caller is allowed to modify outputs, cast-away the const */
+        return (ply_output_t *)best_outputs;
+}
+
 static bool
 create_heads_for_active_connectors (ply_renderer_backend_t *backend)
 {
         ply_hashtable_t *heads_by_controller_id;
         ply_output_t *outputs;
-        int i, j, found, outputs_len;
+        int i, j, found, number_of_setup_outputs, outputs_len;
 
         heads_by_controller_id = ply_hashtable_new (NULL, NULL);
 
@@ -1127,7 +1234,7 @@ create_heads_for_active_connectors (ply_renderer_backend_t *backend)
 
                 outputs[found].connector = connector;
 
-                find_controller_for_output (backend, &outputs[found]);
+                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)
@@ -1169,9 +1276,26 @@ create_heads_for_active_connectors (ply_renderer_backend_t *backend)
                 }
         }
 
-        /* Step 3: TODO
+        /* Step 3:
          * Assign controllers to outputs without a controller
          */
+        number_of_setup_outputs = count_setup_controllers (outputs, outputs_len);
+        if (number_of_setup_outputs != outputs_len) {
+                /* 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 */
+                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;
+                outputs = setup_outputs (backend, outputs, outputs_len);
+        }
+        for (i = 0; i < outputs_len; i++)
+                ply_trace ("Using controller %u for connector %u",
+                           outputs[i].controller_id, outputs[i].connector->connector_id);
 
         /* Step 4:
          * Create heads for all valid outputs