1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/ozone/platform/drm/common/drm_util.h"
10 #include <xf86drmMode.h>
12 #include "ui/display/util/edid_parser.h"
14 #if !defined(DRM_MODE_CONNECTOR_DSI)
15 #define DRM_MODE_CONNECTOR_DSI 16
22 bool IsCrtcInUse(uint32_t crtc
,
23 const ScopedVector
<HardwareDisplayControllerInfo
>& displays
) {
24 for (size_t i
= 0; i
< displays
.size(); ++i
) {
25 if (crtc
== displays
[i
]->crtc()->crtc_id
)
32 uint32_t GetCrtc(int fd
,
33 drmModeConnector
* connector
,
34 drmModeRes
* resources
,
35 const ScopedVector
<HardwareDisplayControllerInfo
>& displays
) {
36 // If the connector already has an encoder try to re-use.
37 if (connector
->encoder_id
) {
38 ScopedDrmEncoderPtr
encoder(drmModeGetEncoder(fd
, connector
->encoder_id
));
39 if (encoder
&& encoder
->crtc_id
&& !IsCrtcInUse(encoder
->crtc_id
, displays
))
40 return encoder
->crtc_id
;
43 // Try to find an encoder for the connector.
44 for (int i
= 0; i
< connector
->count_encoders
; ++i
) {
45 ScopedDrmEncoderPtr
encoder(drmModeGetEncoder(fd
, connector
->encoders
[i
]));
49 for (int j
= 0; j
< resources
->count_crtcs
; ++j
) {
50 // Check if the encoder is compatible with this CRTC
51 if (!(encoder
->possible_crtcs
& (1 << j
)) ||
52 IsCrtcInUse(resources
->crtcs
[j
], displays
))
55 return resources
->crtcs
[j
];
62 // Computes the refresh rate for the specific mode. If we have enough
63 // information use the mode timings to compute a more exact value otherwise
64 // fallback to using the mode's vertical refresh rate (the kernel computes this
65 // the same way, however there is a loss in precision since |vrefresh| is sent
67 float GetRefreshRate(const drmModeModeInfo
& mode
) {
68 if (!mode
.htotal
|| !mode
.vtotal
)
71 float clock
= mode
.clock
;
72 float htotal
= mode
.htotal
;
73 float vtotal
= mode
.vtotal
;
75 return (clock
* 1000.0f
) / (htotal
* vtotal
);
78 DisplayConnectionType
GetDisplayType(drmModeConnector
* connector
) {
79 switch (connector
->connector_type
) {
80 case DRM_MODE_CONNECTOR_VGA
:
81 return DISPLAY_CONNECTION_TYPE_VGA
;
82 case DRM_MODE_CONNECTOR_DVII
:
83 case DRM_MODE_CONNECTOR_DVID
:
84 case DRM_MODE_CONNECTOR_DVIA
:
85 return DISPLAY_CONNECTION_TYPE_DVI
;
86 case DRM_MODE_CONNECTOR_LVDS
:
87 case DRM_MODE_CONNECTOR_eDP
:
88 case DRM_MODE_CONNECTOR_DSI
:
89 return DISPLAY_CONNECTION_TYPE_INTERNAL
;
90 case DRM_MODE_CONNECTOR_DisplayPort
:
91 return DISPLAY_CONNECTION_TYPE_DISPLAYPORT
;
92 case DRM_MODE_CONNECTOR_HDMIA
:
93 case DRM_MODE_CONNECTOR_HDMIB
:
94 return DISPLAY_CONNECTION_TYPE_HDMI
;
96 return DISPLAY_CONNECTION_TYPE_UNKNOWN
;
100 int GetDrmProperty(int fd
,
101 drmModeConnector
* connector
,
102 const std::string
& name
,
103 ScopedDrmPropertyPtr
* property
) {
104 for (int i
= 0; i
< connector
->count_props
; ++i
) {
105 ScopedDrmPropertyPtr
tmp(drmModeGetProperty(fd
, connector
->props
[i
]));
109 if (name
== tmp
->name
) {
110 *property
= tmp
.Pass();
118 std::string
GetNameForEnumValue(drmModePropertyRes
* property
, uint32_t value
) {
119 for (int i
= 0; i
< property
->count_enums
; ++i
)
120 if (property
->enums
[i
].value
== value
)
121 return property
->enums
[i
].name
;
123 return std::string();
126 ScopedDrmPropertyBlobPtr
GetDrmPropertyBlob(int fd
,
127 drmModeConnector
* connector
,
128 const std::string
& name
) {
129 ScopedDrmPropertyPtr property
;
130 int index
= GetDrmProperty(fd
, connector
, name
, &property
);
134 if (property
->flags
& DRM_MODE_PROP_BLOB
) {
135 return ScopedDrmPropertyBlobPtr(
136 drmModeGetPropertyBlob(fd
, connector
->prop_values
[index
]));
142 bool IsAspectPreserving(int fd
, drmModeConnector
* connector
) {
143 ScopedDrmPropertyPtr property
;
144 int index
= GetDrmProperty(fd
, connector
, "scaling mode", &property
);
148 return (GetNameForEnumValue(property
.get(), connector
->prop_values
[index
]) ==
154 HardwareDisplayControllerInfo::HardwareDisplayControllerInfo(
155 ScopedDrmConnectorPtr connector
,
156 ScopedDrmCrtcPtr crtc
)
157 : connector_(connector
.Pass()), crtc_(crtc
.Pass()) {
160 HardwareDisplayControllerInfo::~HardwareDisplayControllerInfo() {
163 ScopedVector
<HardwareDisplayControllerInfo
> GetAvailableDisplayControllerInfos(
165 ScopedDrmResourcesPtr
resources(drmModeGetResources(fd
));
166 DCHECK(resources
) << "Failed to get DRM resources";
167 ScopedVector
<HardwareDisplayControllerInfo
> displays
;
169 for (int i
= 0; i
< resources
->count_connectors
; ++i
) {
170 ScopedDrmConnectorPtr
connector(
171 drmModeGetConnector(fd
, resources
->connectors
[i
]));
173 if (!connector
|| connector
->connection
!= DRM_MODE_CONNECTED
||
174 connector
->count_modes
== 0)
177 uint32_t crtc_id
= GetCrtc(fd
, connector
.get(), resources
.get(), displays
);
181 ScopedDrmCrtcPtr
crtc(drmModeGetCrtc(fd
, crtc_id
));
183 new HardwareDisplayControllerInfo(connector
.Pass(), crtc
.Pass()));
186 return displays
.Pass();
189 bool SameMode(const drmModeModeInfo
& lhs
, const drmModeModeInfo
& rhs
) {
190 return lhs
.clock
== rhs
.clock
&& lhs
.hdisplay
== rhs
.hdisplay
&&
191 lhs
.vdisplay
== rhs
.vdisplay
&& lhs
.vrefresh
== rhs
.vrefresh
&&
192 lhs
.hsync_start
== rhs
.hsync_start
&& lhs
.hsync_end
== rhs
.hsync_end
&&
193 lhs
.htotal
== rhs
.htotal
&& lhs
.hskew
== rhs
.hskew
&&
194 lhs
.vsync_start
== rhs
.vsync_start
&& lhs
.vsync_end
== rhs
.vsync_end
&&
195 lhs
.vtotal
== rhs
.vtotal
&& lhs
.vscan
== rhs
.vscan
&&
196 lhs
.flags
== rhs
.flags
&& strcmp(lhs
.name
, rhs
.name
) == 0;
199 DisplayMode_Params
CreateDisplayModeParams(const drmModeModeInfo
& mode
) {
200 DisplayMode_Params params
;
201 params
.size
= gfx::Size(mode
.hdisplay
, mode
.vdisplay
);
202 params
.is_interlaced
= mode
.flags
& DRM_MODE_FLAG_INTERLACE
;
203 params
.refresh_rate
= GetRefreshRate(mode
);
208 DisplaySnapshot_Params
CreateDisplaySnapshotParams(
209 HardwareDisplayControllerInfo
* info
,
211 size_t display_index
,
212 const gfx::Point
& origin
) {
213 DisplaySnapshot_Params params
;
214 params
.display_id
= display_index
;
215 params
.origin
= origin
;
216 params
.physical_size
=
217 gfx::Size(info
->connector()->mmWidth
, info
->connector()->mmHeight
);
218 params
.type
= GetDisplayType(info
->connector());
219 params
.is_aspect_preserving_scaling
=
220 IsAspectPreserving(fd
, info
->connector());
222 ScopedDrmPropertyBlobPtr
edid_blob(
223 GetDrmPropertyBlob(fd
, info
->connector(), "EDID"));
226 std::vector
<uint8_t> edid(
227 static_cast<uint8_t*>(edid_blob
->data
),
228 static_cast<uint8_t*>(edid_blob
->data
) + edid_blob
->length
);
230 if (!GetDisplayIdFromEDID(edid
, display_index
, ¶ms
.display_id
,
232 params
.display_id
= display_index
;
234 ParseOutputDeviceData(edid
, nullptr, nullptr, ¶ms
.display_name
, nullptr,
236 ParseOutputOverscanFlag(edid
, ¶ms
.has_overscan
);
238 VLOG(1) << "Failed to get EDID blob for connector "
239 << info
->connector()->connector_id
;
242 for (int i
= 0; i
< info
->connector()->count_modes
; ++i
) {
243 const drmModeModeInfo
& mode
= info
->connector()->modes
[i
];
244 params
.modes
.push_back(CreateDisplayModeParams(mode
));
246 if (info
->crtc()->mode_valid
&& SameMode(info
->crtc()->mode
, mode
)) {
247 params
.has_current_mode
= true;
248 params
.current_mode
= params
.modes
.back();
251 if (mode
.type
& DRM_MODE_TYPE_PREFERRED
) {
252 params
.has_native_mode
= true;
253 params
.native_mode
= params
.modes
.back();
257 // If no preferred mode is found then use the first one. Using the first one
258 // since it should be the best mode.
259 if (!params
.has_native_mode
&& !params
.modes
.empty()) {
260 params
.has_native_mode
= true;
261 params
.native_mode
= params
.modes
.front();