root/sound/soc/sof/intel/hda-dai.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hda_check_fes
  2. hda_link_stream_assign
  3. hda_link_dma_params
  4. hda_link_config_ipc
  5. hda_link_hw_params
  6. hda_link_pcm_prepare
  7. hda_link_pcm_trigger
  8. hda_link_hw_free

   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  * This function checks if the host dma channel corresponding
  34  * to the link DMA stream_tag argument is assigned to one
  35  * of the FEs connected to the BE DAI.
  36  */
  37 static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
  38                           int dir, int stream_tag)
  39 {
  40         struct snd_pcm_substream *fe_substream;
  41         struct hdac_stream *fe_hstream;
  42         struct snd_soc_dpcm *dpcm;
  43 
  44         for_each_dpcm_fe(rtd, dir, dpcm) {
  45                 fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
  46                 fe_hstream = fe_substream->runtime->private_data;
  47                 if (fe_hstream->stream_tag == stream_tag)
  48                         return true;
  49         }
  50 
  51         return false;
  52 }
  53 
  54 static struct hdac_ext_stream *
  55         hda_link_stream_assign(struct hdac_bus *bus,
  56                                struct snd_pcm_substream *substream)
  57 {
  58         struct snd_soc_pcm_runtime *rtd = substream->private_data;
  59         struct sof_intel_hda_stream *hda_stream;
  60         struct hdac_ext_stream *res = NULL;
  61         struct hdac_stream *stream = NULL;
  62 
  63         int stream_dir = substream->stream;
  64 
  65         if (!bus->ppcap) {
  66                 dev_err(bus->dev, "stream type not supported\n");
  67                 return NULL;
  68         }
  69 
  70         list_for_each_entry(stream, &bus->stream_list, list) {
  71                 struct hdac_ext_stream *hstream =
  72                         stream_to_hdac_ext_stream(stream);
  73                 if (stream->direction != substream->stream)
  74                         continue;
  75 
  76                 hda_stream = hstream_to_sof_hda_stream(hstream);
  77 
  78                 /* check if link is available */
  79                 if (!hstream->link_locked) {
  80                         if (stream->opened) {
  81                                 /*
  82                                  * check if the stream tag matches the stream
  83                                  * tag of one of the connected FEs
  84                                  */
  85                                 if (hda_check_fes(rtd, stream_dir,
  86                                                   stream->stream_tag)) {
  87                                         res = hstream;
  88                                         break;
  89                                 }
  90                         } else {
  91                                 res = hstream;
  92 
  93                                 /*
  94                                  * This must be a hostless stream.
  95                                  * So reserve the host DMA channel.
  96                                  */
  97                                 hda_stream->host_reserved = 1;
  98                                 break;
  99                         }
 100                 }
 101         }
 102 
 103         if (res) {
 104                 /*
 105                  * Decouple host and link DMA. The decoupled flag
 106                  * is updated in snd_hdac_ext_stream_decouple().
 107                  */
 108                 if (!res->decoupled)
 109                         snd_hdac_ext_stream_decouple(bus, res, true);
 110                 spin_lock_irq(&bus->reg_lock);
 111                 res->link_locked = 1;
 112                 res->link_substream = substream;
 113                 spin_unlock_irq(&bus->reg_lock);
 114         }
 115 
 116         return res;
 117 }
 118 
 119 static int hda_link_dma_params(struct hdac_ext_stream *stream,
 120                                struct hda_pipe_params *params)
 121 {
 122         struct hdac_stream *hstream = &stream->hstream;
 123         unsigned char stream_tag = hstream->stream_tag;
 124         struct hdac_bus *bus = hstream->bus;
 125         struct hdac_ext_link *link;
 126         unsigned int format_val;
 127 
 128         snd_hdac_ext_stream_decouple(bus, stream, true);
 129         snd_hdac_ext_link_stream_reset(stream);
 130 
 131         format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch,
 132                                                  params->format,
 133                                                  params->link_bps, 0);
 134 
 135         dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
 136                 format_val, params->s_freq, params->ch, params->format);
 137 
 138         snd_hdac_ext_link_stream_setup(stream, format_val);
 139 
 140         if (stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) {
 141                 list_for_each_entry(link, &bus->hlink_list, list) {
 142                         if (link->index == params->link_index)
 143                                 snd_hdac_ext_link_set_stream_id(link,
 144                                                                 stream_tag);
 145                 }
 146         }
 147 
 148         stream->link_prepared = 1;
 149 
 150         return 0;
 151 }
 152 
 153 /* Send DAI_CONFIG IPC to the DAI that matches the dai_name and direction */
 154 static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream,
 155                                const char *dai_name, int channel, int dir)
 156 {
 157         struct sof_ipc_dai_config *config;
 158         struct snd_sof_dai *sof_dai;
 159         struct sof_ipc_reply reply;
 160         int ret = 0;
 161 
 162         list_for_each_entry(sof_dai, &hda_stream->sdev->dai_list, list) {
 163                 if (!sof_dai->cpu_dai_name)
 164                         continue;
 165 
 166                 if (!strcmp(dai_name, sof_dai->cpu_dai_name) &&
 167                     dir == sof_dai->comp_dai.direction) {
 168                         config = sof_dai->dai_config;
 169 
 170                         if (!config) {
 171                                 dev_err(hda_stream->sdev->dev,
 172                                         "error: no config for DAI %s\n",
 173                                         sof_dai->name);
 174                                 return -EINVAL;
 175                         }
 176 
 177                         /* update config with stream tag */
 178                         config->hda.link_dma_ch = channel;
 179 
 180                         /* send IPC */
 181                         ret = sof_ipc_tx_message(hda_stream->sdev->ipc,
 182                                                  config->hdr.cmd,
 183                                                  config,
 184                                                  config->hdr.size,
 185                                                  &reply, sizeof(reply));
 186 
 187                         if (ret < 0)
 188                                 dev_err(hda_stream->sdev->dev,
 189                                         "error: failed to set dai config for %s\n",
 190                                         sof_dai->name);
 191                         return ret;
 192                 }
 193         }
 194 
 195         return -EINVAL;
 196 }
 197 
 198 static int hda_link_hw_params(struct snd_pcm_substream *substream,
 199                               struct snd_pcm_hw_params *params,
 200                               struct snd_soc_dai *dai)
 201 {
 202         struct hdac_stream *hstream = substream->runtime->private_data;
 203         struct hdac_bus *bus = hstream->bus;
 204         struct hdac_ext_stream *link_dev;
 205         struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
 206         struct snd_soc_dai *codec_dai = rtd->codec_dai;
 207         struct sof_intel_hda_stream *hda_stream;
 208         struct hda_pipe_params p_params = {0};
 209         struct hdac_ext_link *link;
 210         int stream_tag;
 211         int ret;
 212 
 213         /* get stored dma data if resuming from system suspend */
 214         link_dev = snd_soc_dai_get_dma_data(dai, substream);
 215         if (!link_dev) {
 216                 link_dev = hda_link_stream_assign(bus, substream);
 217                 if (!link_dev)
 218                         return -EBUSY;
 219 
 220                 snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev);
 221         }
 222 
 223         stream_tag = hdac_stream(link_dev)->stream_tag;
 224 
 225         hda_stream = hstream_to_sof_hda_stream(link_dev);
 226 
 227         /* update the DSP with the new tag */
 228         ret = hda_link_config_ipc(hda_stream, dai->name, stream_tag - 1,
 229                                   substream->stream);
 230         if (ret < 0)
 231                 return ret;
 232 
 233         link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
 234         if (!link)
 235                 return -EINVAL;
 236 
 237         /* set the stream tag in the codec dai dma params */
 238         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 239                 snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0);
 240         else
 241                 snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0);
 242 
 243         p_params.s_fmt = snd_pcm_format_width(params_format(params));
 244         p_params.ch = params_channels(params);
 245         p_params.s_freq = params_rate(params);
 246         p_params.stream = substream->stream;
 247         p_params.link_dma_id = stream_tag - 1;
 248         p_params.link_index = link->index;
 249         p_params.format = params_format(params);
 250 
 251         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 252                 p_params.link_bps = codec_dai->driver->playback.sig_bits;
 253         else
 254                 p_params.link_bps = codec_dai->driver->capture.sig_bits;
 255 
 256         return hda_link_dma_params(link_dev, &p_params);
 257 }
 258 
 259 static int hda_link_pcm_prepare(struct snd_pcm_substream *substream,
 260                                 struct snd_soc_dai *dai)
 261 {
 262         struct hdac_ext_stream *link_dev =
 263                                 snd_soc_dai_get_dma_data(dai, substream);
 264         struct snd_sof_dev *sdev =
 265                                 snd_soc_component_get_drvdata(dai->component);
 266         struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
 267         int stream = substream->stream;
 268 
 269         if (link_dev->link_prepared)
 270                 return 0;
 271 
 272         dev_dbg(sdev->dev, "hda: prepare stream dir %d\n", substream->stream);
 273 
 274         return hda_link_hw_params(substream, &rtd->dpcm[stream].hw_params,
 275                                   dai);
 276 }
 277 
 278 static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
 279                                 int cmd, struct snd_soc_dai *dai)
 280 {
 281         struct hdac_ext_stream *link_dev =
 282                                 snd_soc_dai_get_dma_data(dai, substream);
 283         struct sof_intel_hda_stream *hda_stream;
 284         struct snd_soc_pcm_runtime *rtd;
 285         struct hdac_ext_link *link;
 286         struct hdac_stream *hstream;
 287         struct hdac_bus *bus;
 288         int stream_tag;
 289         int ret;
 290 
 291         hstream = substream->runtime->private_data;
 292         bus = hstream->bus;
 293         rtd = snd_pcm_substream_chip(substream);
 294 
 295         link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name);
 296         if (!link)
 297                 return -EINVAL;
 298 
 299         hda_stream = hstream_to_sof_hda_stream(link_dev);
 300 
 301         dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
 302         switch (cmd) {
 303         case SNDRV_PCM_TRIGGER_RESUME:
 304                 /* set up hw_params */
 305                 ret = hda_link_pcm_prepare(substream, dai);
 306                 if (ret < 0) {
 307                         dev_err(dai->dev,
 308                                 "error: setting up hw_params during resume\n");
 309                         return ret;
 310                 }
 311 
 312                 /* fallthrough */
 313         case SNDRV_PCM_TRIGGER_START:
 314         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 315                 snd_hdac_ext_link_stream_start(link_dev);
 316                 break;
 317         case SNDRV_PCM_TRIGGER_SUSPEND:
 318         case SNDRV_PCM_TRIGGER_STOP:
 319                 /*
 320                  * clear link DMA channel. It will be assigned when
 321                  * hw_params is set up again after resume.
 322                  */
 323                 ret = hda_link_config_ipc(hda_stream, dai->name,
 324                                           DMA_CHAN_INVALID, substream->stream);
 325                 if (ret < 0)
 326                         return ret;
 327 
 328                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 329                         stream_tag = hdac_stream(link_dev)->stream_tag;
 330                         snd_hdac_ext_link_clear_stream_id(link, stream_tag);
 331                 }
 332 
 333                 link_dev->link_prepared = 0;
 334 
 335                 /* fallthrough */
 336         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 337                 snd_hdac_ext_link_stream_clear(link_dev);
 338                 break;
 339         default:
 340                 return -EINVAL;
 341         }
 342         return 0;
 343 }
 344 
 345 static int hda_link_hw_free(struct snd_pcm_substream *substream,
 346                             struct snd_soc_dai *dai)
 347 {
 348         unsigned int stream_tag;
 349         struct sof_intel_hda_stream *hda_stream;
 350         struct hdac_bus *bus;
 351         struct hdac_ext_link *link;
 352         struct hdac_stream *hstream;
 353         struct snd_soc_pcm_runtime *rtd;
 354         struct hdac_ext_stream *link_dev;
 355         int ret;
 356 
 357         hstream = substream->runtime->private_data;
 358         bus = hstream->bus;
 359         rtd = snd_pcm_substream_chip(substream);
 360         link_dev = snd_soc_dai_get_dma_data(dai, substream);
 361 
 362         if (!link_dev) {
 363                 dev_dbg(dai->dev,
 364                         "%s: link_dev is not assigned\n", __func__);
 365                 return -EINVAL;
 366         }
 367 
 368         hda_stream = hstream_to_sof_hda_stream(link_dev);
 369 
 370         /* free the link DMA channel in the FW */
 371         ret = hda_link_config_ipc(hda_stream, dai->name, DMA_CHAN_INVALID,
 372                                   substream->stream);
 373         if (ret < 0)
 374                 return ret;
 375 
 376         link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name);
 377         if (!link)
 378                 return -EINVAL;
 379 
 380         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 381                 stream_tag = hdac_stream(link_dev)->stream_tag;
 382                 snd_hdac_ext_link_clear_stream_id(link, stream_tag);
 383         }
 384 
 385         snd_soc_dai_set_dma_data(dai, substream, NULL);
 386         snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK);
 387         link_dev->link_prepared = 0;
 388 
 389         /* free the host DMA channel reserved by hostless streams */
 390         hda_stream->host_reserved = 0;
 391 
 392         return 0;
 393 }
 394 
 395 static const struct snd_soc_dai_ops hda_link_dai_ops = {
 396         .hw_params = hda_link_hw_params,
 397         .hw_free = hda_link_hw_free,
 398         .trigger = hda_link_pcm_trigger,
 399         .prepare = hda_link_pcm_prepare,
 400 };
 401 #endif
 402 
 403 /*
 404  * common dai driver for skl+ platforms.
 405  * some products who use this DAI array only physically have a subset of
 406  * the DAIs, but no harm is done here by adding the whole set.
 407  */
 408 struct snd_soc_dai_driver skl_dai[] = {
 409 {
 410         .name = "SSP0 Pin",
 411 },
 412 {
 413         .name = "SSP1 Pin",
 414 },
 415 {
 416         .name = "SSP2 Pin",
 417 },
 418 {
 419         .name = "SSP3 Pin",
 420 },
 421 {
 422         .name = "SSP4 Pin",
 423 },
 424 {
 425         .name = "SSP5 Pin",
 426 },
 427 {
 428         .name = "DMIC01 Pin",
 429 },
 430 {
 431         .name = "DMIC16k Pin",
 432 },
 433 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
 434 {
 435         .name = "iDisp1 Pin",
 436         .ops = &hda_link_dai_ops,
 437 },
 438 {
 439         .name = "iDisp2 Pin",
 440         .ops = &hda_link_dai_ops,
 441 },
 442 {
 443         .name = "iDisp3 Pin",
 444         .ops = &hda_link_dai_ops,
 445 },
 446 {
 447         .name = "iDisp4 Pin",
 448         .ops = &hda_link_dai_ops,
 449 },
 450 {
 451         .name = "Analog CPU DAI",
 452         .ops = &hda_link_dai_ops,
 453 },
 454 {
 455         .name = "Digital CPU DAI",
 456         .ops = &hda_link_dai_ops,
 457 },
 458 {
 459         .name = "Alt Analog CPU DAI",
 460         .ops = &hda_link_dai_ops,
 461 },
 462 #endif
 463 };

/* [<][>][^][v][top][bottom][index][help] */