| /* ply-monitor.c - monitor setup via KMS |
| * |
| * Copyright (C) 2012, The Chromium OS Authors. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2, or (at your option) |
| * any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
| * 02111-1307, USA. |
| * |
| */ |
| |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <xf86drm.h> |
| #include <xf86drmMode.h> |
| |
| #include "ply-frame-buffer.h" |
| #include "ply-kms.h" |
| #include "ply-utils.h" |
| |
| bool ply_monitor_set_monitors = false; |
| |
| static drmModeCrtc *find_crtc_for_connector(int fd, |
| drmModeRes *resources, |
| drmModeConnector *connector) { |
| int i; |
| int encoder_crtc_id = -1; |
| |
| /* Find the encoder */ |
| for (i = 0; i < resources->count_encoders; i++) { |
| drmModeEncoder *encoder = drmModeGetEncoder(fd, resources->encoders[i]); |
| |
| if (encoder) { |
| if (encoder->encoder_id == connector->encoder_id) { |
| encoder_crtc_id = encoder->crtc_id; |
| drmModeFreeEncoder(encoder); |
| break; |
| } |
| drmModeFreeEncoder(encoder); |
| } |
| } |
| |
| if (encoder_crtc_id == -1) |
| return NULL; |
| |
| /* Find the crtc */ |
| for (i = 0; i < resources->count_crtcs; i++) { |
| drmModeCrtc *crtc = drmModeGetCrtc(fd, resources->crtcs[i]); |
| |
| if (crtc) { |
| if (encoder_crtc_id == crtc->crtc_id) |
| return crtc; |
| drmModeFreeCrtc(crtc); |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static bool is_connector_used(int fd, |
| drmModeRes *resources, |
| drmModeConnector *connector) { |
| bool result = false; |
| drmModeCrtc *crtc = find_crtc_for_connector(fd, resources, connector); |
| |
| if (crtc) { |
| result = crtc->buffer_id != 0; |
| drmModeFreeCrtc(crtc); |
| } |
| |
| return result; |
| } |
| |
| static drmModeConnector* find_used_connector_by_type(int fd, |
| drmModeRes *resources, |
| int type) { |
| int i; |
| for (i = 0; i < resources->count_connectors; i++) { |
| drmModeConnector *connector; |
| |
| connector = drmModeGetConnector(fd, resources->connectors[i]); |
| if (connector) { |
| if ((connector->connector_type == type) && |
| (is_connector_used(fd, resources, connector))) |
| return connector; |
| |
| drmModeFreeConnector(connector); |
| } |
| } |
| return NULL; |
| } |
| |
| static drmModeConnector* find_first_used_connector(int fd, |
| drmModeRes *resources) { |
| int i; |
| for (i = 0; i < resources->count_connectors; i++) { |
| drmModeConnector *connector; |
| |
| connector = drmModeGetConnector(fd, resources->connectors[i]); |
| if (connector) { |
| if (is_connector_used(fd, resources, connector)) |
| return connector; |
| |
| drmModeFreeConnector(connector); |
| } |
| } |
| return NULL; |
| } |
| |
| static drmModeConnector *find_main_monitor(int fd, |
| drmModeRes *resources) { |
| int i; |
| /* |
| * Find the LVDS and eDP connectors. Those are the main screens. |
| */ |
| int kConnectorPriority[] = { |
| DRM_MODE_CONNECTOR_LVDS, |
| DRM_MODE_CONNECTOR_eDP, |
| }; |
| |
| drmModeConnector *main_monitor_connector = NULL; |
| i = 0; |
| do { |
| main_monitor_connector = find_used_connector_by_type( |
| fd, |
| resources, |
| kConnectorPriority[i]); |
| i++; |
| } while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority)); |
| |
| /* |
| * If we didn't find a connector, grab the first one in use. |
| */ |
| if (!main_monitor_connector) |
| main_monitor_connector = find_first_used_connector(fd, resources); |
| |
| return main_monitor_connector; |
| } |
| |
| static void disable_connector(int fd, |
| drmModeRes *resources, |
| drmModeConnector *connector) { |
| drmModeCrtc *crtc = find_crtc_for_connector(fd, resources, connector); |
| |
| if (crtc) { |
| drmModeSetCrtc(fd, |
| crtc->crtc_id, |
| 0, // buffer_id |
| 0, 0, // x,y |
| NULL, // connectors |
| 0, // connector_count |
| NULL); // mode |
| drmModeFreeCrtc(crtc); |
| } |
| } |
| |
| static void disable_non_main_connectors(int fd, |
| drmModeRes *resources, |
| drmModeConnector *main_connector) { |
| int i; |
| |
| for (i = 0; i < resources->count_connectors; i++) { |
| drmModeConnector *connector; |
| |
| connector = drmModeGetConnector(fd, resources->connectors[i]); |
| if (connector->connector_id != main_connector->connector_id) |
| disable_connector(fd, resources, connector); |
| |
| drmModeFreeConnector(connector); |
| } |
| } |
| |
| bool ply_monitor_setup(ply_frame_buffer_t *buffer) { |
| int fd = ply_kms_open(); |
| ply_frame_buffer_area_t *area = &buffer->area; |
| |
| if (fd < 0) { |
| fprintf(stderr, "Unable to open a KMS module\n"); |
| return false; |
| } |
| |
| drmModeRes *resources = drmModeGetResources(fd); |
| if (!resources) { |
| fprintf(stderr, "Unable to get mode resources\n"); |
| drmClose(fd); |
| return false; |
| } |
| |
| drmModeConnector *main_monitor_connector = find_main_monitor(fd, resources); |
| |
| /* |
| * If we didn't find any enabled connector, leave. |
| */ |
| if (!main_monitor_connector) { |
| drmModeFreeResources(resources); |
| drmClose(fd); |
| return false; |
| } |
| |
| if (ply_monitor_set_monitors) |
| disable_non_main_connectors(fd, resources, main_monitor_connector); |
| |
| drmModeCrtc *crtc = find_crtc_for_connector(fd, resources, main_monitor_connector); |
| area->visible_width = crtc->mode.hdisplay; |
| area->visible_height = crtc->mode.vdisplay; |
| |
| drmModeFreeCrtc(crtc); |
| drmModeFreeConnector(main_monitor_connector); |
| drmModeFreeResources(resources); |
| buffer->drm_device_fd = fd; |
| |
| return true; |
| } |
| |
| void ply_monitor_close(ply_frame_buffer_t *buffer) |
| { |
| if (buffer->drm_device_fd >= 0) { |
| drmClose(buffer->drm_device_fd); |
| buffer->drm_device_fd = -1; |
| } |
| } |
| |
| |