18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Modifications by Christian Pellegrin <chripell@evolware.org> 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// s3c24xx_uda134x.c - S3C24XX_UDA134X ALSA SoC Audio board driver 68c2ecf20Sopenharmony_ci// 78c2ecf20Sopenharmony_ci// Copyright 2007 Dension Audio Systems Ltd. 88c2ecf20Sopenharmony_ci// Author: Zoltan Devai 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/gpio.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <sound/soc.h> 158c2ecf20Sopenharmony_ci#include <sound/s3c24xx_uda134x.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "regs-iis.h" 188c2ecf20Sopenharmony_ci#include "s3c24xx-i2s.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct s3c24xx_uda134x { 218c2ecf20Sopenharmony_ci struct clk *xtal; 228c2ecf20Sopenharmony_ci struct clk *pclk; 238c2ecf20Sopenharmony_ci struct mutex clk_lock; 248c2ecf20Sopenharmony_ci int clk_users; 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* #define ENFORCE_RATES 1 */ 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci Unfortunately the S3C24XX in master mode has a limited capacity of 308c2ecf20Sopenharmony_ci generating the clock for the codec. If you define this only rates 318c2ecf20Sopenharmony_ci that are really available will be enforced. But be careful, most 328c2ecf20Sopenharmony_ci user level application just want the usual sampling frequencies (8, 338c2ecf20Sopenharmony_ci 11.025, 22.050, 44.1 kHz) and anyway resampling is a costly 348c2ecf20Sopenharmony_ci operation for embedded systems. So if you aren't very lucky or your 358c2ecf20Sopenharmony_ci hardware engineer wasn't very forward-looking it's better to leave 368c2ecf20Sopenharmony_ci this undefined. If you do so an approximate value for the requested 378c2ecf20Sopenharmony_ci sampling rate in the range -/+ 5% will be chosen. If this in not 388c2ecf20Sopenharmony_ci possible an error will be returned. 398c2ecf20Sopenharmony_ci*/ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic unsigned int rates[33 * 2]; 428c2ecf20Sopenharmony_ci#ifdef ENFORCE_RATES 438c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_rates = { 448c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(rates), 458c2ecf20Sopenharmony_ci .list = rates, 468c2ecf20Sopenharmony_ci .mask = 0, 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci#endif 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 538c2ecf20Sopenharmony_ci struct s3c24xx_uda134x *priv = snd_soc_card_get_drvdata(rtd->card); 548c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 558c2ecf20Sopenharmony_ci int ret = 0; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci mutex_lock(&priv->clk_lock); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (priv->clk_users == 0) { 608c2ecf20Sopenharmony_ci priv->xtal = clk_get(rtd->dev, "xtal"); 618c2ecf20Sopenharmony_ci if (IS_ERR(priv->xtal)) { 628c2ecf20Sopenharmony_ci dev_err(rtd->dev, "%s cannot get xtal\n", __func__); 638c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->xtal); 648c2ecf20Sopenharmony_ci } else { 658c2ecf20Sopenharmony_ci priv->pclk = clk_get(cpu_dai->dev, "iis"); 668c2ecf20Sopenharmony_ci if (IS_ERR(priv->pclk)) { 678c2ecf20Sopenharmony_ci dev_err(rtd->dev, "%s cannot get pclk\n", 688c2ecf20Sopenharmony_ci __func__); 698c2ecf20Sopenharmony_ci clk_put(priv->xtal); 708c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->pclk); 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci if (!ret) { 748c2ecf20Sopenharmony_ci int i, j; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 778c2ecf20Sopenharmony_ci int fs = i ? 256 : 384; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci rates[i*33] = clk_get_rate(priv->xtal) / fs; 808c2ecf20Sopenharmony_ci for (j = 1; j < 33; j++) 818c2ecf20Sopenharmony_ci rates[i*33 + j] = clk_get_rate(priv->pclk) / 828c2ecf20Sopenharmony_ci (j * fs); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci priv->clk_users += 1; 878c2ecf20Sopenharmony_ci mutex_unlock(&priv->clk_lock); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (!ret) { 908c2ecf20Sopenharmony_ci#ifdef ENFORCE_RATES 918c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_list(substream->runtime, 0, 928c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 938c2ecf20Sopenharmony_ci &hw_constraints_rates); 948c2ecf20Sopenharmony_ci if (ret < 0) 958c2ecf20Sopenharmony_ci dev_err(rtd->dev, "%s cannot set constraints\n", 968c2ecf20Sopenharmony_ci __func__); 978c2ecf20Sopenharmony_ci#endif 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci return ret; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1058c2ecf20Sopenharmony_ci struct s3c24xx_uda134x *priv = snd_soc_card_get_drvdata(rtd->card); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci mutex_lock(&priv->clk_lock); 1088c2ecf20Sopenharmony_ci priv->clk_users -= 1; 1098c2ecf20Sopenharmony_ci if (priv->clk_users == 0) { 1108c2ecf20Sopenharmony_ci clk_put(priv->xtal); 1118c2ecf20Sopenharmony_ci priv->xtal = NULL; 1128c2ecf20Sopenharmony_ci clk_put(priv->pclk); 1138c2ecf20Sopenharmony_ci priv->pclk = NULL; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci mutex_unlock(&priv->clk_lock); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream, 1198c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1228c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 1238c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 1248c2ecf20Sopenharmony_ci unsigned int clk = 0; 1258c2ecf20Sopenharmony_ci int ret = 0; 1268c2ecf20Sopenharmony_ci int clk_source, fs_mode; 1278c2ecf20Sopenharmony_ci unsigned long rate = params_rate(params); 1288c2ecf20Sopenharmony_ci long err, cerr; 1298c2ecf20Sopenharmony_ci unsigned int div; 1308c2ecf20Sopenharmony_ci int i, bi; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci err = 999999; 1338c2ecf20Sopenharmony_ci bi = 0; 1348c2ecf20Sopenharmony_ci for (i = 0; i < 2*33; i++) { 1358c2ecf20Sopenharmony_ci cerr = rates[i] - rate; 1368c2ecf20Sopenharmony_ci if (cerr < 0) 1378c2ecf20Sopenharmony_ci cerr = -cerr; 1388c2ecf20Sopenharmony_ci if (cerr < err) { 1398c2ecf20Sopenharmony_ci err = cerr; 1408c2ecf20Sopenharmony_ci bi = i; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci if (bi / 33 == 1) 1448c2ecf20Sopenharmony_ci fs_mode = S3C2410_IISMOD_256FS; 1458c2ecf20Sopenharmony_ci else 1468c2ecf20Sopenharmony_ci fs_mode = S3C2410_IISMOD_384FS; 1478c2ecf20Sopenharmony_ci if (bi % 33 == 0) { 1488c2ecf20Sopenharmony_ci clk_source = S3C24XX_CLKSRC_MPLL; 1498c2ecf20Sopenharmony_ci div = 1; 1508c2ecf20Sopenharmony_ci } else { 1518c2ecf20Sopenharmony_ci clk_source = S3C24XX_CLKSRC_PCLK; 1528c2ecf20Sopenharmony_ci div = bi % 33; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci dev_dbg(rtd->dev, "%s desired rate %lu, %d\n", __func__, rate, bi); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci dev_dbg(rtd->dev, "%s will use: %s %s %d sysclk %d err %ld\n", __func__, 1608c2ecf20Sopenharmony_ci fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS", 1618c2ecf20Sopenharmony_ci clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK", 1628c2ecf20Sopenharmony_ci div, clk, err); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if ((err * 100 / rate) > 5) { 1658c2ecf20Sopenharmony_ci dev_err(rtd->dev, "effective frequency too different " 1668c2ecf20Sopenharmony_ci "from desired (%ld%%)\n", err * 100 / rate); 1678c2ecf20Sopenharmony_ci return -EINVAL; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk, 1718c2ecf20Sopenharmony_ci SND_SOC_CLOCK_IN); 1728c2ecf20Sopenharmony_ci if (ret < 0) 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode); 1768c2ecf20Sopenharmony_ci if (ret < 0) 1778c2ecf20Sopenharmony_ci return ret; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK, 1808c2ecf20Sopenharmony_ci S3C2410_IISMOD_32FS); 1818c2ecf20Sopenharmony_ci if (ret < 0) 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER, 1858c2ecf20Sopenharmony_ci S3C24XX_PRESCALE(div, div)); 1868c2ecf20Sopenharmony_ci if (ret < 0) 1878c2ecf20Sopenharmony_ci return ret; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* set the codec system clock for DAC and ADC */ 1908c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 1918c2ecf20Sopenharmony_ci SND_SOC_CLOCK_OUT); 1928c2ecf20Sopenharmony_ci if (ret < 0) 1938c2ecf20Sopenharmony_ci return ret; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic const struct snd_soc_ops s3c24xx_uda134x_ops = { 1998c2ecf20Sopenharmony_ci .startup = s3c24xx_uda134x_startup, 2008c2ecf20Sopenharmony_ci .shutdown = s3c24xx_uda134x_shutdown, 2018c2ecf20Sopenharmony_ci .hw_params = s3c24xx_uda134x_hw_params, 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(uda134x, 2058c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CPU("s3c24xx-iis")), 2068c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_CODEC("uda134x-codec", "uda134x-hifi")), 2078c2ecf20Sopenharmony_ci DAILINK_COMP_ARRAY(COMP_PLATFORM("s3c24xx-iis"))); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link s3c24xx_uda134x_dai_link = { 2108c2ecf20Sopenharmony_ci .name = "UDA134X", 2118c2ecf20Sopenharmony_ci .stream_name = "UDA134X", 2128c2ecf20Sopenharmony_ci .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 2138c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CBS_CFS, 2148c2ecf20Sopenharmony_ci .ops = &s3c24xx_uda134x_ops, 2158c2ecf20Sopenharmony_ci SND_SOC_DAILINK_REG(uda134x), 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic struct snd_soc_card snd_soc_s3c24xx_uda134x = { 2198c2ecf20Sopenharmony_ci .name = "S3C24XX_UDA134X", 2208c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2218c2ecf20Sopenharmony_ci .dai_link = &s3c24xx_uda134x_dai_link, 2228c2ecf20Sopenharmony_ci .num_links = 1, 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int s3c24xx_uda134x_probe(struct platform_device *pdev) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct snd_soc_card *card = &snd_soc_s3c24xx_uda134x; 2288c2ecf20Sopenharmony_ci struct s3c24xx_uda134x *priv; 2298c2ecf20Sopenharmony_ci int ret; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 2328c2ecf20Sopenharmony_ci if (!priv) 2338c2ecf20Sopenharmony_ci return -ENOMEM; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci mutex_init(&priv->clk_lock); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci card->dev = &pdev->dev; 2388c2ecf20Sopenharmony_ci snd_soc_card_set_drvdata(card, priv); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_card(&pdev->dev, card); 2418c2ecf20Sopenharmony_ci if (ret) 2428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register card: %d\n", ret); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return ret; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic struct platform_driver s3c24xx_uda134x_driver = { 2488c2ecf20Sopenharmony_ci .probe = s3c24xx_uda134x_probe, 2498c2ecf20Sopenharmony_ci .driver = { 2508c2ecf20Sopenharmony_ci .name = "s3c24xx_uda134x", 2518c2ecf20Sopenharmony_ci }, 2528c2ecf20Sopenharmony_ci}; 2538c2ecf20Sopenharmony_cimodule_platform_driver(s3c24xx_uda134x_driver); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ciMODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>"); 2568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver"); 2578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 258