1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Copyright (c) 2019 BayLibre, SAS. |
4 | // Author: Jerome Brunet <jbrunet@baylibre.com> |
5 | |
6 | #include <linux/module.h> |
7 | #include <sound/pcm_params.h> |
8 | #include <sound/soc.h> |
9 | #include <sound/soc-dai.h> |
10 | |
11 | #include "meson-codec-glue.h" |
12 | |
13 | static struct snd_soc_dapm_widget * |
14 | meson_codec_glue_get_input(struct snd_soc_dapm_widget *w) |
15 | { |
16 | struct snd_soc_dapm_path *p; |
17 | struct snd_soc_dapm_widget *in; |
18 | |
19 | snd_soc_dapm_widget_for_each_source_path(w, p) { |
20 | if (!p->connect) |
21 | continue; |
22 | |
23 | /* Check that we still are in the same component */ |
24 | if (snd_soc_dapm_to_component(dapm: w->dapm) != |
25 | snd_soc_dapm_to_component(dapm: p->source->dapm)) |
26 | continue; |
27 | |
28 | if (p->source->id == snd_soc_dapm_dai_in) |
29 | return p->source; |
30 | |
31 | in = meson_codec_glue_get_input(w: p->source); |
32 | if (in) |
33 | return in; |
34 | } |
35 | |
36 | return NULL; |
37 | } |
38 | |
39 | static void meson_codec_glue_input_set_data(struct snd_soc_dai *dai, |
40 | struct meson_codec_glue_input *data) |
41 | { |
42 | snd_soc_dai_dma_data_set_playback(dai, data); |
43 | } |
44 | |
45 | struct meson_codec_glue_input * |
46 | meson_codec_glue_input_get_data(struct snd_soc_dai *dai) |
47 | { |
48 | return snd_soc_dai_dma_data_get_playback(dai); |
49 | } |
50 | EXPORT_SYMBOL_GPL(meson_codec_glue_input_get_data); |
51 | |
52 | static struct meson_codec_glue_input * |
53 | meson_codec_glue_output_get_input_data(struct snd_soc_dapm_widget *w) |
54 | { |
55 | struct snd_soc_dapm_widget *in = |
56 | meson_codec_glue_get_input(w); |
57 | struct snd_soc_dai *dai; |
58 | |
59 | if (WARN_ON(!in)) |
60 | return NULL; |
61 | |
62 | dai = in->priv; |
63 | |
64 | return meson_codec_glue_input_get_data(dai); |
65 | } |
66 | |
67 | int meson_codec_glue_input_hw_params(struct snd_pcm_substream *substream, |
68 | struct snd_pcm_hw_params *params, |
69 | struct snd_soc_dai *dai) |
70 | { |
71 | struct meson_codec_glue_input *data = |
72 | meson_codec_glue_input_get_data(dai); |
73 | |
74 | data->params.rates = snd_pcm_rate_to_rate_bit(rate: params_rate(p: params)); |
75 | data->params.rate_min = params_rate(p: params); |
76 | data->params.rate_max = params_rate(p: params); |
77 | data->params.formats = 1ULL << (__force int) params_format(p: params); |
78 | data->params.channels_min = params_channels(p: params); |
79 | data->params.channels_max = params_channels(p: params); |
80 | data->params.sig_bits = dai->driver->playback.sig_bits; |
81 | |
82 | return 0; |
83 | } |
84 | EXPORT_SYMBOL_GPL(meson_codec_glue_input_hw_params); |
85 | |
86 | int meson_codec_glue_input_set_fmt(struct snd_soc_dai *dai, |
87 | unsigned int fmt) |
88 | { |
89 | struct meson_codec_glue_input *data = |
90 | meson_codec_glue_input_get_data(dai); |
91 | |
92 | /* Save the source stream format for the downstream link */ |
93 | data->fmt = fmt; |
94 | return 0; |
95 | } |
96 | EXPORT_SYMBOL_GPL(meson_codec_glue_input_set_fmt); |
97 | |
98 | int meson_codec_glue_output_startup(struct snd_pcm_substream *substream, |
99 | struct snd_soc_dai *dai) |
100 | { |
101 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
102 | struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget_capture(dai); |
103 | struct meson_codec_glue_input *in_data = meson_codec_glue_output_get_input_data(w); |
104 | |
105 | if (!in_data) |
106 | return -ENODEV; |
107 | |
108 | if (WARN_ON(!rtd->dai_link->c2c_params)) { |
109 | dev_warn(dai->dev, "codec2codec link expected\n" ); |
110 | return -EINVAL; |
111 | } |
112 | |
113 | /* Replace link params with the input params */ |
114 | rtd->dai_link->c2c_params = &in_data->params; |
115 | rtd->dai_link->num_c2c_params = 1; |
116 | |
117 | return snd_soc_runtime_set_dai_fmt(rtd, dai_fmt: in_data->fmt); |
118 | } |
119 | EXPORT_SYMBOL_GPL(meson_codec_glue_output_startup); |
120 | |
121 | int meson_codec_glue_input_dai_probe(struct snd_soc_dai *dai) |
122 | { |
123 | struct meson_codec_glue_input *data; |
124 | |
125 | data = kzalloc(size: sizeof(*data), GFP_KERNEL); |
126 | if (!data) |
127 | return -ENOMEM; |
128 | |
129 | meson_codec_glue_input_set_data(dai, data); |
130 | return 0; |
131 | } |
132 | EXPORT_SYMBOL_GPL(meson_codec_glue_input_dai_probe); |
133 | |
134 | int meson_codec_glue_input_dai_remove(struct snd_soc_dai *dai) |
135 | { |
136 | struct meson_codec_glue_input *data = |
137 | meson_codec_glue_input_get_data(dai); |
138 | |
139 | kfree(objp: data); |
140 | return 0; |
141 | } |
142 | EXPORT_SYMBOL_GPL(meson_codec_glue_input_dai_remove); |
143 | |
144 | MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>" ); |
145 | MODULE_DESCRIPTION("Amlogic Codec Glue Helpers" ); |
146 | MODULE_LICENSE("GPL v2" ); |
147 | |
148 | |