18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SoC audio for HTC Magician 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2006 Philipp Zabel <philipp.zabel@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * based on spitz.c, 88c2ecf20Sopenharmony_ci * Authors: Liam Girdwood <lrg@slimlogic.co.uk> 98c2ecf20Sopenharmony_ci * Richard Purdie <richard@openedhand.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/timer.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/gpio.h> 188c2ecf20Sopenharmony_ci#include <linux/i2c.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <sound/core.h> 218c2ecf20Sopenharmony_ci#include <sound/pcm.h> 228c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 238c2ecf20Sopenharmony_ci#include <sound/soc.h> 248c2ecf20Sopenharmony_ci#include <sound/uda1380.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <mach/magician.h> 278c2ecf20Sopenharmony_ci#include <asm/mach-types.h> 288c2ecf20Sopenharmony_ci#include "../codecs/uda1380.h" 298c2ecf20Sopenharmony_ci#include "pxa2xx-i2s.h" 308c2ecf20Sopenharmony_ci#include "pxa-ssp.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define MAGICIAN_MIC 0 338c2ecf20Sopenharmony_ci#define MAGICIAN_MIC_EXT 1 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic int magician_hp_switch; 368c2ecf20Sopenharmony_cistatic int magician_spk_switch = 1; 378c2ecf20Sopenharmony_cistatic int magician_in_sel = MAGICIAN_MIC; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic void magician_ext_control(struct snd_soc_dapm_context *dapm) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci snd_soc_dapm_mutex_lock(dapm); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (magician_spk_switch) 458c2ecf20Sopenharmony_ci snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker"); 468c2ecf20Sopenharmony_ci else 478c2ecf20Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker"); 488c2ecf20Sopenharmony_ci if (magician_hp_switch) 498c2ecf20Sopenharmony_ci snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); 508c2ecf20Sopenharmony_ci else 518c2ecf20Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci switch (magician_in_sel) { 548c2ecf20Sopenharmony_ci case MAGICIAN_MIC: 558c2ecf20Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Mic"); 568c2ecf20Sopenharmony_ci snd_soc_dapm_enable_pin_unlocked(dapm, "Call Mic"); 578c2ecf20Sopenharmony_ci break; 588c2ecf20Sopenharmony_ci case MAGICIAN_MIC_EXT: 598c2ecf20Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Call Mic"); 608c2ecf20Sopenharmony_ci snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Mic"); 618c2ecf20Sopenharmony_ci break; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci snd_soc_dapm_sync_unlocked(dapm); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci snd_soc_dapm_mutex_unlock(dapm); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int magician_startup(struct snd_pcm_substream *substream) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* check the jack status at stream startup */ 748c2ecf20Sopenharmony_ci magician_ext_control(&rtd->card->dapm); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return 0; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* 808c2ecf20Sopenharmony_ci * Magician uses SSP port for playback. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_cistatic int magician_playback_hw_params(struct snd_pcm_substream *substream, 838c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 868c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 878c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 888c2ecf20Sopenharmony_ci unsigned int width; 898c2ecf20Sopenharmony_ci int ret = 0; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* set codec DAI configuration */ 928c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_MSB | 938c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); 948c2ecf20Sopenharmony_ci if (ret < 0) 958c2ecf20Sopenharmony_ci return ret; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* set cpu DAI configuration */ 988c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | 998c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_CBS_CFS); 1008c2ecf20Sopenharmony_ci if (ret < 0) 1018c2ecf20Sopenharmony_ci return ret; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci width = snd_pcm_format_physical_width(params_format(params)); 1048c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 0, 1, width); 1058c2ecf20Sopenharmony_ci if (ret < 0) 1068c2ecf20Sopenharmony_ci return ret; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* set audio clock as clock source */ 1098c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1108c2ecf20Sopenharmony_ci SND_SOC_CLOCK_OUT); 1118c2ecf20Sopenharmony_ci if (ret < 0) 1128c2ecf20Sopenharmony_ci return ret; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* 1188c2ecf20Sopenharmony_ci * Magician uses I2S for capture. 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_cistatic int magician_capture_hw_params(struct snd_pcm_substream *substream, 1218c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1248c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 1258c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 1268c2ecf20Sopenharmony_ci int ret = 0; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* set codec DAI configuration */ 1298c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_fmt(codec_dai, 1308c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF | 1318c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CBS_CFS); 1328c2ecf20Sopenharmony_ci if (ret < 0) 1338c2ecf20Sopenharmony_ci return ret; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* set cpu DAI configuration */ 1368c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_fmt(cpu_dai, 1378c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF | 1388c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CBS_CFS); 1398c2ecf20Sopenharmony_ci if (ret < 0) 1408c2ecf20Sopenharmony_ci return ret; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* set the I2S system clock as output */ 1438c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, 1448c2ecf20Sopenharmony_ci SND_SOC_CLOCK_OUT); 1458c2ecf20Sopenharmony_ci if (ret < 0) 1468c2ecf20Sopenharmony_ci return ret; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic const struct snd_soc_ops magician_capture_ops = { 1528c2ecf20Sopenharmony_ci .startup = magician_startup, 1538c2ecf20Sopenharmony_ci .hw_params = magician_capture_hw_params, 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic const struct snd_soc_ops magician_playback_ops = { 1578c2ecf20Sopenharmony_ci .startup = magician_startup, 1588c2ecf20Sopenharmony_ci .hw_params = magician_playback_hw_params, 1598c2ecf20Sopenharmony_ci}; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int magician_get_hp(struct snd_kcontrol *kcontrol, 1628c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = magician_hp_switch; 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int magician_set_hp(struct snd_kcontrol *kcontrol, 1698c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (magician_hp_switch == ucontrol->value.integer.value[0]) 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci magician_hp_switch = ucontrol->value.integer.value[0]; 1778c2ecf20Sopenharmony_ci magician_ext_control(&card->dapm); 1788c2ecf20Sopenharmony_ci return 1; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int magician_get_spk(struct snd_kcontrol *kcontrol, 1828c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = magician_spk_switch; 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int magician_set_spk(struct snd_kcontrol *kcontrol, 1898c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (magician_spk_switch == ucontrol->value.integer.value[0]) 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci magician_spk_switch = ucontrol->value.integer.value[0]; 1978c2ecf20Sopenharmony_ci magician_ext_control(&card->dapm); 1988c2ecf20Sopenharmony_ci return 1; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int magician_get_input(struct snd_kcontrol *kcontrol, 2028c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = magician_in_sel; 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int magician_set_input(struct snd_kcontrol *kcontrol, 2098c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci if (magician_in_sel == ucontrol->value.enumerated.item[0]) 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci magician_in_sel = ucontrol->value.enumerated.item[0]; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci switch (magician_in_sel) { 2178c2ecf20Sopenharmony_ci case MAGICIAN_MIC: 2188c2ecf20Sopenharmony_ci gpio_set_value(EGPIO_MAGICIAN_IN_SEL1, 1); 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci case MAGICIAN_MIC_EXT: 2218c2ecf20Sopenharmony_ci gpio_set_value(EGPIO_MAGICIAN_IN_SEL1, 0); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return 1; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int magician_spk_power(struct snd_soc_dapm_widget *w, 2288c2ecf20Sopenharmony_ci struct snd_kcontrol *k, int event) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, SND_SOC_DAPM_EVENT_ON(event)); 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int magician_hp_power(struct snd_soc_dapm_widget *w, 2358c2ecf20Sopenharmony_ci struct snd_kcontrol *k, int event) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci gpio_set_value(EGPIO_MAGICIAN_EP_POWER, SND_SOC_DAPM_EVENT_ON(event)); 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int magician_mic_bias(struct snd_soc_dapm_widget *w, 2428c2ecf20Sopenharmony_ci struct snd_kcontrol *k, int event) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, SND_SOC_DAPM_EVENT_ON(event)); 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* magician machine dapm widgets */ 2498c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = { 2508c2ecf20Sopenharmony_ci SND_SOC_DAPM_HP("Headphone Jack", magician_hp_power), 2518c2ecf20Sopenharmony_ci SND_SOC_DAPM_SPK("Speaker", magician_spk_power), 2528c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("Call Mic", magician_mic_bias), 2538c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("Headset Mic", magician_mic_bias), 2548c2ecf20Sopenharmony_ci}; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/* magician machine audio_map */ 2578c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route audio_map[] = { 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* Headphone connected to VOUTL, VOUTR */ 2608c2ecf20Sopenharmony_ci {"Headphone Jack", NULL, "VOUTL"}, 2618c2ecf20Sopenharmony_ci {"Headphone Jack", NULL, "VOUTR"}, 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* Speaker connected to VOUTL, VOUTR */ 2648c2ecf20Sopenharmony_ci {"Speaker", NULL, "VOUTL"}, 2658c2ecf20Sopenharmony_ci {"Speaker", NULL, "VOUTR"}, 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* Mics are connected to VINM */ 2688c2ecf20Sopenharmony_ci {"VINM", NULL, "Headset Mic"}, 2698c2ecf20Sopenharmony_ci {"VINM", NULL, "Call Mic"}, 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic const char * const input_select[] = {"Call Mic", "Headset Mic"}; 2738c2ecf20Sopenharmony_cistatic const struct soc_enum magician_in_sel_enum = 2748c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE_EXT(2, input_select); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new uda1380_magician_controls[] = { 2778c2ecf20Sopenharmony_ci SOC_SINGLE_BOOL_EXT("Headphone Switch", 2788c2ecf20Sopenharmony_ci (unsigned long)&magician_hp_switch, 2798c2ecf20Sopenharmony_ci magician_get_hp, magician_set_hp), 2808c2ecf20Sopenharmony_ci SOC_SINGLE_BOOL_EXT("Speaker Switch", 2818c2ecf20Sopenharmony_ci (unsigned long)&magician_spk_switch, 2828c2ecf20Sopenharmony_ci magician_get_spk, magician_set_spk), 2838c2ecf20Sopenharmony_ci SOC_ENUM_EXT("Input Select", magician_in_sel_enum, 2848c2ecf20Sopenharmony_ci magician_get_input, magician_set_input), 2858c2ecf20Sopenharmony_ci}; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/* magician digital audio interface glue - connects codec <--> CPU */ 2888c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(playback, 2898c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.0")), 2908c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-0018", 2918c2ecf20Sopenharmony_ci "uda1380-hifi-playback")), 2928c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio"))); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(capture, 2958c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")), 2968c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-0018", 2978c2ecf20Sopenharmony_ci "uda1380-hifi-capture")), 2988c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio"))); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link magician_dai[] = { 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci .name = "uda1380", 3038c2ecf20Sopenharmony_ci .stream_name = "UDA1380 Playback", 3048c2ecf20Sopenharmony_ci .ops = &magician_playback_ops, 3058c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(playback), 3068c2ecf20Sopenharmony_ci}, 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci .name = "uda1380", 3098c2ecf20Sopenharmony_ci .stream_name = "UDA1380 Capture", 3108c2ecf20Sopenharmony_ci .ops = &magician_capture_ops, 3118c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(capture), 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci}; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/* magician audio machine driver */ 3168c2ecf20Sopenharmony_cistatic struct snd_soc_card snd_soc_card_magician = { 3178c2ecf20Sopenharmony_ci .name = "Magician", 3188c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3198c2ecf20Sopenharmony_ci .dai_link = magician_dai, 3208c2ecf20Sopenharmony_ci .num_links = ARRAY_SIZE(magician_dai), 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci .controls = uda1380_magician_controls, 3238c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(uda1380_magician_controls), 3248c2ecf20Sopenharmony_ci .dapm_widgets = uda1380_dapm_widgets, 3258c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets), 3268c2ecf20Sopenharmony_ci .dapm_routes = audio_map, 3278c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(audio_map), 3288c2ecf20Sopenharmony_ci .fully_routed = true, 3298c2ecf20Sopenharmony_ci}; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic struct platform_device *magician_snd_device; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* 3348c2ecf20Sopenharmony_ci * FIXME: move into magician board file once merged into the pxa tree 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_cistatic struct uda1380_platform_data uda1380_info = { 3378c2ecf20Sopenharmony_ci .gpio_power = EGPIO_MAGICIAN_CODEC_POWER, 3388c2ecf20Sopenharmony_ci .gpio_reset = EGPIO_MAGICIAN_CODEC_RESET, 3398c2ecf20Sopenharmony_ci .dac_clk = UDA1380_DAC_CLK_WSPLL, 3408c2ecf20Sopenharmony_ci}; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic struct i2c_board_info i2c_board_info[] = { 3438c2ecf20Sopenharmony_ci { 3448c2ecf20Sopenharmony_ci I2C_BOARD_INFO("uda1380", 0x18), 3458c2ecf20Sopenharmony_ci .platform_data = &uda1380_info, 3468c2ecf20Sopenharmony_ci }, 3478c2ecf20Sopenharmony_ci}; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int __init magician_init(void) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci int ret; 3528c2ecf20Sopenharmony_ci struct i2c_adapter *adapter; 3538c2ecf20Sopenharmony_ci struct i2c_client *client; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (!machine_is_magician()) 3568c2ecf20Sopenharmony_ci return -ENODEV; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci adapter = i2c_get_adapter(0); 3598c2ecf20Sopenharmony_ci if (!adapter) 3608c2ecf20Sopenharmony_ci return -ENODEV; 3618c2ecf20Sopenharmony_ci client = i2c_new_client_device(adapter, i2c_board_info); 3628c2ecf20Sopenharmony_ci i2c_put_adapter(adapter); 3638c2ecf20Sopenharmony_ci if (IS_ERR(client)) 3648c2ecf20Sopenharmony_ci return PTR_ERR(client); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER"); 3678c2ecf20Sopenharmony_ci if (ret) 3688c2ecf20Sopenharmony_ci goto err_request_spk; 3698c2ecf20Sopenharmony_ci ret = gpio_request(EGPIO_MAGICIAN_EP_POWER, "EP_POWER"); 3708c2ecf20Sopenharmony_ci if (ret) 3718c2ecf20Sopenharmony_ci goto err_request_ep; 3728c2ecf20Sopenharmony_ci ret = gpio_request(EGPIO_MAGICIAN_MIC_POWER, "MIC_POWER"); 3738c2ecf20Sopenharmony_ci if (ret) 3748c2ecf20Sopenharmony_ci goto err_request_mic; 3758c2ecf20Sopenharmony_ci ret = gpio_request(EGPIO_MAGICIAN_IN_SEL0, "IN_SEL0"); 3768c2ecf20Sopenharmony_ci if (ret) 3778c2ecf20Sopenharmony_ci goto err_request_in_sel0; 3788c2ecf20Sopenharmony_ci ret = gpio_request(EGPIO_MAGICIAN_IN_SEL1, "IN_SEL1"); 3798c2ecf20Sopenharmony_ci if (ret) 3808c2ecf20Sopenharmony_ci goto err_request_in_sel1; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci gpio_set_value(EGPIO_MAGICIAN_IN_SEL0, 0); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci magician_snd_device = platform_device_alloc("soc-audio", -1); 3858c2ecf20Sopenharmony_ci if (!magician_snd_device) { 3868c2ecf20Sopenharmony_ci ret = -ENOMEM; 3878c2ecf20Sopenharmony_ci goto err_pdev; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci platform_set_drvdata(magician_snd_device, &snd_soc_card_magician); 3918c2ecf20Sopenharmony_ci ret = platform_device_add(magician_snd_device); 3928c2ecf20Sopenharmony_ci if (ret) { 3938c2ecf20Sopenharmony_ci platform_device_put(magician_snd_device); 3948c2ecf20Sopenharmony_ci goto err_pdev; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cierr_pdev: 4008c2ecf20Sopenharmony_ci gpio_free(EGPIO_MAGICIAN_IN_SEL1); 4018c2ecf20Sopenharmony_cierr_request_in_sel1: 4028c2ecf20Sopenharmony_ci gpio_free(EGPIO_MAGICIAN_IN_SEL0); 4038c2ecf20Sopenharmony_cierr_request_in_sel0: 4048c2ecf20Sopenharmony_ci gpio_free(EGPIO_MAGICIAN_MIC_POWER); 4058c2ecf20Sopenharmony_cierr_request_mic: 4068c2ecf20Sopenharmony_ci gpio_free(EGPIO_MAGICIAN_EP_POWER); 4078c2ecf20Sopenharmony_cierr_request_ep: 4088c2ecf20Sopenharmony_ci gpio_free(EGPIO_MAGICIAN_SPK_POWER); 4098c2ecf20Sopenharmony_cierr_request_spk: 4108c2ecf20Sopenharmony_ci return ret; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic void __exit magician_exit(void) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci platform_device_unregister(magician_snd_device); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, 0); 4188c2ecf20Sopenharmony_ci gpio_set_value(EGPIO_MAGICIAN_EP_POWER, 0); 4198c2ecf20Sopenharmony_ci gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, 0); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci gpio_free(EGPIO_MAGICIAN_IN_SEL1); 4228c2ecf20Sopenharmony_ci gpio_free(EGPIO_MAGICIAN_IN_SEL0); 4238c2ecf20Sopenharmony_ci gpio_free(EGPIO_MAGICIAN_MIC_POWER); 4248c2ecf20Sopenharmony_ci gpio_free(EGPIO_MAGICIAN_EP_POWER); 4258c2ecf20Sopenharmony_ci gpio_free(EGPIO_MAGICIAN_SPK_POWER); 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cimodule_init(magician_init); 4298c2ecf20Sopenharmony_cimodule_exit(magician_exit); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Philipp Zabel"); 4328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC Magician"); 4338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 434