162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2017 BayLibre, SAS.
462306a36Sopenharmony_ci * Author: Jerome Brunet <jbrunet@baylibre.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/of_platform.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <sound/soc.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * The everest 7134 is a very simple DA converter with no register
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistruct es7134_clock_mode {
1662306a36Sopenharmony_ci	unsigned int rate_min;
1762306a36Sopenharmony_ci	unsigned int rate_max;
1862306a36Sopenharmony_ci	unsigned int *mclk_fs;
1962306a36Sopenharmony_ci	unsigned int mclk_fs_num;
2062306a36Sopenharmony_ci};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct es7134_chip {
2362306a36Sopenharmony_ci	struct snd_soc_dai_driver *dai_drv;
2462306a36Sopenharmony_ci	const struct es7134_clock_mode *modes;
2562306a36Sopenharmony_ci	unsigned int mode_num;
2662306a36Sopenharmony_ci	const struct snd_soc_dapm_widget *extra_widgets;
2762306a36Sopenharmony_ci	unsigned int extra_widget_num;
2862306a36Sopenharmony_ci	const struct snd_soc_dapm_route *extra_routes;
2962306a36Sopenharmony_ci	unsigned int extra_route_num;
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct es7134_data {
3362306a36Sopenharmony_ci	unsigned int mclk;
3462306a36Sopenharmony_ci	const struct es7134_chip *chip;
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic int es7134_check_mclk(struct snd_soc_dai *dai,
3862306a36Sopenharmony_ci			     struct es7134_data *priv,
3962306a36Sopenharmony_ci			     unsigned int rate)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	unsigned int mfs = priv->mclk / rate;
4262306a36Sopenharmony_ci	int i, j;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	for (i = 0; i < priv->chip->mode_num; i++) {
4562306a36Sopenharmony_ci		const struct es7134_clock_mode *mode = &priv->chip->modes[i];
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci		if (rate < mode->rate_min || rate > mode->rate_max)
4862306a36Sopenharmony_ci			continue;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci		for (j = 0; j < mode->mclk_fs_num; j++) {
5162306a36Sopenharmony_ci			if (mode->mclk_fs[j] == mfs)
5262306a36Sopenharmony_ci				return 0;
5362306a36Sopenharmony_ci		}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		dev_err(dai->dev, "unsupported mclk_fs %u for rate %u\n",
5662306a36Sopenharmony_ci			mfs, rate);
5762306a36Sopenharmony_ci		return -EINVAL;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* should not happen */
6162306a36Sopenharmony_ci	dev_err(dai->dev, "unsupported rate: %u\n", rate);
6262306a36Sopenharmony_ci	return -EINVAL;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int es7134_hw_params(struct snd_pcm_substream *substream,
6662306a36Sopenharmony_ci			    struct snd_pcm_hw_params *params,
6762306a36Sopenharmony_ci			    struct snd_soc_dai *dai)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct es7134_data *priv = snd_soc_dai_get_drvdata(dai);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* mclk has not been provided, assume it is OK */
7262306a36Sopenharmony_ci	if (!priv->mclk)
7362306a36Sopenharmony_ci		return 0;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return es7134_check_mclk(dai, priv, params_rate(params));
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic int es7134_set_sysclk(struct snd_soc_dai *dai, int clk_id,
7962306a36Sopenharmony_ci			     unsigned int freq, int dir)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct es7134_data *priv = snd_soc_dai_get_drvdata(dai);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (dir == SND_SOC_CLOCK_IN && clk_id == 0) {
8462306a36Sopenharmony_ci		priv->mclk = freq;
8562306a36Sopenharmony_ci		return 0;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return -ENOTSUPP;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int es7134_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK |
9462306a36Sopenharmony_ci		SND_SOC_DAIFMT_MASTER_MASK);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
9762306a36Sopenharmony_ci		    SND_SOC_DAIFMT_CBC_CFC)) {
9862306a36Sopenharmony_ci		dev_err(codec_dai->dev, "Invalid DAI format\n");
9962306a36Sopenharmony_ci		return -EINVAL;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return 0;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic int es7134_component_probe(struct snd_soc_component *c)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(c);
10862306a36Sopenharmony_ci	struct es7134_data *priv = snd_soc_component_get_drvdata(c);
10962306a36Sopenharmony_ci	const struct es7134_chip *chip = priv->chip;
11062306a36Sopenharmony_ci	int ret;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (chip->extra_widget_num) {
11362306a36Sopenharmony_ci		ret = snd_soc_dapm_new_controls(dapm, chip->extra_widgets,
11462306a36Sopenharmony_ci						chip->extra_widget_num);
11562306a36Sopenharmony_ci		if (ret) {
11662306a36Sopenharmony_ci			dev_err(c->dev, "failed to add extra widgets\n");
11762306a36Sopenharmony_ci			return ret;
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (chip->extra_route_num) {
12262306a36Sopenharmony_ci		ret = snd_soc_dapm_add_routes(dapm, chip->extra_routes,
12362306a36Sopenharmony_ci					      chip->extra_route_num);
12462306a36Sopenharmony_ci		if (ret) {
12562306a36Sopenharmony_ci			dev_err(c->dev, "failed to add extra routes\n");
12662306a36Sopenharmony_ci			return ret;
12762306a36Sopenharmony_ci		}
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return 0;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic const struct snd_soc_dai_ops es7134_dai_ops = {
13462306a36Sopenharmony_ci	.set_fmt	= es7134_set_fmt,
13562306a36Sopenharmony_ci	.hw_params	= es7134_hw_params,
13662306a36Sopenharmony_ci	.set_sysclk	= es7134_set_sysclk,
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic struct snd_soc_dai_driver es7134_dai = {
14062306a36Sopenharmony_ci	.name = "es7134-hifi",
14162306a36Sopenharmony_ci	.playback = {
14262306a36Sopenharmony_ci		.stream_name = "Playback",
14362306a36Sopenharmony_ci		.channels_min = 2,
14462306a36Sopenharmony_ci		.channels_max = 2,
14562306a36Sopenharmony_ci		.rates = (SNDRV_PCM_RATE_8000_48000 |
14662306a36Sopenharmony_ci			  SNDRV_PCM_RATE_88200      |
14762306a36Sopenharmony_ci			  SNDRV_PCM_RATE_96000      |
14862306a36Sopenharmony_ci			  SNDRV_PCM_RATE_176400     |
14962306a36Sopenharmony_ci			  SNDRV_PCM_RATE_192000),
15062306a36Sopenharmony_ci		.formats = (SNDRV_PCM_FMTBIT_S16_LE  |
15162306a36Sopenharmony_ci			    SNDRV_PCM_FMTBIT_S18_3LE |
15262306a36Sopenharmony_ci			    SNDRV_PCM_FMTBIT_S20_3LE |
15362306a36Sopenharmony_ci			    SNDRV_PCM_FMTBIT_S24_3LE |
15462306a36Sopenharmony_ci			    SNDRV_PCM_FMTBIT_S24_LE),
15562306a36Sopenharmony_ci	},
15662306a36Sopenharmony_ci	.ops = &es7134_dai_ops,
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic const struct es7134_clock_mode es7134_modes[] = {
16062306a36Sopenharmony_ci	{
16162306a36Sopenharmony_ci		/* Single speed mode */
16262306a36Sopenharmony_ci		.rate_min = 8000,
16362306a36Sopenharmony_ci		.rate_max = 50000,
16462306a36Sopenharmony_ci		.mclk_fs = (unsigned int[]) { 256, 384, 512, 768, 1024 },
16562306a36Sopenharmony_ci		.mclk_fs_num = 5,
16662306a36Sopenharmony_ci	}, {
16762306a36Sopenharmony_ci		/* Double speed mode */
16862306a36Sopenharmony_ci		.rate_min = 84000,
16962306a36Sopenharmony_ci		.rate_max = 100000,
17062306a36Sopenharmony_ci		.mclk_fs = (unsigned int[]) { 128, 192, 256, 384, 512 },
17162306a36Sopenharmony_ci		.mclk_fs_num = 5,
17262306a36Sopenharmony_ci	}, {
17362306a36Sopenharmony_ci		/* Quad speed mode */
17462306a36Sopenharmony_ci		.rate_min = 167000,
17562306a36Sopenharmony_ci		.rate_max = 192000,
17662306a36Sopenharmony_ci		.mclk_fs = (unsigned int[]) { 128, 192, 256 },
17762306a36Sopenharmony_ci		.mclk_fs_num = 3,
17862306a36Sopenharmony_ci	},
17962306a36Sopenharmony_ci};
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/* Digital I/O are also supplied by VDD on the es7134 */
18262306a36Sopenharmony_cistatic const struct snd_soc_dapm_route es7134_extra_routes[] = {
18362306a36Sopenharmony_ci	{ "Playback", NULL, "VDD", }
18462306a36Sopenharmony_ci};
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic const struct es7134_chip es7134_chip __maybe_unused = {
18762306a36Sopenharmony_ci	.dai_drv = &es7134_dai,
18862306a36Sopenharmony_ci	.modes = es7134_modes,
18962306a36Sopenharmony_ci	.mode_num = ARRAY_SIZE(es7134_modes),
19062306a36Sopenharmony_ci	.extra_routes = es7134_extra_routes,
19162306a36Sopenharmony_ci	.extra_route_num = ARRAY_SIZE(es7134_extra_routes),
19262306a36Sopenharmony_ci};
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget es7134_dapm_widgets[] = {
19562306a36Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("AOUTL"),
19662306a36Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("AOUTR"),
19762306a36Sopenharmony_ci	SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
19862306a36Sopenharmony_ci	SND_SOC_DAPM_REGULATOR_SUPPLY("VDD", 0, 0),
19962306a36Sopenharmony_ci};
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic const struct snd_soc_dapm_route es7134_dapm_routes[] = {
20262306a36Sopenharmony_ci	{ "AOUTL", NULL, "DAC" },
20362306a36Sopenharmony_ci	{ "AOUTR", NULL, "DAC" },
20462306a36Sopenharmony_ci	{ "DAC", NULL, "VDD" },
20562306a36Sopenharmony_ci};
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic const struct snd_soc_component_driver es7134_component_driver = {
20862306a36Sopenharmony_ci	.probe			= es7134_component_probe,
20962306a36Sopenharmony_ci	.dapm_widgets		= es7134_dapm_widgets,
21062306a36Sopenharmony_ci	.num_dapm_widgets	= ARRAY_SIZE(es7134_dapm_widgets),
21162306a36Sopenharmony_ci	.dapm_routes		= es7134_dapm_routes,
21262306a36Sopenharmony_ci	.num_dapm_routes	= ARRAY_SIZE(es7134_dapm_routes),
21362306a36Sopenharmony_ci	.idle_bias_on		= 1,
21462306a36Sopenharmony_ci	.use_pmdown_time	= 1,
21562306a36Sopenharmony_ci	.endianness		= 1,
21662306a36Sopenharmony_ci};
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic struct snd_soc_dai_driver es7154_dai = {
21962306a36Sopenharmony_ci	.name = "es7154-hifi",
22062306a36Sopenharmony_ci	.playback = {
22162306a36Sopenharmony_ci		.stream_name = "Playback",
22262306a36Sopenharmony_ci		.channels_min = 2,
22362306a36Sopenharmony_ci		.channels_max = 2,
22462306a36Sopenharmony_ci		.rates = (SNDRV_PCM_RATE_8000_48000 |
22562306a36Sopenharmony_ci			  SNDRV_PCM_RATE_88200      |
22662306a36Sopenharmony_ci			  SNDRV_PCM_RATE_96000),
22762306a36Sopenharmony_ci		.formats = (SNDRV_PCM_FMTBIT_S16_LE  |
22862306a36Sopenharmony_ci			    SNDRV_PCM_FMTBIT_S18_3LE |
22962306a36Sopenharmony_ci			    SNDRV_PCM_FMTBIT_S20_3LE |
23062306a36Sopenharmony_ci			    SNDRV_PCM_FMTBIT_S24_3LE |
23162306a36Sopenharmony_ci			    SNDRV_PCM_FMTBIT_S24_LE),
23262306a36Sopenharmony_ci	},
23362306a36Sopenharmony_ci	.ops = &es7134_dai_ops,
23462306a36Sopenharmony_ci};
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic const struct es7134_clock_mode es7154_modes[] = {
23762306a36Sopenharmony_ci	{
23862306a36Sopenharmony_ci		/* Single speed mode */
23962306a36Sopenharmony_ci		.rate_min = 8000,
24062306a36Sopenharmony_ci		.rate_max = 50000,
24162306a36Sopenharmony_ci		.mclk_fs = (unsigned int[]) { 32, 64, 128, 192, 256,
24262306a36Sopenharmony_ci					      384, 512, 768, 1024 },
24362306a36Sopenharmony_ci		.mclk_fs_num = 9,
24462306a36Sopenharmony_ci	}, {
24562306a36Sopenharmony_ci		/* Double speed mode */
24662306a36Sopenharmony_ci		.rate_min = 84000,
24762306a36Sopenharmony_ci		.rate_max = 100000,
24862306a36Sopenharmony_ci		.mclk_fs = (unsigned int[]) { 128, 192, 256, 384, 512,
24962306a36Sopenharmony_ci					      768, 1024},
25062306a36Sopenharmony_ci		.mclk_fs_num = 7,
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci};
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/* Es7154 has a separate supply for digital I/O  */
25562306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget es7154_extra_widgets[] = {
25662306a36Sopenharmony_ci	SND_SOC_DAPM_REGULATOR_SUPPLY("PVDD", 0, 0),
25762306a36Sopenharmony_ci};
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic const struct snd_soc_dapm_route es7154_extra_routes[] = {
26062306a36Sopenharmony_ci	{ "Playback", NULL, "PVDD", }
26162306a36Sopenharmony_ci};
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic const struct es7134_chip es7154_chip __maybe_unused = {
26462306a36Sopenharmony_ci	.dai_drv = &es7154_dai,
26562306a36Sopenharmony_ci	.modes = es7154_modes,
26662306a36Sopenharmony_ci	.mode_num = ARRAY_SIZE(es7154_modes),
26762306a36Sopenharmony_ci	.extra_routes = es7154_extra_routes,
26862306a36Sopenharmony_ci	.extra_route_num = ARRAY_SIZE(es7154_extra_routes),
26962306a36Sopenharmony_ci	.extra_widgets = es7154_extra_widgets,
27062306a36Sopenharmony_ci	.extra_widget_num = ARRAY_SIZE(es7154_extra_widgets),
27162306a36Sopenharmony_ci};
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int es7134_probe(struct platform_device *pdev)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
27662306a36Sopenharmony_ci	struct es7134_data *priv;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
27962306a36Sopenharmony_ci	if (!priv)
28062306a36Sopenharmony_ci		return -ENOMEM;
28162306a36Sopenharmony_ci	platform_set_drvdata(pdev, priv);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	priv->chip = of_device_get_match_data(dev);
28462306a36Sopenharmony_ci	if (!priv->chip) {
28562306a36Sopenharmony_ci		dev_err(dev, "failed to match device\n");
28662306a36Sopenharmony_ci		return -ENODEV;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return devm_snd_soc_register_component(&pdev->dev,
29062306a36Sopenharmony_ci				      &es7134_component_driver,
29162306a36Sopenharmony_ci				      priv->chip->dai_drv, 1);
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci#ifdef CONFIG_OF
29562306a36Sopenharmony_cistatic const struct of_device_id es7134_ids[] = {
29662306a36Sopenharmony_ci	{ .compatible = "everest,es7134", .data = &es7134_chip },
29762306a36Sopenharmony_ci	{ .compatible = "everest,es7144", .data = &es7134_chip },
29862306a36Sopenharmony_ci	{ .compatible = "everest,es7154", .data = &es7154_chip },
29962306a36Sopenharmony_ci	{ }
30062306a36Sopenharmony_ci};
30162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, es7134_ids);
30262306a36Sopenharmony_ci#endif
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic struct platform_driver es7134_driver = {
30562306a36Sopenharmony_ci	.driver = {
30662306a36Sopenharmony_ci		.name = "es7134",
30762306a36Sopenharmony_ci		.of_match_table = of_match_ptr(es7134_ids),
30862306a36Sopenharmony_ci	},
30962306a36Sopenharmony_ci	.probe = es7134_probe,
31062306a36Sopenharmony_ci};
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cimodule_platform_driver(es7134_driver);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC ES7134 audio codec driver");
31562306a36Sopenharmony_ciMODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
31662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
317