前言

分析上一篇没分析完的内容。


一、从这个设备树代码入手

剩下的内容就是:

&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, &regs)) {
		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,xuartpscdns,uart-r1p8驱动程序,即XilinxXUARTPSCadenceUART 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";
				};
			};
	};
};

翻了网上的技术博客,没有一篇解释这玩意儿的!

grepxlnx,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 - vtc

    xilinx_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 - vtc

    xilinx_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子系统 - DP

    xilinx_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 DSI

    xilinx_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_encoderxilinx_drm

&amba_pl {
	hdmi_encoder_0:hdmi_encoder {
	compatible = "digilent,drm-encoder";
};
	xilinx_drm {
	compatible = "xlnx,drm";
	...
	};
};

这一点的答案只能是接受这个套路写法,但实际还需要回到hdmi的底层设计。


总结

补全了节点的梳理内容,感觉吃透难度还挺大,嗐!

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐