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

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

DEFINITIONS

This source file includes following definitions.
  1. get_mult_div
  2. get_bits
  3. hda_dsp_pcm_hw_params
  4. hda_dsp_pcm_trigger
  5. hda_dsp_pcm_pointer
  6. hda_dsp_pcm_open
  7. hda_dsp_pcm_close

   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: Liam Girdwood <liam.r.girdwood@linux.intel.com>
   9 //          Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
  10 //          Rander Wang <rander.wang@intel.com>
  11 //          Keyon Jie <yang.jie@linux.intel.com>
  12 //
  13 
  14 /*
  15  * Hardware interface for generic Intel audio DSP HDA IP
  16  */
  17 
  18 #include <sound/hda_register.h>
  19 #include <sound/pcm_params.h>
  20 #include "../ops.h"
  21 #include "hda.h"
  22 
  23 #define SDnFMT_BASE(x)  ((x) << 14)
  24 #define SDnFMT_MULT(x)  (((x) - 1) << 11)
  25 #define SDnFMT_DIV(x)   (((x) - 1) << 8)
  26 #define SDnFMT_BITS(x)  ((x) << 4)
  27 #define SDnFMT_CHAN(x)  ((x) << 0)
  28 
  29 static inline u32 get_mult_div(struct snd_sof_dev *sdev, int rate)
  30 {
  31         switch (rate) {
  32         case 8000:
  33                 return SDnFMT_DIV(6);
  34         case 9600:
  35                 return SDnFMT_DIV(5);
  36         case 11025:
  37                 return SDnFMT_BASE(1) | SDnFMT_DIV(4);
  38         case 16000:
  39                 return SDnFMT_DIV(3);
  40         case 22050:
  41                 return SDnFMT_BASE(1) | SDnFMT_DIV(2);
  42         case 32000:
  43                 return SDnFMT_DIV(3) | SDnFMT_MULT(2);
  44         case 44100:
  45                 return SDnFMT_BASE(1);
  46         case 48000:
  47                 return 0;
  48         case 88200:
  49                 return SDnFMT_BASE(1) | SDnFMT_MULT(2);
  50         case 96000:
  51                 return SDnFMT_MULT(2);
  52         case 176400:
  53                 return SDnFMT_BASE(1) | SDnFMT_MULT(4);
  54         case 192000:
  55                 return SDnFMT_MULT(4);
  56         default:
  57                 dev_warn(sdev->dev, "can't find div rate %d using 48kHz\n",
  58                          rate);
  59                 return 0; /* use 48KHz if not found */
  60         }
  61 };
  62 
  63 static inline u32 get_bits(struct snd_sof_dev *sdev, int sample_bits)
  64 {
  65         switch (sample_bits) {
  66         case 8:
  67                 return SDnFMT_BITS(0);
  68         case 16:
  69                 return SDnFMT_BITS(1);
  70         case 20:
  71                 return SDnFMT_BITS(2);
  72         case 24:
  73                 return SDnFMT_BITS(3);
  74         case 32:
  75                 return SDnFMT_BITS(4);
  76         default:
  77                 dev_warn(sdev->dev, "can't find %d bits using 16bit\n",
  78                          sample_bits);
  79                 return SDnFMT_BITS(1); /* use 16bits format if not found */
  80         }
  81 };
  82 
  83 int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
  84                           struct snd_pcm_substream *substream,
  85                           struct snd_pcm_hw_params *params,
  86                           struct sof_ipc_stream_params *ipc_params)
  87 {
  88         struct hdac_stream *hstream = substream->runtime->private_data;
  89         struct hdac_ext_stream *stream = stream_to_hdac_ext_stream(hstream);
  90         struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
  91         struct snd_dma_buffer *dmab;
  92         int ret;
  93         u32 size, rate, bits;
  94 
  95         size = params_buffer_bytes(params);
  96         rate = get_mult_div(sdev, params_rate(params));
  97         bits = get_bits(sdev, params_width(params));
  98 
  99         hstream->substream = substream;
 100 
 101         dmab = substream->runtime->dma_buffer_p;
 102 
 103         hstream->format_val = rate | bits | (params_channels(params) - 1);
 104         hstream->bufsize = size;
 105         hstream->period_bytes = params_period_bytes(params);
 106         hstream->no_period_wakeup  =
 107                         (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
 108                         (params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
 109 
 110         ret = hda_dsp_stream_hw_params(sdev, stream, dmab, params);
 111         if (ret < 0) {
 112                 dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret);
 113                 return ret;
 114         }
 115 
 116         /* disable SPIB, to enable buffer wrap for stream */
 117         hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0);
 118 
 119         /* set host_period_bytes to 0 if no IPC position */
 120         if (hda && hda->no_ipc_position)
 121                 ipc_params->host_period_bytes = 0;
 122 
 123         ipc_params->stream_tag = hstream->stream_tag;
 124 
 125         return 0;
 126 }
 127 
 128 int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev,
 129                         struct snd_pcm_substream *substream, int cmd)
 130 {
 131         struct hdac_stream *hstream = substream->runtime->private_data;
 132         struct hdac_ext_stream *stream = stream_to_hdac_ext_stream(hstream);
 133 
 134         return hda_dsp_stream_trigger(sdev, stream, cmd);
 135 }
 136 
 137 snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev,
 138                                       struct snd_pcm_substream *substream)
 139 {
 140         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 141         struct hdac_stream *hstream = substream->runtime->private_data;
 142         struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
 143         struct snd_sof_pcm *spcm;
 144         snd_pcm_uframes_t pos;
 145 
 146         spcm = snd_sof_find_spcm_dai(sdev, rtd);
 147         if (!spcm) {
 148                 dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n",
 149                                      rtd->dai_link->id);
 150                 return 0;
 151         }
 152 
 153         if (hda && !hda->no_ipc_position) {
 154                 /* read position from IPC position */
 155                 pos = spcm->stream[substream->stream].posn.host_posn;
 156                 goto found;
 157         }
 158 
 159         /*
 160          * DPIB/posbuf position mode:
 161          * For Playback, Use DPIB register from HDA space which
 162          * reflects the actual data transferred.
 163          * For Capture, Use the position buffer for pointer, as DPIB
 164          * is not accurate enough, its update may be completed
 165          * earlier than the data written to DDR.
 166          */
 167         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 168                 pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
 169                                        AZX_REG_VS_SDXDPIB_XBASE +
 170                                        (AZX_REG_VS_SDXDPIB_XINTERVAL *
 171                                         hstream->index));
 172         } else {
 173                 /*
 174                  * For capture stream, we need more workaround to fix the
 175                  * position incorrect issue:
 176                  *
 177                  * 1. Wait at least 20us before reading position buffer after
 178                  * the interrupt generated(IOC), to make sure position update
 179                  * happens on frame boundary i.e. 20.833uSec for 48KHz.
 180                  * 2. Perform a dummy Read to DPIB register to flush DMA
 181                  * position value.
 182                  * 3. Read the DMA Position from posbuf. Now the readback
 183                  * value should be >= period boundary.
 184                  */
 185                 usleep_range(20, 21);
 186                 snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR,
 187                                  AZX_REG_VS_SDXDPIB_XBASE +
 188                                  (AZX_REG_VS_SDXDPIB_XINTERVAL *
 189                                   hstream->index));
 190                 pos = snd_hdac_stream_get_pos_posbuf(hstream);
 191         }
 192 
 193         if (pos >= hstream->bufsize)
 194                 pos = 0;
 195 
 196 found:
 197         pos = bytes_to_frames(substream->runtime, pos);
 198 
 199         dev_vdbg(sdev->dev, "PCM: stream %d dir %d position %lu\n",
 200                  hstream->index, substream->stream, pos);
 201         return pos;
 202 }
 203 
 204 int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
 205                      struct snd_pcm_substream *substream)
 206 {
 207         struct hdac_ext_stream *dsp_stream;
 208         int direction = substream->stream;
 209 
 210         dsp_stream = hda_dsp_stream_get(sdev, direction);
 211 
 212         if (!dsp_stream) {
 213                 dev_err(sdev->dev, "error: no stream available\n");
 214                 return -ENODEV;
 215         }
 216 
 217         /* binding pcm substream to hda stream */
 218         substream->runtime->private_data = &dsp_stream->hstream;
 219         return 0;
 220 }
 221 
 222 int hda_dsp_pcm_close(struct snd_sof_dev *sdev,
 223                       struct snd_pcm_substream *substream)
 224 {
 225         struct hdac_stream *hstream = substream->runtime->private_data;
 226         int direction = substream->stream;
 227         int ret;
 228 
 229         ret = hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
 230 
 231         if (ret) {
 232                 dev_dbg(sdev->dev, "stream %s not opened!\n", substream->name);
 233                 return -ENODEV;
 234         }
 235 
 236         /* unbinding pcm substream to hda stream */
 237         substream->runtime->private_data = NULL;
 238         return 0;
 239 }

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