frecon: add atomic modeset V2
This should make switch to console potentially faster on kernels that
support atomic ioctl.
v2: fixed error check condition, added reset of gamma tables and color matrix.
BUG=chromium:863853,b:110258421
TEST=switch to console on Grunt
Change-Id: I9c1bf863959e3840157d5c3675454e238e226041
Signed-off-by: Dominik Behr <dbehr@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1300254
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Reviewed-by: Stéphane Marchesin <marcheu@chromium.org>
diff --git a/drm.c b/drm.c
index 6b2e568..8e7ad95 100644
--- a/drm.c
+++ b/drm.c
@@ -21,6 +21,33 @@
static drm_t* g_drm = NULL;
+static int32_t atomic_set_prop(drm_t* drm, drmModeAtomicReqPtr pset, uint32_t id,
+ drmModeObjectPropertiesPtr props, const char *name, uint64_t value)
+{
+ uint32_t u;
+ int32_t ret;
+ drmModePropertyPtr prop;
+
+ for (u = 0; u < props->count_props; u++) {
+ prop = drmModeGetProperty(drm->fd, props->props[u]);
+ if (!prop)
+ continue;
+ if (strcmp(prop->name, name)) {
+ drmModeFreeProperty(prop);
+ continue;
+ }
+ ret = drmModeAtomicAddProperty(pset, id, prop->prop_id, value);
+ if (ret < 0)
+ LOG(ERROR, "setting atomic property %s failed with %d\n", name, ret);
+ else
+ ret = 0;
+ drmModeFreeProperty(prop);
+ return ret;
+ }
+ LOG(ERROR, "could not find atomic property %s\n", name);
+ return -ENOENT;
+}
+
static int32_t crtc_planes_num(drm_t* drm, int32_t crtc_index)
{
drmModePlanePtr plane;
@@ -365,6 +392,7 @@
drm_t *best_drm = NULL;
for (i = 0; i < DRM_MAX_MINOR; i++) {
+ uint64_t atomic = 0;
drm_t* drm = calloc(1, sizeof(drm_t));
if (!drm)
@@ -394,6 +422,16 @@
/* Set universal planes cap if possible. Ignore any errors. */
drmSetClientCap(drm->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+ ret = drmGetCap(drm->fd, DRM_CLIENT_CAP_ATOMIC, &atomic);
+ if (!ret && atomic) {
+ drm->atomic = true;
+ ret = drmSetClientCap(drm->fd, DRM_CLIENT_CAP_ATOMIC, 1);
+ if (ret < 0) {
+ LOG(ERROR, "Failed to set atomic cap.");
+ drm->atomic = false;
+ }
+ }
+
drm->resources = drmModeGetResources(drm->fd);
if (!drm->resources) {
drm_fini(drm);
@@ -428,12 +466,13 @@
version = drmGetVersion(best_drm->fd);
if (version) {
LOG(INFO,
- "Frecon using drm driver %s, version %d.%d, date(%s), desc(%s)",
+ "Frecon using drm driver %s, version %d.%d, date(%s), desc(%s)%s",
version->name,
version->version_major,
version->version_minor,
version->date,
- version->desc);
+ version->desc,
+ best_drm->atomic ? " using atomic" : "");
drmFreeVersion(version);
}
}
@@ -540,12 +579,188 @@
return drm && drm->fd >= 0 && drm->resources && drm->console_connector_id;
}
+static bool is_crtc_possible(drm_t* drm, uint32_t crtc_id, uint32_t mask)
+{
+ int32_t crtc;
+ for (crtc = 0; crtc < drm->resources->count_crtcs; crtc++)
+ if (drm->resources->crtcs[crtc] == crtc_id)
+ return !!(mask & (1u << crtc));
+
+ return false;
+
+}
+
+#define CHECK(fn) do { ret = fn; if (ret < 0) goto error_mode; } while (0)
+static int32_t drm_setmode_atomic(drm_t* drm, uint32_t fb_id)
+{
+ int32_t ret;
+ int32_t crtc, conn;
+ uint32_t plane;
+ uint32_t console_crtc_id = 0;
+ drmModeObjectPropertiesPtr crtc_props = NULL;
+ drmModeObjectPropertiesPtr plane_props = NULL;
+ drmModeObjectPropertiesPtr conn_props = NULL;
+ drmModePlaneResPtr plane_resources;
+ drmModeAtomicReqPtr pset = NULL;
+ uint32_t mode_id = 0;
+
+ plane_resources = drmModeGetPlaneResources(drm->fd);
+ if (!plane_resources)
+ return -ENOENT;
+
+ get_connector_path(drm, drm->console_connector_id, NULL, &console_crtc_id);
+ if (!console_crtc_id)
+ find_crtc_for_connector(drm, drm->console_connector_id, &console_crtc_id);
+ if (!console_crtc_id) {
+ LOG(ERROR, "Could not get console crtc for connector:%d in modeset.\n", drm->console_connector_id);
+ return -ENOENT;
+ }
+
+ pset = drmModeAtomicAlloc();
+ if (!pset) {
+ ret = -ENOMEM;
+ goto error_mode;
+ }
+
+ for (crtc = 0; crtc < drm->resources->count_crtcs; crtc++) {
+ uint32_t crtc_id = drm->resources->crtcs[crtc];
+
+ 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);
+ if (crtc_id != console_crtc_id)
+ continue;
+ ret = -ENOENT;
+ goto error_mode;
+ }
+
+ if (crtc_id == console_crtc_id) {
+ CHECK(drmModeCreatePropertyBlob(drm->fd, &drm->console_mode_info,
+ sizeof(drm->console_mode_info),
+ &mode_id));
+ /* drm->crtc->mode has been set during init */
+ CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "MODE_ID", mode_id));
+ CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "ACTIVE", 1));
+ /* Reset color matrix to identity and gamma/degamma LUTs to pass through,
+ * ignore errors in case they are not supported. */
+ atomic_set_prop(drm, pset, crtc_id, crtc_props, "CTM", 0);
+ atomic_set_prop(drm, pset, crtc_id, crtc_props, "DEGAMMA_LUT", 0);
+ atomic_set_prop(drm, pset, crtc_id, crtc_props, "GAMMA_LUT", 0);
+ } else {
+ CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "MODE_ID", 0));
+ CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "ACTIVE", 0));
+ }
+
+ drmModeFreeObjectProperties(crtc_props);
+ crtc_props = NULL;
+ }
+
+ for (plane = 0; plane < plane_resources->count_planes; plane++) {
+ drmModePlanePtr planeobj;
+ uint32_t plane_id = plane_resources->planes[plane];
+ uint32_t possible_crtcs;
+ int primary;
+
+ planeobj = drmModeGetPlane(drm->fd, plane_id);
+ if (!planeobj) {
+ LOG(ERROR, "Could not query plane object for plane %d %m.", plane_id);
+ ret = -ENOENT;
+ goto error_mode;
+ }
+
+ possible_crtcs = planeobj->possible_crtcs;
+ drmModeFreePlane(planeobj);
+
+ primary = drm_is_primary_plane(drm, plane_id);
+
+ plane_props = drmModeObjectGetProperties(drm->fd, plane_id, DRM_MODE_OBJECT_PLANE);
+ if (!plane_props) {
+ LOG(ERROR, "Could not query properties for plane %d %m.", plane_id);
+ ret = -ENOENT;
+ goto error_mode;
+ }
+
+ if (is_crtc_possible(drm, console_crtc_id, possible_crtcs) && primary) {
+ CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "FB_ID", fb_id));
+ CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_ID", console_crtc_id));
+ CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_X", 0));
+ CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_Y", 0));
+ CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_W", drm->console_mode_info.hdisplay));
+ CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_H", drm->console_mode_info.vdisplay));
+ CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_X", 0));
+ CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_Y", 0));
+ CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_W", drm->console_mode_info.hdisplay << 16));
+ CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_H", drm->console_mode_info.vdisplay << 16));
+ } else {
+ CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "FB_ID", 0));
+ CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_ID", 0));
+ }
+
+ drmModeFreeObjectProperties(plane_props);
+ plane_props = NULL;
+ }
+
+ for (conn = 0; conn < drm->resources->count_connectors; conn++) {
+ uint32_t conn_id = drm->resources->connectors[conn];
+
+ conn_props = drmModeObjectGetProperties(drm->fd, conn_id, DRM_MODE_OBJECT_CONNECTOR);
+ if (!conn_props) {
+ LOG(ERROR, "Could not query properties for connector %d %m.", conn_id);
+ if (conn_id != drm->console_connector_id)
+ continue;
+ ret = -ENOENT;
+ goto error_mode;
+ }
+ if (conn_id == drm->console_connector_id)
+ CHECK(atomic_set_prop(drm, pset, conn_id, conn_props, "CRTC_ID", console_crtc_id));
+ else
+ CHECK(atomic_set_prop(drm, pset, conn_id, conn_props, "CRTC_ID", 0));
+ drmModeFreeObjectProperties(conn_props);
+ conn_props = NULL;
+ }
+
+ ret = drmModeAtomicCommit(drm->fd, pset,
+ DRM_MODE_ATOMIC_ALLOW_MODESET , NULL);
+ if (ret < 0) {
+ drm_clear_rmfb(drm);
+ /* LOG(INFO, "TIMING: Console switch atomic modeset finished."); */
+ } else {
+ ret = 0;
+ }
+
+error_mode:
+ if (mode_id)
+ drmModeDestroyPropertyBlob(drm->fd, mode_id);
+
+ if (plane_resources)
+ drmModeFreePlaneResources(plane_resources);
+
+ if (crtc_props)
+ drmModeFreeObjectProperties(crtc_props);
+
+ if (conn_props)
+ drmModeFreeObjectProperties(conn_props);
+
+ if (plane_props)
+ drmModeFreeObjectProperties(plane_props);
+
+ drmModeAtomicFree(pset);
+ return ret;
+}
+#undef CHECK
+
int32_t drm_setmode(drm_t* drm, uint32_t fb_id)
{
int conn;
int32_t ret;
uint32_t existing_console_crtc_id = 0;
+ if (drm->atomic)
+ if (drm_setmode_atomic(drm, fb_id) == 0)
+ return 0;
+ /* Fallback to legacy mode set. */
+
get_connector_path(drm, drm->console_connector_id, NULL, &existing_console_crtc_id);
/* Loop through all the connectors, disable ones that are configured and set video mode on console connector. */
diff --git a/drm.h b/drm.h
index 7fc17b9..65a523e 100644
--- a/drm.h
+++ b/drm.h
@@ -27,6 +27,7 @@
bool edid_found;
char edid[EDID_SIZE];
uint32_t delayed_rmfb_fb_id;
+ bool atomic;
} drm_t;
drm_t* drm_scan(void);