ASoC: tas27{64,70}: improve support for Apple codec variants
cover ASoC: tas27{64,70}: improve support for Apple codec variants
Hi all,
This series introduces a number of changes to the drivers for the Texas Instruments TAS2764 and TAS2770 amplifiers in order to introduce (and improve in the case of TAS2770) support for the variants of these amps found in Apple Silicon Macs.
Apple's variant of TAS2764 is known as SN012776, and as always with Apple is a subtly incompatible variant with a number of quirks. It is not publicly available. The TAS2770 variant is known as TAS5770L, and does not require incompatible handling.
Much as with the Cirrus codec patches, I do not
expect that we will get any official acknowledgement that these parts exist from TI, however I would be delighted to be proven wrong.
This series has been living in the downstream Asahi kernel tree[1] for over two years, and has been tested by many thousands of users by this point[2].
Regards,
James
[1] https://github.com/AsahiLinux/linux/tree/asahi-wip
[2] https://stats.asahilinux.org/
---
Hector Martin (14):
ASoC: tas2764: Enable main IRQs
ASoC: tas2764: Power up/down amp on mute ops
ASoC: tas2764: Add SDZ regulator
ASoC: tas2764: Add reg defaults for TAS2764_INT_CLK_CFG
ASoC: tas2764: Mark SW_RESET as volatile
ASoC: tas2764: Fix power control mask
ASoC: tas2764: Wait for ramp-down after shutdown
ASoC: tas2770: Add SDZ regulator
ASoC: tas2770: Power cycle amp on ISENSE/VSENSE change
ASoC: tas2770: Add zero-fill and pull-down controls
ASoC: tas2770: Support setting the PDM TX slot
ASoC: tas2770: Fix volume scale
ASoC: tas2764: Set the SDOUT polarity correctly
ASoC: tas2770: Set the SDOUT polarity correctly
James Calligeros (2):
ASoC: dt-bindings: tas27xx: add compatible for SN012776
ASoC: dt-bindings: tas2770: add compatible for TAS5770L
Martin Povišer (11):
ASoC: tas2764: Extend driver to SN012776
ASoC: tas2764: Add control concerning overcurrent events
ASoC: tas2770: Factor out set_ivsense_slots
ASoC: tas2770: Fix and redo I/V sense TDM slot setting logic
ASoC: tas2764: Reinit cache on part reset
ASoC: tas2764: Configure zeroing of SDOUT slots
ASoC: tas2764: Apply Apple quirks
ASoC: tas2764: Raise regmap range maximum
ASoC: tas2770: Export 'die_temp' to sysfs
ASoC: tas2764: Export 'die_temp' to sysfs
ASoC: tas2764: Crop SDOUT zero-out mask based on BCLK ratio
.../bindings/sound/ti,tas2770.yaml | 1 + .../bindings/sound/ti,tas27xx.yaml | 1 + sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/tas2764-quirks.h | 188 +++++++++++++++ sound/soc/codecs/tas2764.c | 302 +++++++++++++++++++++---- sound/soc/codecs/tas2764.h | 29 ++- sound/soc/codecs/tas2770.c | 264 +++++++++++++++++---- sound/soc/codecs/tas2770.h | 20 ++ 8 files changed, 711 insertions(+), 95 deletions(-)
---
base-commit: cc7708ae5e2aab296203fcec774695fc9d995f48
change-id: 20250214-apple-codec-changes-6e656dc1e24d
Best regards,
Signature · 1 line
James Calligeros <[email protected]>
PATCH 1/27 ASoC: dt-bindings: tas27xx: add compatible for SN012776
The TI SN012776 is a variant of TAS2764 found in Apple Silicon Macs. It continues Apple's long-standing policy of getting vendors to spin out subtly incompatible and Apple-exclusive variants of their publicly available parts.
--- .../bindings/sound/ti,tas27xx.yaml | 1 + 1 file changed, 1 insertion(+)
Documentation/devicetree/bindings/sound/ti,tas27xx.yaml +1 -0
@@ -24,6 +24,7 @@ properties: enum: - ti,tas2764 - ti,tas2780 + - ti,sn012776 # Apple variant of TAS2764 reg: maxItems: 1
Signature · 1 line
2.48.1
PATCH 2/27 ASoC: dt-bindings: tas2770: add compatible for TAS5770L
In reply to James CalligerosA variant of TAS2770 known as TAS5770L is found in Apple Silicon Macs.
--- .../bindings/sound/ti,tas2770.yaml | 1 + 1 file changed, 1 insertion(+)
Documentation/devicetree/bindings/sound/ti,tas2770.yaml +1 -0
@@ -23,6 +23,7 @@ properties: compatible: enum: - ti,tas2770 + - ti,tas5770l # Apple variant reg: maxItems: 1
Signature · 1 line
2.48.1
PATCH 3/27 ASoC: tas2764: Extend driver to SN012776
In reply to James CalligerosFrom: Martin Povišer <[email protected]>
SN012776 is a speaker amp chip found in Apple's 2021 laptops. It appears similar and more-or-less compatible to TAS2764. Extend the TAS2764 driver with some SN012776 specifics and configure the chip assuming it's in one of the Apple machines.
This driver already partially depended on CONFIG_OF/Devicetree, so drop the pretence that it doesn't, too.
--- sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/tas2764.c | 46 ++++++++++++++++++++++--- sound/soc/codecs/tas2764.h | 3 ++ 3 files changed, 45 insertions(+), 5 deletions(-)
sound/soc/codecs/Kconfig +1 -0
@@ -1965,6 +1965,7 @@ config SND_SOC_TAS2562 config SND_SOC_TAS2764 tristate "Texas Instruments TAS2764 Mono Audio amplifier" depends on I2C + depends on OF config SND_SOC_TAS2770 tristate "Texas Instruments TAS2770 speaker amplifier"
sound/soc/codecs/tas2764.c +41 -5
101 lines not on this page · raw message
sound/soc/codecs/tas2764.h +3 -0
@@ -29,6 +29,7 @@#define TAS2764_PWR_CTRL_ACTIVE 0x0#define TAS2764_PWR_CTRL_MUTE BIT(0)#define TAS2764_PWR_CTRL_SHUTDOWN BIT(1) +#define TAS2764_PWR_CTRL_BOP_SRC BIT(7)#define TAS2764_VSENSE_POWER_EN 3#define TAS2764_ISENSE_POWER_EN 4
@@ -110,4 +111,6 @@#define TAS2764_INT_CLK_CFG TAS2764_REG(0x0, 0x5c)#define TAS2764_INT_CLK_CFG_IRQZ_CLR BIT(2) +#define TAS2764_BOP_CFG0 TAS2764_REG(0X0, 0x1d) +#endif /* __TAS2764__ */
Signature · 1 line
2.48.1
Re: [PATCH 03/27] ASoC: tas2764: Extend driver to SN012776
sound/soc/codecs/Kconfig config SND_SOC_TAS2562 · line 1968
Quoted 4 lines
config
SND_SOC_TAS2764sound/soc/codecs/Kconfig +1
-0tristate "Texas Instruments TAS2764 Mono Audio amplifier" depends on I2C + depends on OF
Do we want to drop support for !OF configurations? The alternative would
be extra #ifdef soup. I don't know if this is a useful build configuration but we should be deliberate about adding the depends.
Mark Brown
maintainerRe: [PATCH 03/27] ASoC: tas2764: Extend driver to SN012776
Quoted 7 lines · Alyssa Rosenzweig
sound/soc/codecs/Kconfig config SND_SOC_TAS2562 · line 1968
Quoted 4 lines
config
SND_SOC_TAS2764sound/soc/codecs/Kconfig +1
-0tristate "Texas Instruments TAS2764 Mono Audio amplifier" depends on I2C + depends on OFDo we want to drop support for !OF configurations? The alternative would
be extra #ifdef soup. I don't know if this is a useful build configuration but we should be deliberate about adding the depends.
If the Apple variants of these chips are making their way into laptops I'd not be surprised if the other variants might appear in x86 laptops too. What's the specific reason for dropping the build coverage, is it actually getting in the way at all? We have lots of drivers which build happily with !OF even if practically speaking they'll never appear on such systems since it helps improve build coverage which makes life easier for people doing general coverage.
PATCH 4/27 ASoC: tas2764: Add control concerning overcurrent events
In reply to James CalligerosFrom: Martin Povišer <[email protected]>
Add control to expose the option of autoretry behavior on overcurrent events in the codec.
--- sound/soc/codecs/tas2764.c | 9 +++++++++ sound/soc/codecs/tas2764.h | 4 ++++ 2 files changed, 13 insertions(+)
sound/soc/codecs/tas2764.c +9 -0
@@ -623,12 +623,21 @@ static SOC_ENUM_SINGLE_DECL( tas2764_hpf_enum,TAS2764_DC_BLK0 , TAS2764_DC_BLK0_HPF_FREQ_PB_SHIFT, tas2764_hpf_texts); +static const char *const tas2764_oce_texts[] = { +"Disable" ,"Retry" , +}; + +static SOC_ENUM_SINGLE_DECL ( + tas2764_oce_enum, TAS2764_MISC_CFG1, + TAS2764_MISC_CFG1_OCE_RETRY_SHIFT, tas2764_oce_texts); +static const struct snd_kcontrol_new tas2764_snd_controls[] = {SOC_SINGLE_TLV ("Speaker Volume" ,TAS2764_DVC ,0 ,TAS2764_DVC_MAX ,1 , tas2764_playback_volume),SOC_SINGLE_TLV ("Amp Gain Volume" ,TAS2764_CHNL_0 ,1 ,0x14 ,0 , tas2764_digital_tlv),SOC_ENUM ("HPF Corner Frequency" , tas2764_hpf_enum), +SOC_ENUM ("OCE Handling" , tas2764_oce_enum), };static const struct snd_soc_component_driver soc_component_driver_tas2764 = {
sound/soc/codecs/tas2764.h +4 -0
@@ -44,6 +44,10 @@#define TAS2764_CHNL_0 TAS2764_REG(0X0, 0x03) +/* Miscellaneous */ +#define TAS2764_MISC_CFG1 TAS2764_REG(0x0, 0x06) +#define TAS2764_MISC_CFG1_OCE_RETRY_SHIFT 5 +/* TDM Configuration Reg0 */ #define TAS2764_TDM_CFG0 TAS2764_REG(0X0, 0x08)#define TAS2764_TDM_CFG0_SMP_MASK BIT(5)
Signature · 1 line
2.48.1
Re: [PATCH 04/27] ASoC: tas2764: Add control concerning overcurrent events
Hi James,
thanks for your upstreaming efforts. Let me make one request below.
Quoted 3 lines · James Calligeros
From: Martin Povišer <[email protected]>
The protonmail address appears here by accident. Please replace with [email protected] to match the signed-off-by.
Martin
Quoted 5 lines
Add control to expose the option of autoretry behavior on overcurrent events in the codec.
Signed-off-by Martin Povišer <[email protected]>Signed-off-by James Calligeros <[email protected]>
PATCH 5/27 ASoC: tas2770: Factor out set_ivsense_slots
In reply to James CalligerosFrom: Martin Povišer <[email protected]>
Add a new explicit function for the setting of I/V sense TDM slots.
--- sound/soc/codecs/tas2770.c | 40 +++++++++++++++++-------- 1 file changed, 27 insertions(+), 13 deletions(-)
sound/soc/codecs/tas2770.c +27 -13
@@ -189,6 +189,31 @@ static int tas2770_mute(struct snd_soc_dai *dai, int mute, int direction)return tas2770_update_pwr_ctrl (tas2770); } +static int tas2770_set_ivsense_transmit (struct tas2770_priv *tas2770, +int i_slot,int v_slot) +{ +struct snd_soc_component *component = tas2770->component; +int ret; + + ret =snd_soc_component_update_bits (component,TAS2770_TDM_CFG_REG5 , +TAS2770_TDM_CFG_REG5_VSNS_MASK | +TAS2770_TDM_CFG_REG5_50_MASK , +TAS2770_TDM_CFG_REG5_VSNS_ENABLE | + v_slot); +if (ret <0 ) +return ret; + + ret =snd_soc_component_update_bits (component,TAS2770_TDM_CFG_REG6 , +TAS2770_TDM_CFG_REG6_ISNS_MASK | +TAS2770_TDM_CFG_REG6_50_MASK , +TAS2770_TDM_CFG_REG6_ISNS_ENABLE | + i_slot); +if (ret <0 ) +return ret; + +return 0 ; +} +static int tas2770_set_bitwidth (struct tas2770_priv *tas2770,int bitwidth) {int ret;
@@ -221,19 +246,8 @@ static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth)if (ret <0 )return ret; - ret =snd_soc_component_update_bits (component,TAS2770_TDM_CFG_REG5 , -TAS2770_TDM_CFG_REG5_VSNS_MASK | -TAS2770_TDM_CFG_REG5_50_MASK , -TAS2770_TDM_CFG_REG5_VSNS_ENABLE | - tas2770->v_sense_slot); -if (ret <0 ) -return ret; - - ret =snd_soc_component_update_bits (component,TAS2770_TDM_CFG_REG6 , -TAS2770_TDM_CFG_REG6_ISNS_MASK | -TAS2770_TDM_CFG_REG6_50_MASK , -TAS2770_TDM_CFG_REG6_ISNS_ENABLE | - tas2770->i_sense_slot); + ret =tas2770_set_ivsense_transmit (tas2770, tas2770->i_sense_slot, + tas2770->v_sense_slot);if (ret <0 )return ret;
Signature · 1 line
2.48.1
PATCH 6/27 ASoC: tas2770: Fix and redo I/V sense TDM slot setting logic
In reply to James CalligerosFrom: Martin Povišer <[email protected]>
The former code sets the V slot from inside set_bitwidth according to the bitwidth of the PCM format. That's wrong, since:
* It overrides the V slot parsed from DT binding.
* The V slot is set shifted behind the I slot by the length of the PCM bitwidth, but the PCM bitwidth has no assured relation to the TDM slot width.
Replace the former logic by setting up the I/V sense transmission only in case of both I/V slots being specified in devicetree, and never override those values. In case the slots are left unspecified, disable the transmission completely.
There's an improbable case someone is relying on the old behavior, but if so, that's a setup that only works by accident, and cannot be sanely supported going forward. There's no indication anyone is consuming the I/V sense data up to today, so break the former behavior.
--- sound/soc/codecs/tas2770.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-)
sound/soc/codecs/tas2770.c +11 -10
@@ -224,19 +224,16 @@ static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) ret =snd_soc_component_update_bits (component,TAS2770_TDM_CFG_REG2 ,TAS2770_TDM_CFG_REG2_RXW_MASK ,TAS2770_TDM_CFG_REG2_RXW_16BITS ); - tas2770->v_sense_slot = tas2770->i_sense_slot +2 ;break ;case SNDRV_PCM_FORMAT_S24_LE : ret =snd_soc_component_update_bits (component,TAS2770_TDM_CFG_REG2 ,TAS2770_TDM_CFG_REG2_RXW_MASK ,TAS2770_TDM_CFG_REG2_RXW_24BITS ); - tas2770->v_sense_slot = tas2770->i_sense_slot +4 ;break ;case SNDRV_PCM_FORMAT_S32_LE : ret =snd_soc_component_update_bits (component,TAS2770_TDM_CFG_REG2 ,TAS2770_TDM_CFG_REG2_RXW_MASK ,TAS2770_TDM_CFG_REG2_RXW_32BITS ); - tas2770->v_sense_slot = tas2770->i_sense_slot +4 ;break ;default :
@@ -246,11 +243,6 @@ static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth)if (ret <0 )return ret; - ret =tas2770_set_ivsense_transmit (tas2770, tas2770->i_sense_slot, - tas2770->v_sense_slot); -if (ret <0 ) -return ret; -return 0 ; }
@@ -505,6 +497,7 @@ static int tas2770_codec_probe(struct snd_soc_component *component) {struct tas2770_priv *tas2770 =snd_soc_component_get_drvdata (component); +int ret; tas2770->component = component;
@@ -516,6 +509,14 @@ static int tas2770_codec_probe(struct snd_soc_component *component)tas2770_reset (tas2770);regmap_reinit_cache (tas2770->regmap, &tas2770_i2c_regmap); +if (tas2770->i_sense_slot !=-1 && tas2770->v_sense_slot !=-1 ) { + ret =tas2770_set_ivsense_transmit (tas2770, tas2770->i_sense_slot, + tas2770->v_sense_slot); + +if (ret <0 ) +return ret; + } +return 0 ; }
@@ -643,7 +644,7 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770)dev_info (tas2770->dev,"Property %s is missing setting default slot\n" ,"ti,imon-slot-no" ); - tas2770->i_sense_slot =0 ; + tas2770->i_sense_slot =-1 ; } rc = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no",
@@ -652,7 +653,7 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770)dev_info (tas2770->dev,"Property %s is missing setting default slot\n" ,"ti,vmon-slot-no" ); - tas2770->v_sense_slot =2 ; + tas2770->v_sense_slot =-1 ; } tas2770->sdz_gpio =devm_gpiod_get_optional (dev,"shutdown" ,GPIOD_OUT_HIGH );
Signature · 1 line
2.48.1
PATCH 7/27 ASoC: tas2764: Reinit cache on part reset
In reply to James CalligerosFrom: Martin Povišer <[email protected]>
When the part is reset in component_probe, do not forget to reinit the regcache, otherwise the cache can get out of sync with the part's actual state. (This fix is similar to commit 0a0342ede303 which concerned the tas2770 driver.)
--- sound/soc/codecs/tas2764.c | 3 +++ 1 file changed, 3 insertions(+)
sound/soc/codecs/tas2764.c +3 -0
@@ -539,6 +539,8 @@ static uint8_t sn012776_bop_presets[] = { 0x06, 0x3e, 0x37, 0x30, 0xff, 0xe6 }; +static const struct regmap_config tas2764_i2c_regmap; +static int tas2764_codec_probe (struct snd_soc_component *component) {struct tas2764_priv *tas2764 =snd_soc_component_get_drvdata (component);
@@ -552,6 +554,7 @@ static int tas2764_codec_probe(struct snd_soc_component *component) }tas2764_reset (tas2764); +regmap_reinit_cache (tas2764->regmap, &tas2764_i2c_regmap);if (tas2764->irq) { ret =snd_soc_component_write (tas2764->component,TAS2764_INT_MASK0 ,0xff );
Signature · 1 line
2.48.1
PATCH 8/27 ASoC: tas2764: Configure zeroing of SDOUT slots
In reply to James CalligerosFrom: Martin Povišer <[email protected]>
The codec has an option to zero out certain TDM slots on its SDOUT output according to a preconfigured mask (otherwise the output is, for the duration of unused slots, in a Hi-Z state). Configure this feature based on a mask read from the devicetree.
--- sound/soc/codecs/tas2764.c | 23 +++++++++++++++++++++++ sound/soc/codecs/tas2764.h | 11 +++++++++++ 2 files changed, 34 insertions(+)
sound/soc/codecs/tas2764.c +23 -0
@@ -40,6 +40,7 @@ struct tas2764_priv {int v_sense_slot;int i_sense_slot; +u32 sdout_zero_mask;bool dac_powered;bool unmuted;
@@ -594,6 +595,23 @@ static int tas2764_codec_probe(struct snd_soc_component *component)if (ret <0 )return ret; +if (tas2764->sdout_zero_mask) { +for (i =0 ; i <4 ; i++) { + ret =snd_soc_component_write (component,TAS2764_SDOUT_HIZ_1 + i, + (tas2764->sdout_zero_mask >> (i *8 )) &0xff ); + +if (ret <0 ) +return ret; + } + + ret =snd_soc_component_update_bits (component,TAS2764_SDOUT_HIZ_9 , +TAS2764_SDOUT_HIZ_9_FORCE_0_EN , +TAS2764_SDOUT_HIZ_9_FORCE_0_EN ); + +if (ret <0 ) +return ret; + } +if (tas2764->devid ==DEVID_SN012776 ) { ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, TAS2764_PWR_CTRL_BOP_SRC,
@@ -736,6 +754,11 @@ static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764)if (ret) tas2764->v_sense_slot =2 ; + ret =fwnode_property_read_u32 (dev->fwnode,"ti,sdout-force-zero-mask" , + &tas2764->sdout_zero_mask); +if (ret) + tas2764->sdout_zero_mask =0 ; +return 0 ; }
sound/soc/codecs/tas2764.h +11 -0
@@ -117,4 +117,15 @@#define TAS2764_BOP_CFG0 TAS2764_REG(0X0, 0x1d) +#define TAS2764_SDOUT_HIZ_1 TAS2764_REG(0x1, 0x3d) +#define TAS2764_SDOUT_HIZ_2 TAS2764_REG(0x1, 0x3e) +#define TAS2764_SDOUT_HIZ_3 TAS2764_REG(0x1, 0x3f) +#define TAS2764_SDOUT_HIZ_4 TAS2764_REG(0x1, 0x40) +#define TAS2764_SDOUT_HIZ_5 TAS2764_REG(0x1, 0x41) +#define TAS2764_SDOUT_HIZ_6 TAS2764_REG(0x1, 0x42) +#define TAS2764_SDOUT_HIZ_7 TAS2764_REG(0x1, 0x43) +#define TAS2764_SDOUT_HIZ_8 TAS2764_REG(0x1, 0x44) +#define TAS2764_SDOUT_HIZ_9 TAS2764_REG(0x1, 0x45) +#define TAS2764_SDOUT_HIZ_9_FORCE_0_EN BIT(7) +#endif /* __TAS2764__ */
Signature · 1 line
2.48.1
PATCH 9/27 ASoC: tas2764: Apply Apple quirks
In reply to James CalligerosFrom: Martin Povišer <[email protected]>
Apple's SN012776 driver has some peculiar aspects to its behavior that are suspected to work around issues in the codec part. Add a module parameter for enabling individual quirks that should be imitated after the Apple driver.
Setting some of these by default seems to be required. For example, setting 0xf fixes an issue with transient overcurrent errors which can crash the chip until the next system reboot. To be safe, let's enable all of them by default.
--- sound/soc/codecs/tas2764-quirks.h | 188 +++++++++++++++++++++++++ sound/soc/codecs/tas2764.c | 38 +++++ 2 files changed, 226 insertions(+)
sound/soc/codecs/tas2764-quirks.h new file +188 -0
@@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __TAS2764_QUIRKS__ +#define __TAS2764_QUIRKS__ + +#include <linux/regmap.h> + +#include "tas2764.h" + +/* Bitmask of enabled Apple quirks */ +#define ENABLED_APPLE_QUIRKS 0x3f + +/* +* Disable noise gate and flip down reserved bit in NS_CFG0 +*/ +#define TAS2764_NOISE_GATE_DISABLE BIT(0) + +static const struct reg_sequence tas2764_noise_gate_dis_seq[] = { +REG_SEQ0 (TAS2764_REG (0x0 ,0x35 ),0xb0 ) +}; + +/* +* CONV_VBAT_PVDD_MODE=1 +*/ +#define TAS2764_CONV_VBAT_PVDD_MODE BIT(1) + +static const struct reg_sequence tas2764_conv_vbat_pvdd_mode_seq[] = { +REG_SEQ0 (TAS2764_REG (0x0 ,0x6b ),0x41 ) +}; + +/* +* Reset of DAC modulator when DSP is OFF +*/ +#define TAS2764_DMOD_RST BIT(2) + +static const struct reg_sequence tas2764_dmod_rst_seq[] = { +REG_SEQ0 (TAS2764_REG (0x0 ,0x76 ),0x0 ) +}; + +/* +* Unknown 0x133/0x137 writes (maybe TDM related) +*/ +#define TAS2764_UNK_SEQ0 BIT(3) + +static const struct reg_sequence tas2764_unk_seq0[] = { +REG_SEQ0 (TAS2764_REG (0x1 ,0x33 ),0x80 ), +REG_SEQ0 (TAS2764_REG (0x1 ,0x37 ),0x3a ), +}; + +/* +* Unknown 0x614 - 0x61f writes +*/ +#define TAS2764_APPLE_UNK_SEQ1 BIT(4) + +static const struct reg_sequence tas2764_unk_seq1[] = { +REG_SEQ0 (TAS2764_REG (0x6 ,0x14 ),0x0 ), +REG_SEQ0 (TAS2764_REG (0x6 ,0x15 ),0x13 ), +REG_SEQ0 (TAS2764_REG (0x6 ,0x16 ),0x52 ), +REG_SEQ0 (TAS2764_REG (0x6 ,0x17 ),0x0 ), +REG_SEQ0 (TAS2764_REG (0x6 ,0x18 ),0xe4 ), +REG_SEQ0 (TAS2764_REG (0x6 ,0x19 ),0xc ), +REG_SEQ0 (TAS2764_REG (0x6 ,0x16 ),0xaa ), +REG_SEQ0 (TAS2764_REG (0x6 ,0x1b ),0x0 ), +REG_SEQ0 (TAS2764_REG (0x6 ,0x1c ),0x12 ), +REG_SEQ0 (TAS2764_REG (0x6 ,0x1d ),0xa0 ), +REG_SEQ0 (TAS2764_REG (0x6 ,0x1e ),0xd8 ), +REG_SEQ0 (TAS2764_REG (0x6 ,0x1f ),0x0 ), +}; + +/* +* Unknown writes in the 0xfd page (with secondary paging inside) +*/ +#define TAS2764_APPLE_UNK_SEQ2 BIT(5) + +static const struct reg_sequence tas2764_unk_seq2[] = { +REG_SEQ0 (TAS2764_REG (0xfd ,0x0d ),0xd ), +REG_SEQ0 (TAS2764_REG (0xfd ,0x6c ),0x2 ), +REG_SEQ0 (TAS2764_REG (0xfd ,0x6d ),0xf ), +REG_SEQ0 (TAS2764_REG (0xfd ,0x0d ),0x0 ), +}; + +/* +* Disable 'Thermal Threshold 1' +*/ +#define TAS2764_THERMAL_TH1_DISABLE BIT(6) + +static const struct reg_sequence tas2764_thermal_th1_dis_seq[] = { +REG_SEQ0 (TAS2764_REG (0x1 ,0x47 ),0x2 ), +}; + +/* +* Imitate Apple's shutdown dance +*/ +#define TAS2764_SHUTDOWN_DANCE BIT(7) + +static const struct reg_sequence tas2764_shutdown_dance_init_seq[] = { +/* +* SDZ_MODE=01 (immediate) +* +* We want the shutdown to happen under the influence of +* the magic writes in the 0xfdXX region, so make sure +* the shutdown is immediate and there's no grace period +* followed by the codec part. +*/ +REG_SEQ0 (TAS2764_REG (0x0 ,0x7 ),0x60 ), +}; + +static const struct reg_sequence tas2764_pre_shutdown_seq[] = { +REG_SEQ0 (TAS2764_REG (0xfd ,0x0d ),0xd ),/* switch hidden page */ +REG_SEQ0 (TAS2764_REG (0xfd ,0x64 ),0x4 ),/* do write (unknown semantics) */ +REG_SEQ0 (TAS2764_REG (0xfd ,0x0d ),0x0 ),/* switch hidden page back */ +}; + +static const struct reg_sequence tas2764_post_shutdown_seq[] = { +REG_SEQ0 (TAS2764_REG (0xfd ,0x0d ),0xd ), +REG_SEQ0 (TAS2764_REG (0xfd ,0x64 ),0x0 ),/* revert write from pre sequence */ +REG_SEQ0 (TAS2764_REG (0xfd ,0x0d ),0x0 ), +}; + +static int tas2764_do_quirky_pwr_ctrl_change (struct tas2764_priv *tas2764, +unsigned int target) +{ +unsigned int curr; +int ret; + + curr =snd_soc_component_read_field (tas2764->component, +TAS2764_PWR_CTRL , +TAS2764_PWR_CTRL_MASK ); + +if (target == curr) +return 0 ; + +#define TRANSITION (new, old) ((new) << 8 | (old)) +switch (TRANSITION (target, curr)) { +case TRANSITION (TAS2764_PWR_CTRL_SHUTDOWN ,TAS2764_PWR_CTRL_MUTE ): +case TRANSITION (TAS2764_PWR_CTRL_SHUTDOWN ,TAS2764_PWR_CTRL_ACTIVE ): + ret =regmap_multi_reg_write (tas2764->regmap, tas2764_pre_shutdown_seq, +ARRAY_SIZE (tas2764_pre_shutdown_seq)); +if (ret <0 ) +break ; + + ret =snd_soc_component_update_bits (tas2764->component, +TAS2764_PWR_CTRL , +TAS2764_PWR_CTRL_MASK , +TAS2764_PWR_CTRL_SHUTDOWN ); +if (ret >0 ) +break ; + + ret =regmap_multi_reg_write (tas2764->regmap, tas2764_post_shutdown_seq, +ARRAY_SIZE (tas2764_post_shutdown_seq)); + fallthrough; +default : + ret =snd_soc_component_update_bits (tas2764->component,TAS2764_PWR_CTRL , +TAS2764_PWR_CTRL_MASK , target); + } +#undef TRANSITION + +if (ret <0 ) +return ret; +return 0 ; +} + +/* +* Via devicetree (TODO): +* - switch from spread spectrum to class-D switching +* - disable edge control +* - set BOP settings (the BOP config bits *and* BOP_SRC) +*/ + +/* +* Other setup TODOs: +* - DVC ramp rate +*/ + +static const struct tas2764_quirk_init_sequence { +const struct reg_sequence *seq; +int len; +} tas2764_quirk_init_sequences[] = { + { tas2764_noise_gate_dis_seq,ARRAY_SIZE (tas2764_noise_gate_dis_seq) }, + { tas2764_dmod_rst_seq,ARRAY_SIZE (tas2764_dmod_rst_seq) }, + { tas2764_conv_vbat_pvdd_mode_seq,ARRAY_SIZE (tas2764_conv_vbat_pvdd_mode_seq) }, + { tas2764_unk_seq0,ARRAY_SIZE (tas2764_unk_seq0) }, + { tas2764_unk_seq1,ARRAY_SIZE (tas2764_unk_seq1) }, + { tas2764_unk_seq2,ARRAY_SIZE (tas2764_unk_seq2) }, + { tas2764_thermal_th1_dis_seq,ARRAY_SIZE (tas2764_thermal_th1_dis_seq) }, + { tas2764_shutdown_dance_init_seq,ARRAY_SIZE (tas2764_shutdown_dance_init_seq) }, +}; + +#endif /* __TAS2764_QUIRKS__ */
sound/soc/codecs/tas2764.c +38 -0
@@ -46,6 +46,8 @@ struct tas2764_priv {bool unmuted; }; +#include "tas2764-quirks.h" +static const char *tas2764_int_ltch0_msgs[8 ] = {"fault: over temperature" ,/* INT_LTCH0 & BIT(0) */ "fault: over current" ,
@@ -123,6 +125,9 @@ static int tas2764_update_pwr_ctrl(struct tas2764_priv *tas2764)else val =TAS2764_PWR_CTRL_SHUTDOWN ; +if (ENABLED_APPLE_QUIRKS &TAS2764_SHUTDOWN_DANCE ) +return tas2764_do_quirky_pwr_ctrl_change (tas2764, val); + ret =snd_soc_component_update_bits (component,TAS2764_PWR_CTRL ,TAS2764_PWR_CTRL_MASK , val);if (ret <0 )
@@ -542,6 +547,30 @@ static uint8_t sn012776_bop_presets[] = {static const struct regmap_config tas2764_i2c_regmap; +static int tas2764_apply_init_quirks (struct tas2764_priv *tas2764) +{ +int ret, i; + +for (i =0 ; i <ARRAY_SIZE (tas2764_quirk_init_sequences); i++) { +const struct tas2764_quirk_init_sequence *init_seq = + &tas2764_quirk_init_sequences[i]; + +if (!init_seq->seq) +continue ; + +if (!(BIT (i) &ENABLED_APPLE_QUIRKS )) +continue ; + + ret =regmap_multi_reg_write (tas2764->regmap, init_seq->seq, + init_seq->len); + +if (ret <0 ) +return ret; + } + +return 0 ; +} +static int tas2764_codec_probe (struct snd_soc_component *component) {struct tas2764_priv *tas2764 =snd_soc_component_get_drvdata (component);
@@ -627,6 +656,12 @@ static int tas2764_codec_probe(struct snd_soc_component *component)if (ret <0 )return ret; } + +/* Apply all enabled Apple quirks */ + ret =tas2764_apply_init_quirks (tas2764); + +if (ret <0 ) +return ret; }return 0 ;
@@ -706,6 +741,9 @@ static bool tas2764_volatile_register(struct device *dev, unsigned int reg) case TAS2764_INT_LTCH0 ... TAS2764_INT_LTCH4:case TAS2764_INT_CLK_CFG :return true; + case TAS2764_REG(0xf0, 0x0) ... TAS2764_REG(0xff, 0x0): +/* TI's undocumented registers for the application of quirks */ +return true;default :return false; }
Signature · 1 line
2.48.1
Re: [PATCH 09/27] ASoC: tas2764: Apply Apple quirks
Quoted 14 lines
sound/soc/codecs/tas2764-quirks.h new file +14
-0+#define TRANSITION (new, old) ((new) << 8 | (old)) +switch (TRANSITION (target, curr)) { +case TRANSITION (TAS2764_PWR_CTRL_SHUTDOWN ,TAS2764_PWR_CTRL_MUTE ): +case TRANSITION (TAS2764_PWR_CTRL_SHUTDOWN ,TAS2764_PWR_CTRL_ACTIVE ): + ret =regmap_multi_reg_write (tas2764->regmap, tas2764_pre_shutdown_seq, +ARRAY_SIZE (tas2764_pre_shutdown_seq)); +if (ret <0 ) +break ; + + ret =snd_soc_component_update_bits (tas2764->component, +TAS2764_PWR_CTRL , +TAS2764_PWR_CTRL_MASK , +TAS2764_PWR_CTRL_SHUTDOWN ); +if (ret >0 )
sound/soc/codecs/tas2764-quirks.h line 155
Quoted 10 lines
sound/soc/codecs/tas2764-quirks.h new file +10
-0+break ; + + ret =regmap_multi_reg_write (tas2764->regmap, tas2764_post_shutdown_seq, +ARRAY_SIZE (tas2764_post_shutdown_seq)); + fallthrough; +default : + ret =snd_soc_component_update_bits (tas2764->component,TAS2764_PWR_CTRL , +TAS2764_PWR_CTRL_MASK , target); + } +#undef TRANSITION
Not a fan of this one-off macro, any reason not to do the obvious if-else instead?
PATCH 10/27 ASoC: tas2764: Raise regmap range maximum
In reply to James CalligerosFrom: Martin Povišer <[email protected]>
--- sound/soc/codecs/tas2764.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
sound/soc/codecs/tas2764.c +2 -2
@@ -726,7 +726,7 @@ static const struct reg_default tas2764_reg_defaults[] = {static const struct regmap_range_cfg tas2764_regmap_ranges[] = { { .range_min =0 , - .range_max =1 *128 , + .range_max =0xffff , .selector_reg =TAS2764_PAGE , .selector_mask =0xff , .selector_shift =0 ,
@@ -758,7 +758,7 @@ static const struct regmap_config tas2764_i2c_regmap = { .cache_type = REGCACHE_RBTREE, .ranges = tas2764_regmap_ranges, .num_ranges = ARRAY_SIZE(tas2764_regmap_ranges), - .max_register = 1 * 128, + .max_register = 0xffff, };static int tas2764_parse_dt (struct device *dev,struct tas2764_priv *tas2764)
Signature · 1 line
2.48.1
PATCH 11/27 ASoC: tas2770: Export 'die_temp' to sysfs
In reply to James CalligerosFrom: Martin Povišer <[email protected]>
Export a file for the readout of die temperature measurements. As per the datasheet, the temperature can be calculated by dividing the register value by 16 and then subtracting 93.
--- sound/soc/codecs/tas2770.c | 57 +++++++++++++++++++++++++ 1 file changed, 57 insertions(+)
sound/soc/codecs/tas2770.c +57 -0
@@ -20,6 +20,7 @@#include <linux/regmap.h> #include <linux/of.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <sound/soc.h> #include <sound/pcm.h> #include <sound/pcm_params.h>
@@ -491,6 +492,51 @@ static struct snd_soc_dai_driver tas2770_dai_driver[] = { }, }; +static int tas2770_read_die_temp (struct tas2770_priv *tas2770,int *result) +{ +int ret, reading; + + ret =snd_soc_component_read (tas2770->component,TAS2770_TEMP_MSB ); +if (ret <0 ) +return ret; + reading = ret <<4 ; + + ret =snd_soc_component_read (tas2770->component,TAS2770_TEMP_LSB ); +if (ret <0 ) +return ret; + reading |= ret >>4 ; + +/* +* As per datasheet: divide register by 16 and subtract 93. We don't +* want to divide just yet though. +*/ + *result = reading - (93 *16 ); +return 0 ; +} + +static ssize_t die_temp_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct tas2770_priv *tas2770 =i2c_get_clientdata (to_i2c_client (dev)); +int ret, temp; + + ret =tas2770_read_die_temp (tas2770, &temp); + +if (ret <0 ) +return ret; + +return sysfs_emit (buf,"%d.%03d C\n" , temp /16 , + (temp *1000 /16 ) %1000 ); +} + +static DEVICE_ATTR_RO (die_temp); + +static struct attribute *tas2770_sysfs_attrs[] = { + &dev_attr_die_temp.attr, +NULL +}; +ATTRIBUTE_GROUPS (tas2770_sysfs); +static const struct regmap_config tas2770_i2c_regmap;static int tas2770_codec_probe (struct snd_soc_component *component)
@@ -517,9 +563,19 @@ static int tas2770_codec_probe(struct snd_soc_component *component)return ret; } + ret =sysfs_create_groups (&component->dev->kobj, tas2770_sysfs_groups); + +if (ret <0 ) +return ret; +return 0 ; } +static void tas2770_codec_remove (struct snd_soc_component *component) +{ +sysfs_remove_groups (&component->dev->kobj, tas2770_sysfs_groups); +} + static DECLARE_TLV_DB_SCALE(tas2770_digital_tlv, 1100, 50, 0); static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -12750, 50, 0);
@@ -532,6 +588,7 @@ static const struct snd_kcontrol_new tas2770_snd_controls[] = {static const struct snd_soc_component_driver soc_component_driver_tas2770 = { .probe = tas2770_codec_probe, + .remove = tas2770_codec_remove, .suspend = tas2770_codec_suspend, .resume = tas2770_codec_resume, .controls = tas2770_snd_controls,
Signature · 1 line
2.48.1
Mark Brown
maintainerRe: [PATCH 11/27] ASoC: tas2770: Export 'die_temp' to sysfs
Quoted 5 lines · James Calligeros
From: Martin Povišer <[email protected]>
Export a file for the readout of die temperature measurements. As per the datasheet, the temperature can be calculated by dividing the register value by 16 and then subtracting 93.
Should this be registered with hwmon - it appears to be exactly the sort of thing that gets exported through there?
PATCH 12/27 ASoC: tas2764: Export 'die_temp' to sysfs
In reply to James CalligerosFrom: Martin Povišer <[email protected]>
Export a file for the readout of die temperature measurements. As per the datasheet, the temperature can be calculated by subtracting the value in the register by 93.
--- sound/soc/codecs/tas2764.c | 45 +++++++++++++++++++++++++ sound/soc/codecs/tas2764.h | 3 ++ 2 files changed, 48 insertions(+)
sound/soc/codecs/tas2764.c +45 -0
@@ -16,6 +16,7 @@#include <linux/of.h> #include <linux/of_device.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <sound/soc.h> #include <sound/pcm.h> #include <sound/pcm_params.h>
@@ -571,6 +572,39 @@ static int tas2764_apply_init_quirks(struct tas2764_priv *tas2764)return 0 ; } +static int tas2764_read_die_temp (struct tas2764_priv *tas2764,int *result) +{ +int ret; + + ret =snd_soc_component_read (tas2764->component,TAS2764_TEMP ); +if (ret <0 ) +return ret; + *result = ret -93 ; +return 0 ; +} + +static ssize_t die_temp_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct tas2764_priv *tas2764 =i2c_get_clientdata (to_i2c_client (dev)); +int ret, temp; + + ret =tas2764_read_die_temp (tas2764, &temp); + +if (ret <0 ) +return ret; + +return sysfs_emit (buf,"%d C\n" , temp); +} + +static DEVICE_ATTR_RO (die_temp); + +static struct attribute *tas2764_sysfs_attrs[] = { + &dev_attr_die_temp.attr, +NULL +}; +ATTRIBUTE_GROUPS (tas2764_sysfs); +static int tas2764_codec_probe (struct snd_soc_component *component) {struct tas2764_priv *tas2764 =snd_soc_component_get_drvdata (component);
@@ -664,9 +698,19 @@ static int tas2764_codec_probe(struct snd_soc_component *component)return ret; } + ret =sysfs_create_groups (&component->dev->kobj, tas2764_sysfs_groups); + +if (ret <0 ) +return ret; +return 0 ; } +static void tas2764_codec_remove (struct snd_soc_component *component) +{ +sysfs_remove_groups (&component->dev->kobj, tas2764_sysfs_groups); +} + static DECLARE_TLV_DB_SCALE(tas2764_digital_tlv, 1100, 50, 0); static DECLARE_TLV_DB_SCALE(tas2764_playback_volume, -10050, 50, 1);
@@ -698,6 +742,7 @@ static const struct snd_kcontrol_new tas2764_snd_controls[] = {static const struct snd_soc_component_driver soc_component_driver_tas2764 = { .probe = tas2764_codec_probe, + .remove = tas2764_codec_remove, .suspend = tas2764_codec_suspend, .resume = tas2764_codec_resume, .controls = tas2764_snd_controls,
sound/soc/codecs/tas2764.h +3 -0
@@ -111,6 +111,9 @@#define TAS2764_INT_LTCH3 TAS2764_REG(0x0, 0x50)#define TAS2764_INT_LTCH4 TAS2764_REG(0x0, 0x51) +/* Readout Registers */ +#define TAS2764_TEMP TAS2764_REG(0x0, 0x56) +/* Clock/IRQ Settings */ #define TAS2764_INT_CLK_CFG TAS2764_REG(0x0, 0x5c)#define TAS2764_INT_CLK_CFG_IRQZ_CLR BIT(2)
Signature · 1 line
2.48.1
PATCH 13/27 ASoC: tas2764: Crop SDOUT zero-out mask based on BCLK ratio
In reply to James CalligerosFrom: Martin Povišer <[email protected]>
--- sound/soc/codecs/tas2764.c | 56 +++++++++++++++++-------- 1 file changed, 39 insertions(+), 17 deletions(-)
sound/soc/codecs/tas2764.c +39 -17
@@ -375,6 +375,44 @@ static int tas2764_hw_params(struct snd_pcm_substream *substream,return tas2764_set_samplerate (tas2764,params_rate (params)); } +static int tas2764_write_sdout_zero_mask (struct tas2764_priv *tas2764,int bclk_ratio) +{ +struct snd_soc_component *component = tas2764->component; +int nsense_slots = bclk_ratio /8 ; +u32 cropped_mask; +int i, ret; + +if (!tas2764->sdout_zero_mask) +return 0 ; + + cropped_mask = tas2764->sdout_zero_mask &GENMASK (nsense_slots -1 ,0 ); + +for (i =0 ; i <4 ; i++) { + ret =snd_soc_component_write (component,TAS2764_SDOUT_HIZ_1 + i, + (cropped_mask >> (i *8 )) &0xff ); + +if (ret <0 ) +return ret; + } + + ret =snd_soc_component_update_bits (component,TAS2764_SDOUT_HIZ_9 , +TAS2764_SDOUT_HIZ_9_FORCE_0_EN , +TAS2764_SDOUT_HIZ_9_FORCE_0_EN ); + +if (ret <0 ) +return ret; + +return 0 ; +} + +static int tas2764_set_bclk_ratio (struct snd_soc_dai *dai,unsigned int ratio) +{ +struct snd_soc_component *component = dai->component; +struct tas2764_priv *tas2764 =snd_soc_component_get_drvdata (component); + +return tas2764_write_sdout_zero_mask (tas2764, ratio); +} +static int tas2764_set_fmt (struct snd_soc_dai *dai,unsigned int fmt) {struct snd_soc_component *component = dai->component;
@@ -506,6 +544,7 @@ static int tas2764_set_dai_tdm_slot(struct snd_soc_dai *dai,static const struct snd_soc_dai_ops tas2764_dai_ops = { .mute_stream = tas2764_mute, .hw_params = tas2764_hw_params, + .set_bclk_ratio = tas2764_set_bclk_ratio, .set_fmt = tas2764_set_fmt, .set_tdm_slot = tas2764_set_dai_tdm_slot, .no_capture_mute =1 ,
@@ -658,23 +697,6 @@ static int tas2764_codec_probe(struct snd_soc_component *component)if (ret <0 )return ret; -if (tas2764->sdout_zero_mask) { -for (i =0 ; i <4 ; i++) { - ret =snd_soc_component_write (component,TAS2764_SDOUT_HIZ_1 + i, - (tas2764->sdout_zero_mask >> (i *8 )) &0xff ); - -if (ret <0 ) -return ret; - } - - ret =snd_soc_component_update_bits (component,TAS2764_SDOUT_HIZ_9 , -TAS2764_SDOUT_HIZ_9_FORCE_0_EN , -TAS2764_SDOUT_HIZ_9_FORCE_0_EN ); - -if (ret <0 ) -return ret; - } -if (tas2764->devid ==DEVID_SN012776 ) { ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, TAS2764_PWR_CTRL_BOP_SRC,
Signature · 1 line
2.48.1
PATCH 14/27 ASoC: tas2764: Enable main IRQs
In reply to James CalligerosFrom: Hector Martin <[email protected]>
--- sound/soc/codecs/tas2764.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/codecs/tas2764.c +1 -1
@@ -660,7 +660,7 @@ static int tas2764_codec_probe(struct snd_soc_component *component)regmap_reinit_cache (tas2764->regmap, &tas2764_i2c_regmap);if (tas2764->irq) { - ret =snd_soc_component_write (tas2764->component,TAS2764_INT_MASK0 ,0xff ); + ret =snd_soc_component_write (tas2764->component,TAS2764_INT_MASK0 ,0x00 );if (ret <0 )return ret;
Signature · 1 line
2.48.1
PATCH 15/27 ASoC: tas2764: Power up/down amp on mute ops
In reply to James CalligerosFrom: Hector Martin <[email protected]>
The ASoC convention is that clocks are removed after codec mute, and power up/down is more about top level power management. For these chips, the "mute" state still expects a TDM clock, and yanking the clock in this state will trigger clock errors. So, do the full shutdown<->mute<->active transition on the mute operation, so the amp is in software shutdown by the time the clocks are removed.
This fixes TDM clock errors when streams are stopped.
--- sound/soc/codecs/tas2764.c | 51 ++++++++++--------------- 1 file changed, 21 insertions(+), 30 deletions(-)
sound/soc/codecs/tas2764.c +21 -30
@@ -194,33 +194,6 @@ static SOC_ENUM_SINGLE_DECL(static const struct snd_kcontrol_new tas2764_asi1_mux =SOC_DAPM_ENUM ("ASI1 Source" , tas2764_ASI1_src_enum); -static int tas2764_dac_event (struct snd_soc_dapm_widget *w, -struct snd_kcontrol *kcontrol,int event) -{ -struct snd_soc_component *component =snd_soc_dapm_to_component (w->dapm); -struct tas2764_priv *tas2764 =snd_soc_component_get_drvdata (component); -int ret; - -switch (event) { -case SND_SOC_DAPM_POST_PMU : - tas2764->dac_powered = true; - ret =tas2764_update_pwr_ctrl (tas2764); -break ; -case SND_SOC_DAPM_PRE_PMD : - tas2764->dac_powered = false; - ret =tas2764_update_pwr_ctrl (tas2764); -break ; -default : -dev_err (tas2764->dev,"Unsupported event\n" ); -return -EINVAL ; - } - -if (ret <0 ) -return ret; - -return 0 ; -} -static const struct snd_kcontrol_new isense_switch =SOC_DAPM_SINGLE ("Switch" ,TAS2764_PWR_CTRL ,TAS2764_ISENSE_POWER_EN ,1 ,1 ); static const struct snd_kcontrol_new vsense_switch =
@@ -233,8 +206,7 @@ static const struct snd_soc_dapm_widget tas2764_dapm_widgets[] = { 1, &isense_switch),SND_SOC_DAPM_SWITCH ("VSENSE" ,TAS2764_PWR_CTRL ,TAS2764_VSENSE_POWER_EN ,1 , &vsense_switch), -SND_SOC_DAPM_DAC_E ("DAC" ,NULL ,SND_SOC_NOPM ,0 ,0 , tas2764_dac_event, -SND_SOC_DAPM_POST_PMU |SND_SOC_DAPM_PRE_PMD ), +SND_SOC_DAPM_DAC ("DAC" ,NULL ,SND_SOC_NOPM ,0 ,0 ),SND_SOC_DAPM_OUTPUT ("OUT" ),SND_SOC_DAPM_SIGGEN ("VMON" ),SND_SOC_DAPM_SIGGEN ("IMON" )
@@ -255,9 +227,28 @@ static int tas2764_mute(struct snd_soc_dai *dai, int mute, int direction) {struct tas2764_priv *tas2764 =snd_soc_component_get_drvdata (dai->component); +int ret; + +if (!mute) { + tas2764->dac_powered = true; + ret =tas2764_update_pwr_ctrl (tas2764); +if (ret) +return ret; + } tas2764->unmuted = !mute; -return tas2764_update_pwr_ctrl (tas2764); + ret =tas2764_update_pwr_ctrl (tas2764); +if (ret) +return ret; + +if (mute) { + tas2764->dac_powered = false; + ret =tas2764_update_pwr_ctrl (tas2764); +if (ret) +return ret; + } + +return 0 ; }static int tas2764_set_bitwidth (struct tas2764_priv *tas2764,int bitwidth)
Signature · 1 line
2.48.1
PATCH 16/27 ASoC: tas2764: Add SDZ regulator
In reply to James CalligerosFrom: Hector Martin <[email protected]>
Multiple amps can be connected to the same SDZ GPIO. Using raw GPIOs for this breaks, as there is no concept of refcounting/sharing. In order to model these platforms, introduce support for an SDZ "regulator". This allows us to represent the SDZ GPIO as a simple regulator-fixed, and then the regulator core takes care of refcounting so that all codecs are only powered down once all the driver instances are in the suspend state.
--- sound/soc/codecs/tas2764.c | 39 ++++++++++++++++++++----- 1 file changed, 32 insertions(+), 7 deletions(-)
sound/soc/codecs/tas2764.c +32 -7
@@ -34,6 +34,7 @@ struct tas2764_priv {struct snd_soc_component *component;struct gpio_desc *reset_gpio;struct gpio_desc *sdz_gpio; +struct regulator *sdz_reg;struct regmap *regmap;struct device *dev;int irq;
@@ -153,6 +154,8 @@ static int tas2764_codec_suspend(struct snd_soc_component *component)if (tas2764->sdz_gpio)gpiod_set_value_cansleep (tas2764->sdz_gpio,0 ); +regulator_disable (tas2764->sdz_reg); +regcache_cache_only (tas2764->regmap, true);regcache_mark_dirty (tas2764->regmap);
@@ -164,19 +167,26 @@ static int tas2764_codec_resume(struct snd_soc_component *component)struct tas2764_priv *tas2764 =snd_soc_component_get_drvdata (component);int ret; + ret =regulator_enable (tas2764->sdz_reg); + +if (ret) { +dev_err (tas2764->dev,"Failed to enable regulator\n" ); +return ret; + } +if (tas2764->sdz_gpio) {gpiod_set_value_cansleep (tas2764->sdz_gpio,1 ); -usleep_range (1000 ,2000 ); } - ret =tas2764_update_pwr_ctrl (tas2764); +usleep_range (1000 ,2000 ); +regcache_cache_only (tas2764->regmap, false); + + ret =regcache_sync (tas2764->regmap);if (ret <0 )return ret; -regcache_cache_only (tas2764->regmap, false); - - return regcache_sync(tas2764->regmap); + return tas2764_update_pwr_ctrl(tas2764); }#else #define tas2764_codec_suspend NULL
@@ -209,7 +219,7 @@ static const struct snd_soc_dapm_widget tas2764_dapm_widgets[] = {SND_SOC_DAPM_DAC ("DAC" ,NULL ,SND_SOC_NOPM ,0 ,0 ),SND_SOC_DAPM_OUTPUT ("OUT" ),SND_SOC_DAPM_SIGGEN ("VMON" ), -SND_SOC_DAPM_SIGGEN ("IMON" ) + SND_SOC_DAPM_SIGGEN("IMON"), }; static const struct snd_soc_dapm_route tas2764_audio_map[] = {
@@ -642,11 +652,18 @@ static int tas2764_codec_probe(struct snd_soc_component *component) tas2764->component = component; + ret =regulator_enable (tas2764->sdz_reg); +if (ret !=0 ) { +dev_err (tas2764->dev,"Failed to enable regulator: %d\n" , ret); +return ret; + } +if (tas2764->sdz_gpio) {gpiod_set_value_cansleep (tas2764->sdz_gpio,1 ); -usleep_range (1000 ,2000 ); } +usleep_range (1000 ,2000 ); +tas2764_reset (tas2764);regmap_reinit_cache (tas2764->regmap, &tas2764_i2c_regmap);
@@ -721,6 +738,9 @@ static int tas2764_codec_probe(struct snd_soc_component *component)static void tas2764_codec_remove (struct snd_soc_component *component) { +struct tas2764_priv *tas2764 =snd_soc_component_get_drvdata (component); + +regulator_disable (tas2764->sdz_reg);sysfs_remove_groups (&component->dev->kobj, tas2764_sysfs_groups); }
@@ -823,6 +843,11 @@ static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764) {int ret =0 ; + tas2764->sdz_reg =devm_regulator_get (dev,"SDZ" ); +if (IS_ERR (tas2764->sdz_reg)) +return dev_err_probe (dev,PTR_ERR (tas2764->sdz_reg), +"Failed to get SDZ supply\n" ); + tas2764->reset_gpio =devm_gpiod_get_optional (tas2764->dev,"reset" ,GPIOD_OUT_HIGH );if (IS_ERR (tas2764->reset_gpio)) {
Signature · 1 line
2.48.1
PATCH 17/27 ASoC: tas2764: Add reg defaults for TAS2764_INT_CLK_CFG
In reply to James CalligerosFrom: Hector Martin <[email protected]>
--- sound/soc/codecs/tas2764.c | 1 + 1 file changed, 1 insertion(+)
sound/soc/codecs/tas2764.c +1 -0
@@ -799,6 +799,7 @@ static const struct reg_default tas2764_reg_defaults[] = { { TAS2764_TDM_CFG2, 0x0a }, { TAS2764_TDM_CFG3, 0x10 }, { TAS2764_TDM_CFG5, 0x42 }, + { TAS2764_INT_CLK_CFG, 0x19 }, }; static const struct regmap_range_cfg tas2764_regmap_ranges[] = {
Signature · 1 line
2.48.1
PATCH 18/27 ASoC: tas2764: Mark SW_RESET as volatile
In reply to James CalligerosFrom: Hector Martin <[email protected]>
Since the bit is self-clearing.
--- sound/soc/codecs/tas2764.c | 1 + 1 file changed, 1 insertion(+)
sound/soc/codecs/tas2764.c +1 -0
@@ -817,6 +817,7 @@ static const struct regmap_range_cfg tas2764_regmap_ranges[] = {static bool tas2764_volatile_register (struct device *dev,unsigned int reg) {switch (reg) { +case TAS2764_SW_RST : case TAS2764_INT_LTCH0 ... TAS2764_INT_LTCH4:case TAS2764_INT_CLK_CFG :return true;
Signature · 1 line
2.48.1
PATCH 19/27 ASoC: tas2764: Fix power control mask
In reply to James CalligerosFrom: Hector Martin <[email protected]>
--- sound/soc/codecs/tas2764.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/codecs/tas2764.h +1 -1
@@ -25,7 +25,7 @@/* Power Control */ #define TAS2764_PWR_CTRL TAS2764_REG(0X0, 0x02) -#define TAS2764_PWR_CTRL_MASK GENMASK(1, 0) +#define TAS2764_PWR_CTRL_MASK GENMASK(2, 0)#define TAS2764_PWR_CTRL_ACTIVE 0x0#define TAS2764_PWR_CTRL_MUTE BIT(0)#define TAS2764_PWR_CTRL_SHUTDOWN BIT(1)
Signature · 1 line
2.48.1
Mark Brown
maintainerRe: [PATCH 19/27] ASoC: tas2764: Fix power control mask
Quoted 4 lines · James Calligeros
From: Hector Martin <[email protected]>
Signed-off-by Hector Martin <[email protected]>Signed-off-by James Calligeros <[email protected]>
Fixes should go at the start of the series so that they don't end up with spurious dependencies on other non-fix patches. I did pull some random fixes out of the Asahi tree the other week which are in CI now, I can't remember off hand if this was one of them.
PATCH 20/27 ASoC: tas2764: Wait for ramp-down after shutdown
In reply to James CalligerosFrom: Hector Martin <[email protected]>
When we shut down the amp, we need to wait for the built-in ramp-down before we can remove the TDM clocks. There is no documented status regiter to poll, so the best we can do is a delay. Datasheet says 5.9ms for ramp-down and 1.5ms between shutdown and next startup, so let's do 6ms after mute and 2ms after shutdown. That gives us a cumulative 8ms for ramp-down and guaratees the required minimum shutdown time.
--- sound/soc/codecs/tas2764.c | 8 ++++++++ 1 file changed, 8 insertions(+)
sound/soc/codecs/tas2764.c +8 -0
@@ -159,6 +159,8 @@ static int tas2764_codec_suspend(struct snd_soc_component *component)regcache_cache_only (tas2764->regmap, true);regcache_mark_dirty (tas2764->regmap); +usleep_range (6000 ,7000 ); +return 0 ; }
@@ -252,10 +254,16 @@ static int tas2764_mute(struct snd_soc_dai *dai, int mute, int direction)return ret;if (mute) { +/* Wait for ramp-down */ +usleep_range (6000 ,7000 ); + tas2764->dac_powered = false; ret =tas2764_update_pwr_ctrl (tas2764);if (ret)return ret; + +/* Wait a bit after shutdown */ +usleep_range (2000 ,3000 ); }return 0 ;
Signature · 1 line
2.48.1
PATCH 21/27 ASoC: tas2770: Add SDZ regulator
In reply to James CalligerosFrom: Hector Martin <[email protected]>
Multiple amps can be connected to the same SDZ GPIO. Using raw GPIOs for this breaks, as there is no concept of refcounting/sharing. In order to model these platforms, introduce support for an SDZ "regulator". This allows us to represent the SDZ GPIO as a simple regulator-fixed, and then the regulator core takes care of refcounting so that all codecs are only powered down once all the driver instances are in the suspend state.
This also reworks the sleep/resume logic to copy what tas2764 does, which makes more sense.
--- sound/soc/codecs/tas2770.c | 67 ++++++++++++++++--------- sound/soc/codecs/tas2770.h | 1 + 2 files changed, 45 insertions(+), 23 deletions(-)
sound/soc/codecs/tas2770.c +44 -23
@@ -71,23 +71,21 @@ static int tas2770_codec_suspend(struct snd_soc_component *component)struct tas2770_priv *tas2770 =snd_soc_component_get_drvdata (component);int ret =0 ; -regcache_cache_only (tas2770->regmap, true); -regcache_mark_dirty (tas2770->regmap); + ret =snd_soc_component_update_bits (component,TAS2770_PWR_CTRL , +TAS2770_PWR_CTRL_MASK , +TAS2770_PWR_CTRL_SHUTDOWN ); +if (ret <0 ) +return ret; -if (tas2770->sdz_gpio) { +if (tas2770->sdz_gpio)gpiod_set_value_cansleep (tas2770->sdz_gpio,0 ); - }else { - ret =snd_soc_component_update_bits (component,TAS2770_PWR_CTRL , -TAS2770_PWR_CTRL_MASK , -TAS2770_PWR_CTRL_SHUTDOWN ); -if (ret <0 ) { -regcache_cache_only (tas2770->regmap, false); -regcache_sync (tas2770->regmap); -return ret; - } - ret =0 ; - } +regulator_disable (tas2770->sdz_reg); + +regcache_cache_only (tas2770->regmap, true); +regcache_mark_dirty (tas2770->regmap); + +usleep_range (6000 ,7000 );return ret; }
@@ -97,18 +95,26 @@ static int tas2770_codec_resume(struct snd_soc_component *component)struct tas2770_priv *tas2770 =snd_soc_component_get_drvdata (component);int ret; -if (tas2770->sdz_gpio) { -gpiod_set_value_cansleep (tas2770->sdz_gpio,1 ); -usleep_range (1000 ,2000 ); - }else { - ret =tas2770_update_pwr_ctrl (tas2770); -if (ret <0 ) -return ret; + ret =regulator_enable (tas2770->sdz_reg); + +if (ret) { +dev_err (tas2770->dev,"Failed to enable regulator\n" ); +return ret; } +if (tas2770->sdz_gpio) +gpiod_set_value_cansleep (tas2770->sdz_gpio,1 ); + + +usleep_range (1000 ,2000 ); +regcache_cache_only (tas2770->regmap, false); - return regcache_sync(tas2770->regmap); + ret =regcache_sync (tas2770->regmap); +if (ret <0 ) +return ret; + + return tas2770_update_pwr_ctrl(tas2770); }#else #define tas2770_codec_suspend NULL
@@ -547,11 +553,18 @@ static int tas2770_codec_probe(struct snd_soc_component *component) tas2770->component = component; + ret =regulator_enable (tas2770->sdz_reg); +if (ret !=0 ) { +dev_err (tas2770->dev,"Failed to enable regulator: %d\n" , ret); +return ret; + } +if (tas2770->sdz_gpio) {gpiod_set_value_cansleep (tas2770->sdz_gpio,1 ); -usleep_range (1000 ,2000 ); } +usleep_range (1000 ,2000 ); +tas2770_reset (tas2770);regmap_reinit_cache (tas2770->regmap, &tas2770_i2c_regmap);
@@ -573,7 +586,10 @@ static int tas2770_codec_probe(struct snd_soc_component *component)static void tas2770_codec_remove (struct snd_soc_component *component) { +struct tas2770_priv *tas2770 =snd_soc_component_get_drvdata (component); +sysfs_remove_groups (&component->dev->kobj, tas2770_sysfs_groups); +regulator_disable (tas2770->sdz_reg); } static DECLARE_TLV_DB_SCALE(tas2770_digital_tlv, 1100, 50, 0);
@@ -713,6 +729,11 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) tas2770->v_sense_slot =-1 ; } + tas2770->sdz_reg =devm_regulator_get (dev,"SDZ" ); +if (IS_ERR (tas2770->sdz_reg)) +return dev_err_probe (dev,PTR_ERR (tas2770->sdz_reg), +"Failed to get SDZ supply\n" ); + tas2770->sdz_gpio =devm_gpiod_get_optional (dev,"shutdown" ,GPIOD_OUT_HIGH );if (IS_ERR (tas2770->sdz_gpio)) {if (PTR_ERR (tas2770->sdz_gpio) == -EPROBE_DEFER )
sound/soc/codecs/tas2770.h +1 -0
@@ -134,6 +134,7 @@ struct tas2770_priv {struct snd_soc_component *component;struct gpio_desc *reset_gpio;struct gpio_desc *sdz_gpio; +struct regulator *sdz_reg;struct regmap *regmap;struct device *dev;int v_sense_slot;
Signature · 1 line
2.48.1
PATCH 22/27 ASoC: tas2770: Power cycle amp on ISENSE/VSENSE change
In reply to James CalligerosFrom: Hector Martin <[email protected]>
The ISENSE/VSENSE blocks are only powered up when the amplifier transitions from shutdown to active. This means that if those controls are flipped on while the amplifier is already playing back audio, they will have no effect.
Fix this by forcing a power cycle around transitions in those controls.
--- sound/soc/codecs/tas2770.c | 30 +++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
sound/soc/codecs/tas2770.c +28 -2
@@ -163,11 +163,37 @@ static const struct snd_kcontrol_new isense_switch =static const struct snd_kcontrol_new vsense_switch =SOC_DAPM_SINGLE ("Switch" ,TAS2770_PWR_CTRL ,2 ,1 ,1 ); +static int sense_event (struct snd_soc_dapm_widget *w, +struct snd_kcontrol *kcontrol,int event) +{ +struct snd_soc_component *component =snd_soc_dapm_to_component (w->dapm); +struct tas2770_priv *tas2770 =snd_soc_component_get_drvdata (component); + +/* +* Powering up ISENSE/VSENSE requires a trip through the shutdown state. +* Do that here to ensure that our changes are applied properly, otherwise +* we might end up with non-functional IVSENSE if playback started earlier, +* which would break software speaker protection. +*/ +switch (event) { +case SND_SOC_DAPM_PRE_REG : +return snd_soc_component_update_bits (component,TAS2770_PWR_CTRL , +TAS2770_PWR_CTRL_MASK , +TAS2770_PWR_CTRL_SHUTDOWN ); +case SND_SOC_DAPM_POST_REG : +return tas2770_update_pwr_ctrl (tas2770); +default : +return 0 ; + } +} +static const struct snd_soc_dapm_widget tas2770_dapm_widgets[] = {SND_SOC_DAPM_AIF_IN ("ASI1" ,"ASI1 Playback" ,0 ,SND_SOC_NOPM ,0 ,0 ),SND_SOC_DAPM_MUX ("ASI1 Sel" ,SND_SOC_NOPM ,0 ,0 , &tas2770_asi1_mux), -SND_SOC_DAPM_SWITCH ("ISENSE" ,TAS2770_PWR_CTRL ,3 ,1 , &isense_switch), -SND_SOC_DAPM_SWITCH ("VSENSE" ,TAS2770_PWR_CTRL ,2 ,1 , &vsense_switch), +SND_SOC_DAPM_SWITCH_E ("ISENSE" ,TAS2770_PWR_CTRL ,3 ,1 , &isense_switch, + sense_event,SND_SOC_DAPM_PRE_REG |SND_SOC_DAPM_POST_REG ), +SND_SOC_DAPM_SWITCH_E ("VSENSE" ,TAS2770_PWR_CTRL ,2 ,1 , &vsense_switch, + sense_event,SND_SOC_DAPM_PRE_REG |SND_SOC_DAPM_POST_REG ),SND_SOC_DAPM_DAC_E ("DAC" ,NULL ,SND_SOC_NOPM ,0 ,0 , tas2770_dac_event,SND_SOC_DAPM_POST_PMU |SND_SOC_DAPM_PRE_PMD ),SND_SOC_DAPM_OUTPUT ("OUT" ),
Signature · 1 line
2.48.1
PATCH 23/27 ASoC: tas2770: Add zero-fill and pull-down controls
In reply to James CalligerosFrom: Hector Martin <[email protected]>
Expose the bits that control the behavior of the SDOUT pin when not actively transmitting slot data. Zero-fill is useful when there is a single amp on the SDOUT bus (e.g. Apple machines with mono speakers or a single stereo pair, where L/R are on separate buses).
Pull-down is useful, though not perfect, when multiple amps share a bus. It typically takes around 2 bits for the line to transition from high to low after going Hi-Z, with the pull-down.
--- sound/soc/codecs/tas2770.c | 17 +++++++++++++++++ sound/soc/codecs/tas2770.h | 13 +++++++++++++ 2 files changed, 30 insertions(+)
sound/soc/codecs/tas2770.c +17 -0
@@ -602,6 +602,20 @@ static int tas2770_codec_probe(struct snd_soc_component *component)return ret; } + ret =snd_soc_component_update_bits (component,TAS2770_TDM_CFG_REG4 , +TAS2770_TDM_CFG_REG4_TX_FILL , + tas2770->sdout_zfill ?0 : +TAS2770_TDM_CFG_REG4_TX_FILL ); +if (ret <0 ) +return ret; + + ret =snd_soc_component_update_bits (component,TAS2770_DIN_PD , +TAS2770_DIN_PD_SDOUT , + tas2770->sdout_pd ? +TAS2770_DIN_PD_SDOUT :0 ); +if (ret <0 ) +return ret; + ret =sysfs_create_groups (&component->dev->kobj, tas2770_sysfs_groups);if (ret <0 )
@@ -755,6 +769,9 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) tas2770->v_sense_slot =-1 ; } + tas2770->sdout_pd =fwnode_property_read_bool (dev->fwnode,"ti,sdout-pull-down" ); + tas2770->sdout_zfill =fwnode_property_read_bool (dev->fwnode,"ti,sdout-zero-fill" ); + tas2770->sdz_reg =devm_regulator_get (dev,"SDZ" );if (IS_ERR (tas2770->sdz_reg)) return dev_err_probe(dev, PTR_ERR(tas2770->sdz_reg),
sound/soc/codecs/tas2770.h +13 -0
@@ -67,6 +67,14 @@#define TAS2770_TDM_CFG_REG3_RXS_SHIFT 0x4#define TAS2770_TDM_CFG_REG3_30_MASK GENMASK(3, 0)#define TAS2770_TDM_CFG_REG3_30_SHIFT 0 +/* TDM Configuration Reg4 */ +#define TAS2770_TDM_CFG_REG4 TAS2770_REG(0X0, 0x0E) +#define TAS2770_TDM_CFG_REG4_TX_LSB_CFG BIT(7) +#define TAS2770_TDM_CFG_REG4_TX_KEEPER_CFG BIT(6) +#define TAS2770_TDM_CFG_REG4_TX_KEEPER BIT(5) +#define TAS2770_TDM_CFG_REG4_TX_FILL BIT(4) +#define TAS2770_TDM_CFG_REG4_TX_OFFSET_MASK GENMASK(3, 1) +#define TAS2770_TDM_CFG_REG4_TX_EDGE_FALLING BIT(0)/* TDM Configuration Reg5 */ #define TAS2770_TDM_CFG_REG5 TAS2770_REG(0X0, 0x0F)#define TAS2770_TDM_CFG_REG5_VSNS_MASK BIT(6)
@@ -110,6 +118,9 @@#define TAS2770_TEMP_LSB TAS2770_REG(0X0, 0x2A)/* Interrupt Configuration */ #define TAS2770_INT_CFG TAS2770_REG(0X0, 0x30) +/* Data In Pull-Down */ +#define TAS2770_DIN_PD TAS2770_REG(0X0, 0x31) +#define TAS2770_DIN_PD_SDOUT BIT(7)/* Misc IRQ */ #define TAS2770_MISC_IRQ TAS2770_REG(0X0, 0x32)/* Clock Configuration */
@@ -139,6 +150,8 @@ struct tas2770_priv {struct device *dev;int v_sense_slot;int i_sense_slot; +bool sdout_pd; +bool sdout_zfill;bool dac_powered;bool unmuted; };
Signature · 1 line
2.48.1
PATCH 24/27 ASoC: tas2770: Support setting the PDM TX slot
In reply to James CalligerosFrom: Hector Martin <[email protected]>
We don't actually support configuring the PDM input right now. Rather, this is useful as a hack.
On Apple Silicon machines, amps are split between two I2S buses which are logically ANDed internally at the SoC. Odd and even slot groups are driven by amps on either bus respectively. Since the signals are ANDed, unused slot groups must be driven as zero to avoid corrupting the data from the other side.
On most recent machines (TAS2764-based), this is accomplished using the "SDOUT zero mask" feature of that chip. Unfortunately, TAS2770 does not support this. It does support zeroing out *all* unused slots, which works well for machines with a single amp per I2S bus. That is all, except one.
The 13" M1 MacBook Pro is the only machine using TAS2770 and two amps per I2S bus:
L Bus: SPK0I SPK0V Hi-Z Hi-Z SPK2I SPK2V Hi-Z Hi-Z R Bus: Hi-Z Hi-Z SPK1I SPK2V Hi-Z Hi-Z SPK3I SPK3V
To ensure uncorrupted data, we need to force all the Hi-Z periods to zero. We cannot use the "force all zero" feature, as that would cause a bus conflict between both amps. We can use the pull-down feature, but that leaves a few bits of garbage on the trailing edge of the speaker data, since the pull-down is weak.
This is where the PDM transmit feature comes in. With PDM grounded and disabled (the default state), the PDM slot is transmitted as all zeroes. We can use that to force a zero 16-bit slot after the voltage data for each speaker, cleaning it up. Then the pull-down ensures the line stays low for the subsequent slot:
L Bus: SPK0I SPK0V PDM0 PulDn SPK2I SPK2V PDM0 PulDn R Bus: PDM0 PulDn SPK1I SPK2V PDM0 PulDn SPK3I SPK3V
Yes, this is a horrible hack, but it beats adding dummy slots that would be visible to the userspace capture side. There may be some other way to fix the logical AND behavior on the MCA side... that would make this unnecessary.
("How does Apple deal with this"? - they don't, macOS does not use IVSENSE on TAS2770 machines even though it's physically wired up, but we want to do so on Linux.)
--- sound/soc/codecs/tas2770.c | 25 +++++++++++++++++++++++++ sound/soc/codecs/tas2770.h | 6 ++++++ 2 files changed, 31 insertions(+)
sound/soc/codecs/tas2770.c +25 -0
@@ -247,6 +247,19 @@ static int tas2770_set_ivsense_transmit(struct tas2770_priv *tas2770,return 0 ; } +static int tas2770_set_pdm_transmit (struct tas2770_priv *tas2770,int slot) +{ +struct snd_soc_component *component = tas2770->component; +int ret; + + ret =snd_soc_component_update_bits (component,TAS2770_TDM_CFG_REG7 , +TAS2770_TDM_CFG_REG7_PDM_MASK | +TAS2770_TDM_CFG_REG7_50_MASK , +TAS2770_TDM_CFG_REG7_PDM_ENABLE | + slot); +return ret; +} +static int tas2770_set_bitwidth (struct tas2770_priv *tas2770,int bitwidth) {int ret;
@@ -602,6 +615,13 @@ static int tas2770_codec_probe(struct snd_soc_component *component)return ret; } +if (tas2770->pdm_slot !=-1 ) { + ret =tas2770_set_pdm_transmit (tas2770, tas2770->pdm_slot); + +if (ret <0 ) +return ret; + } + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG4, TAS2770_TDM_CFG_REG4_TX_FILL, tas2770->sdout_zfill ? 0 :
@@ -769,6 +789,11 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) tas2770->v_sense_slot =-1 ; } + rc =fwnode_property_read_u32 (dev->fwnode,"ti,pdm-slot-no" , + &tas2770->pdm_slot); +if (rc) + tas2770->pdm_slot =-1 ; + tas2770->sdout_pd =fwnode_property_read_bool (dev->fwnode,"ti,sdout-pull-down" ); tas2770->sdout_zfill =fwnode_property_read_bool (dev->fwnode,"ti,sdout-zero-fill" );
sound/soc/codecs/tas2770.h +6 -0
@@ -85,6 +85,11 @@#define TAS2770_TDM_CFG_REG6_ISNS_MASK BIT(6)#define TAS2770_TDM_CFG_REG6_ISNS_ENABLE BIT(6)#define TAS2770_TDM_CFG_REG6_50_MASK GENMASK(5, 0) +/* TDM Configuration Reg10 */ +#define TAS2770_TDM_CFG_REG7 TAS2770_REG(0X0, 0x11) +#define TAS2770_TDM_CFG_REG7_PDM_MASK BIT(6) +#define TAS2770_TDM_CFG_REG7_PDM_ENABLE BIT(6) +#define TAS2770_TDM_CFG_REG7_50_MASK GENMASK(5, 0)/* Brown Out Prevention Reg0 */ #define TAS2770_BO_PRV_REG0 TAS2770_REG(0X0, 0x1B)/* Interrupt MASK Reg0 */
@@ -150,6 +155,7 @@ struct tas2770_priv {struct device *dev;int v_sense_slot;int i_sense_slot; +int pdm_slot;bool sdout_pd;bool sdout_zfill;bool dac_powered;
Signature · 1 line
2.48.1
PATCH 25/27 ASoC: tas2770: Fix volume scale
In reply to James CalligerosFrom: Hector Martin <[email protected]>
The scale starts at -100dB, not -128dB.
--- sound/soc/codecs/tas2770.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/codecs/tas2770.c +1 -1
@@ -653,7 +653,7 @@ static void tas2770_codec_remove(struct snd_soc_component *component) } static DECLARE_TLV_DB_SCALE(tas2770_digital_tlv, 1100, 50, 0); -static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -12750, 50, 0); +static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -10050, 50, 0);static const struct snd_kcontrol_new tas2770_snd_controls[] = { SOC_SINGLE_TLV("Speaker Playback Volume", TAS2770_PLAY_CFG_REG2,
Signature · 1 line
2.48.1
Mark Brown
maintainerRe: [PATCH 25/27] ASoC: tas2770: Fix volume scale
Quoted 3 lines · James Calligeros
From: Hector Martin <[email protected]>
The scale starts at -100dB, not -128dB.
This was one of the patches I pulled out, it's already in CI (I left everything for a week in case there were review comments).
PATCH 26/27 ASoC: tas2764: Set the SDOUT polarity correctly
In reply to James CalligerosFrom: Hector Martin <[email protected]>
TX launch polarity needs to be the opposite of RX capture polarity, to generate the right bit slot alignment.
--- sound/soc/codecs/tas2764.c | 10 +++++++++- sound/soc/codecs/tas2764.h | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-)
sound/soc/codecs/tas2764.c +9 -1
@@ -426,7 +426,7 @@ static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) {struct snd_soc_component *component = dai->component;struct tas2764_priv *tas2764 =snd_soc_component_get_drvdata (component); -u8 tdm_rx_start_slot =0 , asi_cfg_0 =0 , asi_cfg_1 =0 ; +u8 tdm_rx_start_slot =0 , asi_cfg_0 =0 , asi_cfg_1 =0 , asi_cfg_4 =0 ;int ret;switch (fmt &SND_SOC_DAIFMT_INV_MASK ) {
@@ -435,12 +435,14 @@ static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) fallthrough;case SND_SOC_DAIFMT_NB_NF : asi_cfg_1 =TAS2764_TDM_CFG1_RX_RISING ; + asi_cfg_4 =TAS2764_TDM_CFG4_TX_FALLING ;break ;case SND_SOC_DAIFMT_IB_IF : asi_cfg_0 ^=TAS2764_TDM_CFG0_FRAME_START ; fallthrough;case SND_SOC_DAIFMT_IB_NF : asi_cfg_1 =TAS2764_TDM_CFG1_RX_FALLING ; + asi_cfg_4 =TAS2764_TDM_CFG4_TX_RISING ;break ; }
@@ -450,6 +452,12 @@ static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)if (ret <0 )return ret; + ret =snd_soc_component_update_bits (component,TAS2764_TDM_CFG4 , +TAS2764_TDM_CFG4_TX_MASK , + asi_cfg_4); +if (ret <0 ) +return ret; +switch (fmt &SND_SOC_DAIFMT_FORMAT_MASK ) {case SND_SOC_DAIFMT_I2S : asi_cfg_0 ^=TAS2764_TDM_CFG0_FRAME_START ;
sound/soc/codecs/tas2764.h +6 -0
@@ -84,6 +84,12 @@#define TAS2764_TDM_CFG3_RXS_SHIFT 0x4#define TAS2764_TDM_CFG3_MASK GENMASK(3, 0) +/* TDM Configuration Reg4 */ +#define TAS2764_TDM_CFG4 TAS2764_REG(0X0, 0x0d) +#define TAS2764_TDM_CFG4_TX_MASK BIT(0) +#define TAS2764_TDM_CFG4_TX_RISING 0x0 +#define TAS2764_TDM_CFG4_TX_FALLING BIT(0) +/* TDM Configuration Reg5 */ #define TAS2764_TDM_CFG5 TAS2764_REG(0X0, 0x0e)#define TAS2764_TDM_CFG5_VSNS_MASK BIT(6)
Signature · 1 line
2.48.1
PATCH 27/27 ASoC: tas2770: Set the SDOUT polarity correctly
In reply to James CalligerosFrom: Hector Martin <[email protected]>
TX launch polarity needs to be the opposite of RX capture polarity, to generate the right bit slot alignment.
--- sound/soc/codecs/tas2770.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
sound/soc/codecs/tas2770.c +8 -1
@@ -358,7 +358,7 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)struct snd_soc_component *component = dai->component;struct tas2770_priv *tas2770 =snd_soc_component_get_drvdata (component); -u8 tdm_rx_start_slot =0 , invert_fpol =0 , fpol_preinv =0 , asi_cfg_1 =0 ; +u8 tdm_rx_start_slot =0 , invert_fpol =0 , fpol_preinv =0 , asi_cfg_1 =0 , asi_cfg_4 =0 ;int ret;switch (fmt &SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK ) {
@@ -375,6 +375,7 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) fallthrough;case SND_SOC_DAIFMT_NB_NF : asi_cfg_1 |=TAS2770_TDM_CFG_REG1_RX_RSING ; + asi_cfg_4 |=TAS2770_TDM_CFG_REG4_TX_EDGE_FALLING ;break ;case SND_SOC_DAIFMT_IB_IF : invert_fpol =1 ;
@@ -393,6 +394,12 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)if (ret <0 )return ret; + ret =snd_soc_component_update_bits (component,TAS2770_TDM_CFG_REG4 , +TAS2770_TDM_CFG_REG4_TX_EDGE_FALLING , + asi_cfg_4); +if (ret <0 ) +return ret; +switch (fmt &SND_SOC_DAIFMT_FORMAT_MASK ) {case SND_SOC_DAIFMT_I2S : tdm_rx_start_slot =1 ;
Signature · 1 line
2.48.1
Quoted 70 lines · James Calligeros
Hi all,
This series introduces a number of changes to the drivers for the Texas Instruments TAS2764 and TAS2770 amplifiers in order to introduce (and improve in the case of TAS2770) support for the variants of these amps found in Apple Silicon Macs.
Apple's variant of TAS2764 is known as SN012776, and as always with Apple is a subtly incompatible variant with a number of quirks. It is not publicly available. The TAS2770 variant is known as TAS5770L, and does not require incompatible handling.
Much as with the Cirrus codec patches, I do not
expect that we will get any official acknowledgement that these parts exist from TI, however I would be delighted to be proven wrong.This series has been living in the downstream Asahi kernel tree[1] for over two years, and has been tested by many thousands of users by this point[2].
Regards,
James[1] https://github.com/AsahiLinux/linux/tree/asahi-wip
[2] https://stats.asahilinux.org/---
Hector Martin (14):
ASoC: tas2764: Enable main IRQs
ASoC: tas2764: Power up/down amp on mute ops
ASoC: tas2764: Add SDZ regulator
ASoC: tas2764: Add reg defaults forTAS2764_INT_CLK_CFG
ASoC: tas2764: MarkSW_RESETas volatile
ASoC: tas2764: Fix power control mask
ASoC: tas2764: Wait for ramp-down after shutdown
ASoC: tas2770: Add SDZ regulator
ASoC: tas2770: Power cycle amp on ISENSE/VSENSE change
ASoC: tas2770: Add zero-fill and pull-down controls
ASoC: tas2770: Support setting the PDM TX slot
ASoC: tas2770: Fix volume scale
ASoC: tas2764: Set the SDOUT polarity correctly
ASoC: tas2770: Set the SDOUT polarity correctlyJames Calligeros (2):
ASoC: dt-bindings: tas27xx: add compatible for SN012776
ASoC: dt-bindings: tas2770: add compatible for TAS5770LMartin Povišer (11):
ASoC: tas2764: Extend driver to SN012776
ASoC: tas2764: Add control concerning overcurrent events
ASoC: tas2770: Factor outset_ivsense_slots
ASoC: tas2770: Fix and redo I/V sense TDM slot setting logic
ASoC: tas2764: Reinit cache on part reset
ASoC: tas2764: Configure zeroing of SDOUT slots
ASoC: tas2764: Apply Apple quirks
ASoC: tas2764: Raise regmap range maximum
ASoC: tas2770: Export 'die_temp' to sysfs
ASoC: tas2764: Export 'die_temp' to sysfs
ASoC: tas2764: Crop SDOUT zero-out mask based on BCLK ratio.../bindings/sound/ti,tas2770.yaml | 1 + .../bindings/sound/ti,tas27xx.yaml | 1 + sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/tas2764-quirks.h | 188 +++++++++++++++ sound/soc/codecs/tas2764.c | 302 +++++++++++++++++++++---- sound/soc/codecs/tas2764.h | 29 ++- sound/soc/codecs/tas2770.c | 264 +++++++++++++++++---- sound/soc/codecs/tas2770.h | 20 ++ 8 files changed, 711 insertions(+), 95 deletions(-)
Quoted 9 lines
---
base-commit:cc7708ae5e2aab296203fcec774695fc9d995f48
change-id: 20250214-apple-codec-changes-6e656dc1e24dBest regards,
--
James Calligeros <[email protected]>
After years of iterations, I think this series looks good to me. :)
Signature · 1 line
真実はいつも一つ!/ Always, there's only one truth!
This looks much better than the v0 I eyeballed last week, good stuff :-) Will leave the technical review on this to people who know more audio than me but it /looks/ just about right to me. Thanks for getting this into shape and submitting it, James :-)
Quoted 77 lines · James Calligeros
Hi all,
This series introduces a number of changes to the drivers for the Texas Instruments TAS2764 and TAS2770 amplifiers in order to introduce (and improve in the case of TAS2770) support for the variants of these amps found in Apple Silicon Macs.
Apple's variant of TAS2764 is known as SN012776, and as always with Apple is a subtly incompatible variant with a number of quirks. It is not publicly available. The TAS2770 variant is known as TAS5770L, and does not require incompatible handling.
Much as with the Cirrus codec patches, I do not
expect that we will get any official acknowledgement that these parts exist from TI, however I would be delighted to be proven wrong.This series has been living in the downstream Asahi kernel tree[1] for over two years, and has been tested by many thousands of users by this point[2].
Regards,
James[1] https://github.com/AsahiLinux/linux/tree/asahi-wip
[2] https://stats.asahilinux.org/---
Hector Martin (14):
ASoC: tas2764: Enable main IRQs
ASoC: tas2764: Power up/down amp on mute ops
ASoC: tas2764: Add SDZ regulator
ASoC: tas2764: Add reg defaults forTAS2764_INT_CLK_CFG
ASoC: tas2764: MarkSW_RESETas volatile
ASoC: tas2764: Fix power control mask
ASoC: tas2764: Wait for ramp-down after shutdown
ASoC: tas2770: Add SDZ regulator
ASoC: tas2770: Power cycle amp on ISENSE/VSENSE change
ASoC: tas2770: Add zero-fill and pull-down controls
ASoC: tas2770: Support setting the PDM TX slot
ASoC: tas2770: Fix volume scale
ASoC: tas2764: Set the SDOUT polarity correctly
ASoC: tas2770: Set the SDOUT polarity correctlyJames Calligeros (2):
ASoC: dt-bindings: tas27xx: add compatible for SN012776
ASoC: dt-bindings: tas2770: add compatible for TAS5770LMartin Povišer (11):
ASoC: tas2764: Extend driver to SN012776
ASoC: tas2764: Add control concerning overcurrent events
ASoC: tas2770: Factor outset_ivsense_slots
ASoC: tas2770: Fix and redo I/V sense TDM slot setting logic
ASoC: tas2764: Reinit cache on part reset
ASoC: tas2764: Configure zeroing of SDOUT slots
ASoC: tas2764: Apply Apple quirks
ASoC: tas2764: Raise regmap range maximum
ASoC: tas2770: Export 'die_temp' to sysfs
ASoC: tas2764: Export 'die_temp' to sysfs
ASoC: tas2764: Crop SDOUT zero-out mask based on BCLK ratio.../bindings/sound/ti,tas2770.yaml | 1 + .../bindings/sound/ti,tas27xx.yaml | 1 + sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/tas2764-quirks.h | 188 +++++++++++++++ sound/soc/codecs/tas2764.c | 302 +++++++++++++++++++++---- sound/soc/codecs/tas2764.h | 29 ++- sound/soc/codecs/tas2770.c | 264 +++++++++++++++++---- sound/soc/codecs/tas2770.h | 20 ++ 8 files changed, 711 insertions(+), 95 deletions(-)---
base-commit:cc7708ae5e2aab296203fcec774695fc9d995f48
change-id: 20250214-apple-codec-changes-6e656dc1e24dBest regards,
Signature · 1 line
James Calligeros <[email protected]>