Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.13.7.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * SoC audio for HP iPAQ hx4700
  4 *
  5 * Copyright (c) 2009 Philipp Zabel
  6 */
  7
  8#include <linux/module.h>
  9#include <linux/timer.h>
 10#include <linux/interrupt.h>
 11#include <linux/platform_device.h>
 12#include <linux/delay.h>
 13#include <linux/gpio/consumer.h>
 14
 15#include <sound/core.h>
 16#include <sound/jack.h>
 17#include <sound/pcm.h>
 18#include <sound/pcm_params.h>
 19#include <sound/soc.h>
 20
 21#include <asm/mach-types.h>
 22#include "pxa2xx-i2s.h"
 23
 24static struct gpio_desc *gpiod_hp_driver, *gpiod_spk_sd;
 25static struct snd_soc_jack hs_jack;
 26
 27/* Headphones jack detection DAPM pin */
 28static struct snd_soc_jack_pin hs_jack_pin[] = {
 29	{
 30		.pin	= "Headphone Jack",
 31		.mask	= SND_JACK_HEADPHONE,
 32		.invert	= 1,
 33	},
 34	{
 35		.pin	= "Speaker",
 36		/* disable speaker when hp jack is inserted */
 37		.mask   = SND_JACK_HEADPHONE,
 38	},
 39};
 40
 41/* Headphones jack detection GPIO */
 42static struct snd_soc_jack_gpio hs_jack_gpio = {
 43	.name		= "earphone-det",
 44	.report		= SND_JACK_HEADPHONE,
 45	.debounce_time	= 200,
 46};
 47
 48/*
 49 * iPAQ hx4700 uses I2S for capture and playback.
 50 */
 51static int hx4700_hw_params(struct snd_pcm_substream *substream,
 52			    struct snd_pcm_hw_params *params)
 53{
 54	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 55	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
 56	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
 57	int ret = 0;
 58
 59	/* set the I2S system clock as output */
 60	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
 61			SND_SOC_CLOCK_OUT);
 62	if (ret < 0)
 63		return ret;
 64
 65	/* inform codec driver about clock freq *
 66	 * (PXA I2S always uses divider 256)    */
 67	ret = snd_soc_dai_set_sysclk(codec_dai, 0, 256 * params_rate(params),
 68			SND_SOC_CLOCK_IN);
 69	if (ret < 0)
 70		return ret;
 71
 72	return 0;
 73}
 74
 75static const struct snd_soc_ops hx4700_ops = {
 76	.hw_params = hx4700_hw_params,
 77};
 78
 79static int hx4700_spk_power(struct snd_soc_dapm_widget *w,
 80			    struct snd_kcontrol *k, int event)
 81{
 82	gpiod_set_value(gpiod_spk_sd, !SND_SOC_DAPM_EVENT_ON(event));
 83	return 0;
 84}
 85
 86static int hx4700_hp_power(struct snd_soc_dapm_widget *w,
 87			   struct snd_kcontrol *k, int event)
 88{
 89	gpiod_set_value(gpiod_hp_driver, !!SND_SOC_DAPM_EVENT_ON(event));
 90	return 0;
 91}
 92
 93/* hx4700 machine dapm widgets */
 94static const struct snd_soc_dapm_widget hx4700_dapm_widgets[] = {
 95	SND_SOC_DAPM_HP("Headphone Jack", hx4700_hp_power),
 96	SND_SOC_DAPM_SPK("Speaker", hx4700_spk_power),
 97	SND_SOC_DAPM_MIC("Built-in Microphone", NULL),
 98};
 99
100/* hx4700 machine audio_map */
101static const struct snd_soc_dapm_route hx4700_audio_map[] = {
102
103	/* Headphone connected to LOUT, ROUT */
104	{"Headphone Jack", NULL, "LOUT"},
105	{"Headphone Jack", NULL, "ROUT"},
106
107	/* Speaker connected to MOUT2 */
108	{"Speaker", NULL, "MOUT2"},
109
110	/* Microphone connected to MICIN */
111	{"MICIN", NULL, "Built-in Microphone"},
112	{"AIN", NULL, "MICOUT"},
113};
114
115/*
116 * Logic for a ak4641 as connected on a HP iPAQ hx4700
117 */
118static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd)
119{
120	int err;
121
122	/* Jack detection API stuff */
123	err = snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
124					 SND_JACK_HEADPHONE, &hs_jack,
125					 hs_jack_pin, ARRAY_SIZE(hs_jack_pin));
126	if (err)
127		return err;
128
129	err = snd_soc_jack_add_gpios(&hs_jack, 1, &hs_jack_gpio);
130
131	return err;
132}
133
134/* hx4700 digital audio interface glue - connects codec <--> CPU */
135SND_SOC_DAILINK_DEFS(ak4641,
136	DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
137	DAILINK_COMP_ARRAY(COMP_CODEC("ak4641.0-0012", "ak4641-hifi")),
138	DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
139
140static struct snd_soc_dai_link hx4700_dai = {
141	.name = "ak4641",
142	.stream_name = "AK4641",
143	.init = hx4700_ak4641_init,
144	.dai_fmt = SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
145		   SND_SOC_DAIFMT_CBS_CFS,
146	.ops = &hx4700_ops,
147	SND_SOC_DAILINK_REG(ak4641),
148};
149
150/* hx4700 audio machine driver */
151static struct snd_soc_card snd_soc_card_hx4700 = {
152	.name			= "iPAQ hx4700",
153	.owner			= THIS_MODULE,
154	.dai_link		= &hx4700_dai,
155	.num_links		= 1,
156	.dapm_widgets		= hx4700_dapm_widgets,
157	.num_dapm_widgets	= ARRAY_SIZE(hx4700_dapm_widgets),
158	.dapm_routes		= hx4700_audio_map,
159	.num_dapm_routes	= ARRAY_SIZE(hx4700_audio_map),
160	.fully_routed		= true,
161};
162
163static int hx4700_audio_probe(struct platform_device *pdev)
164{
165	int ret;
166
167	if (!machine_is_h4700())
168		return -ENODEV;
169
170	gpiod_hp_driver = devm_gpiod_get(&pdev->dev, "hp-driver", GPIOD_ASIS);
171	ret = PTR_ERR_OR_ZERO(gpiod_hp_driver);
172	if (ret)
173		return ret;
174	gpiod_spk_sd = devm_gpiod_get(&pdev->dev, "spk-sd", GPIOD_ASIS);
175	ret = PTR_ERR_OR_ZERO(gpiod_spk_sd);
176	if (ret)
177		return ret;
178
179	hs_jack_gpio.gpiod_dev = &pdev->dev;
180	snd_soc_card_hx4700.dev = &pdev->dev;
181	ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_hx4700);
182
183	return ret;
184}
185
186static int hx4700_audio_remove(struct platform_device *pdev)
187{
188	gpiod_set_value(gpiod_hp_driver, 0);
189	gpiod_set_value(gpiod_spk_sd, 0);
190	return 0;
191}
192
193static struct platform_driver hx4700_audio_driver = {
194	.driver	= {
195		.name = "hx4700-audio",
196		.pm = &snd_soc_pm_ops,
197	},
198	.probe	= hx4700_audio_probe,
199	.remove	= hx4700_audio_remove,
200};
201
202module_platform_driver(hx4700_audio_driver);
203
204MODULE_AUTHOR("Philipp Zabel");
205MODULE_DESCRIPTION("ALSA SoC iPAQ hx4700");
206MODULE_LICENSE("GPL");
207MODULE_ALIAS("platform:hx4700-audio");