aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/generic/audio-graph-card.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-07-06 13:56:51 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2017-07-06 13:56:51 -0400
commit920f2ecdf6c3b3526f60fbd38c68597953cad3ee (patch)
tree18188922ba38a5c53ee8d17032eb5c46dffc7fa2 /sound/soc/generic/audio-graph-card.c
parent9ced560b82606b35adb33a27012a148d418a4c1f (diff)
parentfc18282cdcba984ab89c74d7e844c10114ae0795 (diff)
Merge tag 'sound-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "This development cycle resulted in a fair amount of changes in both core and driver sides. The most significant change in ALSA core is about PCM. Also the support of of-graph card and the new DAPM widget for DSP are noteworthy changes in ASoC core. And there're lots of small changes splat over the tree, as you can see in diffstat. Below are a few highlights: ALSA core: - Removal of set_fs() hackery from PCM core stuff, and the code reorganization / optimization thereafter - Improved support of PCM ack ops, and a new ABI for improved control/status mmap handling - Lots of constifications in various codes ASoC core: - The support of of-graph card, which may work as a better generic device for a replacement of simple-card - New widget types intended mainly for use with DSPs ASoC drivers: - New drivers for Allwinner V3s SoCs - Ensonic ES8316 codec support - More Intel SKL and KBL works - More device support for Intel SST Atom (mostly for cheap tablets and 2-in-1 devices) - Support for Rockchip PDM controllers - Support for STM32 I2S and S/PDIF controllers - Support for ZTE AUD96P22 codecs HD-audio: - Support of new Realtek codecs (ALC215/ALC285/ALC289), more quirks for HP and Dell machines - A few more fixes for i915 component binding" * tag 'sound-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (418 commits) ALSA: hda - Fix unbalance of i915 module refcount ASoC: Intel: Skylake: Remove driver debugfs exit ASoC: Intel: Skylake: explicitly add the headers sst-dsp.h ALSA: hda/realtek - Remove GPIO_MASK ALSA: hda/realtek - Fix typo of pincfg for Dell quirk ALSA: pcm: add a documentation for tracepoints ALSA: atmel: ac97c: fix error return code in atmel_ac97c_probe() ALSA: x86: fix error return code in hdmi_lpe_audio_probe() ASoC: Intel: Skylake: Add support to read firmware registers ASoC: Intel: Skylake: Add sram address to sst_addr structure ASoC: Intel: Skylake: Debugfs facility to dump module config ASoC: Intel: Skylake: Add debugfs support ASoC: fix semicolon.cocci warnings ASoC: rt5645: Add quirk override by module option ASoC: rsnd: make arrays path and cmd_case static const ASoC: audio-graph-card: add widgets and routing for external amplifier support ASoC: audio-graph-card: update bindings for amplifier support ASoC: rt5665: calibration should be done before jack detection ASoC: rsnd: constify dev_pm_ops structures. ASoC: nau8825: change crosstalk-bypass property to bool type ...
Diffstat (limited to 'sound/soc/generic/audio-graph-card.c')
-rw-r--r--sound/soc/generic/audio-graph-card.c338
1 files changed, 338 insertions, 0 deletions
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
new file mode 100644
index 000000000000..105ec3a6e30d
--- /dev/null
+++ b/sound/soc/generic/audio-graph-card.c
@@ -0,0 +1,338 @@
1/*
2 * ASoC audio graph sound card support
3 *
4 * Copyright (C) 2016 Renesas Solutions Corp.
5 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
6 *
7 * based on ${LINUX}/sound/soc/generic/simple-card.c
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13#include <linux/clk.h>
14#include <linux/device.h>
15#include <linux/gpio.h>
16#include <linux/gpio/consumer.h>
17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/of_device.h>
20#include <linux/of_gpio.h>
21#include <linux/of_graph.h>
22#include <linux/platform_device.h>
23#include <linux/string.h>
24#include <sound/jack.h>
25#include <sound/simple_card_utils.h>
26
27struct graph_card_data {
28 struct snd_soc_card snd_card;
29 struct graph_dai_props {
30 struct asoc_simple_dai cpu_dai;
31 struct asoc_simple_dai codec_dai;
32 } *dai_props;
33 struct snd_soc_dai_link *dai_link;
34 struct gpio_desc *pa_gpio;
35};
36
37static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w,
38 struct snd_kcontrol *kcontrol,
39 int event)
40{
41 struct snd_soc_dapm_context *dapm = w->dapm;
42 struct graph_card_data *priv = snd_soc_card_get_drvdata(dapm->card);
43
44 switch (event) {
45 case SND_SOC_DAPM_POST_PMU:
46 gpiod_set_value_cansleep(priv->pa_gpio, 1);
47 break;
48 case SND_SOC_DAPM_PRE_PMD:
49 gpiod_set_value_cansleep(priv->pa_gpio, 0);
50 break;
51 default:
52 return -EINVAL;
53 }
54
55 return 0;
56}
57
58static const struct snd_soc_dapm_widget asoc_graph_card_dapm_widgets[] = {
59 SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM,
60 0, 0, NULL, 0, asoc_graph_card_outdrv_event,
61 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
62};
63
64#define graph_priv_to_card(priv) (&(priv)->snd_card)
65#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i))
66#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev)
67#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i))
68
69static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
70{
71 struct snd_soc_pcm_runtime *rtd = substream->private_data;
72 struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
73 struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
74 int ret;
75
76 ret = asoc_simple_card_clk_enable(&dai_props->cpu_dai);
77 if (ret)
78 return ret;
79
80 ret = asoc_simple_card_clk_enable(&dai_props->codec_dai);
81 if (ret)
82 asoc_simple_card_clk_disable(&dai_props->cpu_dai);
83
84 return ret;
85}
86
87static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
88{
89 struct snd_soc_pcm_runtime *rtd = substream->private_data;
90 struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
91 struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
92
93 asoc_simple_card_clk_disable(&dai_props->cpu_dai);
94
95 asoc_simple_card_clk_disable(&dai_props->codec_dai);
96}
97
98static struct snd_soc_ops asoc_graph_card_ops = {
99 .startup = asoc_graph_card_startup,
100 .shutdown = asoc_graph_card_shutdown,
101};
102
103static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd)
104{
105 struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
106 struct snd_soc_dai *codec = rtd->codec_dai;
107 struct snd_soc_dai *cpu = rtd->cpu_dai;
108 struct graph_dai_props *dai_props =
109 graph_priv_to_props(priv, rtd->num);
110 int ret;
111
112 ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai);
113 if (ret < 0)
114 return ret;
115
116 ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai);
117 if (ret < 0)
118 return ret;
119
120 return 0;
121}
122
123static int asoc_graph_card_dai_link_of(struct device_node *cpu_port,
124 struct graph_card_data *priv,
125 int idx)
126{
127 struct device *dev = graph_priv_to_dev(priv);
128 struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx);
129 struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx);
130 struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
131 struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
132 struct device_node *cpu_ep = of_get_next_child(cpu_port, NULL);
133 struct device_node *codec_ep = of_graph_get_remote_endpoint(cpu_ep);
134 struct device_node *rcpu_ep = of_graph_get_remote_endpoint(codec_ep);
135 int ret;
136
137 if (rcpu_ep != cpu_ep) {
138 dev_err(dev, "remote-endpoint mismatch (%s/%s/%s)\n",
139 cpu_ep->name, codec_ep->name, rcpu_ep->name);
140 ret = -EINVAL;
141 goto dai_link_of_err;
142 }
143
144 ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep,
145 NULL, &dai_link->dai_fmt);
146 if (ret < 0)
147 goto dai_link_of_err;
148
149 /*
150 * we need to consider "mclk-fs" around here
151 * see simple-card
152 */
153
154 ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link);
155 if (ret < 0)
156 goto dai_link_of_err;
157
158 ret = asoc_simple_card_parse_graph_codec(codec_ep, dai_link);
159 if (ret < 0)
160 goto dai_link_of_err;
161
162 ret = asoc_simple_card_of_parse_tdm(cpu_ep, cpu_dai);
163 if (ret < 0)
164 goto dai_link_of_err;
165
166 ret = asoc_simple_card_of_parse_tdm(codec_ep, codec_dai);
167 if (ret < 0)
168 goto dai_link_of_err;
169
170 ret = asoc_simple_card_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai);
171 if (ret < 0)
172 goto dai_link_of_err;
173
174 ret = asoc_simple_card_parse_clk_codec(dev, codec_ep, dai_link, codec_dai);
175 if (ret < 0)
176 goto dai_link_of_err;
177
178 ret = asoc_simple_card_canonicalize_dailink(dai_link);
179 if (ret < 0)
180 goto dai_link_of_err;
181
182 ret = asoc_simple_card_set_dailink_name(dev, dai_link,
183 "%s-%s",
184 dai_link->cpu_dai_name,
185 dai_link->codec_dai_name);
186 if (ret < 0)
187 goto dai_link_of_err;
188
189 dai_link->ops = &asoc_graph_card_ops;
190 dai_link->init = asoc_graph_card_dai_init;
191
192 asoc_simple_card_canonicalize_cpu(dai_link,
193 of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1);
194
195dai_link_of_err:
196 of_node_put(cpu_ep);
197 of_node_put(rcpu_ep);
198 of_node_put(codec_ep);
199
200 return ret;
201}
202
203static int asoc_graph_card_parse_of(struct graph_card_data *priv)
204{
205 struct of_phandle_iterator it;
206 struct device *dev = graph_priv_to_dev(priv);
207 struct snd_soc_card *card = graph_priv_to_card(priv);
208 struct device_node *node = dev->of_node;
209 int rc, idx = 0;
210 int ret;
211
212 ret = asoc_simple_card_of_parse_widgets(card, NULL);
213 if (ret < 0)
214 return ret;
215
216 ret = asoc_simple_card_of_parse_routing(card, NULL, 1);
217 if (ret < 0)
218 return ret;
219
220 /*
221 * we need to consider "mclk-fs" around here
222 * see simple-card
223 */
224
225 of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
226 ret = asoc_graph_card_dai_link_of(it.node, priv, idx++);
227 of_node_put(it.node);
228 if (ret < 0)
229 return ret;
230 }
231
232 return asoc_simple_card_parse_card_name(card, NULL);
233}
234
235static int asoc_graph_get_dais_count(struct device *dev)
236{
237 struct of_phandle_iterator it;
238 struct device_node *node = dev->of_node;
239 int count = 0;
240 int rc;
241
242 of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
243 count++;
244 of_node_put(it.node);
245 }
246
247 return count;
248}
249
250static int asoc_graph_card_probe(struct platform_device *pdev)
251{
252 struct graph_card_data *priv;
253 struct snd_soc_dai_link *dai_link;
254 struct graph_dai_props *dai_props;
255 struct device *dev = &pdev->dev;
256 struct snd_soc_card *card;
257 int num, ret;
258
259 /* Allocate the private data and the DAI link array */
260 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
261 if (!priv)
262 return -ENOMEM;
263
264 num = asoc_graph_get_dais_count(dev);
265 if (num == 0)
266 return -EINVAL;
267
268 dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL);
269 dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL);
270 if (!dai_props || !dai_link)
271 return -ENOMEM;
272
273 priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
274 if (IS_ERR(priv->pa_gpio)) {
275 ret = PTR_ERR(priv->pa_gpio);
276 dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
277 return ret;
278 }
279
280 priv->dai_props = dai_props;
281 priv->dai_link = dai_link;
282
283 /* Init snd_soc_card */
284 card = graph_priv_to_card(priv);
285 card->owner = THIS_MODULE;
286 card->dev = dev;
287 card->dai_link = dai_link;
288 card->num_links = num;
289 card->dapm_widgets = asoc_graph_card_dapm_widgets;
290 card->num_dapm_widgets = ARRAY_SIZE(asoc_graph_card_dapm_widgets);
291
292 ret = asoc_graph_card_parse_of(priv);
293 if (ret < 0) {
294 if (ret != -EPROBE_DEFER)
295 dev_err(dev, "parse error %d\n", ret);
296 goto err;
297 }
298
299 snd_soc_card_set_drvdata(card, priv);
300
301 ret = devm_snd_soc_register_card(dev, card);
302 if (ret < 0)
303 goto err;
304
305 return 0;
306err:
307 asoc_simple_card_clean_reference(card);
308
309 return ret;
310}
311
312static int asoc_graph_card_remove(struct platform_device *pdev)
313{
314 struct snd_soc_card *card = platform_get_drvdata(pdev);
315
316 return asoc_simple_card_clean_reference(card);
317}
318
319static const struct of_device_id asoc_graph_of_match[] = {
320 { .compatible = "audio-graph-card", },
321 {},
322};
323MODULE_DEVICE_TABLE(of, asoc_graph_of_match);
324
325static struct platform_driver asoc_graph_card = {
326 .driver = {
327 .name = "asoc-audio-graph-card",
328 .of_match_table = asoc_graph_of_match,
329 },
330 .probe = asoc_graph_card_probe,
331 .remove = asoc_graph_card_remove,
332};
333module_platform_driver(asoc_graph_card);
334
335MODULE_ALIAS("platform:asoc-audio-graph-card");
336MODULE_LICENSE("GPL v2");
337MODULE_DESCRIPTION("ASoC Audio Graph Sound Card");
338MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");