File Index
Files and directories
Sign in
FILE

omap3pandora.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/ti/omap3pandora.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * omap3pandora.c  --  SoC audio for Pandora Handheld Console
  4 *
  5 * Author: GraÅžvydas Ignotas <notasas@gmail.com>
  6 */
  7
  8#include <linux/clk.h>
  9#include <linux/platform_device.h>
 10#include <linux/gpio/consumer.h>
 11#include <linux/delay.h>
 12#include <linux/regulator/consumer.h>
 13#include <linux/module.h>
 14
 15#include <sound/core.h>
 16#include <sound/pcm.h>
 17#include <sound/soc.h>
 18
 19#include <asm/mach-types.h>
 20#include <linux/platform_data/asoc-ti-mcbsp.h>
 21
 22#include "omap-mcbsp.h"
 23
 24#define PREFIX "ASoC omap3pandora: "
 25
 26static struct regulator *omap3pandora_dac_reg;
 27static struct gpio_desc *dac_power_gpio;
 28static struct gpio_desc *amp_power_gpio;
 29
 30static int omap3pandora_hw_params(struct snd_pcm_substream *substream,
 31	struct snd_pcm_hw_params *params)
 32{
 33	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 34	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
 35	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
 36	int ret;
 37
 38	/* Set the codec system clock for DAC and ADC */
 39	ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
 40					    SND_SOC_CLOCK_IN);
 41	if (ret < 0) {
 42		pr_err(PREFIX "can't set codec system clock\n");
 43		return ret;
 44	}
 45
 46	/* Set McBSP clock to external */
 47	ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT,
 48				     256 * params_rate(params),
 49				     SND_SOC_CLOCK_IN);
 50	if (ret < 0) {
 51		pr_err(PREFIX "can't set cpu system clock\n");
 52		return ret;
 53	}
 54
 55	ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, 8);
 56	if (ret < 0) {
 57		pr_err(PREFIX "can't set SRG clock divider\n");
 58		return ret;
 59	}
 60
 61	return 0;
 62}
 63
 64static int omap3pandora_dac_event(struct snd_soc_dapm_widget *w,
 65	struct snd_kcontrol *k, int event)
 66{
 67	int ret;
 68
 69	/*
 70	 * The PCM1773 DAC datasheet requires 1ms delay between switching
 71	 * VCC power on/off and /PD pin high/low
 72	 */
 73	if (SND_SOC_DAPM_EVENT_ON(event)) {
 74		struct device *dev = snd_soc_dapm_to_dev(w->dapm);
 75
 76		ret = regulator_enable(omap3pandora_dac_reg);
 77		if (ret) {
 78			dev_err(dev, "Failed to power DAC: %d\n", ret);
 79			return ret;
 80		}
 81		mdelay(1);
 82		gpiod_set_value(dac_power_gpio, 1);
 83	} else {
 84		gpiod_set_value(dac_power_gpio, 0);
 85		mdelay(1);
 86		regulator_disable(omap3pandora_dac_reg);
 87	}
 88
 89	return 0;
 90}
 91
 92static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w,
 93	struct snd_kcontrol *k, int event)
 94{
 95	if (SND_SOC_DAPM_EVENT_ON(event))
 96		gpiod_set_value(amp_power_gpio, 1);
 97	else
 98		gpiod_set_value(amp_power_gpio, 0);
 99
100	return 0;
101}
102
103/*
104 * Audio paths on Pandora board:
105 *
106 *  |O| ---> PCM DAC +-> AMP -> Headphone Jack
107 *  |M|         A    +--------> Line Out
108 *  |A| <~~clk~~+
109 *  |P| <--- TWL4030 <--------- Line In and MICs
110 */
111static const struct snd_soc_dapm_widget omap3pandora_dapm_widgets[] = {
112	SND_SOC_DAPM_DAC_E("PCM DAC", "HiFi Playback", SND_SOC_NOPM,
113			   0, 0, omap3pandora_dac_event,
114			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
115	SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM,
116			   0, 0, NULL, 0, omap3pandora_hp_event,
117			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
118	SND_SOC_DAPM_HP("Headphone Jack", NULL),
119	SND_SOC_DAPM_LINE("Line Out", NULL),
120
121	SND_SOC_DAPM_MIC("Mic (internal)", NULL),
122	SND_SOC_DAPM_MIC("Mic (external)", NULL),
123	SND_SOC_DAPM_LINE("Line In", NULL),
124};
125
126static const struct snd_soc_dapm_route omap3pandora_map[] = {
127	{"PCM DAC", NULL, "APLL Enable"},
128	{"Headphone Amplifier", NULL, "PCM DAC"},
129	{"Line Out", NULL, "PCM DAC"},
130	{"Headphone Jack", NULL, "Headphone Amplifier"},
131
132	{"AUXL", NULL, "Line In"},
133	{"AUXR", NULL, "Line In"},
134
135	{"MAINMIC", NULL, "Mic (internal)"},
136	{"Mic (internal)", NULL, "Mic Bias 1"},
137
138	{"SUBMIC", NULL, "Mic (external)"},
139	{"Mic (external)", NULL, "Mic Bias 2"},
140};
141
142static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd)
143{
144	struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(rtd->card);
145
146	/* All TWL4030 output pins are floating */
147	snd_soc_dapm_disable_pin(dapm, "EARPIECE");
148	snd_soc_dapm_disable_pin(dapm, "PREDRIVEL");
149	snd_soc_dapm_disable_pin(dapm, "PREDRIVER");
150	snd_soc_dapm_disable_pin(dapm, "HSOL");
151	snd_soc_dapm_disable_pin(dapm, "HSOR");
152	snd_soc_dapm_disable_pin(dapm, "CARKITL");
153	snd_soc_dapm_disable_pin(dapm, "CARKITR");
154	snd_soc_dapm_disable_pin(dapm, "HFL");
155	snd_soc_dapm_disable_pin(dapm, "HFR");
156	snd_soc_dapm_disable_pin(dapm, "VIBRA");
157
158	return 0;
159}
160
161static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd)
162{
163	struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(rtd->card);
164
165	/* Not comnnected */
166	snd_soc_dapm_disable_pin(dapm, "HSMIC");
167	snd_soc_dapm_disable_pin(dapm, "CARKITMIC");
168	snd_soc_dapm_disable_pin(dapm, "DIGIMIC0");
169	snd_soc_dapm_disable_pin(dapm, "DIGIMIC1");
170
171	return 0;
172}
173
174static const struct snd_soc_ops omap3pandora_ops = {
175	.hw_params = omap3pandora_hw_params,
176};
177
178/* Digital audio interface glue - connects codec <--> CPU */
179SND_SOC_DAILINK_DEFS(out,
180	DAILINK_COMP_ARRAY(COMP_CPU("omap-mcbsp.2")),
181	DAILINK_COMP_ARRAY(COMP_CODEC("twl4030-codec", "twl4030-hifi")),
182	DAILINK_COMP_ARRAY(COMP_PLATFORM("omap-mcbsp.2")));
183
184SND_SOC_DAILINK_DEFS(in,
185	DAILINK_COMP_ARRAY(COMP_CPU("omap-mcbsp.4")),
186	DAILINK_COMP_ARRAY(COMP_CODEC("twl4030-codec", "twl4030-hifi")),
187	DAILINK_COMP_ARRAY(COMP_PLATFORM("omap-mcbsp.4")));
188
189static struct snd_soc_dai_link omap3pandora_dai[] = {
190	{
191		.name = "PCM1773",
192		.stream_name = "HiFi Out",
193		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
194			   SND_SOC_DAIFMT_CBC_CFC,
195		.ops = &omap3pandora_ops,
196		.init = omap3pandora_out_init,
197		SND_SOC_DAILINK_REG(out),
198	}, {
199		.name = "TWL4030",
200		.stream_name = "Line/Mic In",
201		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
202			   SND_SOC_DAIFMT_CBC_CFC,
203		.ops = &omap3pandora_ops,
204		.init = omap3pandora_in_init,
205		SND_SOC_DAILINK_REG(in),
206	}
207};
208
209/* SoC card */
210static struct snd_soc_card snd_soc_card_omap3pandora = {
211	.name = "omap3pandora",
212	.owner = THIS_MODULE,
213	.dai_link = omap3pandora_dai,
214	.num_links = ARRAY_SIZE(omap3pandora_dai),
215
216	.dapm_widgets = omap3pandora_dapm_widgets,
217	.num_dapm_widgets = ARRAY_SIZE(omap3pandora_dapm_widgets),
218	.dapm_routes = omap3pandora_map,
219	.num_dapm_routes = ARRAY_SIZE(omap3pandora_map),
220};
221
222static struct platform_device *omap3pandora_snd_device;
223
224static int __init omap3pandora_soc_init(void)
225{
226	int ret;
227
228	if (!machine_is_omap3_pandora())
229		return -ENODEV;
230
231	pr_info("OMAP3 Pandora SoC init\n");
232
233	omap3pandora_snd_device = platform_device_alloc("soc-audio", -1);
234	if (omap3pandora_snd_device == NULL) {
235		pr_err(PREFIX "Platform device allocation failed\n");
236		return -ENOMEM;
237	}
238
239	platform_set_drvdata(omap3pandora_snd_device, &snd_soc_card_omap3pandora);
240
241	ret = platform_device_add(omap3pandora_snd_device);
242	if (ret) {
243		pr_err(PREFIX "Unable to add platform device\n");
244		goto fail2;
245	}
246
247	dac_power_gpio = devm_gpiod_get(&omap3pandora_snd_device->dev,
248					"dac", GPIOD_OUT_LOW);
249	if (IS_ERR(dac_power_gpio)) {
250		ret = PTR_ERR(dac_power_gpio);
251		goto fail3;
252	}
253
254	amp_power_gpio = devm_gpiod_get(&omap3pandora_snd_device->dev,
255					"amp", GPIOD_OUT_LOW);
256	if (IS_ERR(amp_power_gpio)) {
257		ret = PTR_ERR(amp_power_gpio);
258		goto fail3;
259	}
260
261	omap3pandora_dac_reg = regulator_get(&omap3pandora_snd_device->dev, "vcc");
262	if (IS_ERR(omap3pandora_dac_reg)) {
263		pr_err(PREFIX "Failed to get DAC regulator from %s: %ld\n",
264			dev_name(&omap3pandora_snd_device->dev),
265			PTR_ERR(omap3pandora_dac_reg));
266		ret = PTR_ERR(omap3pandora_dac_reg);
267		goto fail3;
268	}
269
270	return 0;
271
272fail3:
273	platform_device_del(omap3pandora_snd_device);
274fail2:
275	platform_device_put(omap3pandora_snd_device);
276
277	return ret;
278}
279module_init(omap3pandora_soc_init);
280
281static void __exit omap3pandora_soc_exit(void)
282{
283	regulator_put(omap3pandora_dac_reg);
284	platform_device_unregister(omap3pandora_snd_device);
285}
286module_exit(omap3pandora_soc_exit);
287
288MODULE_AUTHOR("Grazvydas Ignotas <notasas@gmail.com>");
289MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora");
290MODULE_LICENSE("GPL");
~/ Read-only file preview.