1 // SPDX-License-Identifier: GPL-2.0
3 // Copyright (c) 2020 BayLibre, SAS.
4 // Author: Jerome Brunet <jbrunet@baylibre.com>
7 #include <linux/delay.h>
8 #include <linux/module.h>
9 #include <linux/regmap.h>
10 #include <linux/regulator/consumer.h>
11 #include <linux/reset.h>
12 #include <sound/soc.h>
13 #include <sound/tlv.h>
26 #define REFP_BUF_EN BIT(12)
27 #define BIAS_CURRENT_EN BIT(13)
28 #define VMID_GEN_FAST BIT(14)
29 #define VMID_GEN_EN BIT(15)
30 #define I2S_MODE BIT(30)
31 #define VOL_CTRL0 0x04
34 #define VOL_CTRL1 0x08
37 #define VC_RAMP_MODE 12
39 #define UNMUTE_MODE 14
40 #define DAC_SOFT_MUTE 15
43 #define LINEOUT_CFG 0x0c
48 #define POWER_CFG 0x10
52 struct regulator
*avdd
;
55 static int t9015_dai_set_fmt(struct snd_soc_dai
*dai
, unsigned int fmt
)
57 struct snd_soc_component
*component
= dai
->component
;
60 switch (fmt
& SND_SOC_DAIFMT_MASTER_MASK
) {
61 case SND_SOC_DAIFMT_CBM_CFM
:
65 case SND_SOC_DAIFMT_CBS_CFS
:
73 snd_soc_component_update_bits(component
, BLOCK_EN
, I2S_MODE
, val
);
75 if (((fmt
& SND_SOC_DAIFMT_FORMAT_MASK
) != SND_SOC_DAIFMT_I2S
) &&
76 ((fmt
& SND_SOC_DAIFMT_FORMAT_MASK
) != SND_SOC_DAIFMT_LEFT_J
))
82 static const struct snd_soc_dai_ops t9015_dai_ops
= {
83 .set_fmt
= t9015_dai_set_fmt
,
86 static struct snd_soc_dai_driver t9015_dai
= {
89 .stream_name
= "Playback",
92 .rates
= SNDRV_PCM_RATE_8000_96000
,
93 .formats
= (SNDRV_PCM_FMTBIT_S8
|
94 SNDRV_PCM_FMTBIT_S16_LE
|
95 SNDRV_PCM_FMTBIT_S20_LE
|
96 SNDRV_PCM_FMTBIT_S24_LE
),
98 .ops
= &t9015_dai_ops
,
101 static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv
, -9525, 0);
103 static const char * const ramp_rate_txt
[] = { "Fast", "Slow" };
104 static SOC_ENUM_SINGLE_DECL(ramp_rate_enum
, VOL_CTRL1
, RAMP_RATE
,
107 static const char * const dacr_in_txt
[] = { "Right", "Left" };
108 static SOC_ENUM_SINGLE_DECL(dacr_in_enum
, BLOCK_EN
, DACR_SRC
, dacr_in_txt
);
110 static const char * const dacl_in_txt
[] = { "Left", "Right" };
111 static SOC_ENUM_SINGLE_DECL(dacl_in_enum
, BLOCK_EN
, DACL_SRC
, dacl_in_txt
);
113 static const char * const mono_txt
[] = { "Stereo", "Mono"};
114 static SOC_ENUM_SINGLE_DECL(mono_enum
, VOL_CTRL1
, DAC_MONO
, mono_txt
);
116 static const struct snd_kcontrol_new t9015_snd_controls
[] = {
117 /* Volume Controls */
118 SOC_ENUM("Playback Channel Mode", mono_enum
),
119 SOC_SINGLE("Playback Switch", VOL_CTRL1
, DAC_SOFT_MUTE
, 1, 1),
120 SOC_DOUBLE_TLV("Playback Volume", VOL_CTRL1
, DACL_VC
, DACR_VC
,
121 0xff, 0, dac_vol_tlv
),
124 SOC_ENUM("Ramp Rate", ramp_rate_enum
),
125 SOC_SINGLE("Volume Ramp Switch", VOL_CTRL1
, VC_RAMP_MODE
, 1, 0),
126 SOC_SINGLE("Mute Ramp Switch", VOL_CTRL1
, MUTE_MODE
, 1, 0),
127 SOC_SINGLE("Unmute Ramp Switch", VOL_CTRL1
, UNMUTE_MODE
, 1, 0),
130 static const struct snd_kcontrol_new t9015_right_dac_mux
=
131 SOC_DAPM_ENUM("Right DAC Source", dacr_in_enum
);
132 static const struct snd_kcontrol_new t9015_left_dac_mux
=
133 SOC_DAPM_ENUM("Left DAC Source", dacl_in_enum
);
135 static const struct snd_soc_dapm_widget t9015_dapm_widgets
[] = {
136 SND_SOC_DAPM_AIF_IN("Right IN", NULL
, 0, SND_SOC_NOPM
, 0, 0),
137 SND_SOC_DAPM_AIF_IN("Left IN", NULL
, 0, SND_SOC_NOPM
, 0, 0),
138 SND_SOC_DAPM_MUX("Right DAC Sel", SND_SOC_NOPM
, 0, 0,
139 &t9015_right_dac_mux
),
140 SND_SOC_DAPM_MUX("Left DAC Sel", SND_SOC_NOPM
, 0, 0,
141 &t9015_left_dac_mux
),
142 SND_SOC_DAPM_DAC("Right DAC", NULL
, BLOCK_EN
, DACR_EN
, 0),
143 SND_SOC_DAPM_DAC("Left DAC", NULL
, BLOCK_EN
, DACL_EN
, 0),
144 SND_SOC_DAPM_OUT_DRV("Right- Driver", BLOCK_EN
, LORN_EN
, 0,
146 SND_SOC_DAPM_OUT_DRV("Right+ Driver", BLOCK_EN
, LORP_EN
, 0,
148 SND_SOC_DAPM_OUT_DRV("Left- Driver", BLOCK_EN
, LOLN_EN
, 0,
150 SND_SOC_DAPM_OUT_DRV("Left+ Driver", BLOCK_EN
, LOLP_EN
, 0,
152 SND_SOC_DAPM_OUTPUT("LORN"),
153 SND_SOC_DAPM_OUTPUT("LORP"),
154 SND_SOC_DAPM_OUTPUT("LOLN"),
155 SND_SOC_DAPM_OUTPUT("LOLP"),
158 static const struct snd_soc_dapm_route t9015_dapm_routes
[] = {
159 { "Right IN", NULL
, "Playback" },
160 { "Left IN", NULL
, "Playback" },
161 { "Right DAC Sel", "Right", "Right IN" },
162 { "Right DAC Sel", "Left", "Left IN" },
163 { "Left DAC Sel", "Right", "Right IN" },
164 { "Left DAC Sel", "Left", "Left IN" },
165 { "Right DAC", NULL
, "Right DAC Sel" },
166 { "Left DAC", NULL
, "Left DAC Sel" },
167 { "Right- Driver", NULL
, "Right DAC" },
168 { "Right+ Driver", NULL
, "Right DAC" },
169 { "Left- Driver", NULL
, "Left DAC" },
170 { "Left+ Driver", NULL
, "Left DAC" },
171 { "LORN", NULL
, "Right- Driver", },
172 { "LORP", NULL
, "Right+ Driver", },
173 { "LOLN", NULL
, "Left- Driver", },
174 { "LOLP", NULL
, "Left+ Driver", },
177 static int t9015_set_bias_level(struct snd_soc_component
*component
,
178 enum snd_soc_bias_level level
)
180 struct t9015
*priv
= snd_soc_component_get_drvdata(component
);
181 enum snd_soc_bias_level now
=
182 snd_soc_component_get_bias_level(component
);
186 case SND_SOC_BIAS_ON
:
187 snd_soc_component_update_bits(component
, BLOCK_EN
,
191 case SND_SOC_BIAS_PREPARE
:
192 snd_soc_component_update_bits(component
, BLOCK_EN
,
196 case SND_SOC_BIAS_STANDBY
:
197 ret
= regulator_enable(priv
->avdd
);
199 dev_err(component
->dev
, "AVDD enable failed\n");
203 if (now
== SND_SOC_BIAS_OFF
) {
204 snd_soc_component_update_bits(component
, BLOCK_EN
,
205 VMID_GEN_EN
| VMID_GEN_FAST
| REFP_BUF_EN
,
206 VMID_GEN_EN
| VMID_GEN_FAST
| REFP_BUF_EN
);
209 snd_soc_component_update_bits(component
, BLOCK_EN
,
215 case SND_SOC_BIAS_OFF
:
216 snd_soc_component_update_bits(component
, BLOCK_EN
,
217 VMID_GEN_EN
| VMID_GEN_FAST
| REFP_BUF_EN
,
220 regulator_disable(priv
->avdd
);
227 static const struct snd_soc_component_driver t9015_codec_driver
= {
228 .set_bias_level
= t9015_set_bias_level
,
229 .controls
= t9015_snd_controls
,
230 .num_controls
= ARRAY_SIZE(t9015_snd_controls
),
231 .dapm_widgets
= t9015_dapm_widgets
,
232 .num_dapm_widgets
= ARRAY_SIZE(t9015_dapm_widgets
),
233 .dapm_routes
= t9015_dapm_routes
,
234 .num_dapm_routes
= ARRAY_SIZE(t9015_dapm_routes
),
235 .suspend_bias_off
= 1,
237 .non_legacy_dai_naming
= 1,
240 static const struct regmap_config t9015_regmap_config
= {
244 .max_register
= POWER_CFG
,
247 static int t9015_probe(struct platform_device
*pdev
)
249 struct device
*dev
= &pdev
->dev
;
252 struct regmap
*regmap
;
255 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
258 platform_set_drvdata(pdev
, priv
);
260 priv
->pclk
= devm_clk_get(dev
, "pclk");
261 if (IS_ERR(priv
->pclk
)) {
262 if (PTR_ERR(priv
->pclk
) != -EPROBE_DEFER
)
263 dev_err(dev
, "failed to get core clock\n");
264 return PTR_ERR(priv
->pclk
);
267 priv
->avdd
= devm_regulator_get(dev
, "AVDD");
268 if (IS_ERR(priv
->avdd
)) {
269 if (PTR_ERR(priv
->avdd
) != -EPROBE_DEFER
)
270 dev_err(dev
, "failed to AVDD\n");
271 return PTR_ERR(priv
->avdd
);
274 ret
= clk_prepare_enable(priv
->pclk
);
276 dev_err(dev
, "core clock enable failed\n");
280 ret
= devm_add_action_or_reset(dev
,
281 (void(*)(void *))clk_disable_unprepare
,
286 ret
= device_reset(dev
);
288 dev_err(dev
, "reset failed\n");
292 regs
= devm_platform_ioremap_resource(pdev
, 0);
294 dev_err(dev
, "register map failed\n");
295 return PTR_ERR(regs
);
298 regmap
= devm_regmap_init_mmio(dev
, regs
, &t9015_regmap_config
);
299 if (IS_ERR(regmap
)) {
300 dev_err(dev
, "regmap init failed\n");
301 return PTR_ERR(regmap
);
305 * Initialize output polarity:
306 * ATM the output polarity is fixed but in the future it might useful
307 * to add DT property to set this depending on the platform needs
309 regmap_write(regmap
, LINEOUT_CFG
, 0x1111);
311 return devm_snd_soc_register_component(dev
, &t9015_codec_driver
,
315 static const struct of_device_id t9015_ids
[] __maybe_unused
= {
316 { .compatible
= "amlogic,t9015", },
319 MODULE_DEVICE_TABLE(of
, t9015_ids
);
321 static struct platform_driver t9015_driver
= {
323 .name
= "t9015-codec",
324 .of_match_table
= of_match_ptr(t9015_ids
),
326 .probe
= t9015_probe
,
329 module_platform_driver(t9015_driver
);
331 MODULE_DESCRIPTION("ASoC Amlogic T9015 codec driver");
332 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
333 MODULE_LICENSE("GPL");