Pillgram::Audio: Difference between revisions
→Schematics: File with copyright |
→TLV320AIC3110: add wiki |
||
(4 intermediate revisions by the same user not shown) | |||
Line 4: | Line 4: | ||
== Device Tree == | == Device Tree == | ||
<syntaxhighlight>/dts-v1/; | <syntaxhighlight> | ||
/plugin/; | /dts-v1/; | ||
/plugin/; | |||
/ { | |||
/ { | |||
compatible = "brcm,bcm2712", "brcm,bcm2711", "brcm,bcm2835"; | compatible = "brcm,bcm2712", "brcm,bcm2711", "brcm,bcm2835"; | ||
// compatible = "brcm,bcm2835"; | |||
fragment@0 { | fragment@0 { | ||
target = <& | target = <&i2s_clk_consumer>; | ||
__overlay__ { | __overlay__ { | ||
status = "okay"; | status = "okay"; | ||
#sound-dai-cells = <0>; | #sound-dai-cells = <0>; | ||
brcm,tx-channels = <2>; | brcm,tx-channels = <2>; | ||
brcm,rx-channels = < | brcm,rx-channels = <1>; | ||
}; | }; | ||
}; | }; | ||
fragment@ | fragment@1 { | ||
target-path = "/"; | |||
__overlay__ { | |||
status = "okay"; | |||
codec_1v8_reg: codec-1v8-reg { | |||
compatible = "regulator-fixed"; | |||
regulator-name = "tlv320aic3104_1v8"; | |||
regulator-min-microvolt = <1800000>; | |||
regulator-max-microvolt = <1800000>; | |||
regulator-always-on; | |||
status = "okay"; | |||
}; | }; | ||
}; | |||
}; | }; | ||
fragment@ | fragment@2 { | ||
target = <&i2c1>; | target = <&i2c1>; | ||
__overlay__ { | __overlay__ { | ||
Line 41: | Line 46: | ||
tlv320aic3110: tlv320aic3110@18 { | tlv320aic3110: tlv320aic3110@18 { | ||
compatible = "ti,tlv320aic3110", "ti,tlv320aic311x"; | compatible = "ti-pg,tlv320aic3110", "ti,tlv320aic3110", "ti,tlv320aic311x"; | ||
reg = <0x18>; | reg = <0x18>; | ||
#sound-dai-cells = <0>; | #sound-dai-cells = <0>; | ||
Line 61: | Line 66: | ||
// gpio-controller; | // gpio-controller; | ||
reset-gpios = <&gpio 13 1>; // GPIO 13 as active low reset | |||
reset-gpios = <&gpio 13 | |||
reset-delay-us = <10000>; /* 10ms delay */ | reset-delay-us = <10000>; /* 10ms delay */ | ||
// Debug properties | // Debug properties (must precede subnodes) | ||
debug; | debug; | ||
linux,debug; | linux,debug; | ||
}; | }; | ||
}; | }; | ||
}; | }; | ||
fragment@ | fragment@3 { | ||
target = <&sound>; | target = <&sound>; | ||
__overlay__ { | __overlay__ { | ||
compatible = "simple-audio-card"; | compatible = "simple-audio-card"; | ||
i2s-controller = <&i2s_clk_consumer>; | |||
simple-audio-card,name = "TLV320AIC3110"; | simple-audio-card,name = "TLV320AIC3110"; | ||
simple-audio-card,format = "i2s"; | simple-audio-card,format = "i2s"; | ||
simple-audio-card,convert-rate = <48000>; | |||
simple-audio-card,mclk-fs = <256>; | |||
simple-audio-card,bitclock-master = <&snd_codec>; | |||
simple-audio-card,frame-master = <&snd_codec>; | |||
simple-audio-card,widgets = | simple-audio-card,widgets = | ||
"Microphone", "Mic Jack", | "Microphone", "Mic Jack", | ||
"Speaker", "Speaker | "Speaker", "External Speaker"; | ||
simple-audio-card,routing = | simple-audio-card,routing = | ||
"Speaker | "External Speaker", "SPL", | ||
"Speaker | "External Speaker", "SPR", | ||
"MIC1LP", "Mic Jack", | "MIC1LP", "Mic Jack", | ||
"MIC1LM", "Mic Jack"; | "MIC1LM", "Mic Jack"; | ||
status = "okay"; | status = "okay"; | ||
simple-audio-card,cpu { | dailink0_master: simple-audio-card,cpu { | ||
sound-dai = <& | sound-dai = <&i2s_clk_consumer>; | ||
dai-tdm-slot-num = <2>; | |||
dai-tdm-slot-width = <32>; | |||
}; | }; | ||
snd_codec: simple-audio-card,codec { | |||
sound-dai = <&tlv320aic3110>; | sound-dai = <&tlv320aic3110>; | ||
clocks = <&mclk_external>; | clocks = <&mclk_external>; | ||
system-clock-direction-out = "out"; | |||
system-clock-frequency = <12288000>; //*** added LAX | system-clock-frequency = <12288000>; //*** added LAX | ||
mclk-fs = <256>; //*** added LAX | mclk-fs = <256>; //*** added LAX | ||
Line 110: | Line 117: | ||
}; | }; | ||
fragment@ | fragment@4 { | ||
target-path = "/"; | target-path = "/"; | ||
__overlay__ { | __overlay__ { | ||
Line 123: | Line 130: | ||
}; | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Explanation == | |||
After carefully reading the source code, I finally found the culprit of my issues. Even if the datasheet suggest I can bypass the PLL if use a proper frequency for MCLK (I chose 12.288Mhz and 11.2896Mhz which divided by 256 gives 48Khz and 41.1Khz), the device driver does not support any arbitrary frequency! They have a bunch of lookup tables for clock rates and playback and if your choice is not present, the driver will fail. | |||
I modified the source code and added the proper lookups for my custom frequencies | |||
Code: Select all | |||
/* ADC dividers can be disabled by configuring them to 0 */ | |||
static const struct aic31xx_rate_divs aic31xx_divs[] = { | |||
... | |||
/* 11.2896 MHz (11289600 Hz) */ | |||
{ 11289600, 44100, 1, 7, 6800, 128, 8, 2, 128, 8, 2}, | |||
/* 12.288 MHz (12288000 Hz) */ | |||
{ 12288000, 44100, 1, 7, 5264, 128, 8, 2, 128, 8, 2}, | |||
/* 11.2896 MHz (11289600 Hz) */ | |||
{ 11289600, 48000, 1, 7, 6800, 128, 8, 2, 128, 8, 2}, | |||
/* 12.288 MHz (12288000 Hz) */ | |||
{ 12288000, 48000, 1, 8, 1920, 128, 8, 2, 128, 8, 2}, | |||
After a fun fight with Ubuntu to compile this, lo and behold, everything plays! | |||
Now for my project which involves an Iot device, I don't want to have to include an unsigned device driver that will need DKMS to install with every kernel update, I will redo my PCB hardware oscillator to use a natively supported frequency. I leave as an exercise to the people at TI to write a function that dynamically creates a lookup entry for a random frequency MCLK and playback combo! | |||
{{Wiki}} |
Latest revision as of 21:18, 30 January 2025
TLV320AIC3110
Schematics
Device Tree
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2712", "brcm,bcm2711", "brcm,bcm2835";
// compatible = "brcm,bcm2835";
fragment@0 {
target = <&i2s_clk_consumer>;
__overlay__ {
status = "okay";
#sound-dai-cells = <0>;
brcm,tx-channels = <2>;
brcm,rx-channels = <1>;
};
};
fragment@1 {
target-path = "/";
__overlay__ {
status = "okay";
codec_1v8_reg: codec-1v8-reg {
compatible = "regulator-fixed";
regulator-name = "tlv320aic3104_1v8";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-always-on;
status = "okay";
};
};
};
fragment@2 {
target = <&i2c1>;
__overlay__ {
#address-cells = <1>; /* Single cell for I2C address */
#size-cells = <0>; /* No size cells for I2C devices */
status = "okay";
tlv320aic3110: tlv320aic3110@18 {
compatible = "ti-pg,tlv320aic3110", "ti,tlv320aic3110", "ti,tlv320aic311x";
reg = <0x18>;
#sound-dai-cells = <0>;
// system-clock-frequency = <12288000>; // Codec clock config
// system-clock-direction-out; // Codec as master
status = "okay";
HPVDD-supply = <&vdd_3v3_reg>;
SPRVDD-supply = <&vdd_5v0_reg>;
SPLVDD-supply = <&vdd_5v0_reg>;
AVDD-supply = <&vdd_3v3_reg>;
IOVDD-supply = <&vdd_3v3_reg>;
DVDD-supply = <&codec_1v8_reg>;
clocks = <&mclk_external>;
clock-names = "mclk";
// system-clk-frequency = <12288000>;
mclk-frequency = <12288000>;
// gpio-controller;
reset-gpios = <&gpio 13 1>; // GPIO 13 as active low reset
reset-delay-us = <10000>; /* 10ms delay */
// Debug properties (must precede subnodes)
debug;
linux,debug;
};
};
};
fragment@3 {
target = <&sound>;
__overlay__ {
compatible = "simple-audio-card";
i2s-controller = <&i2s_clk_consumer>;
simple-audio-card,name = "TLV320AIC3110";
simple-audio-card,format = "i2s";
simple-audio-card,convert-rate = <48000>;
simple-audio-card,mclk-fs = <256>;
simple-audio-card,bitclock-master = <&snd_codec>;
simple-audio-card,frame-master = <&snd_codec>;
simple-audio-card,widgets =
"Microphone", "Mic Jack",
"Speaker", "External Speaker";
simple-audio-card,routing =
"External Speaker", "SPL",
"External Speaker", "SPR",
"MIC1LP", "Mic Jack",
"MIC1LM", "Mic Jack";
status = "okay";
dailink0_master: simple-audio-card,cpu {
sound-dai = <&i2s_clk_consumer>;
dai-tdm-slot-num = <2>;
dai-tdm-slot-width = <32>;
};
snd_codec: simple-audio-card,codec {
sound-dai = <&tlv320aic3110>;
clocks = <&mclk_external>;
system-clock-direction-out = "out";
system-clock-frequency = <12288000>; //*** added LAX
mclk-fs = <256>; //*** added LAX
};
};
};
fragment@4 {
target-path = "/";
__overlay__ {
mclk_external: mclk_external {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <12288000>; // 12.288 MHz
clock-output-names = "mclk";
};
};
};
};
Explanation
After carefully reading the source code, I finally found the culprit of my issues. Even if the datasheet suggest I can bypass the PLL if use a proper frequency for MCLK (I chose 12.288Mhz and 11.2896Mhz which divided by 256 gives 48Khz and 41.1Khz), the device driver does not support any arbitrary frequency! They have a bunch of lookup tables for clock rates and playback and if your choice is not present, the driver will fail.
I modified the source code and added the proper lookups for my custom frequencies
Code: Select all
/* ADC dividers can be disabled by configuring them to 0 */ static const struct aic31xx_rate_divs aic31xx_divs[] = { ... /* 11.2896 MHz (11289600 Hz) */ { 11289600, 44100, 1, 7, 6800, 128, 8, 2, 128, 8, 2}, /* 12.288 MHz (12288000 Hz) */ { 12288000, 44100, 1, 7, 5264, 128, 8, 2, 128, 8, 2}, /* 11.2896 MHz (11289600 Hz) */ { 11289600, 48000, 1, 7, 6800, 128, 8, 2, 128, 8, 2}, /* 12.288 MHz (12288000 Hz) */ { 12288000, 48000, 1, 8, 1920, 128, 8, 2, 128, 8, 2},
After a fun fight with Ubuntu to compile this, lo and behold, everything plays!
Now for my project which involves an Iot device, I don't want to have to include an unsigned device driver that will need DKMS to install with every kernel update, I will redo my PCB hardware oscillator to use a natively supported frequency. I leave as an exercise to the people at TI to write a function that dynamically creates a lookup entry for a random frequency MCLK and playback combo!
© 2000-2025 Jacques Amar | Amar Micro Inc. |