Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * trimslice.c - TrimSlice machine ASoC driver
  4 *
  5 * Copyright (C) 2011 - CompuLab, Ltd.
  6 * Author: Mike Rapoport <mike@compulab.co.il>
  7 *
  8 * Based on code copyright/by:
  9 * Author: Stephen Warren <swarren@nvidia.com>
 10 * Copyright (C) 2010-2011 - NVIDIA, Inc.
 11 */
 12
 13#include <linux/module.h>
 14#include <linux/of.h>
 15#include <linux/platform_device.h>
 16#include <linux/slab.h>
 17
 18#include <sound/core.h>
 19#include <sound/jack.h>
 20#include <sound/pcm.h>
 21#include <sound/pcm_params.h>
 22#include <sound/soc.h>
 23
 24#include "../codecs/tlv320aic23.h"
 25
 26#include "tegra_asoc_utils.h"
 27
 28#define DRV_NAME "tegra-snd-trimslice"
 29
 30struct tegra_trimslice {
 31	struct tegra_asoc_utils_data util_data;
 32};
 33
 34static int trimslice_asoc_hw_params(struct snd_pcm_substream *substream,
 35					struct snd_pcm_hw_params *params)
 36{
 37	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 38	struct snd_soc_dai *codec_dai = rtd->codec_dai;
 39	struct snd_soc_card *card = rtd->card;
 40	struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
 41	int srate, mclk;
 42	int err;
 43
 44	srate = params_rate(params);
 45	mclk = 128 * srate;
 46
 47	err = tegra_asoc_utils_set_rate(&trimslice->util_data, srate, mclk);
 48	if (err < 0) {
 49		dev_err(card->dev, "Can't configure clocks\n");
 50		return err;
 51	}
 52
 53	err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
 54					SND_SOC_CLOCK_IN);
 55	if (err < 0) {
 56		dev_err(card->dev, "codec_dai clock not set\n");
 57		return err;
 58	}
 59
 60	return 0;
 61}
 62
 63static const struct snd_soc_ops trimslice_asoc_ops = {
 64	.hw_params = trimslice_asoc_hw_params,
 65};
 66
 67static const struct snd_soc_dapm_widget trimslice_dapm_widgets[] = {
 68	SND_SOC_DAPM_HP("Line Out", NULL),
 69	SND_SOC_DAPM_LINE("Line In", NULL),
 70};
 71
 72static const struct snd_soc_dapm_route trimslice_audio_map[] = {
 73	{"Line Out", NULL, "LOUT"},
 74	{"Line Out", NULL, "ROUT"},
 75
 76	{"LLINEIN", NULL, "Line In"},
 77	{"RLINEIN", NULL, "Line In"},
 78};
 79
 80SND_SOC_DAILINK_DEFS(single_dsp,
 81	DAILINK_COMP_ARRAY(COMP_EMPTY()),
 82	DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "tlv320aic23-hifi")),
 83	DAILINK_COMP_ARRAY(COMP_EMPTY()));
 84
 85static struct snd_soc_dai_link trimslice_tlv320aic23_dai = {
 86	.name = "TLV320AIC23",
 87	.stream_name = "AIC23",
 88	.ops = &trimslice_asoc_ops,
 89	.dai_fmt = SND_SOC_DAIFMT_I2S |
 90		   SND_SOC_DAIFMT_NB_NF |
 91		   SND_SOC_DAIFMT_CBS_CFS,
 92	SND_SOC_DAILINK_REG(single_dsp),
 93};
 94
 95static struct snd_soc_card snd_soc_trimslice = {
 96	.name = "tegra-trimslice",
 97	.owner = THIS_MODULE,
 98	.dai_link = &trimslice_tlv320aic23_dai,
 99	.num_links = 1,
100
101	.dapm_widgets = trimslice_dapm_widgets,
102	.num_dapm_widgets = ARRAY_SIZE(trimslice_dapm_widgets),
103	.dapm_routes = trimslice_audio_map,
104	.num_dapm_routes = ARRAY_SIZE(trimslice_audio_map),
105	.fully_routed = true,
106};
107
108static int tegra_snd_trimslice_probe(struct platform_device *pdev)
109{
110	struct device_node *np = pdev->dev.of_node;
111	struct snd_soc_card *card = &snd_soc_trimslice;
112	struct tegra_trimslice *trimslice;
113	int ret;
114
115	trimslice = devm_kzalloc(&pdev->dev, sizeof(struct tegra_trimslice),
116				 GFP_KERNEL);
117	if (!trimslice)
118		return -ENOMEM;
119
120	card->dev = &pdev->dev;
121	snd_soc_card_set_drvdata(card, trimslice);
122
123	trimslice_tlv320aic23_dai.codecs->of_node = of_parse_phandle(np,
124			"nvidia,audio-codec", 0);
125	if (!trimslice_tlv320aic23_dai.codecs->of_node) {
126		dev_err(&pdev->dev,
127			"Property 'nvidia,audio-codec' missing or invalid\n");
128		ret = -EINVAL;
129		goto err;
130	}
131
132	trimslice_tlv320aic23_dai.cpus->of_node = of_parse_phandle(np,
133			"nvidia,i2s-controller", 0);
134	if (!trimslice_tlv320aic23_dai.cpus->of_node) {
135		dev_err(&pdev->dev,
136			"Property 'nvidia,i2s-controller' missing or invalid\n");
137		ret = -EINVAL;
138		goto err;
139	}
140
141	trimslice_tlv320aic23_dai.platforms->of_node =
142			trimslice_tlv320aic23_dai.cpus->of_node;
143
144	ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev);
145	if (ret)
146		goto err;
147
148	ret = snd_soc_register_card(card);
149	if (ret) {
150		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
151			ret);
152		goto err_fini_utils;
153	}
154
155	return 0;
156
157err_fini_utils:
158	tegra_asoc_utils_fini(&trimslice->util_data);
159err:
160	return ret;
161}
162
163static int tegra_snd_trimslice_remove(struct platform_device *pdev)
164{
165	struct snd_soc_card *card = platform_get_drvdata(pdev);
166	struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
167
168	snd_soc_unregister_card(card);
169
170	tegra_asoc_utils_fini(&trimslice->util_data);
171
172	return 0;
173}
174
175static const struct of_device_id trimslice_of_match[] = {
176	{ .compatible = "nvidia,tegra-audio-trimslice", },
177	{},
178};
179MODULE_DEVICE_TABLE(of, trimslice_of_match);
180
181static struct platform_driver tegra_snd_trimslice_driver = {
182	.driver = {
183		.name = DRV_NAME,
184		.of_match_table = trimslice_of_match,
185	},
186	.probe = tegra_snd_trimslice_probe,
187	.remove = tegra_snd_trimslice_remove,
188};
189module_platform_driver(tegra_snd_trimslice_driver);
190
191MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
192MODULE_DESCRIPTION("Trimslice machine ASoC driver");
193MODULE_LICENSE("GPL");
194MODULE_ALIAS("platform:" DRV_NAME);