162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * spitz.c -- SoC audio for Sharp SL-Cxx00 models Spitz, Borzoi and Akita 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2005 Wolfson Microelectronics PLC. 662306a36Sopenharmony_ci * Copyright 2005 Openedhand Ltd. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Authors: Liam Girdwood <lrg@slimlogic.co.uk> 962306a36Sopenharmony_ci * Richard Purdie <richard@openedhand.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/moduleparam.h> 1462306a36Sopenharmony_ci#include <linux/timer.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1862306a36Sopenharmony_ci#include <sound/core.h> 1962306a36Sopenharmony_ci#include <sound/pcm.h> 2062306a36Sopenharmony_ci#include <sound/soc.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <asm/mach-types.h> 2362306a36Sopenharmony_ci#include "../codecs/wm8750.h" 2462306a36Sopenharmony_ci#include "pxa2xx-i2s.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define SPITZ_HP 0 2762306a36Sopenharmony_ci#define SPITZ_MIC 1 2862306a36Sopenharmony_ci#define SPITZ_LINE 2 2962306a36Sopenharmony_ci#define SPITZ_HEADSET 3 3062306a36Sopenharmony_ci#define SPITZ_HP_OFF 4 3162306a36Sopenharmony_ci#define SPITZ_SPK_ON 0 3262306a36Sopenharmony_ci#define SPITZ_SPK_OFF 1 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci /* audio clock in Hz - rounded from 12.235MHz */ 3562306a36Sopenharmony_ci#define SPITZ_AUDIO_CLOCK 12288000 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int spitz_jack_func; 3862306a36Sopenharmony_cistatic int spitz_spk_func; 3962306a36Sopenharmony_cistatic struct gpio_desc *gpiod_mic, *gpiod_mute_l, *gpiod_mute_r; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void spitz_ext_control(struct snd_soc_dapm_context *dapm) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci snd_soc_dapm_mutex_lock(dapm); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (spitz_spk_func == SPITZ_SPK_ON) 4662306a36Sopenharmony_ci snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk"); 4762306a36Sopenharmony_ci else 4862306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk"); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* set up jack connection */ 5162306a36Sopenharmony_ci switch (spitz_jack_func) { 5262306a36Sopenharmony_ci case SPITZ_HP: 5362306a36Sopenharmony_ci /* enable and unmute hp jack, disable mic bias */ 5462306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); 5562306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack"); 5662306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack"); 5762306a36Sopenharmony_ci snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); 5862306a36Sopenharmony_ci gpiod_set_value(gpiod_mute_l, 1); 5962306a36Sopenharmony_ci gpiod_set_value(gpiod_mute_r, 1); 6062306a36Sopenharmony_ci break; 6162306a36Sopenharmony_ci case SPITZ_MIC: 6262306a36Sopenharmony_ci /* enable mic jack and bias, mute hp */ 6362306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); 6462306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); 6562306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack"); 6662306a36Sopenharmony_ci snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack"); 6762306a36Sopenharmony_ci gpiod_set_value(gpiod_mute_l, 0); 6862306a36Sopenharmony_ci gpiod_set_value(gpiod_mute_r, 0); 6962306a36Sopenharmony_ci break; 7062306a36Sopenharmony_ci case SPITZ_LINE: 7162306a36Sopenharmony_ci /* enable line jack, disable mic bias and mute hp */ 7262306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); 7362306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); 7462306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack"); 7562306a36Sopenharmony_ci snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack"); 7662306a36Sopenharmony_ci gpiod_set_value(gpiod_mute_l, 0); 7762306a36Sopenharmony_ci gpiod_set_value(gpiod_mute_r, 0); 7862306a36Sopenharmony_ci break; 7962306a36Sopenharmony_ci case SPITZ_HEADSET: 8062306a36Sopenharmony_ci /* enable and unmute headset jack enable mic bias, mute L hp */ 8162306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); 8262306a36Sopenharmony_ci snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack"); 8362306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack"); 8462306a36Sopenharmony_ci snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack"); 8562306a36Sopenharmony_ci gpiod_set_value(gpiod_mute_l, 0); 8662306a36Sopenharmony_ci gpiod_set_value(gpiod_mute_r, 1); 8762306a36Sopenharmony_ci break; 8862306a36Sopenharmony_ci case SPITZ_HP_OFF: 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* jack removed, everything off */ 9162306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); 9262306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); 9362306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack"); 9462306a36Sopenharmony_ci snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack"); 9562306a36Sopenharmony_ci gpiod_set_value(gpiod_mute_l, 0); 9662306a36Sopenharmony_ci gpiod_set_value(gpiod_mute_r, 0); 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci snd_soc_dapm_sync_unlocked(dapm); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci snd_soc_dapm_mutex_unlock(dapm); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int spitz_startup(struct snd_pcm_substream *substream) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* check the jack status at stream startup */ 11062306a36Sopenharmony_ci spitz_ext_control(&rtd->card->dapm); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int spitz_hw_params(struct snd_pcm_substream *substream, 11662306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 11962306a36Sopenharmony_ci struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 12062306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 12162306a36Sopenharmony_ci unsigned int clk = 0; 12262306a36Sopenharmony_ci int ret = 0; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci switch (params_rate(params)) { 12562306a36Sopenharmony_ci case 8000: 12662306a36Sopenharmony_ci case 16000: 12762306a36Sopenharmony_ci case 48000: 12862306a36Sopenharmony_ci case 96000: 12962306a36Sopenharmony_ci clk = 12288000; 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci case 11025: 13262306a36Sopenharmony_ci case 22050: 13362306a36Sopenharmony_ci case 44100: 13462306a36Sopenharmony_ci clk = 11289600; 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* set the codec system clock for DAC and ADC */ 13962306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, 14062306a36Sopenharmony_ci SND_SOC_CLOCK_IN); 14162306a36Sopenharmony_ci if (ret < 0) 14262306a36Sopenharmony_ci return ret; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* set the I2S system clock as input (unused) */ 14562306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, 14662306a36Sopenharmony_ci SND_SOC_CLOCK_IN); 14762306a36Sopenharmony_ci if (ret < 0) 14862306a36Sopenharmony_ci return ret; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic const struct snd_soc_ops spitz_ops = { 15462306a36Sopenharmony_ci .startup = spitz_startup, 15562306a36Sopenharmony_ci .hw_params = spitz_hw_params, 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int spitz_get_jack(struct snd_kcontrol *kcontrol, 15962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = spitz_jack_func; 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int spitz_set_jack(struct snd_kcontrol *kcontrol, 16662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (spitz_jack_func == ucontrol->value.enumerated.item[0]) 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci spitz_jack_func = ucontrol->value.enumerated.item[0]; 17462306a36Sopenharmony_ci spitz_ext_control(&card->dapm); 17562306a36Sopenharmony_ci return 1; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic int spitz_get_spk(struct snd_kcontrol *kcontrol, 17962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = spitz_spk_func; 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int spitz_set_spk(struct snd_kcontrol *kcontrol, 18662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (spitz_spk_func == ucontrol->value.enumerated.item[0]) 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci spitz_spk_func = ucontrol->value.enumerated.item[0]; 19462306a36Sopenharmony_ci spitz_ext_control(&card->dapm); 19562306a36Sopenharmony_ci return 1; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int spitz_mic_bias(struct snd_soc_dapm_widget *w, 19962306a36Sopenharmony_ci struct snd_kcontrol *k, int event) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci gpiod_set_value_cansleep(gpiod_mic, SND_SOC_DAPM_EVENT_ON(event)); 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/* spitz machine dapm widgets */ 20662306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { 20762306a36Sopenharmony_ci SND_SOC_DAPM_HP("Headphone Jack", NULL), 20862306a36Sopenharmony_ci SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias), 20962306a36Sopenharmony_ci SND_SOC_DAPM_SPK("Ext Spk", NULL), 21062306a36Sopenharmony_ci SND_SOC_DAPM_LINE("Line Jack", NULL), 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* headset is a mic and mono headphone */ 21362306a36Sopenharmony_ci SND_SOC_DAPM_HP("Headset Jack", NULL), 21462306a36Sopenharmony_ci}; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/* Spitz machine audio_map */ 21762306a36Sopenharmony_cistatic const struct snd_soc_dapm_route spitz_audio_map[] = { 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* headphone connected to LOUT1, ROUT1 */ 22062306a36Sopenharmony_ci {"Headphone Jack", NULL, "LOUT1"}, 22162306a36Sopenharmony_ci {"Headphone Jack", NULL, "ROUT1"}, 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* headset connected to ROUT1 and LINPUT1 with bias (def below) */ 22462306a36Sopenharmony_ci {"Headset Jack", NULL, "ROUT1"}, 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* ext speaker connected to LOUT2, ROUT2 */ 22762306a36Sopenharmony_ci {"Ext Spk", NULL, "ROUT2"}, 22862306a36Sopenharmony_ci {"Ext Spk", NULL, "LOUT2"}, 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* mic is connected to input 1 - with bias */ 23162306a36Sopenharmony_ci {"LINPUT1", NULL, "Mic Bias"}, 23262306a36Sopenharmony_ci {"Mic Bias", NULL, "Mic Jack"}, 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* line is connected to input 1 - no bias */ 23562306a36Sopenharmony_ci {"LINPUT1", NULL, "Line Jack"}, 23662306a36Sopenharmony_ci}; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic const char * const jack_function[] = {"Headphone", "Mic", "Line", 23962306a36Sopenharmony_ci "Headset", "Off"}; 24062306a36Sopenharmony_cistatic const char * const spk_function[] = {"On", "Off"}; 24162306a36Sopenharmony_cistatic const struct soc_enum spitz_enum[] = { 24262306a36Sopenharmony_ci SOC_ENUM_SINGLE_EXT(5, jack_function), 24362306a36Sopenharmony_ci SOC_ENUM_SINGLE_EXT(2, spk_function), 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic const struct snd_kcontrol_new wm8750_spitz_controls[] = { 24762306a36Sopenharmony_ci SOC_ENUM_EXT("Jack Function", spitz_enum[0], spitz_get_jack, 24862306a36Sopenharmony_ci spitz_set_jack), 24962306a36Sopenharmony_ci SOC_ENUM_EXT("Speaker Function", spitz_enum[1], spitz_get_spk, 25062306a36Sopenharmony_ci spitz_set_spk), 25162306a36Sopenharmony_ci}; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/* spitz digital audio interface glue - connects codec <--> CPU */ 25462306a36Sopenharmony_ciSND_SOC_DAILINK_DEFS(wm8750, 25562306a36Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")), 25662306a36Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC("wm8750.0-001b", "wm8750-hifi")), 25762306a36Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio"))); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic struct snd_soc_dai_link spitz_dai = { 26062306a36Sopenharmony_ci .name = "wm8750", 26162306a36Sopenharmony_ci .stream_name = "WM8750", 26262306a36Sopenharmony_ci .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 26362306a36Sopenharmony_ci SND_SOC_DAIFMT_CBS_CFS, 26462306a36Sopenharmony_ci .ops = &spitz_ops, 26562306a36Sopenharmony_ci SND_SOC_DAILINK_REG(wm8750), 26662306a36Sopenharmony_ci}; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* spitz audio machine driver */ 26962306a36Sopenharmony_cistatic struct snd_soc_card snd_soc_spitz = { 27062306a36Sopenharmony_ci .name = "Spitz", 27162306a36Sopenharmony_ci .owner = THIS_MODULE, 27262306a36Sopenharmony_ci .dai_link = &spitz_dai, 27362306a36Sopenharmony_ci .num_links = 1, 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci .controls = wm8750_spitz_controls, 27662306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(wm8750_spitz_controls), 27762306a36Sopenharmony_ci .dapm_widgets = wm8750_dapm_widgets, 27862306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets), 27962306a36Sopenharmony_ci .dapm_routes = spitz_audio_map, 28062306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(spitz_audio_map), 28162306a36Sopenharmony_ci .fully_routed = true, 28262306a36Sopenharmony_ci}; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic int spitz_probe(struct platform_device *pdev) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct snd_soc_card *card = &snd_soc_spitz; 28762306a36Sopenharmony_ci int ret; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci gpiod_mic = devm_gpiod_get(&pdev->dev, "mic", GPIOD_OUT_LOW); 29062306a36Sopenharmony_ci if (IS_ERR(gpiod_mic)) 29162306a36Sopenharmony_ci return PTR_ERR(gpiod_mic); 29262306a36Sopenharmony_ci gpiod_mute_l = devm_gpiod_get(&pdev->dev, "mute-l", GPIOD_OUT_LOW); 29362306a36Sopenharmony_ci if (IS_ERR(gpiod_mute_l)) 29462306a36Sopenharmony_ci return PTR_ERR(gpiod_mute_l); 29562306a36Sopenharmony_ci gpiod_mute_r = devm_gpiod_get(&pdev->dev, "mute-r", GPIOD_OUT_LOW); 29662306a36Sopenharmony_ci if (IS_ERR(gpiod_mute_r)) 29762306a36Sopenharmony_ci return PTR_ERR(gpiod_mute_r); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci card->dev = &pdev->dev; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci ret = devm_snd_soc_register_card(&pdev->dev, card); 30262306a36Sopenharmony_ci if (ret) 30362306a36Sopenharmony_ci dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", 30462306a36Sopenharmony_ci ret); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return ret; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic struct platform_driver spitz_driver = { 31062306a36Sopenharmony_ci .driver = { 31162306a36Sopenharmony_ci .name = "spitz-audio", 31262306a36Sopenharmony_ci .pm = &snd_soc_pm_ops, 31362306a36Sopenharmony_ci }, 31462306a36Sopenharmony_ci .probe = spitz_probe, 31562306a36Sopenharmony_ci}; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cimodule_platform_driver(spitz_driver); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ciMODULE_AUTHOR("Richard Purdie"); 32062306a36Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC Spitz"); 32162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 32262306a36Sopenharmony_ciMODULE_ALIAS("platform:spitz-audio"); 323