2
* Copyright Ā© 2013 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify it
5
* under the terms of the GNU Lesser General Public License version 3,
6
* as published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU Lesser General Public License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
19
#include "real_kms_output.h"
20
#include "page_flipper.h"
22
#include <boost/throw_exception.hpp>
23
#include <boost/exception/info.hpp>
28
namespace mg = mir::graphics;
29
namespace mgm = mg::mesa;
30
namespace geom = mir::geometry;
35
bool encoder_is_used(mgm::DRMModeResources const& resources, uint32_t encoder_id)
37
bool encoder_used{false};
39
resources.for_each_connector([&](mgm::DRMModeConnectorUPtr connector)
41
if (connector->encoder_id == encoder_id &&
42
connector->connection == DRM_MODE_CONNECTED)
44
auto encoder = resources.encoder(connector->encoder_id);
47
auto crtc = resources.crtc(encoder->crtc_id);
57
bool crtc_is_used(mgm::DRMModeResources const& resources, uint32_t crtc_id)
59
bool crtc_used{false};
61
resources.for_each_connector([&](mgm::DRMModeConnectorUPtr connector)
63
if (connector->connection == DRM_MODE_CONNECTED)
65
auto encoder = resources.encoder(connector->encoder_id);
68
if (encoder->crtc_id == crtc_id)
77
std::vector<mgm::DRMModeEncoderUPtr>
78
connector_available_encoders(mgm::DRMModeResources const& resources,
79
drmModeConnector const* connector)
81
std::vector<mgm::DRMModeEncoderUPtr> encoders;
83
for (int i = 0; i < connector->count_encoders; i++)
85
if (!encoder_is_used(resources, connector->encoders[i]))
86
encoders.push_back(resources.encoder(connector->encoders[i]));
92
bool encoder_supports_crtc_index(drmModeEncoder const* encoder, uint32_t crtc_index)
94
return (encoder->possible_crtcs & (1 << crtc_index));
97
const char *connector_type_name(uint32_t type)
99
static const int nnames = 15;
100
static const char * const names[nnames] =
101
{ // Ordered according to xf86drmMode.h
125
std::string connector_name(const drmModeConnector *conn)
127
std::string name = connector_type_name(conn->connector_type);
129
name += std::to_string(conn->connector_type_id);
135
mgm::RealKMSOutput::RealKMSOutput(int drm_fd, uint32_t connector_id,
136
std::shared_ptr<PageFlipper> const& page_flipper)
137
: drm_fd{drm_fd}, connector_id{connector_id}, page_flipper{page_flipper},
138
connector(), mode_index{0}, current_crtc(), saved_crtc(),
139
using_saved_crtc{true}, has_cursor_{false},
140
power_mode(mir_power_mode_on)
144
DRMModeResources resources{drm_fd};
146
auto encoder = resources.encoder(connector->encoder_id);
149
auto crtc = resources.crtc(encoder->crtc_id);
155
mgm::RealKMSOutput::~RealKMSOutput()
157
restore_saved_crtc();
160
void mgm::RealKMSOutput::reset()
162
DRMModeResources resources{drm_fd};
164
/* Update the connector to ensure we have the latest information */
165
connector = resources.connector(connector_id);
168
BOOST_THROW_EXCEPTION(std::runtime_error("No DRM connector found\n"));
170
// TODO: What if we can't locate the DPMS property?
171
for (int i = 0; i < connector->count_props; i++)
173
auto prop = drmModeGetProperty(drm_fd, connector->props[i]);
174
if (prop && (prop->flags & DRM_MODE_PROP_ENUM)) {
175
if (!strcmp(prop->name, "DPMS"))
177
dpms_enum_id = connector->props[i];
178
drmModeFreeProperty(prop);
181
drmModeFreeProperty(prop);
185
/* Discard previously current crtc */
186
current_crtc = nullptr;
189
geom::Size mgm::RealKMSOutput::size() const
191
drmModeModeInfo const& mode(connector->modes[mode_index]);
192
return {mode.hdisplay, mode.vdisplay};
195
void mgm::RealKMSOutput::configure(geom::Displacement offset, size_t kms_mode_index)
198
mode_index = kms_mode_index;
201
bool mgm::RealKMSOutput::set_crtc(uint32_t fb_id)
204
BOOST_THROW_EXCEPTION(std::runtime_error("Output " +
205
connector_name(connector.get()) +
206
" has no associated CRTC to set a framebuffer on"));
208
auto ret = drmModeSetCrtc(drm_fd, current_crtc->crtc_id,
209
fb_id, fb_offset.dx.as_int(), fb_offset.dy.as_int(),
210
&connector->connector_id, 1,
211
&connector->modes[mode_index]);
214
current_crtc = nullptr;
218
using_saved_crtc = false;
222
void mgm::RealKMSOutput::clear_crtc()
225
* In order to actually clear the output, we need to have a crtc
226
* connected to the output/connector so that we can disconnect
227
* it. However, not being able to get a crtc is OK, since it means
228
* that the output cannot be displaying anything anyway.
233
auto result = drmModeSetCrtc(drm_fd, current_crtc->crtc_id,
234
0, 0, 0, nullptr, 0, nullptr);
237
std::string const msg =
238
"Couldn't clear output " + connector_name(connector.get());
240
BOOST_THROW_EXCEPTION(
241
::boost::enable_error_info(std::runtime_error(msg))
242
<< (boost::error_info<KMSOutput, decltype(result)>(result)));
245
current_crtc = nullptr;
248
bool mgm::RealKMSOutput::schedule_page_flip(uint32_t fb_id)
250
std::unique_lock<std::mutex> lg(power_mutex);
251
if (power_mode != mir_power_mode_on)
254
BOOST_THROW_EXCEPTION(std::runtime_error("Output " +
255
connector_name(connector.get()) +
256
" has no associated CRTC to schedule page flips on"));
258
return page_flipper->schedule_flip(current_crtc->crtc_id, fb_id);
261
void mgm::RealKMSOutput::wait_for_page_flip()
263
std::unique_lock<std::mutex> lg(power_mutex);
264
if (power_mode != mir_power_mode_on)
267
BOOST_THROW_EXCEPTION(std::runtime_error("Output " +
268
connector_name(connector.get()) +
269
" has no associated CRTC to wait on"));
271
page_flipper->wait_for_flip(current_crtc->crtc_id);
274
void mgm::RealKMSOutput::set_cursor(gbm_bo* buffer)
278
if (auto result = drmModeSetCursor(
280
current_crtc->crtc_id,
281
gbm_bo_get_handle(buffer).u32,
282
gbm_bo_get_width(buffer),
283
gbm_bo_get_height(buffer)))
285
BOOST_THROW_EXCEPTION(
286
::boost::enable_error_info(std::runtime_error("drmModeSetCursor() failed"))
287
<< (boost::error_info<KMSOutput, decltype(result)>(result)));
294
void mgm::RealKMSOutput::move_cursor(geometry::Point destination)
298
if (auto result = drmModeMoveCursor(drm_fd, current_crtc->crtc_id,
299
destination.x.as_uint32_t(),
300
destination.y.as_uint32_t()))
302
BOOST_THROW_EXCEPTION(
303
::boost::enable_error_info(std::runtime_error("drmModeMoveCursor() failed"))
304
<< (boost::error_info<KMSOutput, decltype(result)>(result)));
309
void mgm::RealKMSOutput::clear_cursor()
313
drmModeSetCursor(drm_fd, current_crtc->crtc_id, 0, 0, 0);
318
bool mgm::RealKMSOutput::has_cursor() const
323
bool mgm::RealKMSOutput::ensure_crtc()
325
/* Nothing to do if we already have a crtc */
329
/* If the output is not connected there is nothing to do */
330
if (connector->connection != DRM_MODE_CONNECTED)
333
DRMModeResources resources{drm_fd};
335
/* Check to see if there is a crtc already connected */
336
auto encoder = resources.encoder(connector->encoder_id);
338
current_crtc = resources.crtc(encoder->crtc_id);
340
/* If we don't have a current crtc, try to find one */
343
auto available_encoders = connector_available_encoders(resources, connector.get());
347
resources.for_each_crtc([&](DRMModeCrtcUPtr crtc)
349
if (!current_crtc && !crtc_is_used(resources, crtc->crtc_id))
351
for (auto& enc : available_encoders)
353
if (encoder_supports_crtc_index(enc.get(), crtc_index))
355
current_crtc = std::move(crtc);
365
return (current_crtc != nullptr);
368
void mgm::RealKMSOutput::restore_saved_crtc()
370
if (!using_saved_crtc)
372
drmModeSetCrtc(drm_fd, saved_crtc.crtc_id, saved_crtc.buffer_id,
373
saved_crtc.x, saved_crtc.y,
374
&connector->connector_id, 1, &saved_crtc.mode);
376
using_saved_crtc = true;
380
void mgm::RealKMSOutput::set_power_mode(MirPowerMode mode)
382
std::lock_guard<std::mutex> lg(power_mutex);
384
if (power_mode != mode)
387
drmModeConnectorSetProperty(drm_fd, connector_id,