嵌入式系统内核镜像相关(七)
文章目录
前言
分析上一篇没分析完的内容。
一、从这个设备树代码入手
剩下的内容就是:
&axi_ethernet_0 {
local-mac-address = [00 0a 35 00 00 00];
phy-handle = <&phy1>;
xlnx,has-mdio = <0x1>;
phy-mode = "rgmii";
mdio {
phy1: phy@1 {
device_type = "ethernet-phy";
reg = <1>;
};
};
};
&uart0 {
u-boot,dm-pre-reloc;
};
&uart1 {
u-boot,dm-pre-reloc;
};
&axi_dynclk_0 {
compatible = "digilent,axi-dynclk";
clocks = <&clkc 15>;
#clock-cells = <0>;
};
&v_tc_0 {
compatible = "xlnx,v-tc-5.01.a";
};
&amba_pl {
hdmi_encoder_0:hdmi_encoder {
compatible = "digilent,drm-encoder";
};
xilinx_drm {
compatible = "xlnx,drm";
xlnx,vtc = <&v_tc_0>;
xlnx,connector-type = "HDMIA";
xlnx,encoder-slave = <&hdmi_encoder_0>;
clocks = <&axi_dynclk_0>;
dglnt,edid-i2c = <&i2c0>;
planes {
xlnx,pixel-format = "rgb888";
plane0 {
dmas = <&axi_vdma_0 0>;
dma-names = "dma";
};
};
};
};
二、各个节点的分析
2.5 axi_ethernet_0节点
看了zynq-7000.dtsi内的ethernet设备树,如下。
gem0: ethernet@e000b000 {
compatible = "cdns,zynq-gem", "cdns,gem";
reg = <0xe000b000 0x1000>;
status = "disabled";
interrupts = <0 22 4>;
clocks = <&clkc 30>, <&clkc 30>, <&clkc 13>;
clock-names = "pclk", "hclk", "tx_clk";
#address-cells = <1>;
#size-cells = <0>;
};
gem1: ethernet@e000c000 {
compatible = "cdns,zynq-gem", "cdns,gem";
reg = <0xe000c000 0x1000>;
status = "disabled";
interrupts = <0 45 4>;
clocks = <&clkc 31>, <&clkc 31>, <&clkc 14>;
clock-names = "pclk", "hclk", "tx_clk";
#address-cells = <1>;
#size-cells = <0>;
};
可以说是和system-user.dtsi一点关系都没有!没办法只能根据关键词盲猜!bindings文档中以下4个是有概率相关的!
2.5.1 ethernet.txt文档内容
直接机翻,如下:
以下属性适用于以太网控制器:
注意事项:以下文档中所有的“phy*”属性都是以太网特定的。对于通用PHY的“phys”属性,请参阅Documentation/devicetree/bindings/phy/phy-bindings.txt。
- local-mac-address:6字节的数组,指定分配给网络设备的MAC地址;
- mac-address:6字节的数组,指定引导程序最后使用的MAC地址;应在引导程序分配给设备的MAC地址与“local-mac-address”属性不同的情况下使用;
- nvmem-cells:phandle,对MAC地址的nvmem节点的引用;
- nvmem-cell-names:字符串,如果使用nvmem,则应为“mac-address”;
- max-speed:数字,指定设备支持的最大速度,单位为Mbit/s;
- max-frame-size:数字,最大传输单元(IEEE定义的MTU),而不是最大帧大小(设备树规范中存在矛盾)。
- phy-mode:字符串,PHY接口的操作模式。这现在是一个事实上的标准属性;支持的值包括:
- “internal”
- “mii”
- “gmii”
- “sgmii”
- “qsgmii”
- “tbi”
- “rev-mii”
- “rmii”
- “rgmii”(当需要时,由MAC添加RX和TX延迟)
- “rgmii-id”(RGMII,PHY提供内部RX和TX延迟,MAC在这种情况下不应添加RX或TX延迟)
- “rgmii-rxid”(RGMII,PHY提供内部RX延迟,MAC在这种情况下不应添加RX延迟)
- “rgmii-txid”(RGMII,PHY提供内部TX延迟,MAC在这种情况下不应添加TX延迟)
- “rtbi”
- “smii”
- “xgmii”
- “trgmii”
- “2000base-x”
- “2500base-x”
- “rxaui”
- “xaui”
- “10gbase-kr”(10GBASE-KR,XFI,SFI)
- phy-connection-type:与“phy-mode”属性相同,但在设备树规范中有所描述;
- phy-handle:phandle,指定对表示PHY设备的节点的引用;此属性在设备树规范中有描述,因此更受推荐;
- phy:与“phy-handle”属性相同,不推荐用于新的绑定。
- phy-device:与“phy-handle”属性相同,不推荐用于新的绑定。
- rx-fifo-depth:控制器接收FIFO的大小,以字节为单位。这用于可以具有可配置接收FIFO大小的组件,并且对于确定某些配置设置(如流量控制阈值)很有用。
- tx-fifo-depth:控制器发送FIFO的大小,以字节为单位。这用于可以具有可配置FIFO大小的组件。
- managed:字符串,指定PHY管理类型。支持的值包括:“auto”和“in-band-status”。“auto”是默认值,如果未指定fixed-link,则使用MDIO进行管理。
以太网控制器的子节点通常是通过MDIO总线连接的各个PHY设备(有时MDIO总线控制器是分开的)。它们在与本目录相同的phy.txt文件中描述。
对于非MDIO PHY管理,请参阅fixed-link.txt。
2.5.2 mdio.txt文档内容
机翻如下:
通用MDIO总线属性。这些是适用于任何MDIO总线的通用属性。
可选属性:
- reset-gpios:一个GPIO,用于控制该MDIO总线上所有PHY的RESET线。
- reset-delay-us:RESET脉冲宽度,以微秒为单位。
预期会有一个子节点列表,每个总线上的设备一个。这些应遵循通用的phy.txt,或特定于设备的绑定文档。
“reset-delay-us”表示RESET信号脉冲宽度,以微秒为单位,并适用于所有PHY设备。因此,必须根据所有PHY的要求(所有每个PHY的RESET脉冲宽度的最大值)适当地确定它。
示例:
此示例展示了这些可选属性,以及TI Davinci MDIO驱动程序所需的其他属性。
davinci_mdio: ethernet@5c030000 {
compatible = "ti,davinci_mdio";
reg = <0x5c030000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>;
reset-delay-us = <2>;
ethphy0: ethernet-phy@1 {
reg = <1>;
};
ethphy1: ethernet-phy@3 {
reg = <3>;
};
};
2.5.3 phy.txt文档内容
机翻如下:
PHY节点的必需属性:
- interrupts:唯一中断的中断描述符。
- reg:PHY的ID号,通常是一个较小的整数。
可选属性:
-
compatible:兼容列表,可包含“ethernet-phy-ieee802.3-c22”或“ethernet-phy-ieee802.3-c45”,分别用于实现IEEE802.3第22条款或第45条款规范的PHY。如果未指定这两者中的任何一个,则默认假定为第22条款。
如果PHY报告的ID不正确(或根本未报告),则“compatible”列表中可以包含一个带有正确PHY ID的条目,形式为:“ethernet-phy-idAAAA.BBBB”,其中
AAAA - 16位PHY标识符1寄存器的值,以4位十六进制数字表示。这是芯片供应商OUI位3:18。
BBBB - 16位PHY标识符2寄存器的值,以4位十六进制数字表示。这是芯片供应商OUI位19:24,后跟10位供应商特定ID。兼容列表不应包含此处未列出的其他值。
-
max-speed:PHY支持的最大速度(10, 100, 1000等)。
-
broken-turn-around:如果设置,表示PHY设备在MDIO事务结束时未能正确释放低电平的转向线。
-
enet-phy-lane-swap:如果设置,表示PHY将交换TX/RX通道,以补偿电路板设计时通道被交换的情况。
-
enet-phy-lane-no-swap:如果设置,表示PHY将禁用TX/RX通道的交换。此属性允许PHY在例如由于PCB布局设计问题导致错误的引导配置后正常工作。
-
eee-broken-100tx:
-
eee-broken-1000t:
-
eee-broken-10gt:
-
eee-broken-1000kx:
-
eee-broken-10gkx4:
-
eee-broken-10gkr:
标记相应的节能以太网模式为故障,并请求以太网停止宣传该模式。 -
phy-is-integrated:如果设置,表示PHY与以太网MAC集成在同一物理封装中。如有需要,应配置多路复用器以确保使用集成PHY。缺少此属性表示应配置多路复用器以使用外部PHY。
-
reset-gpios:PHY复位信号的GPIO phandle和描述符。
-
reset-assert-us:复位断言后的延迟,以微秒为单位。如果缺少此属性,则将跳过延迟。
-
reset-deassert-us:复位去断言后的延迟,以微秒为单位。如果缺少此属性,则将跳过延迟。
示例:
ethernet-phy@0 {
compatible = "ethernet-phy-id0141.0e90", "ethernet-phy-ieee802.3-c22";
interrupt-parent = <&PIC>;
interrupts = <35 IRQ_TYPE_EDGE_RISING>;
reg = <0>;
reset-gpios = <&gpio1 4 GPIO_ACTIVE_LOW>;
reset-assert-us = <1000>;
reset-deassert-us = <2000>;
};
2.5.4 xilinx-phy.txt文档内容
机翻如下:
Xilinx PCS/PMA PHY绑定的必需属性:
- reg:PHY的ID号,通常是一个较小的整数。
可选属性:
- xlnx,phy-type:描述类型,1000BaseX(设置为0x5)或SGMII(设置为0x4)。
示例:
ethernet-phy@9 {
reg = <9>;
xlnx,phy-type = <0x5>;
};
2.5.5 ./drivers/net/ethernet/ibm/emac/rgmii.c——驱动
再次通过grep搜索has-mdio,最后通过排除法断定设备树的相关元素还得从这个驱动文件里面找,该驱动文件内容如下:
/*
* drivers/net/ethernet/ibm/emac/rgmii.c
*
* Driver for PowerPC 4xx on-chip ethernet controller, RGMII bridge support.
*
* Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
* <benh@kernel.crashing.org>
*
* Based on the arch/ppc version of the driver:
*
* Copyright (c) 2004, 2005 Zultys Technologies.
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
*
* Based on original work by
* Matt Porter <mporter@kernel.crashing.org>
* Copyright 2004 MontaVista Software, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/ethtool.h>
#include <linux/of_address.h>
#include <asm/io.h>
#include "emac.h"
#include "debug.h"
// XXX FIXME: Axon seems to support a subset of the RGMII, we
// thus need to take that into account and possibly change some
// of the bit settings below that don't seem to quite match the
// AXON spec
/* RGMIIx_FER */
#define RGMII_FER_MASK(idx) (0x7 << ((idx) * 4))
#define RGMII_FER_RTBI(idx) (0x4 << ((idx) * 4))
#define RGMII_FER_RGMII(idx) (0x5 << ((idx) * 4))
#define RGMII_FER_TBI(idx) (0x6 << ((idx) * 4))
#define RGMII_FER_GMII(idx) (0x7 << ((idx) * 4))
#define RGMII_FER_MII(idx) RGMII_FER_GMII(idx)
/* RGMIIx_SSR */
#define RGMII_SSR_MASK(idx) (0x7 << ((idx) * 8))
#define RGMII_SSR_10(idx) (0x1 << ((idx) * 8))
#define RGMII_SSR_100(idx) (0x2 << ((idx) * 8))
#define RGMII_SSR_1000(idx) (0x4 << ((idx) * 8))
/* RGMII bridge supports only GMII/TBI and RGMII/RTBI PHYs */
static inline int rgmii_valid_mode(int phy_mode)
{
return phy_interface_mode_is_rgmii(phy_mode) ||
phy_mode == PHY_INTERFACE_MODE_GMII ||
phy_mode == PHY_INTERFACE_MODE_MII ||
phy_mode == PHY_INTERFACE_MODE_TBI ||
phy_mode == PHY_INTERFACE_MODE_RTBI;
}
static inline u32 rgmii_mode_mask(int mode, int input)
{
switch (mode) {
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
return RGMII_FER_RGMII(input);
case PHY_INTERFACE_MODE_TBI:
return RGMII_FER_TBI(input);
case PHY_INTERFACE_MODE_GMII:
return RGMII_FER_GMII(input);
case PHY_INTERFACE_MODE_MII:
return RGMII_FER_MII(input);
case PHY_INTERFACE_MODE_RTBI:
return RGMII_FER_RTBI(input);
default:
BUG();
}
}
int rgmii_attach(struct platform_device *ofdev, int input, int mode)
{
struct rgmii_instance *dev = platform_get_drvdata(ofdev);
struct rgmii_regs __iomem *p = dev->base;
RGMII_DBG(dev, "attach(%d)" NL, input);
/* Check if we need to attach to a RGMII */
if (input < 0 || !rgmii_valid_mode(mode)) {
printk(KERN_ERR "%pOF: unsupported settings !\n",
ofdev->dev.of_node);
return -ENODEV;
}
mutex_lock(&dev->lock);
/* Enable this input */
out_be32(&p->fer, in_be32(&p->fer) | rgmii_mode_mask(mode, input));
printk(KERN_NOTICE "%pOF: input %d in %s mode\n",
ofdev->dev.of_node, input, phy_modes(mode));
++dev->users;
mutex_unlock(&dev->lock);
return 0;
}
void rgmii_set_speed(struct platform_device *ofdev, int input, int speed)
{
struct rgmii_instance *dev = platform_get_drvdata(ofdev);
struct rgmii_regs __iomem *p = dev->base;
u32 ssr;
mutex_lock(&dev->lock);
ssr = in_be32(&p->ssr) & ~RGMII_SSR_MASK(input);
RGMII_DBG(dev, "speed(%d, %d)" NL, input, speed);
if (speed == SPEED_1000)
ssr |= RGMII_SSR_1000(input);
else if (speed == SPEED_100)
ssr |= RGMII_SSR_100(input);
else if (speed == SPEED_10)
ssr |= RGMII_SSR_10(input);
out_be32(&p->ssr, ssr);
mutex_unlock(&dev->lock);
}
void rgmii_get_mdio(struct platform_device *ofdev, int input)
{
struct rgmii_instance *dev = platform_get_drvdata(ofdev);
struct rgmii_regs __iomem *p = dev->base;
u32 fer;
RGMII_DBG2(dev, "get_mdio(%d)" NL, input);
if (!(dev->flags & EMAC_RGMII_FLAG_HAS_MDIO))
return;
mutex_lock(&dev->lock);
fer = in_be32(&p->fer);
fer |= 0x00080000u >> input;
out_be32(&p->fer, fer);
(void)in_be32(&p->fer);
DBG2(dev, " fer = 0x%08x\n", fer);
}
void rgmii_put_mdio(struct platform_device *ofdev, int input)
{
struct rgmii_instance *dev = platform_get_drvdata(ofdev);
struct rgmii_regs __iomem *p = dev->base;
u32 fer;
RGMII_DBG2(dev, "put_mdio(%d)" NL, input);
if (!(dev->flags & EMAC_RGMII_FLAG_HAS_MDIO))
return;
fer = in_be32(&p->fer);
fer &= ~(0x00080000u >> input);
out_be32(&p->fer, fer);
(void)in_be32(&p->fer);
DBG2(dev, " fer = 0x%08x\n", fer);
mutex_unlock(&dev->lock);
}
void rgmii_detach(struct platform_device *ofdev, int input)
{
struct rgmii_instance *dev = platform_get_drvdata(ofdev);
struct rgmii_regs __iomem *p;
BUG_ON(!dev || dev->users == 0);
p = dev->base;
mutex_lock(&dev->lock);
RGMII_DBG(dev, "detach(%d)" NL, input);
/* Disable this input */
out_be32(&p->fer, in_be32(&p->fer) & ~RGMII_FER_MASK(input));
--dev->users;
mutex_unlock(&dev->lock);
}
int rgmii_get_regs_len(struct platform_device *ofdev)
{
return sizeof(struct emac_ethtool_regs_subhdr) +
sizeof(struct rgmii_regs);
}
void *rgmii_dump_regs(struct platform_device *ofdev, void *buf)
{
struct rgmii_instance *dev = platform_get_drvdata(ofdev);
struct emac_ethtool_regs_subhdr *hdr = buf;
struct rgmii_regs *regs = (struct rgmii_regs *)(hdr + 1);
hdr->version = 0;
hdr->index = 0; /* for now, are there chips with more than one
* rgmii ? if yes, then we'll add a cell_index
* like we do for emac
*/
memcpy_fromio(regs, dev->base, sizeof(struct rgmii_regs));
return regs + 1;
}
static int rgmii_probe(struct platform_device *ofdev)
{
struct device_node *np = ofdev->dev.of_node;
struct rgmii_instance *dev;
struct resource regs;
int rc;
rc = -ENOMEM;
dev = kzalloc(sizeof(struct rgmii_instance), GFP_KERNEL);
if (dev == NULL)
goto err_gone;
mutex_init(&dev->lock);
dev->ofdev = ofdev;
rc = -ENXIO;
if (of_address_to_resource(np, 0, ®s)) {
printk(KERN_ERR "%pOF: Can't get registers address\n", np);
goto err_free;
}
rc = -ENOMEM;
dev->base = (struct rgmii_regs __iomem *)ioremap(regs.start,
sizeof(struct rgmii_regs));
if (dev->base == NULL) {
printk(KERN_ERR "%pOF: Can't map device registers!\n", np);
goto err_free;
}
/* Check for RGMII flags */
if (of_get_property(ofdev->dev.of_node, "has-mdio", NULL))
dev->flags |= EMAC_RGMII_FLAG_HAS_MDIO;
/* CAB lacks the right properties, fix this up */
if (of_device_is_compatible(ofdev->dev.of_node, "ibm,rgmii-axon"))
dev->flags |= EMAC_RGMII_FLAG_HAS_MDIO;
DBG2(dev, " Boot FER = 0x%08x, SSR = 0x%08x\n",
in_be32(&dev->base->fer), in_be32(&dev->base->ssr));
/* Disable all inputs by default */
out_be32(&dev->base->fer, 0);
printk(KERN_INFO
"RGMII %pOF initialized with%s MDIO support\n",
ofdev->dev.of_node,
(dev->flags & EMAC_RGMII_FLAG_HAS_MDIO) ? "" : "out");
wmb();
platform_set_drvdata(ofdev, dev);
return 0;
err_free:
kfree(dev);
err_gone:
return rc;
}
static int rgmii_remove(struct platform_device *ofdev)
{
struct rgmii_instance *dev = platform_get_drvdata(ofdev);
WARN_ON(dev->users != 0);
iounmap(dev->base);
kfree(dev);
return 0;
}
static const struct of_device_id rgmii_match[] =
{
{
.compatible = "ibm,rgmii",
},
{
.type = "emac-rgmii",
},
{},
};
static struct platform_driver rgmii_driver = {
.driver = {
.name = "emac-rgmii",
.of_match_table = rgmii_match,
},
.probe = rgmii_probe,
.remove = rgmii_remove,
};
int __init rgmii_init(void)
{
return platform_driver_register(&rgmii_driver);
}
void rgmii_exit(void)
{
platform_driver_unregister(&rgmii_driver);
}
从上述代码可以推敲has-mdio用于检查rgmii,刚好设备树代码中也有这俩要素:
&axi_ethernet_0 {
local-mac-address = [00 0a 35 00 00 00];
phy-handle = <&phy1>;
xlnx,has-mdio = <0x1>;
phy-mode = "rgmii";
mdio {
phy1: phy@1 {
device_type = "ethernet-phy";
reg = <1>;
};
};
};
解释如下:
local-mac-address = [00 0a 35 00 00 00];是6字节的数组,指定分配给网络设备的MAC地址。
phy-handle也就是phandle,指定对表示PHY设备的节点的引用。
has-mdio用于检查是否是RGMII的flag。
phy-mode = "rgmii";字符串,PHY接口的操作模式。
2.5.6 mdio是啥?
mdio出现了那么多次,究竟是啥?借助kimichat进行解释!
MDIO(Management Data Input/Output)是一种用于以太网PHY(物理层)设备管理的串行总线接口。它主要用于在以太网控制器和PHY设备之间进行通信,以便配置和监控PHY设备的状态。
2.5.6.1 mdio的主要特点
- 低引脚数:MDIO总线通常只需要少量引脚,通常包括数据线(MDIO)和时钟线(MDC)。
- 通用性:MDIO总线广泛应用于各种以太网设备,包括交换机、路由器和网络接口卡。
- 标准协议:MDIO总线遵循IEEE 802.3标准,特别是第22条款(Clause 22)和第45条款(Clause 45)。
- 配置和监控:通过MDIO总线,可以读取和写入PHY设备的寄存器,从而实现对PHY的配置和状态监控。
2.5.6.2 mdio的信号定义
- MDIO:数据线,用于双向数据传输。
- MDC:时钟线,用于同步MDIO数据传输。
2.6 uart0和uart1节点
uart的这两个引用后赋值,说句实话,真的很抽象:
&uart0 {
u-boot,dm-pre-reloc;
};
&uart1 {
u-boot,dm-pre-reloc;
};
通过grep大法也没能发现可用于解释的文档!无奈之下,只能找各个网站翻资料。还好,翻出点东西来了!
2.6.1 怎么解释u-boot,dm-pre-reloc?
翻到NXP Semiconductor针对i.MX8Mmini 板级开发包4.14.78_ga的Bootloader定制手册。看看这里的u-boot,dm-pre-reloc是怎么解释的!

简单来说,就是如果某个设备需要在系统启动之前就初始化(例如,某些外设或时钟),则可以在该设备的设备树节点中添加u-boot,dm-pre-reloc属性。这样,即使在系统启动过程中,设备管理器也会优先初始化这些设备。
2.6.2 看看zynq-7000.dtsi中的uart代码?
在zynq-7000.dtsi中如下:
uart0: serial@e0000000 {
compatible = "xlnx,xuartps", "cdns,uart-r1p8";
status = "disabled";
clocks = <&clkc 23>, <&clkc 40>;
clock-names = "uart_clk", "pclk";
reg = <0xE0000000 0x1000>;
interrupts = <0 27 4>;
};
uart1: serial@e0001000 {
compatible = "xlnx,xuartps", "cdns,uart-r1p8";
status = "disabled";
clocks = <&clkc 24>, <&clkc 41>;
clock-names = "uart_clk", "pclk";
reg = <0xE0001000 0x1000>;
interrupts = <0 50 4>;
};
常规的不解释了,就解释稀奇古怪的内容:
compatible = "xlnx,xuartps", "cdns,uart-r1p8";表示该设备兼容xlnx,xuartps和cdns,uart-r1p8驱动程序,即Xilinx的XUARTPS和Cadence的UART R1P8。
status = "disabled";表示该设备在启动时默认是禁用的,不会被内核自动初始化。如果需要启用该设备,可以在设备树中将其改为"okay",或者通过运行时配置启用。
uart应该使用了2个时钟,分别是外设时钟pclk和uart时钟。
interrupts = <0 27 4>;中,0表示中断控制器的索引,27表示中断号,4表示中断类型(通常是中断优先级或触发类型)。
2.7 axi_dynclk_0节点
设备树代码如下:
&axi_dynclk_0 {
compatible = "digilent,axi-dynclk";
clocks = <&clkc 15>;
#clock-cells = <0>;
};
很不幸的是grep搜不到,上网搜了搜出现这个名词的网站还有这个。
先不扯这个,观察一下这个phandle名称,怎么来的?回到vivado,打开block design。结果不言自明!!!!v_tc_0同样如此!
关于axi_dynclk_0节点的设备树代码还可见于该链接。
感觉是个固定的范式!算了,还是记住吧!

2.8 v_tc_0节点
设备树代码如下:
&v_tc_0 {
compatible = "xlnx,v-tc-5.01.a";
};
同前,不过grep发现了点东西!
dention@ubuntu:~/petalinux_proj/test_peta/proj_peta$ grep -rl "v-tc-5.01.a" ./
./build/tmp/work-shared/plnx-zynq7/kernel-source/Documentation/devicetree/bindings/drm/xilinx/vtc.txt
./build/tmp/work-shared/plnx-zynq7/kernel-source/drivers/gpu/drm/xilinx/xilinx_vtc.c
看个vtc.txt即可,直接机翻,如下:
Xilinx 视频时序控制器(VTC)的设备树绑定的必需属性:
- compatible:值应为“xlnx,v-tc-5.01.a”
- reg:VTC IP 的基地址和大小
- interrupts:中断号
- interrupts-parent:中断控制器的 phandle
示例:
v_tc_0: v-tc@40010000 {
compatible = "xlnx,v-tc-5.01.a";
interrupt-parent = <&intc>;
interrupts = <0 54 4>;
reg = <0x40010000 0x10000>;
};
2.9 amba_pl节点
该节点的设备树信息如下:
&amba_pl {
hdmi_encoder_0:hdmi_encoder {
compatible = "digilent,drm-encoder";
};
xilinx_drm {
compatible = "xlnx,drm";
xlnx,vtc = <&v_tc_0>;
xlnx,connector-type = "HDMIA";
xlnx,encoder-slave = <&hdmi_encoder_0>;
clocks = <&axi_dynclk_0>;
dglnt,edid-i2c = <&i2c0>;
planes {
xlnx,pixel-format = "rgb888";
plane0 {
dmas = <&axi_vdma_0 0>;
dma-names = "dma";
};
};
};
};
翻了网上的技术博客,没有一篇解释这玩意儿的!
grep了xlnx,drm,发现了踪迹:
dention@ubuntu:~/petalinux_proj/test_peta/proj_peta$ grep -rl "xlnx,drm" ./
./build/tmp/work-shared/plnx-zynq7/kernel-source/Documentation/devicetree/bindings/drm/xilinx/xilinx_drm.txt
./build/tmp/work-shared/plnx-zynq7/kernel-source/Documentation/devicetree/bindings/dma/xilinx/xilinx_dpdma.txt
./build/tmp/work-shared/plnx-zynq7/kernel-source/drivers/gpu/drm/xilinx/xilinx_drm_drv.c
大概率就是xilinx_drm.txt文档和xilinx_drm_drv.c驱动文件。
2.9.1 xilinx_drm.txt文档内容
机翻,破案!这写法就是套路!!!
Xilinx DRM支持在FPGA上使用Xilinx软IP以及在Xilinx板卡上的IP构建的显示管线。
以下是示例硬件管线的描述
(括号()内的IP是可选的。方括号[]内的IP不需要驱动程序)。
vdma-[remap]-(rgb2yuv)-(cresample)-(osd)-(rgb2yuv)-(cresample)-[axi2vid]-adv7511
(vdma-[remap]-(rgb2yuv)-(cresample)-|) |
si570 -> vtc
必需属性:
- compatible:值应为“xlnx,drm”。
- xlnx,osd:如果硬件设计中使用了屏幕显示IP,则为屏幕显示IP的phandle。
- xlnx,rgb2yuv:如果硬件设计中使用了rgb2ycrcb IP,则为rgb2ycrcb IP的phandle。
- xlnx,cresample:如果硬件设计中使用了色度重采样IP,则为色度重采样IP的phandle。
- xlnx,vtc:视频时序控制器IP的phandle。
- xlnx,encoder-slave:编码器从机的phandle。
- clocks:像素时钟的phandle。
- planes:每个平面资源的子节点。
- xlnx,connector-type:连接器类型。值应为“HDMIA”或“DisplayPort”,具体取决于要使用的连接器类型。
可选属性:
- xlnx,dp-sub:对于ZynqMP,DisplayPort子系统的phandle。
- xlnx,sdi:如果管线中有SDI IP核,则为SDI节点的phandle。
- ports:可以使用设备图绑定来定义连接性。设备树绑定定义在
Documentation/devicetree/bindings/graph.txt中。
平面必需属性:
-
xlnx,pixel-format:平面管理器的格式。值应为以下格式字符串之一。
“yuv420”
“uvy422”
“vuy422”
“yuv422”
“yv4u22”
“yuv444”
“nv12”
“nv21”
“nv16”
“nv61”
“abgr1555”
“argb1555”
“rgba4444”
“bgra4444”
“bgr565”
“rgb565”
“bgr888”
“rgb888”
“xbgr8888”
“xrgb8888”
“abgr8888”
“argb8888”
“bgra8888”
“rgba8888”
平面必需属性:
- dmas:DMA描述符的phandle列表。
- dma-names:DMA的标识符字符串。
- xlnx,rgb2yuv:如果平面使用了rgb2ycrcb IP,则为rgb2ycrcb IP的phandle。
- xlnx,cresample:如果平面使用了色度重采样IP,则为色度重采样IP的phandle。
管线可以按照以下示例或更多的方式进行配置。
-
示例1:
vdma - [remap] - rgb2yuv - cresample - [axi2vid] - adv7511
|
si570 - vtcxilinx_drm { compatible = "xlnx,drm"; xlnx,vtc = <&v_tc_0>; xlnx,encoder-slave = <&adv7511>; xlnx,connector-type = "HDMIA"; clocks = <&si570>; planes { xlnx,pixel-format = "yuv422"; plane0 { dma = <&axi_vdma_0>; dma-names = "axi_vdma_0"; xlnx,rgb2yuv = <&v_rgb2ycrcb_0>; xlnx,cresample = <&v_cresample_0>; }; }; }; -
示例2:
vdma - [remap] --------- osd - cresample - [axi2vid] - adv7511
vdma - [remap] - rgb2yuv -| |
si570 - vtcxilinx_drm { compatible = "xlnx,drm"; xlnx,osd = <&v_osd_0>; xlnx,cresample = <&v_cresample_0>; xlnx,vtc = <&v_tc_0>; xlnx,encoder-slave = <&adv7511>; xlnx,connector-type = "DisplayPort"; clocks = <&si570>; planes { xlnx,pixel-format = "yuv422"; plane0 { dma = <&axi_vdma_0>; dma-names = "axi_vdma_0"; }; plane1 { dma = <&axi_vdma_1>; dma-names = "axi_vdma_1"; xlnx,rgb2yuv = <&v_rgb2ycrcb_0>; }; }; }; -
示例3:
dpdma - ZynqMP DP子系统 - DPxilinx_drm { compatible = "xlnx,drm"; xlnx,encoder-slave = <&xlnx_dp>; clocks = <&si570 0>; xlnx,connector-type = "DisplayPort"; xlnx,dp-sub = <&xlnx_dp_sub>; planes { xlnx,pixel-format = "rgb565"; plane0 { dmas = <&xlnx_dpdma 3>; dma-names = "xlnx_dpdma"; }; plane1 { dmas = <&xlnx_dpdma 0>, <&xlnx_dpdma 1>, <&xlnx_dpdma 2>; dma-names = "xlnx_dpdma_0", "xlnx_dpdma_1", "xlnx_dpdma_2"; }; }; }; -
示例4:
vdma - Xilinx MIPI DSIxilinx_drm: xilinx_drm { compatible = "xlnx,drm"; ports { #address-cells = <1>; #size-cells = <0>; port@0 { reg = <0>; drm_port: endpoint { remote-endpoint = <&mipi_port>; }; }; }; planes { xlnx,pixel-format = "rgb888"; plane0 { dmas = <&axi_vdma_0 0>; dma-names = "axi_vdma_0"; }; }; };
尽管发现这是套路写法,但还是没法儿完美对应这段的设备树代码。接着挖一挖驱动文件!!!
2.9.2 xilinx_drm_drv.c驱动文件
驱动文件内容如下:
/*
* Xilinx DRM KMS support for Xilinx
*
* Copyright (C) 2013 Xilinx, Inc.
*
* Author: Hyun Woo Kwon <hyunk@xilinx.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <linux/component.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include "xilinx_drm_connector.h"
#include "xilinx_drm_crtc.h"
#include "xilinx_drm_drv.h"
#include "xilinx_drm_encoder.h"
#include "xilinx_drm_fb.h"
#include "xilinx_drm_gem.h"
#define DRIVER_NAME "xilinx_drm"
#define DRIVER_DESC "Xilinx DRM KMS support for Xilinx"
#define DRIVER_DATE "20130509"
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
static uint xilinx_drm_fbdev_vres = 2;
module_param_named(fbdev_vres, xilinx_drm_fbdev_vres, uint, 0444);
MODULE_PARM_DESC(fbdev_vres,
"fbdev virtual resolution multiplier for fb (default: 2)");
/*
* TODO: The possible pipeline configurations are numerous with Xilinx soft IPs.
* It's not too bad for now, but the more proper way(Common Display Framework,
* or some internal abstraction) should be considered, when it reaches a point
* that such thing is required.
*/
struct xilinx_drm_private {
struct drm_device *drm;
struct drm_crtc *crtc;
struct drm_fb_helper *fb;
struct platform_device *pdev;
bool is_master;
};
/**
* struct xilinx_video_format_desc - Xilinx Video IP video format description
* @name: Xilinx video format name
* @depth: color depth
* @bpp: bits per pixel
* @xilinx_format: xilinx format code
* @drm_format: drm format code
*/
struct xilinx_video_format_desc {
const char *name;
unsigned int depth;
unsigned int bpp;
unsigned int xilinx_format;
u32 drm_format;
};
static const struct xilinx_video_format_desc xilinx_video_formats[] = {
{ "yuv420", 16, 16, XILINX_VIDEO_FORMAT_YUV420, DRM_FORMAT_YUV420 },
{ "uvy422", 16, 16, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_UYVY },
{ "vuy422", 16, 16, XILINX_VIDEO_FORMAT_YUV422, DRM_FORMAT_VYUY },
{ "yuv422", 16, 16, XILINX_VIDEO_FORMAT_YUV422, DRM_FORMAT_YUYV },
{ "yvu422", 16, 16, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_YVYU },
{ "yuv444", 24, 24, XILINX_VIDEO_FORMAT_YUV444, DRM_FORMAT_YUV444 },
{ "nv12", 16, 16, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_NV12 },
{ "nv21", 16, 16, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_NV21 },
{ "nv16", 16, 16, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_NV16 },
{ "nv61", 16, 16, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_NV61 },
{ "abgr1555", 16, 16, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_ABGR1555 },
{ "argb1555", 16, 16, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_ARGB1555 },
{ "rgba4444", 16, 16, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_RGBA4444 },
{ "bgra4444", 16, 16, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_BGRA4444 },
{ "bgr565", 16, 16, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_BGR565 },
{ "rgb565", 16, 16, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_RGB565 },
{ "bgr888", 24, 24, XILINX_VIDEO_FORMAT_RGB, DRM_FORMAT_BGR888 },
{ "rgb888", 24, 24, XILINX_VIDEO_FORMAT_RGB, DRM_FORMAT_RGB888 },
{ "xbgr8888", 24, 32, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_XBGR8888 },
{ "xrgb8888", 24, 32, XILINX_VIDEO_FORMAT_XRGB, DRM_FORMAT_XRGB8888 },
{ "abgr8888", 32, 32, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_ABGR8888 },
{ "argb8888", 32, 32, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_ARGB8888 },
{ "bgra8888", 32, 32, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_BGRA8888 },
{ "rgba8888", 32, 32, XILINX_VIDEO_FORMAT_NONE, DRM_FORMAT_RGBA8888 },
};
/**
* xilinx_drm_check_format - Check if the given format is supported
* @drm: DRM device
* @fourcc: format fourcc
*
* Check if the given format @fourcc is supported by the current pipeline
*
* Return: true if the format is supported, or false
*/
bool xilinx_drm_check_format(struct drm_device *drm, u32 fourcc)
{
struct xilinx_drm_private *private = drm->dev_private;
return xilinx_drm_crtc_check_format(private->crtc, fourcc);
}
/**
* xilinx_drm_get_format - Get the current device format
* @drm: DRM device
*
* Get the current format of pipeline
*
* Return: the corresponding DRM_FORMAT_XXX
*/
u32 xilinx_drm_get_format(struct drm_device *drm)
{
struct xilinx_drm_private *private = drm->dev_private;
return xilinx_drm_crtc_get_format(private->crtc);
}
/**
* xilinx_drm_get_align - Get the alignment value for pitch
* @drm: DRM object
*
* Get the alignment value for pitch from the plane
*
* Return: The alignment value if successful, or the error code.
*/
unsigned int xilinx_drm_get_align(struct drm_device *drm)
{
struct xilinx_drm_private *private = drm->dev_private;
return xilinx_drm_crtc_get_align(private->crtc);
}
/* poll changed handler */
static void xilinx_drm_output_poll_changed(struct drm_device *drm)
{
struct xilinx_drm_private *private = drm->dev_private;
xilinx_drm_fb_hotplug_event(private->fb);
}
static const struct drm_mode_config_funcs xilinx_drm_mode_config_funcs = {
.fb_create = xilinx_drm_fb_create,
.output_poll_changed = xilinx_drm_output_poll_changed,
};
/* enable vblank */
static int xilinx_drm_enable_vblank(struct drm_device *drm, unsigned int crtc)
{
struct xilinx_drm_private *private = drm->dev_private;
xilinx_drm_crtc_enable_vblank(private->crtc);
return 0;
}
/* disable vblank */
static void xilinx_drm_disable_vblank(struct drm_device *drm, unsigned int crtc)
{
struct xilinx_drm_private *private = drm->dev_private;
xilinx_drm_crtc_disable_vblank(private->crtc);
}
/* initialize mode config */
static void xilinx_drm_mode_config_init(struct drm_device *drm)
{
struct xilinx_drm_private *private = drm->dev_private;
drm->mode_config.min_width = 0;
drm->mode_config.min_height = 0;
drm->mode_config.max_width =
xilinx_drm_crtc_get_max_width(private->crtc);
drm->mode_config.max_height = 4096;
drm->mode_config.funcs = &xilinx_drm_mode_config_funcs;
}
/* convert xilinx format to drm format by code */
int xilinx_drm_format_by_code(unsigned int xilinx_format, u32 *drm_format)
{
const struct xilinx_video_format_desc *format;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(xilinx_video_formats); i++) {
format = &xilinx_video_formats[i];
if (format->xilinx_format == xilinx_format) {
*drm_format = format->drm_format;
return 0;
}
}
DRM_ERROR("Unknown Xilinx video format: %d\n", xilinx_format);
return -EINVAL;
}
/* convert xilinx format to drm format by name */
int xilinx_drm_format_by_name(const char *name, u32 *drm_format)
{
const struct xilinx_video_format_desc *format;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(xilinx_video_formats); i++) {
format = &xilinx_video_formats[i];
if (strcmp(format->name, name) == 0) {
*drm_format = format->drm_format;
return 0;
}
}
DRM_ERROR("Unknown Xilinx video format: %s\n", name);
return -EINVAL;
}
/* get bpp of given format */
unsigned int xilinx_drm_format_bpp(u32 drm_format)
{
const struct xilinx_video_format_desc *format;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(xilinx_video_formats); i++) {
format = &xilinx_video_formats[i];
if (format->drm_format == drm_format)
return format->bpp;
}
return 0;
}
/* get color depth of given format */
unsigned int xilinx_drm_format_depth(u32 drm_format)
{
const struct xilinx_video_format_desc *format;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(xilinx_video_formats); i++) {
format = &xilinx_video_formats[i];
if (format->drm_format == drm_format)
return format->depth;
}
return 0;
}
static int xilinx_drm_bind(struct device *dev)
{
struct xilinx_drm_private *private = dev_get_drvdata(dev);
struct drm_device *drm = private->drm;
return component_bind_all(dev, drm);
}
static void xilinx_drm_unbind(struct device *dev)
{
dev_set_drvdata(dev, NULL);
}
static const struct component_master_ops xilinx_drm_ops = {
.bind = xilinx_drm_bind,
.unbind = xilinx_drm_unbind,
};
static int compare_of(struct device *dev, void *data)
{
struct device_node *np = data;
return dev->of_node == np;
}
static int xilinx_drm_open(struct drm_device *dev, struct drm_file *file)
{
struct xilinx_drm_private *private = dev->dev_private;
/* This is a hack way to allow the root user to run as a master */
if (!(drm_is_primary_client(file) && !dev->master) &&
!file->is_master && capable(CAP_SYS_ADMIN)) {
file->is_master = 1;
private->is_master = true;
}
return 0;
}
static int xilinx_drm_release(struct inode *inode, struct file *filp)
{
struct drm_file *file = filp->private_data;
struct drm_minor *minor = file->minor;
struct drm_device *drm = minor->dev;
struct xilinx_drm_private *private = drm->dev_private;
if (private->is_master) {
private->is_master = false;
file->is_master = 0;
}
return drm_release(inode, filp);
}
/* restore the default mode when xilinx drm is released */
static void xilinx_drm_lastclose(struct drm_device *drm)
{
struct xilinx_drm_private *private = drm->dev_private;
xilinx_drm_crtc_restore(private->crtc);
xilinx_drm_fb_restore_mode(private->fb);
}
static const struct file_operations xilinx_drm_fops = {
.owner = THIS_MODULE,
.open = drm_open,
.release = xilinx_drm_release,
.unlocked_ioctl = drm_ioctl,
.mmap = drm_gem_cma_mmap,
.poll = drm_poll,
.read = drm_read,
#ifdef CONFIG_COMPAT
.compat_ioctl = drm_compat_ioctl,
#endif
.llseek = noop_llseek,
};
static struct drm_driver xilinx_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM |
DRIVER_PRIME | DRIVER_LEGACY,
.open = xilinx_drm_open,
.lastclose = xilinx_drm_lastclose,
.enable_vblank = xilinx_drm_enable_vblank,
.disable_vblank = xilinx_drm_disable_vblank,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_export = drm_gem_prime_export,
.gem_prime_import = drm_gem_prime_import,
.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
.gem_prime_vmap = drm_gem_cma_prime_vmap,
.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
.gem_prime_mmap = drm_gem_cma_prime_mmap,
.gem_free_object = drm_gem_cma_free_object,
.gem_vm_ops = &drm_gem_cma_vm_ops,
.dumb_create = xilinx_drm_gem_cma_dumb_create,
.fops = &xilinx_drm_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
};
#if defined(CONFIG_PM_SLEEP)
/* suspend xilinx drm */
static int xilinx_drm_pm_suspend(struct device *dev)
{
struct xilinx_drm_private *private = dev_get_drvdata(dev);
struct drm_device *drm = private->drm;
struct drm_connector *connector;
drm_kms_helper_poll_disable(drm);
drm_modeset_lock_all(drm);
list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
int old_dpms = connector->dpms;
if (connector->funcs->dpms)
connector->funcs->dpms(connector,
DRM_MODE_DPMS_SUSPEND);
connector->dpms = old_dpms;
}
drm_modeset_unlock_all(drm);
return 0;
}
/* resume xilinx drm */
static int xilinx_drm_pm_resume(struct device *dev)
{
struct xilinx_drm_private *private = dev_get_drvdata(dev);
struct drm_device *drm = private->drm;
struct drm_connector *connector;
drm_modeset_lock_all(drm);
list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
if (connector->funcs->dpms) {
int dpms = connector->dpms;
connector->dpms = DRM_MODE_DPMS_OFF;
connector->funcs->dpms(connector, dpms);
}
}
drm_modeset_unlock_all(drm);
drm_helper_resume_force_mode(drm);
drm_modeset_lock_all(drm);
drm_kms_helper_poll_enable(drm);
drm_modeset_unlock_all(drm);
return 0;
}
#endif
static const struct dev_pm_ops xilinx_drm_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(xilinx_drm_pm_suspend, xilinx_drm_pm_resume)
};
/* init xilinx drm platform */
static int xilinx_drm_platform_probe(struct platform_device *pdev)
{
struct xilinx_drm_private *private;
struct drm_device *drm;
struct drm_encoder *encoder;
struct drm_connector *connector;
const struct drm_format_info *info;
struct device_node *encoder_node, *ep = NULL, *remote;
struct component_match *match = NULL;
unsigned int align, i = 0;
int ret;
u32 format;
drm = drm_dev_alloc(&xilinx_drm_driver, &pdev->dev);
if (IS_ERR(drm))
return PTR_ERR(drm);
private = devm_kzalloc(drm->dev, sizeof(*private), GFP_KERNEL);
if (!private) {
ret = -ENOMEM;
goto err_drm;
}
drm_mode_config_init(drm);
/* create a xilinx crtc */
private->crtc = xilinx_drm_crtc_create(drm);
if (IS_ERR(private->crtc)) {
DRM_DEBUG_DRIVER("failed to create xilinx crtc\n");
ret = PTR_ERR(private->crtc);
goto err_config;
}
while ((encoder_node = of_parse_phandle(drm->dev->of_node,
"xlnx,encoder-slave", i))) {
encoder = xilinx_drm_encoder_create(drm, encoder_node);
of_node_put(encoder_node);
if (IS_ERR(encoder)) {
DRM_DEBUG_DRIVER("failed to create xilinx encoder\n");
ret = PTR_ERR(encoder);
goto err_config;
}
connector = xilinx_drm_connector_create(drm, encoder, i);
if (IS_ERR(connector)) {
DRM_DEBUG_DRIVER("failed to create xilinx connector\n");
ret = PTR_ERR(connector);
goto err_config;
}
i++;
}
while (1) {
ep = of_graph_get_next_endpoint(drm->dev->of_node, ep);
if (!ep)
break;
of_node_put(ep);
remote = of_graph_get_remote_port_parent(ep);
if (!remote || !of_device_is_available(remote)) {
of_node_put(remote);
continue;
}
component_match_add(drm->dev, &match, compare_of, remote);
of_node_put(remote);
i++;
}
if (i == 0) {
DRM_ERROR("failed to get an encoder slave node\n");
return -ENODEV;
}
ret = drm_vblank_init(drm, 1);
if (ret) {
dev_err(&pdev->dev, "failed to initialize vblank\n");
goto err_master;
}
/* enable irq to enable vblank feature */
drm->irq_enabled = 1;
drm->dev_private = private;
private->drm = drm;
xilinx_drm_mode_config_init(drm);
format = xilinx_drm_crtc_get_format(private->crtc);
info = drm_format_info(format);
if (info && info->depth && info->cpp[0]) {
align = xilinx_drm_crtc_get_align(private->crtc);
private->fb = xilinx_drm_fb_init(drm, info->cpp[0] * 8, 1,
align, xilinx_drm_fbdev_vres);
if (IS_ERR(private->fb)) {
DRM_ERROR("failed to initialize drm fb\n");
private->fb = NULL;
}
} else {
dev_info(&pdev->dev, "fbdev is not initialized\n");
}
drm_kms_helper_poll_init(drm);
drm_helper_disable_unused_functions(drm);
platform_set_drvdata(pdev, private);
if (match) {
ret = component_master_add_with_match(drm->dev,
&xilinx_drm_ops, match);
if (ret)
goto err_master;
}
ret = dma_set_coherent_mask(&pdev->dev,
DMA_BIT_MASK(sizeof(dma_addr_t) * 8));
if (ret) {
dev_info(&pdev->dev, "failed to set coherent mask (%zu)\n",
sizeof(dma_addr_t));
}
ret = drm_dev_register(drm, 0);
if (ret < 0)
goto err_master;
return 0;
err_master:
component_master_del(drm->dev, &xilinx_drm_ops);
err_config:
drm_mode_config_cleanup(drm);
if (ret == -EPROBE_DEFER)
DRM_INFO("load() is defered & will be called again\n");
err_drm:
drm_dev_unref(drm);
return ret;
}
/* exit xilinx drm platform */
static int xilinx_drm_platform_remove(struct platform_device *pdev)
{
struct xilinx_drm_private *private = platform_get_drvdata(pdev);
struct drm_device *drm = private->drm;
component_master_del(drm->dev, &xilinx_drm_ops);
drm_kms_helper_poll_fini(drm);
xilinx_drm_fb_fini(private->fb);
drm_mode_config_cleanup(drm);
drm->dev_private = NULL;
drm_dev_unref(private->drm);
return 0;
}
static void xilinx_drm_platform_shutdown(struct platform_device *pdev)
{
struct xilinx_drm_private *private = platform_get_drvdata(pdev);
drm_put_dev(private->drm);
}
static const struct of_device_id xilinx_drm_of_match[] = {
{ .compatible = "xlnx,drm", },
{ /* end of table */ },
};
MODULE_DEVICE_TABLE(of, xilinx_drm_of_match);
static struct platform_driver xilinx_drm_private_driver = {
.probe = xilinx_drm_platform_probe,
.remove = xilinx_drm_platform_remove,
.shutdown = xilinx_drm_platform_shutdown,
.driver = {
.name = "xilinx-drm",
.pm = &xilinx_drm_pm_ops,
.of_match_table = xilinx_drm_of_match,
},
};
module_platform_driver(xilinx_drm_private_driver);
MODULE_AUTHOR("Xilinx, Inc.");
MODULE_DESCRIPTION("Xilinx DRM KMS Driver");
MODULE_LICENSE("GPL v2");
但似乎没有多大用途!
这段设备树代码没点技术功底,看不懂了!只能先当套路记住!
2.9.3 amba_pl设备树中没看懂的地方整理
1、dglnt,edid-i2c = <&i2c0>;是怎么想到添加进去的?
2025/06/24更新
查了网上关于这块的内容,这个链接和链接都不约而同用到这个,我突发奇想,不妨去Github直接搜关键词。于是找到了digilent的链接。
搜到的解释是:只需要在设备树中将链接到HDMI-A接口的i2c主设备作为参数,输入给amba_pl总线下的hdmi_encoder节点、xilinx_drm节点的edid-i2c即可。
在本例和digilent的例子中,设备树的i2c主设备参数分别输入给了digilent_encoder节点和xilinx_drm节点。

2、套路写法里面的plane是这样的:
planes {
xlnx,pixel-format = "rgb888";
plane0 {
dmas = <&axi_vdma_0 0>;
dma-names = "axi_vdma_0";
};
};
但例程里面是:
planes {
xlnx,pixel-format = "rgb888";
plane0 {
dmas = <&axi_vdma_0 0>;
dma-names = "dma";
};
};
在dma-names上的差异怎么解释?我认为可以试着改改!
3、怎么想到是2处节点,而且还恰好是这2处,也就是hdmi_encoder和xilinx_drm?
&amba_pl {
hdmi_encoder_0:hdmi_encoder {
compatible = "digilent,drm-encoder";
};
xilinx_drm {
compatible = "xlnx,drm";
...
};
};
这一点的答案只能是接受这个套路写法,但实际还需要回到hdmi的底层设计。
总结
补全了节点的梳理内容,感觉吃透难度还挺大,嗐!
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐

所有评论(0)