DRM 全称是Direct Rendering Manager,进行显示输出管理、buffer 分配、帧缓冲。对应userspace 库为libdrm,libdrm 库提供了一系列友好的控制封装,使用户可以方便的进行显示的控制和buffer 申请。
DRM的设备节点为"/dev/dri/cardX", X 为0-15 的数值,默认使用的是/dev/dri/card0。
CRTC | 显示控制器,在rockchip 平台是SOC 内部VOP(部分文档也称为LCDC)模块的抽象 |
Plane | 图层,在rockchip 平台是SOC 内部VOP(LCDC)模块win 图层的抽象 |
Encoder | 输出转换器,指RGB、LVDS、DSI、eDP、HDMI、CVBS、VGA 等显示接口 |
Connector | 连接器,指encoder 和panel 之间交互的接口部分 |
Bridge | 桥接设备,一般用于注册encoder 后面另外再接的转换芯片,如DSI2HDMI 转换芯片 |
Panel | 泛指屏,各种LCD、HDMI 等显示设备的抽象 |
GEM | buffer 管理和分配,类似android 下的ion |
drm sysfs 初始化
/**
* drm_sysfs_init - initialize sysfs helpers
*
* This is used to create the DRM class, which is the implicit parent of any
* other top-level DRM sysfs objects.
*
* You must call drm_sysfs_destroy() to release the allocated resources.
*
* Return: 0 on success, negative error code on failure.
*/
int drm_sysfs_init(void)
{
int err;
drm_class = class_create(THIS_MODULE, "drm");//sys/class/drm
if (IS_ERR(drm_class))
return PTR_ERR(drm_class);
err = class_create_file(drm_class, &class_attr_version.attr);
if (err) {
class_destroy(drm_class);
drm_class = NULL;
return err;
}
drm_class->devnode = drm_devnode;
drm_setup_hdcp_srm(drm_class);
return 0;
}
connector 注册的时候会注册进去
static DEVICE_ATTR_RW(status);//显示器的连接状态
static DEVICE_ATTR_RO(enabled);//使能
static DEVICE_ATTR_RO(dpms);//dpms
static DEVICE_ATTR_RO(modes);//分辨率
static struct attribute *connector_dev_attrs[] = {
&dev_attr_status.attr,
&dev_attr_enabled.attr,
&dev_attr_dpms.attr,
&dev_attr_modes.attr,
NULL
};
static struct bin_attribute edid_attr = {
.attr.name = "edid",
.attr.mode = 0444,
.size = 0,
.read = edid_show,
};
static struct bin_attribute *connector_bin_attrs[] = {
&edid_attr,
NULL
};
static const struct attribute_group connector_dev_group = {
.attrs = connector_dev_attrs,
.bin_attrs = connector_bin_attrs,
};
static const struct attribute_group *connector_dev_groups[] = {
&connector_dev_group,
NULL
};
int drm_sysfs_connector_add(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
if (connector->kdev)
return 0;
connector->kdev =
device_create_with_groups(drm_class, dev->primary->kdev, 0,
connector, connector_dev_groups,
"card%d-%s", dev->primary->index,
connector->name);
DRM_DEBUG("adding \"%s\" to sysfs\n",
connector->name);
if (IS_ERR(connector->kdev)) {
DRM_ERROR("failed to register connector device: %ld\n", PTR_ERR(connector->kdev));
return PTR_ERR(connector->kdev);
}
if (connector->ddc)
return sysfs_create_link(&connector->kdev->kobj,
&connector->ddc->dev.kobj, "ddc");
return 0;
}
重要的2个show函数如下,显示器的edid和modes
static ssize_t modes_show(struct device *device,
struct device_attribute *attr,
char *buf)
{
struct drm_connector *connector = to_drm_connector(device);
struct drm_display_mode *mode;
int written = 0;
mutex_lock(&connector->dev->mode_config.mutex);
list_for_each_entry(mode, &connector->modes, head) {
written += snprintf(buf + written, PAGE_SIZE - written, "%s\n",
mode->name);
}
mutex_unlock(&connector->dev->mode_config.mutex);
return written;
}
static ssize_t edid_show(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off,
size_t count)
{
struct device *connector_dev = kobj_to_dev(kobj);
struct drm_connector *connector = to_drm_connector(connector_dev);
unsigned char *edid;
size_t size;
ssize_t ret = 0;
mutex_lock(&connector->dev->mode_config.mutex);
if (!connector->edid_blob_ptr)
goto unlock;
edid = connector->edid_blob_ptr->data;
size = connector->edid_blob_ptr->length;
if (!edid)
goto unlock;
if (off >= size)
goto unlock;
if (off + count > size)
count = size - off;
memcpy(buf, edid + off, count);
ret = count;
unlock:
mutex_unlock(&connector->dev->mode_config.mutex);
return ret;
}