| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- assigned-clocks
- i.MX8MP
- TIM17
- 임베디드 리눅스
- Azure RTOS
- CAN 필터
- CAN FD
- SocketCAN
- 하드웨어 디버깅
- bus-off
- 커스텀 보드
- 리눅스 커널
- PTN5150A
- can_filter
- error-passive
- CC 로직
- vcan
- fsl-sai
- HAL_Delay
- 오디오 클럭
- 임베디드 오디오
- 타임베이스
- can-utils
- EASRC
- 44.1kHz
- 리눅스 CAN
- xHCI
- 임베디드
- EILSEQ
- restart-ms
- Today
- Total
임베디드 일기장
i.MX8MP에서 44.1kHz 재생이 안 되는 이유 — "failed to derive required Tx rate"와 오디오 클럭 트리, assigned-clocks의 의미 본문
i.MX8MP에서 44.1kHz 재생이 안 되는 이유 — "failed to derive required Tx rate"와 오디오 클럭 트리, assigned-clocks의 의미
jaeuuu 2026. 7. 12. 19:46i.MX8MP 보드에서 48kHz 음원은 잘 나오는데 44.1kHz 음원을 재생하면 이런 에러와 함께 실패한다.
fsl-sai 30c30000.sai: failed to derive required Tx rate: 1411200
이 에러의 정체는 "44.1kHz용 비트클럭(BCLK) 1,411,200Hz를 지금 물려 있는 클럭 소스로는 정수 분주로 만들 수 없다"는 것이다. 이 글은 왜 그런지(오디오의 두 클럭 패밀리), 어떻게 고치는지(assigned-clocks), 그리고 그 과정에서 헷갈리는 DT 속성들(clocks vs assigned-clocks)의 관계를 정리한다.
1. 배경: 오디오에는 서로 섞이지 않는 두 클럭 패밀리가 있다
디지털 오디오의 샘플레이트는 두 가문으로 나뉜다.
48k 가문: 8k, 16k, 32k, 48k, 96k, 192k → 기준 MCLK: 24.576MHz (= 512×48k)
44.1k 가문: 11.025k, 22.05k, 44.1k, 88.2k → 기준 MCLK: 22.5792MHz (= 512×44.1k)
SAI의 BCLK/FS는 MCLK을 정수 분주해서 만든다. 44.1kHz 32비트 스테레오면 BCLK = 44100 × 32 = 1,411,200Hz인데, 24.576MHz를 어떤 정수로 나눠도 이 값이 안 나온다(24576000 / 1411200 ≈ 17.414...). 그래서 드라이버가 "derive 불가"를 선언하는 것이다. 두 가문은 정수비 관계가 아니라서, 한쪽 MCLK으로 다른 쪽 샘플레이트를 만들 수 없다. 양쪽을 다 지원하는 오디오 제품이 클럭 소스를 두 개 갖거나 리샘플러를 두는 이유다.
i.MX8MP는 이를 위해 오디오 PLL을 두 개 갖고 있다: AUDIO_PLL1(393.216MHz, 48k 가문용)과 AUDIO_PLL2(361.2672MHz, 44.1k 가문용). 문제의 보드는 SAI의 MCLK 부모가 PLL1 계열로만 잡혀 있어서 44.1k 가문 전체가 불가능했던 것이다.
2. 해법 1: assigned-clocks로 SAI의 부모/주파수를 지정
Device Tree에서 SAI 노드의 클럭 계보를 손본다.
&sai3 {
assigned-clocks = <&clk IMX8MP_CLK_SAI3>;
assigned-clock-parents = <&clk IMX8MP_AUDIO_PLL2_OUT>;
assigned-clock-rates = <11289600>; /* 256×44.1k 등 필요 배수 */
...
};
여기서 개념 정리가 필요하다. clocks와 assigned-clocks는 역할이 완전히 다르다.
clocks: "이 장치가 소비하는 클럭이 클럭 트리의 어느 노드인지"를 연결하는 배선도다. 드라이버가devm_clk_get()으로 핸들을 얻는 대상.assigned-clocks/assigned-clock-parents/assigned-clock-rates: "부팅 시 클럭 프레임워크가 이 클럭의 부모와 주파수를 이렇게 설정해두라"는 초기화 지시다. 배선이 아니라 설정값.
세 assigned 속성은 인덱스로 1:1:1 대응한다. 즉 assigned-clocks의 n번째 항목에 대해 parents의 n번째가 부모, rates의 n번째가 주파수다. 특정 항목의 부모만 바꾸고 rate는 건드리기 싫으면 해당 인덱스에 0을 넣는다. "여러 rate를 동시에" 지정하는 개념이 아니라는 것 — 한 클럭은 한 시점에 하나의 rate만 갖는다 — 도 이 구조에서 자명해진다.
설정이 실제 반영됐는지는 부팅 후 클럭 트리 덤프로 확인한다.
cat /sys/kernel/debug/clk/clk_summary | grep -i -E "sai3|audio_pll"
트레이드오프: SAI 하나의 부모를 PLL2로 옮기면 그 SAI는 이제 48k 가문을 못 만든다. 같은 문제의 거울상이다. 한 SAI에서 두 가문을 오가야 한다면 재생 전에 런타임으로 부모를 전환하는 드라이버 지원이 필요한데, fsl-sai와 클럭 드라이버 조합에 따라 지원 여부가 갈린다. 그래서 실무에서는 다음 해법이 자주 선택된다.
3. 해법 2: EASRC로 클럭 문제를 회피
i.MX8MP에는 하드웨어 비동기 샘플레이트 컨버터(EASRC)가 있다.
&easrc {
fsl,asrc-rate = <48000>;
status = "okay";
};
이 구성에서 오디오 경로에 EASRC를 물리면, 애플리케이션이 44.1kHz로 재생해도 하드웨어가 48kHz로 리샘플해서 SAI에는 항상 48k 가문 클럭만 흐른다. fsl,asrc-rate = <48000>이 그 출력 기준 샘플레이트다. aplay에서 44100을 명시해도 코덱에 도달하는 것은 48000이라는 뜻 — "샘플레이트를 지정했는데 왜 48k로 나가지?"라는 의문의 답이 이것이다.
두 해법의 선택 기준: 비트퍼펙트(무변환) 재생이 중요하면 해법 1(클럭을 진짜로 만들기), 다양한 소스를 유연하게 받는 게 중요하고 리샘플 품질 저하를 수용할 수 있으면 해법 2다. 제품 오디오 경로가 어차피 DSP 믹싱을 거친다면 EASRC 경유가 시스템을 단순하게 만든다.
4. 해법3: assigned-clocks로 SAI의 부모/주파수를 추가 지정
&sai3 {
/* refer to imx8mp clock tree
* sai1 clock referenced by sai1~7 mclk2.
* so, for 44100Hz, sai1 clock enable aduio_pll2 as parents
*/
assigned-clocks = <&clk IMX8MP_CLK_SAI3>, <&clk IMX8MP_CLK_SAI1>;
assigned-clock-parents = <&clk IMX8MP_AUDIO_PLL1_OUT>, <&clk IMX8MP_AUDIO_PLL2_OUT>;
assigned-clock-rates = <24576000>, <11289600>;
...
};
5. 정리
- "failed to derive required Tx rate"는 요청 샘플레이트가 현재 MCLK 부모로부터 정수 분주 불가라는 뜻이다
- 48k 가문과 44.1k 가문은 클럭 소스부터 다르다 — i.MX8MP는 PLL1/PLL2로 이원화되어 있다
clocks는 배선,assigned-clocks3형제는 부팅 시 설정이며 인덱스 1:1:1 대응이다- 해법은 둘: PLL2로 부모 변경(비트퍼펙트) 또는 EASRC 리샘플(유연성) — 제품 요구사항이 결정한다
- 결과 검증은 언제나
/sys/kernel/debug/clk/clk_summary
i.MX8MP 오디오 경로 브링업 중 겪은 사례입니다. 수치는 커널 버전과 보드 구성에 따라 확인 필요.