1e149ca29SPierre-Louis Bossart // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2c6be710fSLiam Girdwood //
3c6be710fSLiam Girdwood // This file is provided under a dual BSD/GPLv2 license. When using or
4c6be710fSLiam Girdwood // redistributing this file, you may do so under either license.
5c6be710fSLiam Girdwood //
6c6be710fSLiam Girdwood // Copyright(c) 2018 Intel Corporation. All rights reserved.
7c6be710fSLiam Girdwood //
8c6be710fSLiam Girdwood // Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
9c6be710fSLiam Girdwood // Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
10c6be710fSLiam Girdwood // Rander Wang <rander.wang@intel.com>
11c6be710fSLiam Girdwood // Keyon Jie <yang.jie@linux.intel.com>
12c6be710fSLiam Girdwood //
13c6be710fSLiam Girdwood
14c6be710fSLiam Girdwood /*
15c6be710fSLiam Girdwood * Hardware interface for generic Intel audio DSP HDA IP
16c6be710fSLiam Girdwood */
17c6be710fSLiam Girdwood
18246dd428SPierre-Louis Bossart #include <linux/moduleparam.h>
19c6be710fSLiam Girdwood #include <sound/hda_register.h>
20c6be710fSLiam Girdwood #include <sound/pcm_params.h>
21d272b657SBard Liao #include <trace/events/sof_intel.h>
22ee1e79b7SRanjani Sridharan #include "../sof-audio.h"
23c6be710fSLiam Girdwood #include "../ops.h"
24c6be710fSLiam Girdwood #include "hda.h"
25c6be710fSLiam Girdwood
26c6be710fSLiam Girdwood #define SDnFMT_BASE(x) ((x) << 14)
27c6be710fSLiam Girdwood #define SDnFMT_MULT(x) (((x) - 1) << 11)
28c6be710fSLiam Girdwood #define SDnFMT_DIV(x) (((x) - 1) << 8)
29c6be710fSLiam Girdwood #define SDnFMT_BITS(x) ((x) << 4)
30c6be710fSLiam Girdwood #define SDnFMT_CHAN(x) ((x) << 0)
31c6be710fSLiam Girdwood
32246dd428SPierre-Louis Bossart static bool hda_always_enable_dmi_l1;
33246dd428SPierre-Louis Bossart module_param_named(always_enable_dmi_l1, hda_always_enable_dmi_l1, bool, 0444);
34246dd428SPierre-Louis Bossart MODULE_PARM_DESC(always_enable_dmi_l1, "SOF HDA always enable DMI l1");
35246dd428SPierre-Louis Bossart
363bd45b8dSPierre-Louis Bossart static bool hda_disable_rewinds;
376c26b505SRanjani Sridharan module_param_named(disable_rewinds, hda_disable_rewinds, bool, 0444);
386c26b505SRanjani Sridharan MODULE_PARM_DESC(disable_rewinds, "SOF HDA disable rewinds");
396c26b505SRanjani Sridharan
hda_dsp_get_mult_div(struct snd_sof_dev * sdev,int rate)4049d7948eSCezary Rojewski u32 hda_dsp_get_mult_div(struct snd_sof_dev *sdev, int rate)
41c6be710fSLiam Girdwood {
42c6be710fSLiam Girdwood switch (rate) {
43c6be710fSLiam Girdwood case 8000:
44c6be710fSLiam Girdwood return SDnFMT_DIV(6);
45c6be710fSLiam Girdwood case 9600:
46c6be710fSLiam Girdwood return SDnFMT_DIV(5);
47c6be710fSLiam Girdwood case 11025:
48c6be710fSLiam Girdwood return SDnFMT_BASE(1) | SDnFMT_DIV(4);
49c6be710fSLiam Girdwood case 16000:
50c6be710fSLiam Girdwood return SDnFMT_DIV(3);
51c6be710fSLiam Girdwood case 22050:
52c6be710fSLiam Girdwood return SDnFMT_BASE(1) | SDnFMT_DIV(2);
53c6be710fSLiam Girdwood case 32000:
54c6be710fSLiam Girdwood return SDnFMT_DIV(3) | SDnFMT_MULT(2);
55c6be710fSLiam Girdwood case 44100:
56c6be710fSLiam Girdwood return SDnFMT_BASE(1);
57c6be710fSLiam Girdwood case 48000:
58c6be710fSLiam Girdwood return 0;
59c6be710fSLiam Girdwood case 88200:
60c6be710fSLiam Girdwood return SDnFMT_BASE(1) | SDnFMT_MULT(2);
61c6be710fSLiam Girdwood case 96000:
62c6be710fSLiam Girdwood return SDnFMT_MULT(2);
63c6be710fSLiam Girdwood case 176400:
64c6be710fSLiam Girdwood return SDnFMT_BASE(1) | SDnFMT_MULT(4);
65c6be710fSLiam Girdwood case 192000:
66c6be710fSLiam Girdwood return SDnFMT_MULT(4);
67c6be710fSLiam Girdwood default:
68c6be710fSLiam Girdwood dev_warn(sdev->dev, "can't find div rate %d using 48kHz\n",
69c6be710fSLiam Girdwood rate);
70c6be710fSLiam Girdwood return 0; /* use 48KHz if not found */
71c6be710fSLiam Girdwood }
72c6be710fSLiam Girdwood };
73c6be710fSLiam Girdwood
hda_dsp_get_bits(struct snd_sof_dev * sdev,int sample_bits)7449d7948eSCezary Rojewski u32 hda_dsp_get_bits(struct snd_sof_dev *sdev, int sample_bits)
75c6be710fSLiam Girdwood {
76c6be710fSLiam Girdwood switch (sample_bits) {
77c6be710fSLiam Girdwood case 8:
78c6be710fSLiam Girdwood return SDnFMT_BITS(0);
79c6be710fSLiam Girdwood case 16:
80c6be710fSLiam Girdwood return SDnFMT_BITS(1);
81c6be710fSLiam Girdwood case 20:
82c6be710fSLiam Girdwood return SDnFMT_BITS(2);
83c6be710fSLiam Girdwood case 24:
84c6be710fSLiam Girdwood return SDnFMT_BITS(3);
85c6be710fSLiam Girdwood case 32:
86c6be710fSLiam Girdwood return SDnFMT_BITS(4);
87c6be710fSLiam Girdwood default:
88c6be710fSLiam Girdwood dev_warn(sdev->dev, "can't find %d bits using 16bit\n",
89c6be710fSLiam Girdwood sample_bits);
90c6be710fSLiam Girdwood return SDnFMT_BITS(1); /* use 16bits format if not found */
91c6be710fSLiam Girdwood }
92c6be710fSLiam Girdwood };
93c6be710fSLiam Girdwood
hda_dsp_pcm_hw_params(struct snd_sof_dev * sdev,struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_sof_platform_stream_params * platform_params)94c6be710fSLiam Girdwood int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
95c6be710fSLiam Girdwood struct snd_pcm_substream *substream,
96c6be710fSLiam Girdwood struct snd_pcm_hw_params *params,
9731f60a0cSPeter Ujfalusi struct snd_sof_platform_stream_params *platform_params)
98c6be710fSLiam Girdwood {
99c6be710fSLiam Girdwood struct hdac_stream *hstream = substream->runtime->private_data;
1007d88b960SPierre-Louis Bossart struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
101c6be710fSLiam Girdwood struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
102c6be710fSLiam Girdwood struct snd_dma_buffer *dmab;
103c6be710fSLiam Girdwood int ret;
104c6be710fSLiam Girdwood
105c6be710fSLiam Girdwood hstream->substream = substream;
106c6be710fSLiam Girdwood
107c6be710fSLiam Girdwood dmab = substream->runtime->dma_buffer_p;
108c6be710fSLiam Girdwood
1091f7b5d52SPeter Ujfalusi /*
1101f7b5d52SPeter Ujfalusi * Use the codec required format val (which is link_bps adjusted) when
1111f7b5d52SPeter Ujfalusi * the DSP is not in use
1121f7b5d52SPeter Ujfalusi */
1131f7b5d52SPeter Ujfalusi if (!sdev->dspless_mode_selected) {
1141f7b5d52SPeter Ujfalusi u32 rate = hda_dsp_get_mult_div(sdev, params_rate(params));
1151f7b5d52SPeter Ujfalusi u32 bits = hda_dsp_get_bits(sdev, params_width(params));
1161f7b5d52SPeter Ujfalusi
117c6be710fSLiam Girdwood hstream->format_val = rate | bits | (params_channels(params) - 1);
1181f7b5d52SPeter Ujfalusi }
1191f7b5d52SPeter Ujfalusi
1201f7b5d52SPeter Ujfalusi hstream->bufsize = params_buffer_bytes(params);
121c6be710fSLiam Girdwood hstream->period_bytes = params_period_bytes(params);
122c6be710fSLiam Girdwood hstream->no_period_wakeup =
123c6be710fSLiam Girdwood (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
124c6be710fSLiam Girdwood (params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
125c6be710fSLiam Girdwood
1267d88b960SPierre-Louis Bossart ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, params);
127c6be710fSLiam Girdwood if (ret < 0) {
128ce1f55baSCurtis Malainey dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret);
129c6be710fSLiam Girdwood return ret;
130c6be710fSLiam Girdwood }
131c6be710fSLiam Girdwood
1326c26b505SRanjani Sridharan /* enable SPIB when rewinds are disabled */
1336c26b505SRanjani Sridharan if (hda_disable_rewinds)
1347d88b960SPierre-Louis Bossart hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, 0);
1356c26b505SRanjani Sridharan else
1367d88b960SPierre-Louis Bossart hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
137c6be710fSLiam Girdwood
13831f60a0cSPeter Ujfalusi if (hda)
13931f60a0cSPeter Ujfalusi platform_params->no_ipc_position = hda->no_ipc_position;
140c6be710fSLiam Girdwood
14131f60a0cSPeter Ujfalusi platform_params->stream_tag = hstream->stream_tag;
142c6be710fSLiam Girdwood
143c6be710fSLiam Girdwood return 0;
144c6be710fSLiam Girdwood }
145c6be710fSLiam Girdwood
1466c26b505SRanjani Sridharan /* update SPIB register with appl position */
hda_dsp_pcm_ack(struct snd_sof_dev * sdev,struct snd_pcm_substream * substream)1476c26b505SRanjani Sridharan int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
1486c26b505SRanjani Sridharan {
1496c26b505SRanjani Sridharan struct hdac_stream *hstream = substream->runtime->private_data;
1506c26b505SRanjani Sridharan struct snd_pcm_runtime *runtime = substream->runtime;
1516c26b505SRanjani Sridharan ssize_t appl_pos, buf_size;
1526c26b505SRanjani Sridharan u32 spib;
1536c26b505SRanjani Sridharan
1546c26b505SRanjani Sridharan appl_pos = frames_to_bytes(runtime, runtime->control->appl_ptr);
1556c26b505SRanjani Sridharan buf_size = frames_to_bytes(runtime, runtime->buffer_size);
1566c26b505SRanjani Sridharan
1576c26b505SRanjani Sridharan spib = appl_pos % buf_size;
1586c26b505SRanjani Sridharan
1596c26b505SRanjani Sridharan /* Allowable value for SPIB is 1 byte to max buffer size */
1606c26b505SRanjani Sridharan if (!spib)
1616c26b505SRanjani Sridharan spib = buf_size;
1626c26b505SRanjani Sridharan
16362582341SPierre-Louis Bossart sof_io_write(sdev, hstream->spib_addr, spib);
1646c26b505SRanjani Sridharan
1656c26b505SRanjani Sridharan return 0;
1666c26b505SRanjani Sridharan }
1676c26b505SRanjani Sridharan
hda_dsp_pcm_trigger(struct snd_sof_dev * sdev,struct snd_pcm_substream * substream,int cmd)168c6be710fSLiam Girdwood int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev,
169c6be710fSLiam Girdwood struct snd_pcm_substream *substream, int cmd)
170c6be710fSLiam Girdwood {
171c6be710fSLiam Girdwood struct hdac_stream *hstream = substream->runtime->private_data;
1727d88b960SPierre-Louis Bossart struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
173c6be710fSLiam Girdwood
1747d88b960SPierre-Louis Bossart return hda_dsp_stream_trigger(sdev, hext_stream, cmd);
175c6be710fSLiam Girdwood }
176c6be710fSLiam Girdwood
hda_dsp_pcm_pointer(struct snd_sof_dev * sdev,struct snd_pcm_substream * substream)177c6be710fSLiam Girdwood snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev,
178c6be710fSLiam Girdwood struct snd_pcm_substream *substream)
179c6be710fSLiam Girdwood {
1801205300aSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
181ee1e79b7SRanjani Sridharan struct snd_soc_component *scomp = sdev->component;
182c6be710fSLiam Girdwood struct hdac_stream *hstream = substream->runtime->private_data;
183c6be710fSLiam Girdwood struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
184c6be710fSLiam Girdwood struct snd_sof_pcm *spcm;
185c6be710fSLiam Girdwood snd_pcm_uframes_t pos;
186c6be710fSLiam Girdwood
187ee1e79b7SRanjani Sridharan spcm = snd_sof_find_spcm_dai(scomp, rtd);
188c6be710fSLiam Girdwood if (!spcm) {
189c6be710fSLiam Girdwood dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n",
190c6be710fSLiam Girdwood rtd->dai_link->id);
191c6be710fSLiam Girdwood return 0;
192c6be710fSLiam Girdwood }
193c6be710fSLiam Girdwood
194c6be710fSLiam Girdwood if (hda && !hda->no_ipc_position) {
195c6be710fSLiam Girdwood /* read position from IPC position */
196c6be710fSLiam Girdwood pos = spcm->stream[substream->stream].posn.host_posn;
197c6be710fSLiam Girdwood goto found;
198c6be710fSLiam Girdwood }
199c6be710fSLiam Girdwood
200ca7ab1dcSPeter Ujfalusi pos = hda_dsp_stream_get_position(hstream, substream->stream, true);
201c6be710fSLiam Girdwood found:
202c6be710fSLiam Girdwood pos = bytes_to_frames(substream->runtime, pos);
203c6be710fSLiam Girdwood
204d272b657SBard Liao trace_sof_intel_hda_dsp_pcm(sdev, hstream, substream, pos);
205c6be710fSLiam Girdwood return pos;
206c6be710fSLiam Girdwood }
207c6be710fSLiam Girdwood
hda_dsp_pcm_open(struct snd_sof_dev * sdev,struct snd_pcm_substream * substream)208c6be710fSLiam Girdwood int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
209c6be710fSLiam Girdwood struct snd_pcm_substream *substream)
210c6be710fSLiam Girdwood {
21189a400bdSRanjani Sridharan struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
212246dd428SPierre-Louis Bossart struct snd_pcm_runtime *runtime = substream->runtime;
21389a400bdSRanjani Sridharan struct snd_soc_component *scomp = sdev->component;
214c6be710fSLiam Girdwood struct hdac_ext_stream *dsp_stream;
21589a400bdSRanjani Sridharan struct snd_sof_pcm *spcm;
216c6be710fSLiam Girdwood int direction = substream->stream;
21789a400bdSRanjani Sridharan u32 flags = 0;
218c6be710fSLiam Girdwood
21989a400bdSRanjani Sridharan spcm = snd_sof_find_spcm_dai(scomp, rtd);
22089a400bdSRanjani Sridharan if (!spcm) {
22189a400bdSRanjani Sridharan dev_err(sdev->dev, "error: can't find PCM with DAI ID %d\n", rtd->dai_link->id);
22289a400bdSRanjani Sridharan return -EINVAL;
22389a400bdSRanjani Sridharan }
224c6be710fSLiam Girdwood
225246dd428SPierre-Louis Bossart /*
2266c26b505SRanjani Sridharan * if we want the .ack to work, we need to prevent the control from being mapped.
2276c26b505SRanjani Sridharan * The status can still be mapped.
2286c26b505SRanjani Sridharan */
2296c26b505SRanjani Sridharan if (hda_disable_rewinds)
2306c26b505SRanjani Sridharan runtime->hw.info |= SNDRV_PCM_INFO_NO_REWINDS | SNDRV_PCM_INFO_SYNC_APPLPTR;
2316c26b505SRanjani Sridharan
2326c26b505SRanjani Sridharan /*
233246dd428SPierre-Louis Bossart * All playback streams are DMI L1 capable, capture streams need
234246dd428SPierre-Louis Bossart * pause push/release to be disabled
235246dd428SPierre-Louis Bossart */
236246dd428SPierre-Louis Bossart if (hda_always_enable_dmi_l1 && direction == SNDRV_PCM_STREAM_CAPTURE)
237246dd428SPierre-Louis Bossart runtime->hw.info &= ~SNDRV_PCM_INFO_PAUSE;
238246dd428SPierre-Louis Bossart
239246dd428SPierre-Louis Bossart if (hda_always_enable_dmi_l1 ||
240a174e72eSKai Vehmanen direction == SNDRV_PCM_STREAM_PLAYBACK ||
24189a400bdSRanjani Sridharan spcm->stream[substream->stream].d0i3_compatible)
24289a400bdSRanjani Sridharan flags |= SOF_HDA_STREAM_DMI_L1_COMPATIBLE;
24389a400bdSRanjani Sridharan
24489a400bdSRanjani Sridharan dsp_stream = hda_dsp_stream_get(sdev, direction, flags);
245c6be710fSLiam Girdwood if (!dsp_stream) {
246c6be710fSLiam Girdwood dev_err(sdev->dev, "error: no stream available\n");
247c6be710fSLiam Girdwood return -ENODEV;
248c6be710fSLiam Girdwood }
249c6be710fSLiam Girdwood
250caebea04SKai Vehmanen /* minimum as per HDA spec */
251caebea04SKai Vehmanen snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4);
252caebea04SKai Vehmanen
253caebea04SKai Vehmanen /* avoid circular buffer wrap in middle of period */
254caebea04SKai Vehmanen snd_pcm_hw_constraint_integer(substream->runtime,
255caebea04SKai Vehmanen SNDRV_PCM_HW_PARAM_PERIODS);
256caebea04SKai Vehmanen
257*8a3f2ad5SPeter Ujfalusi /* Limit the maximum number of periods to not exceed the BDL entries count */
258*8a3f2ad5SPeter Ujfalusi if (runtime->hw.periods_max > HDA_DSP_MAX_BDL_ENTRIES)
259*8a3f2ad5SPeter Ujfalusi snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS,
260*8a3f2ad5SPeter Ujfalusi runtime->hw.periods_min,
261*8a3f2ad5SPeter Ujfalusi HDA_DSP_MAX_BDL_ENTRIES);
262*8a3f2ad5SPeter Ujfalusi
2631f7b5d52SPeter Ujfalusi /* Only S16 and S32 supported by HDA hardware when used without DSP */
2641f7b5d52SPeter Ujfalusi if (sdev->dspless_mode_selected)
2651f7b5d52SPeter Ujfalusi snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_FORMAT,
2661f7b5d52SPeter Ujfalusi SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32);
2671f7b5d52SPeter Ujfalusi
268c6be710fSLiam Girdwood /* binding pcm substream to hda stream */
269c6be710fSLiam Girdwood substream->runtime->private_data = &dsp_stream->hstream;
270c6be710fSLiam Girdwood return 0;
271c6be710fSLiam Girdwood }
272c6be710fSLiam Girdwood
hda_dsp_pcm_close(struct snd_sof_dev * sdev,struct snd_pcm_substream * substream)273c6be710fSLiam Girdwood int hda_dsp_pcm_close(struct snd_sof_dev *sdev,
274c6be710fSLiam Girdwood struct snd_pcm_substream *substream)
275c6be710fSLiam Girdwood {
276c6be710fSLiam Girdwood struct hdac_stream *hstream = substream->runtime->private_data;
277c6be710fSLiam Girdwood int direction = substream->stream;
278c6be710fSLiam Girdwood int ret;
279c6be710fSLiam Girdwood
280c6be710fSLiam Girdwood ret = hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
281c6be710fSLiam Girdwood
282c6be710fSLiam Girdwood if (ret) {
283c6be710fSLiam Girdwood dev_dbg(sdev->dev, "stream %s not opened!\n", substream->name);
284c6be710fSLiam Girdwood return -ENODEV;
285c6be710fSLiam Girdwood }
286c6be710fSLiam Girdwood
287c6be710fSLiam Girdwood /* unbinding pcm substream to hda stream */
288c6be710fSLiam Girdwood substream->runtime->private_data = NULL;
289c6be710fSLiam Girdwood return 0;
290c6be710fSLiam Girdwood }
291