2
* Copyright Ā© 2013 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License version 3 as
6
* 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 General Public License for more details.
13
* You should have received a copy of the GNU 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 <boost/throw_exception.hpp>
20
#include "mir/graphics/display_configuration.h"
21
#include "mir/graphics/display.h"
22
#include "src/server/graphics/default_display_configuration_policy.h"
23
#include "src/server/graphics/gbm/gbm_platform.h"
24
#include "src/server/graphics/gbm/gbm_display.h"
25
#include "src/server/graphics/gbm/kms_display_configuration.h"
27
#include "mir_test_doubles/mock_egl.h"
28
#include "mir_test_doubles/mock_gl.h"
29
#include "mir/graphics/null_display_report.h"
30
#include "mir_test_doubles/null_virtual_terminal.h"
32
#include "mir_test_framework/udev_environment.h"
34
#include "mir_test_doubles/mock_drm.h"
35
#include "mir_test_doubles/mock_gbm.h"
37
#include <gmock/gmock.h>
38
#include <gtest/gtest.h>
42
namespace mg = mir::graphics;
43
namespace mgg = mir::graphics::gbm;
44
namespace geom = mir::geometry;
45
namespace mtd = mir::test::doubles;
46
namespace mtf = mir::mir_test_framework;
51
mg::DisplayConfigurationMode conf_mode_from_drm_mode(drmModeModeInfo const& mode)
53
geom::Size const size{mode.hdisplay, mode.vdisplay};
54
double vrefresh_hz{0.0};
56
/* Calculate vertical refresh rate from DRM mode information */
57
if (mode.htotal != 0.0 && mode.vtotal != 0.0)
59
vrefresh_hz = mode.clock * 1000.0 / (mode.htotal * mode.vtotal);
60
vrefresh_hz = round(vrefresh_hz * 10.0) / 10.0;
63
return mg::DisplayConfigurationMode{size, vrefresh_hz};
66
class GBMDisplayConfigurationTest : public ::testing::Test
69
GBMDisplayConfigurationTest()
71
using namespace testing;
73
/* Needed for display start-up */
74
ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_))
75
.WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]),
79
const char* egl_exts = "EGL_KHR_image EGL_KHR_image_base EGL_MESA_drm_image";
80
const char* gl_exts = "GL_OES_texture_npot GL_OES_EGL_image";
82
ON_CALL(mock_egl, eglQueryString(_,EGL_EXTENSIONS))
83
.WillByDefault(Return(egl_exts));
84
ON_CALL(mock_gl, glGetString(GL_EXTENSIONS))
85
.WillByDefault(Return(reinterpret_cast<const GLubyte*>(gl_exts)));
89
fake_devices.add_standard_drm_devices();
92
std::shared_ptr<mgg::GBMPlatform> create_platform()
94
return std::make_shared<mgg::GBMPlatform>(
95
std::make_shared<mg::NullDisplayReport>(),
96
std::make_shared<mtd::NullVirtualTerminal>());
99
std::shared_ptr<mg::Display> create_display(
100
std::shared_ptr<mg::Platform> const& platform)
102
auto conf_policy = std::make_shared<mg::DefaultDisplayConfigurationPolicy>();
103
return platform->create_display(conf_policy);
106
void setup_sample_modes()
108
using fake = mtd::FakeDRMResources;
111
modes0.push_back(fake::create_mode(1920, 1080, 138500, 2080, 1111, fake::NormalMode));
112
modes0.push_back(fake::create_mode(1920, 1080, 148500, 2200, 1125, fake::PreferredMode));
113
modes0.push_back(fake::create_mode(1680, 1050, 119000, 1840, 1080, fake::NormalMode));
114
modes0.push_back(fake::create_mode(832, 624, 57284, 1152, 667, fake::NormalMode));
116
/* Add the DisplayConfiguration modes corresponding to the DRM modes */
117
for (auto const& mode : modes0)
118
conf_modes0.push_back(conf_mode_from_drm_mode(mode));
121
::testing::NiceMock<mtd::MockEGL> mock_egl;
122
::testing::NiceMock<mtd::MockGL> mock_gl;
123
::testing::NiceMock<mtd::MockDRM> mock_drm;
124
::testing::NiceMock<mtd::MockGBM> mock_gbm;
126
std::vector<drmModeModeInfo> modes0;
127
std::vector<mg::DisplayConfigurationMode> conf_modes0;
128
std::vector<drmModeModeInfo> modes_empty;
130
mtf::UdevEnvironment fake_devices;
135
TEST_F(GBMDisplayConfigurationTest, configuration_is_read_correctly)
137
using namespace ::testing;
139
/* Set up DRM resources */
140
uint32_t const invalid_id{0};
141
uint32_t const crtc0_id{10};
142
uint32_t const encoder0_id{20};
143
uint32_t const encoder1_id{21};
144
uint32_t const connector0_id{30};
145
uint32_t const connector1_id{31};
146
uint32_t const connector2_id{32};
147
geom::Size const connector0_physical_size_mm{480, 270};
148
geom::Size const connector1_physical_size_mm{};
149
geom::Size const connector2_physical_size_mm{};
150
std::vector<uint32_t> possible_encoder_ids_empty;
151
uint32_t const possible_crtcs_mask_empty{0};
152
size_t const max_simultaneous_outputs{1};
154
mtd::FakeDRMResources& resources(mock_drm.fake_drm);
158
resources.add_crtc(crtc0_id, modes0[1]);
160
resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty);
161
resources.add_encoder(encoder1_id, invalid_id, possible_crtcs_mask_empty);
163
resources.add_connector(connector0_id, DRM_MODE_CONNECTOR_HDMIA,
164
DRM_MODE_CONNECTED, encoder0_id,
165
modes0, possible_encoder_ids_empty,
166
connector0_physical_size_mm);
167
resources.add_connector(connector1_id, DRM_MODE_CONNECTOR_Unknown,
168
DRM_MODE_DISCONNECTED, invalid_id,
169
modes_empty, possible_encoder_ids_empty,
170
connector1_physical_size_mm);
171
resources.add_connector(connector2_id, DRM_MODE_CONNECTOR_eDP,
172
DRM_MODE_DISCONNECTED, encoder1_id,
173
modes_empty, possible_encoder_ids_empty,
174
connector2_physical_size_mm);
178
/* Expected results */
179
std::vector<mg::DisplayConfigurationCard> const expected_cards =
182
mg::DisplayConfigurationCardId{0},
183
max_simultaneous_outputs
187
std::vector<mg::DisplayConfigurationOutput> const expected_outputs =
190
mg::DisplayConfigurationOutputId{connector0_id},
191
mg::DisplayConfigurationCardId{0},
192
mg::DisplayConfigurationOutputType::hdmia,
196
connector0_physical_size_mm,
205
mg::DisplayConfigurationOutputId{connector1_id},
206
mg::DisplayConfigurationCardId{0},
207
mg::DisplayConfigurationOutputType::unknown,
209
std::vector<mg::DisplayConfigurationMode>(),
210
std::numeric_limits<size_t>::max(),
211
connector1_physical_size_mm,
215
std::numeric_limits<size_t>::max(),
216
std::numeric_limits<size_t>::max(),
220
mg::DisplayConfigurationOutputId{connector2_id},
221
mg::DisplayConfigurationCardId{0},
222
mg::DisplayConfigurationOutputType::edp,
224
std::vector<mg::DisplayConfigurationMode>(),
225
std::numeric_limits<size_t>::max(),
226
connector2_physical_size_mm,
230
std::numeric_limits<size_t>::max(),
231
std::numeric_limits<size_t>::max(),
237
auto display = create_display(create_platform());
239
auto conf = display->configuration();
241
size_t card_count{0};
243
conf->for_each_card([&](mg::DisplayConfigurationCard const& card)
245
ASSERT_LT(card_count, expected_cards.size());
246
EXPECT_EQ(expected_cards[card_count], card) << "card_count: " << card_count;
250
size_t output_count{0};
252
conf->for_each_output([&](mg::DisplayConfigurationOutput const& output)
254
ASSERT_LT(output_count, expected_outputs.size());
255
EXPECT_EQ(expected_outputs[output_count], output) << "output_count: " << output_count;
259
EXPECT_EQ(expected_outputs.size(), output_count);
262
TEST_F(GBMDisplayConfigurationTest, get_kms_connector_id_returns_correct_id)
264
uint32_t const crtc0_id{10};
265
uint32_t const encoder0_id{20};
266
uint32_t const possible_crtcs_mask_empty{0};
267
std::vector<uint32_t> const connector_ids{30, 31};
268
std::vector<uint32_t> encoder_ids{20};
270
/* Set up DRM resources */
271
mtd::FakeDRMResources& resources(mock_drm.fake_drm);
275
resources.add_crtc(crtc0_id, modes0[1]);
276
resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty);
277
for (auto id : connector_ids)
279
resources.add_connector(id, DRM_MODE_CONNECTOR_DVID,
280
DRM_MODE_CONNECTED, encoder0_id,
288
auto display = create_display(create_platform());
290
auto conf = display->configuration();
291
auto const& kms_conf = std::static_pointer_cast<mgg::KMSDisplayConfiguration>(conf);
293
size_t output_count{0};
295
conf->for_each_output([&](mg::DisplayConfigurationOutput const& output)
297
ASSERT_LT(output_count, connector_ids.size());
299
EXPECT_EQ(connector_ids[output_count],
300
kms_conf->get_kms_connector_id(output.id));
305
TEST_F(GBMDisplayConfigurationTest, get_kms_connector_id_throws_on_invalid_id)
307
uint32_t const crtc0_id{10};
308
uint32_t const encoder0_id{20};
309
uint32_t const possible_crtcs_mask_empty{0};
310
std::vector<uint32_t> const connector_ids{30, 31};
311
std::vector<uint32_t> encoder_ids{20};
313
/* Set up DRM resources */
314
mtd::FakeDRMResources& resources(mock_drm.fake_drm);
318
resources.add_crtc(crtc0_id, modes0[1]);
319
resources.add_encoder(encoder0_id, crtc0_id, possible_crtcs_mask_empty);
320
for (auto id : connector_ids)
322
resources.add_connector(id, DRM_MODE_CONNECTOR_VGA,
323
DRM_MODE_CONNECTED, encoder0_id,
331
auto display = create_display(create_platform());
333
auto conf = display->configuration();
334
auto const& kms_conf = std::static_pointer_cast<mgg::KMSDisplayConfiguration>(conf);
337
kms_conf->get_kms_connector_id(mg::DisplayConfigurationOutputId{29});
338
}, std::runtime_error);
340
kms_conf->get_kms_connector_id(mg::DisplayConfigurationOutputId{32});
341
}, std::runtime_error);
344
TEST_F(GBMDisplayConfigurationTest, returns_updated_configuration)
346
using namespace ::testing;
348
uint32_t const invalid_id{0};
349
std::vector<uint32_t> const crtc_ids{10, 11};
350
std::vector<uint32_t> const encoder_ids{20, 21};
351
std::vector<uint32_t> const connector_ids{30, 31};
352
std::vector<geom::Size> const connector_physical_sizes_mm_before{
355
std::vector<geom::Size> const connector_physical_sizes_mm_after{
358
std::vector<uint32_t> possible_encoder_ids_empty;
359
uint32_t const possible_crtcs_mask_empty{0};
360
size_t const max_simultaneous_outputs{1};
362
/* Expected results */
363
std::vector<mg::DisplayConfigurationCard> const expected_cards =
366
mg::DisplayConfigurationCardId{0},
367
max_simultaneous_outputs
371
std::vector<mg::DisplayConfigurationOutput> const expected_outputs_before =
374
mg::DisplayConfigurationOutputId(connector_ids[0]),
375
mg::DisplayConfigurationCardId{0},
376
mg::DisplayConfigurationOutputType::composite,
380
connector_physical_sizes_mm_before[0],
389
mg::DisplayConfigurationOutputId(connector_ids[1]),
390
mg::DisplayConfigurationCardId{0},
391
mg::DisplayConfigurationOutputType::vga,
393
std::vector<mg::DisplayConfigurationMode>(),
394
std::numeric_limits<size_t>::max(),
395
connector_physical_sizes_mm_before[1],
399
std::numeric_limits<size_t>::max(),
400
std::numeric_limits<size_t>::max(),
405
std::vector<mg::DisplayConfigurationOutput> const expected_outputs_after =
408
mg::DisplayConfigurationOutputId(connector_ids[0]),
409
mg::DisplayConfigurationCardId{0},
410
mg::DisplayConfigurationOutputType::composite,
412
std::vector<mg::DisplayConfigurationMode>(),
413
std::numeric_limits<size_t>::max(),
414
connector_physical_sizes_mm_after[0],
418
std::numeric_limits<size_t>::max(),
419
std::numeric_limits<size_t>::max(),
423
mg::DisplayConfigurationOutputId(connector_ids[1]),
424
mg::DisplayConfigurationCardId{0},
425
mg::DisplayConfigurationOutputType::vga,
429
connector_physical_sizes_mm_after[1],
439
/* Set up DRM resources and check */
440
mtd::FakeDRMResources& resources(mock_drm.fake_drm);
444
resources.add_crtc(crtc_ids[0], modes0[1]);
446
resources.add_encoder(encoder_ids[0], crtc_ids[0], possible_crtcs_mask_empty);
447
resources.add_encoder(encoder_ids[1], invalid_id, possible_crtcs_mask_empty);
449
resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite,
450
DRM_MODE_CONNECTED, encoder_ids[0],
451
modes0, possible_encoder_ids_empty,
452
connector_physical_sizes_mm_before[0]);
453
resources.add_connector(connector_ids[1], DRM_MODE_CONNECTOR_VGA,
454
DRM_MODE_DISCONNECTED, invalid_id,
455
modes_empty, possible_encoder_ids_empty,
456
connector_physical_sizes_mm_before[1]);
460
auto display = create_display(create_platform());
462
auto conf = display->configuration();
464
size_t card_count{0};
466
conf->for_each_card([&](mg::DisplayConfigurationCard const& card)
468
ASSERT_LT(card_count, expected_cards.size());
469
EXPECT_EQ(expected_cards[card_count], card) << "card_count: " << card_count;
473
size_t output_count{0};
475
conf->for_each_output([&](mg::DisplayConfigurationOutput const& output)
477
ASSERT_LT(output_count, expected_outputs_before.size());
478
EXPECT_EQ(expected_outputs_before[output_count], output) << "output_count: " << output_count;
482
EXPECT_EQ(expected_outputs_before.size(), output_count);
484
/* Reset DRM resources and check again */
487
resources.add_crtc(crtc_ids[1], modes0[1]);
489
resources.add_encoder(encoder_ids[0], invalid_id, possible_crtcs_mask_empty);
490
resources.add_encoder(encoder_ids[1], crtc_ids[1], possible_crtcs_mask_empty);
492
resources.add_connector(connector_ids[0], DRM_MODE_CONNECTOR_Composite,
493
DRM_MODE_DISCONNECTED, invalid_id,
494
modes_empty, possible_encoder_ids_empty,
495
connector_physical_sizes_mm_after[0]);
496
resources.add_connector(connector_ids[1], DRM_MODE_CONNECTOR_VGA,
497
DRM_MODE_CONNECTED, encoder_ids[1],
498
modes0, possible_encoder_ids_empty,
499
connector_physical_sizes_mm_after[1]);
503
conf = display->configuration();
507
conf->for_each_card([&](mg::DisplayConfigurationCard const& card)
509
ASSERT_LT(card_count, expected_cards.size());
510
EXPECT_EQ(expected_cards[card_count], card) << "card_count: " << card_count;
516
conf->for_each_output([&](mg::DisplayConfigurationOutput const& output)
518
ASSERT_LT(output_count, expected_outputs_after.size());
519
EXPECT_EQ(expected_outputs_after[output_count], output) << "output_count: " << output_count;
523
EXPECT_EQ(expected_outputs_after.size(), output_count);