Skip to content

Volume setting properties don't work right #9

@samblenny

Description

@samblenny

In the past several days, there's been some discussion on the forum and discord about people having trouble using the TLV320 volume settings on Fruit Jam, including issues with blowing out speakers. I'll leave it to someone else to describe all that stuff. The tldr version is the dac.speaker_volume and dac.headphone_volume setters don't work as expected. This issue is about what I've found while looking for mismatches between the code in Adafruit_CircuitPython_TLV320/adafruit_tlv320.py and the register descriptions in the TLV320 datasheet.

Gain Staging Register Cheat Sheet [edited 8/21]

For all of the stuff below, it's helpful to familiarize yourself with the Functional Block Diagram shown in sections 1.4 and 6.2 of the datasheet. In particular, for each speaker channel and the headphone jack, there are three important gain stages:

  1. Before the DAC, the sample values go through a digital volume stage with attenuation set by Page 0 Registers 64-66
  2. After the DAC, the signals go through an analog attenuation stage with attenuation set by Page 1 Registers 38, 36, and 37
  3. After analog attenuation stage, the signals go through an analog amplifier stage with gain set by Page 1 Registers 42, 40, and 41

Analog Signal Chain Cheat Sheet [edited 8/21]

  1. The DAC (digital to analog converter) output has two analog channels, L and R.
  2. There are two analog inputs, AIN1 and AIN2. These are available as solder pads on the Adafruit TLV320DAC3100 breakout but unused on Fruit Jam.
  3. The DAC outputs and analog inputs go through an analog mixing stage to configure which signals go to which outputs. This is controlled by register P1/R35=0x23. CAUTION: Depending on the setting of bits in P1/R35=0x23, it may be possible to intentionally or accidentally bypass the analog attenuation stage.
  4. After the mixing stage, there are three analog signal chains which the datasheet calls SPK for the mono speaker output, HPL for headphone jack left output, and HPR for headphone jack right output.
  5. Each signal chain has an analog attenuation stage (0 to -78 dB) followed by an amplifier stage (+6 dB to +24 dB for SPK; 0 dB to +9 dB for HPL and HPR).
  6. The signal chains for SPK and HPL share a mixer. That means the speaker and left headphone channel share the same signal source, but their attenuation and amplifier gain settings to control the volume of that signal are independently adjustable.
  7. The signal chain for HPR gets its own mixer.

Problems [edited 8/21, 8/22]

In adafruit_tlv320.py, def speaker_volume() calls self._page1._set_spk_volume(), which includes the line:

self._write_register(_SPK_VOL, value)

Then, _SPK_VOL is defined as _SPK_VOL = const(0x26) (page 1, register 38). On the Functional Block Diagram in section 1.4, Page 1 / Register 38 is "Analog Attenuation" for the left speaker channel (range 0 dB to -78 dB). That path also goes through the "Mono Class-D Speaker Driver" stage (Page 1 / Register 42=0x2A, range +6 dB to +24 dB). That register gets set by _configure_spk_pga(), which gets called by speaker_gain().

It looks to me like the bitfield shifting and masking for Page 1 / Register 42=0x2A are implemented correctly for the amplifier gain bits and mute bit. But, it's not clear to me why the registers for the right channel attenuation and speaker gain seem to be ignored. Perhaps there's some setting that makes the right channel follow the left channel, or maybe it's an oversight. [edit 8/21: it's because Fruit Jam and breakout board speaker jacks are mono]

The most suspicious thing I see is this:

    @speaker_volume.setter
    def speaker_volume(self, db: float) -> None:
        """

        :param db: Volume in dB (0 = max, -63.5 = min)
        """
        if db > 0:
            db = 0
        gain = int(55 + (db * 1.14))
        gain = max(0, min(gain, 127))
        self._page1._set_spk_volume(route_enabled=True, gain=gain)

The comment says 0 dB to -63.5 dB, but that's the range for the digital volume control (Page 0 / Register 65=0x41 and Page 0 / Register 66=0x42). Also, the gain scaling and clipping does not seem appropriate. The correct range for Page 1 / Register 38=0x26 is 0 dB to -78 dB (table 6-106 in the datasheet). So, it looks like the driver is conflating the digital volume adjustment stage (Page 0, registers 64..66) with the analog attenuation stage (Page 1, registers 38, 37, and 36).

The proper values for setting Page 1 / Register 38=0x26 are listed in Table 6-24.

I made a scatter plot of TLV320 datasheet table 6-24 for the speaker volume register value vs. analog gain dB. It turns out to be a non-linear piecewise function, so the linear formula in speaker_volume() can't be right.

Image

This is as far as I've gotten so far with checking the driver's volume setting code against the datasheet. It will take me a while to read the rest of the relevant stuff, so I'm starting this issue now before I forget all the details. I kinda expect there may be a similar problem with the setter for dac.headphone_volume.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions