1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * SoC audio for HTC Magician
5 * Copyright (c) 2006 Philipp Zabel <philipp.zabel@gmail.com>
8 * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
9 * Richard Purdie <richard@openedhand.com>
12 #include <linux/module.h>
13 #include <linux/timer.h>
14 #include <linux/interrupt.h>
15 #include <linux/platform_device.h>
16 #include <linux/delay.h>
17 #include <linux/gpio.h>
18 #include <linux/i2c.h>
20 #include <sound/core.h>
21 #include <sound/pcm.h>
22 #include <sound/pcm_params.h>
23 #include <sound/soc.h>
24 #include <sound/uda1380.h>
26 #include <mach/magician.h>
27 #include <asm/mach-types.h>
28 #include "../codecs/uda1380.h"
29 #include "pxa2xx-i2s.h"
32 #define MAGICIAN_MIC 0
33 #define MAGICIAN_MIC_EXT 1
35 static int magician_hp_switch
;
36 static int magician_spk_switch
= 1;
37 static int magician_in_sel
= MAGICIAN_MIC
;
39 static void magician_ext_control(struct snd_soc_dapm_context
*dapm
)
42 snd_soc_dapm_mutex_lock(dapm
);
44 if (magician_spk_switch
)
45 snd_soc_dapm_enable_pin_unlocked(dapm
, "Speaker");
47 snd_soc_dapm_disable_pin_unlocked(dapm
, "Speaker");
48 if (magician_hp_switch
)
49 snd_soc_dapm_enable_pin_unlocked(dapm
, "Headphone Jack");
51 snd_soc_dapm_disable_pin_unlocked(dapm
, "Headphone Jack");
53 switch (magician_in_sel
) {
55 snd_soc_dapm_disable_pin_unlocked(dapm
, "Headset Mic");
56 snd_soc_dapm_enable_pin_unlocked(dapm
, "Call Mic");
58 case MAGICIAN_MIC_EXT
:
59 snd_soc_dapm_disable_pin_unlocked(dapm
, "Call Mic");
60 snd_soc_dapm_enable_pin_unlocked(dapm
, "Headset Mic");
64 snd_soc_dapm_sync_unlocked(dapm
);
66 snd_soc_dapm_mutex_unlock(dapm
);
69 static int magician_startup(struct snd_pcm_substream
*substream
)
71 struct snd_soc_pcm_runtime
*rtd
= asoc_substream_to_rtd(substream
);
73 /* check the jack status at stream startup */
74 magician_ext_control(&rtd
->card
->dapm
);
80 * Magician uses SSP port for playback.
82 static int magician_playback_hw_params(struct snd_pcm_substream
*substream
,
83 struct snd_pcm_hw_params
*params
)
85 struct snd_soc_pcm_runtime
*rtd
= asoc_substream_to_rtd(substream
);
86 struct snd_soc_dai
*codec_dai
= asoc_rtd_to_codec(rtd
, 0);
87 struct snd_soc_dai
*cpu_dai
= asoc_rtd_to_cpu(rtd
, 0);
91 /* set codec DAI configuration */
92 ret
= snd_soc_dai_set_fmt(codec_dai
, SND_SOC_DAIFMT_MSB
|
93 SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS
);
97 /* set cpu DAI configuration */
98 ret
= snd_soc_dai_set_fmt(cpu_dai
, SND_SOC_DAIFMT_DSP_A
|
99 SND_SOC_DAIFMT_NB_IF
| SND_SOC_DAIFMT_CBS_CFS
);
103 width
= snd_pcm_format_physical_width(params_format(params
));
104 ret
= snd_soc_dai_set_tdm_slot(cpu_dai
, 1, 0, 1, width
);
108 /* set audio clock as clock source */
109 ret
= snd_soc_dai_set_sysclk(cpu_dai
, PXA_SSP_CLK_AUDIO
, 0,
118 * Magician uses I2S for capture.
120 static int magician_capture_hw_params(struct snd_pcm_substream
*substream
,
121 struct snd_pcm_hw_params
*params
)
123 struct snd_soc_pcm_runtime
*rtd
= asoc_substream_to_rtd(substream
);
124 struct snd_soc_dai
*codec_dai
= asoc_rtd_to_codec(rtd
, 0);
125 struct snd_soc_dai
*cpu_dai
= asoc_rtd_to_cpu(rtd
, 0);
128 /* set codec DAI configuration */
129 ret
= snd_soc_dai_set_fmt(codec_dai
,
130 SND_SOC_DAIFMT_MSB
| SND_SOC_DAIFMT_NB_NF
|
131 SND_SOC_DAIFMT_CBS_CFS
);
135 /* set cpu DAI configuration */
136 ret
= snd_soc_dai_set_fmt(cpu_dai
,
137 SND_SOC_DAIFMT_MSB
| SND_SOC_DAIFMT_NB_NF
|
138 SND_SOC_DAIFMT_CBS_CFS
);
142 /* set the I2S system clock as output */
143 ret
= snd_soc_dai_set_sysclk(cpu_dai
, PXA2XX_I2S_SYSCLK
, 0,
151 static const struct snd_soc_ops magician_capture_ops
= {
152 .startup
= magician_startup
,
153 .hw_params
= magician_capture_hw_params
,
156 static const struct snd_soc_ops magician_playback_ops
= {
157 .startup
= magician_startup
,
158 .hw_params
= magician_playback_hw_params
,
161 static int magician_get_hp(struct snd_kcontrol
*kcontrol
,
162 struct snd_ctl_elem_value
*ucontrol
)
164 ucontrol
->value
.integer
.value
[0] = magician_hp_switch
;
168 static int magician_set_hp(struct snd_kcontrol
*kcontrol
,
169 struct snd_ctl_elem_value
*ucontrol
)
171 struct snd_soc_card
*card
= snd_kcontrol_chip(kcontrol
);
173 if (magician_hp_switch
== ucontrol
->value
.integer
.value
[0])
176 magician_hp_switch
= ucontrol
->value
.integer
.value
[0];
177 magician_ext_control(&card
->dapm
);
181 static int magician_get_spk(struct snd_kcontrol
*kcontrol
,
182 struct snd_ctl_elem_value
*ucontrol
)
184 ucontrol
->value
.integer
.value
[0] = magician_spk_switch
;
188 static int magician_set_spk(struct snd_kcontrol
*kcontrol
,
189 struct snd_ctl_elem_value
*ucontrol
)
191 struct snd_soc_card
*card
= snd_kcontrol_chip(kcontrol
);
193 if (magician_spk_switch
== ucontrol
->value
.integer
.value
[0])
196 magician_spk_switch
= ucontrol
->value
.integer
.value
[0];
197 magician_ext_control(&card
->dapm
);
201 static int magician_get_input(struct snd_kcontrol
*kcontrol
,
202 struct snd_ctl_elem_value
*ucontrol
)
204 ucontrol
->value
.enumerated
.item
[0] = magician_in_sel
;
208 static int magician_set_input(struct snd_kcontrol
*kcontrol
,
209 struct snd_ctl_elem_value
*ucontrol
)
211 if (magician_in_sel
== ucontrol
->value
.enumerated
.item
[0])
214 magician_in_sel
= ucontrol
->value
.enumerated
.item
[0];
216 switch (magician_in_sel
) {
218 gpio_set_value(EGPIO_MAGICIAN_IN_SEL1
, 1);
220 case MAGICIAN_MIC_EXT
:
221 gpio_set_value(EGPIO_MAGICIAN_IN_SEL1
, 0);
227 static int magician_spk_power(struct snd_soc_dapm_widget
*w
,
228 struct snd_kcontrol
*k
, int event
)
230 gpio_set_value(EGPIO_MAGICIAN_SPK_POWER
, SND_SOC_DAPM_EVENT_ON(event
));
234 static int magician_hp_power(struct snd_soc_dapm_widget
*w
,
235 struct snd_kcontrol
*k
, int event
)
237 gpio_set_value(EGPIO_MAGICIAN_EP_POWER
, SND_SOC_DAPM_EVENT_ON(event
));
241 static int magician_mic_bias(struct snd_soc_dapm_widget
*w
,
242 struct snd_kcontrol
*k
, int event
)
244 gpio_set_value(EGPIO_MAGICIAN_MIC_POWER
, SND_SOC_DAPM_EVENT_ON(event
));
248 /* magician machine dapm widgets */
249 static const struct snd_soc_dapm_widget uda1380_dapm_widgets
[] = {
250 SND_SOC_DAPM_HP("Headphone Jack", magician_hp_power
),
251 SND_SOC_DAPM_SPK("Speaker", magician_spk_power
),
252 SND_SOC_DAPM_MIC("Call Mic", magician_mic_bias
),
253 SND_SOC_DAPM_MIC("Headset Mic", magician_mic_bias
),
256 /* magician machine audio_map */
257 static const struct snd_soc_dapm_route audio_map
[] = {
259 /* Headphone connected to VOUTL, VOUTR */
260 {"Headphone Jack", NULL
, "VOUTL"},
261 {"Headphone Jack", NULL
, "VOUTR"},
263 /* Speaker connected to VOUTL, VOUTR */
264 {"Speaker", NULL
, "VOUTL"},
265 {"Speaker", NULL
, "VOUTR"},
267 /* Mics are connected to VINM */
268 {"VINM", NULL
, "Headset Mic"},
269 {"VINM", NULL
, "Call Mic"},
272 static const char * const input_select
[] = {"Call Mic", "Headset Mic"};
273 static const struct soc_enum magician_in_sel_enum
=
274 SOC_ENUM_SINGLE_EXT(2, input_select
);
276 static const struct snd_kcontrol_new uda1380_magician_controls
[] = {
277 SOC_SINGLE_BOOL_EXT("Headphone Switch",
278 (unsigned long)&magician_hp_switch
,
279 magician_get_hp
, magician_set_hp
),
280 SOC_SINGLE_BOOL_EXT("Speaker Switch",
281 (unsigned long)&magician_spk_switch
,
282 magician_get_spk
, magician_set_spk
),
283 SOC_ENUM_EXT("Input Select", magician_in_sel_enum
,
284 magician_get_input
, magician_set_input
),
287 /* magician digital audio interface glue - connects codec <--> CPU */
288 SND_SOC_DAILINK_DEFS(playback
,
289 DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.0")),
290 DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-0018",
291 "uda1380-hifi-playback")),
292 DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
294 SND_SOC_DAILINK_DEFS(capture
,
295 DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
296 DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-0018",
297 "uda1380-hifi-capture")),
298 DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
300 static struct snd_soc_dai_link magician_dai
[] = {
303 .stream_name
= "UDA1380 Playback",
304 .ops
= &magician_playback_ops
,
305 SND_SOC_DAILINK_REG(playback
),
309 .stream_name
= "UDA1380 Capture",
310 .ops
= &magician_capture_ops
,
311 SND_SOC_DAILINK_REG(capture
),
315 /* magician audio machine driver */
316 static struct snd_soc_card snd_soc_card_magician
= {
318 .owner
= THIS_MODULE
,
319 .dai_link
= magician_dai
,
320 .num_links
= ARRAY_SIZE(magician_dai
),
322 .controls
= uda1380_magician_controls
,
323 .num_controls
= ARRAY_SIZE(uda1380_magician_controls
),
324 .dapm_widgets
= uda1380_dapm_widgets
,
325 .num_dapm_widgets
= ARRAY_SIZE(uda1380_dapm_widgets
),
326 .dapm_routes
= audio_map
,
327 .num_dapm_routes
= ARRAY_SIZE(audio_map
),
328 .fully_routed
= true,
331 static struct platform_device
*magician_snd_device
;
334 * FIXME: move into magician board file once merged into the pxa tree
336 static struct uda1380_platform_data uda1380_info
= {
337 .gpio_power
= EGPIO_MAGICIAN_CODEC_POWER
,
338 .gpio_reset
= EGPIO_MAGICIAN_CODEC_RESET
,
339 .dac_clk
= UDA1380_DAC_CLK_WSPLL
,
342 static struct i2c_board_info i2c_board_info
[] = {
344 I2C_BOARD_INFO("uda1380", 0x18),
345 .platform_data
= &uda1380_info
,
349 static int __init
magician_init(void)
352 struct i2c_adapter
*adapter
;
353 struct i2c_client
*client
;
355 if (!machine_is_magician())
358 adapter
= i2c_get_adapter(0);
361 client
= i2c_new_client_device(adapter
, i2c_board_info
);
362 i2c_put_adapter(adapter
);
364 return PTR_ERR(client
);
366 ret
= gpio_request(EGPIO_MAGICIAN_SPK_POWER
, "SPK_POWER");
368 goto err_request_spk
;
369 ret
= gpio_request(EGPIO_MAGICIAN_EP_POWER
, "EP_POWER");
372 ret
= gpio_request(EGPIO_MAGICIAN_MIC_POWER
, "MIC_POWER");
374 goto err_request_mic
;
375 ret
= gpio_request(EGPIO_MAGICIAN_IN_SEL0
, "IN_SEL0");
377 goto err_request_in_sel0
;
378 ret
= gpio_request(EGPIO_MAGICIAN_IN_SEL1
, "IN_SEL1");
380 goto err_request_in_sel1
;
382 gpio_set_value(EGPIO_MAGICIAN_IN_SEL0
, 0);
384 magician_snd_device
= platform_device_alloc("soc-audio", -1);
385 if (!magician_snd_device
) {
390 platform_set_drvdata(magician_snd_device
, &snd_soc_card_magician
);
391 ret
= platform_device_add(magician_snd_device
);
393 platform_device_put(magician_snd_device
);
400 gpio_free(EGPIO_MAGICIAN_IN_SEL1
);
402 gpio_free(EGPIO_MAGICIAN_IN_SEL0
);
404 gpio_free(EGPIO_MAGICIAN_MIC_POWER
);
406 gpio_free(EGPIO_MAGICIAN_EP_POWER
);
408 gpio_free(EGPIO_MAGICIAN_SPK_POWER
);
413 static void __exit
magician_exit(void)
415 platform_device_unregister(magician_snd_device
);
417 gpio_set_value(EGPIO_MAGICIAN_SPK_POWER
, 0);
418 gpio_set_value(EGPIO_MAGICIAN_EP_POWER
, 0);
419 gpio_set_value(EGPIO_MAGICIAN_MIC_POWER
, 0);
421 gpio_free(EGPIO_MAGICIAN_IN_SEL1
);
422 gpio_free(EGPIO_MAGICIAN_IN_SEL0
);
423 gpio_free(EGPIO_MAGICIAN_MIC_POWER
);
424 gpio_free(EGPIO_MAGICIAN_EP_POWER
);
425 gpio_free(EGPIO_MAGICIAN_SPK_POWER
);
428 module_init(magician_init
);
429 module_exit(magician_exit
);
431 MODULE_AUTHOR("Philipp Zabel");
432 MODULE_DESCRIPTION("ALSA SoC Magician");
433 MODULE_LICENSE("GPL");