* [PATCH v7 1/4] ASoC: Intel: add Skylake HDA platform driver
2015-07-06 3:24 [PATCH v7 0/4] ASoC: intel - add skylake PCM driver Vinod Koul
@ 2015-07-06 3:24 ` Vinod Koul
2015-07-08 18:56 ` Mark Brown
2015-07-06 3:24 ` [PATCH v7 2/4] ASoC: Intel - add Skylake HDA audio driver Vinod Koul
` (2 subsequent siblings)
3 siblings, 1 reply; 9+ messages in thread
From: Vinod Koul @ 2015-07-06 3:24 UTC (permalink / raw)
To: alsa-devel
Cc: tiwai, liam.r.girdwood, patches.audio, broonie, Jeeja KP,
Vinod Koul, Subhransu S. Prusty
From: Jeeja KP <jeeja.kp@intel.com>
This patch starts to add the Skylake HDA platform driver by defining
SoC CPU dais, DMA driver ops and implements ALSA operations
Signed-off-by: Jeeja KP <jeeja.kp@intel.com>
Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
sound/soc/intel/skylake/skl-pcm.c | 585 ++++++++++++++++++++++++++++++++++++++
sound/soc/intel/skylake/skl.h | 71 +++++
2 files changed, 656 insertions(+)
create mode 100644 sound/soc/intel/skylake/skl-pcm.c
create mode 100644 sound/soc/intel/skylake/skl.h
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
new file mode 100644
index 000000000000..78a9e1e72c09
--- /dev/null
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -0,0 +1,585 @@
+/*
+ * skl-pcm.c -ASoC HDA Platform driver file implementing PCM functionality
+ *
+ * Copyright (C) 2014-2015 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "skl.h"
+
+#define HDA_MONO 1
+#define HDA_STEREO 2
+
+static struct snd_pcm_hardware azx_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */
+ SNDRV_PCM_INFO_HAS_LINK_ATIME |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = AZX_MAX_BUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = AZX_MAX_BUF_SIZE / 2,
+ .periods_min = 2,
+ .periods_max = AZX_MAX_FRAG,
+ .fifo_size = 0,
+};
+
+static inline
+struct hdac_ext_stream *get_hdac_ext_stream(struct snd_pcm_substream *substream)
+{
+ return substream->runtime->private_data;
+}
+
+static struct hdac_ext_bus *get_bus_ctx(struct snd_pcm_substream *substream)
+{
+ struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+ struct hdac_stream *hstream = hdac_stream(stream);
+ struct hdac_bus *bus = hstream->bus;
+
+ return hbus_to_ebus(bus);
+}
+
+static int skl_substream_alloc_pages(struct hdac_ext_bus *ebus,
+ struct snd_pcm_substream *substream,
+ size_t size)
+{
+ struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+
+ hdac_stream(stream)->bufsize = 0;
+ hdac_stream(stream)->period_bytes = 0;
+ hdac_stream(stream)->format_val = 0;
+
+ return snd_pcm_lib_malloc_pages(substream, size);
+}
+
+static int skl_substream_free_pages(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus,
+ struct snd_pcm_runtime *runtime)
+{
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+ /* avoid wrap-around with wall-clock */
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME,
+ 20, 178000000);
+}
+
+static int skl_pcm_open(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct hdac_ext_stream *stream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct skl_dma_params *dma_params;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+ pm_runtime_get_sync(dai->dev);
+
+ stream = snd_hdac_ext_stream_assign(ebus, substream,
+ HDAC_EXT_STREAM_TYPE_COUPLED);
+ if (stream == NULL)
+ return -EBUSY;
+
+ skl_set_pcm_constrains(ebus, runtime);
+
+ /*
+ * disable WALLCLOCK timestamps for capture streams
+ * until we figure out how to handle digital inputs
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */
+ runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME;
+ }
+
+ runtime->private_data = stream;
+
+ dma_params = kzalloc(sizeof(*dma_params), GFP_KERNEL);
+ if (!dma_params)
+ return -ENOMEM;
+
+ dma_params->stream_tag = hdac_stream(stream)->stream_tag;
+ snd_soc_dai_set_dma_data(dai, substream, (void *)dma_params);
+
+ dev_dbg(dai->dev, "stream tag set in dma params=%d\n",
+ dma_params->stream_tag);
+ snd_pcm_set_sync(substream);
+
+ return 0;
+}
+
+static int skl_get_format(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct skl_dma_params *dma_params;
+ int format_val = 0;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ dma_params = (struct skl_dma_params *)
+ snd_soc_dai_get_dma_data(codec_dai, substream);
+
+ if (dma_params)
+ format_val = dma_params->format;
+
+ return format_val;
+}
+
+static int skl_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+ unsigned int format_val;
+ int err;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+ if (hdac_stream(stream)->prepared) {
+ dev_dbg(dai->dev, "already stream is prepared - returning\n");
+ return 0;
+ }
+
+ format_val = skl_get_format(substream, dai);
+ dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n",
+ hdac_stream(stream)->stream_tag, format_val);
+ snd_hdac_stream_reset(hdac_stream(stream));
+
+ err = snd_hdac_stream_set_params(hdac_stream(stream), format_val);
+ if (err < 0)
+ return err;
+
+ err = snd_hdac_stream_setup(hdac_stream(stream));
+ if (err < 0)
+ return err;
+
+ hdac_stream(stream)->prepared = 1;
+
+ return err;
+}
+
+static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+ ret = skl_substream_alloc_pages(ebus, substream,
+ params_buffer_bytes(params));
+ if (ret < 0)
+ return ret;
+
+ if (substream->runtime->dma_area)
+ memset(substream->runtime->dma_area, 0,
+ params_buffer_bytes(params));
+
+ dev_dbg(dai->dev, "format_val, rate=%d, ch=%d, format=%d\n",
+ runtime->rate, runtime->channels, runtime->format);
+
+ return 0;
+}
+
+static void skl_pcm_close(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+ struct skl_dma_params *dma_params = NULL;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+ snd_hdac_ext_stream_release(stream, HDAC_EXT_STREAM_TYPE_COUPLED);
+
+ dma_params = (struct skl_dma_params *)
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ pm_runtime_mark_last_busy(dai->dev);
+ pm_runtime_put_autosuspend(dai->dev);
+ kfree(dma_params);
+}
+
+static int skl_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+ snd_hdac_stream_cleanup(hdac_stream(stream));
+ hdac_stream(stream)->prepared = 0;
+
+ return skl_substream_free_pages(ebus_to_hbus(ebus), substream);
+}
+
+static struct snd_soc_dai_ops skl_pcm_dai_ops = {
+ .startup = skl_pcm_open,
+ .shutdown = skl_pcm_close,
+ .prepare = skl_pcm_prepare,
+ .hw_params = skl_pcm_hw_params,
+ .hw_free = skl_pcm_hw_free,
+};
+
+static struct snd_soc_dai_driver skl_platform_dai[] = {
+{
+ .name = "System Pin",
+ .ops = &skl_pcm_dai_ops,
+ .playback = {
+ .stream_name = "System Playback",
+ .channels_min = HDA_MONO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .capture = {
+ .stream_name = "System Capture",
+ .channels_min = HDA_MONO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "Deepbuffer Pin",
+ .ops = &skl_pcm_dai_ops,
+ .playback = {
+ .stream_name = "Deepbuffer Playback",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "LowLatency Pin",
+ .ops = &skl_pcm_dai_ops,
+ .playback = {
+ .stream_name = "Low Latency Playback",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+};
+
+static int skl_platform_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+ dev_dbg(rtd->cpu_dai->dev, "In %s:%s\n", __func__,
+ dai_link->cpu_dai_name);
+
+ runtime = substream->runtime;
+ snd_soc_set_runtime_hwparams(substream, &azx_pcm_hw);
+
+ return 0;
+}
+
+static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct hdac_ext_bus *ebus = get_bus_ctx(substream);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct hdac_ext_stream *stream;
+ struct snd_pcm_substream *s;
+ bool start;
+ int sbits = 0;
+ unsigned long cookie;
+ struct hdac_stream *hstr;
+
+ stream = get_hdac_ext_stream(substream);
+ hstr = hdac_stream(stream);
+
+ dev_dbg(bus->dev, "In %s cmd=%d\n", __func__, cmd);
+
+ if (!hstr->prepared)
+ return -EPIPE;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ start = true;
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ start = false;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ stream = get_hdac_ext_stream(s);
+ sbits |= 1 << hdac_stream(stream)->index;
+ snd_pcm_trigger_done(s, substream);
+ }
+
+ spin_lock_irqsave(&bus->reg_lock, cookie);
+
+ /* first, set SYNC bits of corresponding streams */
+ snd_hdac_stream_sync_trigger(hstr, true, sbits, AZX_REG_SSYNC);
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ stream = get_hdac_ext_stream(s);
+ if (start)
+ snd_hdac_stream_start(hdac_stream(stream), true);
+ else
+ snd_hdac_stream_stop(hdac_stream(stream));
+ }
+ spin_unlock_irqrestore(&bus->reg_lock, cookie);
+
+ snd_hdac_stream_sync(hstr, start, sbits);
+
+ spin_lock_irqsave(&bus->reg_lock, cookie);
+
+ /* reset SYNC bits */
+ snd_hdac_stream_sync_trigger(hstr, false, sbits, AZX_REG_SSYNC);
+ if (start)
+ snd_hdac_stream_timecounter_init(hstr, sbits);
+ spin_unlock_irqrestore(&bus->reg_lock, cookie);
+
+ return 0;
+}
+
+/* calculate runtime delay from LPIB */
+static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus,
+ struct hdac_ext_stream *sstream,
+ unsigned int pos)
+{
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct hdac_stream *hstream = hdac_stream(sstream);
+ struct snd_pcm_substream *substream = hstream->substream;
+ int stream = substream->stream;
+ unsigned int lpib_pos = snd_hdac_stream_get_pos_lpib(hstream);
+ int delay;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ delay = pos - lpib_pos;
+ else
+ delay = lpib_pos - pos;
+
+ if (delay < 0) {
+ if (delay >= hstream->delay_negative_threshold)
+ delay = 0;
+ else
+ delay += hstream->bufsize;
+ }
+
+ if (delay >= hstream->period_bytes) {
+ dev_info(bus->dev,
+ "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n",
+ delay, hstream->period_bytes);
+ delay = 0;
+ }
+
+ return bytes_to_frames(substream->runtime, delay);
+}
+
+static unsigned int skl_get_position(struct hdac_ext_stream *hstream,
+ int codec_delay)
+{
+ struct hdac_stream *hstr = hdac_stream(hstream);
+ struct snd_pcm_substream *substream = hstr->substream;
+ struct hdac_ext_bus *ebus = get_bus_ctx(substream);
+ unsigned int pos;
+ int delay;
+
+ /* use the position buffer as default */
+ pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream));
+
+ if (pos >= hdac_stream(hstream)->bufsize)
+ pos = 0;
+
+ if (substream->runtime) {
+ delay = skl_get_delay_from_lpib(ebus, hstream, pos)
+ + codec_delay;
+ substream->runtime->delay += delay;
+ }
+
+ return pos;
+}
+
+static snd_pcm_uframes_t skl_platform_pcm_pointer
+ (struct snd_pcm_substream *substream)
+{
+ struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream);
+
+ return bytes_to_frames(substream->runtime,
+ skl_get_position(hstream, 0));
+}
+
+static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream,
+ u64 nsec)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ u64 codec_frames, codec_nsecs;
+
+ if (!codec_dai->driver->ops->delay)
+ return nsec;
+
+ codec_frames = codec_dai->driver->ops->delay(substream, codec_dai);
+ codec_nsecs = div_u64(codec_frames * 1000000000LL,
+ substream->runtime->rate);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return nsec + codec_nsecs;
+
+ return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0;
+}
+
+static int skl_get_time_info(struct snd_pcm_substream *substream,
+ struct timespec *system_ts, struct timespec *audio_ts,
+ struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
+ struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
+{
+ struct hdac_ext_stream *sstream = get_hdac_ext_stream(substream);
+ struct hdac_stream *hstr = hdac_stream(sstream);
+ u64 nsec;
+
+ if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) &&
+ (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) {
+
+ snd_pcm_gettime(substream->runtime, system_ts);
+
+ nsec = timecounter_read(&hstr->tc);
+ nsec = div_u64(nsec, 3); /* can be optimized */
+ if (audio_tstamp_config->report_delay)
+ nsec = skl_adjust_codec_delay(substream, nsec);
+
+ *audio_ts = ns_to_timespec(nsec);
+
+ audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK;
+ audio_tstamp_report->accuracy_report = 1; /* rest of struct is valid */
+ audio_tstamp_report->accuracy = 42; /* 24MHzWallClk == 42ns resolution */
+
+ } else {
+ audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
+ }
+
+ return 0;
+}
+
+static struct snd_pcm_ops skl_platform_ops = {
+ .open = skl_platform_open,
+ .ioctl = snd_pcm_lib_ioctl,
+ .trigger = skl_platform_pcm_trigger,
+ .pointer = skl_platform_pcm_pointer,
+ .get_time_info = skl_get_time_info,
+ .mmap = snd_pcm_lib_default_mmap,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+static void skl_pcm_free(struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+#define MAX_PREALLOC_SIZE (32 * 1024 * 1024)
+
+static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct snd_pcm *pcm = rtd->pcm;
+ unsigned int size;
+ int retval = 0;
+ struct skl *skl = ebus_to_skl(ebus);
+
+ if (dai->driver->playback.channels_min ||
+ dai->driver->capture.channels_min) {
+ /* buffer pre-allocation */
+ size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024;
+ if (size > MAX_PREALLOC_SIZE)
+ size = MAX_PREALLOC_SIZE;
+ retval = snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(skl->pci),
+ size, MAX_PREALLOC_SIZE);
+ if (retval) {
+ dev_err(dai->dev, "dma buffer allocationf fail\n");
+ return retval;
+ }
+ }
+
+ return retval;
+}
+
+static struct snd_soc_platform_driver skl_platform_drv = {
+ .ops = &skl_platform_ops,
+ .pcm_new = skl_pcm_new,
+ .pcm_free = skl_pcm_free,
+};
+
+static const struct snd_soc_component_driver skl_component = {
+ .name = "pcm",
+};
+
+int skl_platform_register(struct device *dev)
+{
+ int ret;
+
+ ret = snd_soc_register_platform(dev, &skl_platform_drv);
+ if (ret) {
+ dev_err(dev, "soc platform registration failed %d\n", ret);
+ return ret;
+ }
+ ret = snd_soc_register_component(dev, &skl_component,
+ skl_platform_dai,
+ ARRAY_SIZE(skl_platform_dai));
+ if (ret) {
+ dev_err(dev, "soc component registration failed %d\n", ret);
+ snd_soc_unregister_platform(dev);
+ }
+
+ return ret;
+
+}
+
+int skl_platform_unregister(struct device *dev)
+{
+ snd_soc_unregister_component(dev);
+ snd_soc_unregister_platform(dev);
+ return 0;
+}
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
new file mode 100644
index 000000000000..cc0f3e263495
--- /dev/null
+++ b/sound/soc/intel/skylake/skl.h
@@ -0,0 +1,71 @@
+/*
+ * skl.h - HD Audio skylake defintions.
+ *
+ * Copyright (C) 2015 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#ifndef __SOUND_SOC_SKL_H
+#define __SOUND_SOC_SKL_H
+
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+
+#define SKL_SUSPEND_DELAY 2000
+
+/* Vendor Specific Registers */
+#define AZX_REG_VS_EM1 0x1000
+#define AZX_REG_VS_INRC 0x1004
+#define AZX_REG_VS_OUTRC 0x1008
+#define AZX_REG_VS_FIFOTRK 0x100C
+#define AZX_REG_VS_FIFOTRK2 0x1010
+#define AZX_REG_VS_EM2 0x1030
+#define AZX_REG_VS_EM3L 0x1038
+#define AZX_REG_VS_EM3U 0x103C
+#define AZX_REG_VS_EM4L 0x1040
+#define AZX_REG_VS_EM4U 0x1044
+#define AZX_REG_VS_LTRC 0x1048
+#define AZX_REG_VS_D0I3C 0x104A
+#define AZX_REG_VS_PCE 0x104B
+#define AZX_REG_VS_L2MAGC 0x1050
+#define AZX_REG_VS_L2LAHPT 0x1054
+#define AZX_REG_VS_SDXDPIB_XBASE 0x1084
+#define AZX_REG_VS_SDXDPIB_XINTERVAL 0x20
+#define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094
+#define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
+
+struct skl {
+ struct hdac_ext_bus ebus;
+ struct pci_dev *pci;
+
+ unsigned int init_failed:1; /* delayed init failed */
+ struct platform_device *dmic_dev;
+};
+
+#define skl_to_ebus(s) (&(s)->ebus)
+#define ebus_to_skl(sbus) \
+ container_of(sbus, struct skl, sbus)
+
+/* to pass dai dma data */
+struct skl_dma_params {
+ u32 format;
+ u8 stream_tag;
+};
+
+int skl_platform_unregister(struct device *dev);
+int skl_platform_register(struct device *dev);
+
+#endif /* __SOUND_SOC_SKL_H */
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v7 1/4] ASoC: Intel: add Skylake HDA platform driver
2015-07-06 3:24 ` [PATCH v7 1/4] ASoC: Intel: add Skylake HDA platform driver Vinod Koul
@ 2015-07-08 18:56 ` Mark Brown
2015-07-09 4:20 ` Vinod Koul
0 siblings, 1 reply; 9+ messages in thread
From: Mark Brown @ 2015-07-08 18:56 UTC (permalink / raw)
To: Vinod Koul
Cc: alsa-devel, tiwai, liam.r.girdwood, patches.audio, Jeeja KP,
Subhransu S. Prusty
[-- Attachment #1.1: Type: text/plain, Size: 1082 bytes --]
On Mon, Jul 06, 2015 at 08:54:23AM +0530, Vinod Koul wrote:
> + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
> + pm_runtime_get_sync(dai->dev);
Check the return value please, this can fail.
> +static int skl_get_format(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
> + struct skl_dma_params *dma_params;
> + int format_val = 0;
> + struct snd_soc_dai *codec_dai = rtd->codec_dai;
> +
> + dma_params = (struct skl_dma_params *)
> + snd_soc_dai_get_dma_data(codec_dai, substream);
Should be no need to cast away from void.
> + if (substream->runtime->dma_area)
> + memset(substream->runtime->dma_area, 0,
> + params_buffer_bytes(params));
Why do we need to memset here?
> + dma_params = (struct skl_dma_params *)
> + snd_soc_dai_get_dma_data(dai, substream);
> +
> + pm_runtime_mark_last_busy(dai->dev);
> + pm_runtime_put_autosuspend(dai->dev);
> + kfree(dma_params);
We check elsewhere for dma_params being NULL, shouldn't we set it to be
NULL when we free?
[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v7 1/4] ASoC: Intel: add Skylake HDA platform driver
2015-07-08 18:56 ` Mark Brown
@ 2015-07-09 4:20 ` Vinod Koul
0 siblings, 0 replies; 9+ messages in thread
From: Vinod Koul @ 2015-07-09 4:20 UTC (permalink / raw)
To: Mark Brown
Cc: alsa-devel, tiwai, liam.r.girdwood, patches.audio, Jeeja KP,
Subhransu S. Prusty
[-- Attachment #1.1: Type: text/plain, Size: 1448 bytes --]
On Wed, Jul 08, 2015 at 07:56:55PM +0100, Mark Brown wrote:
> On Mon, Jul 06, 2015 at 08:54:23AM +0530, Vinod Koul wrote:
>
> > + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
> > + pm_runtime_get_sync(dai->dev);
>
> Check the return value please, this can fail.
Yes..
>
> > +static int skl_get_format(struct snd_pcm_substream *substream,
> > + struct snd_soc_dai *dai)
> > +{
> > + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
> > + struct skl_dma_params *dma_params;
> > + int format_val = 0;
> > + struct snd_soc_dai *codec_dai = rtd->codec_dai;
> > +
> > + dma_params = (struct skl_dma_params *)
> > + snd_soc_dai_get_dma_data(codec_dai, substream);
>
> Should be no need to cast away from void.
Right.
>
> > + if (substream->runtime->dma_area)
> > + memset(substream->runtime->dma_area, 0,
> > + params_buffer_bytes(params));
>
> Why do we need to memset here?
Nope, the buffer is allocated thru snd_pcm_lib_malloc_pages() which IIUC
would for this case do a kzalloc, so we should rmeove this
>
> > + dma_params = (struct skl_dma_params *)
> > + snd_soc_dai_get_dma_data(dai, substream);
> > +
> > + pm_runtime_mark_last_busy(dai->dev);
> > + pm_runtime_put_autosuspend(dai->dev);
> > + kfree(dma_params);
>
> We check elsewhere for dma_params being NULL, shouldn't we set it to be
> NULL when we free?
Thanks for pointing, fixed now
--
~Vinod
[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v7 2/4] ASoC: Intel - add Skylake HDA audio driver
2015-07-06 3:24 [PATCH v7 0/4] ASoC: intel - add skylake PCM driver Vinod Koul
2015-07-06 3:24 ` [PATCH v7 1/4] ASoC: Intel: add Skylake HDA platform driver Vinod Koul
@ 2015-07-06 3:24 ` Vinod Koul
2015-07-08 19:01 ` Mark Brown
2015-07-06 3:24 ` [PATCH v7 3/4] ASoC: Intel - add makefile support for SKL driver Vinod Koul
2015-07-06 3:24 ` [PATCH v7 4/4] ASoC: intel - adds support for decoupled mode in skl driver Vinod Koul
3 siblings, 1 reply; 9+ messages in thread
From: Vinod Koul @ 2015-07-06 3:24 UTC (permalink / raw)
To: alsa-devel
Cc: tiwai, liam.r.girdwood, patches.audio, broonie, Jeeja KP,
Vinod Koul, Subhransu S. Prusty
From: Jeeja KP <jeeja.kp@intel.com>
This patch follows up by adding the HDA controller operations. This
code is mostly derived from Intel HDA PCI driver without legacy bits
Signed-off-by: Jeeja KP <jeeja.kp@intel.com>
Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
sound/soc/intel/skylake/skl.c | 532 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 532 insertions(+)
create mode 100644 sound/soc/intel/skylake/skl.c
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
new file mode 100644
index 000000000000..d801d92e5d98
--- /dev/null
+++ b/sound/soc/intel/skylake/skl.c
@@ -0,0 +1,532 @@
+/*
+ * skl.c - Implementation of ASoC Intel SKL HD Audio driver
+ *
+ * Copyright (C) 2014-2015 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ *
+ * Derived mostly from Intel HDA driver with following copyrights:
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ * PeiSen Hou <pshou@realtek.com.tw>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include "skl.h"
+
+/*
+ * initialize the PCI registers
+ */
+static void skl_update_pci_byte(struct pci_dev *pci, unsigned int reg,
+ unsigned char mask, unsigned char val)
+{
+ unsigned char data;
+
+ pci_read_config_byte(pci, reg, &data);
+ data &= ~mask;
+ data |= (val & mask);
+ pci_write_config_byte(pci, reg, data);
+}
+
+static void skl_init_pci(struct skl *skl)
+{
+ struct hdac_ext_bus *ebus = &skl->ebus;
+
+ /*
+ * Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
+ * TCSEL == Traffic Class Select Register, which sets PCI express QOS
+ * Ensuring these bits are 0 clears playback static on some HD Audio
+ * codecs.
+ * The PCI register TCSEL is defined in the Intel manuals.
+ */
+ dev_dbg(ebus_to_hbus(ebus)->dev, "Clearing TCSEL\n");
+ skl_update_pci_byte(skl->pci, AZX_PCIREG_TCSEL, 0x07, 0);
+}
+
+/* called from IRQ */
+static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr)
+{
+ snd_pcm_period_elapsed(hstr->substream);
+}
+
+static irqreturn_t skl_interrupt(int irq, void *dev_id)
+{
+ struct hdac_ext_bus *ebus = dev_id;
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ u32 status;
+
+#ifdef CONFIG_PM
+ if (!pm_runtime_active(bus->dev))
+ return IRQ_NONE;
+#endif
+
+ spin_lock(&bus->reg_lock);
+
+ status = snd_hdac_chip_readl(bus, INTSTS);
+ if (status == 0 || status == 0xffffffff) {
+ spin_unlock(&bus->reg_lock);
+ return IRQ_NONE;
+ }
+
+ /* clear rirb int */
+ status = snd_hdac_chip_readb(bus, RIRBSTS);
+ if (status & RIRB_INT_MASK) {
+ if (status & RIRB_INT_RESPONSE)
+ snd_hdac_bus_update_rirb(bus);
+ snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
+ }
+
+ spin_unlock(&bus->reg_lock);
+
+ return snd_hdac_chip_readl(bus, INTSTS) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
+}
+
+static irqreturn_t skl_threaded_handler(int irq, void *dev_id)
+{
+ struct hdac_ext_bus *ebus = dev_id;
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ u32 status;
+
+ status = snd_hdac_chip_readl(bus, INTSTS);
+
+ snd_hdac_bus_handle_stream_irq(bus, status, skl_stream_update);
+
+ return IRQ_HANDLED;
+}
+
+static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect)
+{
+ struct skl *skl = ebus_to_skl(ebus);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ int ret;
+
+ ret = request_threaded_irq(skl->pci->irq, skl_interrupt,
+ skl_threaded_handler,
+ IRQF_SHARED,
+ KBUILD_MODNAME, ebus);
+ if (ret) {
+ dev_err(bus->dev,
+ "unable to grab IRQ %d, disabling device\n",
+ skl->pci->irq);
+ return ret;
+ }
+
+ bus->irq = skl->pci->irq;
+ pci_intx(skl->pci, 1);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * power management
+ */
+static int skl_suspend(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+
+ snd_hdac_bus_stop_chip(bus);
+ snd_hdac_bus_enter_link_reset(bus);
+
+ return 0;
+}
+
+static int skl_resume(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct skl *hda = ebus_to_skl(ebus);
+
+ skl_init_pci(hda);
+
+ snd_hdac_bus_init_chip(bus, 1);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM
+static int skl_runtime_suspend(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+
+ dev_dbg(bus->dev, "in %s\n", __func__);
+
+ /* enable controller wake up event */
+ snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK);
+
+ snd_hdac_bus_stop_chip(bus);
+ snd_hdac_bus_enter_link_reset(bus);
+
+ return 0;
+}
+
+static int skl_runtime_resume(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct skl *hda = ebus_to_skl(ebus);
+ int status;
+
+ dev_dbg(bus->dev, "in %s\n", __func__);
+
+ /* Read STATESTS before controller reset */
+ status = snd_hdac_chip_readw(bus, STATESTS);
+
+ skl_init_pci(hda);
+ snd_hdac_bus_init_chip(bus, true);
+ /* disable controller Wake Up event */
+ snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops skl_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(skl_suspend, skl_resume)
+ SET_RUNTIME_PM_OPS(skl_runtime_suspend, skl_runtime_resume, NULL)
+};
+
+/*
+ * destructor
+ */
+static int skl_free(struct hdac_ext_bus *ebus)
+{
+ struct skl *skl = ebus_to_skl(ebus);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+
+ skl->init_failed = 1; /* to be sure */
+
+ snd_hdac_ext_stop_streams(ebus);
+
+ if (bus->irq >= 0)
+ free_irq(bus->irq, (void *)bus);
+ if (bus->remap_addr)
+ iounmap(bus->remap_addr);
+
+ snd_hdac_bus_free_stream_pages(bus);
+ snd_hdac_stream_free_all(ebus);
+ snd_hdac_link_free_all(ebus);
+ pci_release_regions(skl->pci);
+ pci_disable_device(skl->pci);
+
+ snd_hdac_ext_bus_exit(ebus);
+
+ return 0;
+}
+
+static int skl_dmic_device_register(struct skl *skl)
+{
+ struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
+ struct platform_device *pdev;
+ int ret;
+
+ /* SKL has one dmic port, so allocate dmic device for this */
+ pdev = platform_device_alloc("dmic-codec", -1);
+ if (!pdev) {
+ dev_err(bus->dev, "failed to allocate dmic device\n");
+ return -ENOMEM;
+ }
+
+ ret = platform_device_add(pdev);
+ if (ret) {
+ dev_err(bus->dev, "failed to add dmic device: %d\n", ret);
+ platform_device_put(pdev);
+ return ret;
+ }
+ skl->dmic_dev = pdev;
+
+ return 0;
+}
+
+static void skl_dmic_device_unregister(struct skl *skl)
+{
+ if (skl->dmic_dev)
+ platform_device_unregister(skl->dmic_dev);
+}
+
+/*
+ * Probe the given codec address
+ */
+static int probe_codec(struct hdac_ext_bus *ebus, int addr)
+{
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+ (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+ unsigned int res;
+
+ mutex_lock(&bus->cmd_mutex);
+ snd_hdac_bus_send_cmd(bus, cmd);
+ snd_hdac_bus_get_response(bus, addr, &res);
+ mutex_unlock(&bus->cmd_mutex);
+ if (res == -1)
+ return -EIO;
+ dev_dbg(bus->dev, "codec #%d probed OK\n", addr);
+
+ return snd_hdac_ext_bus_device_init(ebus, addr);
+}
+
+/* Codec initialization */
+static int skl_codec_create(struct hdac_ext_bus *ebus)
+{
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ int c, max_slots;
+
+ max_slots = HDA_MAX_CODECS;
+
+ /* First try to probe all given codec slots */
+ for (c = 0; c < max_slots; c++) {
+ if ((bus->codec_mask & (1 << c))) {
+ if (probe_codec(ebus, c) < 0) {
+ /*
+ * Some BIOSen give you wrong codec addresses
+ * that don't exist
+ */
+ dev_warn(bus->dev,
+ "Codec #%d probe error; disabling it...\n", c);
+ bus->codec_mask &= ~(1 << c);
+ /*
+ * More badly, accessing to a non-existing
+ * codec often screws up the controller bus,
+ * and disturbs the further communications.
+ * Thus if an error occurs during probing,
+ * better to reset the controller bus to get
+ * back to the sanity state.
+ */
+ snd_hdac_bus_stop_chip(bus);
+ snd_hdac_bus_init_chip(bus, true);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const struct hdac_bus_ops bus_core_ops = {
+ .command = snd_hdac_bus_send_cmd,
+ .get_response = snd_hdac_bus_get_response,
+};
+
+/*
+ * constructor
+ */
+static int skl_create(struct pci_dev *pci,
+ const struct hdac_io_ops *io_ops,
+ struct skl **rskl)
+{
+ struct skl *skl;
+ struct hdac_ext_bus *ebus;
+
+ int err;
+
+ *rskl = NULL;
+
+ err = pci_enable_device(pci);
+ if (err < 0)
+ return err;
+
+ skl = devm_kzalloc(&pci->dev, sizeof(*skl), GFP_KERNEL);
+ if (!skl) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+ ebus = &skl->ebus;
+ snd_hdac_ext_bus_init(ebus, &pci->dev, &bus_core_ops, io_ops);
+ ebus->bus.use_posbuf = 1;
+ skl->pci = pci;
+
+ ebus->bus.bdl_pos_adj = 0;
+
+ *rskl = skl;
+
+ return 0;
+}
+
+static int skl_first_init(struct hdac_ext_bus *ebus)
+{
+ struct skl *skl = ebus_to_skl(ebus);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct pci_dev *pci = skl->pci;
+ int err;
+ unsigned short gcap;
+ int cp_streams, pb_streams, start_idx;
+
+ err = pci_request_regions(pci, "Skylake HD audio");
+ if (err < 0)
+ return err;
+
+ bus->addr = pci_resource_start(pci, 0);
+ bus->remap_addr = pci_ioremap_bar(pci, 0);
+ if (bus->remap_addr == NULL) {
+ dev_err(bus->dev, "ioremap error\n");
+ return -ENXIO;
+ }
+
+ if (skl_acquire_irq(ebus, 0) < 0)
+ return -EBUSY;
+
+ pci_set_master(pci);
+ synchronize_irq(bus->irq);
+
+ gcap = snd_hdac_chip_readw(bus, GCAP);
+ dev_dbg(bus->dev, "chipset global capabilities = 0x%x\n", gcap);
+
+ /* allow 64bit DMA address if supported by H/W */
+ if (!dma_set_mask(bus->dev, DMA_BIT_MASK(64))) {
+ dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(64));
+ } else {
+ dma_set_mask(bus->dev, DMA_BIT_MASK(32));
+ dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(32));
+ }
+
+ /* read number of streams from GCAP register */
+ cp_streams = (gcap >> 8) & 0x0f;
+ pb_streams = (gcap >> 12) & 0x0f;
+
+ if (!pb_streams && !cp_streams)
+ return -EIO;
+
+ ebus->num_streams = cp_streams + pb_streams;
+
+ /* initialize streams */
+ snd_hdac_ext_stream_init_all
+ (ebus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE);
+ start_idx = cp_streams;
+ snd_hdac_ext_stream_init_all
+ (ebus, start_idx, pb_streams, SNDRV_PCM_STREAM_PLAYBACK);
+
+ err = snd_hdac_bus_alloc_stream_pages(bus);
+ if (err < 0)
+ return err;
+
+ /* initialize chip */
+ skl_init_pci(skl);
+
+ snd_hdac_bus_init_chip(bus, true);
+
+ /* codec detection */
+ if (!bus->codec_mask) {
+ dev_err(bus->dev, "no codecs found!\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int skl_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct skl *skl;
+ struct hdac_ext_bus *ebus = NULL;
+ struct hdac_bus *bus = NULL;
+ int err;
+
+ /* we use ext core ops, so provide NULL for ops here */
+ err = skl_create(pci, NULL, &skl);
+ if (err < 0)
+ return err;
+
+ ebus = &skl->ebus;
+ bus = ebus_to_hbus(ebus);
+
+ err = skl_first_init(ebus);
+ if (err < 0)
+ goto out_free;
+
+ pci_set_drvdata(skl->pci, ebus);
+
+ /* create device for soc dmic */
+ err = skl_dmic_device_register(skl);
+ if (err < 0)
+ goto out_free;
+
+ /* register platform dai and controls */
+ err = skl_platform_register(bus->dev);
+ if (err < 0)
+ goto out_dmic_free;
+
+ /* create codec instances */
+ err = skl_codec_create(ebus);
+ if (err < 0)
+ goto out_unregister;
+
+ pci_set_drvdata(skl->pci, ebus);
+
+ /*configure PM */
+ pm_runtime_set_autosuspend_delay(bus->dev, SKL_SUSPEND_DELAY);
+ pm_runtime_use_autosuspend(bus->dev);
+ pm_runtime_put_noidle(bus->dev);
+ pm_runtime_allow(bus->dev);
+
+ pci_set_drvdata(skl->pci, ebus);
+
+ return 0;
+
+out_unregister:
+ skl_platform_unregister(bus->dev);
+out_dmic_free:
+ skl_dmic_device_unregister(skl);
+out_free:
+ skl->init_failed = 1;
+ skl_free(ebus);
+ pci_set_drvdata(skl->pci, NULL);
+
+ return err;
+}
+
+static void skl_remove(struct pci_dev *pci)
+{
+ struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
+ struct skl *skl = ebus_to_skl(ebus);
+
+ if (pci_dev_run_wake(pci))
+ pm_runtime_get_noresume(&pci->dev);
+ pci_dev_put(pci);
+ skl_platform_unregister(&pci->dev);
+ skl_dmic_device_unregister(skl);
+ skl_free(ebus);
+ dev_set_drvdata(&pci->dev, NULL);
+}
+
+/* PCI IDs */
+static const struct pci_device_id skl_ids[] = {
+ /* Sunrise Point-LP */
+ { PCI_DEVICE(0x8086, 0x9d70), 0},
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, skl_ids);
+
+/* pci_driver definition */
+static struct pci_driver skl_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = skl_ids,
+ .probe = skl_probe,
+ .remove = skl_remove,
+ .driver = {
+ .pm = &skl_pm,
+ },
+};
+module_pci_driver(skl_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel Skylake ASoC HDA driver");
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v7 2/4] ASoC: Intel - add Skylake HDA audio driver
2015-07-06 3:24 ` [PATCH v7 2/4] ASoC: Intel - add Skylake HDA audio driver Vinod Koul
@ 2015-07-08 19:01 ` Mark Brown
2015-07-09 4:23 ` Vinod Koul
0 siblings, 1 reply; 9+ messages in thread
From: Mark Brown @ 2015-07-08 19:01 UTC (permalink / raw)
To: Vinod Koul
Cc: alsa-devel, tiwai, liam.r.girdwood, patches.audio, Jeeja KP,
Subhransu S. Prusty
[-- Attachment #1.1: Type: text/plain, Size: 977 bytes --]
On Mon, Jul 06, 2015 at 08:54:24AM +0530, Vinod Koul wrote:
> +static irqreturn_t skl_interrupt(int irq, void *dev_id)
> +{
> + struct hdac_ext_bus *ebus = dev_id;
> + struct hdac_bus *bus = ebus_to_hbus(ebus);
> + u32 status;
> +
> +#ifdef CONFIG_PM
> + if (!pm_runtime_active(bus->dev))
> + return IRQ_NONE;
> +#endif
There's a stub for pm_runtime_active for !PM which does the right thing
here, no need for ifdefs.
> + /*configure PM */
> + pm_runtime_set_autosuspend_delay(bus->dev, SKL_SUSPEND_DELAY);
> + pm_runtime_use_autosuspend(bus->dev);
> + pm_runtime_put_noidle(bus->dev);
> + pm_runtime_allow(bus->dev);
> +
> + pci_set_drvdata(skl->pci, ebus);
Shouldn't you be setting the driver data before you register the device
and enable PM? Otherwise the PM callbacks might run without it being
set.
> +out_free:
> + skl->init_failed = 1;
> + skl_free(ebus);
> + pci_set_drvdata(skl->pci, NULL);
No need to set the driver data to NULL, the driver core will do it.
[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v7 2/4] ASoC: Intel - add Skylake HDA audio driver
2015-07-08 19:01 ` Mark Brown
@ 2015-07-09 4:23 ` Vinod Koul
0 siblings, 0 replies; 9+ messages in thread
From: Vinod Koul @ 2015-07-09 4:23 UTC (permalink / raw)
To: Mark Brown
Cc: alsa-devel, tiwai, liam.r.girdwood, patches.audio, Jeeja KP,
Subhransu S. Prusty
[-- Attachment #1.1: Type: text/plain, Size: 1239 bytes --]
On Wed, Jul 08, 2015 at 08:01:14PM +0100, Mark Brown wrote:
> On Mon, Jul 06, 2015 at 08:54:24AM +0530, Vinod Koul wrote:
>
> > +static irqreturn_t skl_interrupt(int irq, void *dev_id)
> > +{
> > + struct hdac_ext_bus *ebus = dev_id;
> > + struct hdac_bus *bus = ebus_to_hbus(ebus);
> > + u32 status;
> > +
> > +#ifdef CONFIG_PM
> > + if (!pm_runtime_active(bus->dev))
> > + return IRQ_NONE;
> > +#endif
>
> There's a stub for pm_runtime_active for !PM which does the right thing
> here, no need for ifdefs.
Ah yes, I will fix it here and as well as HDA driver :)
> > + /*configure PM */
> > + pm_runtime_set_autosuspend_delay(bus->dev, SKL_SUSPEND_DELAY);
> > + pm_runtime_use_autosuspend(bus->dev);
> > + pm_runtime_put_noidle(bus->dev);
> > + pm_runtime_allow(bus->dev);
> > +
> > + pci_set_drvdata(skl->pci, ebus);
>
> Shouldn't you be setting the driver data before you register the device
> and enable PM? Otherwise the PM callbacks might run without it being
> set.
Right, will update
>
> > +out_free:
> > + skl->init_failed = 1;
> > + skl_free(ebus);
> > + pci_set_drvdata(skl->pci, NULL);
>
> No need to set the driver data to NULL, the driver core will do it.
Ok
--
~Vinod
[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v7 3/4] ASoC: Intel - add makefile support for SKL driver
2015-07-06 3:24 [PATCH v7 0/4] ASoC: intel - add skylake PCM driver Vinod Koul
2015-07-06 3:24 ` [PATCH v7 1/4] ASoC: Intel: add Skylake HDA platform driver Vinod Koul
2015-07-06 3:24 ` [PATCH v7 2/4] ASoC: Intel - add Skylake HDA audio driver Vinod Koul
@ 2015-07-06 3:24 ` Vinod Koul
2015-07-06 3:24 ` [PATCH v7 4/4] ASoC: intel - adds support for decoupled mode in skl driver Vinod Koul
3 siblings, 0 replies; 9+ messages in thread
From: Vinod Koul @ 2015-07-06 3:24 UTC (permalink / raw)
To: alsa-devel
Cc: tiwai, liam.r.girdwood, patches.audio, broonie, Jeeja KP,
Vinod Koul, Subhransu S. Prusty
From: Jeeja KP <jeeja.kp@intel.com>
This adds makefile and Kconfig to enable Skylake HD audio PCM driver
Signed-off-by: Jeeja KP <jeeja.kp@intel.com>
Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
sound/soc/intel/Kconfig | 7 +++++++
sound/soc/intel/Makefile | 1 +
sound/soc/intel/skylake/Makefile | 3 +++
3 files changed, 11 insertions(+)
create mode 100644 sound/soc/intel/skylake/Makefile
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index f3060a4ca040..9fa24c7003e5 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -132,3 +132,10 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
platforms with MAX98090 audio codec it also can support TI jack chip as aux device.
If unsure select "N".
+
+config SND_SOC_INTEL_SKYLAKE
+ tristate
+ select SND_HDA_EXT_CORE
+ help
+ Say Y here to include support for ASoC Intel "High Definition
+ Audio" (Skylake) and its compatible devices.
diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile
index 3853ec2ddbc7..2972699e1435 100644
--- a/sound/soc/intel/Makefile
+++ b/sound/soc/intel/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_SND_SOC_INTEL_SST) += common/
obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/
obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/
obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += atom/
+obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += skylake/
# Machine support
obj-$(CONFIG_SND_SOC_INTEL_SST) += boards/
diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile
new file mode 100644
index 000000000000..734d17cafde7
--- /dev/null
+++ b/sound/soc/intel/skylake/Makefile
@@ -0,0 +1,3 @@
+snd-soc-skl-objs := skl.o skl-pcm.o
+
+obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v7 4/4] ASoC: intel - adds support for decoupled mode in skl driver
2015-07-06 3:24 [PATCH v7 0/4] ASoC: intel - add skylake PCM driver Vinod Koul
` (2 preceding siblings ...)
2015-07-06 3:24 ` [PATCH v7 3/4] ASoC: Intel - add makefile support for SKL driver Vinod Koul
@ 2015-07-06 3:24 ` Vinod Koul
3 siblings, 0 replies; 9+ messages in thread
From: Vinod Koul @ 2015-07-06 3:24 UTC (permalink / raw)
To: alsa-devel
Cc: tiwai, patches.audio, liam.r.girdwood, Vinod Koul, broonie,
Jeeja KP
Decoupled mode is where audio link is broken to frontend HDA and backend
(hda/i2s/dmic/hdmi) links. This patch adds support for decoupled mode and
then adds dais, dai ops for be/fe cpu dais and interrupt handler change to
support decoupled mode
Signed-off-by: Jeeja KP <jeeja.kp@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
sound/soc/intel/skylake/skl-pcm.c | 367 +++++++++++++++++++++++++++++++++++++-
sound/soc/intel/skylake/skl.c | 11 ++
2 files changed, 370 insertions(+), 8 deletions(-)
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index 78a9e1e72c09..1011d88d6e32 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -96,6 +96,14 @@ static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus,
20, 178000000);
}
+static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus)
+{
+ if (ebus->ppcap)
+ return HDAC_EXT_STREAM_TYPE_HOST;
+ else
+ return HDAC_EXT_STREAM_TYPE_COUPLED;
+}
+
static int skl_pcm_open(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -108,7 +116,7 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,
pm_runtime_get_sync(dai->dev);
stream = snd_hdac_ext_stream_assign(ebus, substream,
- HDAC_EXT_STREAM_TYPE_COUPLED);
+ skl_get_host_stream_type(ebus));
if (stream == NULL)
return -EBUSY;
@@ -144,14 +152,26 @@ static int skl_get_format(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct skl_dma_params *dma_params;
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
int format_val = 0;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- dma_params = (struct skl_dma_params *)
+ if (ebus->ppcap) {
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ format_val = snd_hdac_calc_stream_format(runtime->rate,
+ runtime->channels,
+ runtime->format,
+ 32, 0);
+
+ } else {
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ dma_params = (struct skl_dma_params *)
snd_soc_dai_get_dma_data(codec_dai, substream);
- if (dma_params)
- format_val = dma_params->format;
+ if (dma_params)
+ format_val = dma_params->format;
+ }
return format_val;
}
@@ -192,8 +212,9 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- int ret;
+ int ret, dma_id;
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
ret = skl_substream_alloc_pages(ebus, substream,
@@ -208,6 +229,9 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
dev_dbg(dai->dev, "format_val, rate=%d, ch=%d, format=%d\n",
runtime->rate, runtime->channels, runtime->format);
+ dma_id = hdac_stream(stream)->stream_tag - 1;
+ dev_dbg(dai->dev, "dma_id=%d\n", dma_id);
+
return 0;
}
@@ -215,10 +239,12 @@ static void skl_pcm_close(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
struct skl_dma_params *dma_params = NULL;
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
- snd_hdac_ext_stream_release(stream, HDAC_EXT_STREAM_TYPE_COUPLED);
+
+ snd_hdac_ext_stream_release(stream, skl_get_host_stream_type(ebus));
dma_params = (struct skl_dma_params *)
snd_soc_dai_get_dma_data(dai, substream);
@@ -242,6 +268,169 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream,
return skl_substream_free_pages(ebus_to_hbus(ebus), substream);
}
+static int skl_dmic_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_hw_params params = {0};
+ struct snd_interval *channels, *rate;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+ channels = hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS);
+ channels->min = channels->max = substream->runtime->channels;
+ rate = hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_RATE);
+ rate->min = rate->max = substream->runtime->rate;
+ snd_mask_set(¶ms.masks[SNDRV_PCM_HW_PARAM_FORMAT -
+ SNDRV_PCM_HW_PARAM_FIRST_MASK],
+ substream->runtime->format);
+
+ return 0;
+}
+
+static int skl_link_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct hdac_ext_stream *link_dev;
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct skl_dma_params *dma_params;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int dma_id;
+
+ pr_debug("%s\n", __func__);
+ link_dev = snd_hdac_ext_stream_assign(ebus, substream,
+ HDAC_EXT_STREAM_TYPE_LINK);
+ if (!link_dev)
+ return -EBUSY;
+
+ snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev);
+
+ /* set the stream tag in the codec dai dma params */
+ dma_params = (struct skl_dma_params *)
+ snd_soc_dai_get_dma_data(codec_dai, substream);
+ if (dma_params)
+ dma_params->stream_tag = hdac_stream(link_dev)->stream_tag;
+ snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params);
+ dma_id = hdac_stream(link_dev)->stream_tag - 1;
+
+ return 0;
+}
+
+static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct hdac_ext_stream *link_dev =
+ snd_soc_dai_get_dma_data(dai, substream);
+ unsigned int format_val = 0;
+ struct skl_dma_params *dma_params;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_pcm_hw_params *params;
+ struct snd_interval *channels, *rate;
+ struct hdac_ext_link *link;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+ if (link_dev->link_prepared) {
+ dev_dbg(dai->dev, "already stream is prepared - returning\n");
+ return 0;
+ }
+ params = devm_kzalloc(dai->dev, sizeof(*params), GFP_KERNEL);
+ if (params == NULL)
+ return -ENOMEM;
+
+ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ channels->min = channels->max = substream->runtime->channels;
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ rate->min = rate->max = substream->runtime->rate;
+ snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+ SNDRV_PCM_HW_PARAM_FIRST_MASK],
+ substream->runtime->format);
+
+
+ dma_params = (struct skl_dma_params *)
+ snd_soc_dai_get_dma_data(codec_dai, substream);
+ if (dma_params)
+ format_val = dma_params->format;
+ dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d codec_dai_name=%s\n",
+ hdac_stream(link_dev)->stream_tag, format_val, codec_dai->name);
+
+ snd_hdac_ext_link_stream_reset(link_dev);
+
+ snd_hdac_ext_link_stream_setup(link_dev, format_val);
+
+ link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name);
+ if (!link)
+ return -EINVAL;
+
+ snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_dev)->stream_tag);
+ link_dev->link_prepared = 1;
+
+ return 0;
+}
+
+static int skl_link_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct hdac_ext_stream *link_dev =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ snd_hdac_ext_link_stream_start(link_dev);
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ snd_hdac_ext_link_stream_clear(link_dev);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int skl_link_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct hdac_ext_stream *link_dev =
+ snd_soc_dai_get_dma_data(dai, substream);
+ struct hdac_ext_link *link;
+
+ dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+ link_dev->link_prepared = 0;
+
+ link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name);
+ if (!link)
+ return -EINVAL;
+
+ snd_hdac_ext_link_clear_stream_id(link, hdac_stream(link_dev)->stream_tag);
+ snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK);
+ return 0;
+}
+
+static int skl_hda_be_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return pm_runtime_get_sync(dai->dev);
+}
+
+static void skl_hda_be_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ pm_runtime_mark_last_busy(dai->dev);
+ pm_runtime_put_autosuspend(dai->dev);
+}
+
static struct snd_soc_dai_ops skl_pcm_dai_ops = {
.startup = skl_pcm_open,
.shutdown = skl_pcm_close,
@@ -250,6 +439,21 @@ static struct snd_soc_dai_ops skl_pcm_dai_ops = {
.hw_free = skl_pcm_hw_free,
};
+static struct snd_soc_dai_ops skl_dmic_dai_ops = {
+ .startup = skl_hda_be_startup,
+ .prepare = skl_dmic_prepare,
+ .shutdown = skl_hda_be_shutdown,
+};
+
+static struct snd_soc_dai_ops skl_link_dai_ops = {
+ .startup = skl_hda_be_startup,
+ .prepare = skl_link_pcm_prepare,
+ .hw_params = skl_link_hw_params,
+ .hw_free = skl_link_hw_free,
+ .trigger = skl_link_pcm_trigger,
+ .shutdown = skl_hda_be_shutdown,
+};
+
static struct snd_soc_dai_driver skl_platform_dai[] = {
{
.name = "System Pin",
@@ -270,6 +474,17 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
},
},
{
+ .name = "Reference Pin",
+ .ops = &skl_pcm_dai_ops,
+ .capture = {
+ .stream_name = "Reference Capture",
+ .channels_min = HDA_MONO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
.name = "Deepbuffer Pin",
.ops = &skl_pcm_dai_ops,
.playback = {
@@ -291,6 +506,80 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
},
},
+/* BE CPU Dais */
+{
+ .name = "iDisp Pin",
+ .ops = &skl_link_dai_ops,
+ .playback = {
+ .stream_name = "iDisp Tx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "DMIC01 Pin",
+ .ops = &skl_dmic_dai_ops,
+ .capture = {
+ .stream_name = "DMIC01 Rx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "DMIC23 Pin",
+ .ops = &skl_dmic_dai_ops,
+ .capture = {
+ .stream_name = "DMIC23 Rx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "HD-Codec Pin",
+ .ops = &skl_link_dai_ops,
+ .playback = {
+ .stream_name = "HD-Codec Tx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "HD-Codec Rx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "HD-Codec-SPK Pin",
+ .ops = &skl_link_dai_ops,
+ .playback = {
+ .stream_name = "HD-Codec-SPK Tx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "HD-Codec-AMIC Pin",
+ .ops = &skl_link_dai_ops,
+ .capture = {
+ .stream_name = "HD-Codec-AMIC Rx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
};
static int skl_platform_open(struct snd_pcm_substream *substream)
@@ -308,7 +597,7 @@ static int skl_platform_open(struct snd_pcm_substream *substream)
return 0;
}
-static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
+static int skl_pcm_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
@@ -382,6 +671,68 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
return 0;
}
+static int skl_dsp_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct hdac_ext_bus *ebus = get_bus_ctx(substream);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct hdac_ext_stream *stream;
+ int start;
+ unsigned long cookie;
+ struct hdac_stream *hstr;
+
+ dev_dbg(bus->dev, "In %s cmd=%d streamname=%s\n", __func__, cmd, cpu_dai->name);
+
+ stream = get_hdac_ext_stream(substream);
+ hstr = hdac_stream(stream);
+
+ if (!hstr->prepared)
+ return -EPIPE;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ start = 1;
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ start = 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&bus->reg_lock, cookie);
+
+ if (start)
+ snd_hdac_stream_start(hdac_stream(stream), true);
+ else
+ snd_hdac_stream_stop(hdac_stream(stream));
+
+ if (start)
+ snd_hdac_stream_timecounter_init(hstr, 0);
+
+ spin_unlock_irqrestore(&bus->reg_lock, cookie);
+
+ return 0;
+}
+static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct hdac_ext_bus *ebus = get_bus_ctx(substream);
+
+ if (ebus->ppcap)
+ return skl_dsp_trigger(substream, cmd);
+ else
+ return skl_pcm_trigger(substream, cmd);
+}
+
/* calculate runtime delay from LPIB */
static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus,
struct hdac_ext_stream *sstream,
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index d801d92e5d98..1e70e3962c76 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -382,6 +382,8 @@ static int skl_first_init(struct hdac_ext_bus *ebus)
return -ENXIO;
}
+ snd_hdac_ext_bus_parse_capabilities(ebus);
+
if (skl_acquire_irq(ebus, 0) < 0)
return -EBUSY;
@@ -455,6 +457,15 @@ static int skl_probe(struct pci_dev *pci,
pci_set_drvdata(skl->pci, ebus);
+ /* check if dsp is there */
+ if (ebus->ppcap) {
+ /* TODO register with dsp IPC */
+ dev_dbg(bus->dev, "Register dsp\n");
+ }
+
+ if (ebus->mlcap)
+ snd_hdac_ext_bus_get_ml_capabilities(ebus);
+
/* create device for soc dmic */
err = skl_dmic_device_register(skl);
if (err < 0)
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread