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