|
| 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__ */ |
0 commit comments