1  // SPDX-License-Identifier: GPL-2.0
2  //
3  // soc-link.c
4  //
5  // Copyright (C) 2019 Renesas Electronics Corp.
6  // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
7  //
8  #include <sound/soc.h>
9  #include <sound/soc-link.h>
10  
11  #define soc_link_ret(rtd, ret) _soc_link_ret(rtd, __func__, ret)
_soc_link_ret(struct snd_soc_pcm_runtime * rtd,const char * func,int ret)12  static inline int _soc_link_ret(struct snd_soc_pcm_runtime *rtd,
13  				const char *func, int ret)
14  {
15  	/* Positive, Zero values are not errors */
16  	if (ret >= 0)
17  		return ret;
18  
19  	/* Negative values might be errors */
20  	switch (ret) {
21  	case -EPROBE_DEFER:
22  	case -ENOTSUPP:
23  		break;
24  	default:
25  		dev_err(rtd->dev,
26  			"ASoC: error at %s on %s: %d\n",
27  			func, rtd->dai_link->name, ret);
28  	}
29  
30  	return ret;
31  }
32  
33  /*
34   * We might want to check substream by using list.
35   * In such case, we can update these macros.
36   */
37  #define soc_link_mark_push(rtd, substream, tgt)		((rtd)->mark_##tgt = substream)
38  #define soc_link_mark_pop(rtd, substream, tgt)		((rtd)->mark_##tgt = NULL)
39  #define soc_link_mark_match(rtd, substream, tgt)	((rtd)->mark_##tgt == substream)
40  
snd_soc_link_init(struct snd_soc_pcm_runtime * rtd)41  int snd_soc_link_init(struct snd_soc_pcm_runtime *rtd)
42  {
43  	int ret = 0;
44  
45  	if (rtd->dai_link->init)
46  		ret = rtd->dai_link->init(rtd);
47  
48  	return soc_link_ret(rtd, ret);
49  }
50  
snd_soc_link_exit(struct snd_soc_pcm_runtime * rtd)51  void snd_soc_link_exit(struct snd_soc_pcm_runtime *rtd)
52  {
53  	if (rtd->dai_link->exit)
54  		rtd->dai_link->exit(rtd);
55  }
56  
snd_soc_link_be_hw_params_fixup(struct snd_soc_pcm_runtime * rtd,struct snd_pcm_hw_params * params)57  int snd_soc_link_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
58  				    struct snd_pcm_hw_params *params)
59  {
60  	int ret = 0;
61  
62  	if (rtd->dai_link->be_hw_params_fixup)
63  		ret = rtd->dai_link->be_hw_params_fixup(rtd, params);
64  
65  	return soc_link_ret(rtd, ret);
66  }
67  
snd_soc_link_startup(struct snd_pcm_substream * substream)68  int snd_soc_link_startup(struct snd_pcm_substream *substream)
69  {
70  	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
71  	int ret = 0;
72  
73  	if (rtd->dai_link->ops &&
74  	    rtd->dai_link->ops->startup)
75  		ret = rtd->dai_link->ops->startup(substream);
76  
77  	/* mark substream if succeeded */
78  	if (ret == 0)
79  		soc_link_mark_push(rtd, substream, startup);
80  
81  	return soc_link_ret(rtd, ret);
82  }
83  
snd_soc_link_shutdown(struct snd_pcm_substream * substream,int rollback)84  void snd_soc_link_shutdown(struct snd_pcm_substream *substream,
85  			   int rollback)
86  {
87  	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
88  
89  	if (rollback && !soc_link_mark_match(rtd, substream, startup))
90  		return;
91  
92  	if (rtd->dai_link->ops &&
93  	    rtd->dai_link->ops->shutdown)
94  		rtd->dai_link->ops->shutdown(substream);
95  
96  	/* remove marked substream */
97  	soc_link_mark_pop(rtd, substream, startup);
98  }
99  
snd_soc_link_prepare(struct snd_pcm_substream * substream)100  int snd_soc_link_prepare(struct snd_pcm_substream *substream)
101  {
102  	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
103  	int ret = 0;
104  
105  	if (rtd->dai_link->ops &&
106  	    rtd->dai_link->ops->prepare)
107  		ret = rtd->dai_link->ops->prepare(substream);
108  
109  	return soc_link_ret(rtd, ret);
110  }
111  
snd_soc_link_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)112  int snd_soc_link_hw_params(struct snd_pcm_substream *substream,
113  			   struct snd_pcm_hw_params *params)
114  {
115  	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
116  	int ret = 0;
117  
118  	if (rtd->dai_link->ops &&
119  	    rtd->dai_link->ops->hw_params)
120  		ret = rtd->dai_link->ops->hw_params(substream, params);
121  
122  	/* mark substream if succeeded */
123  	if (ret == 0)
124  		soc_link_mark_push(rtd, substream, hw_params);
125  
126  	return soc_link_ret(rtd, ret);
127  }
128  
snd_soc_link_hw_free(struct snd_pcm_substream * substream,int rollback)129  void snd_soc_link_hw_free(struct snd_pcm_substream *substream, int rollback)
130  {
131  	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
132  
133  	if (rollback && !soc_link_mark_match(rtd, substream, hw_params))
134  		return;
135  
136  	if (rtd->dai_link->ops &&
137  	    rtd->dai_link->ops->hw_free)
138  		rtd->dai_link->ops->hw_free(substream);
139  
140  	/* remove marked substream */
141  	soc_link_mark_pop(rtd, substream, hw_params);
142  }
143  
soc_link_trigger(struct snd_pcm_substream * substream,int cmd)144  static int soc_link_trigger(struct snd_pcm_substream *substream, int cmd)
145  {
146  	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
147  	int ret = 0;
148  
149  	if (rtd->dai_link->ops &&
150  	    rtd->dai_link->ops->trigger)
151  		ret = rtd->dai_link->ops->trigger(substream, cmd);
152  
153  	return soc_link_ret(rtd, ret);
154  }
155  
snd_soc_link_trigger(struct snd_pcm_substream * substream,int cmd,int rollback)156  int snd_soc_link_trigger(struct snd_pcm_substream *substream, int cmd,
157  			 int rollback)
158  {
159  	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
160  	int ret = 0;
161  
162  	switch (cmd) {
163  	case SNDRV_PCM_TRIGGER_START:
164  	case SNDRV_PCM_TRIGGER_RESUME:
165  	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
166  		ret = soc_link_trigger(substream, cmd);
167  		if (ret < 0)
168  			break;
169  		soc_link_mark_push(rtd, substream, trigger);
170  		break;
171  	case SNDRV_PCM_TRIGGER_STOP:
172  	case SNDRV_PCM_TRIGGER_SUSPEND:
173  	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
174  		if (rollback && !soc_link_mark_match(rtd, substream, trigger))
175  			break;
176  
177  		ret = soc_link_trigger(substream, cmd);
178  		soc_link_mark_pop(rtd, substream, startup);
179  	}
180  
181  	return ret;
182  }
183  
snd_soc_link_compr_startup(struct snd_compr_stream * cstream)184  int snd_soc_link_compr_startup(struct snd_compr_stream *cstream)
185  {
186  	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
187  	int ret = 0;
188  
189  	if (rtd->dai_link->compr_ops &&
190  	    rtd->dai_link->compr_ops->startup)
191  		ret = rtd->dai_link->compr_ops->startup(cstream);
192  
193  	if (ret == 0)
194  		soc_link_mark_push(rtd, cstream, compr_startup);
195  
196  	return soc_link_ret(rtd, ret);
197  }
198  EXPORT_SYMBOL_GPL(snd_soc_link_compr_startup);
199  
snd_soc_link_compr_shutdown(struct snd_compr_stream * cstream,int rollback)200  void snd_soc_link_compr_shutdown(struct snd_compr_stream *cstream,
201  				 int rollback)
202  {
203  	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
204  
205  	if (rollback && !soc_link_mark_match(rtd, cstream, compr_startup))
206  		return;
207  
208  	if (rtd->dai_link->compr_ops &&
209  	    rtd->dai_link->compr_ops->shutdown)
210  		rtd->dai_link->compr_ops->shutdown(cstream);
211  
212  	soc_link_mark_pop(rtd, cstream, compr_startup);
213  }
214  EXPORT_SYMBOL_GPL(snd_soc_link_compr_shutdown);
215  
snd_soc_link_compr_set_params(struct snd_compr_stream * cstream)216  int snd_soc_link_compr_set_params(struct snd_compr_stream *cstream)
217  {
218  	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
219  	int ret = 0;
220  
221  	if (rtd->dai_link->compr_ops &&
222  	    rtd->dai_link->compr_ops->set_params)
223  		ret = rtd->dai_link->compr_ops->set_params(cstream);
224  
225  	return soc_link_ret(rtd, ret);
226  }
227  EXPORT_SYMBOL_GPL(snd_soc_link_compr_set_params);
228