diff mbox

[v4,7/7] ASoC: intel - adds support for decoupled mode in skl driver

Message ID 1431341645-2457-8-git-send-email-vinod.koul@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Vinod Koul May 11, 2015, 10:54 a.m. UTC
From: Jeeja KP <jeeja.kp@intel.com>

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/hda-skl-pcm.c | 367 +++++++++++++++++++++++++++++++++-
 sound/soc/intel/skylake/hda-skl.c     |  23 ++-
 sound/soc/intel/skylake/hda-skl.h     |   1 +
 3 files changed, 382 insertions(+), 9 deletions(-)
diff mbox

Patch

diff --git a/sound/soc/intel/skylake/hda-skl-pcm.c b/sound/soc/intel/skylake/hda-skl-pcm.c
index 8f895c0c1777..1ecf1f68a834 100644
--- a/sound/soc/intel/skylake/hda-skl-pcm.c
+++ b/sound/soc/intel/skylake/hda-skl-pcm.c
@@ -102,6 +102,14 @@  static void soc_hda_set_pcm_constrains(struct soc_hdac_bus *sbus,
 				     20, 178000000);
 }
 
+static enum hdac_stream_type hda_get_host_stream_type(struct soc_hdac_bus *sbus)
+{
+	if (sbus->ppcap)
+		return HDAC_STREAM_TYPE_HOST;
+	else
+		return HDAC_STREAM_TYPE_COUPLED;
+}
+
 static int soc_hda_pcm_open(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *dai)
 {
@@ -114,7 +122,7 @@  static int soc_hda_pcm_open(struct snd_pcm_substream *substream,
 	pm_runtime_get_sync(dai->dev);
 
 	stream = snd_soc_hdac_stream_assign(sbus, substream,
-					HDAC_STREAM_TYPE_COUPLED);
+					hda_get_host_stream_type(sbus));
 	if (stream == NULL)
 		return -EBUSY;
 
@@ -149,15 +157,27 @@  static int hda_get_format(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct soc_hdac_bus *sbus = dev_get_drvdata(dai->dev);
 	struct soc_hda_dma_params *dma_params;
 	int format_val = 0;
-	struct snd_soc_dai *codec_dai = rtd->codec_dai;
 
-	dma_params  = (struct soc_hda_dma_params *)
+	if (sbus->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 soc_hda_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;
 }
@@ -196,8 +216,9 @@  static int soc_hda_pcm_hw_params(struct snd_pcm_substream *substream,
 				struct snd_soc_dai *dai)
 {
 	struct soc_hdac_bus *sbus = dev_get_drvdata(dai->dev);
+	struct soc_hdac_stream *stream = get_hdac_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 = substream_alloc_pages(sbus, substream,
@@ -213,17 +234,22 @@  static int soc_hda_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;
+	dev_dbg(dai->dev, "dma_id=%d\n", dma_id);
+
 	return ret;
 }
 
 static void soc_hda_pcm_close(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *dai)
 {
+	struct soc_hdac_bus *sbus = dev_get_drvdata(dai->dev);
 	struct soc_hdac_stream *stream = get_hdac_stream(substream);
 	struct soc_hda_dma_params *dma_params = NULL;
 
 	dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
-	snd_soc_hdac_stream_release(stream, HDAC_STREAM_TYPE_COUPLED);
+
+	snd_soc_hdac_stream_release(stream, hda_get_host_stream_type(sbus));
 
 	dma_params  = (struct soc_hda_dma_params *)
 			snd_soc_dai_get_dma_data(dai, substream);
@@ -247,6 +273,169 @@  static int soc_hda_pcm_hw_free(struct snd_pcm_substream *substream,
 	return substream_free_pages(hdac_bus(sbus), substream);
 }
 
+static int hda_be_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(&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(&params.masks[SNDRV_PCM_HW_PARAM_FORMAT -
+					SNDRV_PCM_HW_PARAM_FIRST_MASK],
+					substream->runtime->format);
+
+	return 0;
+}
+
+static int soc_hda_be_link_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct soc_hdac_bus *sbus = dev_get_drvdata(dai->dev);
+	struct soc_hdac_stream *link_dev;
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct soc_hda_dma_params *dma_params;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	int dma_id;
+
+	pr_debug("%s\n", __func__);
+	link_dev = snd_soc_hdac_stream_assign(sbus, substream,
+					HDAC_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 soc_hda_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;
+
+	return 0;
+}
+
+static int soc_hda_be_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 soc_hdac_bus *sbus = dev_get_drvdata(dai->dev);
+	struct soc_hdac_stream *link_dev =
+			snd_soc_dai_get_dma_data(dai, substream);
+	unsigned int format_val = 0;
+	struct soc_hda_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 soc_hdac_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(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+					SNDRV_PCM_HW_PARAM_FIRST_MASK],
+					substream->runtime->format);
+
+
+	dma_params  = (struct soc_hda_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_soc_hdac_link_stream_reset(link_dev);
+
+	snd_soc_hdac_link_stream_setup(link_dev, format_val);
+
+	link = snd_soc_hdac_bus_get_link(sbus, rtd->codec->component.name);
+	if (!link)
+		return -EINVAL;
+
+	snd_soc_hdac_link_set_stream_id(link, hdac_stream(link_dev)->stream_tag);
+	link_dev->link_prepared = 1;
+
+	return 0;
+}
+
+static int soc_hda_be_link_pcm_trigger(struct snd_pcm_substream *substream,
+	int cmd, struct snd_soc_dai *dai)
+{
+	struct soc_hdac_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_soc_hdac_link_stream_start(link_dev);
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+		snd_soc_hdac_link_stream_clear(link_dev);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int soc_hda_be_link_hw_free(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct soc_hdac_bus *sbus = dev_get_drvdata(dai->dev);
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct soc_hdac_stream *link_dev =
+				snd_soc_dai_get_dma_data(dai, substream);
+	struct soc_hdac_link *link;
+
+	dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+	link_dev->link_prepared = 0;
+
+	link = snd_soc_hdac_bus_get_link(sbus, rtd->codec->component.name);
+	if (!link)
+		return -EINVAL;
+
+	snd_soc_hdac_link_clear_stream_id(link, hdac_stream(link_dev)->stream_tag);
+	snd_soc_hdac_stream_release(link_dev, HDAC_STREAM_TYPE_LINK);
+	return 0;
+}
+
+static int soc_hda_be_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	return pm_runtime_get_sync(dai->dev);
+}
+
+static void soc_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 hda_pcm_dai_ops = {
 	.startup = soc_hda_pcm_open,
 	.shutdown = soc_hda_pcm_close,
@@ -255,6 +444,21 @@  static struct snd_soc_dai_ops hda_pcm_dai_ops = {
 	.hw_free = soc_hda_pcm_hw_free,
 };
 
+static struct snd_soc_dai_ops hda_be_dmic_dai_ops = {
+	.startup = soc_hda_be_startup,
+	.prepare = hda_be_dmic_prepare,
+	.shutdown = soc_hda_be_shutdown,
+};
+
+static struct snd_soc_dai_ops hda_be_link_dai_ops = {
+	.startup = soc_hda_be_startup,
+	.prepare = soc_hda_be_link_pcm_prepare,
+	.hw_params = soc_hda_be_link_hw_params,
+	.hw_free = soc_hda_be_link_hw_free,
+	.trigger = soc_hda_be_link_pcm_trigger,
+	.shutdown = soc_hda_be_shutdown,
+};
+
 static struct snd_soc_dai_driver soc_hda_platform_dai[] = {
 {
 	.name = "System Pin",
@@ -275,6 +479,17 @@  static struct snd_soc_dai_driver soc_hda_platform_dai[] = {
 	},
 },
 {
+	.name = "Reference Pin",
+	.ops = &hda_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 = &hda_pcm_dai_ops,
 	.playback = {
@@ -296,6 +511,80 @@  static struct snd_soc_dai_driver soc_hda_platform_dai[] = {
 		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
 	},
 },
+/* BE CPU  Dais */
+{
+	.name = "iDisp Pin",
+	.ops = &hda_be_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 = &hda_be_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 = &hda_be_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 = &hda_be_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 = &hda_be_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 = &hda_be_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 soc_hda_platform_open(struct snd_pcm_substream *substream)
@@ -312,7 +601,7 @@  static int soc_hda_platform_open(struct snd_pcm_substream *substream)
 	return 0;
 }
 
-static int soc_hda_platform_pcm_trigger(struct snd_pcm_substream *substream,
+static int soc_hda_pcm_trigger(struct snd_pcm_substream *substream,
 					int cmd)
 {
 	struct soc_hdac_bus *sbus = get_bus_ctx(substream);
@@ -385,6 +674,68 @@  static int soc_hda_platform_pcm_trigger(struct snd_pcm_substream *substream,
 	return 0;
 }
 
+static int soc_hda_dsp_trigger(struct snd_pcm_substream *substream,
+		int cmd)
+{
+	struct soc_hdac_bus *sbus = get_bus_ctx(substream);
+	struct hdac_bus *bus = hdac_bus(sbus);
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct soc_hdac_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_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 soc_hda_platform_pcm_trigger(struct snd_pcm_substream *substream,
+					int cmd)
+{
+	struct soc_hdac_bus *sbus = get_bus_ctx(substream);
+
+	if (sbus->ppcap)
+		return soc_hda_dsp_trigger(substream, cmd);
+	else
+		return soc_hda_pcm_trigger(substream, cmd);
+}
+
 /* calculate runtime delay from LPIB */
 static int hda_get_delay_from_lpib(struct soc_hdac_bus *sbus,
 				struct soc_hdac_stream *sstream,
diff --git a/sound/soc/intel/skylake/hda-skl.c b/sound/soc/intel/skylake/hda-skl.c
index 3a8e6f702bac..a0d72f8393f2 100644
--- a/sound/soc/intel/skylake/hda-skl.c
+++ b/sound/soc/intel/skylake/hda-skl.c
@@ -169,10 +169,16 @@  static int azx_acquire_irq(struct soc_hdac_bus *sbus, int do_disconnect)
 	struct hda_skl *hda = to_hda_skl(sbus);
 	struct hdac_bus *bus = hdac_bus(sbus);
 	int ret = 0;
+	unsigned long flags = 0;
+
+	if (sbus->ppcap)
+		flags = IRQF_SHARED;
+	else
+		flags = hda->msi ? 0 : IRQF_SHARED;
 
 	ret = request_threaded_irq(hda->pci->irq, azx_interrupt,
 			azx_threaded_handler,
-			hda->msi ? 0 : IRQF_SHARED,
+			flags,
 			KBUILD_MODNAME, sbus);
 	if (ret) {
 		dev_err(bus->dev,
@@ -346,6 +352,10 @@  static int probe_codec(struct soc_hdac_bus *sbus, int addr)
 	if (res == -1)
 		return -EIO;
 	dev_dbg(bus->dev, "codec #%d probed OK\n", addr);
+
+	if (sbus->mlcap)
+		snd_soc_hdac_bus_map_codec_to_link(sbus, addr);
+
 	return snd_soc_hdac_bus_device_init(sbus, addr);
 }
 
@@ -446,6 +456,8 @@  static int azx_first_init(struct soc_hdac_bus *sbus)
 		if (pci_enable_msi(pci) < 0)
 			hda->msi = 0;
 
+	snd_soc_hdac_bus_parse_capabilities(sbus);
+
 	if (azx_acquire_irq(sbus, 0) < 0)
 		return -EBUSY;
 
@@ -575,6 +587,15 @@  static int azx_probe(struct pci_dev *pci,
 	if (err < 0)
 		goto out_free;
 
+	/* check if dsp is there */
+	if (sbus->ppcap) {
+		/* TODO register with dsp lib */
+		dev_dbg(bus->dev, "Register dsp\n");
+	}
+
+	if (sbus->mlcap)
+		snd_soc_hdac_bus_get_ml_capabilities(sbus);
+
 	/*create device for soc dmic*/
 	err = hda_dmic_device_register(hda);
 	if (err < 0)
diff --git a/sound/soc/intel/skylake/hda-skl.h b/sound/soc/intel/skylake/hda-skl.h
index ed6ccd31b198..7793111e42e5 100644
--- a/sound/soc/intel/skylake/hda-skl.h
+++ b/sound/soc/intel/skylake/hda-skl.h
@@ -23,6 +23,7 @@ 
 
 #include <sound/core.h>
 #include <sound/initval.h>
+#include <sound/soc.h>
 #include <sound/hda_register.h>
 #include <sound/hdaudio.h>
 #include <sound/soc-hdaudio.h>