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