1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) 2 // 3 // This file is provided under a dual BSD/GPLv2 license. When using or 4 // redistributing this file, you may do so under either license. 5 // 6 // Copyright(c) 2018 Intel Corporation. All rights reserved. 7 // 8 // Authors: Keyon Jie <yang.jie@linux.intel.com> 9 // 10 11 #include <sound/pcm_params.h> 12 #include <sound/hdaudio_ext.h> 13 #include "../sof-priv.h" 14 #include "hda.h" 15 16 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) 17 18 struct hda_pipe_params { 19 u8 host_dma_id; 20 u8 link_dma_id; 21 u32 ch; 22 u32 s_freq; 23 u32 s_fmt; 24 u8 linktype; 25 snd_pcm_format_t format; 26 int link_index; 27 int stream; 28 unsigned int host_bps; 29 unsigned int link_bps; 30 }; 31 32 /* 33 * Unlike GP dma, there is a set of stream registers in hda controller 34 * to control the link dma channels. Each register controls one link 35 * dma channel and the relation is fixed. To make sure FW uses correct 36 * link dma channels, host allocates stream registers and sends the 37 * corresponding link dma channels to FW to allocate link dma channel 38 * 39 * FIXME: this API is abused in the sense that tx_num and rx_num are 40 * passed as arguments, not returned. We need to find a better way to 41 * retrieve the stream tag allocated for the link DMA 42 */ 43 static int hda_link_dma_get_channels(struct snd_soc_dai *dai, 44 unsigned int *tx_num, 45 unsigned int *tx_slot, 46 unsigned int *rx_num, 47 unsigned int *rx_slot) 48 { 49 struct hdac_bus *bus; 50 struct hdac_ext_stream *stream; 51 struct snd_pcm_substream substream; 52 struct snd_sof_dev *sdev = 53 snd_soc_component_get_drvdata(dai->component); 54 55 bus = sof_to_bus(sdev); 56 57 memset(&substream, 0, sizeof(substream)); 58 if (*tx_num == 1) { 59 substream.stream = SNDRV_PCM_STREAM_PLAYBACK; 60 stream = snd_hdac_ext_stream_assign(bus, &substream, 61 HDAC_EXT_STREAM_TYPE_LINK); 62 if (!stream) { 63 dev_err(bus->dev, "error: failed to find a free hda ext stream for playback"); 64 return -EBUSY; 65 } 66 67 snd_soc_dai_set_dma_data(dai, &substream, stream); 68 *tx_slot = hdac_stream(stream)->stream_tag - 1; 69 70 dev_dbg(bus->dev, "link dma channel %d for playback", *tx_slot); 71 } 72 73 if (*rx_num == 1) { 74 substream.stream = SNDRV_PCM_STREAM_CAPTURE; 75 stream = snd_hdac_ext_stream_assign(bus, &substream, 76 HDAC_EXT_STREAM_TYPE_LINK); 77 if (!stream) { 78 dev_err(bus->dev, "error: failed to find a free hda ext stream for capture"); 79 return -EBUSY; 80 } 81 82 snd_soc_dai_set_dma_data(dai, &substream, stream); 83 *rx_slot = hdac_stream(stream)->stream_tag - 1; 84 85 dev_dbg(bus->dev, "link dma channel %d for capture", *rx_slot); 86 } 87 88 return 0; 89 } 90 91 static int hda_link_dma_params(struct hdac_ext_stream *stream, 92 struct hda_pipe_params *params) 93 { 94 struct hdac_stream *hstream = &stream->hstream; 95 unsigned char stream_tag = hstream->stream_tag; 96 struct hdac_bus *bus = hstream->bus; 97 struct hdac_ext_link *link; 98 unsigned int format_val; 99 100 snd_hdac_ext_stream_decouple(bus, stream, true); 101 snd_hdac_ext_link_stream_reset(stream); 102 103 format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch, 104 params->format, 105 params->link_bps, 0); 106 107 dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n", 108 format_val, params->s_freq, params->ch, params->format); 109 110 snd_hdac_ext_link_stream_setup(stream, format_val); 111 112 if (stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) { 113 list_for_each_entry(link, &bus->hlink_list, list) { 114 if (link->index == params->link_index) 115 snd_hdac_ext_link_set_stream_id(link, 116 stream_tag); 117 } 118 } 119 120 stream->link_prepared = 1; 121 122 return 0; 123 } 124 125 static int hda_link_hw_params(struct snd_pcm_substream *substream, 126 struct snd_pcm_hw_params *params, 127 struct snd_soc_dai *dai) 128 { 129 struct hdac_stream *hstream = substream->runtime->private_data; 130 struct hdac_bus *bus = hstream->bus; 131 struct hdac_ext_stream *link_dev; 132 struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); 133 struct snd_soc_dai *codec_dai = rtd->codec_dai; 134 struct sof_intel_hda_stream *hda_stream; 135 struct hda_pipe_params p_params = {0}; 136 struct hdac_ext_link *link; 137 int stream_tag; 138 139 link_dev = snd_soc_dai_get_dma_data(dai, substream); 140 141 hda_stream = container_of(link_dev, struct sof_intel_hda_stream, 142 hda_stream); 143 hda_stream->hw_params_upon_resume = 0; 144 145 link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name); 146 if (!link) 147 return -EINVAL; 148 149 stream_tag = hdac_stream(link_dev)->stream_tag; 150 151 /* set the stream tag in the codec dai dma params */ 152 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 153 snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); 154 else 155 snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0); 156 157 p_params.s_fmt = snd_pcm_format_width(params_format(params)); 158 p_params.ch = params_channels(params); 159 p_params.s_freq = params_rate(params); 160 p_params.stream = substream->stream; 161 p_params.link_dma_id = stream_tag - 1; 162 p_params.link_index = link->index; 163 p_params.format = params_format(params); 164 165 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 166 p_params.link_bps = codec_dai->driver->playback.sig_bits; 167 else 168 p_params.link_bps = codec_dai->driver->capture.sig_bits; 169 170 return hda_link_dma_params(link_dev, &p_params); 171 } 172 173 static int hda_link_pcm_prepare(struct snd_pcm_substream *substream, 174 struct snd_soc_dai *dai) 175 { 176 struct hdac_ext_stream *link_dev = 177 snd_soc_dai_get_dma_data(dai, substream); 178 struct sof_intel_hda_stream *hda_stream; 179 struct snd_sof_dev *sdev = 180 snd_soc_component_get_drvdata(dai->component); 181 struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); 182 int stream = substream->stream; 183 184 hda_stream = container_of(link_dev, struct sof_intel_hda_stream, 185 hda_stream); 186 187 /* setup hw_params again only if resuming from system suspend */ 188 if (!hda_stream->hw_params_upon_resume) 189 return 0; 190 191 dev_dbg(sdev->dev, "hda: prepare stream dir %d\n", substream->stream); 192 193 return hda_link_hw_params(substream, &rtd->dpcm[stream].hw_params, 194 dai); 195 } 196 197 static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, 198 int cmd, struct snd_soc_dai *dai) 199 { 200 struct hdac_ext_stream *link_dev = 201 snd_soc_dai_get_dma_data(dai, substream); 202 int ret; 203 204 dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd); 205 switch (cmd) { 206 case SNDRV_PCM_TRIGGER_RESUME: 207 /* set up hw_params */ 208 ret = hda_link_pcm_prepare(substream, dai); 209 if (ret < 0) { 210 dev_err(dai->dev, 211 "error: setting up hw_params during resume\n"); 212 return ret; 213 } 214 215 /* fallthrough */ 216 case SNDRV_PCM_TRIGGER_START: 217 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 218 snd_hdac_ext_link_stream_start(link_dev); 219 break; 220 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 221 case SNDRV_PCM_TRIGGER_SUSPEND: 222 case SNDRV_PCM_TRIGGER_STOP: 223 snd_hdac_ext_link_stream_clear(link_dev); 224 break; 225 default: 226 return -EINVAL; 227 } 228 return 0; 229 } 230 231 /* 232 * FIXME: This API is also abused since it's used for two purposes. 233 * when the substream argument is NULL this function is used for cleanups 234 * that aren't necessarily required, and called explicitly by handling 235 * ASoC core structures, which is not recommended. 236 * This part will be reworked in follow-up patches. 237 */ 238 static int hda_link_hw_free(struct snd_pcm_substream *substream, 239 struct snd_soc_dai *dai) 240 { 241 const char *name; 242 unsigned int stream_tag; 243 struct hdac_bus *bus; 244 struct hdac_ext_link *link; 245 struct hdac_stream *hstream; 246 struct hdac_ext_stream *stream; 247 struct snd_soc_pcm_runtime *rtd; 248 struct hdac_ext_stream *link_dev; 249 struct snd_pcm_substream pcm_substream; 250 251 memset(&pcm_substream, 0, sizeof(pcm_substream)); 252 if (substream) { 253 hstream = substream->runtime->private_data; 254 bus = hstream->bus; 255 rtd = snd_pcm_substream_chip(substream); 256 link_dev = snd_soc_dai_get_dma_data(dai, substream); 257 snd_hdac_ext_stream_decouple(bus, link_dev, false); 258 name = rtd->codec_dai->component->name; 259 link = snd_hdac_ext_bus_get_link(bus, name); 260 if (!link) 261 return -EINVAL; 262 263 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 264 stream_tag = hdac_stream(link_dev)->stream_tag; 265 snd_hdac_ext_link_clear_stream_id(link, stream_tag); 266 } 267 268 link_dev->link_prepared = 0; 269 } else { 270 /* release all hda streams when dai link is unloaded */ 271 pcm_substream.stream = SNDRV_PCM_STREAM_PLAYBACK; 272 stream = snd_soc_dai_get_dma_data(dai, &pcm_substream); 273 if (stream) { 274 snd_soc_dai_set_dma_data(dai, &pcm_substream, NULL); 275 snd_hdac_ext_stream_release(stream, 276 HDAC_EXT_STREAM_TYPE_LINK); 277 } 278 279 pcm_substream.stream = SNDRV_PCM_STREAM_CAPTURE; 280 stream = snd_soc_dai_get_dma_data(dai, &pcm_substream); 281 if (stream) { 282 snd_soc_dai_set_dma_data(dai, &pcm_substream, NULL); 283 snd_hdac_ext_stream_release(stream, 284 HDAC_EXT_STREAM_TYPE_LINK); 285 } 286 } 287 288 return 0; 289 } 290 291 static const struct snd_soc_dai_ops hda_link_dai_ops = { 292 .hw_params = hda_link_hw_params, 293 .hw_free = hda_link_hw_free, 294 .trigger = hda_link_pcm_trigger, 295 .prepare = hda_link_pcm_prepare, 296 .get_channel_map = hda_link_dma_get_channels, 297 }; 298 #endif 299 300 /* 301 * common dai driver for skl+ platforms. 302 * some products who use this DAI array only physically have a subset of 303 * the DAIs, but no harm is done here by adding the whole set. 304 */ 305 struct snd_soc_dai_driver skl_dai[] = { 306 { 307 .name = "SSP0 Pin", 308 }, 309 { 310 .name = "SSP1 Pin", 311 }, 312 { 313 .name = "SSP2 Pin", 314 }, 315 { 316 .name = "SSP3 Pin", 317 }, 318 { 319 .name = "SSP4 Pin", 320 }, 321 { 322 .name = "SSP5 Pin", 323 }, 324 { 325 .name = "DMIC01 Pin", 326 }, 327 { 328 .name = "DMIC16k Pin", 329 }, 330 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) 331 { 332 .name = "iDisp1 Pin", 333 .ops = &hda_link_dai_ops, 334 }, 335 { 336 .name = "iDisp2 Pin", 337 .ops = &hda_link_dai_ops, 338 }, 339 { 340 .name = "iDisp3 Pin", 341 .ops = &hda_link_dai_ops, 342 }, 343 { 344 .name = "Analog CPU DAI", 345 .ops = &hda_link_dai_ops, 346 }, 347 { 348 .name = "Digital CPU DAI", 349 .ops = &hda_link_dai_ops, 350 }, 351 { 352 .name = "Alt Analog CPU DAI", 353 .ops = &hda_link_dai_ops, 354 }, 355 #endif 356 }; 357