Графическая подсистема Linux включает в себя графический интерфейс пользователя, 3D-приложение, DRM/KMS, аппаратное обеспечение и т. д.:
При разработке драйверов дисплея для Linux мы обычно сосредотачиваемся на подсистеме FBDEV (Framebuffer Device), DRM/KMS. С помощью платформы драйверов устройств FrameBuffer мы можем быстро разработать простой в использовании драйвер дисплея.
Однако с постепенным повышением производительности периферийных устройств отображения и внедрением 3D-рендеринга и графического процессора платформа FrameBuffer отстала. Такие функции, как наложение дисплея (уровень меню), ускорение графического процессора и аппаратные курсоры, не могут поддерживаться должным образом. и FrameBuffer будет. Базовая видеопамять доступна пользователю через /dev/fb, что может легко привести к конфликтам доступа, когда разные приложения используют видеопамять, что небезопасно. Поэтому для решения этих проблем необходима современная платформа графического отображения, и родился DRM (Direct Rendering Manager).
DRM распределяет некоторые операции, связанные с современной областью отображения, и делает эти модули независимыми. Если приложения верхнего уровня хотят управлять видеопамятью, эффектами отображения и графическим процессором, они должны делать это в соответствии с ограничениями некоторых платформ.
Мы можем понять структуру DRM с двух точек зрения: пространство пользователя и пространство ядра:
Пользовательское пространство (драйвер libdrm):
Пространство ядра (драйвер DRM):
DRM/KMS обычно используется для обозначения всей подсистемы DRM, но KMS и драйвер DRM — это только две части всей подсистемы DRM.
Libdrm, предоставляемый платформой DRM в пользовательском пространстве, инкапсулирует базовые интерфейсы, в основном инкапсулирует различные интерфейсы IOCTL и предоставляет общие интерфейсы API для верхнего уровня. Пользователи или приложения могут получить доступ, вызывая библиотечные функции, предоставляемые libdrm в пользовательском пространстве. ресурсы, а также управлять и использовать отображаемые ресурсы.
Таким образом, доступ к ресурсам дисплея осуществляется единообразно через libdrm. libdrm передает команду ядру, и, наконец, драйвер DRM принимает запросы каждого приложения и обрабатывает их, что позволяет эффективно избежать конфликтов доступа.
KMS — это большой модуль в рамках DRM, который в основном отвечает за две функции: настройку параметров дисплея и управление экраном дисплея. Можно сказать, что эти две основные функции являются основными возможностями драйвера дисплея. В рамках структуры DRM, чтобы адаптировать эти две части к логике современных устройств отображения, несколько подмодулей разделены для взаимодействия с структурой.
DRM FrameBuffer — это программная абстракция, аппаратно-независимый базовый элемент, описывающий информацию содержимого слоя (ширина, высота, формат пикселей, шаг и т. д.).
Плоскость означает слой. Базовый блок управления отображением. Каждое изображение имеет Planes. Свойства Planes управляют областью отображения изображения, переворачиванием изображения, методом смешивания цветов и т. д. Окончательное изображение проходит через Planes и компонент CRTC для получения смешанного отображения или отдельного. отображение нескольких изображений и другие функции.
CRTC: Контроллер электронно-лучевой трубки отвечает за преобразование отображаемого изображения в соответствии с конкретными требованиями синхронизации на базовом аппаратном уровне. Он также отвечает за переключение кадров, управление питанием, регулировку цвета и т. д. Он может подключать несколько кодеров для реализации функции копирования экрана. .
Кодер, выход преобразования, отвечает за управление питанием. Очевидно, что для выхода требуются разные преобразователи сигналов для преобразования пикселей памяти в сигналы, необходимые дисплею.
Разъем отвечает за доступ к аппаратным устройствам, таким как HDMI, VGA и т. д., и может получать EDID устройства, состояние подключения DPMS и т. д.
Вышеупомянутые компоненты, наконец, завершают полный процесс управления дисплеем DRM, как показано на следующем рисунке:
Вышеупомянутые компоненты CRTC, Planes, Encoder и Connector являются абстракциями аппаратного обеспечения. Даже если соответствующее им реальное оборудование отсутствует, их все равно необходимо реализовать в программном драйвере, иначе подсистема DRM не сможет работать нормально.
GEM отвечает за управление памятью (например, видеопамятью), используемой DRM, и представляет собой программную абстракцию.
Возможности, предоставляемые инфраструктурой GEM, включают в себя:
Драйвер функции дисплея обычно реализуется производителем чипа Rockchip для завершения DRM-хоста. Код драйвера хоста обычно находится в каталоге driver/gpu/drm/xxx/, где xxx относится к производителю чипа.
В драйвере драйвер дисплея Rockchip использует структуру компонентов, драйвер дисплея является главным, а устройство под драйвером дисплея называется компонентом.
Узел портов в узле устройства display_subsystem — это связанный компонент, который фактически указывает на узлы vopl_out и vopb_out (VOP — это интерфейс для различных выходных изображений под этим узлом, который может выводить несколько видеосигналов одновременно). время.
rk3399.dtsi
display_subsystem: display-subsystem {
status = "okay";
compatible = "rockchip,display-subsystem";
ports = <&vopl_out>, <&vopb_out>;
clocks = <&cru PLL_VPLL>, <&cru PLL_CPLL>;
clock-names = "hdmi-tmds-pll", "default-vop-pll";
devfreq = <&dmc>;
logo-memory-region = <&drm_logo>;
secure-memory-region = <&secure_memory>;
route {
route_dsi: route-dsi {
status = "disabled";
logo,uboot = "logo.bmp";
logo,kernel = "logo_kernel.bmp";
logo,mode = "center";
charge_logo,mode = "center";
connect = <&vopb_out_dsi>;
};
......
};
};
vopl: vop@ff8f0000 {
compatible = "rockchip,rk3399-vop-lit";
reg = <0x0 0xff8f0000 0x0 0x600>,
<0x0 0xff8f1c00 0x0 0x200>,
<0x0 0xff8f2000 0x0 0x400>;
reg-names = "regs", "cabc_lut", "gamma_lut";
interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH 0>;
clocks = <&cru ACLK_VOP1>, <&cru DCLK_VOP1>, <&cru HCLK_VOP1>, <&cru DCLK_VOP1_DIV>;
clock-names = "aclk_vop", "dclk_vop", "hclk_vop", "dclk_source";
iommus = <&vopl_mmu>;
power-domains = <&power RK3399_PD_VOPL>;
resets = <&cru SRST_A_VOP1>, <&cru SRST_H_VOP1>, <&cru SRST_D_VOP1>;
reset-names = "axi", "ahb", "dclk";
status = "disabled";
vopl_out: port {
#address-cells = <1>;
#size-cells = <0>;
vopl_out_dsi: endpoint@0 {
reg = <0>;
remote-endpoint = <&dsi_in_vopl>;
};
vopl_out_edp: endpoint@1 {
reg = <1>;
remote-endpoint = <&edp_in_vopl>;
};
vopl_out_hdmi: endpoint@2 {
reg = <2>;
remote-endpoint = <&hdmi_in_vopl>;
};
vopl_out_dp: endpoint@3 {
reg = <3>;
remote-endpoint = <&dp_in_vopl>;
};
vopl_out_dsi1: endpoint@4 {
reg = <4>;
remote-endpoint = <&dsi1_in_vopl>;
};
};
};
vopb: vop@ff900000 {
compatible = "rockchip,rk3399-vop-big";
reg = <0x0 0xff900000 0x0 0x600>,
<0x0 0xff901c00 0x0 0x200>,
<0x0 0xff902000 0x0 0x1000>;
reg-names = "regs", "cabc_lut", "gamma_lut";
interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH 0>;
clocks = <&cru ACLK_VOP0>, <&cru DCLK_VOP0>, <&cru HCLK_VOP0>, <&cru DCLK_VOP0_DIV>;
clock-names = "aclk_vop", "dclk_vop", "hclk_vop", "dclk_source";
resets = <&cru SRST_A_VOP0>, <&cru SRST_H_VOP0>, <&cru SRST_D_VOP0>;
reset-names = "axi", "ahb", "dclk";
power-domains = <&power RK3399_PD_VOPB>;
iommus = <&vopb_mmu>;
status = "disabled";
vopb_out: port {
#address-cells = <1>;
#size-cells = <0>;
vopb_out_edp: endpoint@0 {
reg = <0>;
remote-endpoint = <&edp_in_vopb>;
};
vopb_out_dsi: endpoint@1 {
reg = <1>;
remote-endpoint = <&dsi_in_vopb>;
};
vopb_out_hdmi: endpoint@2 {
reg = <2>;
remote-endpoint = <&hdmi_in_vopb>;
};
vopb_out_dp: endpoint@3 {
reg = <3>;
remote-endpoint = <&dp_in_vopb>;
};
vopb_out_dsi1: endpoint@4 {
reg = <4>;
remote-endpoint = <&dsi1_in_vopb>;
};
};
};
Драйвер платформы rockchip-drm соответствует дереву устройств. Он будет искать узел портов и узел iommus в дереве устройств. Используйте функцию компонент_master_add_with_match, чтобы зарегистрироваться в структуре компонента. Rockchip_drm_ops установлен, и его компоненты можно добавлять через файл. ФункцияComponent_add После того, как мастер сопоставит все компоненты, будет вызвана функция обратного вызова привязки мастера и, наконец, зарегистрирована в ядре DRM с помощью функции drm_dev_register().
Исходный код драйвера платформы выглядит следующим образом: driver/gpu/drm/rockchip/rockchip_drm_drv.c
static const struct component_master_ops rockchip_drm_ops = {
.bind = rockchip_drm_bind,
.unbind = rockchip_drm_unbind,
};
static int rockchip_drm_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct component_match *match = NULL;
struct device_node *np = dev->of_node;
struct device_node *port;
int i;
DRM_INFO("Rockchip DRM driver version: %s\n", DRIVER_VERSION);
if (!np)
return -ENODEV;
for (i = 0;; i++) {
struct device_node *iommu;
port = of_parse_phandle(np, "ports", i);
if (!port)
break;
if (!of_device_is_available(port->parent)) {
of_node_put(port);
continue;
}
iommu = of_parse_phandle(port->parent, "iommus", 0);
if (!iommu || !of_device_is_available(iommu->parent)) {
dev_dbg(dev, "no iommu attached for %s, using non-iommu buffers\n",
port->parent->full_name);
is_support_iommu = false;
}
component_match_add(dev, &match, compare_of, port->parent);
of_node_put(port);
}
......
for (i = 0;; i++) {
port = of_parse_phandle(np, "ports", i);
if (!port)
break;
if (!of_device_is_available(port->parent)) {
of_node_put(port);
continue;
}
rockchip_add_endpoints(dev, &match, port);
of_node_put(port);
}
port = of_parse_phandle(np, "backlight", 0);
if (port && of_device_is_available(port)) {
component_match_add(dev, &match, compare_of, port);
of_node_put(port);
}
return component_master_add_with_match(dev, &rockchip_drm_ops, match);
}
static const struct of_device_id rockchip_drm_dt_ids[] = {
{ .compatible = "rockchip,display-subsystem", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
static struct platform_driver rockchip_drm_platform_driver = {
.probe = rockchip_drm_platform_probe,
.remove = rockchip_drm_platform_remove,
.shutdown = rockchip_drm_platform_shutdown,
.driver = {
.name = "rockchip-drm",
.of_match_table = rockchip_drm_dt_ids,
.pm = &rockchip_drm_pm_ops,
},
};
У блогера установлен экран MIPI DSI, а конфигурация дерева устройств следующая:
dsi@ff960000 {
compatible = "rockchip,rk3399-mipi-dsi";
reg = <0x0 0xff960000 0x0 0x8000>;
interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH 0>;
clocks = <0x8 0xa2 0x8 0x170 0x8 0xa3>;
clock-names = "ref", "pclk", "phy_cfg";
power-domains = <&power RK3399_PD_VIO>;
resets = <&cru SRST_P_MIPI_DSI0>;
reset-names = "apb";
rockchip,grf = <&grf>;
status = "okay";
#address-cells = <0x1>;
#size-cells = <0x0>;
phandle = <0x137>;
ports {
port {
#address-cells = <1>;
#size-cells = <0>;
endpoint@0 {
reg = <0x0>;
remote-endpoint = <&vopb_out_dsi>;
status = "okay";
phandle = <0xab>;
};
endpoint@1 {
reg = <0x1>;
remote-endpoint = <&vopl_out_dsi>;
status = "disabled";
phandle = <0xa3>;
};
};
};
panel@0 {
status = "okay";
compatible = "simple-panel-dsi";
reg = <0x0>;
backlight = <&backlight>;
dsi,flags = <0xa03>;
dsi,format = <MIPI_DSI_FMT_RGB888>;
dsi,lanes = <4>;
dsi,channel = <0>;
enable-delay-ms = <35>;
prepare-delay-ms = <6>;
unprepare-delay-ms = <0>;
disable-delay-ms = <20>;
size,width = <120>;
size,height = <170>;
panel-init-sequence = [29 ...... 29];
panel-exit-sequence = <0x5050128 0x5780110>;
phandle = <0x138>;
power_ctr {
rockchip,debug = <0>;
power_enable = <1>;
phandle = <0x139>;
lcd-rst {
gpios = <&gpio4 RK_PD6 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&lcd_panel_reset>;
rockchip,delay = <6>;
phandle = <0x13a>;
};
};
display-timings {
native-mode = <&timing0>;
phandle = <0x13b>;
timing0 {
clock-frequency = <0x3938700>;
hactive = <0x320>;
vactive = <0x500>;
hsync-len = <0x14>;
hback-porch = <0x14>;
hfront-porch = <0x14>;
vsync-len = <0x4>;
vback-porch = <0x4>;
vfront-porch = <0xa>;
hsync-active = <0x0>;
vsync-active = <0x0>;
de-active = <0x0>;
pixelclk-active = <0x0>;
phandle = <0xba>;
};
};
};
};
Соответствующий драйвер DSI — driver/gpu/drm/rockchip/dw-mipi-dsi.c.
Соответствующий драйвер панели — driver/gpu/drm/panel/panel-simple.c.
static const struct of_device_id dw_mipi_dsi_dt_ids[] = {
{ .compatible = "rockchip,rk3399-mipi-dsi", .data = &rk3399_socdata, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, dw_mipi_dsi_dt_ids);
static struct platform_driver dw_mipi_dsi_driver = {
.probe = dw_mipi_dsi_probe,
.remove = dw_mipi_dsi_remove,
.driver = {
.of_match_table = dw_mipi_dsi_dt_ids,
.pm = &dw_mipi_dsi_pm_ops,
.name = DRIVER_NAME,
},
};
module_platform_driver(dw_mipi_dsi_driver);
static int dw_mipi_dsi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dw_mipi_dsi *dsi;
struct device_node *np = dev->of_node;
struct resource *res;
void __iomem *regs;
int ret;
int dsi_id;
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
if (!dsi)
return -ENOMEM;
dsi_id = of_alias_get_id(np, "dsi");
if (dsi_id < 0)
dsi_id = 0;
dsi->id = dsi_id;
dsi->dev = dev;
dsi->pdata = of_device_get_match_data(dev);
platform_set_drvdata(pdev, dsi);
ret = dw_mipi_dsi_parse_dt(dsi);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(dev, res);
dsi->irq = platform_get_irq(pdev, 0);
dsi->pclk = devm_clk_get(dev, "pclk");
dsi->regmap = devm_regmap_init_mmio(dev, regs,&dw_mipi_dsi_regmap_config);
if (dsi->pdata->soc_type == RK3126) {
dsi->h2p_clk = devm_clk_get(dev, "h2p");
}
}
dsi->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
dsi->rst = devm_reset_control_get(dev, "apb");
ret = mipi_dphy_attach(dsi);
ret = devm_request_irq(dev, dsi->irq, dw_mipi_dsi_irq_handler,IRQF_SHARED, dev_name(dev), dsi);
dsi->dsi_host.ops = &dw_mipi_dsi_host_ops;
dsi->dsi_host.dev = dev;
ret = mipi_dsi_host_register(&dsi->dsi_host);
ret = component_add(dev, &dw_mipi_dsi_ops);
if (ret)
mipi_dsi_host_unregister(&dsi->dsi_host);
return ret;
}
Здесь будет зарегистрировано прерывание mipi dsi, а функция обработки прерывания — dw_mipi_dsi_irq_handler.
4、debug
1. Подключитесь к ADB, установите инструмент тестирования, установите libdrm-tests, выполните modetest, и будет распечатана подробная информация о кодировщиках, разъемах, CRTC и плоскостях.
2. Драйвер DRM создаст 3 узла устройств в /dev/dri: card0, controlD64 и renderD128, которые смогут открыть card0 для работы в пользовательском пространстве.
3. Существует два интерфейса для программирования приложений DRM: устаревший интерфейс и атомарный интерфейс. В настоящее время обычно используется атомарный интерфейс.
Хотя функция DRM отвечает потребностям современных устройств отображения, все еще существует множество старых устройств и программного обеспечения, требующих поддержки кадрового буфера. Поэтому в рамках DRM некоторые коды используются для имитации устройств FB в рамках DRM.
В коде драйвера дисплея, предоставленном Rockchip, также есть код, связанный с моделированием устройств FB. См. файл driver/gpu/drm/rockchip/rockchip_drm_fb.c. Конечный результат заключается в том, что на устройстве появляется знакомая фигура /dev/fb0. каталог.