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