18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// h1940_uda1380.c - ALSA SoC Audio Layer 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org> 68c2ecf20Sopenharmony_ci// Copyright (c) 2010 Vasily Khoruzhick <anarsoul@gmail.com> 78c2ecf20Sopenharmony_ci// 88c2ecf20Sopenharmony_ci// Based on version from Arnaud Patard <arnaud.patard@rtp-net.org> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <sound/soc.h> 158c2ecf20Sopenharmony_ci#include <sound/jack.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "regs-iis.h" 188c2ecf20Sopenharmony_ci#include "s3c24xx-i2s.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic const unsigned int rates[] = { 218c2ecf20Sopenharmony_ci 11025, 228c2ecf20Sopenharmony_ci 22050, 238c2ecf20Sopenharmony_ci 44100, 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_rates = { 278c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(rates), 288c2ecf20Sopenharmony_ci .list = rates, 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic struct gpio_desc *gpiod_speaker_power; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic struct snd_soc_jack hp_jack; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic struct snd_soc_jack_pin hp_jack_pins[] = { 368c2ecf20Sopenharmony_ci { 378c2ecf20Sopenharmony_ci .pin = "Headphone Jack", 388c2ecf20Sopenharmony_ci .mask = SND_JACK_HEADPHONE, 398c2ecf20Sopenharmony_ci }, 408c2ecf20Sopenharmony_ci { 418c2ecf20Sopenharmony_ci .pin = "Speaker", 428c2ecf20Sopenharmony_ci .mask = SND_JACK_HEADPHONE, 438c2ecf20Sopenharmony_ci .invert = 1, 448c2ecf20Sopenharmony_ci }, 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic struct snd_soc_jack_gpio hp_jack_gpios[] = { 488c2ecf20Sopenharmony_ci { 498c2ecf20Sopenharmony_ci .name = "hp-gpio", 508c2ecf20Sopenharmony_ci .report = SND_JACK_HEADPHONE, 518c2ecf20Sopenharmony_ci .invert = 1, 528c2ecf20Sopenharmony_ci .debounce_time = 200, 538c2ecf20Sopenharmony_ci }, 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int h1940_startup(struct snd_pcm_substream *substream) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return snd_pcm_hw_constraint_list(runtime, 0, 618c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 628c2ecf20Sopenharmony_ci &hw_rates); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int h1940_hw_params(struct snd_pcm_substream *substream, 668c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 698c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 708c2ecf20Sopenharmony_ci int div; 718c2ecf20Sopenharmony_ci int ret; 728c2ecf20Sopenharmony_ci unsigned int rate = params_rate(params); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci switch (rate) { 758c2ecf20Sopenharmony_ci case 11025: 768c2ecf20Sopenharmony_ci case 22050: 778c2ecf20Sopenharmony_ci case 44100: 788c2ecf20Sopenharmony_ci div = s3c24xx_i2s_get_clockrate() / (384 * rate); 798c2ecf20Sopenharmony_ci if (s3c24xx_i2s_get_clockrate() % (384 * rate) > (192 * rate)) 808c2ecf20Sopenharmony_ci div++; 818c2ecf20Sopenharmony_ci break; 828c2ecf20Sopenharmony_ci default: 838c2ecf20Sopenharmony_ci dev_err(rtd->dev, "%s: rate %d is not supported\n", 848c2ecf20Sopenharmony_ci __func__, rate); 858c2ecf20Sopenharmony_ci return -EINVAL; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* select clock source */ 898c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_PCLK, rate, 908c2ecf20Sopenharmony_ci SND_SOC_CLOCK_OUT); 918c2ecf20Sopenharmony_ci if (ret < 0) 928c2ecf20Sopenharmony_ci return ret; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* set MCLK division for sample rate */ 958c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, 968c2ecf20Sopenharmony_ci S3C2410_IISMOD_384FS); 978c2ecf20Sopenharmony_ci if (ret < 0) 988c2ecf20Sopenharmony_ci return ret; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* set BCLK division for sample rate */ 1018c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK, 1028c2ecf20Sopenharmony_ci S3C2410_IISMOD_32FS); 1038c2ecf20Sopenharmony_ci if (ret < 0) 1048c2ecf20Sopenharmony_ci return ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* set prescaler division for sample rate */ 1078c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER, 1088c2ecf20Sopenharmony_ci S3C24XX_PRESCALE(div, div)); 1098c2ecf20Sopenharmony_ci if (ret < 0) 1108c2ecf20Sopenharmony_ci return ret; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic struct snd_soc_ops h1940_ops = { 1168c2ecf20Sopenharmony_ci .startup = h1940_startup, 1178c2ecf20Sopenharmony_ci .hw_params = h1940_hw_params, 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int h1940_spk_power(struct snd_soc_dapm_widget *w, 1218c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci if (SND_SOC_DAPM_EVENT_ON(event)) 1248c2ecf20Sopenharmony_ci gpiod_set_value(gpiod_speaker_power, 1); 1258c2ecf20Sopenharmony_ci else 1268c2ecf20Sopenharmony_ci gpiod_set_value(gpiod_speaker_power, 0); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* h1940 machine dapm widgets */ 1328c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = { 1338c2ecf20Sopenharmony_ci SND_SOC_DAPM_HP("Headphone Jack", NULL), 1348c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("Mic Jack", NULL), 1358c2ecf20Sopenharmony_ci SND_SOC_DAPM_SPK("Speaker", h1940_spk_power), 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/* h1940 machine audio_map */ 1398c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route audio_map[] = { 1408c2ecf20Sopenharmony_ci /* headphone connected to VOUTLHP, VOUTRHP */ 1418c2ecf20Sopenharmony_ci {"Headphone Jack", NULL, "VOUTLHP"}, 1428c2ecf20Sopenharmony_ci {"Headphone Jack", NULL, "VOUTRHP"}, 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* ext speaker connected to VOUTL, VOUTR */ 1458c2ecf20Sopenharmony_ci {"Speaker", NULL, "VOUTL"}, 1468c2ecf20Sopenharmony_ci {"Speaker", NULL, "VOUTR"}, 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* mic is connected to VINM */ 1498c2ecf20Sopenharmony_ci {"VINM", NULL, "Mic Jack"}, 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE, 1558c2ecf20Sopenharmony_ci &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins)); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios), 1588c2ecf20Sopenharmony_ci hp_jack_gpios); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* s3c24xx digital audio interface glue - connects codec <--> CPU */ 1648c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(uda1380, 1658c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("s3c24xx-iis")), 1668c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-001a", "uda1380-hifi")), 1678c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_PLATFORM("s3c24xx-iis"))); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link h1940_uda1380_dai[] = { 1708c2ecf20Sopenharmony_ci { 1718c2ecf20Sopenharmony_ci .name = "uda1380", 1728c2ecf20Sopenharmony_ci .stream_name = "UDA1380 Duplex", 1738c2ecf20Sopenharmony_ci .init = h1940_uda1380_init, 1748c2ecf20Sopenharmony_ci .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 1758c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CBS_CFS, 1768c2ecf20Sopenharmony_ci .ops = &h1940_ops, 1778c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(uda1380), 1788c2ecf20Sopenharmony_ci }, 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic struct snd_soc_card h1940_asoc = { 1828c2ecf20Sopenharmony_ci .name = "h1940", 1838c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1848c2ecf20Sopenharmony_ci .dai_link = h1940_uda1380_dai, 1858c2ecf20Sopenharmony_ci .num_links = ARRAY_SIZE(h1940_uda1380_dai), 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci .dapm_widgets = uda1380_dapm_widgets, 1888c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets), 1898c2ecf20Sopenharmony_ci .dapm_routes = audio_map, 1908c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(audio_map), 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int h1940_probe(struct platform_device *pdev) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci h1940_asoc.dev = dev; 1988c2ecf20Sopenharmony_ci hp_jack_gpios[0].gpiod_dev = dev; 1998c2ecf20Sopenharmony_ci gpiod_speaker_power = devm_gpiod_get(&pdev->dev, "speaker-power", 2008c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (IS_ERR(gpiod_speaker_power)) { 2038c2ecf20Sopenharmony_ci dev_err(dev, "Could not get gpio\n"); 2048c2ecf20Sopenharmony_ci return PTR_ERR(gpiod_speaker_power); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return devm_snd_soc_register_card(dev, &h1940_asoc); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic struct platform_driver h1940_audio_driver = { 2118c2ecf20Sopenharmony_ci .driver = { 2128c2ecf20Sopenharmony_ci .name = "h1940-audio", 2138c2ecf20Sopenharmony_ci .pm = &snd_soc_pm_ops, 2148c2ecf20Sopenharmony_ci }, 2158c2ecf20Sopenharmony_ci .probe = h1940_probe, 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_cimodule_platform_driver(h1940_audio_driver); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* Module information */ 2208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Arnaud Patard, Vasily Khoruzhick"); 2218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC H1940"); 2228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2238c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:h1940-audio"); 224