summaryrefslogtreecommitdiff
path: root/tools/proxyclient/experiments/speaker_amp.py
blob: 7a592c35535bed830bdef05d87a30049c4f7f630 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
import sys, pathlib
import time
sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))

# speaker_amp.py -- play audio through the embedded speaker on Mac mini
#
# sample usage with sox:
#
#   sox INPUT_FILE -t raw -r 48000 -c 1 -e signed-int -b 32 -L - gain -63 | python3 ./speaker_amp.py
#
# (expects mono, 24-bit signed samples padded to 32 bits on the msb side)

import argparse
from m1n1.setup import *
from m1n1.hw.dart import DART, DARTRegs
from m1n1.hw.i2c import I2C
from m1n1.hw.pmgr import PMGR
from m1n1.hw.nco import NCO
from m1n1.hw.admac import *
from m1n1.hw.mca import *

argparser = argparse.ArgumentParser()
argparser.add_argument("-f", "--file", "--input", "--samples",
                       type=str, default=None,
                       help='input filename to take samples from ' \
                            '(default: standard input)')
argparser.add_argument("-b", "--bufsize", type=int, default=1024*32,
                       help='size of buffers to keep submitting to DMA')
args = argparser.parse_args()

inputf = open(args.file, "rb") if args.file is not None \
            else sys.stdin.buffer 

p.pmgr_adt_clocks_enable("/arm-io/i2c1")
p.pmgr_adt_clocks_enable("/arm-io/admac-sio")
p.pmgr_adt_clocks_enable("/arm-io/dart-sio")
p.pmgr_adt_clocks_enable("/arm-io/mca-switch")

# reset AUDIO_P
PS_AUDIO_P = PMGR(u).regs[0].PS4[5]
PS_AUDIO_P.set(DEV_DISABLE=1)
PS_AUDIO_P.set(RESET=1)
PS_AUDIO_P.set(RESET=0)
PS_AUDIO_P.set(DEV_DISABLE=0)

i2c1 = I2C(u, "/arm-io/i2c1")

dart_base, _ = u.adt["/arm-io/dart-sio"].get_reg(0) # stream index 2
dart = DART(iface, DARTRegs(u, dart_base), util=u)
dart.initialize()

cl_no = 0

admac = ADMAC(u, "/arm-io/admac-sio", dart, debug=True)
tx_chan = admac.chans[4*cl_no]

tx_chan.disable()
tx_chan.reset()
tx_chan.read_reports() # read stale reports
tx_chan.buswidth = E_BUSWIDTH.W_32BIT
tx_chan.framesize = E_FRAME.F_1_WORD

nco = NCO(u, "/arm-io/nco")
nco[cl_no].set_rate(48000 * 256)
nco[cl_no].enable()

mca_switch1_base = u.adt["/arm-io/mca-switch"].get_reg(1)[0]
mca_cl_base = u.adt["/arm-io/mca-switch"].get_reg(0)[0] + 0x4000*cl_no
cl = MCACluster(u, mca_cl_base)

regs, serdes = cl.regs, cl.txa

regs.SYNCGEN_STATUS.set(RST=1, EN=0)
regs.SYNCGEN_STATUS.set(RST=0)
regs.SYNCGEN_MCLK_SEL.val =(1 + cl_no)
regs.SYNCGEN_HI_PERIOD.val = 0
regs.SYNCGEN_LO_PERIOD.val = 0xfe  # full period minus two

serdes.STATUS.set(EN=0)
serdes.CONF.set(
    NSLOTS=0,
    SLOT_WIDTH=E_SLOT_WIDTH.W_32BIT,
    BCLK_POL=1,
    UNK1=1, UNK2=1,
    IDLE_UNDRIVEN=1,
    SYNC_SEL=(1 + cl_no)
)
serdes.BITDELAY.val = 0

serdes.CHANMASK[0].val = 0xffff_fffe
serdes.CHANMASK[1].val = 0xffff_fffe

regs.PORT_ENABLES.set(CLOCK1=1, CLOCK2=1, DATA=1)
regs.PORT_CLK_SEL.set(SEL=(cl_no + 1))
regs.PORT_DATA_SEL.val = cl_no + 1
regs.MCLK_STATUS.set(EN=1)
regs.SYNCGEN_STATUS.set(EN=1)

p.write32(mca_switch1_base + 0x8000*cl_no, 0x102048)

# toggle the GPIO line driving the speaker-amp IC reset
p.write32(0x23c1002d4, 0x76a02) # invoke reset
p.write32(0x23c1002d4, 0x76a03) # take out of reset

tx_chan.submit(inputf.read(args.bufsize))
tx_chan.enable()
while tx_chan.can_submit():
    tx_chan.submit(inputf.read(args.bufsize))

serdes.STATUS.set(EN=1)

# by ADT and leaked schematic, i2c1 contains TAS5770L,
# which is not a public part. but there's e.g. TAS2770
# with similar registers
#
# https://www.ti.com/product/TAS2770
#
# if the speaker-amp IC loses clock on the serial sample input,
# it automatically switches to software shutdown.
#

i2c1.write_reg(0x31, 0x08, [0x40])
i2c1.write_reg(0x31, 0x0a, [0x06, 0x00, 0x1a])
i2c1.write_reg(0x31, 0x1b, [0x01, 0x82, 0x06])
i2c1.write_reg(0x31, 0x16, [0x50, 0x04])
i2c1.write_reg(0x31, 0x0d, [0x00])
#i2c1.write_reg(0x31, 0x03, [0x14])

# amplifier gain, presumably this is the lowest setting
i2c1.write_reg(0x31, 0x03, [0x0])

# take the IC out of software shutdown
i2c1.write_reg(0x31, 0x02, [0x0c])

while (buf := inputf.read(args.bufsize)):
    while not tx_chan.can_submit():
        tx_chan.poll()
    tx_chan.submit(buf)

# mute
i2c1.write_reg(0x31, 0x02, [0x0d])

# software shutdown
i2c1.write_reg(0x31, 0x02, [0x0e])

tx_chan.disable()