From e45ae1b58ee6526812d03e13f9ce81d7b904947b Mon Sep 17 00:00:00 2001 From: Pat Nakajima Date: Fri, 17 Apr 2026 11:14:45 -0700 Subject: [PATCH] add some scripts --- dump_tlv320dac3100_registers.py | 2199 +++++++++++++++++++++++++++++++ reinit-codec.sh | 73 + setup-audio.sh | 651 +++++++++ tap-mics.sh | 225 ++++ test-mics.sh | 130 ++ volume.sh | 76 ++ 6 files changed, 3354 insertions(+) create mode 100755 dump_tlv320dac3100_registers.py create mode 100755 reinit-codec.sh create mode 100755 setup-audio.sh create mode 100755 tap-mics.sh create mode 100755 test-mics.sh create mode 100755 volume.sh diff --git a/dump_tlv320dac3100_registers.py b/dump_tlv320dac3100_registers.py new file mode 100755 index 0000000..a7a2c10 --- /dev/null +++ b/dump_tlv320dac3100_registers.py @@ -0,0 +1,2199 @@ +#!/usr/bin/env python3 +"""Dump TLV320DAC3100 registers and decode their current values. + +This script uses register names and bitfield descriptions hard-coded from the +TLV320DAC3100 datasheet (Rev. C). It does not parse the PDF at runtime. + +Behavior: +- reads implemented TLV320DAC3100 pages over I2C +- skips fully reserved registers +- decodes non-reserved bitfields where possible +- groups coefficient pairs and other multi-register values +""" + +from __future__ import annotations + +import argparse +import re +import shlex +import shutil +import subprocess +import sys +from typing import Iterable + +DEFINED_PAGES = [0, 1, 3, 8, 9, 12, 13] + + +class CommandError(RuntimeError): + pass + + +SpecEntry = tuple[int, str] | tuple[int, int, str] + + +def parse_int(text: str) -> int: + return int(text, 0) + + +def parse_pages(spec: str) -> list[int]: + if spec.strip().lower() == "all": + return list(DEFINED_PAGES) + + pages: list[int] = [] + for part in spec.split(","): + part = part.strip() + if not part: + continue + page = parse_int(part) + if page not in DEFINED_PAGES: + valid = ", ".join(str(p) for p in DEFINED_PAGES) + raise argparse.ArgumentTypeError( + f"unsupported page {page}; valid pages: {valid}, or 'all'" + ) + if page not in pages: + pages.append(page) + return pages + + +def add_range(target: dict[int, str], start: int, end: int, description: str) -> None: + for reg in range(start, end + 1): + target[reg] = description + + +def build_page_from_spec(spec: Iterable[SpecEntry], *, default: str = "Reserved") -> dict[int, str]: + page: dict[int, str] = {} + for entry in spec: + if len(entry) == 2: + reg, description = entry + page[reg] = description + else: + start, end, description = entry + add_range(page, start, end, description) + + for reg in range(128): + page.setdefault(reg, default) + return page + + +def build_biquad_page(*, register_1_description: str) -> dict[int, str]: + page = { + 0: "Page Control Register", + 1: register_1_description, + 64: "8 MSBs of 3D PGA gain for PRB_P23, PRB_P24 and PRB_P25", + 65: "8 LSBs of 3D PGA gain for PRB_P23, PRB_P24 and PRB_P25", + } + + biquad_regs = [ + ("N0", "15:8"), + ("N0", "7:0"), + ("N1", "15:8"), + ("N1", "7:0"), + ("N2", "15:8"), + ("N2", "7:0"), + ("D1", "15:8"), + ("D1", "7:0"), + ("D2", "15:8"), + ("D2", "7:0"), + ] + + reg = 2 + for biquad in "ABCDEF": + for coeff, bits in biquad_regs: + page[reg] = f"Coefficient {coeff}({bits}) for left DAC-programmable biquad {biquad}" + reg += 1 + + add_range(page, 62, 63, "Reserved") + + reg = 66 + for biquad in "ABCDEF": + for coeff, bits in biquad_regs: + page[reg] = f"Coefficient {coeff}({bits}) for right DAC-programmable biquad {biquad}" + reg += 1 + + add_range(page, 126, 127, "Reserved") + + for reg in range(128): + page.setdefault(reg, "Reserved") + return page + + +def build_first_order_page() -> dict[int, str]: + page = {0: "Page Control Register", 1: "Reserved"} + + six_regs = [ + ("N0", "15:8"), + ("N0", "7:0"), + ("N1", "15:8"), + ("N1", "7:0"), + ("D1", "15:8"), + ("D1", "7:0"), + ] + + reg = 2 + for coeff, bits in six_regs: + page[reg] = f"Coefficient {coeff}({bits}) for left DAC-programmable first-order IIR" + reg += 1 + + for coeff, bits in six_regs: + page[reg] = f"Coefficient {coeff}({bits}) for right DAC-programmable first-order IIR" + reg += 1 + + for coeff, bits in six_regs: + page[reg] = f"Coefficient {coeff}({bits}) for DRC first-order high-pass filter" + reg += 1 + + for coeff, bits in six_regs: + page[reg] = f"Coefficient {coeff}({bits}) for DRC first-order low-pass filter" + reg += 1 + + add_range(page, 26, 127, "Reserved") + return page + + +PAGE0_SPEC: list[SpecEntry] = [ + (0, "Page Control Register"), + (1, "Software Reset"), + (2, "Reserved"), + (3, "OT FLAG"), + (4, "Clock-Gen Muxing (1)"), + (5, "PLL P and R Values"), + (6, "PLL J-Value"), + (7, "PLL D-Value MSB (1)"), + (8, "PLL D-Value LSB (1)"), + (9, 10, "Reserved"), + (11, "DAC NDAC_VAL"), + (12, "DAC MDAC_VAL"), + (13, "DAC DOSR_VAL MSB"), + (14, "DAC DOSR_VAL LSB (1) (2)"), + (15, 24, "Reserved"), + (25, "CLKOUT MUX"), + (26, "CLKOUT M_VAL"), + (27, "Codec Interface Control 1"), + (28, "Data-Slot Offset Programmability"), + (29, "Codec Interface Control 2"), + (30, "BCLK N_VAL"), + (31, "Codec Secondary Interface Control 1"), + (32, "Codec Secondary Interface Control 2"), + (33, "Codec Secondary Interface Control 3"), + (34, "I2C Bus Condition"), + (35, 36, "Reserved"), + (37, "DAC Flag Register"), + (38, "DAC Flag Register"), + (39, "Overflow Flags"), + (40, 43, "Reserved"), + (44, "DAC Interrupt Flags (Sticky Bits)"), + (45, "Reserved"), + (46, "Interrupt Flags—DAC"), + (47, "Reserved"), + (48, "INT1 Control Register"), + (49, "INT2 Control Register"), + (50, "Reserved"), + (51, "GPIO1 In/Out Pin Control"), + (52, "Reserved"), + (53, "Reserved"), + (54, "DIN (IN Pin) Control"), + (55, 59, "Reserved"), + (60, "DAC Processing Block Selection"), + (61, 62, "Reserved"), + (63, "DAC Data-Path Setup"), + (64, "DAC Volume Control"), + (65, "DAC Left Volume Control"), + (66, "DAC Right Volume Control"), + (67, "Headset Detection"), + (68, "DRC Control 1"), + (69, "DRC Control 2"), + (70, "DRC Control 3"), + (71, "Left Beep Generator (1)"), + (72, "Right Beep Generator"), + (73, "Beep Length MSB"), + (74, "Beep-Length Middle Bits"), + (75, "Beep Length LSB"), + (76, "Beep Sin(x) MSB"), + (77, "Beep Sin(x) LSB"), + (78, "Beep Cos(x) MSB"), + (79, "Beep Cos(x) LSB"), + (80, 115, "Reserved"), + (116, "VOL/MICDET-Pin SAR ADC — Volume Control"), + (117, "VOL/MICDET-Pin Gain"), + (118, 127, "Reserved"), +] + +PAGE1_SPEC: list[SpecEntry] = [ + (0, "Page Control Register"), + (1, 29, "Reserved"), + (30, "Headphone and Speaker Amplifier Error Control"), + (31, "Headphone Drivers"), + (32, "Class-D Speaker Amplifier"), + (33, "HP Output Drivers POP Removal Settings"), + (34, "Output Driver PGA Ramp-Down Period Control"), + (35, "DAC_L and DAC_R Output Mixer Routing"), + (36, "Left Analog Volume to HPL"), + (37, "Right Analog Volume to HPR"), + (38, "Left Analog Volume to SPK"), + (39, "Reserved"), + (40, "HPL Driver"), + (41, "HPR Driver"), + (42, "Class-D Speaker (SPK) Driver"), + (43, "Reserved"), + (44, "HP Driver Control"), + (45, "Reserved"), + (46, "MICBIAS"), + (47, 49, "Reserved / undocumented in Rev. C datasheet"), + (50, "Input CM Settings"), + (51, 127, "Reserved"), +] + +PAGE3_SPEC: list[SpecEntry] = [ + (0, "Page Control Register"), + (1, 15, "Reserved"), + (16, "Timer Clock MCLK Divider"), + (17, 127, "Reserved"), +] + +REGISTER_MAP: dict[int, dict[int, str]] = { + 0: build_page_from_spec(PAGE0_SPEC), + 1: build_page_from_spec(PAGE1_SPEC), + 3: build_page_from_spec(PAGE3_SPEC), + 8: build_biquad_page(register_1_description="DAC Coefficient RAM Control"), + 9: build_first_order_page(), + 12: build_biquad_page(register_1_description="Reserved"), + 13: build_first_order_page(), +} + +REGISTER_FIELDS = {(0, 0): {'fields': [{'bits': 'D7–D0', + 'lines': ['0000 0000: Page 0 selected', + '0000 0001: Page 1 selected', + '...', + '1111 1110: Page 254 selected', + '1111 1111: Page 255 selected'], + 'reset': '0000 0000', + 'rw': 'R/W'}]}, + (0, 1): {'fields': [{'bits': 'D7–D1', + 'lines': ['Reserved. Write only zeros to these bits.'], + 'reset': '0000 000', + 'rw': 'R/W'}, + {'bits': 'D0', + 'lines': ["0: Don't care", '1: Self-clearing software reset for control register'], + 'reset': '0', + 'rw': 'R/W'}]}, + (0, 3): {'fields': [{'bits': 'D7–D2', + 'lines': ['Reserved. Do not write to these bits.'], + 'reset': 'XXXX XX', + 'rw': 'R'}, + {'bits': 'D1', + 'lines': ['0: Overtemperature protection flag (active-low). Valid only if speaker amplifier is ' + 'powered up', + '1: Normal operation'], + 'reset': '1', + 'rw': 'R'}, + {'bits': 'D0', 'lines': ['Reserved. Do not write to these bits.'], 'reset': 'X', 'rw': 'R/W'}]}, + (0, 4): {'fields': [{'bits': 'D7–D4', + 'lines': ['Reserved. Write only zeros to these bits.'], + 'reset': '0000', + 'rw': 'R/W'}, + {'bits': 'D3–D2', + 'lines': ['00: PLL_CLKIN = MCLK (device pin)', + '01: PLL_CLKIN = BCLK (device pin)', + '10: PLL_CLKIN = GPIO1 (device pin)', + '11: PLL_CLKIN = DIN (can be used for the system where DAC is not used)'], + 'reset': '00', + 'rw': 'R/W'}, + {'bits': 'D1–D0', + 'lines': ['00: CODEC_CLKIN = MCLK (device pin)', + '01: CODEC_CLKIN = BCLK (device pin)', + '10: CODEC_CLKIN = GPIO1 (device pin)', + '11: CODEC_CLKIN = PLL_CLK (generated on-chip)', + '(1) See Section 6.3.11 for more details on clock generation mutiplexing and ' + 'dividers.'], + 'reset': '00', + 'rw': 'R/W'}]}, + (0, 5): {'fields': [{'bits': 'D7', + 'lines': ['0: PLL is powered down.', '1: PLL is powered up.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6–D4', + 'lines': ['000: PLL divider P = 8', + '001: PLL divider P = 1', + '010: PLL divider P = 2', + '...', + '110: PLL divider P = 6', + '111: PLL divider P = 7'], + 'reset': '001', + 'rw': 'R/W'}, + {'bits': 'D3–D0', + 'lines': ['0000: PLL multiplier R = 16', + '0001: PLL multiplier R = 1', + '0010: PLL multiplier R = 2', + '...', + '1110: PLL multiplier R = 14', + '1111: PLL multiplier R = 15'], + 'reset': '0001', + 'rw': 'R/W'}]}, + (0, 6): {'fields': [{'bits': 'D7–D6', + 'lines': ['Reserved. Write only zeros to these bits.'], + 'reset': '00', + 'rw': 'R/W'}, + {'bits': 'D5–D0', + 'lines': ['00 0000: Do not use (reserved)', + '00 0001: PLL multiplier J = 1', + '00 0010: PLL multiplier J = 2', + '...', + '11 1110: PLL multiplier J = 62', + '11 1111: PLL multiplier J = 63'], + 'reset': '00 0100', + 'rw': 'R/W'}]}, + (0, 7): {'fields': [{'bits': 'D7–D6', + 'lines': ['Reserved. Write only zeros to these bits.'], + 'reset': '00', + 'rw': 'R/W'}, + {'bits': 'D5–D0', + 'lines': ['PLL fractional multiplier D-value MSB bits D[13:8]', + '(1) Note that this register is updated only when Page 0 / Register 8 is written ' + 'immediately after Page 0 / Register 7.'], + 'reset': '00 0000', + 'rw': 'R/W'}]}, + (0, 8): {'fields': [{'bits': 'D7–D0', + 'lines': ['PLL fractional multiplier D-value LSB bits D[7:0]', + '(1) Note that Page 0 / Register 8 must be written immediately after Page 0 / Register ' + '7.'], + 'reset': '0000 0000', + 'rw': 'R/W'}]}, + (0, 11): {'fields': [{'bits': 'D7', + 'lines': ['0: DAC NDAC divider is powered down.', '1: DAC NDAC divider is powered up.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6–D0', + 'lines': ['000 0000: DAC NDAC divider = 128', + '000 0001: DAC NDAC divider = 1', + '000 0010: DAC NDAC divider = 2', + '...', + '111 1110: DAC NDAC divider = 126', + '111 1111: DAC NDAC divider = 127'], + 'reset': '000 0001', + 'rw': 'R/W'}]}, + (0, 12): {'fields': [{'bits': 'D7', + 'lines': ['0: DAC MDAC divider is powered down.', '1: DAC MDAC divider is powered up.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6–D0', + 'lines': ['000 0000: DAC MDAC divider = 128', + '000 0001: DAC MDAC divider = 1', + '000 0010: DAC MDAC divider = 2', + '...', + '111 1110: DAC MDAC divider = 126', + '111 1111: DAC MDAC divider = 127'], + 'reset': '000 0001', + 'rw': 'R/W'}]}, + (0, 13): {'fields': [{'bits': 'D7–D2', 'lines': ['Reserved'], 'reset': '0000 00', 'rw': 'R/W'}, + {'bits': 'D1–D0', 'lines': ['DAC OSR value DOSR(9:8)'], 'reset': '00', 'rw': 'R/W'}]}, + (0, 14): {'fields': [{'bits': 'D7–D0', + 'lines': ['DAC OSR Value DOSR (7:0)', + '0000 0000: DAC OSR (7:0) = 1024 (MSB page 0 / register 13, bits D1–D0 = 00)', + '0000 0001: Reserved', + '0000 0010: DAC OSR (7:0) = 2 (MSB page 0 / register 13, bits D1–D0 = 00)', + '...', + '1111 1110: DAC OSR (7:0) = 1022 (MSB page 0 / register 13, bits D1–D0 = 11)', + '1111 1111: DAC OSR (7:0) = Reserved. Do not Use', + '(1) DAC OSR must be an integral multiple of the interpolation in the DAC miniDSP ' + 'engine (specified in register 16). When using PRB', + 'modes, interpolation ratio is 8 while using Filter-A, 4 while using Filter-B and 2 ' + 'while using Filter-C.', + '(2) Note that Page 0 / Register 14 must be written to immediately after writing to ' + 'Page 0 / Register 13.'], + 'reset': '1000 0000', + 'rw': 'R/W'}]}, + (0, 25): {'fields': [{'bits': 'D7–D3', 'lines': ['Reserved'], 'reset': '0000 0', 'rw': 'R/W'}, + {'bits': 'D2–D0', + 'lines': ['000: CDIV_CLKIN = MCLK (device pin)', + '001: CDIV_CLKIN = BCLK (device pin)', + '010: CDIV_CLKIN = DIN (can be used for the systems where DAC is not required)', + '011: CDIV_CLKIN = PLL_CLK (generated on-chip)', + '100: CDIV_CLKIN = DAC_CLK (DAC DSP clock - generated on-chip)', + '101: CDIV_CLKIN = DAC_MOD_CLK (generated on-chip)', + '110: Reserved', + '111: Reserved'], + 'reset': '000', + 'rw': 'R/W'}]}, + (0, 26): {'fields': [{'bits': 'D7', + 'lines': ['0: CLKOUT M divider is powered down.', '1: CLKOUT M divider is powered up.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6–D0', + 'lines': ['000 0000: CLKOUT divider M = 128', + '000 0001: CLKOUT divider M = 1', + '000 0010: CLKOUT divider M = 2', + '...', + '111 1110: CLKOUT divider M = 126', + '111 1111: CLKOUT divider M = 127'], + 'reset': '000 0001', + 'rw': 'R/W'}]}, + (0, 27): {'fields': [{'bits': 'D7–D6', + 'lines': ['00: Codec interface = I S', + '01: Codec Interface = DSP', + '10: Codec interface = RJF', + '11: Codec interface = LJF'], + 'reset': '00', + 'rw': 'R/W'}, + {'bits': 'D5–D4', + 'lines': ['00: Codec interface word length = 16 bits', + '01: Codec interface word length = 20 bits', + '10: Codec interface word length = 24 bits', + '11: Codec interface word length = 32 bits'], + 'reset': '00', + 'rw': 'R/W'}, + {'bits': 'D3', 'lines': ['0: BCLK is input', '1: BCLK is output'], 'reset': '0', 'rw': 'R/W'}, + {'bits': 'D2', 'lines': ['0: WCLK is input', '1: WCLK is output'], 'reset': '0', 'rw': 'R/W'}, + {'bits': 'D1–D0', 'lines': ['Reserved'], 'reset': '0', 'rw': 'R/W'}]}, + (0, 28): {'fields': [{'bits': 'D7–D0', + 'lines': ['Offset (Measured With Respect to WCLK Rising Edge in DSP Mode)', + '0000 0000: Offset = 0 BCLKs', + '0000 0001: Offset = 1 BCLK', + '0000 0010: Offset = 2 BCLKs', + '...', + '1111 1110: Offset = 254 BCLKs', + '1111 1111: Offset = 255 BCLKs'], + 'reset': '0000 0000', + 'rw': 'R/W'}]}, + (0, 29): {'fields': [{'bits': 'D7–D4', 'lines': ['Reserved'], 'reset': '0000', 'rw': 'R/W'}, + {'bits': 'D3', + 'lines': ['0: BCLK is not inverted (valid for both primary and secondary BCLK)', + '1: BCLK is inverted (valid for both primary and secondary BCLK)'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D2', + 'lines': ['BCLK and WCLK Active Even With Codec Powered Down (Valid for Both Primary and ' + 'Secondary BCLK)', + '0: Disabled', + '1: Enabled'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D1–D0', + 'lines': ['00: BDIV_CLKIN = DAC_CLK (generated on-chip)', + '01: BDIV_CLKIN = DAC_MOD_CLK (generated on-chip)', + '10: Reserved', + '11: Reserved'], + 'reset': '00', + 'rw': 'R/W'}]}, + (0, 30): {'fields': [{'bits': 'D7', + 'lines': ['0: BCLK N-divider is powered down.', '1: BCLK N-divider is powered up.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6–D0', + 'lines': ['000 0000: BCLK divider N = 128', + '000 0001: BCLK divider N = 1', + '000 0010: BCLK divider N = 2', + '...', + '111 1110: BCLK divider N = 126', + '111 1111: BCLK divider N = 127'], + 'reset': '000 0001', + 'rw': 'R/W'}]}, + (0, 31): {'fields': [{'bits': 'D7–D5', + 'lines': ['000: Secondary BCLK is obtained from GPIO1 pin.', + '001: Secondary BCLK is not obtained from the GPIO1 pin.', + '010: Reserved.', + '011: Reserved.', + '100: Reserved', + '101: Reserved.', + '110: Reserved.', + '111: Reserved'], + 'reset': '000', + 'rw': 'R/W'}, + {'bits': 'D4–D2', + 'lines': ['000: Secondary WCLK is obtained from GPIO1 pin.', + '001: Secondary WCLK is not obtained from the GPIO1 pin.', + '010: Reserved.', + '011: Reserved.', + '100: Reserved', + '101: Reserved.', + '110: Reserved.', + '111: Reserved'], + 'reset': '000', + 'rw': 'R/W'}, + {'bits': 'D1–D0', + 'lines': ['00: Secondary DIN is obtained from the GPIO1 pin.', + '01: Secondary DIN is not obtained from the GPIO1 pin.', + '10: Reserved.', + '11: Reserved'], + 'reset': '00', + 'rw': 'R/W'}]}, + (0, 32): {'fields': [{'bits': 'D7–D4', 'lines': ['Reserved'], 'reset': '0000', 'rw': 'R/W'}, + {'bits': 'D3', + 'lines': ['0: Primary BCLK is fed to codec serial-interface and ClockGen blocks.', + '1: Secondary BCLK is fed to codec serial-interface and ClockGen blocks.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D2', + 'lines': ['0: Primary WCLK is fed to codec serial-interface block.', + '1: Secondary WCLK is fed to codec serial-interface block.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D1', 'lines': ['Reserved.'], 'reset': '0', 'rw': 'R/W'}, + {'bits': 'D0', + 'lines': ['0: Primary DIN is fed to codec serial-interface block.', + '1: Secondary DIN is fed to codec serial-interface block.'], + 'reset': '0', + 'rw': 'R/W'}]}, + (0, 33): {'fields': [{'bits': 'D7', + 'lines': ['0: Primary BCLK output = internally generated BCLK clock', + '1: Primary BCLK output = secondary BCLK'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6', + 'lines': ['0: Secondary BCLK output = primary BCLK', + '1: Secondary BCLK output = internally generated BCLK clock'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D5–D4', + 'lines': ['00: Primary WCLK output = internally generated DAC_fS', + '01: Reserved', + '10: Primary WCLK output = secondary WCLK', + '11: Reserved'], + 'reset': '00', + 'rw': 'R/W'}, + {'bits': 'D3–D2', + 'lines': ['00: Secondary WCLK output = primary WCLK', + '01: Secondary WCLK output = internally generated DAC_fS clock', + '10: Reserved', + '11: Reserved'], + 'reset': '00', + 'rw': 'R/W'}, + {'bits': 'D1–D0', 'lines': ['Reserved'], 'reset': '0', 'rw': 'R/W'}]}, + (0, 34): {'fields': [{'bits': 'D7–D6', + 'lines': ['Reserved. Write only the reset value to these bits.'], + 'reset': '00', + 'rw': 'R/W'}, + {'bits': 'D5', + 'lines': ['0: I2C general-call address is ignored.', + '1: Device accepts I2C general-call address.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D4–D0', + 'lines': ['Reserved. Write only zeros to these bits.'], + 'reset': '0 0000', + 'rw': 'R/W'}]}, + (0, 37): {'fields': [{'bits': 'D7', + 'lines': ['0: Left-channel DAC powered down', '1: Left-channel DAC powered up'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D6', 'lines': ['Reserved.'], 'reset': 'X', 'rw': 'R'}, + {'bits': 'D5', + 'lines': ['0: HPL driver powered down', '1: HPL driver powered up'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D4', + 'lines': ['0: Left-channel class-D driver powered down', + '1: Left-channel class-D driver powered up'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D3', + 'lines': ['0: Right-channel DAC powered down', '1: Right-channel DAC powered up'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D2', 'lines': ['Reserved.'], 'reset': 'X', 'rw': 'R'}, + {'bits': 'D1', + 'lines': ['0: HPR driver powered down', '1: HPR driver powered up'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D0', + 'lines': ['0: Right-channel class-D driver powered down', + '1: Right-channel class-D driver powered up'], + 'reset': '0', + 'rw': 'R'}]}, + (0, 38): {'fields': [{'bits': 'D7–D5', 'lines': ['Reserved.'], 'reset': 'XXX', 'rw': 'R'}, + {'bits': 'D4', + 'lines': ['0: Left-channel DAC PGA applied gain ≠ programmed gain', + '1: Left-channel DAC PGA applied gain = programmed gain'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D3–D1', 'lines': ['Reserved.'], 'reset': 'XXX', 'rw': 'R'}, + {'bits': 'D0', + 'lines': ['0: Right-channel DAC PGA applied gain ≠ programmed gain', + '1: Right-channel DAC PGA applied gain = programmed gain'], + 'reset': '0', + 'rw': 'R'}]}, + (0, 39): {'fields': [{'bits': 'D7 (1)', + 'lines': ['Left-Channel DAC Overflow Flag', + '0: Overflow has not occurred.', + '1: Overflow has occurred.'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D6 (1)', + 'lines': ['Right-Channel DAC Overflow Flag', + '0: Overflow has not occurred.', + '1: Overflow has occurred.'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D5 (1)', + 'lines': ['DAC Barrel Shifter Output Overflow Flag', + '0: Overflow has not occurred.', + '1: Overflow has occurred.'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D4–D0', + 'lines': ['Reserved.', + '(1) Sticky flag bIt. These is a read-only bit. This bit is automatically cleared ' + 'once it is read and is set only if the source trigger occurs again.'], + 'reset': '0', + 'rw': 'R'}]}, + (0, 44): {'fields': [{'bits': 'D7 (1)', + 'lines': ['0: No short circuit is detected at HPL / left class-D driver.', + '1: Short circuit is detected at HPL / left class-D driver.'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D6 (1)', + 'lines': ['0: No short circuit is detected at HPR / right class-D driver.', + '1: Short circuit is detected at HPR / right class-D driver.'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D5 (1)', + 'lines': ['0: No headset button pressed.', '1: Headset button pressed.'], + 'reset': 'X', + 'rw': 'R'}, + {'bits': 'D4 (1)', + 'lines': ['0: No headset insertion or removal is detected.', + '1: Headset insertion or removal is detected.'], + 'reset': 'X', + 'rw': 'R'}, + {'bits': 'D3 (1)', + 'lines': ['0: Left DAC signal power is less than or equal to the signal threshold of DRC.', + '1: Left DAC signal power is above the signal threshold of DRC.'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D2 (1)', + 'lines': ['0: Right DAC signal power is less than or equal to the signal threshold of DRC.', + '1: Right DAC signal power is above the signal threshold of DRC.'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D1–D0', + 'lines': ['Reserved.', + '(1) Sticky flag bIt. These is a read-only bit. This bit is automatically cleared ' + 'once it is read and is set only if the source trigger occurs again.'], + 'reset': '0', + 'rw': 'R'}]}, + (0, 46): {'fields': [{'bits': 'D7', + 'lines': ['0: No short circuit detected at HPL / left class-D driver.', + '1: Short circuit detected at HPL / left class-D driver.'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D6', + 'lines': ['0: No short circuit detected at HPR / right class-D driver', + '1: Short circuit detected at HPR / right class-D driver'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D5', + 'lines': ['0: No headset button pressed.', '1: Headset button pressed.'], + 'reset': 'X', + 'rw': 'R'}, + {'bits': 'D4', + 'lines': ['0: Headset removal detected.', '1: Headset insertion detected.'], + 'reset': 'X', + 'rw': 'R'}, + {'bits': 'D3', + 'lines': ['0: Left DAC signal power is less than or equal to signal threshold of DRC.', + '1: Left DAC signal power is greater than signal threshold of DRC.'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D2', + 'lines': ['0: Right DAC signal power is less than or equal to signal threshold of DRC.', + '1: Right DAC signal power is greater than signal threshold of DRC.'], + 'reset': '0', + 'rw': 'R'}, + {'bits': 'D1–D0', 'lines': ['Reserved.'], 'reset': '00', 'rw': 'R'}]}, + (0, 48): {'fields': [{'bits': 'D7', + 'lines': ['0: Headset-insertion detect interrupt is not used in the generation of INT1 ' + 'interrupt.', + '1: Headset-insertion detect interrupt is used in the generation of INT1 interrupt.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6', + 'lines': ['0: Button-press detect interrupt is not used in the generation of INT1 interrupt.', + '1: Button-press detect interrupt is used in the generation of INT1 interrupt.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D5', + 'lines': ['0: DAC DRC signal-power interrupt is not used in the generation of INT1 interrupt.', + '1: DAC DRC signal-power interrupt is used in the generation of INT1 interrupt.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D4', 'lines': ['Reserved'], 'reset': '0', 'rw': 'R/W'}, + {'bits': 'D3', + 'lines': ['0: Short-circuit interrupt is not used in the generation of INT1 interrupt.', + '1: Short-circuit interrupt is used in the generation of INT1 interrupt.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D2', + 'lines': ['0: DAC data overflow does not result in an INT1 interrupt.', + '1: DAC data overflow results in an INT1 interrupt.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D1', 'lines': ['Reserved'], 'reset': '0', 'rw': 'R/W'}, + {'bits': 'D0', + 'lines': ['0: INT1 is only one pulse (active-high) of typical 2-ms duration.', + '1: INT1 is multiple pulses (active-high) of typical 2-ms duration and 4-ms period, ' + 'until flag register 44 is', + 'read by the user.'], + 'reset': '0', + 'rw': 'R/W'}]}, + (0, 49): {'fields': [{'bits': 'D7', + 'lines': ['0: Headset-insertion detect interrupt is not used in the generation of INT2 ' + 'interrupt.', + '1: Headset-insertion detect interrupt is used in the generation of INT2 interrupt.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6', + 'lines': ['0: Button-press detect interrupt is not used in the generation of INT2 interrupt.', + '1: Button-press detect interrupt is used in the generation of INT2 interrupt.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D5', + 'lines': ['0: DAC DRC signal-power interrupt is not used in the generation of INT2 interrupt.', + '1: DAC DRC signal-power interrupt is used in the generation of INT2 interrupt.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D4', 'lines': ['Reserved'], 'reset': '0', 'rw': 'R/W'}, + {'bits': 'D3', + 'lines': ['0: Short-circuit interrupt is not used in the generation of INT2 interrupt.', + '1: Short-circuit interrupt is used in the generation of INT2 interrupt.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D2', + 'lines': ['0: DAC data overflow does not result in an INT2 interrupt.', + '1: DAC data overflow results in an INT2 interrupt.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D1', 'lines': ['Reserved'], 'reset': '0', 'rw': 'R/W'}, + {'bits': 'D0', + 'lines': ['0: INT2 is only one pulse (active-high) of typical 2-ms duration.', + '1: INT2 is multiple pulses (active-high) of typical 2-ms duration and 4-ms period, ' + 'until flag register 44is', + 'read by the user.'], + 'reset': '0', + 'rw': 'R/W'}]}, + (0, 51): {'fields': [{'bits': 'D7–D6', + 'lines': ['Reserved. Do not write any value other than reset value.'], + 'reset': 'XX', + 'rw': 'R/W'}, + {'bits': 'D5–D2', + 'lines': ['0000: GPIO1 disabled (input and output buffers powered down)', + '0001: GPIO1 is in input mode (can be used as secondary BCLK input, secondary WCLK ' + 'input,', + 'secondary DIN input, or in ClockGen block).', + '0010: GPIO1 is used as general-purpose input (GPI).', + '0011: GPIO1 output = general-purpose output', + '0100: GPIO1 output = CLKOUT output', + '0101: GPIO1 output = INT1 output', + '0110: GPIO1 output = INT2 output', + '0111: Reserved', + '1000: GPIO1 output = secondary BCLK output for codec interface', + '1001: GPIO1 output = secondary WCLK output for codec interface', + '1010: Reserved', + '1011: Reserved', + '1100: Reserved', + '1101: Reserved', + '1110: Reserved', + '1111: Reserved'], + 'reset': '0000', + 'rw': 'R/W'}, + {'bits': 'D1', 'lines': ['GPIO1 input buffer value'], 'reset': 'X', 'rw': 'R'}, + {'bits': 'D0', + 'lines': ['0: GPIO1 general-purpose output value = 0', + '1: GPIO1 general-purpose output value = 1'], + 'reset': '0', + 'rw': 'R/W'}]}, + (0, 54): {'fields': [{'bits': 'D7–D3', 'lines': ['Reserved'], 'reset': '0000 0', 'rw': 'R/W'}, + {'bits': 'D2–D1', + 'lines': ['00: DIN disabled (input buffer powered down)', + '01: DIN enabled (can be used as DIN for codec interface or into ClockGen block)', + '10: DIN is used as general-purpose input (GPI)', + '11: Reserved'], + 'reset': '01', + 'rw': 'R/W'}, + {'bits': 'D0', 'lines': ['DIN input-buffer value'], 'reset': 'X', 'rw': 'R'}]}, + (0, 60): {'fields': [{'bits': 'D7–D5', 'lines': ['Reserved. Write only default value.'], 'reset': '000', 'rw': 'R/W'}, + {'bits': 'D4–D0', + 'lines': ['0 0000: Reserved. Do not use.', + '0 0001: DAC signal-processing block PRB_P1', + '0 0010: DAC signal-processing block PRB_P2', + '0 0011: DAC signal-processing block PRB_P3', + '0 0100: DAC signal-processing block PRB_P4', + '...', + '1 1000: DAC signal-processing block PRB_P24', + '1 1001: DAC signal-processing block PRB_P25', + '1 1010–1 1111: Reserved. Do not use.'], + 'reset': '00 0001', + 'rw': 'R/W'}]}, + (0, 63): {'fields': [{'bits': 'D7', + 'lines': ['0: Left-channel DAC is powered down.', '1: Left-channel DAC is powered up.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6', + 'lines': ['0: Right-channel DAC is powered down.', '1: Right-channel DAC is powered up.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D5–D4', + 'lines': ['00: Left-channel DAC data path = off', + '01: Left-channel DAC data path = left data', + '10: Left-channel DAC data path = right data', + '11: Left-channel DAC data path = left-channel and right-channel data [(L + R) / 2]'], + 'reset': '01', + 'rw': 'R/W'}, + {'bits': 'D3–D2', + 'lines': ['00: Right-channel DAC data path = off', + '01: Right-channel DAC data path = right data', + '10: Right-channel DAC data path = left data', + '11: Right-channel DAC data path = left-channel and right-channel data [(L + R) / 2]'], + 'reset': '01', + 'rw': 'R/W'}, + {'bits': 'D1–D0', + 'lines': ['00: DAC-channel volume-control soft-stepping is enabled for one step per sample ' + 'period.', + '01: DAC-channel volume-control soft-stepping is enabled for one step per two sample ' + 'periods.', + '10: DAC-channel volume-control soft-stepping is disabled.', + '11: Reserved. Do not write this sequence to these bits.'], + 'reset': '00', + 'rw': 'R/W'}]}, + (0, 64): {'fields': [{'bits': 'D7–D4', + 'lines': ['Reserved. Write only zeros to these bits.'], + 'reset': '0000', + 'rw': 'R/W'}, + {'bits': 'D3', + 'lines': ['0: Left-channel DAC not muted', '1: Left-channel DAC muted'], + 'reset': '1', + 'rw': 'R/W'}, + {'bits': 'D2', + 'lines': ['0: Right-channel DAC not muted', '1: Right-channel DAC muted'], + 'reset': '1', + 'rw': 'R/W'}, + {'bits': 'D1–D0', + 'lines': ['00: Left and right channels have independent volume control. (1)', + '01: Left-channel volume control Is the programmed value of right-channel volume ' + 'control.', + '10: Right-channel volume control is the programmed value of left-channel volume ' + 'control.', + '11: Same as 00', + '(1) When DRC is enabled, left and right channel volume controls are always ' + 'independent. Program bits D1–D0 to 00.'], + 'reset': '00', + 'rw': 'R/W'}]}, + (0, 65): {'fields': [{'bits': 'D7–D0', + 'lines': ['Left DAC Channel Digital Volume Control Setting', + '0111 1111–0011 0001: Reserved. Do not use', + '0011 0000: Digital volume control = 24 dB', + '0010 1111: Digital volume control = 23.5 dB', + '0010 1110: Digital volume control = 23 dB', + '...', + '0000 0001: Digital volume control = 0.5 dB', + '0000 0000: Digital volume control = 0 dB', + '1111 1111: Digital volume control = –0.5 dB', + '...', + '1000 0010: Digital volume control = –63 dB', + '1000 0001: Digital volume control = –63.5 dB', + '1000 0000: Reserved.'], + 'reset': '0000 0000', + 'rw': 'R/W'}]}, + (0, 66): {'fields': [{'bits': 'D7–D0', + 'lines': ['Right DAC Channel Digital Volume Control Setting', + '0111 1111–0011 0001: Reserved. Do not use', + '0011 0000: Digital volume control = 24 dB', + '0010 1111: Digital volume control = 23.5 dB', + '0010 1110: Digital volume control = 23 dB', + '...', + '0000 0001: Digital volume control = 0.5 dB', + '0000 0000: Digital volume control = 0 dB', + '1111 1111: Digital volume control = –0.5 dB', + '...', + '1000 0010: Digital volume control = –63 dB', + '1000 0001: Digital volume control = –63.5 dB', + '1000 0000: Reserved.'], + 'reset': '0000 0000', + 'rw': 'R/W'}]}, + (0, 67): {'fields': [{'bits': 'D7', + 'lines': ['0: Headset detection disabled', '1: Headset detection enabled'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6–D5', + 'lines': ['00: No headset detected', + '01: Headset without microphone is detected', + '10: Reserved', + '11: Headset with microphone is detected'], + 'reset': 'XX', + 'rw': 'R'}, + {'bits': 'D4–D2', + 'lines': ['Debounce Programming for Glitch Rejection During Headset Detection (1)', + '000: 16 ms (sampled with 2-ms clock)', + '001: 32 ms (sampled with 4-ms clock)', + '010: 64 ms (sampled with 8-ms clock)', + '011: 128 ms (sampled with 16-ms clock)', + '100: 256 ms (sampled with 32-ms clock)', + '101: 512 ms (sampled with 64-ms clock)', + '110: Reserved', + '111: Reserved'], + 'reset': '000', + 'rw': 'R/W'}, + {'bits': 'D1–D0', + 'lines': ['Debounce programming for glitch rejection during headset button-press detection', + '00: 0 ms', + '01: 8 ms (sampled with 1-ms clock)', + '10: 16 ms (sampled with 2-ms clock)', + '11: 32 ms (sampled with 4-ms clock)', + '(1) Note that these times are generated using the 1 MHz reference clock which is ' + 'defined in Page 3 / Register 16.'], + 'reset': '00', + 'rw': 'R/W'}]}, + (0, 68): {'fields': [{'bits': 'D7', + 'lines': ['Reserved. Write only the reset value to these bits.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6', + 'lines': ['0: DRC disabled for left channel', '1: DRC enabled for left channel'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D5', + 'lines': ['0: DRC disabled for right channel', '1: DRC enabled for right channel'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D4–D2', + 'lines': ['000: DRC threshold = –3 dB', + '001: DRC threshold = –6 dB', + '010: DRC threshold = –9 dB', + '011: DRC threshold = –12 dB', + '100: DRC threshold = –15 dB', + '101: DRC threshold = –18 dB', + '110: DRC threshold = –21 dB', + '111: DRC threshold = –24 dB'], + 'reset': '011', + 'rw': 'R/W'}, + {'bits': 'D1–D0', + 'lines': ['00: DRC hysteresis = 0 dB', + '01: DRC hysteresis = 1 dB', + '10: DRC hysteresis = 2 dB', + '11: DRC hysteresis = 3 dB'], + 'reset': '11', + 'rw': 'R/W'}]}, + (0, 69): {'fields': [{'bits': 'D7', + 'lines': ['Reserved. Write only the reset value to these bits.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6–D3', + 'lines': ['DRC Hold Time', + '0000: DRC Hold Disabled', + '0001: DRC Hold Time = 32 DAC Word Clocks', + '0010: DRC Hold Time = 64 DAC Word Clocks', + '0011: DRC Hold Time = 128 DAC Word Clocks', + '0100: DRC Hold Time = 256 DAC Word Clocks', + '0101: DRC Hold Time = 512 DAC Word Clocks', + '0110: DRC Hold Time = 1024 DAC Word Clocks', + '0111: DRC Hold Time = 2048 DAC Word Clocks', + '1000: DRC Hold Time = 4096 DAC Word Clocks', + '1001: DRC Hold Time = 8192 DAC Word Clocks', + '1010: DRC Hold Time = 16 384 DAC Word Clocks', + '1011: DRC Hold Time = 32 768 DAC Word Clocks', + '1100: DRC Hold Time = 65 536 DAC Word Clocks', + '1101: DRC Hold Time = 98 304 DAC Word Clocks', + '1110: DRC Hold Time = 131 072 DAC Word Clocks', + '1111: DRC Hold Time = 163 840 DAC Word Clocks'], + 'reset': '0111', + 'rw': 'R/W'}, + {'bits': 'D2–D0', + 'lines': ['Reserved. Write only the reset value to these bits.'], + 'reset': '000', + 'rw': 'R'}]}, + (0, 70): {'fields': [{'bits': 'D7–D4', + 'lines': ['0000: DRC attack rate = 4 dB per DAC Word Clock', + '0001: DRC attack rate = 2 dB per DAC word clock', + '0010: DRC attack rate = 1 dB per DAC word clock', + '...', + '1110: DRC attack rate = 2.4414e–5 dB per DAC word clock', + '1111: DRC attack rate = 1.2207e–5 dB per DAC word clock'], + 'reset': '0000', + 'rw': 'R/W'}, + {'bits': 'D3–D0', + 'lines': ['Decay Rate is defined as DR / 2[bits D3-D0 value] dB per DAC Word Clock, where DR = ' + '0.015625 dB', + '0000: DRC decay rate (DR) = 0.015625 dB per DAC Word Clock', + '0001: DRC decay rate = DR / 2 dB per DAC Word Clock', + '0010: DRC decay rate = DR / 22 dB per DAC Word Clock', + '0011: DRC decay rate = DR / 23 dB per DAC Word Clock', + '0100: DRC decay rate = DR / 24 dB per DAC Word Clock', + '0101: DRC decay rate = DR / 25 dB per DAC Word Clock', + '0110: DRC decay rate = DR / 26 dB per DAC Word Clock', + '0111: DRC decay rate = DR / 27 dB per DAC Word Clock', + '1000: DRC decay rate = DR / 28 dB per DAC Word Clock', + '1001: DRC decay rate = DR / 29 dB per DAC Word Clock', + '1010: DRC decay rate = DR / 210 dB per DAC Word Clock', + '1011: DRC decay rate = DR / 211 dB per DAC Word Clock', + '1100: DRC decay rate = DR / 212 dB per DAC Word Clock', + '1101: DRC decay rate = DR / 213 dB per DAC Word Clock', + '1110: DRC decay rate = DR / 214 dB per DAC Word Clock', + '1111: DRC decay rate = DR / 215 dB per DAC Word Clock'], + 'reset': '0000', + 'rw': 'R/W'}]}, + (0, 71): {'fields': [{'bits': 'D7', + 'lines': ['0: Beep generator is disabled.', + '1: Beep generator is enabled (self-clearing based on beep duration).'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6', 'lines': ['Reserved. Write only reset value.'], 'reset': '0', 'rw': 'R/W'}, + {'bits': 'D5–D0', + 'lines': ['00 0000: Left-channel beep volume control = 2 dB', + '00 0001: Left-channel beep volume control = 1 dB', + '00 0010: Left-channel beep volume control = 0 dB', + '00 0011: Left-channel beep volume control = –1 dB', + '...', + '11 1110: Left-channel beep volume control = –60 dB', + '11 1111: Left-channel beep volume control = –61 dB', + '(1) The beep generator is only available in PRB_P25 DAC processing mode.'], + 'reset': '00 0000', + 'rw': 'R/W'}]}, + (0, 72): {'fields': [{'bits': 'D7–D6', + 'lines': ['00: Left and right channels have independent beep volume control.', + '01: Left-channel beep volume control is the programmed value of right-channel beep ' + 'volume control.', + '10: Right-channel beep volume control is the programmed value of left-channel beep ' + 'volume control.', + '11: Same as 00'], + 'reset': '00', + 'rw': 'R/W'}, + {'bits': 'D5–D0', + 'lines': ['00 0000: Right-channel beep volume control = 2 dB', + '00 0001: Right-channel beep volume control = 1 dB', + '00 0010: Right-channel beep volume control = 0 dB', + '00 0011: Right-channel beep volume control = –1 dB', + '...', + '11 1110: Right-channel beep volume control = –60 dB', + '11 1111: Right-channel beep volume control = –61 dB'], + 'reset': '00 0000', + 'rw': 'R/W'}]}, + (0, 73): {'fields': [{'bits': 'D7–D0', + 'lines': ['8 MSBs out of 24 bits for the number of samples for which the beep must be ' + 'generated.'], + 'reset': '0000 0000', + 'rw': 'R/W'}]}, + (0, 74): {'fields': [{'bits': 'D7–D0', + 'lines': ['8 middle bits out of 24 bits for the number of samples for which the beep must be ' + 'generated.'], + 'reset': '0000 0000', + 'rw': 'R/W'}]}, + (0, 75): {'fields': [{'bits': 'D7–D0', + 'lines': ['8 LSBs out of 24 bits for the number of samples for which beep must be generated.'], + 'reset': '1110 1110', + 'rw': 'R/W'}]}, + (0, 76): {'fields': [{'bits': 'D7–D0', + 'lines': ['8 MSBs out of 16 bits for sin(2π × fin / fS), where fin is the beep frequency and fS ' + 'is the DAC sample rate.'], + 'reset': '0001 0000', + 'rw': 'R/W'}]}, + (0, 77): {'fields': [{'bits': 'D7–D0', + 'lines': ['8 LSBs out of 16 bits for sin(2π × fin / fS), where fin is the beep frequency and fS ' + 'is the DAC sample rate.'], + 'reset': '1101 1000', + 'rw': 'R/W'}]}, + (0, 78): {'fields': [{'bits': 'D7–D0', + 'lines': ['8 MSBs out of 16 bits for cos(2π × fin / fS), where fin is the beep frequency and fS ' + 'is the DAC sample', + 'rate.'], + 'reset': '0111 1110', + 'rw': 'R/W'}]}, + (0, 79): {'fields': [{'bits': 'D7–D0', + 'lines': ['8 LSBs out of 16 bits for cos(2π × fin / fS), where fin is the beep frequency and fS ' + 'is the DAC sample rate.'], + 'reset': '1110 0011', + 'rw': 'R/W'}]}, + (0, 116): {'fields': [{'bits': 'D7', + 'lines': ['0: DAC volume control is controlled by control register. (7-bit Vol ADC is powered ' + 'down)', + '1: DAC volume control is controlled by pin.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6', + 'lines': ['0: Internal on-chip RC oscillator is used for the 7-bit Vol ADC for pin volume ' + 'control.', + '1: MCLK is used for the 7-bit Vol ADC for pin volume control.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D5–D4', + 'lines': ['00: No hysteresis for volume control ADC output', + '01: Hysteresis of ±1 bit', + '10: Hysteresis of ±2 bits', + '11: Reserved. Do not write this sequence to these bits.'], + 'reset': '00', + 'rw': 'R/W'}, + {'bits': 'D3', 'lines': ['Reserved. Write only reset value.'], 'reset': '0', 'rw': 'R/W'}, + {'bits': 'D2–D0', + 'lines': ['Throughput of the 7-bit Vol ADC for pin volume control, frequency based on MCLK or ' + 'internal oscillator.', + 'MCLK = 12 MHz Internal Oscillator Source', + '000: Throughput = 15.625 Hz 10.68 Hz', + '001: Throughput = 31.25 Hz 21.35 Hz', + '010: Throughput = 62.5 Hz 42.71 Hz', + '011: Throughput = 125 Hz 8.2 Hz', + '100: Throughput = 250 Hz 170 Hz', + '101: Throughput = 500 Hz 340 Hz', + '110: Throughput = 1 kHz 680 Hz', + '111: Throughput = 2 kHz 1.37 kHz', + 'Note: These values are based on a nominal oscillator', + 'frequency of 8.2 MHz. The values scale to the actual', + 'oscillator frequency.'], + 'reset': '000', + 'rw': 'R/W'}]}, + (0, 117): {'fields': [{'bits': 'D7', 'lines': ['Reserved. Write only zero to this bit.'], 'reset': '0', 'rw': 'R/W'}, + {'bits': 'D6–D0', + 'lines': ['000 0000: Gain applied by pin volume control = 18 dB', + '000 0001: Gain applied by pin volume control = 17.5 dB', + '000 0010: Gain applied by pin volume control = 17 dB', + '...', + '010 0011: Gain applied by pin volume control = 0.5 dB', + '010 0100: Gain applied by pin volume control = 0 dB', + '010 0101: Gain applied by pin volume control = –0.5 dB', + '...', + '101 1001: Gain applied by pin volume control = –26.5 dB', + '101 1010: Gain applied by pin volume control = –27 dB', + '101 1011: Gain applied by pin volume control = –28 dB', + '...', + '111 1101: Gain applied by pin volume control = –62 dB', + '111 1110: Gain applied by pin volume control = –63 dB', + '111 1111: Reserved.'], + 'reset': 'XXX XXXX', + 'rw': 'R'}]}, + (1, 0): {'fields': [{'bits': 'D7–D0', + 'lines': ['0000 0000: Page 0 selected', + '0000 0001: Page 1 selected', + '...', + '1111 1110: Page 254 selected', + '1111 1111: Page 255 selected'], + 'reset': '0000 0000', + 'rw': 'R/W'}]}, + (1, 30): {'fields': [{'bits': 'D7–D2', 'lines': ['Reserved'], 'reset': '0000 00', 'rw': 'R/W'}, + {'bits': 'D1', + 'lines': ['0: Reset SPK power-up control bits on short-circuit detection.', + '1: SPK power-up control bits remain unchanged on short-circuit detection.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D0', + 'lines': ['0: Reset HPL and HPR power-up control bits on short-circuit detection if page 1 / ' + 'register 31, D1 = 1.', + '1: HPL and HPR power-up control bits remain unchanged on short-circuit detection.'], + 'reset': '0', + 'rw': 'R/W'}]}, + (1, 31): {'fields': [{'bits': 'D7', + 'lines': ['0: HPL output driver is powered down.', '1: HPL output driver is powered up.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6', + 'lines': ['0: HPR output driver is powered down.', '1: HPR output driver is powered up.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D5', 'lines': ['Reserved. Write only zero to this bit.'], 'reset': '0', 'rw': 'R/W'}, + {'bits': 'D4–D3', + 'lines': ['00: Output common-mode voltage = 1.35 V', + '01: Output common-mode voltage = 1.5 V', + '10: Output common-mode voltage = 1.65 V', + '11: Output common-mode voltage = 1.8 V'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D2', 'lines': ['Reserved. Write only 1 to this bit.'], 'reset': '1', 'rw': 'R/W'}, + {'bits': 'D1', + 'lines': ['0: If short-circuit protection is enabled for headphone driver and short circuit ' + 'detected, device limits the', + 'maximum current to the load.', + '1: If short-circuit protection is enabled for headphone driver and short circuit ' + 'detected, device powers', + 'down the output driver.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D0', + 'lines': ['0: Short circuit is not detected on the headphone driver.', + '1: Short circuit is detected on the headphone driver.'], + 'reset': '0', + 'rw': 'R'}]}, + (1, 32): {'fields': [{'bits': 'D7', + 'lines': ['0: Class-D output driver is powered down.', + '1: Class-D output driver is powered up.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6–D1', + 'lines': ['Reserved. Write only the reset value to this bit.'], + 'reset': '000 011', + 'rw': 'R/W'}, + {'bits': 'D0', + 'lines': ['0: Short circuit is not detected on the class-D driver. Valid only if class-D ' + 'amplifier is powered up. For', + 'short-circuit flag sticky bit, see page 0 / register 44.', + '1: Short circuit is detected on the class-D driver. Valid only if class-D amp is ' + 'powered-up. For short-', + 'circuit flag sticky bit, see page 0 / register 44.'], + 'reset': '0', + 'rw': 'R'}]}, + (1, 33): {'fields': [{'bits': 'D7', + 'lines': ['0: If the power down sequence is activated by device software, power down using page ' + '1 / register 46,', + 'bit D7, then power down the DAC simultaneously with the HP and SP amplifiers.', + '1: If the power down sequence is activated by device software, power down using page ' + '1 / register 46,', + 'bit D7, then power down DAC only after HP and SP amplifiers are completely powered ' + 'down. This is to', + 'optimize power-down POP.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6–D3', + 'lines': ['0000: Driver power-on time = 0 μs', + '0001: Driver power-on time = 15.3 μs', + '0010: Driver power-on time = 153 μs', + '0011: Driver power-on time = 1.53 ms', + '0100: Driver power-on time = 15.3 ms', + '0101: Driver power-on time = 76.2 ms', + '0110: Driver power-on time = 153 ms', + '0111: Driver power-on time = 304 ms', + '1000: Driver power-on time = 610 ms', + '1001: Driver power-on time = 1.22 s', + '1010: Driver power-on time = 3.04 s', + '1011: Driver power-on time = 6.1 s', + '1100–1111: Reserved. Do not write these sequences to these bits.', + 'Note: These values are based on typical oscillator frequency of 8.2 MHz. Scale ' + 'according to the actual', + 'oscillator frequency.'], + 'reset': '0111', + 'rw': 'R/W'}, + {'bits': 'D2–D1', + 'lines': ['00: Driver ramp-up step time = 0 ms', + '01: Driver ramp-up step time = 0.98 ms', + '10: Driver ramp-up step time = 1.95 ms', + '11: Driver ramp-up step time = 3.9 ms', + 'Note: These values are based on typical oscillator frequency of 8.2 MHz. Scale ' + 'according to the actual', + 'oscillator frequency.'], + 'reset': '11', + 'rw': 'R/W'}, + {'bits': 'D0', + 'lines': ['0: Weakly driven output common-mode voltage is generated from resistor divider of ' + 'the AVDD supply.', + '1: Reserved'], + 'reset': '0', + 'rw': 'R/W'}]}, + (1, 34): {'fields': [{'bits': 'D7', + 'lines': ['Reserved. Write only the reset value to this bit.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6–D4', + 'lines': ['Speaker power-up wait time (duration based on using internal oscillator)', + '000: Wait time = 0 ms', + '001: Wait time = 3.04 ms', + '010: Wait time = 7.62 ms', + '011: Wait time = 12.2 ms', + '100: Wait time = 15.3 ms', + '101: Wait time = 19.8 ms', + '110: Wait time = 24.4 ms', + '111: Wait time = 30.5 ms', + 'Note: These values are based on typical oscillator frequency of 8.2 MHz. Scale ' + 'according to the actual', + 'oscillator frequency.'], + 'reset': '000', + 'rw': 'R/W'}, + {'bits': 'D3–D0', + 'lines': ['Reserved. Write only the reset value to these bits.'], + 'reset': '0000', + 'rw': 'R/W'}]}, + (1, 35): {'fields': [{'bits': 'D7–D6', + 'lines': ['00: DAC_L is not routed anywhere.', + '01: DAC_L is routed to the left-channel mixer amplifier.', + '10: DAC_L is routed directly to the HPL driver.', + '11: Reserved'], + 'reset': '00', + 'rw': 'R/W'}, + {'bits': 'D5', + 'lines': ['0: AIN1 input is not routed to the left-channel mixer amplifier.', + '1: AIN1 input is routed to the left-channel mixer amplifier.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D4', + 'lines': ['0: AIN2 input is not routed to the left-channel mixer amplifier.', + '1: AIN2 input is routed to the left-channel mixer amplifier.'], + 'reset': '0', + 'rw': ''}, + {'bits': 'D3–D2', + 'lines': ['00: DAC_R is not routed anywhere.', + '01: DAC_R is routed to the right-channel mixer amplifier.', + '10: DAC_R is routed directly to the HPR driver.', + '11: Reserved'], + 'reset': '00', + 'rw': 'R/W'}, + {'bits': 'D1', + 'lines': ['0: AIN2 input is not routed to the right-channel mixer amplifier.', + '1: AIN2 input is routed to the right-channel mixer amplifier.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D0', + 'lines': ['0: HPL driver output is not routed to the HPR driver.', + '1: HPL driver output is routed to the HPR driver input (used for differential output ' + 'mode).'], + 'reset': '0', + 'rw': 'R/W'}]}, + (1, 36): {'fields': [{'bits': 'D7', + 'lines': ['0: Left-channel analog volume control is not routed to HPL output driver.', + '1: Left-channel analog volume control is routed to HPL output driver.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6–D0', + 'lines': ['Left-channel analog volume control gain (non-linear) for the HPL output driver, 0 dB ' + 'to –78 dB. See'], + 'reset': '111 1111', + 'rw': 'R/W'}]}, + (1, 37): {'fields': [{'bits': 'D7', + 'lines': ['0: Right-channel analog volume control is not routed to HPR output driver.', + '1: Right-channel analog volume control is routed to HPR output driver.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6–D0', + 'lines': ['Right-channel analog volume control gain (non-linear) for the HPR output driver, 0 ' + 'dB to –78 dB. See'], + 'reset': '111 1111', + 'rw': 'R/W'}]}, + (1, 38): {'fields': [{'bits': 'D7', + 'lines': ['0: Left-channel analog volume control output is not routed to class-D output driver.', + '1: Left-channel analog volume control output is routed to class-D output driver.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6–D0', + 'lines': ['Left-channel analog volume control output gain (non-linear) for the class-D output ' + 'driver, 0 dB to –78', + 'dB. See Table 6-24.'], + 'reset': '111 1111', + 'rw': 'R/W'}]}, + (1, 40): {'fields': [{'bits': 'D7', 'lines': ['Reserved. Write only zero to this bit.'], 'reset': '0', 'rw': 'R/W'}, + {'bits': 'D6–D3', + 'lines': ['0000: HPL driver PGA = 0 dB', + '0001: HPL driver PGA = 1 dB', + '0010: HPL driver PGA = 2 dB', + '...', + '1000: HPL driver PGA = 8 dB', + '1001: HPL driver PGA = 9 dB', + '1010–1111: Reserved. Do not write these sequences to these bits.'], + 'reset': '0000', + 'rw': 'R/W'}, + {'bits': 'D2', + 'lines': ['0: HPL driver is muted.', '1: HPL driver is not muted.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D1', 'lines': ['Reserved'], 'reset': '1', 'rw': 'R/W'}, + {'bits': 'D0', + 'lines': ['0: Not all programmed gains to HPL have been applied yet.', + '1: All programmed gains to HPL have been applied.'], + 'reset': '0', + 'rw': 'R'}]}, + (1, 41): {'fields': [{'bits': 'D7', 'lines': ['Reserved. Write only zero to this bit.'], 'reset': '0', 'rw': 'R/W'}, + {'bits': 'D6–D3', + 'lines': ['0000: HPR driver PGA = 0 dB', + '0001: HPR driver PGA = 1 dB', + '0010: HPR driver PGA = 2 dB', + '...', + '1000: HPR driver PGA = 8 dB', + '1001: HPR driver PGA = 9 dB', + '1010–1111: Reserved. Do not write these sequences to these bits.'], + 'reset': '0000', + 'rw': 'R/W'}, + {'bits': 'D2', + 'lines': ['0: HPR driver is muted.', '1: HPR driver is not muted.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D1', 'lines': ["Reserved. Write only '1' to this bit."], 'reset': '1', 'rw': 'R/W'}, + {'bits': 'D0', + 'lines': ['0: Not all programmed gains to HPR have been applied yet.', + '1: All programmed gains to HPR have been applied.'], + 'reset': '0', + 'rw': 'R'}]}, + (1, 42): {'fields': [{'bits': 'D7–D5', + 'lines': ['Reserved. Write only zeros to these bits.'], + 'reset': '000', + 'rw': 'R/W'}, + {'bits': 'D4–D3', + 'lines': ['00: Class-D driver output stage gain = 6 dB', + '01: Class-D driver output stage gain = 12 dB', + '10: Class-D driver output stage gain = 18 dB', + '11: Class-D driver output stage gain = 24 dB'], + 'reset': '00', + 'rw': 'R/W'}, + {'bits': 'D2', + 'lines': ['0: Class-D driver is muted.', '1: Class-D driver is not muted.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D1', 'lines': ['Reserved. Write only zero to this bit.'], 'reset': '0', 'rw': 'R/W'}, + {'bits': 'D0', + 'lines': ['0: Not all programmed gains to the Class-D driver have been applied yet.', + '1: All programmed gains to the Class-D driver have been applied.'], + 'reset': '0', + 'rw': 'R'}]}, + (1, 44): {'fields': [{'bits': 'D7–D5', + 'lines': ['Debounce time for the headset short-circuit detection', + 'MCLK/DIV (Page 3 /', + '(1)', + 'register 16) = 1-MHz Internal Oscillator Source', + 'Source', + '000: Debounce time = 0 μs 0 μs', + '001: Debounce time = 8 μs 7.8 μs', + '010: Debounce time = 16 μs 15.6 μs', + '011: Debounce time = 32 μs 31.2 μs', + '100: Debounce time = 64 μs 62.4 μs', + '101: Debounce time = 128 μs 124.9 μs', + '110: Debounce time = 256 μs 250 μs', + '111: Debounce time = 512 μs 500 μs', + 'Note: These values are based on a nominal oscillator', + 'frequency of 8.2 MHz. The values scale to the actual', + 'oscillator frequency.'], + 'reset': '000', + 'rw': 'R/W'}, + {'bits': 'D4–D3', + 'lines': ['00: Default mode for the DAC', + '01: DAC performance increased by increasing the current', + '10: Reserved', + '11: DAC performance increased further by increasing the current again', + '(1) The clock used for the debounce has a clock period = debounce duration / 8.'], + 'reset': '00', + 'rw': 'R/W'}, + {'bits': 'D2', + 'lines': ['0: HPL output driver is programmed as headphone driver.', + '1: HPL output driver is programmed as lineout driver.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D1', + 'lines': ['0: HPR output driver is programmed as headphone driver.', + '1: HPR output driver is programmed as lineout driver.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D0', 'lines': ['Reserved. Write only zero to this bit.'], 'reset': '0', 'rw': 'R/W'}]}, + (1, 46): {'fields': [{'bits': 'D7', + 'lines': ['0: Device software power down is not enabled.', + '1: Device software power down is enabled.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6–D4', + 'lines': ['Reserved. Write only zeros to these bits.'], + 'reset': '000', + 'rw': 'R/W'}, + {'bits': 'D3', + 'lines': ['0: Programmed MICBIAS is not powered up if headset detection is enabled but headset ' + 'is not inserted.', + '1: Programmed MICBIAS is powered up even if headset is not inserted.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D2', 'lines': ['Reserved. Write only zero to this bit.'], 'reset': '0', 'rw': 'R/W'}, + {'bits': 'D1–D0', + 'lines': ['00: MICBIAS output is powered down.', + '01: MICBIAS output is powered to 2 V.', + '10: MICBIAS output is powered to 2.5 V.', + '11: MICBIAS output is powered to AVDD.'], + 'reset': '00', + 'rw': 'R/W'}]}, + (1, 50): {'fields': [{'bits': 'D7', + 'lines': ['0: AIN1 input is floating, if it is not used for the analog bypass.', + '1: AIN1 input is connected to CM internally, if it is not used for the analog ' + 'bypass.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D6', + 'lines': ['0: AIN2 input is floating, if it is not used for the analog bypass.', + '1: AIN2 input is connected to CM internally, if it is not used for the analog ' + 'bypass.'], + 'reset': '0', + 'rw': 'R/W'}, + {'bits': 'D5–D0', + 'lines': ['Reserved. Write only zeros to these bits.'], + 'reset': '00 0000', + 'rw': 'R/W'}]}, + (3, 0): {'fields': [{'bits': 'D7–D0', + 'lines': ['0000 0000: Page 0 selected', + '0000 0001: Page 1 selected', + '...', + '1111 1110: Page 254 selected', + '1111 1111: Page 255 selected', + 'The only register used in page 3 is register 16. The remaining page-3 registers are ' + 'reserved and must not', + 'be written to.'], + 'reset': '0000 0000', + 'rw': 'R/W'}]}, + (3, 16): {'fields': [{'bits': 'D7', + 'lines': ['0: Internal oscillator is used for programmable delay timer.', + '1: External MCLK (1) is used for programmable delay timer.'], + 'reset': '1', + 'rw': 'R/W'}, + {'bits': 'D6–D0', + 'lines': ['MCLK Divider to Generate 1-MHz Clock for the Programmable Delay Timer', + '000 0000: MCLK divider = 128', + '000 0001: MCLK divider = 1', + '000 0010: MCLK divider = 2', + '...', + '111 1110: MCLK divider = 126', + '111 1111: MCLK divider = 127', + '(1) External clock is used only to control the delay programmed between the ' + 'conversions and not used for doing the actual conversion. This', + 'feature is provided in case a more accurate delay is desired because the internal ' + 'oscillator frequency varies from device to device.', + 'Default values shown for this page only become valid 100 μs following a hardware or ' + 'software reset.'], + 'reset': '000 0001', + 'rw': 'R/W'}]}} + +ANALOG_VOLUME_DB = [0.0, + -0.5, + -1.0, + -1.5, + -2.0, + -2.5, + -3.0, + -3.5, + -4.0, + -4.5, + -5.0, + -5.5, + -6.0, + -6.5, + -7.0, + -7.5, + -8.0, + -8.5, + -9.0, + -9.5, + -10.0, + -10.5, + -11.0, + -11.5, + -12.0, + -12.5, + -13.0, + -13.5, + -14.0, + -14.5, + -15.0, + -15.5, + -16.0, + -16.5, + -17.0, + -17.5, + -18.1, + -18.6, + -19.1, + -19.6, + -20.1, + -20.6, + -21.1, + -21.6, + -22.1, + -22.6, + -23.1, + -23.6, + -24.1, + -24.6, + -25.1, + -25.6, + -26.1, + -26.6, + -27.1, + -27.6, + -28.1, + -28.6, + -29.1, + -29.6, + -30.1, + -30.6, + -31.1, + -31.6, + -32.1, + -32.6, + -33.1, + -33.6, + -34.1, + -34.6, + -35.2, + -35.7, + -36.2, + -36.7, + -37.2, + -37.7, + -38.2, + -38.7, + -39.2, + -39.7, + -40.2, + -40.7, + -41.2, + -41.7, + -42.1, + -42.7, + -43.2, + -43.8, + -44.3, + -44.8, + -45.2, + -45.8, + -46.2, + -46.7, + -47.4, + -47.9, + -48.2, + -48.7, + -49.3, + -50.0, + -50.3, + -51.0, + -51.4, + -51.8, + -52.2, + -52.7, + -53.7, + -54.2, + -55.3, + -56.7, + -58.3, + -60.2, + -62.7, + -64.3, + -66.2, + -68.7, + -72.2, + -78.3, + -78.3, + -78.3, + -78.3, + -78.3, + -78.3, + -78.3, + -78.3, + -78.3, + -78.3, + -78.3] + +PAGE_CONTROL_FIELDS = [ + { + "bits": "D7–D0", + "rw": "R/W", + "reset": "0000 0000", + "lines": [ + "0000 0000: Page 0 selected", + "0000 0001: Page 1 selected", + "...", + "1111 1110: Page 254 selected", + "1111 1111: Page 255 selected", + ], + } +] + +for page in (8, 9, 12, 13): + REGISTER_FIELDS[(page, 0)] = {"fields": PAGE_CONTROL_FIELDS} + +REGISTER_FIELDS[(8, 1)] = { + "fields": [ + { + "bits": "D7–D3", + "rw": "R/W", + "reset": "0000 0", + "lines": ["Reserved. Write only the reset value."], + }, + { + "bits": "D2", + "rw": "R/W", + "reset": "0", + "lines": [ + "0: Adaptive filtering disabled in DAC processing block", + "1: Adaptive filtering enabled in DAC processing block", + ], + }, + { + "bits": "D1", + "rw": "R", + "reset": "0", + "lines": [ + "0: In adaptive filter mode, DAC processing block accesses DAC coefficient Buffer A and the external control interface accesses DAC coefficient Buffer B", + "1: In adaptive filter mode, DAC processing block accesses DAC coefficient Buffer B and the external control interface accesses DAC coefficient Buffer A", + ], + }, + { + "bits": "D0", + "rw": "R/W", + "reset": "0", + "lines": [ + "0: DAC coefficient buffers are not switched at the next frame boundary.", + "1: DAC coefficient buffers are switched at the next frame boundary, if adaptive filtering mode is enabled. This bit self-clears on switching.", + ], + }, + ] +} + +PAGE0_GROUPS = { + 7: {"registers": (7, 8), "name": "PLL fractional multiplier D-value", "kind": "pll_d"}, + 13: {"registers": (13, 14), "name": "DAC DOSR value", "kind": "dosr"}, + 73: {"registers": (73, 74, 75), "name": "Beep length", "kind": "beep_length"}, + 76: {"registers": (76, 77), "name": "Beep sin(x)", "kind": "s16"}, + 78: {"registers": (78, 79), "name": "Beep cos(x)", "kind": "s16"}, +} +PAGE0_GROUPED_REGS = {reg for group in PAGE0_GROUPS.values() for reg in group["registers"]} + +BINARY_ENUM_RANGE_RE = re.compile(r"^([01 ]+)[–-]([01 ]+):\s*(.+)$") +BINARY_ENUM_EXACT_RE = re.compile(r"^([01 ]+):\s*(.+)$") +PAIR_COEFF_MSB_RE = re.compile(r"^Coefficient ([A-Z0-9]+)\(15:8\) for (.+)$") +PAIR_COEFF_LSB_RE = re.compile(r"^Coefficient ([A-Z0-9]+)\(7:0\) for (.+)$") +PAIR_MSB_RE = re.compile(r"^8 MSBs of (.+)$") +PAIR_LSB_RE = re.compile(r"^8 LSBs of (.+)$") + + +def run_command(argv: list[str], *, check: bool = True) -> subprocess.CompletedProcess[str]: + proc = subprocess.run(argv, capture_output=True, text=True) + if check and proc.returncode != 0: + stderr = proc.stderr.strip() + stdout = proc.stdout.strip() + detail = stderr or stdout or f"exit status {proc.returncode}" + raise CommandError(f"{shlex.join(argv)} failed: {detail}") + return proc + + +def select_page(bus: int, addr: int, page: int) -> None: + run_command([ + "i2cset", + "-y", + "-f", + str(bus), + f"0x{addr:02x}", + "0x00", + f"0x{page:02x}", + ]) + + +def read_register(bus: int, addr: int, reg: int) -> int: + proc = run_command([ + "i2cget", + "-y", + "-f", + str(bus), + f"0x{addr:02x}", + f"0x{reg:02x}", + ]) + match = re.search(r"0x([0-9A-Fa-f]{1,2})(?:\s|$)", proc.stdout) + if not match: + raise CommandError(f"unexpected i2cget output: {proc.stdout.strip()!r}") + return int(match.group(1), 16) + + +def read_page(bus: int, addr: int) -> list[int]: + i2ctransfer = shutil.which("i2ctransfer") + if i2ctransfer: + values: list[int] = [] + for start in range(0, 128, 32): + proc = run_command([ + i2ctransfer, + "-y", + "-f", + str(bus), + f"w1@0x{addr:02x}", + f"0x{start:02x}", + "r32", + ]) + tokens = re.findall(r"0x([0-9A-Fa-f]{2})(?:\s|$)", proc.stdout) + if len(tokens) != 32: + raise CommandError( + "unexpected i2ctransfer output while reading a page: " + f"{proc.stdout.strip()!r}" + ) + values.extend(int(token, 16) for token in tokens) + return values + + return [read_register(bus, addr, reg) for reg in range(128)] + + +def is_reserved_register(page: int, reg: int) -> bool: + return REGISTER_MAP[page][reg].startswith("Reserved") + + +def normalize_bits(bits: str) -> str: + return bits.replace("-", "–") + + +def bit_range(bits: str) -> tuple[int, int]: + cleaned = re.sub(r"\s*\(\d+\)", "", normalize_bits(bits)) + match = re.match(r"^D(\d+)(?:[–-]D?(\d+))?$", cleaned) + if not match: + raise ValueError(f"unsupported bit spec: {bits!r}") + hi = int(match.group(1)) + lo = int(match.group(2) or match.group(1)) + if hi < lo: + hi, lo = lo, hi + return hi, lo + + +def bit_width(bits: str) -> int: + hi, lo = bit_range(bits) + return hi - lo + 1 + + +def extract_bits(value: int, bits: str) -> int: + hi, lo = bit_range(bits) + return (value >> lo) & ((1 << (hi - lo + 1)) - 1) + + +def format_bit_value(value: int, width: int) -> str: + return format(value, f"0{width}b") + + +def to_signed16(value: int) -> int: + return value - 0x10000 if value & 0x8000 else value + + +def format_db(value: float) -> str: + return f"{value:g} dB" + + +def field_is_reserved(field: dict[str, object]) -> bool: + lines = field.get("lines", []) + if not lines: + return False + first = str(lines[0]).strip() + return first.startswith("Reserved") + + +def decode_special_field(page: int, reg: int, bits: str, raw: int) -> str | None: + key = (page, reg, normalize_bits(bits)) + + if reg == 0 and normalize_bits(bits) == "D7–D0": + return f"Page {raw} selected" + + if key == (0, 5, "D6–D4"): + return f"PLL divider P = {8 if raw == 0 else raw}" + if key == (0, 5, "D3–D0"): + return f"PLL multiplier R = {16 if raw == 0 else raw}" + if key == (0, 6, "D5–D0"): + return "Reserved. Do not use" if raw == 0 else f"PLL multiplier J = {raw}" + if key == (0, 11, "D6–D0"): + return f"DAC NDAC divider = {128 if raw == 0 else raw}" + if key == (0, 12, "D6–D0"): + return f"DAC MDAC divider = {128 if raw == 0 else raw}" + if key == (0, 26, "D6–D0"): + return f"CLKOUT divider M = {128 if raw == 0 else raw}" + if key == (0, 28, "D7–D0"): + suffix = "" if raw == 1 else "s" + return f"Offset = {raw} BCLK{suffix}" + if key == (0, 30, "D6–D0"): + return f"BCLK divider N = {128 if raw == 0 else raw}" + if key == (0, 60, "D4–D0"): + if 1 <= raw <= 25: + return f"DAC signal-processing block PRB_P{raw}" + return "Reserved. Do not use" + if key in {(0, 65, "D7–D0"), (0, 66, "D7–D0")}: + if raw == 0x80: + return "Reserved" + if 0x00 <= raw <= 0x30: + return f"Digital volume control = {format_db(raw * 0.5)}" + if 0x81 <= raw <= 0xFF: + return f"Digital volume control = {format_db(-0.5 * (0x100 - raw))}" + return "Reserved. Do not use" + if key == (0, 70, "D7–D4"): + return f"DRC attack rate = {4 / (2 ** raw):g} dB per DAC word clock" + if key == (0, 71, "D5–D0"): + return f"Left-channel beep volume control = {format_db(2 - raw)}" + if key == (0, 72, "D5–D0"): + return f"Right-channel beep volume control = {format_db(2 - raw)}" + if key == (0, 117, "D6–D0"): + return "Reserved" if raw == 0x7F else f"Gain applied by pin volume control = {format_db(18 - 0.5 * raw)}" + if key == (1, 36, "D6–D0"): + return f"Left-channel analog gain to HPL = {format_db(ANALOG_VOLUME_DB[raw])}" + if key == (1, 37, "D6–D0"): + return f"Right-channel analog gain to HPR = {format_db(ANALOG_VOLUME_DB[raw])}" + if key == (1, 38, "D6–D0"): + return f"Left-channel analog gain to SPK = {format_db(ANALOG_VOLUME_DB[raw])}" + if key == (1, 40, "D6–D3"): + return "Reserved. Do not write this sequence" if raw > 9 else f"HPL driver PGA = {format_db(raw)}" + if key == (1, 41, "D6–D3"): + return "Reserved. Do not write this sequence" if raw > 9 else f"HPR driver PGA = {format_db(raw)}" + if key == (3, 16, "D6–D0"): + return f"MCLK divider = {128 if raw == 0 else raw}" + + return None + + +def parse_enum_lines(lines: list[str]) -> tuple[list[str], list[tuple[int, int, str]]]: + intros: list[str] = [] + enums_mutable: list[list[object]] = [] + for line in lines: + cleaned = " ".join(str(line).split()) + if not cleaned or cleaned == "...": + continue + if cleaned.startswith("(") or cleaned.startswith("Note:") or re.match(r"^6\.\d", cleaned): + continue + if cleaned.startswith("The only register used") or cleaned.startswith("The remaining"): + continue + if cleaned.startswith("Default values shown"): + continue + + match = BINARY_ENUM_RANGE_RE.match(cleaned) + if match: + start = int(match.group(1).replace(" ", ""), 2) + end = int(match.group(2).replace(" ", ""), 2) + enums_mutable.append([start, end, match.group(3)]) + continue + + match = BINARY_ENUM_EXACT_RE.match(cleaned) + if match: + code = int(match.group(1).replace(" ", ""), 2) + enums_mutable.append([code, code, match.group(2)]) + continue + + if enums_mutable: + enums_mutable[-1][2] = f"{enums_mutable[-1][2]} {cleaned}".strip() + else: + intros.append(cleaned) + + enums = [(int(start), int(end), str(text)) for start, end, text in enums_mutable] + return intros, enums + + +def decode_field(page: int, reg: int, field: dict[str, object], value: int) -> str | None: + bits = str(field["bits"]) + raw = extract_bits(value, bits) + + special = decode_special_field(page, reg, bits, raw) + if special is not None: + return special + + intros, enums = parse_enum_lines(list(field.get("lines", []))) + for start, end, text in enums: + if start <= raw <= end: + return text + + if intros: + return "; ".join(intros) + + return None + + +def print_register_value(page: int, reg: int, value: int) -> None: + name = REGISTER_MAP[page][reg] + print(f"P{page:02d} R{reg:03d} (0x{reg:02X}) = 0x{value:02X} {name}") + info = REGISTER_FIELDS.get((page, reg)) + if not info: + return + for field in info.get("fields", []): + if field_is_reserved(field): + continue + bits = str(field["bits"]) + raw = extract_bits(value, bits) + width = bit_width(bits) + meaning = decode_field(page, reg, field, value) + if meaning is None: + continue + print(f" {bits} = {format_bit_value(raw, width)} -> {meaning}") + + +def print_register_description(page: int, reg: int) -> None: + print(f"P{page:02d} R{reg:03d} (0x{reg:02X}) {REGISTER_MAP[page][reg]}") + + +def print_page0_group_value(start_reg: int, values: list[int]) -> None: + group = PAGE0_GROUPS[start_reg] + regs = group["registers"] + kind = group["kind"] + name = group["name"] + + if kind == "pll_d": + raw = ((values[7] & 0x3F) << 8) | values[8] + print(f"P00 R007/R008 (0x07/0x08) = 0x{raw:04X} ({raw}) {name}") + return + + if kind == "dosr": + raw = ((values[13] & 0x03) << 8) | values[14] + if raw == 0: + meaning = "effective DOSR = 1024" + elif raw in (1, 0x3FF): + meaning = "Reserved / invalid DOSR value" + else: + meaning = f"effective DOSR = {raw}" + print(f"P00 R013/R014 (0x0D/0x0E) = 0x{raw:03X} ({raw}) {name}; {meaning}") + return + + if kind == "beep_length": + raw = (values[73] << 16) | (values[74] << 8) | values[75] + print(f"P00 R073/R074/R075 (0x49/0x4A/0x4B) = 0x{raw:06X} ({raw}) {name} in DAC samples") + return + + if kind == "s16": + msb, lsb = regs + raw = (values[msb] << 8) | values[lsb] + signed = to_signed16(raw) + print( + f"P00 R{msb:03d}/R{lsb:03d} (0x{msb:02X}/0x{lsb:02X}) = 0x{raw:04X} ({signed:+d}) {name}" + ) + return + + raise AssertionError(f"unknown page-0 group kind: {kind}") + + +def print_page0_group_description(start_reg: int) -> None: + group = PAGE0_GROUPS[start_reg] + regs = group["registers"] + joined_regs = "/".join(f"R{reg:03d}" for reg in regs) + joined_hex = "/".join(f"0x{reg:02X}" for reg in regs) + print(f"P00 {joined_regs} ({joined_hex}) {group['name']}") + + +def pair_label(desc1: str, desc2: str) -> str | None: + msb_match = PAIR_COEFF_MSB_RE.match(desc1) + lsb_match = PAIR_COEFF_LSB_RE.match(desc2) + if msb_match and lsb_match: + if msb_match.group(1) == lsb_match.group(1) and msb_match.group(2) == lsb_match.group(2): + return f"Coefficient {msb_match.group(1)} for {msb_match.group(2)}" + + msb_match = PAIR_MSB_RE.match(desc1) + lsb_match = PAIR_LSB_RE.match(desc2) + if msb_match and lsb_match and msb_match.group(1) == lsb_match.group(1): + return msb_match.group(1) + + return None + + +def print_coeff_page_values(page: int, values: list[int]) -> None: + reg = 0 + while reg < 128: + if is_reserved_register(page, reg): + reg += 1 + continue + + if (page, reg) in REGISTER_FIELDS: + print_register_value(page, reg, values[reg]) + reg += 1 + continue + + if reg + 1 < 128 and not is_reserved_register(page, reg + 1): + label = pair_label(REGISTER_MAP[page][reg], REGISTER_MAP[page][reg + 1]) + if label is not None: + raw = (values[reg] << 8) | values[reg + 1] + signed = to_signed16(raw) + print( + f"P{page:02d} R{reg:03d}/R{reg + 1:03d} (0x{reg:02X}/0x{reg + 1:02X}) = 0x{raw:04X} ({signed:+d}) {label}" + ) + reg += 2 + continue + + print_register_value(page, reg, values[reg]) + reg += 1 + + +def print_coeff_page_descriptions(page: int) -> None: + reg = 0 + while reg < 128: + if is_reserved_register(page, reg): + reg += 1 + continue + + if (page, reg) in REGISTER_FIELDS: + print_register_description(page, reg) + reg += 1 + continue + + if reg + 1 < 128 and not is_reserved_register(page, reg + 1): + label = pair_label(REGISTER_MAP[page][reg], REGISTER_MAP[page][reg + 1]) + if label is not None: + print( + f"P{page:02d} R{reg:03d}/R{reg + 1:03d} (0x{reg:02X}/0x{reg + 1:02X}) {label}" + ) + reg += 2 + continue + + print_register_description(page, reg) + reg += 1 + + +def print_page_values(page: int, values: list[int]) -> None: + if page in (8, 9, 12, 13): + print_coeff_page_values(page, values) + return + + for reg in range(128): + if is_reserved_register(page, reg): + continue + if page == 0 and reg in PAGE0_GROUPED_REGS: + if reg in PAGE0_GROUPS: + print_page0_group_value(reg, values) + continue + print_register_value(page, reg, values[reg]) + + +def print_page_descriptions(page: int) -> None: + if page in (8, 9, 12, 13): + print_coeff_page_descriptions(page) + return + + for reg in range(128): + if is_reserved_register(page, reg): + continue + if page == 0 and reg in PAGE0_GROUPED_REGS: + if reg in PAGE0_GROUPS: + print_page0_group_description(reg) + continue + print_register_description(page, reg) + + +def make_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Dump TLV320DAC3100 registers and decode their meanings.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument("--bus", type=parse_int, default=1, help="I2C bus number") + parser.add_argument( + "--addr", + type=parse_int, + default=0x18, + help="7-bit TLV320DAC3100 I2C address", + ) + parser.add_argument( + "--pages", + type=parse_pages, + default=list(DEFINED_PAGES), + help="comma-separated page list, or 'all'", + ) + parser.add_argument( + "--descriptions-only", + action="store_true", + help="do not touch I2C; print only non-reserved registers and grouped fields", + ) + return parser + + +def main() -> int: + parser = make_parser() + args = parser.parse_args() + + try: + print("# TLV320DAC3100 register dump") + print("# source: hard-coded from tlv320dac3100.pdf (Rev. C)") + if args.descriptions_only: + print("# mode: descriptions only (reserved registers omitted)") + else: + print( + f"# mode: live I2C read on bus {args.bus}, addr 0x{args.addr:02x} " + "(reserved registers omitted)" + ) + + for page in args.pages: + print() + print(f"[page {page}]") + if args.descriptions_only: + print_page_descriptions(page) + continue + + select_page(args.bus, args.addr, page) + values = read_page(args.bus, args.addr) + print_page_values(page, values) + + if not args.descriptions_only: + try: + select_page(args.bus, args.addr, 0) + except CommandError: + pass + + return 0 + except (CommandError, argparse.ArgumentTypeError, ValueError) as exc: + print(f"error: {exc}", file=sys.stderr) + return 1 + except KeyboardInterrupt: + print("interrupted", file=sys.stderr) + return 130 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/reinit-codec.sh b/reinit-codec.sh new file mode 100755 index 0000000..6582703 --- /dev/null +++ b/reinit-codec.sh @@ -0,0 +1,73 @@ +#!/bin/bash +# Reinitialize the TLV320DAC3100 codec without a full reboot. +# Tears down the ALSA driver, resets the codec via i2c, then re-binds +# the driver so ALSA comes back fresh. +# +# Usage: sudo ./reinit-codec.sh + +set -euo pipefail + +BUS=1 +ADDR=0x18 +DRIVER_PATH="/sys/bus/i2c/drivers/tlv320aic31xx-codec" +DEVICE="1-0018" + +w() { i2cset -y $BUS $ADDR "$@"; } + +echo "=== Reinitializing TLV320DAC3100 ===" + +# 1. Stop anything using the sound card +echo "[1/5] Stopping audio consumers..." +if command -v pulseaudio &>/dev/null; then + pulseaudio --kill 2>/dev/null || true +fi +if command -v pipewire &>/dev/null; then + systemctl --user stop pipewire pipewire-pulse 2>/dev/null || true +fi +fuser -k /dev/snd/* 2>/dev/null || true +sleep 0.2 + +# 2. Unbind the kernel driver so it releases the codec +echo "[2/5] Unbinding kernel driver..." +if [ -e "$DRIVER_PATH/$DEVICE" ]; then + echo "$DEVICE" > "$DRIVER_PATH/unbind" + sleep 0.3 + echo " Driver unbound." +else + echo " Device not bound, skipping." +fi + +# 3. Software-reset the codec over i2c +echo "[3/5] Resetting codec via i2c..." +# Select page 0 +w 0x00 0x00 +# Software reset +w 0x01 0x01 +sleep 0.5 +echo " Codec reset." + +# 4. Re-bind the kernel driver (it will re-probe and init the codec) +echo "[4/5] Re-binding kernel driver..." +echo "$DEVICE" > "$DRIVER_PATH/bind" +sleep 1 +echo " Driver bound." + +# 5. Restore mixer defaults +echo "[5/5] Restoring mixer settings..." +sleep 0.5 + +# Unmute and set sensible defaults via amixer +amixer -c0 sset 'DAC' 127 127 2>/dev/null || true +amixer -c0 sset 'Speaker Driver' 0 2>/dev/null || true +amixer -c0 sset 'Speaker Analog' 100 2>/dev/null || true +amixer -c0 sset 'HP Analog' 100 100 2>/dev/null || true +amixer -c0 sset 'HP Driver' 0 0 2>/dev/null || true + +# Restart audio daemons if they were running +if command -v pipewire &>/dev/null; then + systemctl --user start pipewire pipewire-pulse 2>/dev/null || true +fi + +echo "" +echo "=== Codec reinitialized ===" +aplay -l 2>/dev/null | grep -A1 "barepiaudio" || echo "(warning: card not visible yet)" diff --git a/setup-audio.sh b/setup-audio.sh new file mode 100755 index 0000000..ef1776f --- /dev/null +++ b/setup-audio.sh @@ -0,0 +1,651 @@ +#!/bin/bash +# setup-audio.sh — Set up barepi audio (TLV320DAC3100 + TDK T5848 mics) on a fresh Pi +# Usage: sudo ./setup-audio.sh +set -euo pipefail + +if [ "$(id -u)" -ne 0 ]; then + echo "Run as root: sudo ./setup-audio.sh" + exit 1 +fi + +TARGET_USER="${TARGET_USER:-${SUDO_USER:-}}" +if [ -z "$TARGET_USER" ] || [ "$TARGET_USER" = "root" ]; then + echo "Could not determine target user." >&2 + echo "Run with sudo from the target account or pass TARGET_USER=." >&2 + exit 1 +fi + +TARGET_HOME="$(getent passwd "$TARGET_USER" | cut -d: -f6 || true)" +if [ -z "$TARGET_HOME" ] || [ ! -d "$TARGET_HOME" ]; then + echo "Could not determine home directory for user: $TARGET_USER" >&2 + exit 1 +fi + +CONFIG="/boot/firmware/config.txt" +OVERLAYS="/boot/firmware/overlays" +RESTORE_HELPER="/usr/local/sbin/barepi-audio-restore" +RESTORE_SERVICE="/etc/systemd/system/barepi-audio-restore.service" + +echo "=== barepi audio setup ===" +echo "User: $TARGET_USER Home: $TARGET_HOME" +echo "" + +# 1. Install packages +echo "[1/7] Installing packages..." +apt-get update +apt-get install -y alsa-utils libasound2-plugins swh-plugins i2c-tools device-tree-compiler +echo " Done." + +# 2. Compile and install device tree overlay +echo "[2/7] Installing device tree overlay..." +DTS=$(mktemp /tmp/i2s-audio-XXXX.dts) +trap 'rm -f "$DTS"' EXIT +cat > "$DTS" << 'DTS_EOF' +/dts-v1/; +/plugin/; + +/ { + compatible = "brcm,bcm2712"; + + fragment@0 { + target = <&i2s_clk_producer>; + __overlay__ { + status = "okay"; + }; + }; + + fragment@1 { + target-path = "/"; + __overlay__ { + tlv320_dvdd: regulator-tlv320-dvdd { + compatible = "regulator-fixed"; + regulator-name = "tlv320-dvdd-1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + tlv320_iovdd: regulator-tlv320-iovdd { + compatible = "regulator-fixed"; + regulator-name = "tlv320-iovdd-3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + tlv320_avdd: regulator-tlv320-avdd { + compatible = "regulator-fixed"; + regulator-name = "tlv320-avdd-3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + tlv320_hpvdd: regulator-tlv320-hpvdd { + compatible = "regulator-fixed"; + regulator-name = "tlv320-hpvdd-3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + tlv320_spkvdd: regulator-tlv320-spkvdd { + compatible = "regulator-fixed"; + regulator-name = "tlv320-spkvdd-5v0"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + /* + * The T5848 is a control-less I2S microphone from Linux's point + * of view, so use the generic ICS43432 codec DAI as a simple + * capture endpoint and keep the speaker path unchanged. + */ + mic_codec: t5848-codec { + compatible = "invensense,ics43432"; + #sound-dai-cells = <0>; + }; + }; + }; + + fragment@2 { + target = <&i2c_arm>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + dac_codec: tlv320dac3100@18 { + compatible = "ti,tlv320dac3100"; + reg = <0x18>; + #sound-dai-cells = <0>; + AVDD-supply = <&tlv320_avdd>; + IOVDD-supply = <&tlv320_iovdd>; + DVDD-supply = <&tlv320_dvdd>; + HPVDD-supply = <&tlv320_hpvdd>; + SPLVDD-supply = <&tlv320_spkvdd>; + SPRVDD-supply = <&tlv320_spkvdd>; + reset-gpios = <&gpio 12 1>; /* GPIO_ACTIVE_LOW */ + ti,pll-clkin-src = <1>; + status = "okay"; + }; + }; + }; + + fragment@3 { + target-path = "/"; + __overlay__ { + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "barepi-audio"; + simple-audio-card,format = "i2s"; + status = "okay"; + + dailink0: simple-audio-card,dai-link@0 { + format = "i2s"; + bitclock-master = <&cpu0>; + frame-master = <&cpu0>; + + cpu0: cpu { + sound-dai = <&i2s_clk_producer>; + }; + + codec0: codec { + sound-dai = <&dac_codec>; + system-clock-frequency = <1536000>; + }; + }; + + dailink1: simple-audio-card,dai-link@1 { + format = "i2s"; + bitclock-master = <&cpu1>; + frame-master = <&cpu1>; + + cpu1: cpu { + sound-dai = <&i2s_clk_producer>; + }; + + codec1: codec { + sound-dai = <&mic_codec>; + }; + }; + }; + }; + }; +}; +DTS_EOF +dtc -@ -I dts -O dtb -o "$OVERLAYS/i2s-audio.dtbo" "$DTS" +rm -f "$DTS" +trap - EXIT +echo " Installed $OVERLAYS/i2s-audio.dtbo" + +# 3. Patch config.txt +echo "[3/7] Configuring config.txt..." +CHANGED=0 + +if grep -q '^dtparam=i2s=on' "$CONFIG"; then + echo " dtparam=i2s=on already set." +elif grep -q '^#.*dtparam=i2s=on' "$CONFIG"; then + sed -i 's/^#.*dtparam=i2s=on/dtparam=i2s=on/' "$CONFIG" + CHANGED=1 + echo " Uncommented dtparam=i2s=on." +else + if grep -q '^dtparam=audio=on' "$CONFIG"; then + sed -i '/^dtparam=audio=on/a dtparam=i2s=on' "$CONFIG" + else + echo "dtparam=i2s=on" >> "$CONFIG" + fi + CHANGED=1 + echo " Added dtparam=i2s=on." +fi + +if grep -q '^dtoverlay=i2s-audio' "$CONFIG"; then + echo " dtoverlay=i2s-audio already set." +elif grep -q '^#.*dtoverlay=i2s-audio' "$CONFIG"; then + sed -i 's/^#.*dtoverlay=i2s-audio/dtoverlay=i2s-audio/' "$CONFIG" + CHANGED=1 + echo " Uncommented dtoverlay=i2s-audio." +else + if grep -q '^dtparam=i2s=on' "$CONFIG"; then + sed -i '/^dtparam=i2s=on/a dtoverlay=i2s-audio' "$CONFIG" + else + echo "dtoverlay=i2s-audio" >> "$CONFIG" + fi + CHANGED=1 + echo " Added dtoverlay=i2s-audio." +fi + +if grep -q '^gpio=12=op,dh' "$CONFIG"; then + echo " gpio=12=op,dh already set." +elif grep -q '^#.*gpio=12=op,dh' "$CONFIG"; then + sed -i 's/^#.*gpio=12=op,dh/gpio=12=op,dh/' "$CONFIG" + CHANGED=1 + echo " Uncommented gpio=12=op,dh." +elif grep -q '^gpio=12=' "$CONFIG"; then + echo " Existing gpio=12=... line found; left unchanged." >&2 + echo " Make sure it keeps the DAC out of reset (gpio=12=op,dh)." >&2 +else + if grep -q '^dtoverlay=i2s-audio' "$CONFIG"; then + sed -i '/^dtoverlay=i2s-audio/a gpio=12=op,dh' "$CONFIG" + elif grep -q '^dtparam=i2s=on' "$CONFIG"; then + sed -i '/^dtparam=i2s=on/a gpio=12=op,dh' "$CONFIG" + else + echo "gpio=12=op,dh" >> "$CONFIG" + fi + CHANGED=1 + echo " Added gpio=12=op,dh." +fi + +# 4. Install .asoundrc +echo "[4/7] Installing .asoundrc..." +cat > "$TARGET_HOME/.asoundrc" << 'ASOUNDRC' +defaults.pcm.rate_converter "samplerate_best" + +pcm.!default { + type plug + slave { + pcm "filtered" + rate 48000 + } +} + +pcm.filtered { + type ladspa + slave.pcm "out" + path "/usr/lib/ladspa" + plugins [ + { + label highpass_iir + input { + controls [ 100 1 ] + } + } + { + label hardLimiter + input { + controls [ 0 1 0 ] + } + } + ] +} + +pcm.out { + type plug + slave { + pcm "hw:CARD=barepiaudio,DEV=1" + format S16_LE + } +} + +pcm.barepi_playback { + type plug + slave { + pcm "out" + rate 48000 + } +} + +pcm.barepi_capture { + type plug + slave { + pcm "hw:CARD=barepiaudio,DEV=0" + format S32_LE + rate 48000 + channels 2 + } +} + +ctl.!default { + type hw + card barepiaudio +} +ASOUNDRC +chown "$TARGET_USER:$TARGET_USER" "$TARGET_HOME/.asoundrc" +echo " Installed $TARGET_HOME/.asoundrc" + +# 5. Install volume.sh +echo "[5/7] Installing volume.sh..." +cat > "$TARGET_HOME/volume.sh" << 'VOLSCRIPT' +#!/bin/bash +# DAC3100 speaker-path helper +# Usage: ./volume.sh [dac] [driver] [analog] +# dac: 0-175 (digital volume, 127=0dB) +# driver: 0-3 (class-D gain: 6/12/18/24 dB) +# analog: 0-127 (analog volume, 127=0dB) + +set -euo pipefail + +CARD_ID="barepiaudio" +CARD_NAME="barepi-audio" +DEFAULT_DAC=127 +DEFAULT_DRIVER=0 +DEFAULT_ANALOG=50 + +find_card() { + awk -v id="$CARD_ID" -v name="$CARD_NAME" ' + index($0, "[" id "]") || index($0, name) { print $1; exit } + ' /proc/asound/cards +} + +show_status() { + echo "Using ALSA card: $CARD ($CARD_NAME)" + amixer -c "$CARD" sget 'DAC' || true + amixer -c "$CARD" sget 'DAC Left Input' || true + amixer -c "$CARD" sget 'DAC Right Input' || true + amixer -c "$CARD" sget 'Output Left From Left DAC' || true + amixer -c "$CARD" sget 'Output Right From Right DAC' || true + amixer -c "$CARD" sget 'Speaker' || true + amixer -c "$CARD" sget 'Speaker Driver' || true + amixer -c "$CARD" sget 'Speaker Analog' || true +} + +enable_speaker_path() { + echo "Enabling TLV320 speaker path..." + amixer -c "$CARD" sset 'DAC Left Input' 'Left Data' >/dev/null || true + amixer -c "$CARD" sset 'DAC Right Input' 'Right Data' >/dev/null || true + amixer -c "$CARD" sset 'Output Left From Left DAC' on >/dev/null || true + amixer -c "$CARD" sset 'Output Right From Right DAC' on >/dev/null || true + amixer -c "$CARD" sset 'Speaker' on >/dev/null || true + amixer -c "$CARD" sset 'HP Left' off >/dev/null || true + amixer -c "$CARD" sset 'HP Right' off >/dev/null || true + amixer -c "$CARD" sset 'HP Driver' 0 >/dev/null || true + amixer -c "$CARD" sset 'HP Driver' off >/dev/null || true +} + +CARD="$(find_card || true)" +if [ -z "$CARD" ]; then + echo "Could not find ALSA card for $CARD_NAME." >&2 + echo "Reboot after setup, then check /proc/asound/cards." >&2 + exit 1 +fi + +if [ $# -eq 0 ]; then + show_status + exit 0 +fi + +DAC=${1:-$DEFAULT_DAC} +DRIVER=${2:-$DEFAULT_DRIVER} +ANALOG=${3:-$DEFAULT_ANALOG} + +enable_speaker_path + +echo "Setting levels..." +amixer -c "$CARD" sset 'DAC' "${DAC},${DAC}" >/dev/null +amixer -c "$CARD" sset 'Speaker Driver' "$DRIVER" >/dev/null || true +amixer -c "$CARD" sset 'Speaker Driver' on >/dev/null || true +amixer -c "$CARD" sset 'Speaker Analog' "$ANALOG" >/dev/null || true + +alsactl store "$CARD" >/dev/null 2>&1 || alsactl store >/dev/null 2>&1 || true + +echo "" +echo "Using ALSA card: $CARD ($CARD_NAME)" +echo "Set: dac=$DAC driver=$DRIVER analog=$ANALOG" +echo "Example: ./volume.sh 127 0 50" +VOLSCRIPT +chown "$TARGET_USER:$TARGET_USER" "$TARGET_HOME/volume.sh" +chmod +x "$TARGET_HOME/volume.sh" +echo " Installed $TARGET_HOME/volume.sh" + +# 6. Install a mic test helper +echo "[6/7] Installing test-mics.sh..." +cat > "$TARGET_HOME/test-mics.sh" << 'MICTEST' +#!/bin/bash +# Capture a short stereo sample from the barepi mic path and print per-channel stats. +# Usage: +# ./test-mics.sh [seconds] [out.wav] +# ./test-mics.sh --thsel-high [seconds] [out.wav] +# ./test-mics.sh --thsel-low [seconds] [out.wav] + +set -euo pipefail + +RATE=48000 +FORMAT=S32_LE +CHANNELS=2 +DURATION=3 +OUT="" +THSEL="" +CAPTURE_PCM="${CAPTURE_PCM:-barepi_capture}" + +usage() { + cat <<'EOF' +Usage: + ./test-mics.sh [seconds] [out.wav] + ./test-mics.sh --thsel-high [seconds] [out.wav] + ./test-mics.sh --thsel-low [seconds] [out.wav] +EOF +} + +set_thsel() { + local level="$1" + command -v pinctrl >/dev/null 2>&1 || return 0 + if [ "$level" = "high" ]; then + sudo pinctrl set 8 op pn dh >/dev/null + else + sudo pinctrl set 8 op pn dl >/dev/null + fi +} + +prepare_wake_pin() { + command -v pinctrl >/dev/null 2>&1 || return 0 + sudo pinctrl set 13 ip pn >/dev/null 2>&1 || true +} + +show_pins() { + command -v pinctrl >/dev/null 2>&1 || return 0 + sudo pinctrl get 8,13 || true +} + +positional=() +while [ $# -gt 0 ]; do + case "$1" in + --thsel-high) + THSEL="high" + shift + ;; + --thsel-low) + THSEL="low" + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + positional+=("$1") + shift + ;; + esac +done + +if [ ${#positional[@]} -ge 1 ]; then + DURATION="${positional[0]}" +fi +if [ ${#positional[@]} -ge 2 ]; then + OUT="${positional[1]}" +fi +if [ -z "$OUT" ]; then + OUT="/tmp/barepi-mics-$(date +%Y%m%d-%H%M%S).wav" +fi + +if ! arecord -L 2>/dev/null | grep -qx 'barepi_capture'; then + CAPTURE_PCM="plughw:CARD=barepiaudio,DEV=0" +fi + +if [ -n "$THSEL" ]; then + set_thsel "$THSEL" +fi +prepare_wake_pin +show_pins + +echo "Recording ${DURATION}s from ${CAPTURE_PCM} -> ${OUT}" +arecord -q -D "$CAPTURE_PCM" -f "$FORMAT" -c "$CHANNELS" -r "$RATE" -d "$DURATION" "$OUT" + +python3 - <<'PY' "$OUT" +import math +import struct +import sys +import wave + +path = sys.argv[1] +with wave.open(path, 'rb') as w: + channels = w.getnchannels() + width = w.getsampwidth() + rate = w.getframerate() + frames = w.getnframes() + data = w.readframes(frames) + +if channels != 2 or width != 4: + print(f"Recorded {channels}ch / {width * 8}bit at {rate} Hz") + sys.exit(0) + +samples = list(struct.iter_unpack('10} peak={l_peak:>10} peak_dBFS={20*math.log10(max(l_peak,1)/full_scale):6.1f}") +print(f"Right: rms={int(r_rms):>10} peak={r_peak:>10} peak_dBFS={20*math.log10(max(r_peak,1)/full_scale):6.1f}") +PY +MICTEST +chown "$TARGET_USER:$TARGET_USER" "$TARGET_HOME/test-mics.sh" +chmod +x "$TARGET_HOME/test-mics.sh" +echo " Installed $TARGET_HOME/test-mics.sh" + +# 7. Install a boot-time restore helper so the speaker path comes up after reboot +echo "[7/7] Installing boot-time mixer restore..." +cat > "$RESTORE_HELPER" << 'RESTORESCRIPT' +#!/bin/bash +set -euo pipefail + +CARD_ID="barepiaudio" +CARD_NAME="barepi-audio" +I2C_BUS="${I2C_BUS:-1}" +CODEC_ADDR="${CODEC_ADDR:-0x18}" +MIC_THSEL_LEVEL="${MIC_THSEL_LEVEL:-high}" +DAC=${1:-127} +DRIVER=${2:-0} +ANALOG=${3:-50} + +find_card() { + awk -v id="$CARD_ID" -v name="$CARD_NAME" ' + index($0, "[" id "]") || index($0, name) { print $1; exit } + ' /proc/asound/cards +} + +codec_ready() { + i2cget -y -f "$I2C_BUS" "$CODEC_ADDR" 0 >/dev/null 2>&1 +} + +set_mixer() { + amixer -q -c "$CARD" "$@" >/dev/null 2>&1 || true +} + +set_mic_pins() { + if ! command -v pinctrl >/dev/null 2>&1; then + return 0 + fi + + case "$MIC_THSEL_LEVEL" in + low) + pinctrl set 8 op pn dl >/dev/null 2>&1 || true + ;; + *) + pinctrl set 8 op pn dh >/dev/null 2>&1 || true + ;; + esac + + pinctrl set 13 ip pn >/dev/null 2>&1 || true +} + +CARD="" +for _ in $(seq 1 20); do + CARD="$(find_card || true)" + if [ -n "$CARD" ]; then + break + fi + sleep 1 +done + +if [ -z "$CARD" ]; then + echo "barepi-audio card not present; skipping restore." + exit 0 +fi + +for _ in $(seq 1 20); do + if codec_ready; then + break + fi + sleep 1 +done + +if ! codec_ready; then + echo "TLV320 codec not responding on i2c-${I2C_BUS} addr ${CODEC_ADDR}; skipping restore." + exit 0 +fi + +set_mic_pins +set_mixer sset 'DAC Left Input' 'Left Data' +set_mixer sset 'DAC Right Input' 'Right Data' +set_mixer sset 'Output Left From Left DAC' on +set_mixer sset 'Output Right From Right DAC' on +set_mixer sset 'Speaker' on +set_mixer sset 'Speaker Driver' "$DRIVER" +set_mixer sset 'Speaker Driver' on +set_mixer sset 'Speaker Analog' "$ANALOG" +set_mixer sset 'DAC' "${DAC},${DAC}" +set_mixer sset 'HP Left' off +set_mixer sset 'HP Right' off +set_mixer sset 'HP Driver' '0,0' +set_mixer sset 'HP Driver' off +RESTORESCRIPT +chmod 755 "$RESTORE_HELPER" + +cat > "$RESTORE_SERVICE" << EOF +[Unit] +Description=Restore barepi TLV320 speaker routing +After=sound.target +Wants=sound.target + +[Service] +Type=oneshot +ExecStart=$RESTORE_HELPER 127 0 50 + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable barepi-audio-restore.service >/dev/null + +if grep -q '\[barepiaudio\]' /proc/asound/cards 2>/dev/null || grep -q 'barepi-audio' /proc/asound/cards 2>/dev/null; then + "$RESTORE_HELPER" 127 0 50 || true + echo " Applied mixer defaults to the live barepi-audio card." +else + echo " Live barepi-audio card not present yet; defaults will be applied at boot." +fi + +echo "" +echo "=== Done ===" +if [ "$CHANGED" -eq 1 ]; then + echo "config.txt was modified — reboot to load the overlay/reset config." +else + echo "config.txt unchanged. Reboot if this is a first-time setup." +fi +echo "Speaker routing defaults and mic GPIO defaults will be restored automatically after boot." +echo "Manual speaker check/adjust: $TARGET_HOME/volume.sh" +echo "Manual mic test: $TARGET_HOME/test-mics.sh" diff --git a/tap-mics.sh b/tap-mics.sh new file mode 100755 index 0000000..2e92263 --- /dev/null +++ b/tap-mics.sh @@ -0,0 +1,225 @@ +#!/bin/bash +# Live per-channel mic level meter for the barepi capture path. +# Usage: +# ./tap-mics.sh +# ./tap-mics.sh --thsel-high +# ./tap-mics.sh --thsel-low +# ./tap-mics.sh --window-ms 125 +# Press Ctrl-C to stop. + +set -euo pipefail + +RATE=48000 +WINDOW_MS=125 +THSEL="" +CAPTURE_PCM="${CAPTURE_PCM:-barepi_capture}" + +usage() { + cat <<'EOF' +Usage: + ./tap-mics.sh + ./tap-mics.sh --thsel-high + ./tap-mics.sh --thsel-low + ./tap-mics.sh --window-ms 125 + +Environment: + CAPTURE_PCM=... Override the ALSA capture device. +EOF +} + +set_thsel() { + local level="$1" + command -v pinctrl >/dev/null 2>&1 || return 0 + if [ "$level" = "high" ]; then + sudo pinctrl set 8 op pn dh >/dev/null + else + sudo pinctrl set 8 op pn dl >/dev/null + fi +} + +prepare_wake_pin() { + command -v pinctrl >/dev/null 2>&1 || return 0 + sudo pinctrl set 13 ip pn >/dev/null 2>&1 || true +} + +show_pins() { + command -v pinctrl >/dev/null 2>&1 || return 0 + sudo pinctrl get 8,13 || true +} + +while [ $# -gt 0 ]; do + case "$1" in + --thsel-high) + THSEL="high" + shift + ;; + --thsel-low) + THSEL="low" + shift + ;; + --window-ms) + WINDOW_MS="${2:-}" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +if ! [[ "$WINDOW_MS" =~ ^[0-9]+$ ]] || [ "$WINDOW_MS" -le 0 ]; then + echo "--window-ms must be a positive integer" >&2 + exit 1 +fi + +if ! arecord -L 2>/dev/null | grep -qx 'barepi_capture'; then + CAPTURE_PCM="plughw:CARD=barepiaudio,DEV=0" +fi + +if [ -n "$THSEL" ]; then + set_thsel "$THSEL" +fi +prepare_wake_pin +show_pins + +echo "Live tap test from ${CAPTURE_PCM} (${RATE} Hz, window ${WINDOW_MS} ms). Press Ctrl-C to stop." +python3 - <<'PY' "$CAPTURE_PCM" "$RATE" "$WINDOW_MS" +import math +import signal +import struct +import subprocess +import sys + +pcm = sys.argv[1] +rate = int(sys.argv[2]) +window_ms = int(sys.argv[3]) +channels = 2 +sample_bytes = 4 +frame_bytes = channels * sample_bytes +frames_per_chunk = max(1, rate * window_ms // 1000) +chunk_bytes = frames_per_chunk * frame_bytes +full_scale = float(2**31 - 1) + +cmd = [ + "arecord", "-q", + "-D", pcm, + "-t", "raw", + "-f", "S32_LE", + "-c", str(channels), + "-r", str(rate), + "-", +] + +proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, +) +assert proc.stdout is not None + +stopped = False + +def handle_signal(signum, frame): + raise KeyboardInterrupt + +signal.signal(signal.SIGINT, handle_signal) +signal.signal(signal.SIGTERM, handle_signal) + +def dbfs(value: float) -> float: + if value <= 0: + return -186.6 + return 20.0 * math.log10(value / full_scale) + +def bar(db: float, width: int = 24, floor: float = -72.0) -> str: + if db <= floor: + filled = 0 + elif db >= 0: + filled = width + else: + filled = int(round((db - floor) / (0 - floor) * width)) + filled = max(0, min(width, filled)) + return "#" * filled + "." * (width - filled) + +def read_exact(n: int) -> bytes: + parts = [] + got = 0 + while got < n: + chunk = proc.stdout.read(n - got) + if not chunk: + raise EOFError + parts.append(chunk) + got += len(chunk) + return b"".join(parts) + +max_l_peak = 0 +max_r_peak = 0 +last_len = 0 + +try: + while True: + data = read_exact(chunk_bytes) + l_sum = 0 + r_sum = 0 + l_peak = 0 + r_peak = 0 + frames = 0 + + for l, r in struct.iter_unpack(" l_peak: + l_peak = l_abs + if r_abs > r_peak: + r_peak = r_abs + l_sum += l * l + r_sum += r * r + + l_rms = math.sqrt(l_sum / frames) if frames else 0.0 + r_rms = math.sqrt(r_sum / frames) if frames else 0.0 + max_l_peak = max(max_l_peak, l_peak) + max_r_peak = max(max_r_peak, r_peak) + + l_rms_db = dbfs(l_rms) + r_rms_db = dbfs(r_rms) + l_peak_db = dbfs(l_peak) + r_peak_db = dbfs(r_peak) + + line = ( + f"L rms {l_rms_db:6.1f} peak {l_peak_db:6.1f} [{bar(l_peak_db)}] " + f"R rms {r_rms_db:6.1f} peak {r_peak_db:6.1f} [{bar(r_peak_db)}]" + ) + pad = " " * max(0, last_len - len(line)) + sys.stdout.write("\r" + line + pad) + sys.stdout.flush() + last_len = len(line) +except KeyboardInterrupt: + pass +except EOFError: + pass +finally: + if proc.poll() is None: + proc.terminate() + try: + proc.wait(timeout=1.0) + except subprocess.TimeoutExpired: + proc.kill() + proc.wait() + +print() +print( + "Max peaks seen: " + f"L={dbfs(max_l_peak):.1f} dBFS " + f"R={dbfs(max_r_peak):.1f} dBFS" +) +if max_l_peak == 0 and max_r_peak > 0: + print("Only the right channel showed activity during this run.") +elif max_r_peak == 0 and max_l_peak > 0: + print("Only the left channel showed activity during this run.") +PY diff --git a/test-mics.sh b/test-mics.sh new file mode 100755 index 0000000..31fb014 --- /dev/null +++ b/test-mics.sh @@ -0,0 +1,130 @@ +#!/bin/bash +# Capture a short stereo sample from the barepi mic path and print per-channel stats. +# Usage: +# ./test-mics.sh [seconds] [out.wav] +# ./test-mics.sh --thsel-high [seconds] [out.wav] +# ./test-mics.sh --thsel-low [seconds] [out.wav] + +set -euo pipefail + +RATE=48000 +FORMAT=S32_LE +CHANNELS=2 +DURATION=3 +OUT="" +THSEL="" +CAPTURE_PCM="${CAPTURE_PCM:-barepi_capture}" + +usage() { + cat <<'EOF' +Usage: + ./test-mics.sh [seconds] [out.wav] + ./test-mics.sh --thsel-high [seconds] [out.wav] + ./test-mics.sh --thsel-low [seconds] [out.wav] +EOF +} + +set_thsel() { + local level="$1" + command -v pinctrl >/dev/null 2>&1 || return 0 + if [ "$level" = "high" ]; then + sudo pinctrl set 8 op pn dh >/dev/null + else + sudo pinctrl set 8 op pn dl >/dev/null + fi +} + +prepare_wake_pin() { + command -v pinctrl >/dev/null 2>&1 || return 0 + sudo pinctrl set 13 ip pn >/dev/null 2>&1 || true +} + +show_pins() { + command -v pinctrl >/dev/null 2>&1 || return 0 + sudo pinctrl get 8,13 || true +} + +positional=() +while [ $# -gt 0 ]; do + case "$1" in + --thsel-high) + THSEL="high" + shift + ;; + --thsel-low) + THSEL="low" + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + positional+=("$1") + shift + ;; + esac +done + +if [ ${#positional[@]} -ge 1 ]; then + DURATION="${positional[0]}" +fi +if [ ${#positional[@]} -ge 2 ]; then + OUT="${positional[1]}" +fi +if [ -z "$OUT" ]; then + OUT="/tmp/barepi-mics-$(date +%Y%m%d-%H%M%S).wav" +fi + +if ! arecord -L 2>/dev/null | grep -qx 'barepi_capture'; then + CAPTURE_PCM="plughw:CARD=barepiaudio,DEV=0" +fi + +if [ -n "$THSEL" ]; then + set_thsel "$THSEL" +fi +prepare_wake_pin +show_pins + +echo "Recording ${DURATION}s from ${CAPTURE_PCM} -> ${OUT}" +arecord -q -D "$CAPTURE_PCM" -f "$FORMAT" -c "$CHANNELS" -r "$RATE" -d "$DURATION" "$OUT" + +python3 - <<'PY' "$OUT" +import math +import struct +import sys +import wave + +path = sys.argv[1] +with wave.open(path, 'rb') as w: + channels = w.getnchannels() + width = w.getsampwidth() + rate = w.getframerate() + frames = w.getnframes() + data = w.readframes(frames) + +if channels != 2 or width != 4: + print(f"Recorded {channels}ch / {width * 8}bit at {rate} Hz") + sys.exit(0) + +samples = list(struct.iter_unpack('10} peak={l_peak:>10} peak_dBFS={20*math.log10(max(l_peak,1)/full_scale):6.1f}") +print(f"Right: rms={int(r_rms):>10} peak={r_peak:>10} peak_dBFS={20*math.log10(max(r_peak,1)/full_scale):6.1f}") +PY diff --git a/volume.sh b/volume.sh new file mode 100755 index 0000000..2cec935 --- /dev/null +++ b/volume.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# DAC3100 speaker-path helper +# Usage: ./volume.sh [dac] [driver] [analog] +# dac: 0-175 (digital volume, 127=0dB) +# driver: 0-3 (class-D gain: 6/12/18/24 dB) +# analog: 0-127 (analog volume, 127=0dB) + +set -euo pipefail + +CARD_ID="barepiaudio" +CARD_NAME="barepi-audio" +DEFAULT_DAC=127 +DEFAULT_DRIVER=0 +DEFAULT_ANALOG=50 + +find_card() { + awk -v id="$CARD_ID" -v name="$CARD_NAME" ' + index($0, "[" id "]") || index($0, name) { print $1; exit } + ' /proc/asound/cards +} + +show_status() { + echo "Using ALSA card: $CARD ($CARD_NAME)" + amixer -c "$CARD" sget 'DAC' || true + amixer -c "$CARD" sget 'DAC Left Input' || true + amixer -c "$CARD" sget 'DAC Right Input' || true + amixer -c "$CARD" sget 'Output Left From Left DAC' || true + amixer -c "$CARD" sget 'Output Right From Right DAC' || true + amixer -c "$CARD" sget 'Speaker' || true + amixer -c "$CARD" sget 'Speaker Driver' || true + amixer -c "$CARD" sget 'Speaker Analog' || true +} + +enable_speaker_path() { + echo "Enabling TLV320 speaker path..." + amixer -c "$CARD" sset 'DAC Left Input' 'Left Data' >/dev/null || true + amixer -c "$CARD" sset 'DAC Right Input' 'Right Data' >/dev/null || true + amixer -c "$CARD" sset 'Output Left From Left DAC' on >/dev/null || true + amixer -c "$CARD" sset 'Output Right From Right DAC' on >/dev/null || true + amixer -c "$CARD" sset 'Speaker' on >/dev/null || true + amixer -c "$CARD" sset 'HP Left' off >/dev/null || true + amixer -c "$CARD" sset 'HP Right' off >/dev/null || true + amixer -c "$CARD" sset 'HP Driver' 0 >/dev/null || true + amixer -c "$CARD" sset 'HP Driver' off >/dev/null || true +} + +CARD="$(find_card || true)" +if [ -z "$CARD" ]; then + echo "Could not find ALSA card for $CARD_NAME." >&2 + echo "Reboot after setup, then check /proc/asound/cards." >&2 + exit 1 +fi + +if [ $# -eq 0 ]; then + show_status + exit 0 +fi + +DAC=${1:-$DEFAULT_DAC} +DRIVER=${2:-$DEFAULT_DRIVER} +ANALOG=${3:-$DEFAULT_ANALOG} + +enable_speaker_path + +echo "Setting levels..." +amixer -c "$CARD" sset 'DAC' "$DAC" "$DAC" >/dev/null +amixer -c "$CARD" sset 'Speaker Driver' "$DRIVER" >/dev/null || true +amixer -c "$CARD" sset 'Speaker Driver' on >/dev/null || true +amixer -c "$CARD" sset 'Speaker Analog' "$ANALOG" >/dev/null || true + +alsactl store "$CARD" >/dev/null 2>&1 || alsactl store >/dev/null 2>&1 || true + +echo "" +echo "Using ALSA card: $CARD ($CARD_NAME)" +echo "Set: dac=$DAC driver=$DRIVER analog=$ANALOG" +echo "Example: ./volume.sh 127 0 50"