1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // soc-dai.c 4 // 5 // Copyright (C) 2019 Renesas Electronics Corp. 6 // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 7 // 8 9 #include <sound/soc.h> 10 #include <sound/soc-dai.h> 11 12 /** 13 * snd_soc_dai_set_sysclk - configure DAI system or master clock. 14 * @dai: DAI 15 * @clk_id: DAI specific clock ID 16 * @freq: new clock frequency in Hz 17 * @dir: new clock direction - input/output. 18 * 19 * Configures the DAI master (MCLK) or system (SYSCLK) clocking. 20 */ 21 int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, 22 unsigned int freq, int dir) 23 { 24 if (dai->driver->ops->set_sysclk) 25 return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); 26 27 return snd_soc_component_set_sysclk(dai->component, clk_id, 0, 28 freq, dir); 29 } 30 EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); 31 32 /** 33 * snd_soc_dai_set_clkdiv - configure DAI clock dividers. 34 * @dai: DAI 35 * @div_id: DAI specific clock divider ID 36 * @div: new clock divisor. 37 * 38 * Configures the clock dividers. This is used to derive the best DAI bit and 39 * frame clocks from the system or master clock. It's best to set the DAI bit 40 * and frame clocks as low as possible to save system power. 41 */ 42 int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, 43 int div_id, int div) 44 { 45 if (dai->driver->ops->set_clkdiv) 46 return dai->driver->ops->set_clkdiv(dai, div_id, div); 47 else 48 return -EINVAL; 49 } 50 EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv); 51 52 /** 53 * snd_soc_dai_set_pll - configure DAI PLL. 54 * @dai: DAI 55 * @pll_id: DAI specific PLL ID 56 * @source: DAI specific source for the PLL 57 * @freq_in: PLL input clock frequency in Hz 58 * @freq_out: requested PLL output clock frequency in Hz 59 * 60 * Configures and enables PLL to generate output clock based on input clock. 61 */ 62 int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, 63 unsigned int freq_in, unsigned int freq_out) 64 { 65 if (dai->driver->ops->set_pll) 66 return dai->driver->ops->set_pll(dai, pll_id, source, 67 freq_in, freq_out); 68 69 return snd_soc_component_set_pll(dai->component, pll_id, source, 70 freq_in, freq_out); 71 } 72 EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); 73 74 /** 75 * snd_soc_dai_set_bclk_ratio - configure BCLK to sample rate ratio. 76 * @dai: DAI 77 * @ratio: Ratio of BCLK to Sample rate. 78 * 79 * Configures the DAI for a preset BCLK to sample rate ratio. 80 */ 81 int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) 82 { 83 if (dai->driver->ops->set_bclk_ratio) 84 return dai->driver->ops->set_bclk_ratio(dai, ratio); 85 else 86 return -EINVAL; 87 } 88 EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); 89 90 /** 91 * snd_soc_dai_set_fmt - configure DAI hardware audio format. 92 * @dai: DAI 93 * @fmt: SND_SOC_DAIFMT_* format value. 94 * 95 * Configures the DAI hardware format and clocking. 96 */ 97 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 98 { 99 if (dai->driver->ops->set_fmt == NULL) 100 return -ENOTSUPP; 101 return dai->driver->ops->set_fmt(dai, fmt); 102 } 103 EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt); 104 105 /** 106 * snd_soc_xlate_tdm_slot - generate tx/rx slot mask. 107 * @slots: Number of slots in use. 108 * @tx_mask: bitmask representing active TX slots. 109 * @rx_mask: bitmask representing active RX slots. 110 * 111 * Generates the TDM tx and rx slot default masks for DAI. 112 */ 113 static int snd_soc_xlate_tdm_slot_mask(unsigned int slots, 114 unsigned int *tx_mask, 115 unsigned int *rx_mask) 116 { 117 if (*tx_mask || *rx_mask) 118 return 0; 119 120 if (!slots) 121 return -EINVAL; 122 123 *tx_mask = (1 << slots) - 1; 124 *rx_mask = (1 << slots) - 1; 125 126 return 0; 127 } 128 129 /** 130 * snd_soc_dai_set_tdm_slot() - Configures a DAI for TDM operation 131 * @dai: The DAI to configure 132 * @tx_mask: bitmask representing active TX slots. 133 * @rx_mask: bitmask representing active RX slots. 134 * @slots: Number of slots in use. 135 * @slot_width: Width in bits for each slot. 136 * 137 * This function configures the specified DAI for TDM operation. @slot contains 138 * the total number of slots of the TDM stream and @slot_with the width of each 139 * slot in bit clock cycles. @tx_mask and @rx_mask are bitmasks specifying the 140 * active slots of the TDM stream for the specified DAI, i.e. which slots the 141 * DAI should write to or read from. If a bit is set the corresponding slot is 142 * active, if a bit is cleared the corresponding slot is inactive. Bit 0 maps to 143 * the first slot, bit 1 to the second slot and so on. The first active slot 144 * maps to the first channel of the DAI, the second active slot to the second 145 * channel and so on. 146 * 147 * TDM mode can be disabled by passing 0 for @slots. In this case @tx_mask, 148 * @rx_mask and @slot_width will be ignored. 149 * 150 * Returns 0 on success, a negative error code otherwise. 151 */ 152 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, 153 unsigned int tx_mask, unsigned int rx_mask, 154 int slots, int slot_width) 155 { 156 if (dai->driver->ops->xlate_tdm_slot_mask) 157 dai->driver->ops->xlate_tdm_slot_mask(slots, 158 &tx_mask, &rx_mask); 159 else 160 snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); 161 162 dai->tx_mask = tx_mask; 163 dai->rx_mask = rx_mask; 164 165 if (dai->driver->ops->set_tdm_slot) 166 return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask, 167 slots, slot_width); 168 else 169 return -ENOTSUPP; 170 } 171 EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); 172 173 /** 174 * snd_soc_dai_set_channel_map - configure DAI audio channel map 175 * @dai: DAI 176 * @tx_num: how many TX channels 177 * @tx_slot: pointer to an array which imply the TX slot number channel 178 * 0~num-1 uses 179 * @rx_num: how many RX channels 180 * @rx_slot: pointer to an array which imply the RX slot number channel 181 * 0~num-1 uses 182 * 183 * configure the relationship between channel number and TDM slot number. 184 */ 185 int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, 186 unsigned int tx_num, unsigned int *tx_slot, 187 unsigned int rx_num, unsigned int *rx_slot) 188 { 189 if (dai->driver->ops->set_channel_map) 190 return dai->driver->ops->set_channel_map(dai, tx_num, tx_slot, 191 rx_num, rx_slot); 192 else 193 return -ENOTSUPP; 194 } 195 EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map); 196 197 /** 198 * snd_soc_dai_get_channel_map - Get DAI audio channel map 199 * @dai: DAI 200 * @tx_num: how many TX channels 201 * @tx_slot: pointer to an array which imply the TX slot number channel 202 * 0~num-1 uses 203 * @rx_num: how many RX channels 204 * @rx_slot: pointer to an array which imply the RX slot number channel 205 * 0~num-1 uses 206 */ 207 int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai, 208 unsigned int *tx_num, unsigned int *tx_slot, 209 unsigned int *rx_num, unsigned int *rx_slot) 210 { 211 if (dai->driver->ops->get_channel_map) 212 return dai->driver->ops->get_channel_map(dai, tx_num, tx_slot, 213 rx_num, rx_slot); 214 else 215 return -ENOTSUPP; 216 } 217 EXPORT_SYMBOL_GPL(snd_soc_dai_get_channel_map); 218 219 /** 220 * snd_soc_dai_set_tristate - configure DAI system or master clock. 221 * @dai: DAI 222 * @tristate: tristate enable 223 * 224 * Tristates the DAI so that others can use it. 225 */ 226 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate) 227 { 228 if (dai->driver->ops->set_tristate) 229 return dai->driver->ops->set_tristate(dai, tristate); 230 else 231 return -EINVAL; 232 } 233 EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate); 234 235 /** 236 * snd_soc_dai_digital_mute - configure DAI system or master clock. 237 * @dai: DAI 238 * @mute: mute enable 239 * @direction: stream to mute 240 * 241 * Mutes the DAI DAC. 242 */ 243 int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, 244 int direction) 245 { 246 if (dai->driver->ops->mute_stream) 247 return dai->driver->ops->mute_stream(dai, mute, direction); 248 else if (direction == SNDRV_PCM_STREAM_PLAYBACK && 249 dai->driver->ops->digital_mute) 250 return dai->driver->ops->digital_mute(dai, mute); 251 else 252 return -ENOTSUPP; 253 } 254 EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); 255 256 int snd_soc_dai_hw_params(struct snd_soc_dai *dai, 257 struct snd_pcm_substream *substream, 258 struct snd_pcm_hw_params *params) 259 { 260 struct snd_soc_pcm_runtime *rtd = substream->private_data; 261 int ret; 262 263 /* perform any topology hw_params fixups before DAI */ 264 if (rtd->dai_link->be_hw_params_fixup) { 265 ret = rtd->dai_link->be_hw_params_fixup(rtd, params); 266 if (ret < 0) { 267 dev_err(rtd->dev, 268 "ASoC: hw_params topology fixup failed %d\n", 269 ret); 270 return ret; 271 } 272 } 273 274 if (dai->driver->ops->hw_params) { 275 ret = dai->driver->ops->hw_params(substream, params, dai); 276 if (ret < 0) { 277 dev_err(dai->dev, "ASoC: can't set %s hw params: %d\n", 278 dai->name, ret); 279 return ret; 280 } 281 } 282 283 return 0; 284 } 285 286 void snd_soc_dai_hw_free(struct snd_soc_dai *dai, 287 struct snd_pcm_substream *substream) 288 { 289 if (dai->driver->ops->hw_free) 290 dai->driver->ops->hw_free(substream, dai); 291 } 292 293 int snd_soc_dai_startup(struct snd_soc_dai *dai, 294 struct snd_pcm_substream *substream) 295 { 296 int ret = 0; 297 298 if (dai->driver->ops->startup) 299 ret = dai->driver->ops->startup(substream, dai); 300 301 return ret; 302 } 303 304 void snd_soc_dai_shutdown(struct snd_soc_dai *dai, 305 struct snd_pcm_substream *substream) 306 { 307 if (dai->driver->ops->shutdown) 308 dai->driver->ops->shutdown(substream, dai); 309 } 310 311 int snd_soc_dai_prepare(struct snd_soc_dai *dai, 312 struct snd_pcm_substream *substream) 313 { 314 int ret = 0; 315 316 if (dai->driver->ops->prepare) 317 ret = dai->driver->ops->prepare(substream, dai); 318 319 return ret; 320 } 321 322 int snd_soc_dai_trigger(struct snd_soc_dai *dai, 323 struct snd_pcm_substream *substream, 324 int cmd) 325 { 326 int ret = 0; 327 328 if (dai->driver->ops->trigger) 329 ret = dai->driver->ops->trigger(substream, cmd, dai); 330 331 return ret; 332 } 333 334 int snd_soc_dai_bespoke_trigger(struct snd_soc_dai *dai, 335 struct snd_pcm_substream *substream, 336 int cmd) 337 { 338 int ret = 0; 339 340 if (dai->driver->ops->bespoke_trigger) 341 ret = dai->driver->ops->bespoke_trigger(substream, cmd, dai); 342 343 return ret; 344 } 345 346 snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai, 347 struct snd_pcm_substream *substream) 348 { 349 int delay = 0; 350 351 if (dai->driver->ops->delay) 352 delay = dai->driver->ops->delay(substream, dai); 353 354 return delay; 355 } 356 357 int snd_soc_dai_probe(struct snd_soc_dai *dai) 358 { 359 if (dai->driver->probe) 360 return dai->driver->probe(dai); 361 return 0; 362 } 363 364 int snd_soc_dai_remove(struct snd_soc_dai *dai) 365 { 366 if (dai->driver->remove) 367 return dai->driver->remove(dai); 368 return 0; 369 } 370 371 int snd_soc_dai_compress_new(struct snd_soc_dai *dai, 372 struct snd_soc_pcm_runtime *rtd, int num) 373 { 374 if (dai->driver->compress_new) 375 return dai->driver->compress_new(rtd, num); 376 return -ENOTSUPP; 377 } 378 379 /* 380 * snd_soc_dai_stream_valid() - check if a DAI supports the given stream 381 * 382 * Returns true if the DAI supports the indicated stream type. 383 */ 384 bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int dir) 385 { 386 struct snd_soc_pcm_stream *stream = snd_soc_dai_get_pcm_stream(dai, dir); 387 388 /* If the codec specifies any channels at all, it supports the stream */ 389 return stream->channels_min; 390 } 391