diff options
Diffstat (limited to 'minui/graphics_drm.cpp')
-rw-r--r-- | minui/graphics_drm.cpp | 499 |
1 files changed, 438 insertions, 61 deletions
diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp index 95759e38..17ab00af 100644 --- a/minui/graphics_drm.cpp +++ b/minui/graphics_drm.cpp @@ -14,8 +14,35 @@ * limitations under the License. */ +/* + * DRM based mode setting test program + * Copyright 2008 Tungsten Graphics + * Jakob Bornecrantz <jakob@tungstengraphics.com> + * Copyright 2008 Intel Corporation + * Jesse Barnes <jesse.barnes@intel.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + #include "graphics_drm.h" +#include <errno.h> #include <fcntl.h> #include <poll.h> #include <stdio.h> @@ -29,12 +56,256 @@ #include <android-base/macros.h> #include <android-base/stringprintf.h> #include <android-base/unique_fd.h> +#include <string> #include <drm_fourcc.h> #include <xf86drm.h> #include <xf86drmMode.h> +#include <sstream> #include "minui/minui.h" +#define find_prop_id(_res, type, Type, obj_id, prop_name, prop_id) \ + do { \ + int j = 0; \ + int prop_count = 0; \ + struct Type *obj = NULL; \ + obj = (_res); \ + if (!obj || main_monitor_##type->type##_id != (obj_id)){ \ + prop_id = 0; \ + break; \ + } \ + prop_count = (int)obj->props->count_props; \ + for (j = 0; j < prop_count; ++j) \ + if (!strcmp(obj->props_info[j]->name, (prop_name))) \ + break; \ + (prop_id) = (j == prop_count)? \ + 0 : obj->props_info[j]->prop_id; \ + } while (0) + +#define add_prop(res, type, Type, id, id_name, id_val) \ + find_prop_id(res, type, Type, id, id_name, prop_id); \ + if (prop_id) \ + drmModeAtomicAddProperty(atomic_req, id, prop_id, id_val); + +/** + * enum sde_rm_topology_name - HW resource use case in use by connector + * @SDE_RM_TOPOLOGY_NONE: No topology in use currently + * @SDE_RM_TOPOLOGY_SINGLEPIPE: 1 LM, 1 PP, 1 INTF/WB + * @SDE_RM_TOPOLOGY_SINGLEPIPE_DSC: 1 LM, 1 DSC, 1 PP, 1 INTF/WB + * @SDE_RM_TOPOLOGY_SINGLEPIPE_VDC: 1 LM, 1 VDC, 1 PP, 1 INTF/WB + * @SDE_RM_TOPOLOGY_DUALPIPE: 2 LM, 2 PP, 2 INTF/WB + * @SDE_RM_TOPOLOGY_DUALPIPE_DSC: 2 LM, 2 DSC, 2 PP, 2 INTF/WB + * @SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE: 2 LM, 2 PP, 3DMux, 1 INTF/WB + * @SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC: 2 LM, 2 PP, 3DMux, 1 DSC, 1 INTF/WB + * @SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC: 2 LM, 2 PP, 3DMux, 1 VDC, 1 INTF/WB + * @SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE: 2 LM, 2 PP, 2 DSC Merge, 1 INTF/WB + * @SDE_RM_TOPOLOGY_PPSPLIT: 1 LM, 2 PPs, 2 INTF/WB + * @SDE_RM_TOPOLOGY_QUADPIPE_3DMERGE 4 LM, 4 PP, 3DMux, 2 INTF + * @SDE_RM_TOPOLOGY_QUADPIPE_3DMERGE_DSC 4 LM, 4 PP, 3DMux, 3 DSC, 2 INTF + * @SDE_RM_TOPOLOGY_QUADPIPE_DSCMERE 4 LM, 4 PP, 4 DSC Merge, 2 INTF + * @SDE_RM_TOPOLOGY_QUADPIPE_DSC4HSMERGE 4 LM, 4 PP, 4 DSC Merge, 1 INTF + */ + +static uint32_t get_lm_number(const std::string &topology) { + if (topology == "sde_singlepipe") return 1; + if (topology == "sde_singlepipe_dsc") return 1; + if (topology == "sde_singlepipe_vdc") return 1; + if (topology == "sde_dualpipe") return 2; + if (topology == "sde_dualpipe_dsc") return 2; + if (topology == "sde_dualpipe_vdc") return 2; + if (topology == "sde_dualpipemerge") return 2; + if (topology == "sde_dualpipemerge_dsc") return 2; + if (topology == "sde_dualpipemerge_vdc") return 2; + if (topology == "sde_dualpipe_dscmerge") return 2; + if (topology == "sde_ppsplit") return 1; + if (topology == "sde_quadpipemerge") return 4; + if (topology == "sde_quadpipe_3dmerge_dsc") return 4; + if (topology == "sde_quadpipe_dscmerge") return 4; + if (topology == "sde_quadpipe_dsc4hsmerge") return 4; + return DEFAULT_NUM_LMS; +} + +static uint32_t get_topology_lm_number(int fd, uint32_t blob_id) { + uint32_t num_lm = DEFAULT_NUM_LMS; + + drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(fd, blob_id); + if (!blob) { + return num_lm; + } + + const char *fmt_str = (const char *)(blob->data); + std::stringstream stream(fmt_str); + std::string line = {}; + const std::string topology = "topology="; + + while (std::getline(stream, line)) { + if (line.find(topology) != std::string::npos) { + num_lm = get_lm_number(std::string(line, topology.length())); + break; + } + } + + drmModeFreePropertyBlob(blob); + return num_lm; +} + +static int find_plane_prop_id(uint32_t obj_id, const char *prop_name, + Plane *plane_res) { + int i, j = 0; + int prop_count = 0; + struct Plane *obj = NULL; + + for (i = 0; i < NUM_PLANES; ++i) { + obj = &plane_res[i]; + if (!obj || obj->plane->plane_id != obj_id) + continue; + prop_count = (int)obj->props->count_props; + for (j = 0; j < prop_count; ++j) + if (!strcmp(obj->props_info[j]->name, prop_name)) + return obj->props_info[j]->prop_id; + break; + } + + return 0; +} + +static int atomic_add_prop_to_plane(Plane *plane_res, drmModeAtomicReq *req, + uint32_t obj_id, const char *prop_name, + uint64_t value) { + uint32_t prop_id; + + prop_id = find_plane_prop_id(obj_id, prop_name, plane_res); + if (prop_id == 0) { + printf("Could not find obj_id = %d\n", obj_id); + return -EINVAL; + } + + if (drmModeAtomicAddProperty(req, obj_id, prop_id, value) < 0) { + printf("Could not add prop_id = %d for obj_id %d\n", + prop_id, obj_id); + return -EINVAL; + } + + return 0; +} + +int MinuiBackendDrm::AtomicPopulatePlane(int plane, drmModeAtomicReqPtr atomic_req) { + uint32_t src_x, src_y, src_w, src_h; + uint32_t crtc_x, crtc_y, crtc_w, crtc_h; + int width = main_monitor_crtc->mode.hdisplay; + int height = main_monitor_crtc->mode.vdisplay; + int zpos = 0; + + src_y = 0; + src_w = width/number_of_lms; + src_h = height; + crtc_y = 0; + crtc_w = width/number_of_lms; + crtc_h = height; + src_x = (width/number_of_lms) * plane; + crtc_x = (width/number_of_lms) * plane; + + /* populate z-order property required for 4 layer mixer */ + if (number_of_lms == 4) + zpos = plane >> 1; + + atomic_add_prop_to_plane(plane_res, atomic_req, + plane_res[plane].plane->plane_id, "zpos", zpos); + + if (atomic_add_prop_to_plane(plane_res, atomic_req, + plane_res[plane].plane->plane_id, "FB_ID", + GRSurfaceDrms[current_buffer]->fb_id)) + return -EINVAL; + + if (atomic_add_prop_to_plane(plane_res, atomic_req, + plane_res[plane].plane->plane_id, "SRC_X", src_x << 16)) + return -EINVAL; + + if (atomic_add_prop_to_plane(plane_res, atomic_req, + plane_res[plane].plane->plane_id, "SRC_Y", src_y << 16)) + return -EINVAL; + + if (atomic_add_prop_to_plane(plane_res, atomic_req, + plane_res[plane].plane->plane_id, "SRC_W", src_w << 16)) + return -EINVAL; + + if (atomic_add_prop_to_plane(plane_res, atomic_req, + plane_res[plane].plane->plane_id, "SRC_H", src_h << 16)) + return -EINVAL; + + if (atomic_add_prop_to_plane(plane_res, atomic_req, + plane_res[plane].plane->plane_id, "CRTC_X", crtc_x)) + return -EINVAL; + + if (atomic_add_prop_to_plane(plane_res, atomic_req, + plane_res[plane].plane->plane_id, "CRTC_Y", crtc_y)) + return -EINVAL; + + if (atomic_add_prop_to_plane(plane_res, atomic_req, + plane_res[plane].plane->plane_id, "CRTC_W", crtc_w)) + return -EINVAL; + + if (atomic_add_prop_to_plane(plane_res, atomic_req, + plane_res[plane].plane->plane_id, "CRTC_H", crtc_h)) + return -EINVAL; + + if (atomic_add_prop_to_plane(plane_res, atomic_req, + plane_res[plane].plane->plane_id, "CRTC_ID", + main_monitor_crtc->crtc_id)) + return -EINVAL; + + return 0; +} + +int MinuiBackendDrm::TeardownPipeline(drmModeAtomicReqPtr atomic_req) { + uint32_t i, prop_id; + int ret; + + /* During suspend, tear down pipeline */ + add_prop(&conn_res, connector, Connector, main_monitor_connector->connector_id, "CRTC_ID", 0); + add_prop(&crtc_res, crtc, Crtc, main_monitor_crtc->crtc_id, "MODE_ID", 0); + add_prop(&crtc_res, crtc, Crtc, main_monitor_crtc->crtc_id, "ACTIVE", 0); + + for(i = 0; i < number_of_lms; i++) { + ret = atomic_add_prop_to_plane(plane_res, atomic_req, + plane_res[i].plane->plane_id, "CRTC_ID", 0); + if (ret < 0) { + printf("Failed to tear down plane %d\n", i); + return ret; + } + + if (drmModeAtomicAddProperty(atomic_req, plane_res[i].plane->plane_id, fb_prop_id, 0) < 0) { + printf("Failed to add property for plane_id=%d\n", plane_res[i].plane->plane_id); + return -EINVAL; + } + } + + return 0; +} + +int MinuiBackendDrm::SetupPipeline(drmModeAtomicReqPtr atomic_req) { + uint32_t i, prop_id; + int ret; + + for(i = 0; i < number_of_lms; i++) { + add_prop(&conn_res, connector, Connector, main_monitor_connector->connector_id, + "CRTC_ID", main_monitor_crtc->crtc_id); + add_prop(&crtc_res, crtc, Crtc, main_monitor_crtc->crtc_id, "MODE_ID", crtc_res.mode_blob_id); + add_prop(&crtc_res, crtc, Crtc, main_monitor_crtc->crtc_id, "ACTIVE", 1); + } + + /* Setup planes */ + for(i = 0; i < number_of_lms; i++) { + ret = AtomicPopulatePlane(i, atomic_req); + if (ret < 0) { + printf("Error populating plane_id = %d\n", plane_res[i].plane->plane_id); + return ret; + } + } + + return 0; +} + GRSurfaceDrm::~GRSurfaceDrm() { if (mmapped_buffer_) { munmap(mmapped_buffer_, row_bytes * height); @@ -138,35 +409,40 @@ std::unique_ptr<GRSurfaceDrm> GRSurfaceDrm::Create(int drm_fd, int width, int he return surface; } -void MinuiBackendDrm::DrmDisableCrtc(int drm_fd, drmModeCrtc* crtc) { - if (crtc) { - drmModeSetCrtc(drm_fd, crtc->crtc_id, - 0, // fb_id - 0, 0, // x,y - nullptr, // connectors - 0, // connector_count - nullptr); // mode - } +int MinuiBackendDrm::DrmDisableCrtc(drmModeAtomicReqPtr atomic_req) { + return TeardownPipeline(atomic_req); } -bool MinuiBackendDrm::DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, - const std::unique_ptr<GRSurfaceDrm>& surface) { - if (drmModeSetCrtc(drm_fd, crtc->crtc_id, surface->fb_id, 0, 0, // x,y - &main_monitor_connector->connector_id, - 1, // connector_count - &main_monitor_crtc->mode) != 0) { - perror("Failed to drmModeSetCrtc"); - return false; - } - return true; +int MinuiBackendDrm::DrmEnableCrtc(drmModeAtomicReqPtr atomic_req){ + return SetupPipeline(atomic_req); } void MinuiBackendDrm::Blank(bool blank) { - if (blank) { - DrmDisableCrtc(drm_fd, main_monitor_crtc); - } else { - DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[current_buffer]); + int ret = 0; + + if (blank == current_blank_state) + return; + + drmModeAtomicReqPtr atomic_req = drmModeAtomicAlloc(); + if (!atomic_req) { + printf("Atomic Alloc failed\n"); + return; + } + + if (blank) + ret = DrmDisableCrtc(atomic_req); + else + ret = DrmEnableCrtc(atomic_req); + + if (!ret) + ret = drmModeAtomicCommit(drm_fd, atomic_req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + + if (!ret) { + printf("Atomic Commit failed, rc = %d\n", ret); + current_blank_state = blank; } + + drmModeAtomicFree(atomic_req); } static drmModeCrtc* find_crtc_for_connector(int fd, drmModeRes* resources, @@ -272,20 +548,68 @@ drmModeConnector* MinuiBackendDrm::FindMainMonitor(int fd, drmModeRes* resources } void MinuiBackendDrm::DisableNonMainCrtcs(int fd, drmModeRes* resources, drmModeCrtc* main_crtc) { + uint32_t prop_id; + drmModeAtomicReqPtr atomic_req = drmModeAtomicAlloc(); + for (int i = 0; i < resources->count_connectors; i++) { drmModeConnector* connector = drmModeGetConnector(fd, resources->connectors[i]); drmModeCrtc* crtc = find_crtc_for_connector(fd, resources, connector); if (crtc->crtc_id != main_crtc->crtc_id) { - DrmDisableCrtc(fd, crtc); + // Switching to atomic commit. Given only crtc, we can only set ACTIVE = 0 + // to disable any Nonmain CRTCs + find_prop_id(&crtc_res, crtc, Crtc, crtc->crtc_id, "ACTIVE", prop_id); + if (prop_id == 0) + return; + + if (drmModeAtomicAddProperty(atomic_req, main_monitor_crtc->crtc_id, prop_id, 0) < 0) + return; } drmModeFreeCrtc(crtc); } + + if (!drmModeAtomicCommit(drm_fd, atomic_req,DRM_MODE_ATOMIC_ALLOW_MODESET, NULL)) + printf("Atomic Commit failed in DisableNonMainCrtcs\n"); + + drmModeAtomicFree(atomic_req); +} + +void MinuiBackendDrm::UpdatePlaneFB() { + uint32_t i, prop_id; + + /* Set atomic req */ + drmModeAtomicReqPtr atomic_req = drmModeAtomicAlloc(); + if (!atomic_req) { + printf("Atomic Alloc failed. Could not update fb_id\n"); + return; + } + + /* Add conn-crtc association property required + * for driver to recognize quadpipe topology. + */ + add_prop(&conn_res, connector, Connector, main_monitor_connector->connector_id, + "CRTC_ID", main_monitor_crtc->crtc_id); + + /* Add property */ + for(i = 0; i < number_of_lms; i++) + drmModeAtomicAddProperty(atomic_req, plane_res[i].plane->plane_id, + fb_prop_id, GRSurfaceDrms[current_buffer]->fb_id); + + /* Commit changes */ + int32_t ret; + ret = drmModeAtomicCommit(drm_fd, atomic_req, + DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + + drmModeAtomicFree(atomic_req); + + if (ret) + printf("Atomic commit failed ret=%d\n", ret); } GRSurface* MinuiBackendDrm::Init() { drmModeRes* res = nullptr; drm_fd = -1; + number_of_lms = DEFAULT_NUM_LMS; /* Consider DRM devices in order. */ for (int i = 0; i < DRM_MAX_MINOR; i++) { auto dev_name = android::base::StringPrintf(DRM_DEV_NAME, DRM_DIR_NAME, i); @@ -353,58 +677,111 @@ GRSurface* MinuiBackendDrm::Init() { current_buffer = 0; - // We will likely encounter errors in the backend functions (i.e. Flip) if EnableCrtc fails. - if (!DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[1])) { - return nullptr; + drmSetClientCap(drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); + drmSetClientCap(drm_fd, DRM_CLIENT_CAP_ATOMIC, 1); + + /* Get possible plane_ids */ + drmModePlaneRes *plane_options = drmModeGetPlaneResources(drm_fd); + if (!plane_options || !plane_options->planes || (plane_options->count_planes < number_of_lms)) + return NULL; + + /* Set crtc resources */ + crtc_res.props = drmModeObjectGetProperties(drm_fd, + main_monitor_crtc->crtc_id, + DRM_MODE_OBJECT_CRTC); + if (!crtc_res.props) + return NULL; + + crtc_res.props_info = static_cast<drmModePropertyRes **> + (calloc(crtc_res.props->count_props, + sizeof(crtc_res.props_info))); + if (!crtc_res.props_info) + return NULL; + else + for (int j = 0; j < (int)crtc_res.props->count_props; ++j) + crtc_res.props_info[j] = drmModeGetProperty(drm_fd, + crtc_res.props->props[j]); + + /* Set connector resources */ + conn_res.props = drmModeObjectGetProperties(drm_fd, + main_monitor_connector->connector_id, + DRM_MODE_OBJECT_CONNECTOR); + if (!conn_res.props) + return NULL; + + conn_res.props_info = static_cast<drmModePropertyRes **> + (calloc(conn_res.props->count_props, + sizeof(conn_res.props_info))); + if (!conn_res.props_info) + return NULL; + else { + for (int j = 0; j < (int)conn_res.props->count_props; ++j) { + + conn_res.props_info[j] = drmModeGetProperty(drm_fd, + conn_res.props->props[j]); + + /* Get preferred mode information and extract the + * number of layer mixers needed from the topology name. + */ + if (!strcmp(conn_res.props_info[j]->name, "mode_properties")) { + number_of_lms = get_topology_lm_number(drm_fd, conn_res.props->prop_values[j]); + printf("number of lms in topology %d\n", number_of_lms); + } + } } - return GRSurfaceDrms[0].get(); -} + /* Set plane resources */ + for(uint32_t i = 0; i < number_of_lms; ++i) { + plane_res[i].plane = drmModeGetPlane(drm_fd, plane_options->planes[i]); + if (!plane_res[i].plane) + return NULL; + } -static void page_flip_complete(__unused int fd, - __unused unsigned int sequence, - __unused unsigned int tv_sec, - __unused unsigned int tv_usec, - void *user_data) { - *static_cast<bool*>(user_data) = false; -} + for (uint32_t i = 0; i < number_of_lms; ++i) { + struct Plane *obj = &plane_res[i]; + unsigned int j; + obj->props = drmModeObjectGetProperties(drm_fd, obj->plane->plane_id, + DRM_MODE_OBJECT_PLANE); + if (!obj->props) + continue; + obj->props_info = static_cast<drmModePropertyRes **> + (calloc(obj->props->count_props, sizeof(*obj->props_info))); + if (!obj->props_info) + continue; + for (j = 0; j < obj->props->count_props; ++j) + obj->props_info[j] = drmModeGetProperty(drm_fd, obj->props->props[j]); + } -GRSurface* MinuiBackendDrm::Flip() { - bool ongoing_flip = true; - if (drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id, GRSurfaceDrms[current_buffer]->fb_id, - DRM_MODE_PAGE_FLIP_EVENT, &ongoing_flip) != 0) { - perror("Failed to drmModePageFlip"); - return nullptr; + drmModeFreePlaneResources(plane_options); + plane_options = NULL; + + /* Setup pipe and blob_id */ + if (drmModeCreatePropertyBlob(drm_fd, &main_monitor_crtc->mode, sizeof(drmModeModeInfo), + &crtc_res.mode_blob_id)) { + printf("failed to create mode blob\n"); + return NULL; } - while (ongoing_flip) { - struct pollfd fds = { - .fd = drm_fd, - .events = POLLIN - }; + /* Save fb_prop_id*/ + uint32_t prop_id; + prop_id = find_plane_prop_id(plane_res[0].plane->plane_id, "FB_ID", plane_res); + fb_prop_id = prop_id; - if (poll(&fds, 1, -1) == -1 || !(fds.revents & POLLIN)) { - perror("Failed to poll() on drm fd"); - break; - } + Blank(false); - drmEventContext evctx = { - .version = DRM_EVENT_CONTEXT_VERSION, - .page_flip_handler = page_flip_complete - }; + return GRSurfaceDrms[0].get(); +} - if (drmHandleEvent(drm_fd, &evctx) != 0) { - perror("Failed to drmHandleEvent"); - break; - } - } +GRSurface* MinuiBackendDrm::Flip() { + UpdatePlaneFB(); current_buffer = 1 - current_buffer; return GRSurfaceDrms[current_buffer].get(); } MinuiBackendDrm::~MinuiBackendDrm() { - DrmDisableCrtc(drm_fd, main_monitor_crtc); + Blank(true); + drmModeDestroyPropertyBlob(drm_fd, crtc_res.mode_blob_id); drmModeFreeCrtc(main_monitor_crtc); drmModeFreeConnector(main_monitor_connector); close(drm_fd); |