Структура DRM Linux и анализ ситуации
Структура DRM Linux и анализ ситуации

1. Система управления цифровыми правами

Графическая подсистема Linux включает в себя графический интерфейс пользователя, 3D-приложение, DRM/KMS, аппаратное обеспечение и т. д.:

При разработке драйверов дисплея для Linux мы обычно сосредотачиваемся на подсистеме FBDEV (Framebuffer Device), DRM/KMS. С помощью платформы драйверов устройств FrameBuffer мы можем быстро разработать простой в использовании драйвер дисплея.

Однако с постепенным повышением производительности периферийных устройств отображения и внедрением 3D-рендеринга и графического процессора платформа FrameBuffer отстала. Такие функции, как наложение дисплея (уровень меню), ускорение графического процессора и аппаратные курсоры, не могут поддерживаться должным образом. и FrameBuffer будет. Базовая видеопамять доступна пользователю через /dev/fb, что может легко привести к конфликтам доступа, когда разные приложения используют видеопамять, что небезопасно. Поэтому для решения этих проблем необходима современная платформа графического отображения, и родился DRM (Direct Rendering Manager).

DRM распределяет некоторые операции, связанные с современной областью отображения, и делает эти модули независимыми. Если приложения верхнего уровня хотят управлять видеопамятью, эффектами отображения и графическим процессором, они должны делать это в соответствии с ограничениями некоторых платформ.

Мы можем понять структуру DRM с двух точек зрения: пространство пользователя и пространство ядра:

Пользовательское пространство (драйвер libdrm):

  • Libdrm(DRM рамкасуществоватьпользовательское пространствоиз Lib)

Пространство ядра (драйвер DRM):

  • KMS (настройка режима ядра, настройка режима отображения ядра)
  • GEM (Graphic Execution Manager, графический менеджер выполнения)

DRM/KMS обычно используется для обозначения всей подсистемы DRM, но KMS и драйвер DRM — это только две части всей подсистемы DRM.

Libdrm

Libdrm, предоставляемый платформой DRM в пользовательском пространстве, инкапсулирует базовые интерфейсы, в основном инкапсулирует различные интерфейсы IOCTL и предоставляет общие интерфейсы API для верхнего уровня. Пользователи или приложения могут получить доступ, вызывая библиотечные функции, предоставляемые libdrm в пользовательском пространстве. ресурсы, а также управлять и использовать отображаемые ресурсы.

Таким образом, доступ к ресурсам дисплея осуществляется единообразно через libdrm. libdrm передает команду ядру, и, наконец, драйвер DRM принимает запросы каждого приложения и обрабатывает их, что позволяет эффективно избежать конфликтов доступа.

KMS(Kernel Mode Setting)

KMS — это большой модуль в рамках DRM, который в основном отвечает за две функции: настройку параметров дисплея и управление экраном дисплея. Можно сказать, что эти две основные функции являются основными возможностями драйвера дисплея. В рамках структуры DRM, чтобы адаптировать эти две части к логике современных устройств отображения, несколько подмодулей разделены для взаимодействия с структурой.

1、DRM FrameBuffer

DRM FrameBuffer — это программная абстракция, аппаратно-независимый базовый элемент, описывающий информацию содержимого слоя (ширина, высота, формат пикселей, шаг и т. д.).

2、Planes

Плоскость означает слой. Базовый блок управления отображением. Каждое изображение имеет Planes. Свойства Planes управляют областью отображения изображения, переворачиванием изображения, методом смешивания цветов и т. д. Окончательное изображение проходит через Planes и компонент CRTC для получения смешанного отображения или отдельного. отображение нескольких изображений и другие функции.

3、CRTC

CRTC: Контроллер электронно-лучевой трубки отвечает за преобразование отображаемого изображения в соответствии с конкретными требованиями синхронизации на базовом аппаратном уровне. Он также отвечает за переключение кадров, управление питанием, регулировку цвета и т. д. Он может подключать несколько кодеров для реализации функции копирования экрана. .

4、Encoder

Кодер, выход преобразования, отвечает за управление питанием. Очевидно, что для выхода требуются разные преобразователи сигналов для преобразования пикселей памяти в сигналы, необходимые дисплею.

5、Connector

Разъем отвечает за доступ к аппаратным устройствам, таким как HDMI, VGA и т. д., и может получать EDID устройства, состояние подключения DPMS и т. д.

Вышеупомянутые компоненты, наконец, завершают полный процесс управления дисплеем DRM, как показано на следующем рисунке:

Вышеупомянутые компоненты CRTC, Planes, Encoder и Connector являются абстракциями аппаратного обеспечения. Даже если соответствующее им реальное оборудование отсутствует, их все равно необходимо реализовать в программном драйвере, иначе подсистема DRM не сможет работать нормально.

GEM(generic DRM memory-management)

GEM отвечает за управление памятью (например, видеопамятью), используемой DRM, и представляет собой программную абстракцию.

Возможности, предоставляемые инфраструктурой GEM, включают в себя:

  1. Распределение и освобождение памяти
  2. выполнение команды
  3. Управление при выполнении команд

2. Внедрение DRM платформы РК

Драйвер функции дисплея обычно реализуется производителем чипа Rockchip для завершения DRM-хоста. Код драйвера хоста обычно находится в каталоге driver/gpu/drm/xxx/, где xxx относится к производителю чипа.

В драйвере драйвер дисплея Rockchip использует структуру компонентов, драйвер дисплея является главным, а устройство под драйвером дисплея называется компонентом.

Узел портов в узле устройства display_subsystem — это связанный компонент, который фактически указывает на узлы vopl_out и vopb_out (VOP — это интерфейс для различных выходных изображений под этим узлом, который может выводить несколько видеосигналов одновременно). время.

rk3399.dtsi

Язык кода:javascript
копировать
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>;
  };
    ......
 };
};
Язык кода:javascript
копировать
 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

Язык кода:javascript
копировать
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);
}
Язык кода:javascript
копировать
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,
 },
};

3. Пример RK3399

У блогера установлен экран MIPI DSI, а конфигурация дерева устройств следующая:

Язык кода:javascript
копировать
 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.

Язык кода:javascript
копировать
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);
Язык кода:javascript
копировать
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: устаревший интерфейс и атомарный интерфейс. В настоящее время обычно используется атомарный интерфейс.

5. Постскриптум

Хотя функция DRM отвечает потребностям современных устройств отображения, все еще существует множество старых устройств и программного обеспечения, требующих поддержки кадрового буфера. Поэтому в рамках DRM некоторые коды используются для имитации устройств FB в рамках DRM.

В коде драйвера дисплея, предоставленном Rockchip, также есть код, связанный с моделированием устройств FB. См. файл driver/gpu/drm/rockchip/rockchip_drm_fb.c. Конечный результат заключается в том, что на устройстве появляется знакомая фигура /dev/fb0. каталог.

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose