~ubuntu-branches/ubuntu/wily/mir/wily-proposed

« back to all changes in this revision

Viewing changes to src/platform/graphics/mesa/real_kms_output.cpp

  • Committer: Package Import Robot
  • Author(s): Ubuntu daily release
  • Date: 2014-01-08 02:04:38 UTC
  • mto: This revision was merged to the branch mainline in revision 58.
  • Revision ID: package-import@ubuntu.com-20140108020438-e1npu0pm7qdv5wc4
Tags: upstream-0.1.3+14.04.20140108
ImportĀ upstreamĀ versionĀ 0.1.3+14.04.20140108

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright Ā© 2013 Canonical Ltd.
 
3
 *
 
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.
 
7
 *
 
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.
 
12
 *
 
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/>.
 
15
 *
 
16
 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
 
17
 */
 
18
 
 
19
#include "real_kms_output.h"
 
20
#include "page_flipper.h"
 
21
 
 
22
#include <boost/throw_exception.hpp>
 
23
#include <boost/exception/info.hpp>
 
24
 
 
25
#include <stdexcept>
 
26
#include <vector>
 
27
 
 
28
namespace mg = mir::graphics;
 
29
namespace mgm = mg::mesa;
 
30
namespace geom = mir::geometry;
 
31
 
 
32
namespace
 
33
{
 
34
 
 
35
bool encoder_is_used(mgm::DRMModeResources const& resources, uint32_t encoder_id)
 
36
{
 
37
    bool encoder_used{false};
 
38
 
 
39
    resources.for_each_connector([&](mgm::DRMModeConnectorUPtr connector)
 
40
    {
 
41
        if (connector->encoder_id == encoder_id &&
 
42
            connector->connection == DRM_MODE_CONNECTED)
 
43
        {
 
44
            auto encoder = resources.encoder(connector->encoder_id);
 
45
            if (encoder)
 
46
            {
 
47
                auto crtc = resources.crtc(encoder->crtc_id);
 
48
                if (crtc)
 
49
                    encoder_used = true;
 
50
            }
 
51
        }
 
52
    });
 
53
 
 
54
    return encoder_used;
 
55
}
 
56
 
 
57
bool crtc_is_used(mgm::DRMModeResources const& resources, uint32_t crtc_id)
 
58
{
 
59
    bool crtc_used{false};
 
60
 
 
61
    resources.for_each_connector([&](mgm::DRMModeConnectorUPtr connector)
 
62
    {
 
63
        if (connector->connection == DRM_MODE_CONNECTED)
 
64
        {
 
65
            auto encoder = resources.encoder(connector->encoder_id);
 
66
            if (encoder)
 
67
            {
 
68
                if (encoder->crtc_id == crtc_id)
 
69
                    crtc_used = true;
 
70
            }
 
71
        }
 
72
    });
 
73
 
 
74
    return crtc_used;
 
75
}
 
76
 
 
77
std::vector<mgm::DRMModeEncoderUPtr>
 
78
connector_available_encoders(mgm::DRMModeResources const& resources,
 
79
                             drmModeConnector const* connector)
 
80
{
 
81
    std::vector<mgm::DRMModeEncoderUPtr> encoders;
 
82
 
 
83
    for (int i = 0; i < connector->count_encoders; i++)
 
84
    {
 
85
        if (!encoder_is_used(resources, connector->encoders[i]))
 
86
            encoders.push_back(resources.encoder(connector->encoders[i]));
 
87
    }
 
88
 
 
89
    return encoders;
 
90
}
 
91
 
 
92
bool encoder_supports_crtc_index(drmModeEncoder const* encoder, uint32_t crtc_index)
 
93
{
 
94
    return (encoder->possible_crtcs & (1 << crtc_index));
 
95
}
 
96
 
 
97
const char *connector_type_name(uint32_t type)
 
98
{
 
99
    static const int nnames = 15;
 
100
    static const char * const names[nnames] =
 
101
    {   // Ordered according to xf86drmMode.h
 
102
        "Unknown",
 
103
        "VGA",
 
104
        "DVII",
 
105
        "DVID",
 
106
        "DVIA",
 
107
        "Composite",
 
108
        "SVIDEO",
 
109
        "LVDS",
 
110
        "Component",
 
111
        "9PinDIN",
 
112
        "DisplayPort",
 
113
        "HDMIA",
 
114
        "HDMIB",
 
115
        "TV",
 
116
        "eDP"
 
117
    };
 
118
 
 
119
    if (type >= nnames)
 
120
        type = 0;
 
121
 
 
122
    return names[type];
 
123
}
 
124
 
 
125
std::string connector_name(const drmModeConnector *conn)
 
126
{
 
127
    std::string name = connector_type_name(conn->connector_type);
 
128
    name += '-';
 
129
    name += std::to_string(conn->connector_type_id);
 
130
    return name;
 
131
}
 
132
 
 
133
}
 
134
 
 
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)
 
141
{
 
142
    reset();
 
143
 
 
144
    DRMModeResources resources{drm_fd};
 
145
 
 
146
    auto encoder = resources.encoder(connector->encoder_id);
 
147
    if (encoder)
 
148
    {
 
149
        auto crtc = resources.crtc(encoder->crtc_id);
 
150
        if (crtc)
 
151
            saved_crtc = *crtc;
 
152
    }
 
153
}
 
154
 
 
155
mgm::RealKMSOutput::~RealKMSOutput()
 
156
{
 
157
    restore_saved_crtc();
 
158
}
 
159
 
 
160
void mgm::RealKMSOutput::reset()
 
161
{
 
162
    DRMModeResources resources{drm_fd};
 
163
 
 
164
    /* Update the connector to ensure we have the latest information */
 
165
    connector = resources.connector(connector_id);
 
166
 
 
167
    if (!connector)
 
168
        BOOST_THROW_EXCEPTION(std::runtime_error("No DRM connector found\n"));
 
169
 
 
170
    // TODO: What if we can't locate the DPMS property?
 
171
    for (int i = 0; i < connector->count_props; i++)
 
172
    {
 
173
        auto prop = drmModeGetProperty(drm_fd, connector->props[i]);
 
174
        if (prop && (prop->flags & DRM_MODE_PROP_ENUM)) {
 
175
            if (!strcmp(prop->name, "DPMS"))
 
176
            {
 
177
                dpms_enum_id = connector->props[i];
 
178
                drmModeFreeProperty(prop);
 
179
                break;
 
180
            }
 
181
            drmModeFreeProperty(prop);
 
182
        }
 
183
    }
 
184
 
 
185
    /* Discard previously current crtc */
 
186
    current_crtc = nullptr;
 
187
}
 
188
 
 
189
geom::Size mgm::RealKMSOutput::size() const
 
190
{
 
191
    drmModeModeInfo const& mode(connector->modes[mode_index]);
 
192
    return {mode.hdisplay, mode.vdisplay};
 
193
}
 
194
 
 
195
void mgm::RealKMSOutput::configure(geom::Displacement offset, size_t kms_mode_index)
 
196
{
 
197
    fb_offset = offset;
 
198
    mode_index = kms_mode_index;
 
199
}
 
200
 
 
201
bool mgm::RealKMSOutput::set_crtc(uint32_t fb_id)
 
202
{
 
203
    if (!ensure_crtc())
 
204
        BOOST_THROW_EXCEPTION(std::runtime_error("Output " +
 
205
            connector_name(connector.get()) +
 
206
            " has no associated CRTC to set a framebuffer on"));
 
207
 
 
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]);
 
212
    if (ret)
 
213
    {
 
214
        current_crtc = nullptr;
 
215
        return false;
 
216
    }
 
217
 
 
218
    using_saved_crtc = false;
 
219
    return true;
 
220
}
 
221
 
 
222
void mgm::RealKMSOutput::clear_crtc()
 
223
{
 
224
    /*
 
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.
 
229
     */
 
230
    if (!ensure_crtc())
 
231
        return;
 
232
 
 
233
    auto result = drmModeSetCrtc(drm_fd, current_crtc->crtc_id,
 
234
                                 0, 0, 0, nullptr, 0, nullptr);
 
235
    if (result)
 
236
    {
 
237
        std::string const msg =
 
238
            "Couldn't clear output " + connector_name(connector.get());
 
239
 
 
240
        BOOST_THROW_EXCEPTION(
 
241
            ::boost::enable_error_info(std::runtime_error(msg))
 
242
                << (boost::error_info<KMSOutput, decltype(result)>(result)));
 
243
    }
 
244
 
 
245
    current_crtc = nullptr;
 
246
}
 
247
 
 
248
bool mgm::RealKMSOutput::schedule_page_flip(uint32_t fb_id)
 
249
{
 
250
    std::unique_lock<std::mutex> lg(power_mutex);
 
251
    if (power_mode != mir_power_mode_on)
 
252
        return true;
 
253
    if (!current_crtc)
 
254
        BOOST_THROW_EXCEPTION(std::runtime_error("Output " +
 
255
            connector_name(connector.get()) +
 
256
            " has no associated CRTC to schedule page flips on"));
 
257
 
 
258
    return page_flipper->schedule_flip(current_crtc->crtc_id, fb_id);
 
259
}
 
260
 
 
261
void mgm::RealKMSOutput::wait_for_page_flip()
 
262
{
 
263
    std::unique_lock<std::mutex> lg(power_mutex);
 
264
    if (power_mode != mir_power_mode_on)
 
265
        return;
 
266
    if (!current_crtc)
 
267
        BOOST_THROW_EXCEPTION(std::runtime_error("Output " +
 
268
            connector_name(connector.get()) +
 
269
            " has no associated CRTC to wait on"));
 
270
 
 
271
    page_flipper->wait_for_flip(current_crtc->crtc_id);
 
272
}
 
273
 
 
274
void mgm::RealKMSOutput::set_cursor(gbm_bo* buffer)
 
275
{
 
276
    if (current_crtc)
 
277
    {
 
278
        if (auto result = drmModeSetCursor(
 
279
                drm_fd,
 
280
                current_crtc->crtc_id,
 
281
                gbm_bo_get_handle(buffer).u32,
 
282
                gbm_bo_get_width(buffer),
 
283
                gbm_bo_get_height(buffer)))
 
284
        {
 
285
            BOOST_THROW_EXCEPTION(
 
286
                ::boost::enable_error_info(std::runtime_error("drmModeSetCursor() failed"))
 
287
                    << (boost::error_info<KMSOutput, decltype(result)>(result)));
 
288
        }
 
289
 
 
290
        has_cursor_ = true;
 
291
    }
 
292
}
 
293
 
 
294
void mgm::RealKMSOutput::move_cursor(geometry::Point destination)
 
295
{
 
296
    if (current_crtc)
 
297
    {
 
298
        if (auto result = drmModeMoveCursor(drm_fd, current_crtc->crtc_id,
 
299
                                            destination.x.as_uint32_t(),
 
300
                                            destination.y.as_uint32_t()))
 
301
        {
 
302
            BOOST_THROW_EXCEPTION(
 
303
                ::boost::enable_error_info(std::runtime_error("drmModeMoveCursor() failed"))
 
304
                    << (boost::error_info<KMSOutput, decltype(result)>(result)));
 
305
        }
 
306
    }
 
307
}
 
308
 
 
309
void mgm::RealKMSOutput::clear_cursor()
 
310
{
 
311
    if (current_crtc)
 
312
    {
 
313
        drmModeSetCursor(drm_fd, current_crtc->crtc_id, 0, 0, 0);
 
314
        has_cursor_ = false;
 
315
    }
 
316
}
 
317
 
 
318
bool mgm::RealKMSOutput::has_cursor() const
 
319
{
 
320
    return has_cursor_;
 
321
}
 
322
 
 
323
bool mgm::RealKMSOutput::ensure_crtc()
 
324
{
 
325
    /* Nothing to do if we already have a crtc */
 
326
    if (current_crtc)
 
327
        return true;
 
328
 
 
329
    /* If the output is not connected there is nothing to do */
 
330
    if (connector->connection != DRM_MODE_CONNECTED)
 
331
        return false;
 
332
 
 
333
    DRMModeResources resources{drm_fd};
 
334
 
 
335
    /* Check to see if there is a crtc already connected */
 
336
    auto encoder = resources.encoder(connector->encoder_id);
 
337
    if (encoder)
 
338
        current_crtc = resources.crtc(encoder->crtc_id);
 
339
 
 
340
    /* If we don't have a current crtc, try to find one */
 
341
    if (!current_crtc)
 
342
    {
 
343
        auto available_encoders = connector_available_encoders(resources, connector.get());
 
344
 
 
345
        int crtc_index = 0;
 
346
 
 
347
        resources.for_each_crtc([&](DRMModeCrtcUPtr crtc)
 
348
        {
 
349
            if (!current_crtc && !crtc_is_used(resources, crtc->crtc_id))
 
350
            {
 
351
                for (auto& enc : available_encoders)
 
352
                {
 
353
                    if (encoder_supports_crtc_index(enc.get(), crtc_index))
 
354
                    {
 
355
                        current_crtc = std::move(crtc);
 
356
                        break;
 
357
                    }
 
358
                }
 
359
            }
 
360
 
 
361
            crtc_index++;
 
362
        });
 
363
    }
 
364
 
 
365
    return (current_crtc != nullptr);
 
366
}
 
367
 
 
368
void mgm::RealKMSOutput::restore_saved_crtc()
 
369
{
 
370
    if (!using_saved_crtc)
 
371
    {
 
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);
 
375
 
 
376
        using_saved_crtc = true;
 
377
    }
 
378
}
 
379
 
 
380
void mgm::RealKMSOutput::set_power_mode(MirPowerMode mode)
 
381
{
 
382
    std::lock_guard<std::mutex> lg(power_mutex);
 
383
 
 
384
    if (power_mode != mode)
 
385
    {
 
386
        power_mode = mode;
 
387
        drmModeConnectorSetProperty(drm_fd, connector_id,
 
388
                                   dpms_enum_id, mode);
 
389
    }
 
390
}