18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tosa.c -- SoC audio for Tosa 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2005 Wolfson Microelectronics PLC. 68c2ecf20Sopenharmony_ci * Copyright 2005 Openedhand Ltd. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Authors: Liam Girdwood <lrg@slimlogic.co.uk> 98c2ecf20Sopenharmony_ci * Richard Purdie <richard@openedhand.com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * GPIO's 128c2ecf20Sopenharmony_ci * 1 - Jack Insertion 138c2ecf20Sopenharmony_ci * 5 - Hookswitch (headset answer/hang up switch) 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 188c2ecf20Sopenharmony_ci#include <linux/device.h> 198c2ecf20Sopenharmony_ci#include <linux/gpio.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <sound/core.h> 228c2ecf20Sopenharmony_ci#include <sound/pcm.h> 238c2ecf20Sopenharmony_ci#include <sound/soc.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/mach-types.h> 268c2ecf20Sopenharmony_ci#include <mach/tosa.h> 278c2ecf20Sopenharmony_ci#include <mach/audio.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define TOSA_HP 0 308c2ecf20Sopenharmony_ci#define TOSA_MIC_INT 1 318c2ecf20Sopenharmony_ci#define TOSA_HEADSET 2 328c2ecf20Sopenharmony_ci#define TOSA_HP_OFF 3 338c2ecf20Sopenharmony_ci#define TOSA_SPK_ON 0 348c2ecf20Sopenharmony_ci#define TOSA_SPK_OFF 1 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int tosa_jack_func; 378c2ecf20Sopenharmony_cistatic int tosa_spk_func; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic void tosa_ext_control(struct snd_soc_dapm_context *dapm) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci snd_soc_dapm_mutex_lock(dapm); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci /* set up jack connection */ 458c2ecf20Sopenharmony_ci switch (tosa_jack_func) { 468c2ecf20Sopenharmony_ci case TOSA_HP: 478c2ecf20Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Mic (Internal)"); 488c2ecf20Sopenharmony_ci snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); 498c2ecf20Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); 508c2ecf20Sopenharmony_ci break; 518c2ecf20Sopenharmony_ci case TOSA_MIC_INT: 528c2ecf20Sopenharmony_ci snd_soc_dapm_enable_pin_unlocked(dapm, "Mic (Internal)"); 538c2ecf20Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); 548c2ecf20Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci case TOSA_HEADSET: 578c2ecf20Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Mic (Internal)"); 588c2ecf20Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); 598c2ecf20Sopenharmony_ci snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack"); 608c2ecf20Sopenharmony_ci break; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (tosa_spk_func == TOSA_SPK_ON) 648c2ecf20Sopenharmony_ci snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker"); 658c2ecf20Sopenharmony_ci else 668c2ecf20Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker"); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci snd_soc_dapm_sync_unlocked(dapm); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci snd_soc_dapm_mutex_unlock(dapm); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int tosa_startup(struct snd_pcm_substream *substream) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* check the jack status at stream startup */ 788c2ecf20Sopenharmony_ci tosa_ext_control(&rtd->card->dapm); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic const struct snd_soc_ops tosa_ops = { 848c2ecf20Sopenharmony_ci .startup = tosa_startup, 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int tosa_get_jack(struct snd_kcontrol *kcontrol, 888c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = tosa_jack_func; 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int tosa_set_jack(struct snd_kcontrol *kcontrol, 958c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (tosa_jack_func == ucontrol->value.enumerated.item[0]) 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci tosa_jack_func = ucontrol->value.enumerated.item[0]; 1038c2ecf20Sopenharmony_ci tosa_ext_control(&card->dapm); 1048c2ecf20Sopenharmony_ci return 1; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int tosa_get_spk(struct snd_kcontrol *kcontrol, 1088c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = tosa_spk_func; 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int tosa_set_spk(struct snd_kcontrol *kcontrol, 1158c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (tosa_spk_func == ucontrol->value.enumerated.item[0]) 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci tosa_spk_func = ucontrol->value.enumerated.item[0]; 1238c2ecf20Sopenharmony_ci tosa_ext_control(&card->dapm); 1248c2ecf20Sopenharmony_ci return 1; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* tosa dapm event handlers */ 1288c2ecf20Sopenharmony_cistatic int tosa_hp_event(struct snd_soc_dapm_widget *w, 1298c2ecf20Sopenharmony_ci struct snd_kcontrol *k, int event) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci gpio_set_value(TOSA_GPIO_L_MUTE, SND_SOC_DAPM_EVENT_ON(event) ? 1 : 0); 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* tosa machine dapm widgets */ 1368c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget tosa_dapm_widgets[] = { 1378c2ecf20Sopenharmony_ciSND_SOC_DAPM_HP("Headphone Jack", tosa_hp_event), 1388c2ecf20Sopenharmony_ciSND_SOC_DAPM_HP("Headset Jack", NULL), 1398c2ecf20Sopenharmony_ciSND_SOC_DAPM_MIC("Mic (Internal)", NULL), 1408c2ecf20Sopenharmony_ciSND_SOC_DAPM_SPK("Speaker", NULL), 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* tosa audio map */ 1448c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route audio_map[] = { 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* headphone connected to HPOUTL, HPOUTR */ 1478c2ecf20Sopenharmony_ci {"Headphone Jack", NULL, "HPOUTL"}, 1488c2ecf20Sopenharmony_ci {"Headphone Jack", NULL, "HPOUTR"}, 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* ext speaker connected to LOUT2, ROUT2 */ 1518c2ecf20Sopenharmony_ci {"Speaker", NULL, "LOUT2"}, 1528c2ecf20Sopenharmony_ci {"Speaker", NULL, "ROUT2"}, 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* internal mic is connected to mic1, mic2 differential - with bias */ 1558c2ecf20Sopenharmony_ci {"MIC1", NULL, "Mic Bias"}, 1568c2ecf20Sopenharmony_ci {"MIC2", NULL, "Mic Bias"}, 1578c2ecf20Sopenharmony_ci {"Mic Bias", NULL, "Mic (Internal)"}, 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* headset is connected to HPOUTR, and LINEINR with bias */ 1608c2ecf20Sopenharmony_ci {"Headset Jack", NULL, "HPOUTR"}, 1618c2ecf20Sopenharmony_ci {"LINEINR", NULL, "Mic Bias"}, 1628c2ecf20Sopenharmony_ci {"Mic Bias", NULL, "Headset Jack"}, 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic const char * const jack_function[] = {"Headphone", "Mic", "Line", 1668c2ecf20Sopenharmony_ci "Headset", "Off"}; 1678c2ecf20Sopenharmony_cistatic const char * const spk_function[] = {"On", "Off"}; 1688c2ecf20Sopenharmony_cistatic const struct soc_enum tosa_enum[] = { 1698c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE_EXT(5, jack_function), 1708c2ecf20Sopenharmony_ci SOC_ENUM_SINGLE_EXT(2, spk_function), 1718c2ecf20Sopenharmony_ci}; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new tosa_controls[] = { 1748c2ecf20Sopenharmony_ci SOC_ENUM_EXT("Jack Function", tosa_enum[0], tosa_get_jack, 1758c2ecf20Sopenharmony_ci tosa_set_jack), 1768c2ecf20Sopenharmony_ci SOC_ENUM_EXT("Speaker Function", tosa_enum[1], tosa_get_spk, 1778c2ecf20Sopenharmony_ci tosa_set_spk), 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(ac97, 1818c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97")), 1828c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec", "wm9712-hifi")), 1838c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio"))); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(ac97_aux, 1868c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97-aux")), 1878c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec", "wm9712-aux")), 1888c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio"))); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link tosa_dai[] = { 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci .name = "AC97", 1938c2ecf20Sopenharmony_ci .stream_name = "AC97 HiFi", 1948c2ecf20Sopenharmony_ci .ops = &tosa_ops, 1958c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(ac97), 1968c2ecf20Sopenharmony_ci}, 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci .name = "AC97 Aux", 1998c2ecf20Sopenharmony_ci .stream_name = "AC97 Aux", 2008c2ecf20Sopenharmony_ci .ops = &tosa_ops, 2018c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(ac97_aux), 2028c2ecf20Sopenharmony_ci}, 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic struct snd_soc_card tosa = { 2068c2ecf20Sopenharmony_ci .name = "Tosa", 2078c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2088c2ecf20Sopenharmony_ci .dai_link = tosa_dai, 2098c2ecf20Sopenharmony_ci .num_links = ARRAY_SIZE(tosa_dai), 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci .controls = tosa_controls, 2128c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(tosa_controls), 2138c2ecf20Sopenharmony_ci .dapm_widgets = tosa_dapm_widgets, 2148c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(tosa_dapm_widgets), 2158c2ecf20Sopenharmony_ci .dapm_routes = audio_map, 2168c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(audio_map), 2178c2ecf20Sopenharmony_ci .fully_routed = true, 2188c2ecf20Sopenharmony_ci}; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int tosa_probe(struct platform_device *pdev) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct snd_soc_card *card = ⤩ 2238c2ecf20Sopenharmony_ci int ret; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ret = gpio_request_one(TOSA_GPIO_L_MUTE, GPIOF_OUT_INIT_LOW, 2268c2ecf20Sopenharmony_ci "Headphone Jack"); 2278c2ecf20Sopenharmony_ci if (ret) 2288c2ecf20Sopenharmony_ci return ret; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci card->dev = &pdev->dev; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_card(&pdev->dev, card); 2338c2ecf20Sopenharmony_ci if (ret) { 2348c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", 2358c2ecf20Sopenharmony_ci ret); 2368c2ecf20Sopenharmony_ci gpio_free(TOSA_GPIO_L_MUTE); 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci return ret; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int tosa_remove(struct platform_device *pdev) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci gpio_free(TOSA_GPIO_L_MUTE); 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic struct platform_driver tosa_driver = { 2488c2ecf20Sopenharmony_ci .driver = { 2498c2ecf20Sopenharmony_ci .name = "tosa-audio", 2508c2ecf20Sopenharmony_ci .pm = &snd_soc_pm_ops, 2518c2ecf20Sopenharmony_ci }, 2528c2ecf20Sopenharmony_ci .probe = tosa_probe, 2538c2ecf20Sopenharmony_ci .remove = tosa_remove, 2548c2ecf20Sopenharmony_ci}; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cimodule_platform_driver(tosa_driver); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* Module information */ 2598c2ecf20Sopenharmony_ciMODULE_AUTHOR("Richard Purdie"); 2608c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC Tosa"); 2618c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2628c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:tosa-audio"); 263