Note

This is the documentation for the latest development branch and may refer to features that are not available in released versions. If you are looking for a specific release, use the drop-down menu on the left.

Audio Usage Instructions#

Overview#

The K230 audio subsystem provides three capture paths and one playback path:

Path

Signal Source

Pins Used

Sound Card

Built-in codec capture

Onboard analog microphone

Internal to the chip; no pins used

K230_I2S_INNO

External I2S digital microphone capture

I2S slave device (digital microphone)

IO32, IO33 + IO34 or IO36

K230_I2S_INNO

PDM digital microphone capture

PDM microphone, up to 4 data lines and 8 channels

IO26 + IO27 (or IO37), IO28 (or IO35), IO36, IO34

K230_PDM

Playback

Built-in codec or external I2S DAC

Headphone jack / IO35

K230_I2S_INNO

The default image enables only the built-in codec. Both the external I2S digital microphone and the PDM digital microphone require board-level device tree modifications, as described in the following two sections.

View the sound cards in the system:

[root@canaan ~ ]#cat /proc/asound/cards
 0 [K230I2SINNO    ]: K230_I2S_INNO - K230_I2S_INNO
                      K230_I2S_INNO
 1 [K230PDM        ]: K230_PDM - K230_PDM
                      K230_PDM

The sound card numbering is determined by driver registration order. When PDM is not configured, only card 0 is present. The hw:0,0, hw:1,0, and amixer -c0 values in this document use the numbering from the actual test results above. Refer to cat /proc/asound/cards for the actual numbering.

Built-in codec#

The sound node in the board-level device tree (present by default on each board and requires no modifications):

sound {
    status                      = "okay";
    compatible                  = "canaan,k230-audio-inno";
    canaan,model                = "K230_I2S_INNO";
    canaan,k230-i2s-controller  = <&i2s>;
    canaan,k230-audio-codec     = <&inno_codec>;
};

Recording and playback:

# Onboard analog microphone recording
arecord -D hw:0,0 -f S16_LE -r 44100 -c 2 -d 10 test.wav

# Playback
aplay -D hw:0,0 test.wav

External I2S Digital Microphone#

The external digital microphone connects to the K230 as an I2S slave device, with the K230 providing the clock. Two wiring options are supported, and the option is selected in the device tree.

Pins#

Pin

function

Feature

Purpose

io32

alt2

IIS_CLK

Bit clock, required for both wiring options

io33

alt2

IIS_WS

Frame clock, required for both wiring options

io34

alt2

IIS_D_IN0

Data input for wiring option A, corresponding to channels 0/1

io36

alt2

IIS_D_IN1

Data input for wiring option B, corresponding to channels 2/3

io35

alt2

IIS_D_OUT0

Data output, required only when connecting an external I2S DAC

Device Tree Properties#

Add the following to the sound node. Both properties are optional, and when neither is specified, the behavior is the same as that of the default image:

Property

Value

Default

Purpose

canaan,i2s-route

internal-codec / external-i2s

internal-codec

Source of channels 0/1: built-in codec or IO34

canaan,rx1-source

none / external-i2s

none

Whether to enable IO36 as channels 2/3

canaan,i2s-route is only the default value at startup and can be switched at runtime using the Codec Route control. canaan,rx1-source determines the maximum number of capture channels (2 or 4) and can only be determined at startup.

Wiring Option A: External Digital Microphone on IO34, 2 Channels#

Channels 0/1 come from IO34, and the built-in codec is not involved in capture.

/ {
    sound {
        status                      = "okay";
        compatible                  = "canaan,k230-audio-inno";
        canaan,model                = "K230_I2S_INNO";
        canaan,k230-i2s-controller  = <&i2s>;
        canaan,k230-audio-codec     = <&inno_codec>;

        canaan,i2s-route = "external-i2s";
    };
};

&iomux {
    i2s_pins: i2s_pins {
        pin_clk_ws {
            pins = "io32", "io33";  /* IIS_CLK, IIS_WS */
            function = "alt2";
            output-enable;
            input-disable;
            bias-disable;
            drive-strength = <7>;
        };
        pin_din0 {
            pins = "io34";
            function = "alt2";      /* IIS_D_IN0 */
            input-enable;
            output-disable;
            bias-disable;
        };
        pin_dout0 {
            pins = "io35";
            function = "alt2";      /* IIS_D_OUT0, can be omitted for recording only */
            output-enable;
            input-disable;
            bias-disable;
            drive-strength = <7>;
        };
    };
};

&i2s {
    pinctrl-names = "default";
    pinctrl-0 = <&i2s_pins>;
};

Capture:

amixer -c0 cset name='Codec Route' 'External I2S'   # Switch to IO34
arecord -D hw:0,0 -f S16_LE -r 44100 -c 2 -d 10 test.wav

amixer -c0 cset name='Codec Route' 'Internal Codec' # Switch back to the onboard analog microphone

Attention

Codec Route 是双向的:切到 External I2S 后,采集来自 IO34,播放数据从 IO35 输出,板载喇叭无声。要听声音需要在 IO35 接外置 I2S DAC,或者先切回 Internal Codec 再 aplay。

录音或播放进行中不能切换,此时 amixer 会返回 Device or resource busy。

要让选择在重启后保留,推荐用设备树属性 canaan,i2s-route 设定开机默认值。 默认镜像开机不执行 alsactl restore,所以只跑 alsactl store 是不生效的, 要用它得自己补一个开机恢复脚本:

alsactl store                       # 存到 /var/lib/alsa/asound.state
cat > /etc/init.d/S60alsa <<'EOF'
#!/bin/sh
[ "$1" = start ] && alsactl restore
EOF
chmod +x /etc/init.d/S60alsa

Wiring Option B: Built-in Codec + External Digital Microphone on IO36, 4 Channels#

Channels 0/1 are from the onboard analog microphone, and channels 2/3 are from the external digital microphone on IO36. Both are captured simultaneously. Add only canaan,rx1-source to the sound node:

/ {
    sound {
        status                      = "okay";
        compatible                  = "canaan,k230-audio-inno";
        canaan,model                = "K230_I2S_INNO";
        canaan,k230-i2s-controller  = <&i2s>;
        canaan,k230-audio-codec     = <&inno_codec>;

        canaan,rx1-source = "external-i2s";
    };
};

&iomux {
    i2s_pins: i2s_pins {
        pin_clk_ws {
            pins = "io32", "io33";  /* IIS_CLK, IIS_WS */
            function = "alt2";
            output-enable;
            input-disable;
            bias-disable;
            drive-strength = <7>;
        };
        pin_din1 {
            pins = "io36";
            function = "alt2";      /* IIS_D_IN1 → channels 2/3 */
            input-enable;
            output-disable;
            bias-disable;
        };
    };
};

&i2s {
    pinctrl-names = "default";
    pinctrl-0 = <&i2s_pins>;
};

With this wiring option, IO34 is not involved in capture and requires no configuration. Leave Codec Route at its default value Internal Codec, and playback will output normally through the onboard speaker.

Capture and split into two files:

arecord -D hw:0,0 -f S16_LE -r 44100 -c 4 -d 10 test_4.wav

ffmpeg -i test_4.wav -af "pan=stereo|c0=c0|c1=c1" Built-in.wav
ffmpeg -i test_4.wav -af "pan=stereo|c0=c2|c1=c3" External.wav

The interleaving order of the 4 channels is [Built-in L][Built-in R][External L][External R].

Verification#

# Wiring option B should print external-i2s. The device tree string has a trailing '\0'; remove it with tr for easy script comparison
cat /proc/device-tree/sound/canaan,rx1-source | tr -d '\0'

arecord -D hw:0,0 --dump-hw-params              # Wiring option B should display CHANNELS: [2 4]
amixer -c0 cget name='Codec Route'              # View the current path

When this property is not specified, /proc/device-tree/sound/canaan,rx1-source does not exist. It is normal for cat to report No such file or directory; this indicates that the default value none is being used.

PDM Digital Microphone#

PDM is an independent capture path that does not occupy the I2S controller. It is registered as a separate sound card, K230_PDM, capture only, no playback, and can be used simultaneously with recording and playback on the built-in codec.

Wiring and Pins#

A PDM microphone only requires a clock, data, ground, and power supply. Two microphones can be connected to one data line, with the L/R pins of the two microphones connected to GND and VDD respectively, each occupying a different clock edge:

K230 IO26 ─── CLK ──┬──▶ MIC A   L/R connected to GND → rising edge
                    └──▶ MIC B   L/R connected to VDD → falling edge

K230 IO27 ◀── DATA ─┬─── MIC A
                    └─── MIC B   Both microphones share this data line

The chip has four data lines, with two microphones on each line, supporting up to 8 channels:

Data Line

Pin

Function

Corresponding Channels

PDM_IN0

io27 (or io37)

alt3 (io37 is alt2)

0, 1

PDM_IN1

io28 (or io35)

alt3 (io35 is alt2)

2, 3

PDM_IN2

io36

alt2

4, 5

PDM_IN3

io34

alt2

6, 7

The clock always uses io26 (alt3, PDM_CLK).

Device Tree Configuration#

Add a sound_pdm node under the root node of the board device tree and configure the pins. The following is the minimum configuration for one data line and 2 channels:

/ {
    sound_pdm: sound_pdm {
        status        = "okay";
        compatible    = "canaan,k230-audio-pdm";
        canaan,model  = "K230_PDM";
        clocks        = <&pdm_clk>;
        clock-names   = "pdm";
        dmas          = <&pdma 1 0xfff 0 0x21>;
        dma-names     = "rx";
        pinctrl-names = "default";
        pinctrl-0     = <&pdm_pins>;
    };
};

&iomux {
    pdm_pins: pdm_pins {
        pin_clk {
            pins = "io26";
            function = "alt3";      /* PDM_CLK */
            output-enable;
            input-disable;
            bias-disable;
            drive-strength = <7>;
            input-schmitt = <0>;
        };
        pin_data0 {
            pins = "io27";
            function = "alt3";      /* PDM_IN0 */
            input-enable;
            output-disable;
            bias-disable;
            drive-strength = <4>;
            input-schmitt = <0>;
        };
    };
};

The clocks and dmas lines use a fixed format and can be copied as-is.

Optional Properties#

All four properties are optional. If omitted, the default values shown below are used (one data line, both edges, 2 channels):

Property

Value

Default

Purpose

canaan,pdm-data-lines

1 ~ 4

1

Number of connected data lines, counted starting from PDM_IN0

canaan,pdm-edge

both / rising / falling

both

Clock edge used for sampling; both means two microphones on one line

canaan,pdm-oversample

32 / 64 / 128

64

Oversampling ratio, which determines the PDM clock frequency together with the sample rate

canaan,pdm-io-group

io27-28 / io35-37

io27-28

Pin group to which the first two data lines are connected

Number of channels = number of data lines × (both ? 2 : 1), up to 8.

Attention

canaan,pdm-io-group 必须和实际接线一致。前两根数据线接在 io35/io37 上的板卡 要写 canaan,pdm-io-group = "io35-37",配错的表现是全程静音。

选了 io35-37 之后,io35 和 io37 就不能再作 I2S 数据输出,也就是 io35(IIS_D_OUT0)上接不了外置 I2S DAC 播放;同时 io27、io28 无论 pinctrl 怎么配都收不到 PDM 数据了。第 3、4 根数据线(io36、io34)不受这个属性影响, 按 pinctrl 配置即可。

Adding Data Lines#

canaan,pdm-data-lines and the pins in pdm_pins must be changed together. List as many pins in the pins array as the number of data lines being used. When all 4 lines are connected (8 channels):

    sound_pdm: sound_pdm {
        /* Other properties are the same as above */
        canaan,pdm-data-lines = <4>;    /* 4 lines × both edges = 8 channels */
    };
&iomux {
    pdm_pins: pdm_pins {
        pin_clk {
            pins = "io26";
            function = "alt3";      /* PDM_CLK */
            output-enable;
            input-disable;
            bias-disable;
            drive-strength = <7>;
            input-schmitt = <0>;
        };

        pin_data01 {
            pins = "io27", "io28";  /* PDM_IN0、PDM_IN1 */
            function = "alt3";
            input-enable;
            output-disable;
            bias-disable;
            drive-strength = <4>;
            input-schmitt = <0>;
        };

        pin_data23 {
            pins = "io36", "io34";  /* PDM_IN2、PDM_IN3 */
            function = "alt2";
            input-enable;
            output-disable;
            bias-disable;
            drive-strength = <4>;
            input-schmitt = <0>;
        };
    };
};

All pins in a child node share the same function and configuration, so the data pins are divided into two child nodes according to the mux setting: io27 and io28 are alt3, while io36 and io34 are alt2. Pins with different settings cannot be placed together.

For 6 channels only, use canaan,pdm-data-lines = <3> and change the pins of pin_data23 to include only "io36". For 4 channels only, use <2> and omit the entire pin_data23 node.

Attention

When using the 4th line (io34), Codec Route must be switched to External I2S, otherwise channels 6 and 7 will always contain constant full-scale values:

amixer -c0 cset name='Codec Route' 'External I2S'

After switching, capture and playback on the built-in codec are unavailable (onboard analog microphone and onboard speaker). Using only 3 lines (io27 + io28 + io36, 6 channels) does not have this restriction.

Capture#

# First confirm that the configuration detected by the driver matches the actual wiring
dmesg | grep -i pdm
# PDM capture: 1 data line(s) on IO27/IO28, both edge, 64x oversampling, up to 2 channels

# Record
arecord -D hw:1,0 -f S16_LE -r 48000 -c 2 -d 10 pdm.wav

# Play back through the onboard speaker for listening
aplay -D hw:0,0 pdm.wav

The hardware outputs 24-bit data, and the driver supports three formats:

-f

Resulting Data

Use Case

S16_LE

Upper 16 bits of 24-bit data

General recording and speech recognition; recommended

S24_LE

Full 24-bit data stored in the low bits of a 4-byte container

When full precision is required

S32_LE

24-bit data shifted left by 8 bits

When the subsequent processing chain requires 32-bit alignment

For multichannel capture, the channel count must be selected continuously from 1 to the maximum and cannot be specified with gaps:

arecord -D hw:1,0 --dump-hw-params 2>&1 | grep -i channels
# 根线时应为 CHANNELS: [1 8]

amixer -c0 cset name='Codec Route' 'External I2S'   # 需要声道 6、7 时才执行
arecord -D hw:1,0 -f S16_LE -r 48000 -c 8 -d 5 八声道.wav

# 拆成单声道文件
for i in 0 1 2 3 4 5 6 7; do
    ffmpeg -y -i 八声道.wav -af "pan=mono|c0=c$i" ch$i.wav
done

Channel mapping when canaan,pdm-edge = "both":

Channel

Source

Channel

Source

0

io27, L/R connected to GND

4

io36, L/R connected to GND

1

io27, L/R connected to VDD

5

io36, L/R connected to VDD

2

io28, L/R connected to GND

6

io34, L/R connected to GND

3

io28, L/R connected to VDD

7

io34, L/R connected to VDD

PDM and the built-in codec can be used simultaneously:

arecord -D hw:1,0 -f S16_LE -r 48000 -c 2 -d 20 pdm.wav &
arecord -D hw:0,0 -f S16_LE -r 48000 -c 2 -d 20 onboard-mic.wav &
aplay   -D hw:0,0 test.wav
wait

Resource Conflicts#

io34 and io36 serve as both I2S data inputs and PDM data inputs, and can only be used by one path at a time:

Requirement

PDM Data Lines

Available External I2S Digital Mics

2-Channel PDM

io27

Both io34 and io36 are available

4-Channel PDM

io27 + io28

Both io34 and io36 are available

6-Channel PDM

+ io36

Only io34 remains available

8-Channel PDM

+ io36 + io34

Neither is available, and Codec Route must be switched

The onboard analog microphone (built-in codec capture) and speaker playback are unaffected under the above combinations, but are unavailable after Codec Route is switched to External I2S.

The table above applies when the first two data lines are connected to io27 and io28 (the default canaan,pdm-io-group = "io27-28"). When they are connected to io35 and io37 instead (canaan,pdm-io-group = "io35-37"), there is an additional conflict: io35 is IIS_D_OUT0, so once it is occupied by PDM, external I2S DAC playback is unavailable, while onboard speaker playback remains unaffected.

Comments list
Comments
Log in