linuxOS_AP06/buildroot/package/frecon/0011-Support-multiple-monitors.patch
2025-06-03 12:28:32 +08:00

1653 lines
40 KiB
Diff

From 027b04dc44b82447ca6c2760e67320831fba726a Mon Sep 17 00:00:00 2001
From: Jeffy Chen <jeffy.chen@rock-chips.com>
Date: Wed, 28 Feb 2024 18:05:41 +0800
Subject: [PATCH 11/11] Support multiple monitors
Tested on RK3588 EVB with:
1/ FRECON_OUTPUT_CONFIG=fit:270:1280x800,DSI-1=:90: frecon
2/ Connect HDMI
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
drm.c | 703 +++++++++++++++++++++++++++++++++------------------------
drm.h | 66 ++++--
fb.c | 416 ++++++++++++++++++----------------
fb.h | 79 +++----
main.c | 3 +-
5 files changed, 726 insertions(+), 541 deletions(-)
diff --git a/drm.c b/drm.c
index 471210e..1ba6fd9 100644
--- a/drm.c
+++ b/drm.c
@@ -16,71 +16,51 @@
#include <unistd.h>
#include "drm.h"
-#include "input.h"
#include "util.h"
static drm_t* g_drm = NULL;
-
-static int32_t crtc_planes_num(drm_t* drm, int32_t crtc_index)
+drm_t *drm_get()
{
- drmModePlanePtr plane;
- int32_t planes_num = 0;
- drmModePlaneResPtr plane_resources = drmModeGetPlaneResources(drm->fd);
-
- if (!plane_resources)
- return 1; /* Just pretend there is one plane. */
-
- for (uint32_t p = 0; p < plane_resources->count_planes; p++) {
- plane = drmModeGetPlane(drm->fd, plane_resources->planes[p]);
-
- if (plane->possible_crtcs & (1 << crtc_index))
- planes_num++;
-
- drmModeFreePlane(plane);
- }
- drmModeFreePlaneResources(plane_resources);
- return planes_num;
+ return g_drm;
}
-static bool get_connector_path(drm_t* drm, uint32_t connector_id, uint32_t* ret_encoder_id, uint32_t* ret_crtc_id)
+/* Get current CRTC for given connector_id. */
+static bool get_crtc_for_connector(drm_t* drm, uint32_t connector_id, uint32_t* ret_crtc_id)
{
drmModeConnector* connector = drmModeGetConnector(drm->fd, connector_id);
drmModeEncoder* encoder;
+ uint32_t crtc_id = 0;
if (!connector)
return false; /* Error. */
- if (ret_encoder_id)
- *ret_encoder_id = connector->encoder_id;
if (!connector->encoder_id) {
drmModeFreeConnector(connector);
- if (ret_crtc_id)
- *ret_crtc_id = 0;
- return true; /* Not connected. */
+ goto out; /* Not connected. */
}
encoder = drmModeGetEncoder(drm->fd, connector->encoder_id);
if (!encoder) {
- if (ret_crtc_id)
- *ret_crtc_id = 0;
drmModeFreeConnector(connector);
- return false; /* Error. */
+ goto out; /* Error. */
}
- if (ret_crtc_id)
- *ret_crtc_id = encoder->crtc_id;
+ crtc_id = encoder->crtc_id;
drmModeFreeEncoder(encoder);
drmModeFreeConnector(connector);
- return true; /* Connected. */
+out:
+ if (ret_crtc_id)
+ *ret_crtc_id = crtc_id;
+
+ return !!crtc_id; /* Connected. */
}
-/* Find CRTC with most planes for given connector_id. */
+/* Find CRTC for given connector_id. */
static bool find_crtc_for_connector(drm_t* drm, uint32_t connector_id, uint32_t* ret_crtc_id)
{
int enc;
int32_t crtc_id = -1;
- int32_t max_crtc_planes = -1;
drmModeConnector* connector = drmModeGetConnector(drm->fd, connector_id);
if (!connector)
@@ -92,16 +72,10 @@ static bool find_crtc_for_connector(drm_t* drm, uint32_t connector_id, uint32_t*
if (encoder) {
for (crtc = 0; crtc < drm->resources->count_crtcs; crtc++) {
- int32_t crtc_planes;
-
if (!(encoder->possible_crtcs & (1 << crtc)))
continue;
- crtc_planes = crtc_planes_num(drm, crtc);
- if (max_crtc_planes < crtc_planes) {
- crtc_id = drm->resources->crtcs[crtc];
- max_crtc_planes = crtc_planes;
- }
+ crtc_id = drm->resources->crtcs[crtc];
}
drmModeFreeEncoder(encoder);
@@ -150,36 +124,6 @@ static int drm_is_primary_plane(drm_t* drm, uint32_t plane_id)
return ret;
}
-/* Disable all planes except for primary on crtc we use. */
-static void drm_disable_non_primary_planes(drm_t* drm, uint32_t console_crtc_id)
-{
- int ret;
-
- if (!drm->plane_resources)
- return;
-
- for (uint32_t p = 0; p < drm->plane_resources->count_planes; p++) {
- drmModePlanePtr plane;
- plane = drmModeGetPlane(drm->fd,
- drm->plane_resources->planes[p]);
- if (plane) {
- int primary = drm_is_primary_plane(drm, plane->plane_id);
- if (!(plane->crtc_id == console_crtc_id && primary != 0)) {
- ret = drmModeSetPlane(drm->fd, plane->plane_id, plane->crtc_id,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0);
- if (ret) {
- LOG(WARNING, "Unable to disable plane:%d %m", plane->plane_id);
- }
- }
- drmModeFreePlane(plane);
- }
- }
-}
-
static bool drm_is_internal(unsigned type)
{
unsigned t;
@@ -216,114 +160,330 @@ static drmModeConnector* find_first_connected_connector(drm_t* drm, bool interna
return NULL;
}
-static bool find_main_monitor(drm_t* drm)
+static uint32_t find_main_monitor(drm_t* drm)
{
- int modes;
- uint32_t console_crtc_id = 0;
- int lid_state = input_check_lid_state();
- drmModeConnector* main_monitor_connector = NULL;
-
- drm->console_connector_id = 0;
+ drmModeConnector* connector;
+ uint32_t connector_id = 0;
/*
* Find the LVDS/eDP/DSI connectors. Those are the main screens.
*/
- if (lid_state <= 0)
- main_monitor_connector = find_first_connected_connector(drm, true, false);
+ connector = find_first_connected_connector(drm, true, false);
/*
* Now try external connectors.
*/
- if (!main_monitor_connector)
- main_monitor_connector =
- find_first_connected_connector(drm, false, true);
+ if (!connector)
+ connector = find_first_connected_connector(drm, false, true);
/*
* If we still didn't find a connector, give up and return.
*/
- if (!main_monitor_connector)
+ if (!connector)
+ return 0;
+
+ if (connector->count_modes)
+ connector_id = connector->connector_id;
+
+ drmModeFreeConnector(connector);
+ return connector_id;
+}
+
+static bool drm_is_connected(drm_t* drm, uint32_t connector_id)
+{
+ drmModeConnector* connector = drmModeGetConnector(drm->fd, connector_id);
+ if (!connector)
return false;
- if (!main_monitor_connector->count_modes)
+ if (connector->connection == DRM_MODE_CONNECTED) {
+ drmModeFreeConnector(connector);
+ return true;
+ }
+ drmModeFreeConnector(connector);
+ return false;
+}
+
+/* based on weston-13: libweston/backend-drm/drm.c */
+static const char *const connector_type_names[] = {
+ [DRM_MODE_CONNECTOR_Unknown] = "Unknown",
+ [DRM_MODE_CONNECTOR_VGA] = "VGA",
+ [DRM_MODE_CONNECTOR_DVII] = "DVI-I",
+ [DRM_MODE_CONNECTOR_DVID] = "DVI-D",
+ [DRM_MODE_CONNECTOR_DVIA] = "DVI-A",
+ [DRM_MODE_CONNECTOR_Composite] = "Composite",
+ [DRM_MODE_CONNECTOR_SVIDEO] = "SVIDEO",
+ [DRM_MODE_CONNECTOR_LVDS] = "LVDS",
+ [DRM_MODE_CONNECTOR_Component] = "Component",
+ [DRM_MODE_CONNECTOR_9PinDIN] = "DIN",
+ [DRM_MODE_CONNECTOR_DisplayPort] = "DP",
+ [DRM_MODE_CONNECTOR_HDMIA] = "HDMI-A",
+ [DRM_MODE_CONNECTOR_HDMIB] = "HDMI-B",
+ [DRM_MODE_CONNECTOR_TV] = "TV",
+ [DRM_MODE_CONNECTOR_eDP] = "eDP",
+ [DRM_MODE_CONNECTOR_VIRTUAL] = "Virtual",
+ [DRM_MODE_CONNECTOR_DSI] = "DSI",
+ [DRM_MODE_CONNECTOR_DPI] = "DPI",
+};
+
+static char *drm_make_name(drm_t *drm, uint32_t connector_id)
+{
+ drmModeConnector *connector;
+ char *name;
+ const char *type_name = NULL;
+ int ret;
+
+ connector = drmModeGetConnector(drm->fd, connector_id);
+ if (!connector)
+ return NULL;
+
+ if (connector->connector_type < ARRAY_SIZE(connector_type_names))
+ type_name = connector_type_names[connector->connector_type];
+
+ if (!type_name)
+ type_name = "UNNAMED";
+
+ ret = asprintf(&name, "%s-%d", type_name, connector->connector_type_id);
+ if (ret < 0)
+ name = NULL;
+
+ drmModeFreeConnector(connector);
+ return name;
+}
+
+static bool drm_get_mode(drm_t* drm, drm_output_t *output)
+{
+ drmModeConnector *connector;
+ drmModeModeInfo *preferred = NULL, *mode;
+ int i;
+
+ connector = drmModeGetConnector(drm->fd, output->connector_id);
+ if (!connector)
return false;
- drm->console_connector_id = main_monitor_connector->connector_id;
+ if (connector->connection != DRM_MODE_CONNECTED ||
+ !connector->count_modes)
+ goto err;
- for (modes = 0; modes < main_monitor_connector->count_modes; modes++) {
- if (main_monitor_connector->modes[modes].type &
- DRM_MODE_TYPE_PREFERRED) {
- drm->console_mode_info = main_monitor_connector->modes[modes];
+ for (i = 0; i < connector->count_modes; i++) {
+ mode = &connector->modes[i];
+
+ if (!preferred && mode->type & DRM_MODE_TYPE_PREFERRED)
+ preferred = mode;
+
+ if (mode->hdisplay == output->preferred_width &&
+ mode->vdisplay == output->preferred_height) {
+ preferred = mode;
break;
}
}
- /* If there was no preferred mode use first one. */
- if (modes == main_monitor_connector->count_modes)
- drm->console_mode_info = main_monitor_connector->modes[0];
-
- drmModeFreeConnector(main_monitor_connector);
- get_connector_path(drm, drm->console_connector_id, NULL, &console_crtc_id);
+ if (preferred)
+ output->mode = *preferred;
+ else
+ output->mode = connector->modes[connector->count_modes - 1];
- if (!console_crtc_id)
- /* No existing path, find one. */
- find_crtc_for_connector(drm, drm->console_connector_id, &console_crtc_id);
+ drmModeFreeConnector(connector);
+ return true;
+err:
+ drmModeFreeConnector(connector);
+ return false;
+}
- if (!console_crtc_id)
- /* Cannot find CRTC for connector. We will not be able to use it. */
+static bool drm_remove_output(drm_t* drm, uint32_t connector_id)
+{
+ if (!connector_id)
return false;
- return true;
+ drm_for_each_output(drm, output) {
+ if (output->connector_id != connector_id)
+ continue;
+
+ LOG(INFO, "Removing output(%s)", output->name);
+
+ drmModeSetCrtc(drm->fd, output->crtc_id, -1, 0, 0,
+ NULL, -1, NULL);
+
+ free(output->name);
+
+ /* Hacky way to remove entry */
+ do {
+ memcpy(output, output + 1, sizeof(*output));
+ output ++;
+ } while (output->connector_id);
+
+ drm->count_outputs --;
+ return true;
+ }
+
+ return false;
}
-static void drm_clear_rmfb(drm_t* drm)
+static bool drm_add_output(drm_t* drm, uint32_t connector_id)
{
- if (drm->delayed_rmfb_fb_id) {
- drmModeRmFB(drm->fd, drm->delayed_rmfb_fb_id);
- drm->delayed_rmfb_fb_id = 0;
+ drmModePlaneResPtr pres;
+ drm_output_t *output;
+ uint32_t crtc_pipe = 0, count_primaries = 0;
+ const char *env;
+ int i;
+
+ if (!connector_id)
+ return false;
+
+ drm_remove_output(drm, connector_id);
+
+ if (drm->count_outputs >= MAX_OUTPUTS)
+ return false;
+
+ if (!drm_is_connected(drm, connector_id))
+ return false;
+
+ output = &drm->outputs[drm->count_outputs];
+ output->connector_id = connector_id;
+
+ output->name = drm_make_name(drm, connector_id);
+ if (!output->name)
+ goto err;
+
+ if (!drm_get_mode(drm, output))
+ goto err;
+
+ if (!get_crtc_for_connector(drm, connector_id, &output->crtc_id) &&
+ !find_crtc_for_connector(drm, connector_id, &output->crtc_id))
+ goto err;
+
+ for (i = 0; i < drm->resources->count_crtcs; i++) {
+ if (drm->resources->crtcs[i] == output->crtc_id) {
+ crtc_pipe = i;
+ break;
+ }
}
+
+ pres = drmModeGetPlaneResources(drm->fd);
+ if (!pres)
+ goto err;
+
+ for (i = 0; i < (int)pres->count_planes; i++) {
+ drmModePlanePtr plane;
+
+ if (!drm_is_primary_plane(drm, pres->planes[i]))
+ continue;
+
+ count_primaries ++;
+
+ if ((count_primaries - 1) != crtc_pipe)
+ continue;
+
+ plane = drmModeGetPlane(drm->fd, pres->planes[i]);
+ if (plane) {
+ if (plane->possible_crtcs & 1 << crtc_pipe)
+ output->plane_id = plane->plane_id;
+ drmModeFreePlane(plane);
+ }
+ break;
+ }
+
+ drmModeFreePlaneResources(pres);
+
+ if (!output->plane_id)
+ goto err;
+
+ /* For example: FRECON_OUTPUT_CONFIG="fit:0,DSI-1=stretch:90:1920x1080" */
+ if ((env = getenv("FRECON_OUTPUT_CONFIG"))) {
+ char fill[64] = "\0";
+ int rotation = 0;
+
+ if (strstr(env, output->name))
+ env = strstr(env, output->name) +
+ strlen(output->name) + 1;
+
+ output->preferred_width = output->preferred_height = 0;
+ sscanf(env, "%[a-z]", fill);
+ sscanf(env, "%*[^0-9,]%d", &rotation);
+ sscanf(env, "%*[^0-9,]%*d:%dx%d",
+ &output->preferred_width, &output->preferred_height);
+
+ if (!strcmp(fill, "fit"))
+ output->fill = DRM_FILL_FIT;
+ else if (!strcmp(fill, "stretch"))
+ output->fill = DRM_FILL_STRETCH;
+ else if (!strcmp(fill, "none"))
+ output->fill = DRM_FILL_NONE;
+
+ if (rotation == 90)
+ output->rotation = DRM_ROTATION_90;
+ else if (rotation == 180)
+ output->rotation = DRM_ROTATION_180;
+ else if (rotation == 270)
+ output->rotation = DRM_ROTATION_270;
+ else
+ output->rotation = DRM_ROTATION_0;
+ } else if ((env = getenv("FRECON_FB_ROTATE"))) {
+ int rotation = atoi(env);
+
+ if (rotation == 90)
+ output->rotation = DRM_ROTATION_90;
+ else if (rotation == 180)
+ output->rotation = DRM_ROTATION_180;
+ else if (rotation == 270)
+ output->rotation = DRM_ROTATION_270;
+ else
+ output->rotation = DRM_ROTATION_0;
+ }
+
+ drm->count_outputs ++;
+
+ LOG(INFO, "Added output(%s): fill=%s, rotate=%d, %dx%d", output->name,
+ (output->fill == DRM_FILL_FIT) ? "fit" :
+ (output->fill == DRM_FILL_STRETCH ? "stretch" : "none"),
+ output->rotation * 90,
+ output->preferred_width, output->preferred_height);
+
+ return true;
+err:
+ if (output->name)
+ free(output->name);
+
+ memset(output, 0, sizeof(*output));
+ return false;
}
static void drm_fini(drm_t* drm)
{
+ drm_output_t *output;
+
if (!drm)
return;
- if (drm->fd >= 0) {
- drm_clear_rmfb(drm);
+ if (drm->fd < 0) {
+ free(drm);
+ return;
+ }
- if (drm->plane_resources) {
- drmModeFreePlaneResources(drm->plane_resources);
- drm->plane_resources = NULL;
- }
+ output = &drm->outputs[0];
+ while (output->connector_id)
+ drm_remove_output(drm, output->connector_id);
- if (drm->resources) {
- drmModeFreeResources(drm->resources);
- drm->resources = NULL;
- }
+ if (drm->resources)
+ drmModeFreeResources(drm->resources);
- drmClose(drm->fd);
- drm->fd = -1;
- }
+ drmClose(drm->fd);
free(drm);
}
-static bool drm_equal(drm_t* l, drm_t* r)
+void drm_close(void)
{
- if (!l && !r)
- return true;
- if ((!l && r) || (l && !r))
- return false;
+ if (!g_drm)
+ return;
- if (l->console_connector_id != r->console_connector_id)
- return false;
- return true;
+ drm_fini(g_drm);
+ g_drm = NULL;
}
/*
* Find DRM object to display frecon on.
*/
-drm_t* drm_scan(void)
+static drm_t* drm_scan(void)
{
int ret;
uint64_t atomic = 0;
@@ -364,15 +524,6 @@ drm_t* drm_scan(void)
return NULL;
}
- drm->plane_resources = drmModeGetPlaneResources(drm->fd);
-
- if (!find_main_monitor(drm)) {
- drm_fini(drm);
- return NULL;
- }
-
- drm->refcount = 1;
-
version = drmGetVersion(drm->fd);
if (version) {
LOG(INFO,
@@ -388,198 +539,166 @@ drm_t* drm_scan(void)
return drm;
}
-void drm_set(drm_t* drm_)
+bool drm_init(void)
{
- if (g_drm) {
- drm_delref(g_drm);
- g_drm = NULL;
- }
- g_drm = drm_;
-}
+ drm_t *drm;
+ uint32_t connector_id;
+ const char *env;
+ int i;
-void drm_close(void)
-{
- if (g_drm) {
- drm_delref(g_drm);
- g_drm = NULL;
- }
-}
+ drm_close();
-void drm_delref(drm_t* drm)
-{
+ drm = drm_scan();
if (!drm)
- return;
- if (drm->refcount) {
- drm->refcount--;
- } else {
- LOG(ERROR, "Imbalanced drm_close()");
- }
- if (drm->refcount) {
- return;
- }
-
- drm_fini(drm);
-}
+ return false;
-drm_t* drm_addref(void)
-{
- if (g_drm) {
- g_drm->refcount++;
- return g_drm;
+ env = getenv("FRECON_SINGLE_HEAD");
+ if (env && !strcmp(env, "1")) {
+ LOG(INFO, "Single head mode.");
+ drm->single_head = true;
}
- return NULL;
-}
-
-/*
- * Returns true if connector/crtc/driver have changed and framebuffer object have to be re-created.
- */
-bool drm_rescan(void)
-{
- drm_t* ndrm;
-
- ndrm = drm_scan();
- if (ndrm) {
- if (drm_equal(ndrm, g_drm)) {
- drm_fini(ndrm);
- } else {
- drm_delref(g_drm);
- g_drm = ndrm;
- return true;
- }
+ if (drm->single_head) {
+ connector_id = find_main_monitor(drm);
+ drm_add_output(drm, connector_id);
} else {
- if (g_drm) {
- drm_delref(g_drm); /* No usable monitor/drm object. */
- g_drm = NULL;
- return true;
+ for (i = 0; i < drm->resources->count_connectors; i++) {
+ connector_id = drm->resources->connectors[i];
+ drm_add_output(drm, connector_id);
}
}
- return false;
-}
-bool drm_valid(drm_t* drm) {
- return drm && drm->fd >= 0 && drm->resources && drm->console_connector_id;
-}
+ LOG(INFO, "Inited with %d outputs", drm->count_outputs);
-static int remove_gamma_properties(drm_t* drm, uint32_t crtc_id) {
- drmModeObjectPropertiesPtr crtc_props = NULL;
+ g_drm = drm;
+ return true;
+}
- crtc_props = drmModeObjectGetProperties(drm->fd,
- crtc_id,
- DRM_MODE_OBJECT_CRTC);
- if (!crtc_props) {
- LOG(ERROR, "Could not query properties for crtc %d %m.", crtc_id);
- return -ENOENT;
- }
+/* Return true if we already has it and will keep it */
+static bool drm_got_output(drm_t *drm, uint32_t connector_id)
+{
+ drm_for_each_output(drm, output) {
+ drmModeModeInfo mode = output->mode;
- for (uint32_t i = 0; i < crtc_props->count_props; i++) {
- drmModePropertyPtr prop;
- prop = drmModeGetProperty(drm->fd, crtc_props->props[i]);
- if (!prop)
+ if (output->connector_id != connector_id)
continue;
- // Remove the GAMMA_LUT and DEGAMMA_LUT properties.
- if (!strcmp(prop->name, "GAMMA_LUT") ||
- !strcmp(prop->name, "DEGAMMA_LUT")) {
- // Ignore the return in case it is not supported.
- if (drmModeObjectSetProperty(drm->fd, crtc_id,
- DRM_MODE_OBJECT_CRTC,
- crtc_props->props[i],
- 0)) {
- LOG(ERROR, "Unable to remove %s from crtc:%d %m", prop->name, crtc_id);
- }
+ /* Check connected and the preferred mode is unchanged */
+ if (drm_is_connected(drm, connector_id)) {
+ if (drm_get_mode(drm, output) &&
+ !strcmp(mode.name, output->mode.name))
+ return true;
}
- drmModeFreeProperty(prop);
+
+ output->mode = mode;
+ break;
}
- drmModeFreeObjectProperties(crtc_props);
- return 0;
-}
+ return false;
+}
-int32_t drm_setmode(drm_t* drm, uint32_t fb_id)
+/*
+ * Returns true if connector/crtc/driver have changed and framebuffer object have to be re-created.
+ */
+bool drm_rescan(void)
{
- int conn;
- int32_t ret = 0;
- uint32_t existing_console_crtc_id = 0;
+ drm_t* drm = g_drm;
+ uint32_t connector_id;
+ bool changed;
+ int i;
- get_connector_path(drm, drm->console_connector_id, NULL, &existing_console_crtc_id);
+ drmModeFreeResources(drm->resources);
- /* Loop through all the connectors, disable ones that are configured and set video mode on console connector. */
- for (conn = 0; conn < drm->resources->count_connectors; conn++) {
- uint32_t connector_id = drm->resources->connectors[conn];
+ drm->resources = drmModeGetResources(drm->fd);
+ if (!drm->resources)
+ goto err;
- if (connector_id == drm->console_connector_id) {
- uint32_t console_crtc_id = 0;
+ if (drm->single_head) {
+ connector_id = find_main_monitor(drm);
+ if (drm_got_output(drm, connector_id))
+ goto out;
- if (existing_console_crtc_id)
- console_crtc_id = existing_console_crtc_id;
- else {
- find_crtc_for_connector(drm, connector_id, &console_crtc_id);
+ if (drm_remove_output(drm, drm->outputs[0].connector_id))
+ changed = true;
- if (!console_crtc_id) {
- LOG(ERROR, "Could not get console crtc for connector:%d in modeset.\n", drm->console_connector_id);
- return -ENOENT;
- }
- }
+ if (drm_add_output(drm, connector_id))
+ changed = true;
- ret = drmModeSetCrtc(drm->fd, console_crtc_id,
- fb_id,
- 0, 0, // x,y
- &drm->console_connector_id,
- 1, // connector_count
- &drm->console_mode_info); // mode
+ goto out;
+ }
- if (ret) {
- LOG(ERROR, "Unable to set crtc:%d connector:%d %m", console_crtc_id, drm->console_connector_id);
- return ret;
- }
+ for (i = 0; i < drm->resources->count_connectors; i++) {
+ connector_id = drm->resources->connectors[i];
+
+ if (!drm_is_connected(drm, connector_id)) {
+ if (drm_remove_output(drm, connector_id))
+ changed = true;
+ continue;
+ }
+
+ if (drm_got_output(drm, connector_id))
+ continue;
- ret = drmModeSetCursor(drm->fd, console_crtc_id,
- 0, 0, 0);
+ drm_add_output(drm, connector_id);
+ changed = true;
+ }
+out:
+ if (changed)
+ LOG(INFO, "Updated with %d outputs", drm->count_outputs);
- if (ret)
- LOG(ERROR, "Unable to hide cursor on crtc:%d %m.", console_crtc_id);
+ return changed;
+err:
+ drm_fini(drm);
+ g_drm = NULL;
+ return true;
+}
- ret = remove_gamma_properties(drm, console_crtc_id);
- if (ret)
- LOG(ERROR, "Unable to remove gamma LUT properties from crtc:%d %m.", console_crtc_id);
+int32_t drm_setmode(drm_t *drm, drm_output_t *output, uint32_t fb_id,
+ uint32_t src_w, uint32_t src_h)
+{
+ uint32_t dst_x, dst_y, dst_w, dst_h;
+ int32_t ret;
- drm_disable_non_primary_planes(drm, console_crtc_id);
+ if (!output)
+ return 0;
- } else {
- uint32_t crtc_id = 0;
+ /* Make sure that all planes and cursor are disabled on the CRTC */
+ drmModeSetCrtc(drm->fd, output->crtc_id, 0, 0, 0, NULL, 0, NULL);
- get_connector_path(drm, connector_id, NULL, &crtc_id);
- if (!crtc_id)
- /* This connector is not configured, skip. */
- continue;
+ if (!fb_id)
+ return 0;
- if (existing_console_crtc_id && existing_console_crtc_id == crtc_id)
- /* This connector is mirroring from the same CRTC as console. It will be turned off when console is set. */
- continue;
+ LOG(INFO, "Displaying on output(%s)", output->name);
- ret = drmModeSetCrtc(drm->fd, crtc_id, 0, // buffer_id
- 0, 0, // x,y
- NULL, // connectors
- 0, // connector_count
- NULL); // mode
- if (ret)
- LOG(ERROR, "Unable to disable crtc %d: %m", crtc_id);
- }
+ ret = drmModeSetCrtc(drm->fd, output->crtc_id, fb_id, 0, 0,
+ &output->connector_id, 1, &output->mode);
+ if (ret) {
+ LOG(ERROR, "Unable to set crtc:%d connector:%d %m",
+ output->crtc_id, output->connector_id);
+ return ret;
}
- drm_clear_rmfb(drm);
- /* LOG(INFO, "TIMING: Console switch modeset finished."); */
- return ret;
-}
+ dst_x = dst_y = 0;
+ dst_w = output->mode.hdisplay;
+ dst_h = output->mode.vdisplay;
+
+ switch(output->fill) {
+ case DRM_FILL_NONE:
+ dst_w = src_w;
+ dst_h = src_h;
+ break;
+ case DRM_FILL_FIT:
+ if (dst_w * src_h > src_w * dst_h)
+ dst_w = src_w * dst_h / src_h;
+ else
+ dst_h = dst_w * src_h / src_w;
+ break;
+ case DRM_FILL_STRETCH:
+ default:
+ break;
+ }
-/*
- * Delayed rmfb(). We want to keep fb at least till after next modeset
- * so our transitions are cleaner (e.g. when recreating term after exitin
- * shell). Also it keeps fb around till Chrome starts.
- */
-void drm_rmfb(drm_t* drm, uint32_t fb_id)
-{
- drm_clear_rmfb(drm);
- drm->delayed_rmfb_fb_id = fb_id;
+ return drmModeSetPlane(drm->fd, output->plane_id, output->crtc_id,
+ fb_id, 0, dst_x, dst_y, dst_w, dst_h,
+ 0, 0, src_w << 16, src_h << 16);
}
diff --git a/drm.h b/drm.h
index ff42efc..4afa343 100644
--- a/drm.h
+++ b/drm.h
@@ -7,30 +7,70 @@
#ifndef DRM_H
#define DRM_H
-
#include <stdbool.h>
#include <stdio.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
+#define MAX_OUTPUTS 16
+
+typedef enum {
+ DRM_FILL_NONE,
+ DRM_FILL_FIT,
+ DRM_FILL_STRETCH,
+} drm_fill_t;
+
+typedef enum {
+ DRM_ROTATION_0,
+ DRM_ROTATION_90,
+ DRM_ROTATION_180,
+ DRM_ROTATION_270,
+ DRM_ROTATION_MAX,
+} drm_rotation_t;
+
+typedef struct _output_t {
+ char *name;
+ uint32_t connector_id;
+
+ uint32_t crtc_id;
+ uint32_t plane_id;
+ drmModeModeInfo mode;
+
+ drm_fill_t fill;
+ drm_rotation_t rotation;
+
+ uint32_t preferred_width;
+ uint32_t preferred_height;
+} drm_output_t;
+
typedef struct _drm_t {
- int refcount;
int fd;
drmModeRes* resources;
- drmModePlaneResPtr plane_resources;
- uint32_t console_connector_id;
- drmModeModeInfo console_mode_info;
- uint32_t delayed_rmfb_fb_id;
+
+ bool single_head;
+
+ drm_output_t outputs[MAX_OUTPUTS];
+ uint32_t count_outputs;
} drm_t;
-drm_t* drm_scan(void);
-void drm_set(drm_t* drm);
+#define drm_output_width(output) \
+ (((output)->rotation == DRM_ROTATION_0 || \
+ (output)->rotation == DRM_ROTATION_180) ? \
+ (output)->mode.hdisplay : (output)->mode.vdisplay)
+
+#define drm_output_height(output) \
+ (((output)->rotation == DRM_ROTATION_0 || \
+ (output)->rotation == DRM_ROTATION_180) ? \
+ (output)->mode.vdisplay : (output)->mode.hdisplay)
+
+#define drm_for_each_output(drm, output) \
+ for (drm_output_t *output = &(drm)->outputs[0]; output->name; output++)
+
+bool drm_init(void);
void drm_close(void);
-drm_t* drm_addref(void);
-void drm_delref(drm_t* drm);
+drm_t* drm_get(void);
bool drm_rescan(void);
-bool drm_valid(drm_t* drm);
-int32_t drm_setmode(drm_t* drm, uint32_t fb_id);
-void drm_rmfb(drm_t* drm, uint32_t fb_id);
+int32_t drm_setmode(drm_t *drm, drm_output_t *output, uint32_t fb_id,
+ uint32_t src_w, uint32_t src_h);
#endif
diff --git a/fb.c b/fb.c
index 71de475..5d40795 100644
--- a/fb.c
+++ b/fb.c
@@ -19,18 +19,73 @@
#include "util.h"
#include "fb.h"
-static int fb_buffer_create(fb_t* fb,
- int* pitch)
+static bool fb_buf_lock(fb_t *fb, fb_buf_t *buf)
+{
+ if (buf->lock.count == 0 && buf->handle > 0) {
+ buf->lock.map =
+ mmap(0, buf->size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fb->drm->fd, buf->lock.map_offset);
+ if (buf->lock.map == MAP_FAILED) {
+ LOG(ERROR, "mmap failed");
+ return false;
+ }
+ }
+
+ if (buf->lock.map)
+ buf->lock.count++;
+
+ return true;
+}
+
+static void fb_buf_unlock(fb_t *fb, fb_buf_t *buf)
+{
+ if (buf->lock.count > 0)
+ buf->lock.count--;
+ else
+ LOG(ERROR, "buf locking unbalanced");
+
+ if (buf->lock.count == 0 && buf->handle > 0) {
+ int32_t ret;
+ struct drm_clip_rect clip_rect = {
+ 0, 0, buf->width, buf->height
+ };
+ munmap(buf->lock.map, buf->size);
+ ret = drmModeDirtyFB(fb->drm->fd, buf->fb_id, &clip_rect, 1);
+ if (ret) {
+ int loglevel = ERROR;
+ /* Do not print "normal" errors by default. */
+ if (errno == ENOSYS || errno == EACCES)
+ loglevel = DEBUG;
+ LOG(loglevel, "drmModeDirtyFB failed: %d %m", errno);
+ }
+ }
+}
+
+static int fb_buffer_ensure(fb_t *fb, drm_output_t *output)
{
struct drm_mode_create_dumb create_dumb;
struct drm_mode_destroy_dumb destroy_dumb;
- uint32_t* fb_buffer;
+ fb_buf_t *buf;
+ uint32_t fb_id;
int ret;
+ fb_for_each_buf(fb, tmp)
+ if (tmp->rotation == output->rotation)
+ return 0;
+
+ buf = &fb->bufs[fb->count_bufs];
+
memset(&create_dumb, 0, sizeof (create_dumb));
create_dumb.bpp = 32;
- create_dumb.width = fb->drm->console_mode_info.hdisplay;
- create_dumb.height = fb->drm->console_mode_info.vdisplay;
+
+ drm_for_each_output(fb->drm, tmp) {
+ if (tmp->rotation != output->rotation)
+ continue;
+
+ create_dumb.width = MAX(create_dumb.width, tmp->mode.hdisplay);
+ create_dumb.height = MAX(create_dumb.height,
+ tmp->mode.vdisplay);
+ }
ret = drmIoctl(fb->drm->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
if (ret) {
@@ -38,10 +93,7 @@ static int fb_buffer_create(fb_t* fb,
return ret;
}
- fb->buffer_properties.size = create_dumb.size;
- fb->buffer_handle = create_dumb.handle;
-
- struct drm_mode_map_dumb map_dumb;
+ struct drm_mode_map_dumb map_dumb = {0};
map_dumb.handle = create_dumb.handle;
ret = drmIoctl(fb->drm->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
if (ret) {
@@ -49,30 +101,91 @@ static int fb_buffer_create(fb_t* fb,
goto destroy_buffer;
}
- fb->lock.map_offset = map_dumb.offset;
-
uint32_t handles[4] = {0};
uint32_t pitches[4] = {0};
uint32_t offsets[4] = {0};
handles[0] = create_dumb.handle;
pitches[0] = create_dumb.pitch;
- ret = drmModeAddFB2(fb->drm->fd, fb->drm->console_mode_info.hdisplay, fb->drm->console_mode_info.vdisplay,
+ ret = drmModeAddFB2(fb->drm->fd, create_dumb.width, create_dumb.height,
DRM_FORMAT_XRGB8888, handles,
- pitches, offsets, &fb->fb_id, 0);
+ pitches, offsets, &fb_id, 0);
if (ret) {
LOG(ERROR, "drmModeAddFB2 failed");
goto destroy_buffer;
}
- *pitch = create_dumb.pitch;
+ buf->rotation = output->rotation;
+ buf->width = create_dumb.width;
+ buf->height = create_dumb.height;
+ buf->pitch = create_dumb.pitch;
+ buf->size = create_dumb.size;
+ buf->handle = create_dumb.handle;
+ buf->fb_id = fb_id;
+ buf->lock.map_offset = map_dumb.offset;
+
+ switch (buf->rotation) {
+ case DRM_ROTATION_90:
+ buf->src_width = fb->height;
+ buf->src_height = fb->width;
+
+ buf->m[0][0] = 0;
+ buf->m[0][1] = -1;
+ buf->m[0][2] = buf->src_width - 1;
+
+ buf->m[1][0] = 1;
+ buf->m[1][1] = 0;
+ buf->m[1][2] = 0;
+ break;
+ case DRM_ROTATION_270:
+ buf->src_width = fb->height;
+ buf->src_height = fb->width;
+
+ buf->m[0][0] = 0;
+ buf->m[0][1] = 1;
+ buf->m[0][2] = 0;
+
+ buf->m[1][0] = -1;
+ buf->m[1][1] = 0;
+ buf->m[1][2] = buf->src_height - 1;
+ break;
+ case DRM_ROTATION_180:
+ buf->src_width = fb->width;
+ buf->src_height = fb->height;
+
+ buf->m[0][0] = -1;
+ buf->m[0][1] = 0;
+ buf->m[0][2] = buf->src_width - 1;
+
+ buf->m[1][0] = 0;
+ buf->m[1][1] = -1;
+ buf->m[1][2] = buf->src_height - 1;
+ break;
+ case DRM_ROTATION_0:
+ default:
+ buf->src_width = fb->width;
+ buf->src_height = fb->height;
+
+ buf->m[0][0] = 1;
+ buf->m[0][1] = 0;
+ buf->m[0][2] = 0;
- fb_buffer = fb_lock(fb);
- if (fb_buffer) {
- memset(fb_buffer, 0, fb->buffer_properties.size);
- fb_unlock(fb);
+ buf->m[1][0] = 0;
+ buf->m[1][1] = 1;
+ buf->m[1][2] = 0;
+ break;
}
+ fb->count_bufs ++;
+
+ LOG(INFO, "Created FB buf: %dx%d(%dx%d) for rotate=%d",
+ buf->width, buf->height, buf->src_width, buf->src_height,
+ buf->rotation * 90);
+
+ if (fb_buf_lock(fb, buf)) {
+ memset(buf->lock.map, 0, buf->size);
+ fb_buf_unlock(fb, buf);
+ }
return 0;
destroy_buffer:
@@ -87,84 +200,59 @@ void fb_buffer_destroy(fb_t* fb)
{
struct drm_mode_destroy_dumb destroy_dumb;
- if (fb->buffer_handle <= 0)
- goto unref_drm;
+ fb_for_each_buf(fb, buf) {
+ if (buf->lock.count) {
+ munmap(buf->lock.map, buf->size);
+ buf->lock.map = NULL;
+ buf->lock.count = 0;
+ }
- drm_rmfb(fb->drm, fb->fb_id);
- fb->fb_id = 0;
- destroy_dumb.handle = fb->buffer_handle;
- drmIoctl(fb->drm->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb);
- fb->buffer_handle = 0;
- fb->lock.map = NULL;
- fb->lock.count = 0;
-unref_drm:
- if (fb->drm) {
- drm_delref(fb->drm);
- fb->drm = NULL;
+ LOG(INFO, "Destroying FB buf: %dx%d for rotate=%d",
+ buf->width, buf->height, buf->rotation * 90);
+
+ drmModeRmFB(fb->drm->fd, buf->fb_id);
+ buf->fb_id = 0;
+
+ destroy_dumb.handle = buf->handle;
+ drmIoctl(fb->drm->fd, DRM_IOCTL_MODE_DESTROY_DUMB,
+ &destroy_dumb);
+ buf->handle = 0;
}
+
+ fb->count_bufs = 0;
}
int fb_buffer_init(fb_t* fb)
{
- int32_t width, height, pitch = 0;
const char *buf;
int r;
- /* reuse the buffer_properties if it was set before */
- if (!fb->buffer_properties.width || !fb->buffer_properties.height ||
- !fb->buffer_properties.pitch || !fb->buffer_properties.scaling ||
- !fb->buffer_properties.rotation) {
- /* some reasonable defaults */
- fb->buffer_properties.width = 640;
- fb->buffer_properties.height = 480;
- fb->buffer_properties.pitch = 640 * 4;
- fb->buffer_properties.scaling = 1;
- fb->buffer_properties.rotation = DRM_MODE_ROTATE_0;
- }
-
- fb->drm = drm_addref();
-
- if (!fb->drm) {
- LOG(WARNING, "No monitor available, running headless!");
+ fb->drm = drm_get();
+ if (!fb->drm)
return -ENODEV;
- }
-
- width = fb->drm->console_mode_info.hdisplay;
- height = fb->drm->console_mode_info.vdisplay;
- r = fb_buffer_create(fb, &pitch);
- if (r < 0) {
- LOG(ERROR, "fb_buffer_create failed");
- return r;
+ fb->width = fb->height = INT32_MAX;
+ drm_for_each_output(fb->drm, output) {
+ fb->width = MIN(fb->width, drm_output_width(output));
+ fb->height = MIN(fb->height, drm_output_height(output));
}
- fb->buffer_properties.width = width;
- fb->buffer_properties.height = height;
- fb->buffer_properties.pitch = pitch;
+ LOG(INFO, "Initing FB: %dx%d", fb->width, fb->height);
- buf = getenv("FRECON_FB_ROTATE");
- if (buf) {
- switch (atoi(buf)) {
- case 90:
- fb->buffer_properties.rotation = DRM_MODE_ROTATE_90;
- break;
- case 180:
- fb->buffer_properties.rotation = DRM_MODE_ROTATE_180;
- break;
- case 270:
- fb->buffer_properties.rotation = DRM_MODE_ROTATE_270;
- break;
- default:
- fb->buffer_properties.rotation = DRM_MODE_ROTATE_0;
- break;
+ drm_for_each_output(fb->drm, output) {
+ r = fb_buffer_ensure(fb, output);
+ if (r < 0) {
+ LOG(ERROR, "failed to create buffer for %s",
+ output->name);
+ return r;
}
}
buf = getenv("FRECON_FB_SCALE");
if (buf) {
- fb->buffer_properties.scaling = atoi(buf);
- if (fb->buffer_properties.scaling < 1)
- fb->buffer_properties.scaling = 1;
+ fb->scaling = atoi(buf);
+ if (fb->scaling < 1)
+ fb->scaling = 1;
}
return 0;
@@ -195,90 +283,63 @@ void fb_close(fb_t* fb)
int32_t fb_setmode(fb_t* fb)
{
- /* headless mode */
- if (!drm_valid(fb->drm))
+ if (!fb->drm)
return 0;
- return drm_setmode(fb->drm, fb->fb_id);
-}
-
-uint32_t* fb_lock(fb_t* fb)
-{
- if (fb->lock.count == 0 && fb->buffer_handle > 0) {
- fb->lock.map =
- mmap(0, fb->buffer_properties.size, PROT_READ | PROT_WRITE,
- MAP_SHARED, fb->drm->fd, fb->lock.map_offset);
- if (fb->lock.map == MAP_FAILED) {
- LOG(ERROR, "mmap failed");
- return NULL;
+ drm_for_each_output(fb->drm, output) {
+ fb_for_each_buf(fb, buf) {
+ if (buf->rotation == output->rotation) {
+ int32_t r = drm_setmode(fb->drm, output,
+ buf->fb_id,
+ buf->src_width,
+ buf->src_height);
+ if (r < 0)
+ LOG(ERROR, "Failed to set mode for %s",
+ output->name);
+ break;
+ }
}
}
- if (fb->lock.map)
- fb->lock.count++;
-
- return fb->lock.map;
+ return 0;
}
-void fb_unlock(fb_t* fb)
-{
- if (fb->lock.count > 0)
- fb->lock.count--;
- else
- LOG(ERROR, "fb locking unbalanced");
+bool fb_lock(fb_t* fb) {
+ fb_for_each_buf(fb, buf) {
+ if (fb_buf_lock(fb, buf))
+ continue;
- if (fb->lock.count == 0 && fb->buffer_handle > 0) {
- int32_t ret;
- struct drm_clip_rect clip_rect = {
- 0, 0, fb->buffer_properties.width, fb->buffer_properties.height
- };
- munmap(fb->lock.map, fb->buffer_properties.size);
- ret = drmModeDirtyFB(fb->drm->fd, fb->fb_id, &clip_rect, 1);
- if (ret) {
- int loglevel = ERROR;
- /* Do not print "normal" errors by default. */
- if (errno == ENOSYS || errno == EACCES)
- loglevel = DEBUG;
- LOG(loglevel, "drmModeDirtyFB failed: %d %m", errno);
+ fb_for_each_buf(fb, tmp) {
+ if (tmp == buf)
+ break;
+ fb_buf_unlock(fb, tmp);
}
+ return false;
}
+ return true;
+}
+
+void fb_unlock(fb_t* fb) {
+ fb_for_each_buf(fb, buf)
+ fb_buf_unlock(fb, buf);
}
int32_t fb_getwidth(fb_t* fb)
{
- switch (fb->buffer_properties.rotation) {
- case DRM_MODE_ROTATE_90:
- case DRM_MODE_ROTATE_270:
- return fb->buffer_properties.height;
- break;
- case DRM_MODE_ROTATE_0:
- case DRM_MODE_ROTATE_180:
- default:
- return fb->buffer_properties.width;
- }
+ return fb->width;
}
int32_t fb_getheight(fb_t* fb)
{
- switch (fb->buffer_properties.rotation) {
- case DRM_MODE_ROTATE_90:
- case DRM_MODE_ROTATE_270:
- return fb->buffer_properties.width;
- break;
- case DRM_MODE_ROTATE_0:
- case DRM_MODE_ROTATE_180:
- default:
- return fb->buffer_properties.height;
- }
+ return fb->height;
}
int32_t fb_getscaling(fb_t* fb)
{
- return fb->buffer_properties.scaling;
+ return fb->scaling;
}
-bool
-fb_stepper_init(fb_stepper_t *s, fb_t *fb, int32_t x, int32_t y, uint32_t width, uint32_t height)
+bool fb_stepper_init(fb_stepper_t *s, fb_t *fb, int32_t x, int32_t y, uint32_t width, uint32_t height)
{
s->fb = fb;
s->start_x = x;
@@ -287,67 +348,44 @@ fb_stepper_init(fb_stepper_t *s, fb_t *fb, int32_t x, int32_t y, uint32_t width,
s->h = height;
s->x = 0;
s->y = 0;
- s->pitch_div_4 = s->fb->buffer_properties.pitch >> 2;
/* quick check if whole rect is outside fb */
if (x + width <= 0 || y + height <= 0)
return false;
- switch (s->fb->buffer_properties.rotation) {
- case DRM_MODE_ROTATE_90:
- case DRM_MODE_ROTATE_270:
- s->max_x = s->fb->buffer_properties.height;
- s->max_y = s->fb->buffer_properties.width;
- break;
- case DRM_MODE_ROTATE_180:
- case DRM_MODE_ROTATE_0:
- default:
- s->max_x = s->fb->buffer_properties.width;
- s->max_y = s->fb->buffer_properties.height;
- }
-
- if (x >= s->max_x
- || y >= s->max_y)
+ s->max_x = s->fb->width;
+ s->max_y = s->fb->height;
+ if (x >= s->max_x || y >= s->max_y)
return false;
- switch (s->fb->buffer_properties.rotation) {
- case DRM_MODE_ROTATE_90:
- s->m[0][0] = 0;
- s->m[0][1] = -1;
- s->m[0][2] = s->fb->buffer_properties.width - 1;
-
- s->m[1][0] = 1;
- s->m[1][1] = 0;
- s->m[1][2] = 0;
- break;
- case DRM_MODE_ROTATE_270:
- s->m[0][0] = 0;
- s->m[0][1] = 1;
- s->m[0][2] = 0;
-
- s->m[1][0] = -1;
- s->m[1][1] = 0;
- s->m[1][2] = s->fb->buffer_properties.height - 1;
- break;
- case DRM_MODE_ROTATE_180:
- s->m[0][0] = -1;
- s->m[0][1] = 0;
- s->m[0][2] = s->fb->buffer_properties.width - 1;
-
- s->m[1][0] = 0;
- s->m[1][1] = -1;
- s->m[1][2] = s->fb->buffer_properties.height - 1;
- break;
- case DRM_MODE_ROTATE_0:
- default:
- s->m[0][0] = 1;
- s->m[0][1] = 0;
- s->m[0][2] = 0;
+ return true;
+}
- s->m[1][0] = 0;
- s->m[1][1] = 1;
- s->m[1][2] = 0;
+bool fb_stepper_step_x(fb_stepper_t *s, uint32_t rgba)
+{
+ int32_t x = s->start_x + s->x;
+ int32_t y = s->start_y + s->y;
+ int32_t p;
+
+ if (x >= 0 && x < s->max_x && y >= 0 && y < s->max_y) {
+ fb_for_each_buf(s->fb, buf) {
+ p = (x * buf->m[0][0] + y * buf->m[0][1] + buf->m[0][2])
+ + (x * buf->m[1][0] + y * buf->m[1][1] +
+ buf->m[1][2]) * buf->pitch / 4;
+ buf->lock.map[p] = rgba;
+ }
}
+ s->x++;
+ if (s->x >= s->w) {
+ s->x = 0;
+ return false;
+ }
return true;
}
+
+bool fb_stepper_step_y(fb_stepper_t *s)
+{
+ s->y++;
+ return s->y < s->h;
+}
diff --git a/fb.h b/fb.h
index 8c67e0f..8596e87 100644
--- a/fb.h
+++ b/fb.h
@@ -9,15 +9,6 @@
#include "drm.h"
-typedef struct {
- int32_t width;
- int32_t height;
- int32_t pitch;
- int32_t scaling;
- int32_t size;
- int32_t rotation; // DRM_MODE_ROTATE_*
-} buffer_properties_t;
-
typedef struct {
int32_t count;
uint64_t map_offset;
@@ -25,60 +16,58 @@ typedef struct {
} fb_lock_t;
typedef struct {
- drm_t *drm;
- buffer_properties_t buffer_properties;
- fb_lock_t lock;
- uint32_t buffer_handle;
+ uint32_t handle;
+
+ uint32_t width;
+ uint32_t height;
+ uint32_t pitch;
+ uint32_t size;
+
+ uint32_t src_width;
+ uint32_t src_height;
+
uint32_t fb_id;
+
+ int32_t m[2][3];
+
+ drm_rotation_t rotation;
+ fb_lock_t lock;
+} fb_buf_t;
+
+typedef struct {
+ drm_t *drm;
+
+ int32_t width;
+ int32_t height;
+
+ int32_t scaling;
+
+ fb_buf_t bufs[DRM_ROTATION_MAX];
+ uint32_t count_bufs;
} fb_t;
typedef struct {
fb_t *fb;
int32_t start_x, start_y;
- uint32_t w, h;
- uint32_t x, y;
+ int32_t x, y, w, h;
int32_t max_x, max_y;
- uint32_t pitch_div_4;
- int32_t m[2][3];
} fb_stepper_t;
+#define fb_for_each_buf(fb, buf) \
+ for (fb_buf_t *buf = &(fb)->bufs[0]; buf->handle; buf++)
+
fb_t* fb_init(void);
void fb_close(fb_t* fb);
int32_t fb_setmode(fb_t* fb);
int fb_buffer_init(fb_t* fb);
void fb_buffer_destroy(fb_t* fb);
-uint32_t* fb_lock(fb_t* fb);
+bool fb_lock(fb_t* fb);
void fb_unlock(fb_t* fb);
int32_t fb_getwidth(fb_t* fb);
int32_t fb_getheight(fb_t* fb);
int32_t fb_getscaling(fb_t* fb);
bool fb_stepper_init(fb_stepper_t *s, fb_t *fb, int32_t x, int32_t y, uint32_t width, uint32_t height);
-
-bool static inline fb_stepper_step_x(fb_stepper_t *s, uint32_t rgba)
-{
- int32_t x = s->start_x + s->x;
- int32_t y = s->start_y + s->y;
- int32_t p;
-
- if (x >= 0 && x < s->max_x
- && y >= 0 && y < s->max_y) {
- p = (x * s->m[0][0] + y * s->m[0][1] + s->m[0][2])
- + (x * s->m[1][0] + y * s->m[1][1] + s->m[1][2]) * s->pitch_div_4;
- s->fb->lock.map[p] = rgba;
- }
-
- s->x++;
- if (s->x >= s->w) {
- s->x = 0;
- return false;
- }
- return true;
-}
-
-bool inline fb_stepper_step_y(fb_stepper_t *s)
-{
- s->y++;
- return s->y < s->h;
-}
+bool fb_stepper_step_x(fb_stepper_t *s, uint32_t rgba);
+bool fb_stepper_step_y(fb_stepper_t *s);
#endif
diff --git a/main.c b/main.c
index 5187531..e67bb87 100644
--- a/main.c
+++ b/main.c
@@ -181,7 +181,6 @@ int main(int argc, char* argv[])
int ret;
int c;
unsigned vt;
- drm_t* drm;
if (getenv("FRECON_VTS"))
command_flags.enable_vts = true;
@@ -271,7 +270,7 @@ int main(int argc, char* argv[])
return EXIT_FAILURE;
}
- drm_set(drm = drm_scan());
+ drm_init();
if (command_flags.pre_create_vts) {
for (unsigned vt = 1;
--
2.20.1