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_display_configuration.h"
20
#include "drm_mode_resources.h"
25
#include <boost/throw_exception.hpp>
29
namespace mg = mir::graphics;
30
namespace mgm = mir::graphics::mesa;
31
namespace geom = mir::geometry;
36
bool kms_modes_are_equal(drmModeModeInfo const& info1, drmModeModeInfo const& info2)
38
return (info1.clock == info2.clock &&
39
info1.hdisplay == info2.hdisplay &&
40
info1.hsync_start == info2.hsync_start &&
41
info1.hsync_end == info2.hsync_end &&
42
info1.htotal == info2.htotal &&
43
info1.hskew == info2.hskew &&
44
info1.vdisplay == info2.vdisplay &&
45
info1.vsync_start == info2.vsync_start &&
46
info1.vsync_end == info2.vsync_end &&
47
info1.vtotal == info2.vtotal);
50
double calculate_vrefresh_hz(drmModeModeInfo const& mode)
52
if (mode.htotal == 0 || mode.vtotal == 0)
55
/* mode.clock is in KHz */
56
double vrefresh_hz = mode.clock * 1000.0 / (mode.htotal * mode.vtotal);
58
/* Round to first decimal */
59
return round(vrefresh_hz * 10.0) / 10.0;
62
mg::DisplayConfigurationOutputType
63
kms_connector_type_to_output_type(uint32_t connector_type)
65
return static_cast<mg::DisplayConfigurationOutputType>(connector_type);
70
mgm::RealKMSDisplayConfiguration::RealKMSDisplayConfiguration(int drm_fd)
76
mgm::RealKMSDisplayConfiguration::RealKMSDisplayConfiguration(
77
RealKMSDisplayConfiguration const& conf)
78
: KMSDisplayConfiguration(), drm_fd{conf.drm_fd},
79
card(conf.card), outputs{conf.outputs}
83
mgm::RealKMSDisplayConfiguration& mgm::RealKMSDisplayConfiguration::operator=(
84
RealKMSDisplayConfiguration const& conf)
90
outputs = conf.outputs;
96
void mgm::RealKMSDisplayConfiguration::for_each_card(
97
std::function<void(DisplayConfigurationCard const&)> f) const
102
void mgm::RealKMSDisplayConfiguration::for_each_output(
103
std::function<void(DisplayConfigurationOutput const&)> f) const
105
for (auto const& output : outputs)
109
void mgm::RealKMSDisplayConfiguration::configure_output(
110
DisplayConfigurationOutputId id, bool used,
111
geometry::Point top_left, size_t mode_index,
112
MirPowerMode power_mode)
114
auto iter = find_output_with_id(id);
116
if (iter != outputs.end())
118
auto& output = *iter;
120
if (used && mode_index >= output.modes.size())
121
BOOST_THROW_EXCEPTION(std::runtime_error("Invalid mode_index for used output"));
124
output.top_left = top_left;
125
output.current_mode_index = mode_index;
126
output.power_mode = power_mode;
130
BOOST_THROW_EXCEPTION(std::runtime_error("Trying to configure invalid output"));
134
uint32_t mgm::RealKMSDisplayConfiguration::get_kms_connector_id(
135
DisplayConfigurationOutputId id) const
137
auto iter = find_output_with_id(id);
139
if (iter == outputs.end())
141
BOOST_THROW_EXCEPTION(
142
std::runtime_error("Failed to find DisplayConfigurationOutput with provided id"));
145
return id.as_value();
148
size_t mgm::RealKMSDisplayConfiguration::get_kms_mode_index(
149
DisplayConfigurationOutputId id,
150
size_t conf_mode_index) const
152
auto iter = find_output_with_id(id);
154
if (iter == outputs.end() || conf_mode_index >= iter->modes.size())
156
BOOST_THROW_EXCEPTION(
157
std::runtime_error("Failed to find valid mode index for DisplayConfigurationOutput with provided id/mode_index"));
160
return conf_mode_index;
162
void mgm::RealKMSDisplayConfiguration::update()
164
DRMModeResources resources{drm_fd};
166
size_t max_outputs = std::min(resources.num_crtcs(), resources.num_connectors());
167
card = {mg::DisplayConfigurationCardId{0}, max_outputs};
169
resources.for_each_connector([&](DRMModeConnectorUPtr connector)
171
add_or_update_output(resources, *connector);
175
void mgm::RealKMSDisplayConfiguration::add_or_update_output(
176
DRMModeResources const& resources,
177
drmModeConnector const& connector)
179
DisplayConfigurationOutputId id{static_cast<int>(connector.connector_id)};
180
DisplayConfigurationCardId card_id{0};
181
DisplayConfigurationOutputType const type{
182
kms_connector_type_to_output_type(connector.connector_type)};
183
geom::Size physical_size{connector.mmWidth, connector.mmHeight};
184
bool connected{connector.connection == DRM_MODE_CONNECTED};
185
size_t current_mode_index{std::numeric_limits<size_t>::max()};
186
size_t preferred_mode_index{std::numeric_limits<size_t>::max()};
187
std::vector<DisplayConfigurationMode> modes;
188
std::vector<MirPixelFormat> formats {mir_pixel_format_argb_8888,
189
mir_pixel_format_xrgb_8888};
191
drmModeModeInfo current_mode_info = drmModeModeInfo();
193
/* Get information about the current mode */
194
auto encoder = resources.encoder(connector.encoder_id);
197
auto crtc = resources.crtc(encoder->crtc_id);
199
current_mode_info = crtc->mode;
202
/* Add all the available modes and find the current and preferred one */
203
for (int m = 0; m < connector.count_modes; m++)
205
drmModeModeInfo& mode_info = connector.modes[m];
207
geom::Size size{mode_info.hdisplay, mode_info.vdisplay};
209
double vrefresh_hz = calculate_vrefresh_hz(mode_info);
211
modes.push_back({size, vrefresh_hz});
213
if (kms_modes_are_equal(mode_info, current_mode_info))
214
current_mode_index = m;
216
if ((mode_info.type & DRM_MODE_TYPE_PREFERRED) == DRM_MODE_TYPE_PREFERRED)
217
preferred_mode_index = m;
220
/* Add or update the output */
221
auto iter = find_output_with_id(id);
223
if (iter == outputs.end())
225
outputs.push_back({id, card_id, type, formats, modes, preferred_mode_index,
226
physical_size, connected, false, geom::Point(),
227
current_mode_index, 0u, mir_power_mode_on});
231
auto& output = *iter;
233
output.modes = modes;
234
output.preferred_mode_index = preferred_mode_index;
235
output.physical_size_mm = physical_size;
236
output.connected = connected;
237
output.current_mode_index = current_mode_index;
241
std::vector<mg::DisplayConfigurationOutput>::iterator
242
mgm::RealKMSDisplayConfiguration::find_output_with_id(DisplayConfigurationOutputId id)
244
return std::find_if(outputs.begin(), outputs.end(),
245
[id](DisplayConfigurationOutput const& output)
247
return output.id == id;
251
std::vector<mg::DisplayConfigurationOutput>::const_iterator
252
mgm::RealKMSDisplayConfiguration::find_output_with_id(DisplayConfigurationOutputId id) const
254
return std::find_if(outputs.begin(), outputs.end(),
255
[id](DisplayConfigurationOutput const& output)
257
return output.id == id;