/* retrieve resources */
drmModeRes *res;
res = drmModeGetResources(fd);
通过drmModeGetResources能获取到有限个connector和crtc。而某个connector是否能与某个crtc一起协同工作,需要encoder的支持。每个connector都有有限个可供使用的encoder列表,而encoder需要与crtc适配上才能工作。
每个连接器都有一个可供使用的编码器的有限列表,可以通过conn->count_encoders来遍历每个连接器对应的编码器。每个编码器只能与有限的crtc列表一起工作,crtc只有有限个:res->count_crtcs,与连接器一样的少 res->count_connectors。
/*
* modeset_find_crtc(fd, res, conn, dev): This small helper tries to find a
* suitable CRTC for the given connector. We have actually have to introduce one
* more DRM object to make this more clear: Encoders.
* Encoders help the CRTC to convert data from a framebuffer into the right
* format that can be used for the chosen connector. We do not have to
* understand any more of these conversions to make use of it. However, you must
* know that each connector has a limited list of encoders that it can use. And
* each encoder can only work with a limited list of CRTCs. So what we do is
* trying each encoder that is available and looking for a CRTC that this
* encoder can work with. If we find the first working combination, we are happy
* and write it into the @dev structure.
* But before iterating all available encoders, we first try the currently
* active encoder+crtc on a connector to avoid a full modeset.
*
* However, before we can use a CRTC we must make sure that no other device,
* that we setup previously, is already using this CRTC. Remember, we can only
* drive one connector per CRTC! So we simply iterate through the "modeset_list"
* of previously setup devices and check that this CRTC wasn't used before.
* Otherwise, we continue with the next CRTC/Encoder combination.
*/
static int modeset_find_crtc(int fd, drmModeRes *res, drmModeConnector *conn,
struct modeset_dev *dev)
{
drmModeEncoder *enc;
unsigned int i, j;
int32_t crtc;
struct modeset_dev *iter;
/* first try the currently conected encoder+crtc */
if (conn->encoder_id)
enc = drmModeGetEncoder(fd, conn->encoder_id);
else
enc = NULL;
if (enc) {
if (enc->crtc_id) {
crtc = enc->crtc_id;
for (iter = modeset_list; iter; iter = iter->next) {
if (iter->crtc == crtc) {
crtc = -1;
break;
}
}
if (crtc >= 0) {
drmModeFreeEncoder(enc);
dev->crtc = crtc;
return 0;
}
}
drmModeFreeEncoder(enc);
}
/* If the connector is not currently bound to an encoder or if the
* encoder+crtc is already used by another connector (actually unlikely
* but lets be safe), iterate all other available encoders to find a
* matching CRTC. */
for (i = 0; i < conn->count_encoders; ++i) {
enc = drmModeGetEncoder(fd, conn->encoders[i]);
if (!enc) {
fprintf(stderr, "cannot retrieve encoder %u:%u (%d): %m\n",
i, conn->encoders[i], errno);
continue;
}
/* iterate all global CRTCs */
for (j = 0; j < res->count_crtcs; ++j) {
/* check whether this CRTC works with the encoder */
if (!(enc->possible_crtcs & (1 << j)))
continue;
/* check that no other device already uses this CRTC */
crtc = res->crtcs[j];
for (iter = modeset_list; iter; iter = iter->next) {
if (iter->crtc == crtc) {
crtc = -1;
break;
}
}
/* we have found a CRTC, so save it and return */
if (crtc >= 0) {
drmModeFreeEncoder(enc);
dev->crtc = crtc;
return 0;
}
}
drmModeFreeEncoder(enc);
}
fprintf(stderr, "cannot find suitable CRTC for connector %u\n",
conn->connector_id);
return -ENOENT;
}
vblank:垂直空白是时间段:当显示控制器暂停扫描帧缓冲区时。之后垂直空白结束后,帧缓冲区再次逐行扫描并接着是一个垂直空白。
调用者调用drmModePageFlip函数为下一个vblank安排缓冲区翻转,当下一个vblank时间段到了后,内核会使用上次drmModePageFlip传入的buf->fb以完成下一次的缓冲区扫描操作.
当内核完成页面翻转工作后,DRM fd将变得可读.此时调用者可以再次调用drmModePageFlip函数为下一个vblank安排缓冲区翻转。