Pulse-width modulation (PWM) is a technique that modifies the duty cycle of a pulsing signal to encode information or to control the amount of energy provided to a charge.

On the ConnectCore 8X system-on-module there are:

  • On the i.MX8QXP system-on-chip:

    • Four PWM channels from the FlexTimer module.

    • One PWM channel from the parallel display interface.

    • Two PWM channels from the MIPI-DSI interface.

  • On the MCA

    • Three PWM controllers (each one with several channels).

The current v4.14 BSP does not support the four PWM channels from the FlexTimer module in the CPU.

This chapter describes the PWM channels from the CPU. See MCA Pulse Width Modulation (PWM) for information about the MCA PWM controllers.

On the ConnectCore 8X SBC Express:

  • MIPI-DSI0 PWM0 is routed to LVDS connector for backlight management.

On the ConnectCore 8X SBC Pro:

  • Parallel LCD PWM is available at SPI0_CS1 pad on the expansion connector.

  • MIPI-DSI0 PWM0 is routed to LVDS0 connector for backlight management.

  • MIPI-DSI1 PWM0 is routed to LVDS1 connector for backlight management.

Kernel configuration

You can manage the i.MX8QXP PWM driver support through the following kernel configuration option:

  • i.MX PWM support (CONFIG_PWM_IMX)

This option is enabled as built-in on the default ConnectCore 8X kernel configuration file.

Kernel driver

The driver for the i.MX8QXP PWM is located at:

File Description

drivers/pwm/pwm-imx.c

PWM driver

Device tree bindings and customization

The i.MX8QXP PWM interface is documented at Documentation/devicetree/bindings/pwm/imx-pwm.txt.

i.MX8QXP PWM interfaces

The common i.MX8QXP CPU device tree file contains entries for the MIPI-DSI PWM channels and the LCD PWM channel:

i.MX8QXP device tree
	pwm_adma_lcdif: pwm@5a190000 {
		compatible = "fsl,imx8qxp-pwm", "fsl,imx27-pwm";
		reg = <0x0 0x5a190000 0 0x1000>;
		clocks = <&clk IMX8QXP_PWM_IPG_CLK>,
			 <&clk IMX8QXP_PWM_CLK>;
		clock-names = "ipg", "per";
		assigned-clocks = <&clk IMX8QXP_PWM_CLK>;
		assigned-clock-rates = <24000000>;
		#pwm-cells = <2>;
		power-domains = <&pd_dma_pwm0>;
		status = "disabled";
	};

	...

	pwm_mipi_lvds0: pwm@56224000 {
		compatible = "fsl,imx8qxp-pwm", "fsl,imx27-pwm";
		reg = <0x0 0x56224000 0 0x1000>;
		clocks = <&clk IMX8QXP_MIPI0_PWM_IPG_CLK>,
			 <&clk IMX8QXP_MIPI0_PWM_CLK>,
			 <&clk IMX8QXP_MIPI0_PWM_32K_CLK>;
		clock-names = "ipg", "per", "32k";
		assigned-clocks = <&clk IMX8QXP_MIPI0_PWM_CLK>;
		assigned-clock-rates = <24000000>;
		#pwm-cells = <2>;
		power-domains = <&pd_mipi_0_pwm0>;
		status = "disabled";
	};

	...

	pwm_mipi_lvds1: pwm@56244000 {
		compatible = "fsl,imx8qxp-pwm", "fsl,imx27-pwm";
		reg = <0x0 0x56244000 0 0x1000>;
		clocks = <&clk IMX8QXP_MIPI1_PWM_IPG_CLK>,
			 <&clk IMX8QXP_MIPI1_PWM_CLK>,
			 <&clk IMX8QXP_MIPI1_PWM_32K_CLK>;
		clock-names = "ipg", "per", "32k";
		assigned-clocks = <&clk IMX8QXP_MIPI1_PWM_CLK>;
		assigned-clock-rates = <24000000>;
		#pwm-cells = <2>;
		power-domains = <&pd_mipi_1_pwm0>;
		status = "disabled";
	};

IOMUX configuration

You must configure the pads that are to be used as i.MX8QXP PWMs. See Pin multiplexing (IOMUX).

i.MX8QXP pads should only have one IOMUX configuration. Remove other configurations for those pads, like GPIO, when configuring them as PWMs.

The following external pads are configured as PWMs on the default device tree:

  • On the ConnectCore 8X SBC Express LVDS connector:

    Pad Signal PWM

    16

    MIPI_DSI0_PWM0_OUT

    MIPI-DSI0 PWM0

  • On the ConnectCore 8X SBC Pro LVDS0 connector:

    Pad Signal PWM

    16

    MIPI_DSI0_PWM0_OUT

    MIPI-DSI0 PWM0

Example: MIPI-DSI0 PWM0 on ConnectCore 8X SBC Pro

For example, MIPI-DSI0 PWM0 is available on pin 16 of the LVDS0 connector of the ConnectCore 8X SBC Pro.

The device tree must:

  • Configure the IOMUX of pad MIPI_DSI0_GPIO0_00 to work as PWM

  • Enable the PWM node

  • Since it’s connected to the display, enable the backlight node as consumer of the PWM

ConnectCore 8X SBC Pro device tree
	lvds_backlight0: lvds_backlight@0 {
		compatible = "pwm-backlight";
		pwms = <&pwm_mipi_lvds0 0 100000 0>;

		brightness-levels = < 0  1  2  3  4  5  6  7  8  9
				     10 11 12 13 14 15 16 17 18 19
				     20 21 22 23 24 25 26 27 28 29
				     30 31 32 33 34 35 36 37 38 39
				     40 41 42 43 44 45 46 47 48 49
				     50 51 52 53 54 55 56 57 58 59
				     60 61 62 63 64 65 66 67 68 69
				     70 71 72 73 74 75 76 77 78 79
				     80 81 82 83 84 85 86 87 88 89
				     90 91 92 93 94 95 96 97 98 99
				    100>;
		default-brightness-level = <80>;
		power-supply = <&reg_5v_display>;
	};

	...

&pwm_mipi_lvds0 {
	status = "okay";
};

...

&iomuxc {

	...

	pinctrl_lvds0: lvds0grp {
		fsl,pins = <
			/* LVDS0 touch interrupt */
			SC_P_QSPI0B_DATA2_LSIO_GPIO3_IO20	0x06000020
			/* LVDS0 PWM backlight */
			SC_P_MIPI_DSI0_GPIO0_00_MIPI_DSI0_PWM0_OUT	0x00000020
		>;
	};

	...
};

Depending on the frequency of the PWM signal and the hardware around it, you must enable the pad settings carefully (the numerical value following the IOMUX definition on the device tree). See Documentation/devicetree/bindings/pinctrl/fsl,imx8qxp-pinctrl.txt for information about the different values. Also see the NXP application note AN5078 Influence of pin setting on system function and performance for additional information.

Using the PWM channels

Control PWM signal from sysfs

If you are not using a backlight driver to manage the PWM signal, you can to control it manually from user space Each PWM interface is registered in the system as a standalone PWM controller.

On the ConnectCore 8X SBC Pro, parallel LCD PWM is available at SPI0_CS1 pad on the expansion connector. This PWM channel is disabled by default. To enable it, you must enable the pwm_adma_lcdif node and comment the IOMUX of SPIO_CS1 pad.

Patch to enable PWM functionality on SPIO0_CS1 pad
diff --git a/arch/arm64/boot/dts/digi/ccimx8qxp-sbc-pro-wb.dts b/arch/arm64/boot/dts/digi/ccimx8qxp-sbc-pro-wb.dts
index 9fc378fb755c..f9f2cce0283f 100644
--- a/arch/arm64/boot/dts/digi/ccimx8qxp-sbc-pro-wb.dts
+++ b/arch/arm64/boot/dts/digi/ccimx8qxp-sbc-pro-wb.dts
@@ -278,9 +278,9 @@
 };

 /* PWM signal on Expansion connector: SPI0_CS1 (conflicts with SPI0 CS1) */
-//&pwm_adma_lcdif {
-//     status = "okay";
-//};
+&pwm_adma_lcdif {
+       status = "okay";
+};

 /*
  * Uncomment to enable MCA ADC channels on Expansion connector:
diff --git a/arch/arm64/boot/dts/digi/ccimx8x-sbc-pro.dtsi b/arch/arm64/boot/dts/digi/ccimx8x-sbc-pro.dtsi
index 0cb1d0d7303f..81bc878e6db6 100644
--- a/arch/arm64/boot/dts/digi/ccimx8x-sbc-pro.dtsi
+++ b/arch/arm64/boot/dts/digi/ccimx8x-sbc-pro.dtsi
@@ -810,7 +810,7 @@
			SC_P_SPI0_SDO_ADMA_SPI0_SDO     0x0600004c
			SC_P_SPI0_SDI_ADMA_SPI0_SDI     0x0600004c
			SC_P_SPI0_CS0_ADMA_SPI0_CS0     0x0600004c
-                       SC_P_SPI0_CS1_ADMA_SPI0_CS1     0x0600004c
+                       //SC_P_SPI0_CS1_ADMA_SPI0_CS1   0x0600004c
		>;
	};

Each PWM interface is registered in the system as a standalone PWM controller. The PWM interfaces appear under /sys/class/pwm:

~# ls -l /sys/class/pwm/
lrwxrwxrwx    1 root     root             0 Jan 21 12:04 pwmchip0 -> ../../devices/platform/5a190000.pwm/pwm/pwmchip0
lrwxrwxrwx    1 root     root             0 Jan 21 12:04 pwmchip1 -> ../../devices/platform/56224000.pwm/pwm/pwmchip1
lrwxrwxrwx    1 root     root             0 Jan 21 12:04 pwmchip10 -> ../../devices/platform/5a800000.i2c/i2c-0/0-0063/mca-pwm/pwm/pwmchip10
lrwxrwxrwx    1 root     root             0 Jan 21 12:04 pwmchip2 -> ../../devices/platform/5a800000.i2c/i2c-0/0-0063/mca-pwm/pwm/pwmchip2
lrwxrwxrwx    1 root     root             0 Jan 21 12:04 pwmchip8 -> ../../devices/platform/5a800000.i2c/i2c-0/0-0063/mca-pwm/pwm/pwmchip8

The PWM interfaces begin numbering with an index of 0. The indexes are calculated using the number of channels of the previous interface. On the example above:

  • i.MX8QXP LCD PWM (1 channel) is pwmchip0

  • i.MX8QXP MIPI-DSI0 PWM (1 channel) is pwmchip1

  • MCA PWM0 (6 channels) is pwmchip2

  • MCA PWM1 (2 channels) is pwmchip8

  • MCA PWM2 (2 channels) is pwmchip10

Check the number of channels of an interface by printing the value of npwm:

~# cat /sys/class/pwm/pwmchip0/npwm
1

To access one channel of a PWM interface, export the channel index. For example, to access channel 0 of LCD PWM:

~# echo 0 > /sys/class/pwm/pwmchip0/export
You won’t be able to request PWM channels that are in use by other drivers, like those used by the backlight.

Now you can access the PWM channel and configure its settings:

~# ls /sys/class/pwm/pwmchip0/pwm0/
capture     duty_cycle  enable      period      polarity    power       uevent

Period and duty cycle must be given in nanoseconds. For example, to configure a 100kHz signal with 20% duty cycle:

~# echo 10000 > /sys/class/pwm/pwmchip0/pwm0/period
~# echo 2000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle

To enable the PWM signal:

~# echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable

The default polarity is normal (active high for the duty cycle). To invert the polarity:

~# echo inversed > /sys/class/pwm/pwmchip0/pwm0/polarity

Using Digi APIx library from a C application

An example application called apix-pwm-example is included in the dey-examples-digiapix recipe (part of dey-examples package) of meta-digi layer. This application shows how to generate a PWM signal using Digi APIx library on the ConnectCore 8X platform.

Go to GitHub to see the application instructions and source code.

See PWM API for more information about the PWM APIx.