Skip to content

Commit 961b962

Browse files
povikjannau
authored andcommitted
ASoC: tas2764: Add optional 'Apple quirks'
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. Signed-off-by: Martin Povišer <povik+lin@cutebit.org>
1 parent 769b0e3 commit 961b962

2 files changed

Lines changed: 213 additions & 26 deletions

File tree

sound/soc/codecs/tas2764-quirks.h

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
#ifndef __TAS2764_QUIRKS__
3+
#define __TAS2764_QUIRKS__
4+
5+
#include <linux/regmap.h>
6+
7+
#include "tas2764.h"
8+
9+
/*
10+
* Disable noise gate and flip down reserved bit in NS_CFG0
11+
*/
12+
#define TAS2764_NOISE_GATE_DISABLE BIT(0)
13+
14+
struct reg_sequence tas2764_noise_gate_dis_seq[] = {
15+
REG_SEQ0(TAS2764_REG(0x0, 0x35), 0xb0)
16+
};
17+
18+
/*
19+
* CONV_VBAT_PVDD_MODE=1
20+
*/
21+
#define TAS2764_CONV_VBAT_PVDD_MODE BIT(1)
22+
23+
struct reg_sequence tas2764_conv_vbat_pvdd_mode_seq[] = {
24+
REG_SEQ0(TAS2764_REG(0x0, 0x6b), 0x41)
25+
};
26+
27+
/*
28+
* Reset of DAC modulator when DSP is OFF
29+
*/
30+
#define TAS2764_DMOD_RST BIT(2)
31+
32+
struct reg_sequence tas2764_dmod_rst_seq[] = {
33+
REG_SEQ0(TAS2764_REG(0x0, 0x76), 0x0)
34+
};
35+
36+
/*
37+
* Unknown 0x133/0x137 writes (maybe TDM related)
38+
*/
39+
#define TAS2764_UNK_SEQ0 BIT(3)
40+
41+
struct reg_sequence tas2764_unk_seq0[] = {
42+
REG_SEQ0(TAS2764_REG(0x1, 0x33), 0x80),
43+
REG_SEQ0(TAS2764_REG(0x1, 0x37), 0x3a),
44+
};
45+
46+
/*
47+
* Unknown 0x614 - 0x61f writes
48+
*/
49+
#define TAS2764_APPLE_UNK_SEQ1 BIT(4)
50+
51+
struct reg_sequence tas2764_unk_seq1[] = {
52+
REG_SEQ0(TAS2764_REG(0x6, 0x14), 0x0),
53+
REG_SEQ0(TAS2764_REG(0x6, 0x15), 0x13),
54+
REG_SEQ0(TAS2764_REG(0x6, 0x16), 0x52),
55+
REG_SEQ0(TAS2764_REG(0x6, 0x17), 0x0),
56+
REG_SEQ0(TAS2764_REG(0x6, 0x18), 0xe4),
57+
REG_SEQ0(TAS2764_REG(0x6, 0x19), 0xc),
58+
REG_SEQ0(TAS2764_REG(0x6, 0x16), 0xaa),
59+
REG_SEQ0(TAS2764_REG(0x6, 0x1b), 0x0),
60+
REG_SEQ0(TAS2764_REG(0x6, 0x1c), 0x12),
61+
REG_SEQ0(TAS2764_REG(0x6, 0x1d), 0xa0),
62+
REG_SEQ0(TAS2764_REG(0x6, 0x1e), 0xd8),
63+
REG_SEQ0(TAS2764_REG(0x6, 0x1f), 0x0),
64+
};
65+
66+
/*
67+
* Unknown writes in the 0xfd page (with secondary paging inside)
68+
*/
69+
#define TAS2764_APPLE_UNK_SEQ2 BIT(5)
70+
71+
struct reg_sequence tas2764_unk_seq2[] = {
72+
REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd),
73+
REG_SEQ0(TAS2764_REG(0xfd, 0x6c), 0x2),
74+
REG_SEQ0(TAS2764_REG(0xfd, 0x6d), 0xf),
75+
REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0),
76+
};
77+
78+
/*
79+
* Disable 'Thermal Threshold 1'
80+
*/
81+
#define TAS2764_THERMAL_TH1_DISABLE BIT(6)
82+
83+
struct reg_sequence tas2764_thermal_th1_dis_seq[] = {
84+
REG_SEQ0(TAS2764_REG(0x1, 0x47), 0x2),
85+
};
86+
87+
/*
88+
* Imitate Apple's shutdown dance
89+
*/
90+
#define TAS2764_SHUTDOWN_DANCE BIT(7)
91+
92+
struct reg_sequence tas2764_shutdown_dance_init_seq[] = {
93+
/*
94+
* SDZ_MODE=01 (immediate)
95+
*
96+
* We want the shutdown to happen under the influence of
97+
* the magic writes in the 0xfdXX region, so make sure
98+
* the shutdown is immediate and there's no grace period
99+
* followed by the codec part.
100+
*/
101+
REG_SEQ0(TAS2764_REG(0x0, 0x7), 0x60),
102+
};
103+
104+
struct reg_sequence tas2764_pre_shutdown_seq[] = {
105+
REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd), /* switch hidden page */
106+
REG_SEQ0(TAS2764_REG(0xfd, 0x64), 0x4), /* do write (unknown semantics) */
107+
REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0), /* switch hidden page back */
108+
};
109+
110+
struct reg_sequence tas2764_post_shutdown_seq[] = {
111+
REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd),
112+
REG_SEQ0(TAS2764_REG(0xfd, 0x64), 0x0), /* revert write from pre sequence */
113+
REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0),
114+
};
115+
116+
static int tas2764_do_quirky_pwr_ctrl_change(struct tas2764_priv *tas2764,
117+
unsigned int target)
118+
{
119+
unsigned int curr;
120+
int ret;
121+
122+
curr = snd_soc_component_read_field(tas2764->component,
123+
TAS2764_PWR_CTRL,
124+
TAS2764_PWR_CTRL_MASK);
125+
126+
if (target == curr)
127+
return 0;
128+
129+
#define TRANSITION(new, old) ((new) << 8 | (old))
130+
switch (TRANSITION(target, curr)) {
131+
case TRANSITION(TAS2764_PWR_CTRL_SHUTDOWN, TAS2764_PWR_CTRL_MUTE):
132+
case TRANSITION(TAS2764_PWR_CTRL_SHUTDOWN, TAS2764_PWR_CTRL_ACTIVE):
133+
ret = regmap_multi_reg_write(tas2764->regmap, tas2764_pre_shutdown_seq,
134+
ARRAY_SIZE(tas2764_pre_shutdown_seq));
135+
if (ret < 0)
136+
break;
137+
138+
ret = snd_soc_component_update_bits(tas2764->component,
139+
TAS2764_PWR_CTRL,
140+
TAS2764_PWR_CTRL_MASK,
141+
TAS2764_PWR_CTRL_SHUTDOWN);
142+
if (ret > 0)
143+
break;
144+
145+
ret = regmap_multi_reg_write(tas2764->regmap, tas2764_post_shutdown_seq,
146+
ARRAY_SIZE(tas2764_post_shutdown_seq));
147+
fallthrough;
148+
default:
149+
ret = snd_soc_component_update_bits(tas2764->component, TAS2764_PWR_CTRL,
150+
TAS2764_PWR_CTRL_MASK, target);
151+
}
152+
#undef TRANSITION
153+
154+
if (ret < 0)
155+
return ret;
156+
return 0;
157+
}
158+
159+
/*
160+
* Via devicetree (TODO):
161+
* - switch from spread spectrum to class-D switching
162+
* - disable edge control
163+
* - set BOP settings (the BOP config bits *and* BOP_SRC)
164+
*/
165+
166+
/*
167+
* Other setup TODOs:
168+
* - DVC ramp rate
169+
*/
170+
171+
static struct tas2764_quirk_init_sequence {
172+
struct reg_sequence *seq;
173+
int len;
174+
} tas2764_quirk_init_sequences[] = {
175+
{ tas2764_noise_gate_dis_seq, ARRAY_SIZE(tas2764_noise_gate_dis_seq) },
176+
{ tas2764_dmod_rst_seq, ARRAY_SIZE(tas2764_dmod_rst_seq) },
177+
{ tas2764_conv_vbat_pvdd_mode_seq, ARRAY_SIZE(tas2764_conv_vbat_pvdd_mode_seq) },
178+
{ tas2764_unk_seq0, ARRAY_SIZE(tas2764_unk_seq0) },
179+
{ tas2764_unk_seq1, ARRAY_SIZE(tas2764_unk_seq1) },
180+
{ tas2764_unk_seq2, ARRAY_SIZE(tas2764_unk_seq2) },
181+
{ tas2764_thermal_th1_dis_seq, ARRAY_SIZE(tas2764_thermal_th1_dis_seq) },
182+
{ tas2764_shutdown_dance_init_seq, ARRAY_SIZE(tas2764_shutdown_dance_init_seq) },
183+
};
184+
185+
#endif /* __TAS2764_QUIRKS__ */

sound/soc/codecs/tas2764.c

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ struct tas2764_priv {
4747
bool unmuted;
4848
};
4949

50+
static int apple_quirks;
51+
module_param(apple_quirks, int, 0644);
52+
MODULE_PARM_DESC(apple_quirks, "Mask of quirks to mimic after Apple's SN012776 driver");
53+
54+
#include "tas2764-quirks.h"
55+
5056
static const char *tas2764_int_ltch0_msgs[8] = {
5157
"fault: over temperature", /* INT_LTCH0 & BIT(0) */
5258
"fault: over current",
@@ -124,6 +130,9 @@ static int tas2764_update_pwr_ctrl(struct tas2764_priv *tas2764)
124130
else
125131
val = TAS2764_PWR_CTRL_SHUTDOWN;
126132

133+
if (apple_quirks & TAS2764_SHUTDOWN_DANCE)
134+
return tas2764_do_quirky_pwr_ctrl_change(tas2764, val);
135+
127136
ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
128137
TAS2764_PWR_CTRL_MASK, val);
129138
if (ret < 0)
@@ -577,32 +586,25 @@ static uint8_t sn012776_bop_presets[] = {
577586

578587
static const struct regmap_config tas2764_i2c_regmap;
579588

580-
static int tas2764_apply_unk_apple_quirk(struct snd_soc_component *component)
589+
static int tas2764_apply_init_quirks(struct tas2764_priv * tas2764)
581590
{
582-
int ret;
583-
584-
ret = snd_soc_component_write(component, 0xfd0d, 0xd);
585-
586-
if (ret < 0)
587-
return ret;
588-
589-
590-
ret = snd_soc_component_write(component, 0xfd6c, 0x2);
591-
592-
if (ret < 0)
593-
return ret;
591+
int ret, i;
594592

593+
for (i = 0; i < ARRAY_SIZE(tas2764_quirk_init_sequences); i++) {
594+
struct tas2764_quirk_init_sequence *init_seq = \
595+
&tas2764_quirk_init_sequences[i];
596+
if (!init_seq->seq)
597+
continue;
595598

596-
ret = snd_soc_component_write(component, 0xfd6d, 0xf);
599+
if (!(BIT(i) & apple_quirks))
600+
continue;
597601

598-
if (ret < 0)
599-
return ret;
602+
ret = regmap_multi_reg_write(tas2764->regmap, init_seq->seq,
603+
init_seq->len);
600604

601-
602-
ret = snd_soc_component_write(component, 0xfd0d, 0x0);
603-
604-
if (ret < 0)
605-
return ret;
605+
if (ret < 0)
606+
return ret;
607+
}
606608

607609
return 0;
608610
}
@@ -692,11 +694,6 @@ static int tas2764_codec_probe(struct snd_soc_component *component)
692694
}
693695

694696
if (tas2764->devid == DEVID_SN012776) {
695-
ret = tas2764_apply_unk_apple_quirk(component);
696-
697-
if (ret < 0)
698-
return ret;
699-
700697
ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
701698
TAS2764_PWR_CTRL_BOP_SRC,
702699
TAS2764_PWR_CTRL_BOP_SRC);
@@ -711,6 +708,11 @@ static int tas2764_codec_probe(struct snd_soc_component *component)
711708
if (ret < 0)
712709
return ret;
713710
}
711+
712+
ret = tas2764_apply_init_quirks(tas2764);
713+
714+
if (ret < 0)
715+
return ret;
714716
}
715717

716718
ret = sysfs_create_groups(&component->dev->kobj, tas2764_sysfs_groups);

0 commit comments

Comments
 (0)