blob: e02b9e28c474477823a024eea51fd592d0bebdea [file] [log] [blame]
/* 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;
}
}