Skip to content

Conversation

@stavinsky
Copy link

@stavinsky stavinsky commented Dec 17, 2025

This patch series adds basic audio support for Sophgo CV1800B, as used on boards such as the Milk-V Duo. The series introduces an AIAO audio mux driver, an I2S controller driver, corresponding DT bindings, and DTS updates to wire the components together.

The implementation is based on vendor documentation and testing on real hardware. This series relies on recent fixes in the DesignWare AXI DMA support; in particular, correct operation depends on the DMA changes discussed at:
https://lore.kernel.org/all/20251214224601.598358-1-inochiama@gmail.com/ The current driver implementation supports a fixed audio configuration of 48 kHz sample rate. The series has been tested on the Milk-V Duo and Milk-V Duo 256M boards using the Sophgo SG2002 SoC. The implementation is expected to also work on Milk-V Duo boards based on the SG2000 SoC, as the audio and DMA blocks are closely related.

Known hardware limitation:
On CV1800B / SG2002, the I2S2 output pins cannot be enabled via pinctrl alone. Due to SoC design constraints, the output path becomes functional only after additional vendor-specific register programming. This series makes the limitation explicit and does not attempt to work around it implicitly via pinctrl or undocumented behavior.

@inochisa inochisa self-requested a review December 17, 2025 22:32
Copy link
Collaborator

@inochisa inochisa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a first step to review. I suggest you should check you patch format by executing scripts/checkpatch.pl with --strict parameter. I will continue to review your logic after this change. Thanks.

@stavinsky stavinsky force-pushed the i2s-driver-with-mux branch 4 times, most recently from 9ea2622 to f8343e3 Compare December 19, 2025 14:27
@stavinsky

This comment was marked as resolved.

@inochisa
Copy link
Collaborator

Good update, and I think you should do the additional check for your patches:

  1. make DT_CHECKER_FLAGS=-m dt_binding_check
  2. make DT_CHECKER_FLAGS=-m W=1 dtbs_check

@stavinsky stavinsky force-pushed the i2s-driver-with-mux branch 5 times, most recently from cbb793a to 14e4e8c Compare December 20, 2025 20:14
@stavinsky

This comment was marked as resolved.

@inochisa
Copy link
Collaborator

I don't know how it is better for you if I click resolve on resolved threads or you prefer to track changes and close threads by yourself.

For simple thing like code refactor, you can just close the threads, for some complex thing, just leave it. And I will decide whether it is OK to close

@inochisa
Copy link
Collaborator

@stavinsky Also, if you can, just do a squash merge for the last commit, it is too hard to see the change.

@stavinsky

This comment was marked as resolved.

@stavinsky stavinsky requested a review from inochisa December 22, 2025 10:12
@stavinsky stavinsky force-pushed the i2s-driver-with-mux branch 2 times, most recently from 98cfdb1 to b986bf9 Compare December 22, 2025 19:02
@stavinsky

This comment was marked as resolved.

@stavinsky stavinsky force-pushed the i2s-driver-with-mux branch 2 times, most recently from 1cda8f1 to 6519dcd Compare December 24, 2025 11:57
@stavinsky
Copy link
Author

@inochisa
I've started to implement internal codec driver (adc for now). I came up with the fact that the simple-audio-card is not enough to describe all possible configurations for our AIAO subsystem. (at least there is no way to request s16_le over 32 bit i2s slot which is the format for internal adc)
It probably means that we have to implement our own "machine driver" to properly connect cpu, codec, and mux.
In that case tdm driver stays the same, but mux will be replaced with sound card machine driver.

If you don't like this idea we could stay with current mux, but we will have to hardcode i2s slot width to 32, which is most common in my practice. And we will probably be not able to support PDM mode (I dont know if anyone will want to used it at all )

possible configuration could be like this

/* i2s nodes remains the same  */
 / {
	sound {
		compatible = "sophgo,cv1800b-aiao";
		model = "cv1800b audio";

		link@0 {
			link-name = "i2s0-internal-adc";
			cpu-dai = <&i2s0>;
			codec-dai = <&int_adc>;

			dai-format = "i2s";
			bitclock-master = <&i2s0>;
			frame-master = <&i2s0>;
			sophgo,sdi-in = <CV180x_INT_ADC>;
		};

		link@1 {
			link-name = "i2s3-internal-dac";
			cpu-dai = <&i2s3>;
			codec-dai = <&int_dac>;

			dai-format = "i2s";
			bitclock-master = <&i2s3>;
			frame-master = <&i2s3>;
			sophgo,sdo-out = <CV180x_INT_DAC>;
		};

		link@2 {
			link-name = "i2s2-ics43432";
			cpu-dai = <&i2s2>;
			codec-dai = <&ics43432>;

			dai-format = "i2s";
			bitclock-master = "cpu";
			frame-master = "cpu";
			sophgo,sdi-in = <CV180x_EXT_I2S2>;
		};
	};
};

int_adc: codec@300a100 {
	compatible = "sophgo,cv1800b-adc";
	reg = <0x0300a100 0x100>;
	#sound-dai-cells = <0>;
	status = "okay";
};

int_dac: codec@300a000 {
	compatible = "sophgo,cv1800b-dac";
	reg = <0x0300a000 0x100>;
	#sound-dai-cells = <0>;
	status = "okay";
};

codec0: my-adc {
	compatible = "invensense,ics43432";
	#sound-dai-cells = <0>;
};

@inochisa
Copy link
Collaborator

inochisa commented Dec 26, 2025

I've started to implement internal codec driver (adc for now). I came up with the fact that the simple-audio-card is not enough to describe all possible configurations for our AIAO subsystem. (at least there is no way to request s16_le over 32 bit i2s slot which is the format for internal adc) It probably means that we have to implement our own "machine driver" to properly connect cpu, codec, and mux. In that case tdm driver stays the same, but mux will be replaced with sound card machine driver.

If you don't like this idea we could stay with current mux, but we will have to hardcode i2s slot width to 32, which is most common in my practice.

I think it is worthy to switch to the machine driver. I prefer to keep SoC driver.
But in fact, I do not like the current mux. It is works, but is too ugly. And I doubt the subsystem maintainer will agree as you used a very cold framework. I also want to combine them as one device, but it is also a mess.

And we will probably be not able to support PDM mode (I dont know if anyone will want to used it at all )

It is fine to leave it if it is hard to implement, but I suggest leaving room for the PDM mode, it is a pain to refactor everything at the time we want to support it. In some times, it means the driver itself should be abandoned and a new one should be added.

PS1: how about using simple-audio-card,aux-devs to controll the mux?


i2s0: i2s@4100000 {
compatible = "sophgo,cv1800b-i2s";
reg = <0x04100000 0x10000>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reg = <0x04100000 0x8000>; The mux is at the last 0x8000.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true. Found this problem yesterday. it should be 1000 not 10000. Typo.

@inochisa
Copy link
Collaborator

Some bad (or not so bad) thoughs:

  1. switch mux driver to a syscon device, and let all i2s, pcm, dac.adc drivers access this device directly via regmap. The property can be something like sophgo,audio-control
  2. Allow to set the I2S mode in the DTS again, use something like sophgo,i2s-tx-only and sophgo,i2s-rx-only, the default is txrx if no one is set. Since the i2s driver know its mode, it then can setup in a right way instead of enable all of it.

@stavinsky
Copy link
Author

I think it is worthy to switch to the machine driver. I prefer to keep SoC driver. But in fact, I do not like the current mux. It is works, but is too ugly. And I doubt the subsystem maintainer will agree as you used a very cold framework. I also want to combine them as one device, but it is also a mess.

And we will probably be not able to support PDM mode (I dont know if anyone will want to used it at all )

It is fine to leave it if it is hard to implement, but I suggest leaving room for the PDM mode, it is a pain to refactor everything at the time we want to support it. In some times, it means the driver itself should be abandoned and a new one should be added.

PS1: how about using simple-audio-card,aux-devs to controll the mux?

I think I can write sound card driver in a few days. This means we will have possibility to configure everything we have in hardware.
So the mux will be replaced with the new sound card driver. (probably we will have even less amount of code for now)
The structure I've described before looks unfinished but it is already describing everything we need in future. (i2s, pdm, internal codecs)
so the plan is:
step 1: i2s driver that we already have without mux settings at all in dts (the card driver will be a glue between i2s and codecs as intended)
step 2: sound card driver with ability to connect external/internal codecs via DAI link (easy part if we implement only basics, alsa have all needed callbacks, I just need to implement it in TDM module)
step 3: internal codec (possibly another patch series)
step 4: pdm, pcm formats for full implementation of the driver (another patch series)
step 5: DAPM - but i'm not sure I have enough energy to do that part

If you agree wth the plan, i will make this pull request as draft and come back to you when the card driver will be ready.
The plan eliminates your not so bad thoughts and we will make it as ALSA recommends.
But I still need your thoughts about proposed DT for the card driver.

@inochisa
Copy link
Collaborator

I think I can write sound card driver in a few days. This means we will have possibility to configure everything we have in hardware.
So the mux will be replaced with the new sound card driver. (probably we will have even less amount of code for now)

Great

The structure I've described before looks unfinished but it is already describing everything we need in future. (i2s, pdm, internal codecs) so the plan is:
step 1: i2s driver that we already have without mux settings at all in dts (the card driver will be a glue between i2s and codecs as intended)

Agreed

step 2: sound card driver with ability to connect external/internal codecs via DAI link (easy part if we implement only basics, alsa have all needed callbacks, I just need to implement it in TDM module)

OK, I think it is reasonable, It seems like better to leave the connection in the board.

step 3: internal codec (possibly another patch series)

Another is fine. Just be simple

step 4: pdm, pcm formats for full implementation of the driver (another patch series)

It is OK for me.

step 5: DAPM - but i'm not sure I have enough energy to do that part

Anyway, it is enough for something, if you feel too hard for it, I will take the rest. Thanks for your energy :).

If you agree wth the plan, i will make this pull request as draft and come back to you when the card driver will be ready.

I think it is good plan, let's start it.

The plan eliminates your not so bad thoughts and we will make it as ALSA recommends.

It is good to follow ALSA, and I think it is necessary to keep simple.

But I still need your thoughts about proposed DT for the card driver.

In fact, it is kind of relaxed for devicetree, In my view, I prefer a extensible design, but if it is too hard, leave it and just make it worked.

@stavinsky stavinsky marked this pull request as draft December 26, 2025 13:28
@stavinsky
Copy link
Author

In fact, it is kind of relaxed for devicetree, In my view, I prefer a extensible design, but if it is too hard, leave it and just make it worked.

I will try to replicate dt as much as possible in the same way as simple-audio-card does with only 1 addition: mux connections. This mostly why we need our card.

@stavinsky stavinsky force-pushed the i2s-driver-with-mux branch from 6519dcd to 2c44a76 Compare January 7, 2026 16:46
@stavinsky stavinsky marked this pull request as ready for review January 7, 2026 16:46
@stavinsky
Copy link
Author

@inochisa Hey. Hope you are doing well.

I made a decision to get rid of mux driver in this commit.
by default it is allocated in the following order:
TDM0 -> internal adc
TDM1 -> external I2S1
TDM2 -> external I2S2
TDM3 -> internal dac
So I've just described everything with simple-audio-card in the same way.

Internal ADC/DAC now works out of the box. I2S2 with external mic still need these settings for ethernet IP block. You said it should be done by mdio but mdio is not available yet.

What we have now

tdm almost the same. Added a few callbacks to be compatible with simple-card
adc codec - works only on 48kHz 2 channels
dac codec - works only on 48kHz 2 channels

Testing and results

can be tested like this. Ideally with Duo Module 01 EVB board as it have standard jack and both channels available.
on Milk Duo 256M capture also works with left channel, playback was not tested.

 modprobe cv1800b-sound-dac dyndbg=+pt && \
 modprobe cv1800b-sound-adc dyndbg=+pt && \
 modprobe cv1800b-tdm  dyndbg=+pt && \
 modprobe snd-soc-simple-card dyndbg=+pt

amixer sset 'Internal I2S' 12
arecord -D hw:0,1 -f S16_LE -c 2 -r 48000  -d 10 -t wav > dump.wav
aplay  -D hw:0,0 -f S16_LE -c 2 -r 48000  dump.wav

My external I2S mic was also tested on Milk 256M and it works fine with 32 bit slot on I2S2 (ethernet pins from the side)
For internal DAC/ADC it is important to use provided configuration in DT example specially clocks and rates. It is important because internal ADC uses TDM3 MCLK as master clock. So it should be enabled and with predefined frequency.

all the commits were checked with

./scripts/checkpatch.pl --subjective --strict -g HEAD
make DT_CHECKER_FLAGS=-m dt_binding_check
make DT_CHECKER_FLAGS=-m W=1 dtbs_check

As always appreciate for you support and attention to details.
Cheers :)

P.S. i've ordered PCM mic to try implement PCM mode. But this is a different story.
Right now only I2S will be available.

compatible = "sophgo,cv1800b-i2s";
reg = <0x04110000 0x10000>;
#address-cells = <1>;
#size-cells = <0>;// check if can be dropped
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest just leave it, also remove the comment,

@inochisa
Copy link
Collaborator

I made a decision to get rid of mux driver in this commit. by default it is allocated in the following order: TDM0 -> internal adc TDM1 -> external I2S1 TDM2 -> external I2S2 TDM3 -> internal dac So I've just described everything with simple-audio-card in the same way.

How about adding a property "sophgo,mux" syscon node? so we can configure the mux if needed.

Internal ADC/DAC now works out of the box. I2S2 with external mic still need these settings for ethernet IP block. You said it should be done by mdio but mdio is not available yet.

The ethernet change I think it maybe too dirty to be upstreamed, so I think it is OK to just leave a comment for it

@inochisa
Copy link
Collaborator

inochisa commented Jan 11, 2026

@stavinsky In genernal, I think it is good enough and ready to go, feel free to sent it to the LKML and check if there is more advice, thanks for your PR.

@stavinsky
Copy link
Author

stavinsky commented Jan 11, 2026

I made a decision to get rid of mux driver in this commit. by default it is allocated in the following order: TDM0 -> internal adc TDM1 -> external I2S1 TDM2 -> external I2S2 TDM3 -> internal dac So I've just described everything with simple-audio-card in the same way.

How about adding a property "sophgo,mux" syscon node? so we can configure the mux if needed.

I'm preparing another patch series with custom sound card. Mux, clocks and so on should be there i think.

Internal ADC/DAC now works out of the box. I2S2 with external mic still need these settings for ethernet IP block. You said it should be done by mdio but mdio is not available yet.

The ethernet change I think it maybe too dirty to be upstreamed, so I think it is OK to just leave a comment for it

yep. Agree.

Purpose: introduce DT schema for the CPU driver

Signed-off-by: Anton D. Stavinskii <stavinsky@gmail.com>
The actual CPU DAI controller.

Signed-off-by: Anton D. Stavinskii <stavinsky@gmail.com>
Document the internal ADC audio codec integrated in the Sophgo
CV1800B SoC.

Signed-off-by: Anton D. Stavinskii <stavinsky@gmail.com>
Codec DAI endpoint for TXDAC + basic controls.

Signed-off-by: Anton D. Stavinskii <stavinsky@gmail.com>
Document the internal DAC audio codec integrated in the Sophgo
CV1800B SoC.

Signed-off-by: Anton D. Stavinskii <stavinsky@gmail.com>
Codec DAI endpoint for TXDAC + basic controls.

Signed-off-by: Anton D. Stavinskii <stavinsky@gmail.com>
New dts nodes for TDMs and readable DMA names

Signed-off-by: Anton D. Stavinskii <stavinsky@gmail.com>
Example of usage internal dac/adc and external I2S mic

Signed-off-by: Anton D. Stavinskii <stavinsky@gmail.com>
@stavinsky stavinsky force-pushed the i2s-driver-with-mux branch from 2c44a76 to b8b3663 Compare January 15, 2026 18:07
@stavinsky
Copy link
Author

@stavinsky In genernal, I think it is good enough and ready to go, feel free to sent it to the LKML and check if there is more advice, thanks for your PR.

Thanks for your help. It was a great journey. Ive sent to lkml as you can see :)

unicornx pushed a commit that referenced this pull request Jan 21, 2026
When interrupting perf stat in repeat mode with a signal the signal is
passed to the child process but the repeat doesn't terminate:
```
$ perf stat -v --null --repeat 10 sleep 1
Control descriptor is not initialized
[ perf stat: executing run #1 ... ]
[ perf stat: executing run #2 ... ]
^Csleep: Interrupt
[ perf stat: executing run #3 ... ]
[ perf stat: executing run #4 ... ]
[ perf stat: executing run #5 ... ]
[ perf stat: executing run #6 ... ]
[ perf stat: executing run #7 ... ]
[ perf stat: executing run #8 ... ]
[ perf stat: executing run #9 ... ]
[ perf stat: executing run #10 ... ]

 Performance counter stats for 'sleep 1' (10 runs):

            0.9500 +- 0.0512 seconds time elapsed  ( +-  5.39% )

0.01user 0.02system 0:09.53elapsed 0%CPU (0avgtext+0avgdata 18940maxresident)k
29944inputs+0outputs (0major+2629minor)pagefaults 0swaps
```

Terminate the repeated run and give a reasonable exit value:
```
$ perf stat -v --null --repeat 10 sleep 1
Control descriptor is not initialized
[ perf stat: executing run #1 ... ]
[ perf stat: executing run #2 ... ]
[ perf stat: executing run #3 ... ]
^Csleep: Interrupt

 Performance counter stats for 'sleep 1' (10 runs):

             0.680 +- 0.321 seconds time elapsed  ( +- 47.16% )

Command exited with non-zero status 130
0.00user 0.01system 0:02.05elapsed 0%CPU (0avgtext+0avgdata 70688maxresident)k
0inputs+0outputs (0major+5002minor)pagefaults 0swaps
```

Note, this also changes the exit value for non-repeat runs when
interrupted by a signal.

Reported-by: Ingo Molnar <mingo@kernel.org>
Closes: https://lore.kernel.org/lkml/aS5wjmbAM9ka3M2g@gmail.com/
Signed-off-by: Ian Rogers <irogers@google.com>
Tested-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants