1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * linux/sound/soc/pxa/z2.c
4  *
5  * SoC Audio driver for Aeronix Zipit Z2
6  *
7  * Copyright (C) 2009 Ken McGuire <kenm@desertweyr.com>
8  * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
9  */
10 
11 #include <linux/module.h>
12 #include <linux/moduleparam.h>
13 #include <linux/timer.h>
14 #include <linux/interrupt.h>
15 #include <linux/platform_device.h>
16 #include <linux/gpio.h>
17 
18 #include <sound/core.h>
19 #include <sound/pcm.h>
20 #include <sound/soc.h>
21 #include <sound/jack.h>
22 
23 #include <asm/mach-types.h>
24 #include <mach/hardware.h>
25 #include <mach/audio.h>
26 #include <mach/z2.h>
27 
28 #include "../codecs/wm8750.h"
29 #include "pxa2xx-i2s.h"
30 
31 static struct snd_soc_card snd_soc_z2;
32 
z2_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)33 static int z2_hw_params(struct snd_pcm_substream *substream,
34 	struct snd_pcm_hw_params *params)
35 {
36 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
37 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
38 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
39 	unsigned int clk = 0;
40 	int ret = 0;
41 
42 	switch (params_rate(params)) {
43 	case 8000:
44 	case 16000:
45 	case 48000:
46 	case 96000:
47 		clk = 12288000;
48 		break;
49 	case 11025:
50 	case 22050:
51 	case 44100:
52 		clk = 11289600;
53 		break;
54 	}
55 
56 	/* set the codec system clock for DAC and ADC */
57 	ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
58 		SND_SOC_CLOCK_IN);
59 	if (ret < 0)
60 		return ret;
61 
62 	/* set the I2S system clock as input (unused) */
63 	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
64 		SND_SOC_CLOCK_IN);
65 	if (ret < 0)
66 		return ret;
67 
68 	return 0;
69 }
70 
71 static struct snd_soc_jack hs_jack;
72 
73 /* Headset jack detection DAPM pins */
74 static struct snd_soc_jack_pin hs_jack_pins[] = {
75 	{
76 		.pin	= "Mic Jack",
77 		.mask	= SND_JACK_MICROPHONE,
78 	},
79 	{
80 		.pin	= "Headphone Jack",
81 		.mask	= SND_JACK_HEADPHONE,
82 	},
83 	{
84 		.pin    = "Ext Spk",
85 		.mask   = SND_JACK_HEADPHONE,
86 		.invert = 1
87 	},
88 };
89 
90 /* Headset jack detection gpios */
91 static struct snd_soc_jack_gpio hs_jack_gpios[] = {
92 	{
93 		.gpio		= GPIO37_ZIPITZ2_HEADSET_DETECT,
94 		.name		= "hsdet-gpio",
95 		.report		= SND_JACK_HEADSET,
96 		.debounce_time	= 200,
97 		.invert		= 1,
98 	},
99 };
100 
101 /* z2 machine dapm widgets */
102 static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
103 	SND_SOC_DAPM_HP("Headphone Jack", NULL),
104 	SND_SOC_DAPM_MIC("Mic Jack", NULL),
105 	SND_SOC_DAPM_SPK("Ext Spk", NULL),
106 
107 	/* headset is a mic and mono headphone */
108 	SND_SOC_DAPM_HP("Headset Jack", NULL),
109 };
110 
111 /* Z2 machine audio_map */
112 static const struct snd_soc_dapm_route z2_audio_map[] = {
113 
114 	/* headphone connected to LOUT1, ROUT1 */
115 	{"Headphone Jack", NULL, "LOUT1"},
116 	{"Headphone Jack", NULL, "ROUT1"},
117 
118 	/* ext speaker connected to LOUT2, ROUT2  */
119 	{"Ext Spk", NULL, "ROUT2"},
120 	{"Ext Spk", NULL, "LOUT2"},
121 
122 	/* mic is connected to R input 2 - with bias */
123 	{"RINPUT2", NULL, "Mic Bias"},
124 	{"Mic Bias", NULL, "Mic Jack"},
125 };
126 
127 /*
128  * Logic for a wm8750 as connected on a Z2 Device
129  */
z2_wm8750_init(struct snd_soc_pcm_runtime * rtd)130 static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd)
131 {
132 	int ret;
133 
134 	/* Jack detection API stuff */
135 	ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
136 				    &hs_jack, hs_jack_pins,
137 				    ARRAY_SIZE(hs_jack_pins));
138 	if (ret)
139 		goto err;
140 
141 	ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
142 				hs_jack_gpios);
143 	if (ret)
144 		goto err;
145 
146 	return 0;
147 
148 err:
149 	return ret;
150 }
151 
152 static const struct snd_soc_ops z2_ops = {
153 	.hw_params = z2_hw_params,
154 };
155 
156 /* z2 digital audio interface glue - connects codec <--> CPU */
157 SND_SOC_DAILINK_DEFS(wm8750,
158 	DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
159 	DAILINK_COMP_ARRAY(COMP_CODEC("wm8750.0-001b", "wm8750-hifi")),
160 	DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
161 
162 static struct snd_soc_dai_link z2_dai = {
163 	.name		= "wm8750",
164 	.stream_name	= "WM8750",
165 	.init		= z2_wm8750_init,
166 	.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
167 			  SND_SOC_DAIFMT_CBS_CFS,
168 	.ops		= &z2_ops,
169 	SND_SOC_DAILINK_REG(wm8750),
170 };
171 
172 /* z2 audio machine driver */
173 static struct snd_soc_card snd_soc_z2 = {
174 	.name		= "Z2",
175 	.owner		= THIS_MODULE,
176 	.dai_link	= &z2_dai,
177 	.num_links	= 1,
178 
179 	.dapm_widgets = wm8750_dapm_widgets,
180 	.num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
181 	.dapm_routes = z2_audio_map,
182 	.num_dapm_routes = ARRAY_SIZE(z2_audio_map),
183 	.fully_routed = true,
184 };
185 
186 static struct platform_device *z2_snd_device;
187 
z2_init(void)188 static int __init z2_init(void)
189 {
190 	int ret;
191 
192 	if (!machine_is_zipit2())
193 		return -ENODEV;
194 
195 	z2_snd_device = platform_device_alloc("soc-audio", -1);
196 	if (!z2_snd_device)
197 		return -ENOMEM;
198 
199 	platform_set_drvdata(z2_snd_device, &snd_soc_z2);
200 	ret = platform_device_add(z2_snd_device);
201 
202 	if (ret)
203 		platform_device_put(z2_snd_device);
204 
205 	return ret;
206 }
207 
z2_exit(void)208 static void __exit z2_exit(void)
209 {
210 	platform_device_unregister(z2_snd_device);
211 }
212 
213 module_init(z2_init);
214 module_exit(z2_exit);
215 
216 MODULE_AUTHOR("Ken McGuire <kenm@desertweyr.com>, "
217 		"Marek Vasut <marek.vasut@gmail.com>");
218 MODULE_DESCRIPTION("ALSA SoC ZipitZ2");
219 MODULE_LICENSE("GPL");
220