1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * SoC audio for HTC Magician 4 * 5 * Copyright (c) 2006 Philipp Zabel <philipp.zabel@gmail.com> 6 * 7 * based on spitz.c, 8 * Authors: Liam Girdwood <lrg@slimlogic.co.uk> 9 * Richard Purdie <richard@openedhand.com> 10 */ 11 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> 19 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> 25 26#include <mach/magician.h> 27#include <asm/mach-types.h> 28#include "../codecs/uda1380.h" 29#include "pxa2xx-i2s.h" 30#include "pxa-ssp.h" 31 32#define MAGICIAN_MIC 0 33#define MAGICIAN_MIC_EXT 1 34 35static int magician_hp_switch; 36static int magician_spk_switch = 1; 37static int magician_in_sel = MAGICIAN_MIC; 38 39static void magician_ext_control(struct snd_soc_dapm_context *dapm) 40{ 41 42 snd_soc_dapm_mutex_lock(dapm); 43 44 if (magician_spk_switch) 45 snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker"); 46 else 47 snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker"); 48 if (magician_hp_switch) 49 snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); 50 else 51 snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); 52 53 switch (magician_in_sel) { 54 case MAGICIAN_MIC: 55 snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Mic"); 56 snd_soc_dapm_enable_pin_unlocked(dapm, "Call Mic"); 57 break; 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"); 61 break; 62 } 63 64 snd_soc_dapm_sync_unlocked(dapm); 65 66 snd_soc_dapm_mutex_unlock(dapm); 67} 68 69static int magician_startup(struct snd_pcm_substream *substream) 70{ 71 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 72 73 /* check the jack status at stream startup */ 74 magician_ext_control(&rtd->card->dapm); 75 76 return 0; 77} 78 79/* 80 * Magician uses SSP port for playback. 81 */ 82static int magician_playback_hw_params(struct snd_pcm_substream *substream, 83 struct snd_pcm_hw_params *params) 84{ 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); 88 unsigned int width; 89 int ret = 0; 90 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); 94 if (ret < 0) 95 return ret; 96 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); 100 if (ret < 0) 101 return ret; 102 103 width = snd_pcm_format_physical_width(params_format(params)); 104 ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 0, 1, width); 105 if (ret < 0) 106 return ret; 107 108 /* set audio clock as clock source */ 109 ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 110 SND_SOC_CLOCK_OUT); 111 if (ret < 0) 112 return ret; 113 114 return 0; 115} 116 117/* 118 * Magician uses I2S for capture. 119 */ 120static int magician_capture_hw_params(struct snd_pcm_substream *substream, 121 struct snd_pcm_hw_params *params) 122{ 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); 126 int ret = 0; 127 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); 132 if (ret < 0) 133 return ret; 134 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); 139 if (ret < 0) 140 return ret; 141 142 /* set the I2S system clock as output */ 143 ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, 144 SND_SOC_CLOCK_OUT); 145 if (ret < 0) 146 return ret; 147 148 return 0; 149} 150 151static const struct snd_soc_ops magician_capture_ops = { 152 .startup = magician_startup, 153 .hw_params = magician_capture_hw_params, 154}; 155 156static const struct snd_soc_ops magician_playback_ops = { 157 .startup = magician_startup, 158 .hw_params = magician_playback_hw_params, 159}; 160 161static int magician_get_hp(struct snd_kcontrol *kcontrol, 162 struct snd_ctl_elem_value *ucontrol) 163{ 164 ucontrol->value.integer.value[0] = magician_hp_switch; 165 return 0; 166} 167 168static int magician_set_hp(struct snd_kcontrol *kcontrol, 169 struct snd_ctl_elem_value *ucontrol) 170{ 171 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 172 173 if (magician_hp_switch == ucontrol->value.integer.value[0]) 174 return 0; 175 176 magician_hp_switch = ucontrol->value.integer.value[0]; 177 magician_ext_control(&card->dapm); 178 return 1; 179} 180 181static int magician_get_spk(struct snd_kcontrol *kcontrol, 182 struct snd_ctl_elem_value *ucontrol) 183{ 184 ucontrol->value.integer.value[0] = magician_spk_switch; 185 return 0; 186} 187 188static int magician_set_spk(struct snd_kcontrol *kcontrol, 189 struct snd_ctl_elem_value *ucontrol) 190{ 191 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 192 193 if (magician_spk_switch == ucontrol->value.integer.value[0]) 194 return 0; 195 196 magician_spk_switch = ucontrol->value.integer.value[0]; 197 magician_ext_control(&card->dapm); 198 return 1; 199} 200 201static int magician_get_input(struct snd_kcontrol *kcontrol, 202 struct snd_ctl_elem_value *ucontrol) 203{ 204 ucontrol->value.enumerated.item[0] = magician_in_sel; 205 return 0; 206} 207 208static int magician_set_input(struct snd_kcontrol *kcontrol, 209 struct snd_ctl_elem_value *ucontrol) 210{ 211 if (magician_in_sel == ucontrol->value.enumerated.item[0]) 212 return 0; 213 214 magician_in_sel = ucontrol->value.enumerated.item[0]; 215 216 switch (magician_in_sel) { 217 case MAGICIAN_MIC: 218 gpio_set_value(EGPIO_MAGICIAN_IN_SEL1, 1); 219 break; 220 case MAGICIAN_MIC_EXT: 221 gpio_set_value(EGPIO_MAGICIAN_IN_SEL1, 0); 222 } 223 224 return 1; 225} 226 227static int magician_spk_power(struct snd_soc_dapm_widget *w, 228 struct snd_kcontrol *k, int event) 229{ 230 gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, SND_SOC_DAPM_EVENT_ON(event)); 231 return 0; 232} 233 234static int magician_hp_power(struct snd_soc_dapm_widget *w, 235 struct snd_kcontrol *k, int event) 236{ 237 gpio_set_value(EGPIO_MAGICIAN_EP_POWER, SND_SOC_DAPM_EVENT_ON(event)); 238 return 0; 239} 240 241static int magician_mic_bias(struct snd_soc_dapm_widget *w, 242 struct snd_kcontrol *k, int event) 243{ 244 gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, SND_SOC_DAPM_EVENT_ON(event)); 245 return 0; 246} 247 248/* magician machine dapm widgets */ 249static 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), 254}; 255 256/* magician machine audio_map */ 257static const struct snd_soc_dapm_route audio_map[] = { 258 259 /* Headphone connected to VOUTL, VOUTR */ 260 {"Headphone Jack", NULL, "VOUTL"}, 261 {"Headphone Jack", NULL, "VOUTR"}, 262 263 /* Speaker connected to VOUTL, VOUTR */ 264 {"Speaker", NULL, "VOUTL"}, 265 {"Speaker", NULL, "VOUTR"}, 266 267 /* Mics are connected to VINM */ 268 {"VINM", NULL, "Headset Mic"}, 269 {"VINM", NULL, "Call Mic"}, 270}; 271 272static const char * const input_select[] = {"Call Mic", "Headset Mic"}; 273static const struct soc_enum magician_in_sel_enum = 274 SOC_ENUM_SINGLE_EXT(2, input_select); 275 276static 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), 285}; 286 287/* magician digital audio interface glue - connects codec <--> CPU */ 288SND_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"))); 293 294SND_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"))); 299 300static struct snd_soc_dai_link magician_dai[] = { 301{ 302 .name = "uda1380", 303 .stream_name = "UDA1380 Playback", 304 .ops = &magician_playback_ops, 305 SND_SOC_DAILINK_REG(playback), 306}, 307{ 308 .name = "uda1380", 309 .stream_name = "UDA1380 Capture", 310 .ops = &magician_capture_ops, 311 SND_SOC_DAILINK_REG(capture), 312} 313}; 314 315/* magician audio machine driver */ 316static struct snd_soc_card snd_soc_card_magician = { 317 .name = "Magician", 318 .owner = THIS_MODULE, 319 .dai_link = magician_dai, 320 .num_links = ARRAY_SIZE(magician_dai), 321 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, 329}; 330 331static struct platform_device *magician_snd_device; 332 333/* 334 * FIXME: move into magician board file once merged into the pxa tree 335 */ 336static 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, 340}; 341 342static struct i2c_board_info i2c_board_info[] = { 343 { 344 I2C_BOARD_INFO("uda1380", 0x18), 345 .platform_data = &uda1380_info, 346 }, 347}; 348 349static int __init magician_init(void) 350{ 351 int ret; 352 struct i2c_adapter *adapter; 353 struct i2c_client *client; 354 355 if (!machine_is_magician()) 356 return -ENODEV; 357 358 adapter = i2c_get_adapter(0); 359 if (!adapter) 360 return -ENODEV; 361 client = i2c_new_client_device(adapter, i2c_board_info); 362 i2c_put_adapter(adapter); 363 if (IS_ERR(client)) 364 return PTR_ERR(client); 365 366 ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER"); 367 if (ret) 368 goto err_request_spk; 369 ret = gpio_request(EGPIO_MAGICIAN_EP_POWER, "EP_POWER"); 370 if (ret) 371 goto err_request_ep; 372 ret = gpio_request(EGPIO_MAGICIAN_MIC_POWER, "MIC_POWER"); 373 if (ret) 374 goto err_request_mic; 375 ret = gpio_request(EGPIO_MAGICIAN_IN_SEL0, "IN_SEL0"); 376 if (ret) 377 goto err_request_in_sel0; 378 ret = gpio_request(EGPIO_MAGICIAN_IN_SEL1, "IN_SEL1"); 379 if (ret) 380 goto err_request_in_sel1; 381 382 gpio_set_value(EGPIO_MAGICIAN_IN_SEL0, 0); 383 384 magician_snd_device = platform_device_alloc("soc-audio", -1); 385 if (!magician_snd_device) { 386 ret = -ENOMEM; 387 goto err_pdev; 388 } 389 390 platform_set_drvdata(magician_snd_device, &snd_soc_card_magician); 391 ret = platform_device_add(magician_snd_device); 392 if (ret) { 393 platform_device_put(magician_snd_device); 394 goto err_pdev; 395 } 396 397 return 0; 398 399err_pdev: 400 gpio_free(EGPIO_MAGICIAN_IN_SEL1); 401err_request_in_sel1: 402 gpio_free(EGPIO_MAGICIAN_IN_SEL0); 403err_request_in_sel0: 404 gpio_free(EGPIO_MAGICIAN_MIC_POWER); 405err_request_mic: 406 gpio_free(EGPIO_MAGICIAN_EP_POWER); 407err_request_ep: 408 gpio_free(EGPIO_MAGICIAN_SPK_POWER); 409err_request_spk: 410 return ret; 411} 412 413static void __exit magician_exit(void) 414{ 415 platform_device_unregister(magician_snd_device); 416 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); 420 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); 426} 427 428module_init(magician_init); 429module_exit(magician_exit); 430 431MODULE_AUTHOR("Philipp Zabel"); 432MODULE_DESCRIPTION("ALSA SoC Magician"); 433MODULE_LICENSE("GPL"); 434