Audio clock synchronization via PTP on IMX8MP

Dear Toradex support,

I have a question related to the clock synchronization via PTP on IMX8MP with Mallow board V1.1. My goal is to synchronize audio clock via PTP with ethernet clock. So far, I was unable to synchronize clocks but I am gonna explain the processes I experimented with.

First of all, my goal is to have ethernet clock as master and audio clock as slave. The PTP synchronization could be done via software and it is already compiled within Yocto build (I am using tdx-reference-multimedia-image). This means I am able to use phc2sys, which is, from what I have understand, the software way to use PTP synchronization. However, I was not able to find a way to expose custom clock to the /dev/ so phc2sys does not see the clock. When I run:

phc2sys -s /dev/ptp0 -c /dev/custom_audio_clock -w

or other variants, I got the error: “valid destination clock must be selected.”

There is no /dev/custom_audio_clock even though when I run:

cat /sys/kernel/debug/clk/clk_summary

I can see the custom_audio_clock derived from audio_pll1:

I guess the problem is that my custom clock is handled by imx8mp clock driver and it does not expose it to /dev/. Here is a snippet of my clock configuration in device tree (imx8mp-verdin-dev.dtsi):

/ {
	sound {
		compatible = "simple-audio-card";
		simple-audio-card,bitclock-master = <&cpu_dai>;
		simple-audio-card,format = "msb";
		simple-audio-card,frame-master = <&cpu_dai>;
		simple-audio-card,name = "ak4621";
		simple-audio-card,routing =
			"Headphones", "LOUT1",
			"Headphones", "ROUT1",
			"LMIC", "Mic In",
			"RMIC", "Mic In";
		simple-audio-card,widgets =
			"Headphones", "Headphones",
			"Microphone", "Mic In";

		codec_dai: simple-audio-card,codec {
			sound-dai = <&ak4621>;
		};

		cpu_dai: simple-audio-card,cpu {
			sound-dai = <&sai1>;
		};
	};
};

&clk {
    custom_audio_clock: custom_audio_clock {
        compatible = "fixed-factor-clock";
        clocks = <&audio_blk_ctrl IMX8MP_CLK_AUDIO_BLK_CTRL_SAI1_MCLK1>;
        clock-names = "sai1_mclk1_clk";
        clock-div = <1>; /* need to achieve 12288000 Hz! */
        clock-mult = <1>;
        #clock-cells = <0>;
    };
};

&fec {
    phy-supply = <&reg_eth2phy>;
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_fec>;
    phy-mode = "rgmii-id";
    phy-handle = <&ethphy0>;
    fsl,magic-packet;
    fsl,ptp-clock-source = <1>; /* Use custom_clock as the PTP clock source */
    fsl,ptp-timer-period = <81380>; /* 12288000 Hz = 81.38 ns period */
};

/* Verdin QSPI_1 */
&flexspi {
	status = "okay";
    ak4621: audio-codec@0 {
		compatible = "asahi-kasei,ak4621";
		reg = <0>;
		clocks = <&custom_audio_clock>;
		clock-names = "mclk";
		#sound-dai-cells = <0>;
	};
};

I tested the audio codec and it works fine. I am not sure about the “compatible” propery of the audio clock as I used “fixed-clock” before and it worked as well. But I suppose a clock for PTP synchronization should be dynamic and I am not sure what should I use. I tried to expose the clock to /dev/ using custom clock driver like this:

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/device.h>


static ssize_t custom_audio_clock_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    return sprintf(buf, "custom_audio_clock\n");
}

static DEVICE_ATTR(custom_audio_clock, 0444, custom_audio_clock_show, NULL);

static int custom_clock_probe(struct platform_device *pdev)
{
    struct clk *clk;
    struct device *dev = &pdev->dev;
    struct clk *parent_clk;

    dev_info(dev, "custom_audio_clock probe started\n");

    /* Get the parent clock from the device tree */
    parent_clk = devm_clk_get(dev, "sai1_mclk1_clk");
    if (IS_ERR(parent_clk)) {
        dev_err(dev, "Failed to get parent clock\n");
        return PTR_ERR(parent_clk);
    }

    /* Create a fixed-factor clock */
    clk = clk_register_fixed_factor(dev, "custom_audio_clock", __clk_get_name(parent_clk), 0, 1, 1);
    if (IS_ERR(clk)) {
        dev_err(dev, "Failed to register custom_audio_clock\n");
        return PTR_ERR(clk);
    }

    /* Create a device node in /dev */
    if (device_create_file(dev, &dev_attr_custom_audio_clock)) {
        dev_err(dev, "Failed to create device node\n");
        clk_unregister_fixed_factor(clk);
        return -ENODEV;
    }

    dev_info(dev, "custom_audio_clock registered with name: %s\n", __clk_get_name(clk));
    return 0;
}

static int custom_clock_remove(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;

    /* Remove the device node */
    device_remove_file(dev, &dev_attr_custom_audio_clock);

    dev_info(dev, "custom_audio_clock unregistered\n");
    return 0;
}

static const struct of_device_id custom_clock_of_match[] = {
    { .compatible = "custom-audio-clock", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, custom_clock_of_match);

static struct platform_driver custom_clock_driver = {
    .probe = custom_clock_probe,
    .remove = custom_clock_remove,
    .driver = {
        .name = "custom_audio_clock",
        .of_match_table = custom_clock_of_match,
    },
};

module_platform_driver(custom_clock_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Custom Audio Clock Driver");
MODULE_AUTHOR("xistva02");

It got compiled, I added it to Makefile and Kconfig:

FILESEXTRAPATHS:prepend := "${THISDIR}/linux-toradex:"

SRC_URI += " \
	file://0001-add-ak4621-dt-support.patch \
    file://0001-add-dynamic-clock.patch \
    file://0001-add-custom-clk-config.patch \
    file://clk-custom.c \
"

# Copy the custom clock driver to the source tree
do_configure:append() {
    cp ${WORKDIR}/clk-custom.c ${S}/drivers/clk/
}

# Update PTP configuration after patches are applied
do_configure:append() {
    echo "CONFIG_PTP_1588_CLOCK=y" >> ${B}/.config
    echo "CONFIG_NETWORK_PHY_TIMESTAMPING=y" >> ${B}/.config
    echo "CONFIG_CUSTOM_CLOCK=y" >> ${B}/.config
}

and it seemed okay. But even when I changed the “compatible” of the clock to “custom-audio-clock”, the module was not loaded and the clock was not visible even in clk_summary. The debug messages from probe was not printed in dmesg.

So my question is: how can I synchronize the custom audio clock derived from audio_pll1 with ethernet clock? Is there a way to expose it to /dev/ so the phc2sys can see it or should it be done any other way?

Thank you.

Best regards

Matej I.

Hi, I’m the Toradex AI Assistant. The Toradex Support team will follow up with you shortly!

Did you like my answer?

:+1: :-1:


Hello Matej,

Thank you for reaching out with your question about synchronizing the audio clock with the Ethernet clock using PTP on the iMX8MP. It sounds like you’ve made significant progress, and I’d be happy to help you further.

To achieve synchronization using phc2sys, both clocks need to be recognized by the system as PTP hardware clocks. Here are some steps and considerations to help you proceed:

  1. Device Tree Configuration: Your device tree setup seems mostly correct. Ensure that the custom audio clock is defined with the appropriate properties. Using a “fixed-factor-clock” is suitable if the clock is derived from a parent clock with a fixed relationship.

  2. Clock Driver: Your custom clock driver should ensure that the clock is registered with the kernel’s clock framework. If the driver isn’t loading, check the following:

    • Ensure the compatible string in the device tree matches the one expected by the driver.
    • Verify that the driver is compiled and included in the kernel image.
    • Check dmesg logs for any errors or messages related to the driver loading process.
  3. Exposing Clocks to /dev/: Typically, only network interface clocks are exposed as PTP hardware clocks under /dev/ptp*. For a custom clock, you might need to explore kernel support for exposing it as a PTP clock. This could involve more advanced driver development, potentially using existing PTP clock drivers as a reference.

  4. Using phc2sys: Once your clocks are correctly exposed, you can use phc2sys to synchronize them. The command you attempted (phc2sys -s /dev/ptp0 -c /dev/custom_audio_clock -w) is correct in format, but it requires both clocks to be visible under /dev/.

  5. Troubleshooting: If the custom clock is not visible, revisit the driver and device tree configuration. Ensure that the clock is properly registered and that there are no errors during boot or module loading. You might also need to consult the Linux kernel documentation on clock and PTP support for more advanced configurations.

By ensuring that your custom clock is properly registered and exposed, you should be able to achieve the desired synchronization with the Ethernet clock using PTP.

I hope this helps, and I wish you success in your project!

Best regards,
Toradex AI Assistant

Please, also check if these links can help you solve your problem:

https://community.nxp.com/t5/Layerscape/phc2sys-Waiting-for-ptp4l/m-p/1433168.

It seems that .c file is compiled but it the device tree compatible node with the compatible from the driver are not matched. Or the driver itself is never loaded within the build, there are no debug messages printed, no module in lsmod.

The AI answer was not helpful, any idea how to fix this?

Thank you.

Matej I.

Dear @bruno.tx ,

would you have any ideas please? I tried to adjust the custom clock driver for PTP purposes, but it is never even loaded. How to proceed to load the driver? Or is it possible to synchronize audio_pll1 and ethernet clock just by register modifications?

I think it should be possible to route ethernet clock to be master of audio_pll1, hence the audio clock would be synchronized. In IMX8MP Reference manual (full), on page 231, is it possible to modify ENET_AXI_CLK_ROOT to AUDIO_PLL1_CLK and assign 0x8880101?

Thank you.

Matej I.