| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- #!/usr/bin/env python3
- """Convert a PPC2 TAS57xx .cfg file (I2C write commands) to a C header or raw
- binary with a packed byte stream: each command is [reg, len, data[0..len-1]]
- with no padding. The stream is terminated by 0xFF, 0xFF.
- Usage:
- python3 convert_cfg.py tt_hf1.cfg # C header to stdout
- python3 convert_cfg.py --bin tt_hf1.cfg # raw .bin file
- """
- import sys
- # TAS575x/578x register names by page
- # Page 0x00: Device Control
- PAGE0_REGS = {
- 0x00: "Page select",
- 0x01: "Reset",
- 0x02: "Standby/Shutdown ctrl",
- 0x03: "Mute",
- 0x04: "PLL/DSP source",
- 0x05: "De-emphasis / SDOUT / Auto-detect",
- 0x06: "SDOUT select",
- 0x07: "GPIO/SDOUT",
- 0x08: "GPIO enable",
- 0x09: "BCK/LRCLK config",
- 0x0C: "Master mode BCK divider",
- 0x0D: "PLL reference clock",
- 0x0E: "DAC reference clock",
- 0x0F: "PLL divider P",
- 0x10: "PLL multiplier J",
- 0x11: "PLL divider D (MSB)",
- 0x12: "PLL divider D (LSB)",
- 0x13: "PLL divider R",
- 0x14: "DSP clock divider",
- 0x15: "DAC clock divider",
- 0x16: "NCP clock divider",
- 0x17: "OSR clock divider",
- 0x1B: "Master BCK divider",
- 0x1C: "Master LRCLK divider (MSB)",
- 0x1D: "Master LRCLK divider (LSB)",
- 0x20: "IDAC (MSB)",
- 0x21: "IDAC (LSB)",
- 0x22: "FS speed mode",
- 0x25: "Ignore errors",
- 0x28: "I2S config",
- 0x2A: "DAC data path",
- 0x2B: "DSP program select",
- 0x3C: "Clock detect",
- 0x3D: "Digital volume fine (Ch B)",
- 0x3E: "Digital volume fine (Ch A)",
- 0x3F: "Auto mute time",
- 0x40: "Digital volume ctrl",
- 0x41: "Volume ramp config",
- 0x44: "Digital volume L",
- 0x45: "Digital volume R",
- 0x48: "Auto mute ctrl",
- 0x54: "Mute output select",
- 0x55: "GPIO output select",
- 0x56: "Analog output enable",
- 0x59: "GPIO output ctrl",
- 0x5C: "FS speed detect",
- 0x6A: "Output amplitude ctrl",
- 0x6B: "Output amplitude ctrl 2",
- 0x6C: "Undervoltage protection",
- 0x6E: "Power stage ctrl 1",
- 0x6F: "Power stage ctrl 2",
- 0x70: "Power stage ctrl 3",
- 0x71: "Power stage ctrl 4",
- 0x76: "VCOM ramp rate",
- 0x77: "VCOM power ctrl",
- 0x78: "VCOM power ctrl 2",
- }
- # Page 0x01: Output Control
- PAGE1_REGS = {
- 0x00: "Page select",
- 0x01: "Output amplitude ctrl",
- 0x02: "Analog gain ctrl",
- 0x06: "Analog output ctrl",
- 0x07: "Analog boost ctrl",
- 0x08: "Analog boost ctrl 2",
- }
- # DSP coefficient page descriptions
- DSP_PAGE_NAMES = {
- # miniDSP A (Channel A) coefficient pages
- 0x2C: "DSP-A: Process block coefficients",
- 0x2D: "DSP-A: Process block coefficients (cont.)",
- 0x2E: "DSP-A: Biquad filters",
- 0x2F: "DSP-A: Biquad filters (cont.)",
- 0x30: "DSP-A: Biquad/DRC",
- 0x31: "DSP-A: DRC/filters",
- 0x32: "DSP-A: Mixer/crossover",
- 0x33: "DSP-A: Interpolation/misc",
- 0x34: "DSP-A: Adaptation",
- # miniDSP B (Channel B) coefficient pages (mirror of A)
- 0x3E: "DSP-B: Process block coefficients",
- 0x3F: "DSP-B: Process block coefficients (cont.)",
- 0x40: "DSP-B: Biquad filters",
- 0x41: "DSP-B: Biquad filters (cont.)",
- 0x42: "DSP-B: Biquad/DRC",
- 0x43: "DSP-B: DRC/filters",
- 0x44: "DSP-B: Mixer/crossover",
- 0x45: "DSP-B: Interpolation/misc",
- 0x46: "DSP-B: Adaptation",
- }
- # These pages contain miniDSP instruction RAM
- INSTRUCTION_PAGES = set(range(0x98, 0xAA))
- def get_reg_comment(page, reg, data_len):
- """Return a register annotation comment, or empty string."""
- if page == 0x00:
- name = PAGE0_REGS.get(reg)
- if name:
- return f" // {name}"
- # Multi-byte writes might span known regs
- for offset in range(data_len):
- if (reg + offset) in PAGE0_REGS:
- names = []
- for o in range(data_len):
- n = PAGE0_REGS.get(reg + o, f"0x{reg+o:02X}")
- names.append(n)
- return f" // {' / '.join(names)}"
- elif page == 0x01:
- name = PAGE1_REGS.get(reg)
- if name:
- return f" // {name}"
- elif page == 0x7F:
- if reg == 0x00:
- return " // Book select"
- return ""
- def get_page_comment(page):
- """Return extra context for a page select comment."""
- if page in DSP_PAGE_NAMES:
- return f" — {DSP_PAGE_NAMES[page]}"
- if page in INSTRUCTION_PAGES:
- return " — miniDSP instruction RAM"
- if page == 0x00:
- return " — Device Control"
- if page == 0x01:
- return " — Output Control"
- return ""
- def convert(cfg_path):
- with open(cfg_path) as f:
- lines = f.readlines()
- name = cfg_path.rsplit('/', 1)[-1].rsplit('.', 1)[0]
- out = []
- out.append(f"// Auto-generated from {cfg_path}")
- out.append(f"// Packed byte stream: each command is [reg, len, data...]")
- out.append(f"// Terminated by 0xFF, 0xFF")
- out.append("")
- out.append("#pragma once")
- out.append("")
- out.append("#include <stdint.h>")
- out.append("")
- out.append(f"static const uint8_t {name}_seq[] = {{")
- current_page = 0x00
- pending_comments = []
- total_bytes = 0
- for line in lines:
- stripped = line.strip()
- if not stripped:
- continue
- if stripped.startswith('#'):
- pending_comments.append(stripped)
- continue
- if stripped.startswith('w '):
- parts = stripped.split()
- reg = int(parts[2], 16)
- data_bytes = parts[3:]
- data_len = len(data_bytes)
- data_hex = ', '.join(f'0x{b.upper()}' for b in data_bytes)
- # Track page changes
- is_page_select = (reg == 0x00 and data_len == 1)
- if is_page_select:
- new_page = int(data_bytes[0], 16)
- # Emit pending comments
- for c in pending_comments:
- comment_text = c.lstrip('# ').strip()
- if comment_text:
- if comment_text.startswith("Setting Page"):
- page_num = int(comment_text.split("0x")[1], 16)
- extra = get_page_comment(page_num)
- out.append(f' // {comment_text}{extra}')
- else:
- out.append(f' // {comment_text}')
- pending_comments = []
- # Build register annotation
- reg_comment = ""
- if is_page_select:
- current_page = new_page
- reg_comment = " // Page select"
- elif reg == 0x7F:
- reg_comment = " // Book select"
- else:
- reg_comment = get_reg_comment(current_page, reg, data_len)
- out.append(
- f' 0x{reg:02X}, {data_len}, {data_hex},{reg_comment}'
- )
- total_bytes += 2 + data_len # reg + len + data
- out.append(' 0xFF, 0xFF // end of stream')
- total_bytes += 2
- out.append('};')
- out.append(f'// Total: {total_bytes} bytes')
- out.append('')
- return '\n'.join(out)
- def convert_bin(cfg_path):
- """Convert a .cfg file to a raw binary packed byte stream."""
- with open(cfg_path) as f:
- lines = f.readlines()
- data = bytearray()
- for line in lines:
- stripped = line.strip()
- if not stripped or stripped.startswith('#'):
- continue
- if stripped.startswith('w '):
- parts = stripped.split()
- reg = int(parts[2], 16)
- data_bytes = [int(b, 16) for b in parts[3:]]
- data.append(reg)
- data.append(len(data_bytes))
- data.extend(data_bytes)
- data.extend([0xFF, 0xFF])
- return bytes(data)
- if __name__ == '__main__':
- if len(sys.argv) < 2:
- print(f"Usage: {sys.argv[0]} [--bin] <cfg_file>", file=sys.stderr)
- sys.exit(1)
- bin_mode = '--bin' in sys.argv
- cfg_file = [a for a in sys.argv[1:] if a != '--bin'][0]
- if bin_mode:
- out_path = cfg_file.rsplit('.', 1)[0] + '.bin'
- data = convert_bin(cfg_file)
- with open(out_path, 'wb') as f:
- f.write(data)
- print(f"Wrote {len(data)} bytes to {out_path}", file=sys.stderr)
- else:
- print(convert(cfg_file))
|