• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * h1940-uda1380.c  --  ALSA Soc Audio Layer
3  *
4  * Copyright (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
5  * Copyright (c) 2010 Vasily Khoruzhick <anarsoul@gmail.com>
6  *
7  * Based on version from Arnaud Patard <arnaud.patard@rtp-net.org>
8  *
9  * This program is free software; you can redistribute  it and/or modify it
10  * under  the terms of  the GNU General  Public License as published by the
11  * Free Software Foundation;  either version 2 of the  License, or (at your
12  * option) any later version.
13  *
14  */
15 
16 #include <linux/types.h>
17 #include <linux/gpio.h>
18 #include <linux/module.h>
19 
20 #include <sound/soc.h>
21 #include <sound/jack.h>
22 
23 #include "regs-iis.h"
24 #include <asm/mach-types.h>
25 
26 #include "s3c24xx-i2s.h"
27 
28 static unsigned int rates[] = {
29 	11025,
30 	22050,
31 	44100,
32 };
33 
34 static struct snd_pcm_hw_constraint_list hw_rates = {
35 	.count = ARRAY_SIZE(rates),
36 	.list = rates,
37 	.mask = 0,
38 };
39 
40 static struct snd_soc_jack hp_jack;
41 
42 static struct snd_soc_jack_pin hp_jack_pins[] = {
43 	{
44 		.pin	= "Headphone Jack",
45 		.mask	= SND_JACK_HEADPHONE,
46 	},
47 	{
48 		.pin	= "Speaker",
49 		.mask	= SND_JACK_HEADPHONE,
50 		.invert	= 1,
51 	},
52 };
53 
54 static struct snd_soc_jack_gpio hp_jack_gpios[] = {
55 	{
56 		.gpio			= S3C2410_GPG(4),
57 		.name			= "hp-gpio",
58 		.report			= SND_JACK_HEADPHONE,
59 		.invert			= 1,
60 		.debounce_time		= 200,
61 	},
62 };
63 
h1940_startup(struct snd_pcm_substream * substream)64 static int h1940_startup(struct snd_pcm_substream *substream)
65 {
66 	struct snd_pcm_runtime *runtime = substream->runtime;
67 
68 	runtime->hw.rate_min = hw_rates.list[0];
69 	runtime->hw.rate_max = hw_rates.list[hw_rates.count - 1];
70 	runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
71 
72 	return snd_pcm_hw_constraint_list(runtime, 0,
73 					SNDRV_PCM_HW_PARAM_RATE,
74 					&hw_rates);
75 }
76 
h1940_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)77 static int h1940_hw_params(struct snd_pcm_substream *substream,
78 				struct snd_pcm_hw_params *params)
79 {
80 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
81 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
82 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
83 	int div;
84 	int ret;
85 	unsigned int rate = params_rate(params);
86 
87 	switch (rate) {
88 	case 11025:
89 	case 22050:
90 	case 44100:
91 		div = s3c24xx_i2s_get_clockrate() / (384 * rate);
92 		if (s3c24xx_i2s_get_clockrate() % (384 * rate) > (192 * rate))
93 			div++;
94 		break;
95 	default:
96 		dev_err(&rtd->dev, "%s: rate %d is not supported\n",
97 			__func__, rate);
98 		return -EINVAL;
99 	}
100 
101 	/* set codec DAI configuration */
102 	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
103 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
104 	if (ret < 0)
105 		return ret;
106 
107 	/* set cpu DAI configuration */
108 	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
109 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
110 	if (ret < 0)
111 		return ret;
112 
113 	/* select clock source */
114 	ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_PCLK, rate,
115 			SND_SOC_CLOCK_OUT);
116 	if (ret < 0)
117 		return ret;
118 
119 	/* set MCLK division for sample rate */
120 	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
121 		S3C2410_IISMOD_384FS);
122 	if (ret < 0)
123 		return ret;
124 
125 	/* set BCLK division for sample rate */
126 	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
127 		S3C2410_IISMOD_32FS);
128 	if (ret < 0)
129 		return ret;
130 
131 	/* set prescaler division for sample rate */
132 	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
133 		S3C24XX_PRESCALE(div, div));
134 	if (ret < 0)
135 		return ret;
136 
137 	return 0;
138 }
139 
140 static struct snd_soc_ops h1940_ops = {
141 	.startup	= h1940_startup,
142 	.hw_params	= h1940_hw_params,
143 };
144 
h1940_spk_power(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)145 static int h1940_spk_power(struct snd_soc_dapm_widget *w,
146 				struct snd_kcontrol *kcontrol, int event)
147 {
148 	if (SND_SOC_DAPM_EVENT_ON(event))
149 		gpio_set_value(S3C_GPIO_END + 9, 1);
150 	else
151 		gpio_set_value(S3C_GPIO_END + 9, 0);
152 
153 	return 0;
154 }
155 
156 /* h1940 machine dapm widgets */
157 static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
158 	SND_SOC_DAPM_HP("Headphone Jack", NULL),
159 	SND_SOC_DAPM_MIC("Mic Jack", NULL),
160 	SND_SOC_DAPM_SPK("Speaker", h1940_spk_power),
161 };
162 
163 /* h1940 machine audio_map */
164 static const struct snd_soc_dapm_route audio_map[] = {
165 	/* headphone connected to VOUTLHP, VOUTRHP */
166 	{"Headphone Jack", NULL, "VOUTLHP"},
167 	{"Headphone Jack", NULL, "VOUTRHP"},
168 
169 	/* ext speaker connected to VOUTL, VOUTR  */
170 	{"Speaker", NULL, "VOUTL"},
171 	{"Speaker", NULL, "VOUTR"},
172 
173 	/* mic is connected to VINM */
174 	{"VINM", NULL, "Mic Jack"},
175 };
176 
177 static struct platform_device *s3c24xx_snd_device;
178 
h1940_uda1380_init(struct snd_soc_pcm_runtime * rtd)179 static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd)
180 {
181 	struct snd_soc_codec *codec = rtd->codec;
182 	struct snd_soc_dapm_context *dapm = &codec->dapm;
183 	int err;
184 
185 	snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
186 	snd_soc_dapm_enable_pin(dapm, "Speaker");
187 	snd_soc_dapm_enable_pin(dapm, "Mic Jack");
188 
189 	snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
190 		&hp_jack);
191 
192 	snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins),
193 		hp_jack_pins);
194 
195 	snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
196 		hp_jack_gpios);
197 
198 	return 0;
199 }
200 
201 /* s3c24xx digital audio interface glue - connects codec <--> CPU */
202 static struct snd_soc_dai_link h1940_uda1380_dai[] = {
203 	{
204 		.name		= "uda1380",
205 		.stream_name	= "UDA1380 Duplex",
206 		.cpu_dai_name	= "s3c24xx-iis",
207 		.codec_dai_name	= "uda1380-hifi",
208 		.init		= h1940_uda1380_init,
209 		.platform_name	= "s3c24xx-iis",
210 		.codec_name	= "uda1380-codec.0-001a",
211 		.ops		= &h1940_ops,
212 	},
213 };
214 
215 static struct snd_soc_card h1940_asoc = {
216 	.name = "h1940",
217 	.owner = THIS_MODULE,
218 	.dai_link = h1940_uda1380_dai,
219 	.num_links = ARRAY_SIZE(h1940_uda1380_dai),
220 
221 	.dapm_widgets = uda1380_dapm_widgets,
222 	.num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
223 	.dapm_routes = audio_map,
224 	.num_dapm_routes = ARRAY_SIZE(audio_map),
225 };
226 
h1940_init(void)227 static int __init h1940_init(void)
228 {
229 	int ret;
230 
231 	if (!machine_is_h1940())
232 		return -ENODEV;
233 
234 	/* configure some gpios */
235 	ret = gpio_request(S3C_GPIO_END + 9, "speaker-power");
236 	if (ret)
237 		goto err_out;
238 
239 	ret = gpio_direction_output(S3C_GPIO_END + 9, 0);
240 	if (ret)
241 		goto err_gpio;
242 
243 	s3c24xx_snd_device = platform_device_alloc("soc-audio", -1);
244 	if (!s3c24xx_snd_device) {
245 		ret = -ENOMEM;
246 		goto err_gpio;
247 	}
248 
249 	platform_set_drvdata(s3c24xx_snd_device, &h1940_asoc);
250 	ret = platform_device_add(s3c24xx_snd_device);
251 
252 	if (ret)
253 		goto err_plat;
254 
255 	return 0;
256 
257 err_plat:
258 	platform_device_put(s3c24xx_snd_device);
259 err_gpio:
260 	gpio_free(S3C_GPIO_END + 9);
261 
262 err_out:
263 	return ret;
264 }
265 
h1940_exit(void)266 static void __exit h1940_exit(void)
267 {
268 	platform_device_unregister(s3c24xx_snd_device);
269 	snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
270 		hp_jack_gpios);
271 	gpio_free(S3C_GPIO_END + 9);
272 }
273 
274 module_init(h1940_init);
275 module_exit(h1940_exit);
276 
277 /* Module information */
278 MODULE_AUTHOR("Arnaud Patard, Vasily Khoruzhick");
279 MODULE_DESCRIPTION("ALSA SoC H1940");
280 MODULE_LICENSE("GPL");
281